//! Raise and acknowledge a disposable QEMU EDU PCI INTx behind two bridges. use super::*; use os_pci_discovery::{ClaimTable, EcamWindow}; use os_pci_interrupts::{RouteTable, Source, swizzle}; 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(|_| "k1455-dtb-host")?; let (pci_start, pci_end) = host.memory_aperture.ok_or("k1455-aperture")?; let edu_bar = pci_start.checked_add(0x10_0000).ok_or("k1455-bar-overflow")?; if pci_start != 0x1000_0000 || edu_bar + 0x10_0000 > pci_end { return Err("k1455-bar-aperture"); } let window = EcamWindow::new(host.base, host.size, host.first_bus, host.last_bus) .map_err(|_| "k1455-ecam-window")?; let mut configs = ClaimTable::<3>::new(); let outer = configs.claim(window, 0, 3, 0, 0).map_err(|_| "k1455-outer")?; let inner = configs.claim(window, 1, 1, 0, 0).map_err(|_| "k1455-inner")?; let edu = configs.claim(window, 2, 2, 0, 0).map_err(|_| "k1455-edu")?; if edu.physical != host.base + 0x210000 || unsafe { read_volatile(edu.physical as *const u32) } != 0x11e8_1234 { return Err("k1455-edu-identity"); } write_line("TURKOS/OS/K1455-STAGE: EDU-CONFIG"); let pin = unsafe { read_volatile((edu.physical + 0x3d) as *const u8) }; let pin_on_inner = swizzle(pin, 2).map_err(|_| "k1455-first-swizzle")?; let root_pin = swizzle(pin_on_inner, 1).map_err(|_| "k1455-second-swizzle")?; let routes = host.slot3_intx.ok_or("k1455-interrupt-map")?; let spi = routes[(root_pin - 1) as usize]; let source = Source::new(0, 3, 0, root_pin).map_err(|_| "k1455-source")?; let mut ownership = RouteTable::<1>::new(); let route = ownership.claim(source, spi).map_err(|_| "k1455-route-claim")?; if pin != 1 || pin_on_inner != 3 || root_pin != 4 || spi != 37 || ownership.resolve(source) != Ok(37) { return Err("k1455-route-values"); } write_line("TURKOS/OS/K1455-STAGE: ROUTE"); let gic = boot.interrupts.gic_distributor; let pending = (gic + 0x204) as *const u32; let clear_pending = (gic + 0x284) as *mut u32; let bit = 1u32 << (spi - 32); if unsafe { read_volatile(pending) } & bit != 0 { return Err("k1455-preexisting-pending"); } write_line("TURKOS/OS/K1455-STAGE: GIC-CLEAR"); let outer_command = unsafe { read_volatile((outer.physical + 4) as *const u16) }; let inner_command = unsafe { read_volatile((inner.physical + 4) as *const u16) }; let edu_command = unsafe { read_volatile((edu.physical + 4) as *const u16) }; let outer_memory = unsafe { read_volatile((outer.physical + 0x20) as *const u32) }; let inner_memory = unsafe { read_volatile((inner.physical + 0x20) as *const u32) }; let old_bar = unsafe { read_volatile((edu.physical + 0x10) as *const u32) }; let daif: u64; unsafe { asm!("mrs {value}, daif", value = out(reg) daif, options(nomem, nostack, preserves_flags)); } let id: u32; let mut asserted = false; let mut acknowledged = false; let readback: u32; unsafe { asm!("msr daifset, #2", "isb", options(nostack, preserves_flags)); write_volatile((edu.physical + 4) as *mut u16, edu_command & !2); write_volatile((edu.physical + 0x10) as *mut u32, edu_bar as u32); write_volatile((outer.physical + 0x20) as *mut u32, 0x1010_1010); write_volatile((inner.physical + 0x20) as *mut u32, 0x1010_1010); write_volatile((outer.physical + 4) as *mut u16, outer_command | 2); write_volatile((inner.physical + 4) as *mut u16, inner_command | 2); write_volatile((edu.physical + 4) as *mut u16, (edu_command | 2) & !0x400); asm!("dsb sy", options(nostack, preserves_flags)); readback = read_volatile((edu.physical + 0x10) as *const u32); id = read_volatile(edu_bar as *const u32); write_line("TURKOS/OS/K1455-STAGE: EDU-MMIO"); let original_status = read_volatile((edu_bar + 0x24) as *const u32); if original_status == 0 { write_volatile((edu_bar + 0x60) as *mut u32, 1); asm!("dsb sy", options(nostack, preserves_flags)); for _ in 0..2_000 { let status = read_volatile((edu_bar + 0x24) as *const u32); if status & 1 != 0 && read_volatile(pending) & bit != 0 { asserted = true; break; } } write_volatile((edu_bar + 0x64) as *mut u32, 1); write_line("TURKOS/OS/K1455-STAGE: RAISE-ACK"); asm!("dsb sy", options(nostack, preserves_flags)); write_volatile(clear_pending, bit); asm!("dsb sy", options(nostack, preserves_flags)); for _ in 0..2_000 { if read_volatile((edu_bar + 0x24) as *const u32) & 1 == 0 && read_volatile(pending) & bit == 0 { acknowledged = true; break; } } } write_volatile((edu.physical + 4) as *mut u16, edu_command & !2); write_volatile((inner.physical + 4) as *mut u16, inner_command & !2); write_volatile((outer.physical + 4) as *mut u16, outer_command & !2); write_volatile((edu.physical + 0x10) as *mut u32, old_bar); write_volatile((inner.physical + 0x20) as *mut u32, inner_memory); write_volatile((outer.physical + 0x20) as *mut u32, outer_memory); write_volatile((edu.physical + 4) as *mut u16, edu_command); write_volatile((inner.physical + 4) as *mut u16, inner_command); write_volatile((outer.physical + 4) as *mut u16, outer_command); asm!("dsb sy", options(nostack, preserves_flags)); } let restored = unsafe { read_volatile((outer.physical + 4) as *const u16) == outer_command && read_volatile((inner.physical + 4) as *const u16) == inner_command && read_volatile((edu.physical + 4) as *const u16) == edu_command && read_volatile((outer.physical + 0x20) as *const u32) == outer_memory && read_volatile((inner.physical + 0x20) as *const u32) == inner_memory && read_volatile((edu.physical + 0x10) as *const u32) == old_bar }; write_line("TURKOS/OS/K1455-STAGE: PCI-RESTORED"); let unmapped = unsafe { k1450_pci_device_map::unmap_bar_page(edu_bar) }; write_line("TURKOS/OS/K1455-STAGE: UNMAPPED"); unsafe { asm!("msr daif, {value}", "isb", value = in(reg) daif, options(nostack, preserves_flags)); } configs.release(edu).map_err(|_| "k1455-edu-release")?; configs.release(inner).map_err(|_| "k1455-inner-release")?; configs.release(outer).map_err(|_| "k1455-outer-release")?; ownership.release(route).map_err(|_| "k1455-route-release")?; if id == 0 || id == u32::MAX || readback & !0xf != edu_bar as u32 || !asserted || !acknowledged || !restored || unmapped.is_err() || configs.count() != 0 || ownership.count() != 0 { return Err("k1455-intx-assert-ack"); } write_str("TURKOS/OS/K1455-INTX: PIN=0x"); write_hex(pin as usize); write_str(" ROOT-PIN=0x"); write_hex(root_pin as usize); write_str(" SPI=0x"); write_hex(spi as usize); write_str(" EDU-ID=0x"); write_hex(id as usize); write_line(" GIC-PENDING=OBSERVED ACK=1 BAR=RESTORED UNMAPPED=1 CLAIMS=0"); Ok(()) }