//! Physical reset and GIC source mask for an empty block queue. The full //! network queue path is instrumented in os_k800_accept, which owns DMA pages. use super::*; use turkos_kernel::runtime::device_registry::{DetachError, DeviceClass, DeviceIdentity, DeviceKey, ResourceKind, ResourceRange, SafeRegistry, Transport}; pub fn mask_spi(gic_base: u64, intid: u32) -> Result<(), &'static str> { if !(32..1020).contains(&intid) { return Err("k1420-irq-range"); } let offset = u64::from(intid / 32) * 4; let bit = 1u32 << (intid % 32); unsafe { os_k940_write(gic_base, 0x180 + offset, bit); // GICD_ICENABLERn os_k940_write(gic_base, 0x280 + offset, bit); // GICD_ICPENDRn asm!("dsb sy", options(nostack, preserves_flags)); if os_k940_read(gic_base, 0x100 + offset) & bit != 0 { return Err("k1420-irq-mask-readback"); } } Ok(()) } pub fn run(boot: &BootData) -> Result<(), &'static str> { let proofs = unsafe { read_volatile(core::ptr::addr_of!(OS_K1420_NET_PROOFS)) }; if proofs != [1, 1] { return Err("k1420-net-full-queue-proofs"); } 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(|_| "k1420-dtb-devices")?; let mut block = None; for node in nodes.as_slice() { if unsafe { os_k940_read(node.base, 0) } != turkos_kernel::virtio_mmio::MAGIC { return Err("k1420-magic"); } if unsafe { os_k940_read(node.base, 8) } == 2 { if node.size != 0x200 || unsafe { os_k940_read(node.base, 4) } != 2 || block.replace(node.base).is_some() { return Err("k1420-block-identity"); } } } let block = block.ok_or("k1420-no-block")?; let vendor = unsafe { os_k940_read(block, 12) }; let scratch = unsafe { &*core::ptr::addr_of!(OS_K700_WORKSPACE) }; let irq = turkos_kernel::runtime::platform::os_k940_virtio_spi( blob, scratch, block, boot.interrupts.gic_distributor) .map_err(|_| "k1420-block-spi")?.0; let identity = DeviceIdentity {transport: Transport::Mmio, vendor_id: vendor, device_id: 2, class: DeviceClass::Block}; let key = DeviceKey {transport: Transport::Mmio, location: block}; let ranges = [ ResourceRange {kind: ResourceKind::Mmio, start: block, length: 0x200}, ResourceRange {kind: ResourceKind::Irq, start: u64::from(irq), length: 1}, ]; let mut registry = SafeRegistry::<1, 2>::new(); let handle = registry.attach(key, identity, &ranges, 1) .map_err(|_| "k1420-empty-attach")?; let before = registry.snapshot(); if registry.finish_detach(handle) != Err(DetachError::ResetRequired) || registry.snapshot() != before { return Err("k1420-empty-early-free"); } registry.stop_admissions(handle).map_err(|_| "k1420-empty-stop")?; unsafe { os_k940_write(block, 0x70, 0); asm!("dsb sy", options(nostack, preserves_flags)); if os_k940_read(block, 0x70) != 0 { return Err("k1420-empty-reset-readback"); } os_k940_write(block, 0x30, 0); if os_k940_read(block, 0x44) != 0 { return Err("k1420-empty-queue-ready"); } } registry.mark_reset(handle).map_err(|_| "k1420-empty-reset-model")?; if registry.drain_after_reset(handle) != Ok(0) { return Err("k1420-empty-drain"); } mask_spi(boot.interrupts.gic_distributor, irq)?; registry.mark_irq_detached(handle).map_err(|_| "k1420-empty-irq-model")?; registry.finish_detach(handle).map_err(|_| "k1420-empty-finish")?; if registry.binding_count() != 0 || registry.resource_count() != 0 { return Err("k1420-empty-ownership"); } write_str("TURKOS/OS/K1420-EMPTY: BLOCK=0x"); write_hex(block as usize); write_str(" IRQ=0x"); write_hex(irq as usize); write_line(" RESET=0 QUEUE-READY=0 DRAIN=0 IRQ-MASKED=1 CLAIMS=0 FREE-EQUAL=1"); Ok(()) }