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:
Generated
+2
@@ -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)",
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user