//! Map one disposable downstream PCI BAR through a DTB-bounded aperture. use super::*; use os_pci_device_map::{Aperture, PageMapTable}; use os_pci_discovery::{ClaimTable, EcamWindow}; const TABLE_ADDRESS_MASK: u64 = 0x0000_ffff_ffff_f000; pub(super) unsafe fn unmap_bar_page(physical: u64) -> Result<(), &'static str> { let root_value: u64; unsafe { asm!("mrs {root}, ttbr0_el1", root = out(reg) root_value, options(nomem, nostack, preserves_flags)); } let root = root_value & TABLE_ADDRESS_MASK; if root == 0 || physical >= (1 << 30) { return Err("k1450-root"); } let root_entry = unsafe { read_volatile(root as *const u64) }; if root_entry & 3 != 3 { return Err("k1450-root-entry"); } let l2 = root_entry & TABLE_ADDRESS_MASK; let l2_slot = ((physical >> 21) & 511) as usize; let l2_entry = unsafe { read_volatile((l2 as *const u64).add(l2_slot)) }; if l2_entry & 3 != 3 { return Err("k1450-l2-entry"); } let l3 = l2_entry & TABLE_ADDRESS_MASK; let l3_slot = ((physical >> 12) & 511) as usize; let page = unsafe { (l3 as *mut u64).add(l3_slot) }; let descriptor = unsafe { read_volatile(page) }; if descriptor & 3 != 3 || descriptor & TABLE_ADDRESS_MASK != physical { return Err("k1450-device-entry"); } unsafe { write_volatile(page, 0); asm!("dsb ishst", "tlbi vaae1is, {va}", "dsb ish", "isb", va = in(reg) (physical >> 12), options(nostack, preserves_flags)); } if unsafe { read_volatile(page) } != 0 { return Err("k1450-unmap-readback"); } Ok(()) } 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(|_| "k1450-dtb-host")?; let (start, end) = host.memory_aperture.ok_or("k1450-memory-aperture")?; let aperture = Aperture::new(start, end).map_err(|_| "k1450-aperture")?; let mut mappings = PageMapTable::<1>::new(); let mapped = mappings.claim(start, 0x1000, aperture) .map_err(|_| "k1450-map-claim")?; let window = EcamWindow::new(host.base, host.size, host.first_bus, host.last_bus) .map_err(|_| "k1450-ecam-window")?; let mut configs = ClaimTable::<3>::new(); let outer = configs.claim(window, 0, 3, 0, 0).map_err(|_| "k1450-outer")?; let inner = configs.claim(window, 1, 1, 0, 0).map_err(|_| "k1450-inner")?; let leaf = configs.claim(window, 2, 1, 0, 0).map_err(|_| "k1450-leaf")?; if start != 0x1000_0000 || end <= start + 0x1000 || outer.physical != host.base + 0x18000 || inner.physical != host.base + 0x108000 || leaf.physical != host.base + 0x208000 { return Err("k1450-addresses"); } 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 leaf_command = unsafe { read_volatile((leaf.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((leaf.physical + 0x14) as *const u32) }; let bridge_window = 0x1000_1000u32; let assigned_bar = (start as u32) | (old_bar & 0xf); let observed: u32; let assigned_readback: u32; unsafe { write_volatile((leaf.physical + 4) as *mut u16, leaf_command & !2); write_volatile((leaf.physical + 0x14) as *mut u32, assigned_bar); write_volatile((outer.physical + 0x20) as *mut u32, bridge_window); write_volatile((inner.physical + 0x20) as *mut u32, bridge_window); write_volatile((outer.physical + 4) as *mut u16, outer_command | 2); write_volatile((inner.physical + 4) as *mut u16, inner_command | 2); write_volatile((leaf.physical + 4) as *mut u16, leaf_command | 2); asm!("dsb sy", options(nostack, preserves_flags)); assigned_readback = read_volatile((leaf.physical + 0x14) as *const u32); observed = read_volatile(start as *const u32); write_volatile((leaf.physical + 4) as *mut u16, leaf_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((leaf.physical + 0x14) 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((leaf.physical + 4) as *mut u16, leaf_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((leaf.physical + 4) as *const u16) == leaf_command && read_volatile((outer.physical + 0x20) as *const u32) == outer_memory && read_volatile((inner.physical + 0x20) as *const u32) == inner_memory && read_volatile((leaf.physical + 0x14) as *const u32) == old_bar }; let unmapped = unsafe { unmap_bar_page(start) }; configs.release(leaf).map_err(|_| "k1450-leaf-release")?; configs.release(inner).map_err(|_| "k1450-inner-release")?; configs.release(outer).map_err(|_| "k1450-outer-release")?; mappings.release(mapped).map_err(|_| "k1450-map-release")?; if !restored || assigned_readback & !0xf != start as u32 || observed == u32::MAX || unmapped.is_err() || configs.count() != 0 || mappings.count() != 0 { return Err("k1450-bar-map-restore"); } write_str("TURKOS/OS/K1450-MAP: BAR1=0x"); write_hex(start as usize); write_str(" VALUE=0x"); write_hex(observed as usize); write_line(" DEVICE-PTE=1 BRIDGES=RESTORED BAR=RESTORED UNMAPPED=1 CLAIMS=0"); Ok(()) }