//! Configure and traverse two disposable QEMU PCI bridges. use super::*; use os_pci_bridges::{BusTree, Error as BridgeError}; use os_pci_discovery::{ClaimTable, EcamWindow}; pub fn run(boot: &BootData) -> Result<(), &'static str> { let blob = unsafe { core::slice::from_raw_parts(boot.dtb_address as *const u8, boot.dtb_size) }; let host = dtb::discover_pci_ecam(blob).map_err(|_| "k1440-dtb-host")?; let window = EcamWindow::new(host.base, host.size, host.first_bus, host.last_bus) .map_err(|_| "k1440-ecam-window")?; let mut claims = ClaimTable::<3>::new(); let outer = claims.claim(window, 0, 3, 0, 0).map_err(|_| "k1440-outer-claim")?; let inner = claims.claim(window, 1, 1, 0, 0).map_err(|_| "k1440-inner-claim")?; let leaf = claims.claim(window, 2, 1, 0, 0).map_err(|_| "k1440-leaf-claim")?; if claims.count() != 3 || outer.physical != host.base + 0x18000 || inner.physical != host.base + 0x108000 || leaf.physical != host.base + 0x208000 { return Err("k1440-ecam-addresses"); } // SAFETY: the three DTB-bounded ECAM pages are Device-nGnRnE mappings // installed before TTBR0. This isolated QEMU guest owns both disposable // bridges and may configure their bus registers before traversal. let outer_id = unsafe { read_volatile(outer.physical as *const u32) }; let outer_header = unsafe { read_volatile((outer.physical + 0x0c) as *const u32) }; if outer_id != 0x0001_1b36 || (outer_header >> 16) & 0x7f != 1 { return Err("k1440-outer-identity"); } unsafe { write_volatile((outer.physical + 0x18) as *mut u32, 0x0002_0100); asm!("dsb sy", options(nostack, preserves_flags)); if read_volatile((outer.physical + 0x18) as *const u32) & 0x00ff_ffff != 0x0002_0100 { return Err("k1440-outer-bus-readback"); } } let inner_id = unsafe { read_volatile(inner.physical as *const u32) }; let inner_header = unsafe { read_volatile((inner.physical + 0x0c) as *const u32) }; if inner_id != 0x0001_1b36 || (inner_header >> 16) & 0x7f != 1 { return Err("k1440-inner-identity"); } unsafe { write_volatile((inner.physical + 0x18) as *mut u32, 0x0002_0201); asm!("dsb sy", options(nostack, preserves_flags)); if read_volatile((inner.physical + 0x18) as *const u32) & 0x00ff_ffff != 0x0002_0201 { return Err("k1440-inner-bus-readback"); } } let leaf_id = unsafe { read_volatile(leaf.physical as *const u32) }; if leaf_id & 0xffff != 0x1af4 || leaf_id >> 16 == 0xffff { return Err("k1440-leaf-identity"); } let mut tree = BusTree::<2>::new(host.first_bus, host.last_bus) .map_err(|_| "k1440-tree-root")?; let parent = tree.add_root(0, 1, 2).map_err(|_| "k1440-tree-outer")?; let before = tree.snapshot(); if tree.add_child(parent, 1, 1, 2) != Err(BridgeError::SecondaryCollision) || tree.snapshot() != before { return Err("k1440-parent-collision"); } let child = tree.add_child(parent, 1, 2, 2) .map_err(|_| "k1440-tree-inner")?; if tree.owner(1) != Some(parent) || tree.owner(2) != Some(child) || tree.count() != 2 { return Err("k1440-duplicate-bus-owner"); } claims.release(leaf).map_err(|_| "k1440-leaf-release")?; claims.release(inner).map_err(|_| "k1440-inner-release")?; claims.release(outer).map_err(|_| "k1440-outer-release")?; if claims.count() != 0 { return Err("k1440-claim-leak"); } write_str("TURKOS/OS/K1440-BRIDGES: OUTER=0x"); write_hex(outer_id as usize); write_str(" INNER=0x"); write_hex(inner_id as usize); write_str(" LEAF=0x"); write_hex(leaf_id as usize); write_line(" BUS1-OWNER=1 BUS2-OWNER=2 DISCOVERED=3 COLLISION=REJECTED CLAIMS=0"); Ok(()) }