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.
This commit is contained in:
Dan Robertson
2018-02-20 02:56:30 +00:00
parent 42adde5809
commit 4d349192da
8 changed files with 574 additions and 201 deletions
Generated
+2
View File
@@ -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)",
+2
View File
@@ -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"
+136 -134
View File
@@ -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);
}
}
}
+22 -1
View File
@@ -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<u32> for PciBar {
fn from(bar: u32) -> Self {
if bar & 0xFFFFFFFC == 0 {
@@ -16,3 +27,13 @@ impl From<u32> 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")
}
}
}
+28 -1
View File
@@ -1,4 +1,4 @@
#[derive(Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PciClass {
Legacy,
Storage,
@@ -48,3 +48,30 @@ impl From<u8> for PciClass {
}
}
}
impl Into<u8> 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
}
}
}
+21 -20
View File
@@ -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<u8> {
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<PciHeader> {
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)
}
}
+353 -35
View File
@@ -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::<PciHeader>()/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::<PciHeader>()/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<T: ConfigReader>(reader: T) -> Result<PciHeader, PciHeaderError> {
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);
}
+10 -10
View File
@@ -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");
}
}