//! Disposable real MMIO v2 queue publication and reset in a QEMU RNG device. use super::*; use os_virtio_mmio_v2::{Identity, Queue, Registry, Window}; unsafe fn read(base: u64, offset: u64) -> u32 { unsafe { read_volatile((base + offset) as *const u32) } } unsafe fn write(base: u64, offset: u64, value: u32) { unsafe { write_volatile((base + offset) as *mut u32, value) } } pub fn run(boot: &BootData, tables: &mmu::Tables, allocator: &mut memory::FrameAllocator) -> Result<(), &'static str> { let ram_end = boot.hardware.ram_base.checked_add(boot.hardware.ram_size) .ok_or("k1465-ram-overflow")?; let blob = unsafe { core::slice::from_raw_parts(boot.dtb_address as *const u8, boot.dtb_size) }; let nodes = dtb::discover_virtio_mmio(blob).map_err(|_| "k1465-dtb-virtio")?; let mut chosen = None; for node in nodes.as_slice() { let window = Window::new(node.base, node.size, boot.hardware.ram_base, ram_end) .map_err(|_| "k1465-dtb-window")?; if !tables.device_page_matches(window.base & !0xfff) { return Err("k1465-device-mapping"); } let identity = unsafe { Identity {magic: read(node.base, 0), version: read(node.base, 4), device_id: read(node.base, 8), vendor_id: read(node.base, 0xc)} }; if identity.device_id == 4 { if chosen.replace((window, identity)).is_some() { return Err("k1465-duplicate-rng"); } } } let (window, identity) = chosen.ok_or("k1465-rng-missing")?; if identity.magic != os_virtio_mmio_v2::MAGIC || identity.version != os_virtio_mmio_v2::VERSION || identity.vendor_id != 0x554d_4551 { return Err("k1465-rng-identity"); } let base = window.base; unsafe { write(base, 0x30, 0); } if unsafe { read(base, 0x70) != 0 || read(base, 0x44) != 0 } { return Err("k1465-rng-already-owned"); } let mut registry = Registry::<1>::new(); let claim = registry.claim(window, identity).map_err(|_| "k1465-claim")?; let free_before = allocator.remaining_pages(); let mut frames = [0u64; 3]; let mut allocated = 0usize; for frame in &mut frames { match allocator.allocate() { Some(address) => { *frame = address; allocated += 1; } None => break, } } if allocated != 3 { for frame in &frames[..allocated] { allocator.release(*frame).map_err(|_| "k1465-alloc-rollback")?; } registry.release(claim).map_err(|_| "k1465-claim-rollback")?; return Err("k1465-queue-frames"); } for frame in frames { unsafe { core::ptr::write_bytes(frame as *mut u8, 0, memory::PAGE_SIZE as usize); } } let mut failure = None; let (offered_low, offered_high, accepted_high, maximum, size, status_ready, queue_ready) = unsafe { write(base, 0x70, 0); write(base, 0x70, 1); write(base, 0x70, 3); write(base, 0x14, 0); let offered_low = read(base, 0x10); write(base, 0x14, 1); let offered_high = read(base, 0x10); let accepted_high = offered_high & 3; if registry.negotiate(claim, (offered_low, offered_high), (0, accepted_high)).is_err() { failure = Some("k1465-feature-model"); } write(base, 0x24, 0); write(base, 0x20, 0); write(base, 0x24, 1); write(base, 0x20, accepted_high); write(base, 0x70, 0xb); if read(base, 0x70) != 0xb { failure = Some("k1465-features-ok"); } write(base, 0x30, 0); let maximum = read(base, 0x34); let mut size = 8u32; while size > maximum && size > 1 { size >>= 1; } if maximum == 0 || maximum > 1024 || size > maximum { failure = Some("k1465-queue-maximum"); } if failure.is_none() { let queue = Queue {maximum: maximum as u16, size: size as u16, desc: frames[0], available: frames[1], used: frames[2]}; if registry.queue_ready(claim, queue).is_err() { failure = Some("k1465-queue-model"); } else { write(base, 0x38, size); write(base, 0x80, frames[0] as u32); write(base, 0x84, (frames[0] >> 32) as u32); write(base, 0x90, frames[1] as u32); write(base, 0x94, (frames[1] >> 32) as u32); write(base, 0xa0, frames[2] as u32); write(base, 0xa4, (frames[2] >> 32) as u32); asm!("dsb sy", options(nostack, preserves_flags)); write(base, 0x44, 1); if read(base, 0x44) != 1 { failure = Some("k1465-queue-ready-readback"); } else if registry.driver_ok(claim).is_err() { failure = Some("k1465-driver-model"); } else { write(base, 0x70, 0xf); if read(base, 0x70) != 0xf { failure = Some("k1465-driver-ok-readback"); } } } } let status_ready = read(base, 0x70); let queue_ready = read(base, 0x44); (offered_low, offered_high, accepted_high, maximum, size, status_ready, queue_ready) }; unsafe { write(base, 0x70, 0); asm!("dsb sy", options(nostack, preserves_flags)); write(base, 0x30, 0); } let status_reset = unsafe { read(base, 0x70) }; let queue_reset = unsafe { read(base, 0x44) }; if status_reset != 0 || queue_reset != 0 { return Err("k1465-reset-not-observed"); } registry.observe_reset(claim, status_reset, queue_reset) .map_err(|_| "k1465-model-reset")?; registry.release(claim).map_err(|_| "k1465-release")?; for frame in frames { allocator.release(frame).map_err(|_| "k1465-frame-release")?; } if allocator.remaining_pages() != free_before || registry.count() != 0 { return Err("k1465-owner-restoration"); } if let Some(reason) = failure { return Err(reason); } if status_ready != 0xf || queue_ready != 1 { return Err("k1465-publication"); } write_str("TURKOS/OS/K1465-MMIO-V2: BASE=0x"); write_hex(base as usize); write_str(" QMAX=0x"); write_hex(maximum as usize); write_str(" QSIZE=0x"); write_hex(size as usize); write_str(" HOSTLOW=0x"); write_hex(offered_low as usize); write_str(" HOSTHIGH=0x"); write_hex(offered_high as usize); write_str(" ACCEPTHIGH=0x"); write_hex(accepted_high as usize); write_line(" STATUS=0xf QUEUE=1 RESET=0 CLAIMS=0 FRAMES=RETURNED"); Ok(()) }