From 4d349192dacf3d103eef5cd38e06407f1dc1722d Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Tue, 20 Feb 2018 02:56:30 +0000 Subject: [PATCH] Allow PCI Config space parsing to handle types - Update the PCI config space parsing to be able to handle multiple types. - Use a trait to abstract out reading from the config space in order to allow testing/fuzzing of the parser. --- Cargo.lock | 2 + pcid/Cargo.toml | 2 + pcid/src/main.rs | 270 ++++++++++++++-------------- pcid/src/pci/bar.rs | 23 ++- pcid/src/pci/class.rs | 29 ++- pcid/src/pci/func.rs | 41 ++--- pcid/src/pci/header.rs | 388 +++++++++++++++++++++++++++++++++++++---- pcid/src/pci/mod.rs | 20 +-- 8 files changed, 574 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fffa149671..4818f23456 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,6 +342,8 @@ dependencies = [ name = "pcid" version = "0.1.0" dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/pcid/Cargo.toml b/pcid/Cargo.toml index bc28166532..56465ba9fd 100644 --- a/pcid/Cargo.toml +++ b/pcid/Cargo.toml @@ -3,6 +3,8 @@ name = "pcid" version = "0.1.0" [dependencies] +bitflags = "1.0" +byteorder = "1.2" redox_syscall = "0.1" serde = "1.0" serde_derive = "1.0" diff --git a/pcid/src/main.rs b/pcid/src/main.rs index ea8f3495a7..e8f8eea829 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -1,6 +1,9 @@ #![deny(warnings)] #![feature(asm)] +#![feature(iterator_step_by)] +#[macro_use] extern crate bitflags; +extern crate byteorder; #[macro_use] extern crate serde_derive; extern crate syscall; extern crate toml; @@ -12,11 +15,136 @@ use std::process::Command; use syscall::iopl; use config::Config; -use pci::{Pci, PciBar, PciClass}; +use pci::{Pci, PciClass, PciHeader, PciHeaderError, PciHeaderType}; mod config; mod pci; +fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, + dev_num: u8, func_num: u8, header: PciHeader) { + let raw_class: u8 = header.class().into(); + let mut string = format!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}", + bus_num, dev_num, func_num, header.vendor_id(), header.device_id(), raw_class, + header.subclass(), header.interface(), header.revision(), header.class()); + + match header.class() { + PciClass::Storage => match header.subclass() { + 0x01 => { + string.push_str(" IDE"); + }, + 0x06 => { + string.push_str(" SATA"); + }, + _ => () + }, + PciClass::SerialBus => match header.subclass() { + 0x03 => match header.interface() { + 0x00 => { + string.push_str(" UHCI"); + }, + 0x10 => { + string.push_str(" OHCI"); + }, + 0x20 => { + string.push_str(" EHCI"); + }, + 0x30 => { + string.push_str(" XHCI"); + }, + _ => () + }, + _ => () + }, + _ => () + } + + for (i, bar) in header.bars().iter().enumerate() { + if !bar.is_none() { + string.push_str(&format!(" {}={}", i, bar)); + } + } + + string.push('\n'); + + print!("{}", string); + + for driver in config.drivers.iter() { + if let Some(class) = driver.class { + if class != raw_class { continue; } + } + + if let Some(subclass) = driver.subclass { + if subclass != header.subclass() { continue; } + } + + if let Some(interface) = driver.interface { + if interface != header.interface() { continue; } + } + + if let Some(vendor) = driver.vendor { + if vendor != header.vendor_id() { continue; } + } + + if let Some(device) = driver.device { + if device != header.device_id() { continue; } + } + + if let Some(ref device_id_range) = driver.device_id_range { + if header.device_id() < device_id_range.start || + device_id_range.end <= header.device_id() { continue; } + } + + if let Some(ref args) = driver.command { + // Enable bus mastering, memory space, and I/O space + unsafe { + let cmd = pci.read(bus_num, dev_num, func_num, 0x04); + println!("PCI CMD: {:>02X}", cmd); + pci.write(bus_num, dev_num, func_num, 0x04, cmd | 7); + } + + // TODO: find a better way to pass the header data down to the + // device driver, making passing the capabilities list etc + // posible. + let mut args = args.iter(); + if let Some(program) = args.next() { + let mut command = Command::new(program); + for arg in args { + let arg = match arg.as_str() { + "$BUS" => format!("{:>02X}", bus_num), + "$DEV" => format!("{:>02X}", dev_num), + "$FUNC" => format!("{:>02X}", func_num), + "$NAME" => format!("pci-{:>02X}.{:>02X}.{:>02X}", bus_num, dev_num, func_num), + "$BAR0" => format!("{}", header.get_bar(0)), + "$BAR1" => format!("{}", header.get_bar(1)), + "$BAR2" if header.header_type() == PciHeaderType::GENERAL => + format!("{}", header.get_bar(2)), + "$BAR3" if header.header_type() == PciHeaderType::GENERAL => + format!("{}", header.get_bar(2)), + "$BAR4" if header.header_type() == PciHeaderType::GENERAL => + format!("{}", header.get_bar(2)), + "$BAR5" if header.header_type() == PciHeaderType::GENERAL => + format!("{}", header.get_bar(2)), + "$IRQ" => format!("{}", header.interrupt_line()), + "$VENID" => format!("{:>04X}", header.vendor_id()), + "$DEVID" => format!("{:>04X}", header.device_id()), + _ => arg.clone() + }; + command.arg(&arg); + } + + println!("PCID SPAWN {:?}", command); + match command.spawn() { + Ok(mut child) => match child.wait() { + Ok(_status) => (), //println!("pcid: waited for {}: {:?}", line, status.code()), + Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err) + }, + Err(err) => println!("pcid: failed to execute {:?}: {}", command, err) + } + } + } + } +} + fn main() { let mut config = Config::default(); @@ -38,140 +166,14 @@ fn main() { for bus in pci.buses() { for dev in bus.devs() { for func in dev.funcs() { - if let Some(header) = func.header() { - let pci_class = PciClass::from(header.class); - - let mut string = unsafe { - format!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}", - bus.num, dev.num, func.num, - header.vendor_id, header.device_id, - header.class, header.subclass, header.interface, header.revision, - pci_class) - }; - - match pci_class { - PciClass::Storage => match header.subclass { - 0x01 => { - string.push_str(" IDE"); - }, - 0x06 => { - string.push_str(" SATA"); - }, - _ => () - }, - PciClass::SerialBus => match header.subclass { - 0x03 => match header.interface { - 0x00 => { - string.push_str(" UHCI"); - }, - 0x10 => { - string.push_str(" OHCI"); - }, - 0x20 => { - string.push_str(" EHCI"); - }, - 0x30 => { - string.push_str(" XHCI"); - }, - _ => () - }, - _ => () - }, - _ => () + let func_num = func.num; + match PciHeader::from_reader(func) { + Ok(header) => { + handle_parsed_header(&config, &pci, bus.num, dev.num, func_num, header); } - - unsafe { - for i in 0..header.bars.len() { - match PciBar::from(header.bars[i]) { - PciBar::None => (), - PciBar::Memory(address) => string.push_str(&format!(" {}={:>08X}", i, address)), - PciBar::Port(address) => string.push_str(&format!(" {}={:>04X}", i, address)) - } - } - } - - string.push('\n'); - - print!("{}", string); - - for driver in config.drivers.iter() { - if let Some(class) = driver.class { - if class != header.class { continue; } - } - - if let Some(subclass) = driver.subclass { - if subclass != header.subclass { continue; } - } - - if let Some(interface) = driver.interface { - if interface != header.interface { continue; } - } - - if let Some(vendor) = driver.vendor { - if vendor != header.vendor_id { continue; } - } - - if let Some(device) = driver.device { - if device != header.device_id { continue; } - } - - if let Some(ref device_id_range) = driver.device_id_range { - if header.device_id < device_id_range.start || - device_id_range.end <= header.device_id { continue; } - } - - if let Some(ref args) = driver.command { - // Enable bus mastering, memory space, and I/O space - unsafe { - let cmd = pci.read(bus.num, dev.num, func.num, 0x04); - println!("PCI CMD: {:>02X}", cmd); - pci.write(bus.num, dev.num, func.num, 0x04, cmd | 7); - } - - let mut args = args.iter(); - if let Some(program) = args.next() { - let mut command = Command::new(program); - for arg in args { - let bar_arg = |i| -> String { - match PciBar::from(header.bars[i]) { - PciBar::None => String::new(), - PciBar::Memory(address) => format!("{:>08X}", address), - PciBar::Port(address) => format!("{:>04X}", address) - } - }; - let arg = unsafe { - match arg.as_str() { - "$BUS" => format!("{:>02X}", bus.num), - "$DEV" => format!("{:>02X}", dev.num), - "$FUNC" => format!("{:>02X}", func.num), - "$NAME" => format!("pci-{:>02X}.{:>02X}.{:>02X}", bus.num, dev.num, func.num), - "$BAR0" => bar_arg(0), - "$BAR1" => bar_arg(1), - "$BAR2" => bar_arg(2), - "$BAR3" => bar_arg(3), - "$BAR4" => bar_arg(4), - "$BAR5" => bar_arg(5), - "$IRQ" => format!("{}", header.interrupt_line), - "$VENID" => format!("{:>04X}",header.vendor_id), - "$DEVID" => format!("{:>04X}",header.device_id), - "$SUBSYSID" => format!("{:>04X}",header.subsystem_id), - _ => arg.clone() - } - }; - command.arg(&arg); - } - - //println!("PCID SPAWN {:?}", command); - - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(_status) => (), //println!("pcid: waited for {}: {:?}", line, status.code()), - Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err) - }, - Err(err) => println!("pcid: failed to execute {:?}: {}", command, err) - } - } - } + Err(PciHeaderError::NoDevice) => {}, + Err(PciHeaderError::UnknownHeaderType(id)) => { + println!("pcid: unknown header type: {}", id); } } } diff --git a/pcid/src/pci/bar.rs b/pcid/src/pci/bar.rs index 190fa05d2d..b1efde2e38 100644 --- a/pcid/src/pci/bar.rs +++ b/pcid/src/pci/bar.rs @@ -1,10 +1,21 @@ -#[derive(Debug)] +use std::fmt; + +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PciBar { None, Memory(u32), Port(u16) } +impl PciBar { + pub fn is_none(&self) -> bool { + match self { + &PciBar::None => true, + _ => false, + } + } +} + impl From for PciBar { fn from(bar: u32) -> Self { if bar & 0xFFFFFFFC == 0 { @@ -16,3 +27,13 @@ impl From for PciBar { } } } + +impl fmt::Display for PciBar { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &PciBar::Memory(address) => write!(f, "{:>08X}", address), + &PciBar::Port(address) => write!(f, "{:>04X}", address), + &PciBar::None => write!(f, "None") + } + } +} diff --git a/pcid/src/pci/class.rs b/pcid/src/pci/class.rs index 21f7f69bbe..98c503c2b3 100644 --- a/pcid/src/pci/class.rs +++ b/pcid/src/pci/class.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PciClass { Legacy, Storage, @@ -48,3 +48,30 @@ impl From for PciClass { } } } + +impl Into for PciClass { + fn into(self) -> u8 { + match self { + PciClass::Legacy => 0x00, + PciClass::Storage => 0x01, + PciClass::Network => 0x02, + PciClass::Display => 0x03, + PciClass::Multimedia => 0x04, + PciClass::Memory => 0x05, + PciClass::Bridge => 0x06, + PciClass::SimpleComms => 0x07, + PciClass::Peripheral => 0x08, + PciClass::Input => 0x09, + PciClass::Docking => 0x0A, + PciClass::Processor => 0x0B, + PciClass::SerialBus => 0x0C, + PciClass::Wireless => 0x0D, + PciClass::IntelligentIo => 0x0E, + PciClass::SatelliteComms => 0x0F, + PciClass::Cryptography => 0x10, + PciClass::SignalProc => 0x11, + PciClass::Unknown => 0xFF, + PciClass::Reserved(reserved) => reserved + } + } +} diff --git a/pcid/src/pci/func.rs b/pcid/src/pci/func.rs index 578e5c61be..5c206f3607 100644 --- a/pcid/src/pci/func.rs +++ b/pcid/src/pci/func.rs @@ -1,30 +1,31 @@ -use std::ops::DerefMut; +use byteorder::{LittleEndian, ByteOrder}; -use super::{PciDev, PciHeader}; +use super::PciDev; + +pub trait ConfigReader { + unsafe fn read_range(&self, offset: u8, len: u8) -> Vec { + assert!(len > 3 && len % 4 == 0); + let mut ret = Vec::with_capacity(len as usize); + let results = (offset..offset + len).step_by(4).fold(Vec::new(), |mut acc, offset| { + let val = self.read_u32(offset); + acc.push(val); + acc + }); + ret.set_len(len as usize); + LittleEndian::write_u32_into(&*results, &mut ret); + ret + } + + unsafe fn read_u32(&self, offset: u8) -> u32; +} pub struct PciFunc<'pci> { pub dev: &'pci PciDev<'pci>, pub num: u8 } -impl<'pci> PciFunc<'pci> { - pub fn header(&self) -> Option { - if unsafe { self.read(0) } != 0xFFFFFFFF { - let mut header = PciHeader::default(); - { - let dwords = header.deref_mut(); - dwords.iter_mut().fold(0usize, |offset, dword| { - *dword = unsafe { self.read(offset as u8) }; - offset + 4 - }); - } - Some(header) - } else { - None - } - } - - pub unsafe fn read(&self, offset: u8) -> u32 { +impl<'pci> ConfigReader for PciFunc<'pci> { + unsafe fn read_u32(&self, offset: u8) -> u32 { self.dev.read(self.num, offset) } } diff --git a/pcid/src/pci/header.rs b/pcid/src/pci/header.rs index 332542a569..272d917b1f 100644 --- a/pcid/src/pci/header.rs +++ b/pcid/src/pci/header.rs @@ -1,43 +1,361 @@ -use std::ops::{Deref, DerefMut}; -use std::{slice, mem}; +use byteorder::{LittleEndian, ByteOrder}; -#[derive(Default)] -#[repr(packed)] -pub struct PciHeader { - pub vendor_id: u16, - pub device_id: u16, - pub command: u16, - pub status: u16, - pub revision: u8, - pub interface: u8, - pub subclass: u8, - pub class: u8, - pub cache_line_size: u8, - pub latency_timer: u8, - pub header_type: u8, - pub bist: u8, - pub bars: [u32; 6], - pub cardbus_cis_ptr: u32, - pub subsystem_vendor_id: u16, - pub subsystem_id: u16, - pub expansion_rom_bar: u32, - pub capabilities: u8, - pub reserved: [u8; 7], - pub interrupt_line: u8, - pub interrupt_pin: u8, - pub min_grant: u8, - pub max_latency: u8 +use super::func::ConfigReader; +use super::class::PciClass; +use super::bar::PciBar; + +#[derive(Debug, PartialEq)] +pub enum PciHeaderError { + NoDevice, + UnknownHeaderType(u8) } -impl Deref for PciHeader { - type Target = [u32]; - fn deref(&self) -> &[u32] { - unsafe { slice::from_raw_parts(self as *const PciHeader as *const u32, mem::size_of::()/4) as &[u32] } +bitflags! { + /// Flags found in the status register of a PCI device + pub struct PciHeaderType: u8 { + /// A general PCI device (Type 0x01). + const GENERAL = 0b00000000; + /// A PCI-to-PCI bridge device (Type 0x01). + const PCITOPCI = 0b00000001; + /// A PCI-to-PCI bridge device (Type 0x02). + const CARDBUSBRIDGE = 0b00000010; + /// A multifunction device. + const MULTIFUNCTION = 0b01000000; + /// Mask used for fetching the header type. + const HEADER_TYPE = 0b00000011; } } -impl DerefMut for PciHeader { - fn deref_mut(&mut self) -> &mut [u32] { - unsafe { slice::from_raw_parts_mut(self as *mut PciHeader as *mut u32, mem::size_of::()/4) as &mut [u32] } +#[derive(Debug, PartialEq)] +pub enum PciHeader { + General { + vendor_id: u16, + device_id: u16, + command: u16, + status: u16, + revision: u8, + interface: u8, + subclass: u8, + class: PciClass, + cache_line_size: u8, + latency_timer: u8, + header_type: PciHeaderType, + bist: u8, + bars: [PciBar; 6], + cardbus_cis_ptr: u32, + subsystem_vendor_id: u16, + subsystem_id: u16, + expansion_rom_bar: u32, + cap_pointer: u8, + interrupt_line: u8, + interrupt_pin: u8, + min_grant: u8, + max_latency: u8 + }, + PciToPci { + vendor_id: u16, + device_id: u16, + command: u16, + status: u16, + revision: u8, + interface: u8, + subclass: u8, + class: PciClass, + cache_line_size: u8, + latency_timer: u8, + header_type: PciHeaderType, + bist: u8, + bars: [PciBar; 2], + primary_bus_num: u8, + secondary_bus_num: u8, + subordinate_bus_num: u8, + secondary_latency_timer: u8, + io_base: u8, + io_limit: u8, + secondary_status: u16, + mem_base: u16, + mem_limit: u16, + prefetch_base: u16, + prefetch_limit: u16, + prefetch_base_upper: u32, + prefetch_limit_upper: u32, + io_base_upper: u16, + io_limit_upper: u16, + cap_pointer: u8, + expansion_rom: u32, + interrupt_line: u8, + interrupt_pin : u8, + bridge_control: u16 } } + +impl PciHeader { + /// Parse the bytes found in the Configuration Space of the PCI device into + /// a more usable PciHeader. + pub fn from_reader(reader: T) -> Result { + if unsafe { reader.read_u32(0) } != 0xffffffff { + // Read the initial 16 bytes and set variables used by all header types. + let bytes = unsafe { reader.read_range(0, 16) }; + let vendor_id = LittleEndian::read_u16(&bytes[0..2]); + let device_id = LittleEndian::read_u16(&bytes[2..4]); + let command = LittleEndian::read_u16(&bytes[4..6]); + let status = LittleEndian::read_u16(&bytes[6..8]); + let revision = bytes[8]; + let interface = bytes[9]; + let subclass = bytes[10]; + let class = PciClass::from(bytes[11]); + let cache_line_size = bytes[12]; + let latency_timer = bytes[13]; + let header_type = PciHeaderType::from_bits_truncate(bytes[14]); + let bist = bytes[15]; + match header_type & PciHeaderType::HEADER_TYPE { + PciHeaderType::GENERAL => { + let bytes = unsafe { reader.read_range(16, 48) }; + let bars = [ + PciBar::from(LittleEndian::read_u32(&bytes[0..4])), + PciBar::from(LittleEndian::read_u32(&bytes[4..8])), + PciBar::from(LittleEndian::read_u32(&bytes[8..12])), + PciBar::from(LittleEndian::read_u32(&bytes[12..16])), + PciBar::from(LittleEndian::read_u32(&bytes[16..20])), + PciBar::from(LittleEndian::read_u32(&bytes[20..24])), + ]; + let cardbus_cis_ptr = LittleEndian::read_u32(&bytes[24..28]); + let subsystem_vendor_id = LittleEndian::read_u16(&bytes[28..30]); + let subsystem_id = LittleEndian::read_u16(&bytes[30..32]); + let expansion_rom_bar = LittleEndian::read_u32(&bytes[32..36]); + // TODO: Parse out the capabilities list. + let cap_pointer = bytes[36]; + let interrupt_line = bytes[44]; + let interrupt_pin = bytes[45]; + let min_grant = bytes[46]; + let max_latency = bytes[47]; + Ok(PciHeader::General { + vendor_id, device_id, command, status, revision, interface, + subclass, class, cache_line_size, latency_timer, header_type, + bist, bars, cardbus_cis_ptr, subsystem_vendor_id, subsystem_id, + expansion_rom_bar, cap_pointer, interrupt_line, interrupt_pin, + min_grant, max_latency + }) + }, + PciHeaderType::PCITOPCI => { + let bytes = unsafe { reader.read_range(16, 48) }; + let bars = [ + PciBar::from(LittleEndian::read_u32(&bytes[0..4])), + PciBar::from(LittleEndian::read_u32(&bytes[4..8])), + ]; + let primary_bus_num = bytes[8]; + let secondary_bus_num = bytes[9]; + let subordinate_bus_num = bytes[10]; + let secondary_latency_timer = bytes[11]; + let io_base = bytes[12]; + let io_limit = bytes[13]; + let secondary_status = LittleEndian::read_u16(&bytes[14..16]); + let mem_base = LittleEndian::read_u16(&bytes[16..18]); + let mem_limit = LittleEndian::read_u16(&bytes[18..20]); + let prefetch_base = LittleEndian::read_u16(&bytes[20..22]); + let prefetch_limit = LittleEndian::read_u16(&bytes[22..24]); + let prefetch_base_upper = LittleEndian::read_u32(&bytes[24..28]); + let prefetch_limit_upper = LittleEndian::read_u32(&bytes[28..32]); + let io_base_upper = LittleEndian::read_u16(&bytes[32..34]); + let io_limit_upper = LittleEndian::read_u16(&bytes[34..36]); + // TODO: Parse out the capabilities list. + let cap_pointer = bytes[36]; + let expansion_rom = LittleEndian::read_u32(&bytes[40..44]); + let interrupt_line = bytes[44]; + let interrupt_pin = bytes[45]; + let bridge_control = LittleEndian::read_u16(&bytes[46..48]); + Ok(PciHeader::PciToPci { + vendor_id, device_id, command, status, revision, interface, + subclass, class, cache_line_size, latency_timer, header_type, + bist, bars, primary_bus_num, secondary_bus_num, subordinate_bus_num, + secondary_latency_timer, io_base, io_limit, secondary_status, + mem_base, mem_limit, prefetch_base, prefetch_limit, prefetch_base_upper, + prefetch_limit_upper, io_base_upper, io_limit_upper, cap_pointer, + expansion_rom, interrupt_line, interrupt_pin, bridge_control + }) + + }, + id => Err(PciHeaderError::UnknownHeaderType(id.bits())) + } + } else { + Err(PciHeaderError::NoDevice) + } + } + + /// Return the Header Type. + pub fn header_type(&self) -> PciHeaderType { + match self { + &PciHeader::General { header_type, .. } | &PciHeader::PciToPci { header_type, .. } => header_type, + } + } + + /// Return the Vendor ID field. + pub fn vendor_id(&self) -> u16 { + match self { + &PciHeader::General { vendor_id, .. } | &PciHeader::PciToPci { vendor_id, .. } => vendor_id, + } + } + + /// Return the Device ID field. + pub fn device_id(&self) -> u16 { + match self { + &PciHeader::General { device_id, .. } | &PciHeader::PciToPci { device_id, .. } => device_id, + } + } + + /// Return the Revision field. + pub fn revision(&self) -> u8 { + match self { + &PciHeader::General { revision, .. } | &PciHeader::PciToPci { revision, .. } => revision, + } + } + + /// Return the Interface field. + pub fn interface(&self) -> u8 { + match self { + &PciHeader::General { interface, .. } | &PciHeader::PciToPci { interface, .. } => interface, + } + } + + /// Return the Subclass field. + pub fn subclass(&self) -> u8 { + match self { + &PciHeader::General { subclass, .. } | &PciHeader::PciToPci { subclass, .. } => subclass, + } + } + + /// Return the Class field. + pub fn class(&self) -> PciClass { + match self { + &PciHeader::General { class, .. } | &PciHeader::PciToPci { class, .. } => class, + } + } + + /// Return the Headers BARs. + pub fn bars(&self) -> &[PciBar] { + match self { + &PciHeader::General { ref bars, .. } => bars, + &PciHeader::PciToPci { ref bars, .. } => bars, + } + } + + /// Return the BAR at the given index. + /// + /// # Panics + /// This function panics if the requested BAR index is beyond the length of the header + /// types BAR array. + pub fn get_bar(&self, idx: usize) -> PciBar { + match self { + &PciHeader::General { bars, .. } => { + assert!(idx < 6, "the general PCI device only has 6 BARs"); + bars[idx] + }, + &PciHeader::PciToPci { bars, .. } => { + assert!(idx < 2, "the general PCI device only has 2 BARs"); + bars[idx] + } + } + } + + /// Return the Interrupt Line field. + pub fn interrupt_line(&self) -> u8 { + match self { + &PciHeader::General { interrupt_line, .. } | &PciHeader::PciToPci { interrupt_line, .. } => + interrupt_line, + } + } + +} + +#[cfg(test)] +impl<'a> ConfigReader for &'a [u8] { + unsafe fn read_u32(&self, offset: u8) -> u32 { + let offset = offset as usize; + assert!(offset < self.len()); + LittleEndian::read_u32(&self[offset..offset + 4]) + } +} + +#[cfg(test)] +mod test { + use super::{PciHeaderError, PciHeader, PciHeaderType}; + use super::super::func::ConfigReader; + use super::super::class::PciClass; + use super::super::bar::PciBar; + + const IGB_DEV_BYTES: [u8; 256] = [ + 0x86, 0x80, 0x33, 0x15, 0x07, 0x04, 0x10, 0x00, 0x03, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x50, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x58, 0xf7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x15, 0x33, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, + 0x01, 0x50, 0x23, 0xc8, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x70, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0xa0, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x10, 0x00, 0x02, 0x00, 0xc2, 0x8c, 0x00, 0x10, 0x0f, 0x28, 0x19, 0x00, 0x11, 0x5c, 0x42, 0x00, + 0x42, 0x00, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]; + + #[test] + fn tset_parse_igb_dev() { + let header = PciHeader::from_reader(&IGB_DEV_BYTES[..]).unwrap(); + assert_eq!(header.header_type(), PciHeaderType::GENERAL); + assert_eq!(header.device_id(), 0x1533); + assert_eq!(header.vendor_id(), 0x8086); + assert_eq!(header.revision(), 3); + assert_eq!(header.interface(), 0); + assert_eq!(header.class(), PciClass::Network); + assert_eq!(header.subclass(), 0); + assert_eq!(header.bars().len(), 6); + assert_eq!(header.get_bar(0), PciBar::Memory(0xf7500000)); + assert_eq!(header.get_bar(1), PciBar::None); + assert_eq!(header.get_bar(2), PciBar::Port(0xb000)); + assert_eq!(header.get_bar(3), PciBar::Memory(0xf7580000)); + assert_eq!(header.get_bar(4), PciBar::None); + assert_eq!(header.get_bar(5), PciBar::None); + assert_eq!(header.interrupt_line(), 10); + } + + #[test] + fn test_parse_nonexistent() { + let bytes = [ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff + ]; + assert_eq!(PciHeader::from_reader(&bytes[..]), Err(PciHeaderError::NoDevice)); + } + + #[test] + fn test_read_range() { + let res = unsafe { (&IGB_DEV_BYTES[..]).read_range(0, 4) }; + assert_eq!(res, &[0x86, 0x80, 0x33, 0x15][..]); + + let res = unsafe { (&IGB_DEV_BYTES[..]).read_range(16, 32) }; + let expected = [ + 0x00, 0x00, 0x50, 0xf7, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x58, 0xf7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd9, 0x15, 0x33, 0x15 + ]; + assert_eq!(res, expected); + } + + macro_rules! read_range_should_panic { + ($name:ident, $len:expr) => { + #[test] + #[should_panic(expected = "assertion failed: len > 3 && len % 4 == 0")] + fn $name() { + let _ = unsafe { (&IGB_DEV_BYTES[..]).read_range(0, $len) }; + } + } + } + + read_range_should_panic!(test_short_len, 2); + read_range_should_panic!(test_not_mod_4_len, 7); +} diff --git a/pcid/src/pci/mod.rs b/pcid/src/pci/mod.rs index ef83986e60..e70048fd84 100644 --- a/pcid/src/pci/mod.rs +++ b/pcid/src/pci/mod.rs @@ -3,14 +3,14 @@ pub use self::bus::{PciBus, PciBusIter}; pub use self::class::PciClass; pub use self::dev::{PciDev, PciDevIter}; pub use self::func::PciFunc; -pub use self::header::PciHeader; +pub use self::header::{PciHeader, PciHeaderError, PciHeaderType}; mod bar; mod bus; mod class; mod dev; mod func; -mod header; +pub mod header; pub struct Pci; @@ -28,10 +28,10 @@ impl Pci { let address = 0x80000000 | ((bus as u32) << 16) | ((dev as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xFC); let value: u32; asm!("mov dx, 0xCF8 - out dx, eax - mov dx, 0xCFC - in eax, dx" - : "={eax}"(value) : "{eax}"(address) : "dx" : "intel", "volatile"); + out dx, eax + mov dx, 0xCFC + in eax, dx" + : "={eax}"(value) : "{eax}"(address) : "dx" : "intel", "volatile"); value } @@ -39,11 +39,11 @@ impl Pci { pub unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u8, value: u32) { let address = 0x80000000 | ((bus as u32) << 16) | ((dev as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xFC); asm!("mov dx, 0xCF8 - out dx, eax" - : : "{eax}"(address) : "dx" : "intel", "volatile"); + out dx, eax" + : : "{eax}"(address) : "dx" : "intel", "volatile"); asm!("mov dx, 0xCFC - out dx, eax" - : : "{eax}"(value) : "dx" : "intel", "volatile"); + out dx, eax" + : : "{eax}"(value) : "dx" : "intel", "volatile"); } }