Unverified Commit 452c586b authored by Jeremy Soller's avatar Jeremy Soller Committed by GitHub

Merge pull request #28 from dlrobertson/update_pci_parsing

Allow PCI Config space parsing to handle types
parents 42adde58 4d349192
......@@ -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"
......
#![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");
},
_ => ()
},
_ => ()
},
_ => ()
}
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))
}
}
let func_num = func.num;
match PciHeader::from_reader(func) {
Ok(header) => {
handle_parsed_header(&config, &pci, bus.num, dev.num, func_num, header);
}
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);
}
}
}
......
#[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")
}
}
}
#[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
}
}
}
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)
}
}
use std::ops::{Deref, DerefMut};
use std::{slice, mem};
#[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 byteorder::{LittleEndian, ByteOrder};
use super::func::ConfigReader;
use super::class::PciClass;
use super::bar::PciBar;
#[derive(Debug, PartialEq)]
pub enum PciHeaderError {
NoDevice,
UnknownHeaderType(u8)
}
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 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] }
#[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 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] }
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,