pci_for_framework/
lib.rs

1/* Copyright (c) 2015 The Robigalia Project Developers
2 * Licensed under the Apache License, Version 2.0
3 * <LICENSE-APACHE or
4 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 * license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6 * at your option. All files in the project carrying such
7 * notice may not be copied, modified, or distributed except
8 * according to those terms.
9 */
10#![no_std]
11
12//! PCI bus management
13//!
14//! This crate defines various traits, functions, and types for working with the PCI local bus.
15//!
16//!
17//! It is assumed that PCI(e) is already configured - that is, that each device has been allocated
18//! the memory it requests and the BARs are already configured correctly. The firmware (BIOS, UEFI)
19//! usually does this on PC platforms.
20//!
21//! This crate is not yet suitable for multicore use - nothing is synchronized.
22//!
23//! This crate does not yet contain any hardware-specific workarounds for buggy or broken hardware.
24//!
25//! This crate cannot yet exploit PCIe memory-mapped configuration spaces.
26//!
27//! This crate only supports x86, currently.
28
29extern crate alloc;
30
31use alloc::vec::Vec;
32use bitflags::bitflags;
33
34/// A trait defining port I/O operations.
35///
36/// All port I/O operations are parametric over this trait. This allows operating systems to use
37/// this crate without modifications, by suitably instantiating this trait with their own
38/// primitives.
39pub trait PortOps {
40    unsafe fn read8(&self, port: u16) -> u8;
41    unsafe fn read16(&self, port: u16) -> u16;
42    unsafe fn read32(&self, port: u16) -> u32;
43
44    unsafe fn write8(&self, port: u16, val: u8);
45    unsafe fn write16(&self, port: u16, val: u16);
46    unsafe fn write32(&self, port: u16, val: u32);
47}
48
49const CONFIG_ADDRESS: u16 = 0x0CF8;
50const CONFIG_DATA: u16 = 0x0CFC;
51
52#[derive(Debug, Copy, Clone, PartialEq, Eq)]
53pub enum CSpaceAccessMethod {
54    // The legacy, deprecated (as of PCI 2.0) IO-range method.
55    // Until/unless there is a relevant platform that requires this, leave it out.
56    // IO_Mechanism_2
57    /// The legacy (pre-PCIe) 2-IO port method as specified on page 50 of PCI Local Bus
58    /// Specification 3.0.
59    IO,
60    // PCIe memory-mapped configuration space access
61    //MemoryMapped(*mut u8),
62}
63
64// All IO-bus ops are 32-bit, we mask and shift to get the values we want.
65
66impl CSpaceAccessMethod {
67    pub unsafe fn read8<T: PortOps>(self, ops: &T, loc: Location, offset: u16) -> u8 {
68        let val = self.read32(ops, loc, offset & 0b11111100);
69        ((val >> ((offset as usize & 0b11) << 3)) & 0xFF) as u8
70    }
71
72    /// Returns a value in native endian.
73    pub unsafe fn read16<T: PortOps>(self, ops: &T, loc: Location, offset: u16) -> u16 {
74        let val = self.read32(ops, loc, offset & 0b11111100);
75        ((val >> ((offset as usize & 0b10) << 3)) & 0xFFFF) as u16
76    }
77
78    /// Returns a value in native endian.
79    pub unsafe fn read32<T: PortOps>(self, ops: &T, loc: Location, offset: u16) -> u32 {
80        debug_assert!(
81            (offset & 0b11) == 0,
82            "misaligned PCI configuration dword u32 read"
83        );
84        match self {
85            CSpaceAccessMethod::IO => {
86                ops.write32(
87                    CONFIG_ADDRESS,
88                    loc.encode() | ((offset as u32) & 0b11111100),
89                );
90                ops.read32(CONFIG_DATA).to_le()
91            } //MemoryMapped(ptr) => {
92              //    // FIXME: Clarify whether the rules for GEP/GEPi forbid using regular .offset() here.
93              //    ::core::intrinsics::volatile_load(::core::intrinsics::arith_offset(ptr, offset as usize))
94              //}
95        }
96    }
97
98    pub unsafe fn write8<T: PortOps>(self, ops: &T, loc: Location, offset: u16, val: u8) {
99        let old = self.read32(ops, loc, offset);
100        let dest = offset as usize & 0b11 << 3;
101        let mask = (0xFF << dest) as u32;
102        self.write32(
103            ops,
104            loc,
105            offset,
106            ((val as u32) << dest | (old & !mask)).to_le(),
107        );
108    }
109
110    /// Converts val to little endian before writing.
111    pub unsafe fn write16<T: PortOps>(self, ops: &T, loc: Location, offset: u16, val: u16) {
112        let old = self.read32(ops, loc, offset);
113        let dest = offset as usize & 0b10 << 3;
114        let mask = (0xFFFF << dest) as u32;
115        self.write32(
116            ops,
117            loc,
118            offset,
119            ((val as u32) << dest | (old & !mask)).to_le(),
120        );
121    }
122
123    /// Takes a value in native endian, converts it to little-endian, and writes it to the PCI
124    /// device configuration space at register `offset`.
125    pub unsafe fn write32<T: PortOps>(self, ops: &T, loc: Location, offset: u16, val: u32) {
126        debug_assert!(
127            (offset & 0b11) == 0,
128            "misaligned PCI configuration dword u32 read"
129        );
130        match self {
131            CSpaceAccessMethod::IO => {
132                ops.write32(CONFIG_ADDRESS, loc.encode() | (offset as u32 & 0b11111100));
133                ops.write32(CONFIG_DATA, val.to_le())
134            } //MemoryMapped(ptr) => {
135              //    // FIXME: Clarify whether the rules for GEP/GEPi forbid using regular .offset() here.
136              //    ::core::intrinsics::volatile_load(::core::intrinsics::arith_offset(ptr, offset as usize))
137              //}
138        }
139    }
140}
141
142/// Physical location of a device on the bus
143#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
144pub struct Location {
145    pub bus: u8,
146    pub device: u8,
147    pub function: u8,
148}
149
150impl Location {
151    #[inline(always)]
152    fn encode(self) -> u32 {
153        (1 << 31)
154            | ((self.bus as u32) << 16)
155            | (((self.device as u32) & 0b11111) << 11)
156            | (((self.function as u32) & 0b111) << 8)
157    }
158}
159
160#[derive(Debug, Copy, Clone, PartialEq, Eq)]
161pub struct Identifier {
162    pub vendor_id: u16,
163    pub device_id: u16,
164    pub revision_id: u8,
165    pub prog_if: u8,
166    pub class: u8,
167    pub subclass: u8,
168}
169
170bitflags! {
171    pub struct Command: u16 {
172        const IO_SPACE                  = 0x0001;
173        const MEMORY_SPACE              = 0x0002;
174        const BUS_MASTER                = 0x0004;
175        const SPECIAL_CYCLES            = 0x0008;
176        const MWI_ENABLE                = 0x0010;
177        const VGA_PALETTE_SNOOP         = 0x0020;
178        const PARITY_ERROR_RESPONSE     = 0x0040;
179        const STEPPING_CONTROL          = 0x0080;
180        const SERR_ENABLE               = 0x0100;
181        const FAST_BACK_TO_BACK_ENABLE  = 0x0200;
182        const INTERRUPT_DISABLE         = 0x0400;
183        const RESERVED_11               = 0x0800;
184        const RESERVED_12               = 0x1000;
185        const RESERVED_13               = 0x2000;
186        const RESERVED_14               = 0x4000;
187        const RESERVED_15               = 0x8000;
188    }
189}
190
191bitflags! {
192    pub struct Status: u16 {
193        const RESERVED_0                = 0x0001;
194        const RESERVED_1                = 0x0002;
195        const RESERVED_2                = 0x0004;
196        const INTERRUPT_STATUS          = 0x0008;
197        const CAPABILITIES_LIST         = 0x0010;
198        const MHZ66_CAPABLE             = 0x0020;
199        const RESERVED_6                = 0x0040;
200        const FAST_BACK_TO_BACK_CAPABLE = 0x0080;
201        const MASTER_DATA_PARITY_ERROR  = 0x0100;
202        const DEVSEL_MEDIUM_TIMING      = 0x0200;
203        const DEVSEL_SLOW_TIMING        = 0x0400;
204        const SIGNALED_TARGET_ABORT     = 0x0800;
205        const RECEIVED_TARGET_ABORT     = 0x1000;
206        const RECEIVED_MASTER_ABORT     = 0x2000;
207        const SIGNALED_SYSTEM_ERROR     = 0x4000;
208        const DETECTED_PARITY_ERROR     = 0x8000;
209    }
210}
211
212bitflags! {
213    pub struct BridgeControl: u16 {
214        const PARITY_ERROR_RESPONSE_ENABLE = 0x0001;
215        const SERR_ENABLE               = 0x0002;
216        const ISA_ENABLE                = 0x0004;
217        const VGA_ENABLE                = 0x0008;
218        const RESERVED_4                = 0x0010;
219        const MASTER_ABORT_MODE         = 0x0020;
220        const SECONDARY_BUS_RESET       = 0x0040;
221        const FAST_BACK_TO_BACK_ENABLE  = 0x0080;
222        const PRIMARY_DISCARD_TIMER     = 0x0100;
223        const SECONDARY_DISCARD_TIMER   = 0x0200;
224        const DISCARD_TIMER_STATUS      = 0x0400;
225        const DISCARD_TIMER_SERR_ENABLED = 0x0800;
226        const RESERVED_12               = 0x1000;
227        const RESERVED_13               = 0x2000;
228        const RESERVED_14               = 0x4000;
229        const RESERVED_15               = 0x8000;
230    }
231}
232
233/// A device on the PCI bus.
234///
235/// Although accessing configuration space may be expensive, it is not cached.
236#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct PCIDevice {
238    pub loc: Location,
239    pub id: Identifier,
240    pub command: Command,
241    pub status: Status,
242    pub cache_line_size: u8,
243    pub latency_timer: u8,
244    pub multifunction: bool,
245    pub bist_capable: bool,
246    pub bars: [Option<BAR>; 6],
247    pub kind: DeviceKind,
248    pub pic_interrupt_line: u8,
249    pub interrupt_pin: Option<InterruptPin>,
250    pub cspace_access_method: CSpaceAccessMethod,
251    pub capabilities: Option<Vec<Capability>>,
252}
253
254pub enum PCIScanError {}
255
256#[derive(Debug, Copy, Clone, PartialEq, Eq)]
257pub enum Prefetchable {
258    Yes,
259    No,
260}
261
262#[derive(Debug, Copy, Clone, PartialEq, Eq)]
263pub enum Type {
264    Bits32,
265    Bits64,
266}
267
268#[derive(Debug, Copy, Clone, PartialEq, Eq)]
269pub enum DeviceKind {
270    Device(DeviceDetails),
271    PciBridge(PciBridgeDetails),
272    CardbusBridge(CardbusBridgeDetails),
273    Unknown,
274}
275
276#[derive(Debug, Copy, Clone, PartialEq, Eq)]
277pub struct DeviceDetails {
278    pub cardbus_cis_ptr: u32,
279    pub subsystem_vendor_id: u16,
280    pub subsystem_id: u16,
281    pub expansion_rom_base_addr: u32,
282    pub min_grant: u8,
283    pub max_latency: u8,
284}
285
286#[derive(Debug, Copy, Clone, PartialEq, Eq)]
287pub struct PciBridgeDetails {
288    pub primary_bus: u8,
289    pub secondary_bus: u8,
290    pub subordinate_bus: u8,
291    pub secondary_latency_timer: u8,
292    pub io_base: u32,
293    pub io_limit: u32,
294    pub secondary_status: Status,
295    pub mem_base: u32,
296    pub mem_limit: u32,
297    pub prefetchable_mem_base: u64,
298    pub prefetchable_mem_limit: u64,
299    pub expansion_rom_base_addr: u32,
300    pub bridge_control: BridgeControl,
301}
302
303#[derive(Debug, Copy, Clone, PartialEq, Eq)]
304pub struct CardbusBridgeDetails {
305    pub socket_base_addr: u32,
306    pub secondary_status: Status,
307    pub pci_bus: u8,
308    pub cardbus_bus: u8,
309    pub subordinate_bus: u8,
310    pub cardbus_latency_timer: u8,
311    pub mem_base_0: u32,
312    pub mem_limit_0: u32,
313    pub mem_base_1: u32,
314    pub mem_limit_1: u32,
315    pub io_base_0: u32,
316    pub io_limit_0: u32,
317    pub io_base_1: u32,
318    pub io_limit_1: u32,
319    pub subsystem_device_id: u16,
320    pub subsystem_vendor_id: u16,
321    pub legacy_mode_base_addr: u32,
322    pub bridge_control: BridgeControl,
323}
324
325#[derive(Debug, Copy, Clone, PartialEq, Eq)]
326pub enum InterruptPin {
327    INTA = 1,
328    INTB,
329    INTC,
330    INTD,
331}
332
333bitflags! {
334    pub struct CapabilityMSIMessageControl: u16 {
335        const ADDR64_CAPABLE = 1 << 7;
336        const MULTIPLE_MESSAGE_ENABLE_2 = 1 << 4;
337        const MULTIPLE_MESSAGE_ENABLE_4 = 2 << 4;
338        const MULTIPLE_MESSAGE_ENABLE_8 = 3 << 4;
339        const MULTIPLE_MESSAGE_ENABLE_16 = 4 << 4;
340        const MULTIPLE_MESSAGE_ENABLE_32 = 5 << 4;
341        const MULTIPLE_MESSAGE_CAPABLE_2 = 1 << 1;
342        const MULTIPLE_MESSAGE_CAPABLE_4 = 2 << 1;
343        const MULTIPLE_MESSAGE_CAPABLE_8 = 3 << 1;
344        const MULTIPLE_MESSAGE_CAPABLE_16 = 4 << 1;
345        const MULTIPLE_MESSAGE_CAPABLE_32 = 5 << 1;
346        const ENABLE = 1 << 0;
347    }
348}
349
350#[derive(Debug, Copy, Clone, PartialEq, Eq)]
351pub struct CapabilityMSIData {
352    message_control: CapabilityMSIMessageControl,
353    message_address: u64,
354    message_data: u16,
355}
356
357#[derive(Debug, Copy, Clone, PartialEq, Eq)]
358pub struct CapabilitySATAData {
359    major_revision: u32,
360    minor_revision: u32,
361    bar_offset: u32,
362    bar_location: u32,
363}
364
365#[derive(Debug, Copy, Clone, PartialEq, Eq)]
366pub struct CapabilityPMData {
367    pme_support: u32,
368    d2_support: u32,
369    d1_support: u32,
370    aux_current: u32,
371    dsi: u32,
372    pme_clock: u32,
373    version: u32,
374}
375
376#[derive(Debug, Copy, Clone, PartialEq, Eq)]
377pub struct CapabilityEXPData {
378    interrupt_message_number: u16,
379    slot_implemented: u16,
380    device_port_type: u16,
381    cap_version: u16,
382}
383
384#[derive(Debug, Copy, Clone, PartialEq, Eq)]
385pub enum CapabilityData {
386    PM(CapabilityPMData),     // Power Management
387    AGP,                      // Accelerated Graphics Part
388    VPD,                      // Vital Product Data
389    SLOTID,                   // Slot Identification
390    MSI(CapabilityMSIData),   // Message Signalled Interrupts
391    CHSWP,                    // CompactPCI HotSwap
392    PCIX,                     // PCI-X
393    HP,                       // HyperTransport
394    VNDR,                     // Vendor-Specific
395    DBG,                      // Debug port
396    CCRC,                     // CompactPCI Central Resource Control
397    SHPC,                     // PCI Standard Hot-Plug Controller
398    SSVID,                    // Bridge subsystem vendor/device ID
399    AGP3,                     // AGP Target PCI-PCI bridge
400    SECDEV,                   // Secure Device
401    EXP(CapabilityEXPData),   // PCI Express
402    MSIX,                     // MSI-X
403    SATA(CapabilitySATAData), // SATA Data/Index Conf.
404    AF,                       // PCI Advanced Features
405    Unknown(u8),
406}
407
408#[derive(Debug, Copy, Clone, PartialEq, Eq)]
409pub struct Capability {
410    cap_ptr: u16,
411    data: CapabilityData,
412}
413
414#[derive(Debug, Copy, Clone, PartialEq, Eq)]
415pub enum BAR {
416    Memory(u64, u32, Prefetchable, Type),
417    IO(u32, u32),
418}
419
420impl BAR {
421    pub unsafe fn decode<T: PortOps>(
422        ops: &T,
423        loc: Location,
424        am: CSpaceAccessMethod,
425        idx: u16,
426    ) -> (Option<BAR>, usize) {
427        let raw = am.read32(ops, loc, 16 + (idx << 2));
428        am.write32(ops, loc, 16 + (idx << 2), !0);
429        let len_encoded = am.read32(ops, loc, 16 + (idx << 2));
430        am.write32(ops, loc, 16 + (idx << 2), raw);
431        if raw == 0 && len_encoded == 0 {
432            return (None, idx as usize + 1);
433        }
434        if raw & 1 == 0 {
435            let mut bits64 = false;
436            let base: u64 = match (raw & 0b110) >> 1 {
437                0 => (raw & !0xF) as u64,
438                2 => {
439                    bits64 = true;
440                    ((raw & !0xF) as u64)
441                        | ((am.read32(ops, loc, 16 + ((idx + 1) << 2)) as u64) << 32)
442                }
443                _ => {
444                    debug_assert!(false, "bad type in memory BAR");
445                    return (None, idx as usize + 1);
446                }
447            };
448            let len = !(len_encoded & !0xF).wrapping_add(1);
449            (
450                Some(BAR::Memory(
451                    base,
452                    len,
453                    if raw & 0b1000 == 0 {
454                        Prefetchable::No
455                    } else {
456                        Prefetchable::Yes
457                    },
458                    if bits64 { Type::Bits64 } else { Type::Bits32 },
459                )),
460                if bits64 { idx + 2 } else { idx + 1 } as usize,
461            )
462        } else {
463            let len = !(len_encoded & !0x3) + 1;
464            (Some(BAR::IO(raw & !0x3, len)), idx as usize + 1)
465        }
466    }
467}
468
469pub struct BusScan<'a, T: PortOps + 'a> {
470    loc: Location,
471    ops: &'a T,
472    am: CSpaceAccessMethod,
473}
474
475impl<'a, T: PortOps> BusScan<'a, T> {
476    fn done(&self) -> bool {
477        if self.loc.bus == 255 && self.loc.device == 31 && self.loc.function == 7 {
478            true
479        } else {
480            false
481        }
482    }
483
484    fn increment(&mut self) {
485        // TODO: Decide whether this is actually nicer than taking a u16 and incrementing until it
486        // wraps.
487        if self.loc.function < 7 {
488            self.loc.function += 1;
489            return;
490        } else {
491            self.loc.function = 0;
492            if self.loc.device < 31 {
493                self.loc.device += 1;
494                return;
495            } else {
496                self.loc.device = 0;
497                if self.loc.bus == 255 {
498                    self.loc.device = 31;
499                    self.loc.device = 7;
500                } else {
501                    self.loc.bus += 1;
502                    return;
503                }
504            }
505        }
506    }
507}
508
509impl<'a, T: PortOps> ::core::iter::Iterator for BusScan<'a, T> {
510    type Item = PCIDevice;
511    #[inline]
512    fn next(&mut self) -> Option<PCIDevice> {
513        // FIXME: very naive atm, could be smarter and waste much less time by only scanning used
514        // busses.
515        let mut ret = None;
516        loop {
517            if self.done() {
518                return ret;
519            }
520            unsafe {
521                ret = probe_function(self.ops, self.loc, self.am);
522            }
523            self.increment();
524            if ret.is_some() {
525                return ret;
526            }
527        }
528    }
529}
530
531pub unsafe fn probe_function<T: PortOps>(
532    ops: &T,
533    loc: Location,
534    am: CSpaceAccessMethod,
535) -> Option<PCIDevice> {
536    // FIXME: it'd be more efficient to use read32 and decode separately.
537    let vid = am.read16(ops, loc, 0);
538    if vid == 0xFFFF {
539        return None;
540    }
541    let did = am.read16(ops, loc, 2);
542    let command = Command::from_bits_truncate(am.read16(ops, loc, 4));
543    let status = Status::from_bits_truncate(am.read16(ops, loc, 6));
544    let rid = am.read8(ops, loc, 8);
545    let prog_if = am.read8(ops, loc, 9);
546    let subclass = am.read8(ops, loc, 10);
547    let class = am.read8(ops, loc, 11);
548    let id = Identifier {
549        vendor_id: vid,
550        device_id: did,
551        revision_id: rid,
552        prog_if: prog_if,
553        class: class,
554        subclass: subclass,
555    };
556    let cache_line_size = am.read8(ops, loc, 12);
557    let latency_timer = am.read8(ops, loc, 13);
558    let bist_capable = am.read8(ops, loc, 15) & (1 << 7) != 0;
559    let hdrty_mf = am.read8(ops, loc, 14);
560    let hdrty = hdrty_mf & !(1 << 7);
561    let mf = hdrty_mf & (1 << 7) != 0;
562    let pic_interrupt_line = am.read8(ops, loc, 0x3C);
563    let interrupt_pin = match am.read8(ops, loc, 0x3D) {
564        1 => Some(InterruptPin::INTA),
565        2 => Some(InterruptPin::INTB),
566        3 => Some(InterruptPin::INTC),
567        4 => Some(InterruptPin::INTD),
568        _ => None,
569    };
570    let kind;
571    let max;
572
573    match hdrty {
574        0 => {
575            max = 6;
576            kind = DeviceKind::Device(DeviceDetails {
577                cardbus_cis_ptr: am.read32(ops, loc, 0x28),
578                subsystem_vendor_id: am.read16(ops, loc, 0x2C),
579                subsystem_id: am.read16(ops, loc, 0x2E),
580                expansion_rom_base_addr: am.read32(ops, loc, 0x30),
581                min_grant: am.read8(ops, loc, 0x3E),
582                max_latency: am.read8(ops, loc, 0x3F),
583            });
584        }
585        1 => {
586            max = 2;
587            kind = DeviceKind::PciBridge(PciBridgeDetails {
588                primary_bus: am.read8(ops, loc, 0x18),
589                secondary_bus: am.read8(ops, loc, 0x19),
590                subordinate_bus: am.read8(ops, loc, 0x1a),
591                secondary_latency_timer: am.read8(ops, loc, 0x1b),
592                secondary_status: Status::from_bits_truncate(am.read16(ops, loc, 0x1e)),
593                io_base: (am.read8(ops, loc, 0x1c) as u32 & 0xF0) << 8
594                    | (am.read16(ops, loc, 0x30) as u32) << 16,
595                io_limit: 0xFFF
596                    | (am.read8(ops, loc, 0x1d) as u32 & 0xF0) << 8
597                    | (am.read16(ops, loc, 0x32) as u32) << 16,
598                mem_base: (am.read16(ops, loc, 0x20) as u32 & 0xFFF0) << 16,
599                mem_limit: 0xFFFFF | (am.read16(ops, loc, 0x22) as u32 & 0xFFF0) << 16,
600                prefetchable_mem_base: (am.read16(ops, loc, 0x24) as u64 & 0xFFF0) << 16
601                    | am.read32(ops, loc, 0x28) as u64,
602                prefetchable_mem_limit: 0xFFFFF
603                    | (am.read16(ops, loc, 0x26) as u64 & 0xFFF0) << 16
604                    | am.read32(ops, loc, 0x2c) as u64,
605                expansion_rom_base_addr: am.read32(ops, loc, 0x38),
606                bridge_control: BridgeControl::from_bits_truncate(am.read16(ops, loc, 0x3e)),
607            });
608        }
609        2 => {
610            max = 0;
611            kind = DeviceKind::CardbusBridge(CardbusBridgeDetails {
612                socket_base_addr: am.read32(ops, loc, 0x10),
613                secondary_status: Status::from_bits_truncate(am.read16(ops, loc, 0x16)),
614                pci_bus: am.read8(ops, loc, 0x18),
615                cardbus_bus: am.read8(ops, loc, 0x19),
616                subordinate_bus: am.read8(ops, loc, 0x1a),
617                cardbus_latency_timer: am.read8(ops, loc, 0x1b),
618                mem_base_0: am.read32(ops, loc, 0x1c),
619                mem_limit_0: am.read32(ops, loc, 0x20),
620                mem_base_1: am.read32(ops, loc, 0x24),
621                mem_limit_1: am.read32(ops, loc, 0x28),
622                io_base_0: am.read32(ops, loc, 0x2c),
623                io_limit_0: am.read32(ops, loc, 0x30),
624                io_base_1: am.read32(ops, loc, 0x34),
625                io_limit_1: am.read32(ops, loc, 0x38),
626                bridge_control: BridgeControl::from_bits_truncate(am.read16(ops, loc, 0x3e)),
627                subsystem_device_id: am.read16(ops, loc, 0x40),
628                subsystem_vendor_id: am.read16(ops, loc, 0x42),
629                legacy_mode_base_addr: am.read32(ops, loc, 0x44),
630            });
631        }
632        _ => {
633            max = 0;
634            kind = DeviceKind::Unknown;
635            debug_assert!(
636                false,
637                "pci: unknown device header type {} for {:?} {:?}",
638                hdrty, loc, id
639            );
640        }
641    };
642
643    let mut capabilities = None;
644    if status.contains(Status::CAPABILITIES_LIST) {
645        let mut caps = Vec::new();
646        // traverse capabilities list
647        let mut cap_pointer = am.read8(ops, loc, 0x34) as u16;
648        while cap_pointer > 0 {
649            let cap_id = am.read8(ops, loc, cap_pointer);
650            let data = match cap_id {
651                0x01 => {
652                    let cap = am.read32(ops, loc, cap_pointer + 0x4);
653                    CapabilityData::PM(CapabilityPMData {
654                        pme_support: cap >> 27,
655                        d2_support: (cap >> 26) & 0x1,
656                        d1_support: (cap >> 25) & 0x1,
657                        aux_current: (cap >> 22) & 0x7,
658                        dsi: (cap >> 21) & 0x1,
659                        pme_clock: (cap >> 19) & 0x1,
660                        version: (cap >> 16) & 0x7,
661                    })
662                }
663                0x02 => CapabilityData::AGP,
664                0x03 => CapabilityData::VPD,
665                0x04 => CapabilityData::SLOTID,
666                0x05 => {
667                    let message_control = CapabilityMSIMessageControl::from_bits_truncate(
668                        am.read16(ops, loc, cap_pointer + 0x02),
669                    );
670                    let (addr, data) =
671                        if message_control.contains(CapabilityMSIMessageControl::ADDR64_CAPABLE) {
672                            // 64bit
673                            let lo = am.read32(ops, loc, cap_pointer + 0x04) as u64;
674                            let hi = am.read32(ops, loc, cap_pointer + 0x08) as u64;
675                            let data = am.read16(ops, loc, cap_pointer + 0x0C);
676                            ((hi << 32) | lo, data)
677                        } else {
678                            // 32bit
679                            let addr = am.read32(ops, loc, cap_pointer + 0x04) as u64;
680                            let data = am.read16(ops, loc, cap_pointer + 0x0C);
681                            (addr, data)
682                        };
683                    CapabilityData::MSI(CapabilityMSIData {
684                        message_control: message_control,
685                        message_address: addr,
686                        message_data: data,
687                    })
688                }
689                0x10 => {
690                    let cap = am.read16(ops, loc, cap_pointer + 0x2);
691                    CapabilityData::EXP(CapabilityEXPData {
692                        interrupt_message_number: (cap >> 9) & 0b11111,
693                        slot_implemented: (cap >> 8) & 0x1,
694                        device_port_type: (cap >> 4) & 0xf,
695                        cap_version: cap & 0xf,
696                    })
697                }
698                0x11 => CapabilityData::MSIX,
699                0x12 => {
700                    let sata_cr0 = am.read32(ops, loc, cap_pointer);
701                    let sata_cr1 = am.read32(ops, loc, cap_pointer + 0x4);
702                    CapabilityData::SATA(CapabilitySATAData {
703                        major_revision: (sata_cr0 >> 20) & 0xf,
704                        minor_revision: (sata_cr0 >> 16) & 0xf,
705                        bar_offset: (sata_cr1 >> 4) & 0xfffff,
706                        bar_location: sata_cr1 & 0xf,
707                    })
708                }
709                _ => CapabilityData::Unknown(cap_id),
710            };
711            caps.push(Capability {
712                cap_ptr: cap_pointer,
713                data: data,
714            });
715            cap_pointer = am.read8(ops, loc, cap_pointer + 1) as u16;
716        }
717        capabilities = Some(caps);
718    }
719
720    let mut bars = [None, None, None, None, None, None];
721    let mut i = 0;
722    while i < max {
723        let (bar, next) = BAR::decode(ops, loc, am, i as u16);
724        bars[i] = bar;
725        i = next;
726    }
727
728    Some(PCIDevice {
729        loc: loc,
730        id: id,
731        command: command,
732        status: status,
733        cache_line_size: cache_line_size,
734        latency_timer: latency_timer,
735        multifunction: mf,
736        bist_capable: bist_capable,
737        bars: bars,
738        kind: kind,
739        pic_interrupt_line: pic_interrupt_line,
740        interrupt_pin: interrupt_pin,
741        cspace_access_method: am,
742        capabilities: capabilities,
743    })
744}
745
746pub unsafe fn scan_bus<'a, T: PortOps>(ops: &'a T, am: CSpaceAccessMethod) -> BusScan<'a, T> {
747    BusScan {
748        loc: Location {
749            bus: 0,
750            device: 0,
751            function: 0,
752        },
753        ops: ops,
754        am: am,
755    }
756}