1use bitflags::bitflags;
4use core::{
5 array,
6 convert::TryFrom,
7 fmt::{self, Display, Formatter},
8};
9use log::warn;
10
11const INVALID_READ: u32 = 0xffffffff;
12
13const MAX_DEVICES: u8 = 32;
15const MAX_FUNCTIONS: u8 = 8;
17
18const STATUS_COMMAND_OFFSET: u8 = 0x04;
20const BAR0_OFFSET: u8 = 0x10;
22
23pub const PCI_CAP_ID_VNDR: u8 = 0x09;
25
26bitflags! {
27 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
29 pub struct Status: u16 {
30 const INTERRUPT_STATUS = 1 << 3;
33 const CAPABILITIES_LIST = 1 << 4;
35 const MHZ_66_CAPABLE = 1 << 5;
37 const FAST_BACK_TO_BACK_CAPABLE = 1 << 7;
40 const MASTER_DATA_PARITY_ERROR = 1 << 8;
42 const SIGNALED_TARGET_ABORT = 1 << 11;
45 const RECEIVED_TARGET_ABORT = 1 << 12;
47 const RECEIVED_MASTER_ABORT = 1 << 13;
49 const SIGNALED_SYSTEM_ERROR = 1 << 14;
51 const DETECTED_PARITY_ERROR = 1 << 15;
53 }
54}
55
56bitflags! {
57 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
59 pub struct Command: u16 {
60 const IO_SPACE = 1 << 0;
62 const MEMORY_SPACE = 1 << 1;
64 const BUS_MASTER = 1 << 2;
66 const SPECIAL_CYCLES = 1 << 3;
68 const MEMORY_WRITE_AND_INVALIDATE_ENABLE = 1 << 4;
70 const VGA_PALETTE_SNOOP = 1 << 5;
72 const PARITY_ERROR_RESPONSE = 1 << 6;
74 const SERR_ENABLE = 1 << 8;
77 const FAST_BACK_TO_BACK_ENABLE = 1 << 9;
79 const INTERRUPT_DISABLE = 1 << 10;
81 }
82}
83
84#[derive(Copy, Clone, Debug, Eq, PartialEq)]
86pub enum PciError {
87 InvalidBarType,
89}
90
91impl Display for PciError {
92 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
93 match self {
94 Self::InvalidBarType => write!(f, "Invalid PCI BAR type."),
95 }
96 }
97}
98
99#[derive(Debug)]
101pub struct PciRoot {
102 mmio_base: *mut u32,
103 cam: Cam,
104}
105
106#[derive(Copy, Clone, Debug, Eq, PartialEq)]
108pub enum Cam {
109 MmioCam,
113 Ecam,
117}
118
119impl Cam {
120 pub const fn size(self) -> u32 {
122 match self {
123 Self::MmioCam => 0x1000000,
124 Self::Ecam => 0x10000000,
125 }
126 }
127}
128
129impl PciRoot {
130 pub unsafe fn new(mmio_base: *mut u8, cam: Cam) -> Self {
141 assert!(mmio_base as usize & 0x3 == 0);
142 Self {
143 mmio_base: mmio_base as *mut u32,
144 cam,
145 }
146 }
147
148 unsafe fn unsafe_clone(&self) -> Self {
155 Self {
156 mmio_base: self.mmio_base,
157 cam: self.cam,
158 }
159 }
160
161 fn cam_offset(&self, device_function: DeviceFunction, register_offset: u8) -> u32 {
162 assert!(device_function.valid());
163
164 let bdf = (device_function.bus as u32) << 8
165 | (device_function.device as u32) << 3
166 | device_function.function as u32;
167 let address =
168 bdf << match self.cam {
169 Cam::MmioCam => 8,
170 Cam::Ecam => 12,
171 } | register_offset as u32;
172 assert!(address < self.cam.size());
174 assert!(address & 0x3 == 0);
176 address
177 }
178
179 pub(crate) fn config_read_word(
181 &self,
182 device_function: DeviceFunction,
183 register_offset: u8,
184 ) -> u32 {
185 let address = self.cam_offset(device_function, register_offset);
186 unsafe {
189 (self.mmio_base.add((address >> 2) as usize)).read_volatile()
191 }
192 }
193
194 pub(crate) fn config_write_word(
196 &mut self,
197 device_function: DeviceFunction,
198 register_offset: u8,
199 data: u32,
200 ) {
201 let address = self.cam_offset(device_function, register_offset);
202 unsafe {
205 (self.mmio_base.add((address >> 2) as usize)).write_volatile(data)
207 }
208 }
209
210 pub fn enumerate_bus(&self, bus: u8) -> BusDeviceIterator {
212 let root = unsafe { self.unsafe_clone() };
214 BusDeviceIterator {
215 root,
216 next: DeviceFunction {
217 bus,
218 device: 0,
219 function: 0,
220 },
221 }
222 }
223
224 pub fn get_status_command(&self, device_function: DeviceFunction) -> (Status, Command) {
226 let status_command = self.config_read_word(device_function, STATUS_COMMAND_OFFSET);
227 let status = Status::from_bits_truncate((status_command >> 16) as u16);
228 let command = Command::from_bits_truncate(status_command as u16);
229 (status, command)
230 }
231
232 pub fn set_command(&mut self, device_function: DeviceFunction, command: Command) {
234 self.config_write_word(
235 device_function,
236 STATUS_COMMAND_OFFSET,
237 command.bits().into(),
238 );
239 }
240
241 pub fn capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator {
243 CapabilityIterator {
244 root: self,
245 device_function,
246 next_capability_offset: self.capabilities_offset(device_function),
247 }
248 }
249
250 pub fn bars(
252 &mut self,
253 device_function: DeviceFunction,
254 ) -> Result<[Option<BarInfo>; 6], PciError> {
255 let mut bars = array::from_fn(|_| None);
256 let mut bar_index = 0;
257 while bar_index < 6 {
258 let info = self.bar_info(device_function, bar_index)?;
259 let takes_two_entries = info.takes_two_entries();
260 bars[usize::from(bar_index)] = Some(info);
261 bar_index += if takes_two_entries { 2 } else { 1 };
262 }
263 Ok(bars)
264 }
265
266 pub fn bar_info(
268 &mut self,
269 device_function: DeviceFunction,
270 bar_index: u8,
271 ) -> Result<BarInfo, PciError> {
272 let bar_orig = self.config_read_word(device_function, BAR0_OFFSET + 4 * bar_index);
273
274 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, 0xffffffff);
276 let size_mask = self.config_read_word(device_function, BAR0_OFFSET + 4 * bar_index);
277 let size = (!(size_mask & 0xfffffff0)).wrapping_add(1);
280
281 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, bar_orig);
283
284 if bar_orig & 0x00000001 == 0x00000001 {
285 let address = bar_orig & 0xfffffffc;
287 Ok(BarInfo::IO { address, size })
288 } else {
289 let mut address = u64::from(bar_orig & 0xfffffff0);
291 let prefetchable = bar_orig & 0x00000008 != 0;
292 let address_type = MemoryBarType::try_from(((bar_orig & 0x00000006) >> 1) as u8)?;
293 if address_type == MemoryBarType::Width64 {
294 if bar_index >= 5 {
295 return Err(PciError::InvalidBarType);
296 }
297 let address_top =
298 self.config_read_word(device_function, BAR0_OFFSET + 4 * (bar_index + 1));
299 address |= u64::from(address_top) << 32;
300 }
301 Ok(BarInfo::Memory {
302 address_type,
303 prefetchable,
304 address,
305 size,
306 })
307 }
308 }
309
310 pub fn set_bar_32(&mut self, device_function: DeviceFunction, bar_index: u8, address: u32) {
312 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, address);
313 }
314
315 pub fn set_bar_64(&mut self, device_function: DeviceFunction, bar_index: u8, address: u64) {
317 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, address as u32);
318 self.config_write_word(
319 device_function,
320 BAR0_OFFSET + 4 * (bar_index + 1),
321 (address >> 32) as u32,
322 );
323 }
324
325 fn capabilities_offset(&self, device_function: DeviceFunction) -> Option<u8> {
327 let (status, _) = self.get_status_command(device_function);
328 if status.contains(Status::CAPABILITIES_LIST) {
329 Some((self.config_read_word(device_function, 0x34) & 0xFC) as u8)
330 } else {
331 None
332 }
333 }
334}
335
336unsafe impl Send for PciRoot {}
338
339unsafe impl Sync for PciRoot {}
342
343#[derive(Clone, Debug, Eq, PartialEq)]
345pub enum BarInfo {
346 Memory {
348 address_type: MemoryBarType,
350 prefetchable: bool,
353 address: u64,
355 size: u32,
357 },
358 IO {
360 address: u32,
362 size: u32,
364 },
365}
366
367impl BarInfo {
368 pub fn takes_two_entries(&self) -> bool {
371 matches!(
372 self,
373 BarInfo::Memory {
374 address_type: MemoryBarType::Width64,
375 ..
376 }
377 )
378 }
379
380 pub fn memory_address_size(&self) -> Option<(u64, u32)> {
383 if let Self::Memory { address, size, .. } = self {
384 Some((*address, *size))
385 } else {
386 None
387 }
388 }
389}
390
391impl Display for BarInfo {
392 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
393 match self {
394 Self::Memory {
395 address_type,
396 prefetchable,
397 address,
398 size,
399 } => write!(
400 f,
401 "Memory space at {:#010x}, size {}, type {:?}, prefetchable {}",
402 address, size, address_type, prefetchable
403 ),
404 Self::IO { address, size } => {
405 write!(f, "I/O space at {:#010x}, size {}", address, size)
406 }
407 }
408 }
409}
410
411#[derive(Copy, Clone, Debug, Eq, PartialEq)]
413pub enum MemoryBarType {
414 Width32,
416 Below1MiB,
418 Width64,
420}
421
422impl From<MemoryBarType> for u8 {
423 fn from(bar_type: MemoryBarType) -> Self {
424 match bar_type {
425 MemoryBarType::Width32 => 0,
426 MemoryBarType::Below1MiB => 1,
427 MemoryBarType::Width64 => 2,
428 }
429 }
430}
431
432impl TryFrom<u8> for MemoryBarType {
433 type Error = PciError;
434
435 fn try_from(value: u8) -> Result<Self, Self::Error> {
436 match value {
437 0 => Ok(Self::Width32),
438 1 => Ok(Self::Below1MiB),
439 2 => Ok(Self::Width64),
440 _ => Err(PciError::InvalidBarType),
441 }
442 }
443}
444
445#[derive(Debug)]
447pub struct CapabilityIterator<'a> {
448 root: &'a PciRoot,
449 device_function: DeviceFunction,
450 next_capability_offset: Option<u8>,
451}
452
453impl<'a> Iterator for CapabilityIterator<'a> {
454 type Item = CapabilityInfo;
455
456 fn next(&mut self) -> Option<Self::Item> {
457 let offset = self.next_capability_offset?;
458
459 let capability_header = self.root.config_read_word(self.device_function, offset);
461 let id = capability_header as u8;
462 let next_offset = (capability_header >> 8) as u8;
463 let private_header = (capability_header >> 16) as u16;
464
465 self.next_capability_offset = if next_offset == 0 {
466 None
467 } else if next_offset < 64 || next_offset & 0x3 != 0 {
468 warn!("Invalid next capability offset {:#04x}", next_offset);
469 None
470 } else {
471 Some(next_offset)
472 };
473
474 Some(CapabilityInfo {
475 offset,
476 id,
477 private_header,
478 })
479 }
480}
481
482#[derive(Debug, Copy, Clone, Eq, PartialEq)]
484pub struct CapabilityInfo {
485 pub offset: u8,
487 pub id: u8,
489 pub private_header: u16,
491}
492
493#[derive(Debug)]
495pub struct BusDeviceIterator {
496 root: PciRoot,
499 next: DeviceFunction,
500}
501
502impl Iterator for BusDeviceIterator {
503 type Item = (DeviceFunction, DeviceFunctionInfo);
504
505 fn next(&mut self) -> Option<Self::Item> {
506 while self.next.device < MAX_DEVICES {
507 let current = self.next;
509 let device_vendor = self.root.config_read_word(current, 0);
510
511 self.next.function += 1;
513 if self.next.function >= MAX_FUNCTIONS {
514 self.next.function = 0;
515 self.next.device += 1;
516 }
517
518 if device_vendor != INVALID_READ {
519 let class_revision = self.root.config_read_word(current, 8);
520 let device_id = (device_vendor >> 16) as u16;
521 let vendor_id = device_vendor as u16;
522 let class = (class_revision >> 24) as u8;
523 let subclass = (class_revision >> 16) as u8;
524 let prog_if = (class_revision >> 8) as u8;
525 let revision = class_revision as u8;
526 let bist_type_latency_cache = self.root.config_read_word(current, 12);
527 let header_type = HeaderType::from((bist_type_latency_cache >> 16) as u8 & 0x7f);
528 return Some((
529 current,
530 DeviceFunctionInfo {
531 vendor_id,
532 device_id,
533 class,
534 subclass,
535 prog_if,
536 revision,
537 header_type,
538 },
539 ));
540 }
541 }
542 None
543 }
544}
545
546#[derive(Copy, Clone, Debug, Eq, PartialEq)]
548pub struct DeviceFunction {
549 pub bus: u8,
551 pub device: u8,
553 pub function: u8,
555}
556
557impl DeviceFunction {
558 pub fn valid(&self) -> bool {
561 self.device < 32 && self.function < 8
562 }
563}
564
565impl Display for DeviceFunction {
566 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
567 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
568 }
569}
570
571#[derive(Clone, Debug, Eq, PartialEq)]
573pub struct DeviceFunctionInfo {
574 pub vendor_id: u16,
576 pub device_id: u16,
578 pub class: u8,
580 pub subclass: u8,
582 pub prog_if: u8,
584 pub revision: u8,
586 pub header_type: HeaderType,
588}
589
590impl Display for DeviceFunctionInfo {
591 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
592 write!(
593 f,
594 "{:04x}:{:04x} (class {:02x}.{:02x}, rev {:02x}) {:?}",
595 self.vendor_id,
596 self.device_id,
597 self.class,
598 self.subclass,
599 self.revision,
600 self.header_type,
601 )
602 }
603}
604
605#[derive(Copy, Clone, Debug, Eq, PartialEq)]
607pub enum HeaderType {
608 Standard,
610 PciPciBridge,
612 PciCardbusBridge,
614 Unrecognised(u8),
616}
617
618impl From<u8> for HeaderType {
619 fn from(value: u8) -> Self {
620 match value {
621 0x00 => Self::Standard,
622 0x01 => Self::PciPciBridge,
623 0x02 => Self::PciCardbusBridge,
624 _ => Self::Unrecognised(value),
625 }
626 }
627}