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