Nothing to see here, move along
at main 481 lines 16 kB view raw
1use crate::acpi::mcfg::McfgEntry; 2use crate::mem::phys::BitmapFrameAllocator; 3use crate::static_vec::StaticVec; 4use x86_64::structures::paging::OffsetPageTable; 5 6use super::config::{self, EcamAddress}; 7 8pub const MAX_PCI_DEVICES: usize = 64; 9 10#[repr(C)] 11#[derive(Debug, Clone, Copy)] 12pub struct BlockedRange { 13 pub start: u16, 14 pub end: u16, 15} 16 17#[allow(dead_code)] 18#[derive(Debug, Clone, Copy)] 19pub struct MsixCapInfo { 20 pub cap_offset: u16, 21 pub table_size: u16, 22 pub table_bir: u8, 23 pub table_offset: u32, 24 pub pba_bir: u8, 25 pub pba_offset: u32, 26} 27 28#[allow(dead_code)] 29#[repr(C)] 30#[derive(Debug, Clone, Copy)] 31pub struct PciDeviceInfo { 32 pub bus: u8, 33 pub device: u8, 34 pub function: u8, 35 pub vendor_id: u16, 36 pub device_id: u16, 37 pub class_code: u8, 38 pub subclass: u8, 39 pub prog_if: u8, 40 pub header_type: u8, 41 pub interrupt_line: u8, 42 pub interrupt_pin: u8, 43 pub bars: [BarInfo; 6], 44 pub blocked_config_ranges: [Option<BlockedRange>; 4], 45 pub msix_cap: Option<MsixCapInfo>, 46} 47 48#[allow(dead_code)] 49#[repr(C)] 50#[derive(Debug, Clone, Copy)] 51pub enum BarInfo { 52 None, 53 Memory { 54 phys_base: u64, 55 size: u64, 56 is_64bit: bool, 57 prefetchable: bool, 58 }, 59 Io { 60 port_base: u16, 61 size: u16, 62 }, 63} 64 65impl BarInfo { 66 pub fn to_wire(self) -> lancer_core::pci::BarInfoWire { 67 match self { 68 BarInfo::None => lancer_core::pci::BarInfoWire::zeroed(), 69 BarInfo::Memory { 70 phys_base, 71 size, 72 is_64bit, 73 prefetchable, 74 } => { 75 let flags = match is_64bit { 76 true => lancer_core::pci::BAR_FLAG_64BIT, 77 false => 0, 78 } | match prefetchable { 79 true => lancer_core::pci::BAR_FLAG_PREFETCHABLE, 80 false => 0, 81 }; 82 lancer_core::pci::BarInfoWire { 83 tag: lancer_core::pci::BAR_TAG_MEMORY, 84 flags, 85 io_port_base: 0, 86 io_size: 0, 87 _reserved: [0; 2], 88 mem_phys_base: phys_base, 89 mem_size: size, 90 } 91 } 92 BarInfo::Io { port_base, size } => lancer_core::pci::BarInfoWire { 93 tag: lancer_core::pci::BAR_TAG_IO, 94 flags: 0, 95 io_port_base: port_base, 96 io_size: size, 97 _reserved: [0; 2], 98 mem_phys_base: 0, 99 mem_size: 0, 100 }, 101 } 102 } 103} 104 105impl PciDeviceInfo { 106 pub fn to_wire(self) -> lancer_core::pci::PciDeviceInfoWire { 107 let bars = [ 108 self.bars[0].to_wire(), 109 self.bars[1].to_wire(), 110 self.bars[2].to_wire(), 111 self.bars[3].to_wire(), 112 self.bars[4].to_wire(), 113 self.bars[5].to_wire(), 114 ]; 115 let blocked_config_ranges = [ 116 self.blocked_config_ranges[0].map_or( 117 lancer_core::pci::BlockedRangeWire::zeroed(), 118 |r| lancer_core::pci::BlockedRangeWire { 119 start: r.start, 120 end: r.end, 121 }, 122 ), 123 self.blocked_config_ranges[1].map_or( 124 lancer_core::pci::BlockedRangeWire::zeroed(), 125 |r| lancer_core::pci::BlockedRangeWire { 126 start: r.start, 127 end: r.end, 128 }, 129 ), 130 self.blocked_config_ranges[2].map_or( 131 lancer_core::pci::BlockedRangeWire::zeroed(), 132 |r| lancer_core::pci::BlockedRangeWire { 133 start: r.start, 134 end: r.end, 135 }, 136 ), 137 self.blocked_config_ranges[3].map_or( 138 lancer_core::pci::BlockedRangeWire::zeroed(), 139 |r| lancer_core::pci::BlockedRangeWire { 140 start: r.start, 141 end: r.end, 142 }, 143 ), 144 ]; 145 lancer_core::pci::PciDeviceInfoWire { 146 bus: self.bus, 147 device: self.device, 148 function: self.function, 149 class_code: self.class_code, 150 subclass: self.subclass, 151 prog_if: self.prog_if, 152 header_type: self.header_type, 153 interrupt_line: self.interrupt_line, 154 interrupt_pin: self.interrupt_pin, 155 _pad0: 0, 156 vendor_id: self.vendor_id, 157 device_id: self.device_id, 158 _pad1: [0; 2], 159 bars, 160 blocked_config_ranges, 161 } 162 } 163} 164 165pub type DeviceTable = StaticVec<PciDeviceInfo, MAX_PCI_DEVICES>; 166 167pub static DEVICE_TABLE: spin::Mutex<DeviceTable> = spin::Mutex::new(StaticVec::new()); 168 169pub fn enumerate_segment( 170 segment: &McfgEntry, 171 mapper: &mut OffsetPageTable, 172 allocator: &mut BitmapFrameAllocator, 173 hhdm_offset: u64, 174 table: &mut DeviceTable, 175) { 176 (segment.start_bus..=segment.end_bus).for_each(|bus| { 177 (0u8..32).for_each(|dev| { 178 let addr = match EcamAddress::new(segment.base_address, bus, dev, 0) { 179 Some(a) => a, 180 None => return, 181 }; 182 if config::ensure_ecam_mapped(addr, mapper, allocator, hhdm_offset).is_err() { 183 return; 184 } 185 186 let vendor = config::read_config_u16(addr, 0x00); 187 match vendor { 188 0xFFFF => {} 189 _ => { 190 let header_type = (config::read_config_u32(addr, 0x0C) >> 16) as u8; 191 let multifunction = header_type & 0x80 != 0; 192 let func_limit = match multifunction { 193 true => 8u8, 194 false => 1u8, 195 }; 196 197 (0..func_limit).for_each(|func| { 198 let faddr = match EcamAddress::new(segment.base_address, bus, dev, func) { 199 Some(a) => a, 200 None => return, 201 }; 202 match func { 203 0 => {} 204 _ => { 205 if config::ensure_ecam_mapped(faddr, mapper, allocator, hhdm_offset) 206 .is_err() 207 { 208 return; 209 } 210 } 211 } 212 probe_function(faddr, table); 213 }); 214 } 215 } 216 }); 217 }); 218} 219 220const PCI_CAP_MSI: u8 = 0x05; 221const PCI_CAP_MSIX: u8 = 0x11; 222 223struct CapScanResult { 224 blocked: [Option<BlockedRange>; 4], 225 msix: Option<MsixCapInfo>, 226} 227 228fn scan_capabilities(addr: EcamAddress) -> CapScanResult { 229 let mut result = CapScanResult { 230 blocked: [None; 4], 231 msix: None, 232 }; 233 let range_idx = 0usize; 234 235 let status_cmd = config::read_config_u32(addr, 0x04); 236 let status = (status_cmd >> 16) as u16; 237 if status & 0x10 == 0 { 238 return result; 239 } 240 241 let cap_ptr_word = config::read_config_u32(addr, 0x34); 242 let first_ptr = (cap_ptr_word & 0xFC) as u16; 243 244 (0..48u16).fold((first_ptr, range_idx), |(ptr, ri), _| match ptr { 245 0 => (0, ri), 246 _ if ri >= 4 => (0, ri), 247 _ => { 248 let header = config::read_config_u32(addr, ptr); 249 let cap_id = (header & 0xFF) as u8; 250 let next = ((header >> 8) & 0xFC) as u16; 251 let new_ri = match cap_id { 252 PCI_CAP_MSI => { 253 let msg_ctrl = config::read_config_u16(addr, ptr + 2); 254 let is_64bit = (msg_ctrl >> 7) & 1 != 0; 255 let has_pvmask = (msg_ctrl >> 8) & 1 != 0; 256 let cap_size: u16 = match (is_64bit, has_pvmask) { 257 (false, false) => 10, 258 (false, true) => 14, 259 (true, false) => 14, 260 (true, true) => 20, 261 }; 262 result.blocked[ri] = Some(BlockedRange { 263 start: ptr, 264 end: ptr + cap_size, 265 }); 266 ri + 1 267 } 268 PCI_CAP_MSIX => { 269 result.blocked[ri] = Some(BlockedRange { 270 start: ptr, 271 end: ptr + 12, 272 }); 273 274 let msg_ctrl = config::read_config_u16(addr, ptr + 2); 275 let table_size = (msg_ctrl & 0x7FF) + 1; 276 277 let table_word = config::read_config_u32(addr, ptr + 4); 278 let table_bir = (table_word & 0x7) as u8; 279 let table_offset = table_word & !0x7; 280 281 let pba_word = config::read_config_u32(addr, ptr + 8); 282 let pba_bir = (pba_word & 0x7) as u8; 283 let pba_offset = pba_word & !0x7; 284 285 result.msix = Some(MsixCapInfo { 286 cap_offset: ptr, 287 table_size, 288 table_bir, 289 table_offset, 290 pba_bir, 291 pba_offset, 292 }); 293 294 ri + 1 295 } 296 _ => ri, 297 }; 298 299 (next, new_ri) 300 } 301 }); 302 303 result 304} 305 306fn probe_function(addr: EcamAddress, table: &mut DeviceTable) { 307 let vendor_id = config::read_config_u16(addr, 0x00); 308 match vendor_id { 309 0xFFFF => {} 310 _ => { 311 let device_id = config::read_config_u16(addr, 0x02); 312 let class_rev = config::read_config_u32(addr, 0x08); 313 let class_code = (class_rev >> 24) as u8; 314 let subclass = (class_rev >> 16) as u8; 315 let prog_if = (class_rev >> 8) as u8; 316 317 let header_word = config::read_config_u32(addr, 0x0C); 318 let header_type = ((header_word >> 16) as u8) & 0x7F; 319 320 let (interrupt_line, interrupt_pin) = match header_type { 321 0 => { 322 let irq_word = config::read_config_u32(addr, 0x3C); 323 (irq_word as u8, (irq_word >> 8) as u8) 324 } 325 _ => (0, 0), 326 }; 327 328 let bars = match header_type { 329 0 => decode_bars(addr, 6), 330 1 => { 331 let mut b = [BarInfo::None; 6]; 332 let partial = decode_bars(addr, 2); 333 b[0] = partial[0]; 334 b[1] = partial[1]; 335 b 336 } 337 _ => [BarInfo::None; 6], 338 }; 339 340 let cap_scan = scan_capabilities(addr); 341 342 crate::kprintln!( 343 " PCI: {:02x}:{:02x}.{} vendor={:04x} device={:04x} class={:02x}/{:02x}", 344 addr.bus, 345 addr.device(), 346 addr.function(), 347 vendor_id, 348 device_id, 349 class_code, 350 subclass, 351 ); 352 353 let info = PciDeviceInfo { 354 bus: addr.bus, 355 device: addr.device(), 356 function: addr.function(), 357 vendor_id, 358 device_id, 359 class_code, 360 subclass, 361 prog_if, 362 header_type, 363 interrupt_line, 364 interrupt_pin, 365 bars, 366 blocked_config_ranges: cap_scan.blocked, 367 msix_cap: cap_scan.msix, 368 }; 369 370 match table.push(info) { 371 Ok(()) => {} 372 Err(_) => { 373 crate::kprintln!( 374 " PCI: device table full, dropping {:02x}:{:02x}.{}", 375 addr.bus, 376 addr.device(), 377 addr.function(), 378 ); 379 } 380 } 381 } 382 } 383} 384 385fn decode_bars(addr: EcamAddress, count: usize) -> [BarInfo; 6] { 386 debug_assert!(count <= 6); 387 let count = count.min(6); 388 let mut bars = [BarInfo::None; 6]; 389 let mut idx = 0usize; 390 391 let saved_cmd = config::read_config_u16(addr, 0x04); 392 config::write_config_u16(addr, 0x04, saved_cmd & !0x03u16); 393 394 core::iter::from_fn(|| match idx < count { 395 true => { 396 let current = idx; 397 let offset = 0x10 + (current as u16) * 4; 398 399 let original = config::read_config_u32(addr, offset); 400 match original { 401 0 => { 402 idx += 1; 403 Some((current, BarInfo::None)) 404 } 405 _ => { 406 let is_io = original & 1 != 0; 407 match is_io { 408 true => { 409 config::write_config_u32(addr, offset, 0xFFFF_FFFF); 410 let sizing = config::read_config_u32(addr, offset); 411 config::write_config_u32(addr, offset, original); 412 413 let io_mask = sizing & !0x3u32; 414 let size = (!io_mask).wrapping_add(1) as u16; 415 let port_base = (original & !0x3) as u16; 416 417 idx += 1; 418 Some((current, BarInfo::Io { port_base, size })) 419 } 420 false => { 421 let is_64bit = (original >> 1) & 0x3 == 0x2; 422 let prefetchable = (original >> 3) & 1 != 0; 423 424 config::write_config_u32(addr, offset, 0xFFFF_FFFF); 425 let sizing_lo = config::read_config_u32(addr, offset); 426 config::write_config_u32(addr, offset, original); 427 428 let (phys_base, size) = match is_64bit { 429 true if current + 1 < count => { 430 let hi_offset = offset + 4; 431 let original_hi = config::read_config_u32(addr, hi_offset); 432 config::write_config_u32(addr, hi_offset, 0xFFFF_FFFF); 433 let sizing_hi = config::read_config_u32(addr, hi_offset); 434 config::write_config_u32(addr, hi_offset, original_hi); 435 436 let base = 437 ((original_hi as u64) << 32) | ((original & !0xF) as u64); 438 let sizing_full = 439 ((sizing_hi as u64) << 32) | ((sizing_lo & !0xF) as u64); 440 let sz = (!sizing_full).wrapping_add(1); 441 442 idx += 2; 443 (base, sz) 444 } 445 _ => { 446 let base = (original & !0xF) as u64; 447 let mask = sizing_lo & !0xF; 448 let sz = (!(mask as u64)).wrapping_add(1) & 0xFFFF_FFFF; 449 450 idx += match is_64bit { 451 true => 2, 452 false => 1, 453 }; 454 (base, sz) 455 } 456 }; 457 458 Some(( 459 current, 460 BarInfo::Memory { 461 phys_base, 462 size, 463 is_64bit, 464 prefetchable, 465 }, 466 )) 467 } 468 } 469 } 470 } 471 } 472 false => None, 473 }) 474 .for_each(|(i, bar)| { 475 bars[i] = bar; 476 }); 477 478 config::write_config_u16(addr, 0x04, saved_cmd); 479 480 bars 481}