diff --git a/Cargo.lock b/Cargo.lock index 5bd694432b..1ec51a7bb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,6 +956,7 @@ name = "pcid" version = "0.1.0" dependencies = [ "bincode", + "bit_field", "bitflags 1.3.2", "byteorder 1.4.3", "common", diff --git a/nvmed/src/main.rs b/nvmed/src/main.rs index 9074dcc4d7..2fed31c18c 100644 --- a/nvmed/src/main.rs +++ b/nvmed/src/main.rs @@ -11,7 +11,7 @@ use std::{slice, usize}; use pcid_interface::{PciBar, PciFeature, PciFeatureInfo, PciFunction, PcidServerHandle}; use syscall::{ - Event, Mmio, Packet, Result, SchemeBlockMut, + Event, Mmio, Packet, Result, SchemeBlockMut, PAGE_SIZE, }; use redox_log::{OutputBuilder, RedoxLogger}; @@ -299,7 +299,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { .fetch_config() .expect("nvmed: failed to fetch config"); - let mut scheme_name = format!("disk.pci-{:x}+{:x}+{:x}-nvme", pci_config.func.bus_num, pci_config.func.dev_num, pci_config.func.func_num); + let scheme_name = format!("disk.pci-{}-nvme", pci_config.func.addr); let _logger_ref = setup_logging(&scheme_name); diff --git a/pcid/Cargo.toml b/pcid/Cargo.toml index f77bb23486..1927fa84f0 100644 --- a/pcid/Cargo.toml +++ b/pcid/Cargo.toml @@ -14,6 +14,7 @@ path = "src/lib.rs" [dependencies] bincode = "1.2" bitflags = "1" +bit_field = "0.10" byteorder = "1.2" libc = "0.2" log = "0.4" diff --git a/pcid/src/driver_interface/mod.rs b/pcid/src/driver_interface/mod.rs index e563c6d06a..1132423793 100644 --- a/pcid/src/driver_interface/mod.rs +++ b/pcid/src/driver_interface/mod.rs @@ -4,12 +4,12 @@ use std::{env, io}; use std::os::unix::io::{FromRawFd, RawFd}; -use serde::{Serialize, Deserialize, de::DeserializeOwned}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use thiserror::Error; pub use crate::pci::cap::Capability; pub use crate::pci::msi; -pub use crate::pci::{PciBar, PciHeader}; +pub use crate::pci::{PciAddress, PciBar, PciHeader}; pub mod irq_helpers; @@ -28,14 +28,8 @@ pub enum LegacyInterruptPin { #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PciFunction { - /// Number of PCI bus - pub bus_num: u8, - - /// Number of PCI device - pub dev_num: u8, - - /// Number of PCI function - pub func_num: u8, + /// Address of the PCI function. + pub addr: PciAddress, /// PCI Base Address Registers pub bars: [PciBar; 6], @@ -58,7 +52,8 @@ pub struct PciFunction { } impl PciFunction { pub fn name(&self) -> String { - format!("pci-{:>02X}.{:>02X}.{:>02X}", self.bus_num, self.dev_num, self.func_num) + // FIXME stop replacing : with - once it is a valid character in scheme names + format!("pci-{}", self.addr).replace(':', "-") } } diff --git a/pcid/src/main.rs b/pcid/src/main.rs index 2a41da0a3a..df1799ffb0 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -10,7 +10,7 @@ use log::{debug, error, info, warn, trace}; use redox_log::{OutputBuilder, RedoxLogger}; use crate::config::Config; -use crate::pci::{CfgAccess, Pci, PciIter, PciBar, PciBus, PciClass, PciDev, PciFunc, PciHeader, PciHeaderError, PciHeaderType}; +use crate::pci::{CfgAccess, Pci, PciAddress, PciBar, PciBus, PciClass, PciFunc, PciHeader, PciHeaderError, PciHeaderType}; use crate::pci::cap::Capability as PciCapability; use crate::pci::func::{ConfigReader, ConfigWriter}; use crate::pcie::Pcie; @@ -34,33 +34,20 @@ struct Args { pub struct DriverHandler { config: config::DriverConfig, - bus_num: u8, - dev_num: u8, - func_num: u8, + addr: PciAddress, header: PciHeader, capabilities: Vec<(u8, PciCapability)>, state: Arc, } -fn with_pci_func_raw T>(pci: &dyn CfgAccess, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T { - let bus = PciBus { - pci, - num: bus_num, - }; - let dev = PciDev { - bus: &bus, - num: dev_num, - }; +fn with_pci_func_raw T>(pci: &dyn CfgAccess, addr: PciAddress, function: F) -> T { let func = PciFunc { - dev: &dev, - num: func_num, + pci, + addr, }; function(&func) } impl DriverHandler { - fn with_pci_func_raw T>(&self, function: F) -> T { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, function) - } fn respond(&mut self, request: driver_interface::PcidClientRequest, args: &driver_interface::SubdriverArguments) -> driver_interface::PcidClientResponse { use driver_interface::*; use crate::pci::cap::{MsiCapability, MsixCapability}; @@ -89,7 +76,7 @@ impl DriverHandler { None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)), }; unsafe { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| { capability.set_enabled(true); capability.write_message_control(func, offset); }); @@ -102,7 +89,7 @@ impl DriverHandler { None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)), }; unsafe { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| { capability.set_msix_enabled(true); capability.write_a(func, offset); }); @@ -162,7 +149,7 @@ impl DriverHandler { info.set_mask_bits(mask_bits); } unsafe { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| { info.write_all(func, offset); }); } @@ -174,7 +161,7 @@ impl DriverHandler { if let Some(mask) = function_mask { info.set_function_mask(mask); unsafe { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| { info.write_a(func, offset); }); } @@ -186,7 +173,7 @@ impl DriverHandler { } PcidClientRequest::ReadConfig(offset) => { let value = unsafe { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| { func.read_u32(offset) }) }; @@ -194,7 +181,7 @@ impl DriverHandler { }, PcidClientRequest::WriteConfig(offset, value) => { unsafe { - with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| { func.write_u32(offset, value); }); } @@ -224,19 +211,16 @@ pub struct State { } impl State { fn preferred_cfg_access(&self) -> &dyn CfgAccess { - // TODO - //self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess) - &*self.pci as &dyn CfgAccess + self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess) } } -fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, - dev_num: u8, func_num: u8, header: PciHeader) { +fn handle_parsed_header(state: Arc, config: &Config, addr: PciAddress, header: PciHeader) { let pci = state.preferred_cfg_access(); 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, + let mut string = format!("PCI {} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}", + addr, header.vendor_id(), header.device_id(), raw_class, header.subclass(), header.interface(), header.revision(), header.class()); match header.class() { PciClass::Legacy if header.subclass() == 1 => string.push_str(" VGA CTL"), @@ -327,24 +311,24 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, if let Some(ref args) = driver.command { // Enable bus mastering, memory space, and I/O space unsafe { - let mut data = pci.read(bus_num, dev_num, func_num, 0x04); + let mut data = pci.read(addr, 0x04); data |= 7; - pci.write(bus_num, dev_num, func_num, 0x04, data); + pci.write(addr, 0x04, data); } // Set IRQ line to 9 if not set let mut irq; - let mut interrupt_pin; + let interrupt_pin; unsafe { - let mut data = pci.read(bus_num, dev_num, func_num, 0x3C); + let mut data = pci.read(addr, 0x3C); irq = (data & 0xFF) as u8; interrupt_pin = ((data & 0x0000_FF00) >> 8) as u8; if irq == 0xFF { irq = 9; } data = (data & 0xFFFFFF00) | irq as u32; - pci.write(bus_num, dev_num, func_num, 0x3C, data); + pci.write(addr, 0x3C, data); }; // Find BAR sizes @@ -363,11 +347,11 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, let offset = 0x10 + (i as u8) * 4; - let original = pci.read(bus_num, dev_num, func_num, offset.into()); - pci.write(bus_num, dev_num, func_num, offset.into(), 0xFFFFFFFF); + let original = pci.read(addr, offset.into()); + pci.write(addr, offset.into(), 0xFFFFFFFF); - let new = pci.read(bus_num, dev_num, func_num, offset.into()); - pci.write(bus_num, dev_num, func_num, offset.into(), original); + let new = pci.read(addr, offset.into()); + pci.write(addr, offset.into(), original); let masked = if new & 1 == 1 { new & 0xFFFFFFFC @@ -385,17 +369,9 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, } let capabilities = if header.status() & (1 << 4) != 0 { - let bus = PciBus { - pci: state.preferred_cfg_access(), - num: bus_num, - }; - let dev = PciDev { - bus: &bus, - num: dev_num - }; let func = PciFunc { - dev: &dev, - num: func_num, + pci: state.preferred_cfg_access(), + addr }; crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(header.cap_pointer(), &func) }.collect::>() } else { @@ -421,9 +397,7 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, let func = driver_interface::PciFunction { bars, bar_sizes, - bus_num, - dev_num, - func_num, + addr, devid: header.device_id(), legacy_interrupt_line: irq, legacy_interrupt_pin, @@ -439,9 +413,9 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, 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), + "$BUS" => format!("{:>02X}", addr.bus()), + "$DEV" => format!("{:>02X}", addr.device()), + "$FUNC" => format!("{:>02X}", addr.function()), "$NAME" => func.name(), "$BAR0" => format!("{}", bars[0]), "$BAR1" => format!("{}", bars[1]), @@ -491,15 +465,13 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, match command.envs(envs).spawn() { Ok(mut child) => { let driver_handler = DriverHandler { - bus_num, - dev_num, - func_num, + addr, config: driver.clone(), header, state: Arc::clone(&state), capabilities, }; - let thread = thread::spawn(move || { + thread::spawn(move || { // RFLAGS are no longer kept in the relibc clone() implementation. unsafe { syscall::iopl(3).expect("pcid: failed to set IOPL"); } @@ -613,30 +585,25 @@ fn main(args: Args) { let mut bus_nums = vec![0]; let mut bus_i = 0; - 'bus: while bus_i < bus_nums.len() { + while bus_i < bus_nums.len() { let bus_num = bus_nums[bus_i]; bus_i += 1; - let bus = PciBus { pci, num: bus_num }; + let bus = PciBus { num: bus_num }; 'dev: for dev in bus.devs() { - for func in dev.funcs() { - let func_num = func.num; + for func in dev.funcs(pci) { + let func_addr = func.addr; match PciHeader::from_reader(func) { Ok(header) => { - handle_parsed_header(Arc::clone(&state), &config, bus.num, dev.num, func_num, header); + handle_parsed_header(Arc::clone(&state), &config, func_addr, header); if let PciHeader::PciToPci { secondary_bus_num, .. } = header { bus_nums.push(secondary_bus_num); } } Err(PciHeaderError::NoDevice) => { - if func_num == 0 { - if dev.num == 0 { - trace!("PCI {:>02X}: no bus", bus.num); - continue 'bus; - } else { + if func_addr.function() == 0 { trace!("PCI {:>02X}/{:>02X}: no dev", bus.num, dev.num); continue 'dev; - } } }, Err(PciHeaderError::UnknownHeaderType(id)) => { diff --git a/pcid/src/pci/bus.rs b/pcid/src/pci/bus.rs index 0211c6d4ac..9900d118d9 100644 --- a/pcid/src/pci/bus.rs +++ b/pcid/src/pci/bus.rs @@ -1,49 +1,39 @@ -use super::{PciDev, CfgAccess}; +use super::PciDev; -pub struct PciBus<'pci> { - pub pci: &'pci dyn CfgAccess, - pub num: u8 +#[derive(Copy, Clone)] +pub struct PciBus { + pub num: u8, } -impl<'pci> PciBus<'pci> { - pub fn devs(&'pci self) -> PciBusIter<'pci> { +impl<'pci> PciBus { + pub fn devs(self) -> PciBusIter { PciBusIter::new(self) } +} - pub unsafe fn read(&self, dev: u8, func: u8, offset: u16) -> u32 { - self.pci.read(self.num, dev, func, offset) - } - pub unsafe fn write(&self, dev: u8, func: u8, offset: u16, value: u32) { - self.pci.write(self.num, dev, func, offset, value) +pub struct PciBusIter { + bus: PciBus, + num: u8, +} + +impl PciBusIter { + pub fn new(bus: PciBus) -> Self { + PciBusIter { bus, num: 0 } } } -pub struct PciBusIter<'pci> { - bus: &'pci PciBus<'pci>, - num: u8 -} - -impl<'pci> PciBusIter<'pci> { - pub fn new(bus: &'pci PciBus<'pci>) -> Self { - PciBusIter { - bus, - num: 0 - } - } -} - -impl<'pci> Iterator for PciBusIter<'pci> { - type Item = PciDev<'pci>; +impl Iterator for PciBusIter { + type Item = PciDev; fn next(&mut self) -> Option { match self.num { dev_num if dev_num < 32 => { let dev = PciDev { bus: self.bus, - num: self.num + num: self.num, }; self.num += 1; Some(dev) - }, + } _ => None, } } diff --git a/pcid/src/pci/dev.rs b/pcid/src/pci/dev.rs index ca2e505bb4..b64eeb1034 100644 --- a/pcid/src/pci/dev.rs +++ b/pcid/src/pci/dev.rs @@ -1,34 +1,26 @@ -use super::{PciBus, PciFunc}; +use super::{CfgAccess, PciAddress, PciBus, PciFunc}; -pub struct PciDev<'pci> { - pub bus: &'pci PciBus<'pci>, - pub num: u8 +#[derive(Copy, Clone)] +pub struct PciDev { + pub bus: PciBus, + pub num: u8, } -impl<'pci> PciDev<'pci> { - pub fn funcs(&'pci self) -> PciDevIter<'pci> { - PciDevIter::new(self) - } - - pub unsafe fn read(&self, func: u8, offset: u16) -> u32 { - self.bus.read(self.num, func, offset) - } - pub unsafe fn write(&self, func: u8, offset: u16, value: u32) { - self.bus.write(self.num, func, offset, value); +impl<'pci> PciDev { + pub fn funcs(self, pci: &'pci dyn CfgAccess) -> PciDevIter<'pci> { + PciDevIter::new(self, pci) } } pub struct PciDevIter<'pci> { - dev: &'pci PciDev<'pci>, - num: u8 + pci: &'pci dyn CfgAccess, + dev: PciDev, + num: u8, } impl<'pci> PciDevIter<'pci> { - pub fn new(dev: &'pci PciDev<'pci>) -> Self { - PciDevIter { - dev: dev, - num: 0 - } + pub fn new(dev: PciDev, pci: &'pci dyn CfgAccess) -> Self { + PciDevIter { pci, dev, num: 0 } } } @@ -38,12 +30,12 @@ impl<'pci> Iterator for PciDevIter<'pci> { match self.num { func_num if func_num < 8 => { let func = PciFunc { - dev: self.dev, - num: self.num + pci: self.pci, + addr: PciAddress::new(0, self.dev.bus.num, self.dev.num, func_num), }; self.num += 1; Some(func) - }, + } _ => None, } } diff --git a/pcid/src/pci/func.rs b/pcid/src/pci/func.rs index fbdda1cfc6..6625542db9 100644 --- a/pcid/src/pci/func.rs +++ b/pcid/src/pci/func.rs @@ -1,16 +1,18 @@ -use byteorder::{LittleEndian, ByteOrder}; +use byteorder::{ByteOrder, LittleEndian}; -use super::PciDev; +use super::{CfgAccess, PciAddress, PciDev}; pub trait ConfigReader { unsafe fn read_range(&self, offset: u16, len: u16) -> Vec { assert!(len > 3 && len % 4 == 0, "invalid range length: {}", len); 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 - }); + 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 @@ -31,17 +33,17 @@ pub trait ConfigWriter { } pub struct PciFunc<'pci> { - pub dev: &'pci PciDev<'pci>, - pub num: u8, + pub pci: &'pci dyn CfgAccess, + pub addr: PciAddress, } impl<'pci> ConfigReader for PciFunc<'pci> { unsafe fn read_u32(&self, offset: u16) -> u32 { - self.dev.read(self.num, offset) + self.pci.read(self.addr, offset) } } impl<'pci> ConfigWriter for PciFunc<'pci> { unsafe fn write_u32(&self, offset: u16, value: u32) { - self.dev.write(self.num, offset, value); + self.pci.write(self.addr, offset, value); } } diff --git a/pcid/src/pci/header.rs b/pcid/src/pci/header.rs index 6139c45bb9..2c7b0556ff 100644 --- a/pcid/src/pci/header.rs +++ b/pcid/src/pci/header.rs @@ -1,15 +1,15 @@ use bitflags::bitflags; -use byteorder::{LittleEndian, ByteOrder}; -use serde::{Serialize, Deserialize}; +use byteorder::{ByteOrder, LittleEndian}; +use serde::{Deserialize, Serialize}; -use super::func::ConfigReader; -use super::class::PciClass; use super::bar::PciBar; +use super::class::PciClass; +use super::func::ConfigReader; #[derive(Debug, PartialEq)] pub enum PciHeaderError { NoDevice, - UnknownHeaderType(u8) + UnknownHeaderType(u8), } bitflags! { @@ -30,8 +30,7 @@ bitflags! { } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -pub enum PciHeader { - General { +pub struct SharedPciHeader { vendor_id: u16, device_id: u16, command: u16, @@ -44,6 +43,12 @@ pub enum PciHeader { latency_timer: u8, header_type: PciHeaderType, bist: u8, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum PciHeader { + General { + shared: SharedPciHeader, bars: [PciBar; 6], cardbus_cis_ptr: u32, subsystem_vendor_id: u16, @@ -53,21 +58,10 @@ pub enum PciHeader { interrupt_line: u8, interrupt_pin: u8, min_grant: u8, - max_latency: 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, + shared: SharedPciHeader, bars: [PciBar; 2], primary_bus_num: u8, secondary_bus_num: u8, @@ -87,9 +81,9 @@ pub enum PciHeader { cap_pointer: u8, expansion_rom: u32, interrupt_line: u8, - interrupt_pin : u8, - bridge_control: u16 - } + interrupt_pin: u8, + bridge_control: u16, + }, } impl PciHeader { @@ -111,7 +105,7 @@ impl PciHeader { addr |= (LittleEndian::read_u32(high_bytes) as u64) << 32; bars[i] = PciBar::Memory64(addr); i += 2; - }, + } bar => { bars[i] = bar; i += 1; @@ -138,6 +132,21 @@ impl PciHeader { let latency_timer = bytes[13]; let header_type = PciHeaderType::from_bits_truncate(bytes[14]); let bist = bytes[15]; + let shared = SharedPciHeader { + vendor_id, + device_id, + command, + status, + revision, + interface, + subclass, + class, + cache_line_size, + latency_timer, + header_type, + bist, + }; + match header_type & PciHeaderType::HEADER_TYPE { PciHeaderType::GENERAL => { let bytes = unsafe { reader.read_range(16, 48) }; @@ -153,13 +162,19 @@ impl PciHeader { 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 + shared, + 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 mut bars = [PciBar::None; 2]; @@ -185,17 +200,31 @@ impl PciHeader { 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 + shared, + 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())) + } + id => Err(PciHeaderError::UnknownHeaderType(id.bits())), } } else { Err(PciHeaderError::NoDevice) @@ -205,49 +234,98 @@ impl PciHeader { /// Return the Header Type. pub fn header_type(&self) -> PciHeaderType { match self { - &PciHeader::General { header_type, .. } | &PciHeader::PciToPci { header_type, .. } => header_type, + &PciHeader::General { + shared: SharedPciHeader { header_type, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { 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, + &PciHeader::General { + shared: SharedPciHeader { vendor_id, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { 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, + &PciHeader::General { + shared: SharedPciHeader { device_id, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { device_id, .. }, + .. + } => device_id, } } /// Return the Revision field. pub fn revision(&self) -> u8 { match self { - &PciHeader::General { revision, .. } | &PciHeader::PciToPci { revision, .. } => revision, + &PciHeader::General { + shared: SharedPciHeader { revision, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { revision, .. }, + .. + } => revision, } } /// Return the Interface field. pub fn interface(&self) -> u8 { match self { - &PciHeader::General { interface, .. } | &PciHeader::PciToPci { interface, .. } => interface, + &PciHeader::General { + shared: SharedPciHeader { interface, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { interface, .. }, + .. + } => interface, } } /// Return the Subclass field. pub fn subclass(&self) -> u8 { match self { - &PciHeader::General { subclass, .. } | &PciHeader::PciToPci { subclass, .. } => subclass, + &PciHeader::General { + shared: SharedPciHeader { subclass, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { subclass, .. }, + .. + } => subclass, } } /// Return the Class field. pub fn class(&self) -> PciClass { match self { - &PciHeader::General { class, .. } | &PciHeader::PciToPci { class, .. } => class, + &PciHeader::General { + shared: SharedPciHeader { class, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { class, .. }, + .. + } => class, } } @@ -269,7 +347,7 @@ impl PciHeader { &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] @@ -280,20 +358,29 @@ impl PciHeader { /// Return the Interrupt Line field. pub fn interrupt_line(&self) -> u8 { match self { - &PciHeader::General { interrupt_line, .. } | &PciHeader::PciToPci { interrupt_line, .. } => - interrupt_line, + &PciHeader::General { interrupt_line, .. } + | &PciHeader::PciToPci { interrupt_line, .. } => interrupt_line, } } pub fn status(&self) -> u16 { match self { - &PciHeader::General { status, .. } | &PciHeader::PciToPci { status, .. } => status, + &PciHeader::General { + shared: SharedPciHeader { status, .. }, + .. + } + | &PciHeader::PciToPci { + shared: SharedPciHeader { status, .. }, + .. + } => status, } } pub fn cap_pointer(&self) -> u8 { match self { - &PciHeader::General { cap_pointer, .. } | &PciHeader::PciToPci { cap_pointer, .. } => cap_pointer, + &PciHeader::General { cap_pointer, .. } | &PciHeader::PciToPci { cap_pointer, .. } => { + cap_pointer + } } } } @@ -380,7 +467,7 @@ mod test { macro_rules! read_range_should_panic { ($name:ident, $len:expr) => { #[test] - #[should_panic(expected = "assertion failed: len > 3 && len % 4 == 0")] + #[should_panic(expected = "invalid range length")] fn $name() { let _ = unsafe { (&IGB_DEV_BYTES[..]).read_range(0, $len) }; } diff --git a/pcid/src/pci/mod.rs b/pcid/src/pci/mod.rs index e1a18b472f..84baf13f53 100644 --- a/pcid/src/pci/mod.rs +++ b/pcid/src/pci/mod.rs @@ -1,6 +1,9 @@ use std::convert::TryFrom; +use std::fmt; use std::sync::{Mutex, Once}; +use bit_field::BitField; +use serde::{Deserialize, Serialize}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use syscall::io::{Io as _, Pio}; @@ -23,11 +26,69 @@ pub mod header; pub mod msi; pub trait CfgAccess { - unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32; - unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32; + unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32; + unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32); +} - unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32); - unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32); +// Copied from the pci_types crate, version 0.6.1. It has been modified to add serde support. +// FIXME If we start using it in the future use the upstream version instead. +/// The address of a PCIe function. +/// +/// PCIe supports 65536 segments, each with 256 buses, each with 32 slots, each with 8 possible functions. We pack this into a `u32`: +/// +/// ```ignore +/// 32 16 8 3 0 +/// +-------------------------------+---------------+---------+------+ +/// | segment | bus | device | func | +/// +-------------------------------+---------------+---------+------+ +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)] +pub struct PciAddress(u32); + +impl PciAddress { + pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> PciAddress { + let mut result = 0; + result.set_bits(0..3, function as u32); + result.set_bits(3..8, device as u32); + result.set_bits(8..16, bus as u32); + result.set_bits(16..32, segment as u32); + PciAddress(result) + } + + pub fn segment(&self) -> u16 { + self.0.get_bits(16..32) as u16 + } + + pub fn bus(&self) -> u8 { + self.0.get_bits(8..16) as u8 + } + + pub fn device(&self) -> u8 { + self.0.get_bits(3..8) as u8 + } + + pub fn function(&self) -> u8 { + self.0.get_bits(0..3) as u8 + } +} + +impl fmt::Display for PciAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{:02x}-{:02x}:{:02x}.{}", + self.segment(), + self.bus(), + self.device(), + self.function() + ) + } +} + +impl fmt::Debug for PciAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } } pub struct Pci { @@ -51,98 +112,91 @@ impl Pci { // make sure that pcid is not granted io port permission unless pcie memory-mapped // configuration space is not available. info!("PCI: couldn't find or access PCIe extended configuration, and thus falling back to PCI 3.0 io ports"); - unsafe { syscall::iopl(3).expect("pcid: failed to set iopl to 3"); } + unsafe { + syscall::iopl(3).expect("pcid: failed to set iopl to 3"); + } } - fn address(bus: u8, dev: u8, func: u8, offset: u8) -> u32 { + fn address(address: PciAddress, offset: u8) -> u32 { // TODO: Find the part of pcid that uses an unaligned offset! // // assert_eq!(offset & 0xFC, offset, "pci offset is not aligned"); // let offset = offset & 0xFC; - assert_eq!(dev & 0x1F, dev, "pci device larger than 5 bits"); - assert_eq!(func & 0x7, func, "pci func larger than 3 bits"); + assert_eq!( + address.segment(), + 0, + "usage of multiple segments requires PCIe extended configuration" + ); - 0x80000000 | (u32::from(bus) << 16) | (u32::from(dev) << 11) | (u32::from(func) << 8) | u32::from(offset) + 0x80000000 + | (u32::from(address.bus()) << 16) + | (u32::from(address.device()) << 11) + | (u32::from(address.function()) << 8) + | u32::from(offset) } } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl CfgAccess for Pci { - unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { + unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 { + let _guard = self.lock.lock().unwrap(); + self.iopl_once.call_once(Self::set_iopl); - let offset = u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); - let address = Self::address(bus, dev, func, offset); + let offset = + u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); + let address = Self::address(address, offset); Pio::::new(0xCF8).write(address); Pio::::new(0xCFC).read() } - unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { + unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) { let _guard = self.lock.lock().unwrap(); - self.read_nolock(bus, dev, func, offset) - } - unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { self.iopl_once.call_once(Self::set_iopl); - let offset = u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); - let address = Self::address(bus, dev, func, offset); + let offset = + u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); + let address = Self::address(address, offset); Pio::::new(0xCF8).write(address); Pio::::new(0xCFC).write(value); } - unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { - let _guard = self.lock.lock().unwrap(); - self.write_nolock(bus, dev, func, offset, value) - } } #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] impl CfgAccess for Pci { - unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { - todo!("Pci::CfgAccess::read_nolock on this architecture") + unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 { + let _guard = self.lock.lock().unwrap(); + todo!("Pci::CfgAccess::read on this architecture") } - unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { + unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32) { let _guard = self.lock.lock().unwrap(); - self.read_nolock(bus, dev, func, offset) - } - - unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { - todo!("Pci::CfgAccess::write_nolock on this architecture") - } - unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { - let _guard = self.lock.lock().unwrap(); - self.write_nolock(bus, dev, func, offset, value) + todo!("Pci::CfgAccess::write on this architecture") } } pub struct PciIter<'pci> { pci: &'pci dyn CfgAccess, - num: Option + num: Option, } impl<'pci> PciIter<'pci> { pub fn new(pci: &'pci dyn CfgAccess) -> Self { - PciIter { - pci, - num: Some(0) - } + PciIter { pci, num: Some(0) } } } impl<'pci> Iterator for PciIter<'pci> { - type Item = PciBus<'pci>; + type Item = PciBus; fn next(&mut self) -> Option { match self.num { Some(bus_num) => { - let bus = PciBus { - pci: self.pci, - num: bus_num - }; + let bus = PciBus { num: bus_num }; self.num = bus_num.checked_add(1); Some(bus) - }, + } None => None, } } diff --git a/pcid/src/pcie/mod.rs b/pcid/src/pcie/mod.rs index 995f4b7482..623471a230 100644 --- a/pcid/src/pcie/mod.rs +++ b/pcid/src/pcie/mod.rs @@ -1,12 +1,12 @@ -use std::{fmt, fs, io, mem, ptr, slice}; use std::collections::BTreeMap; use std::sync::{Arc, Mutex}; +use std::{fmt, fs, io, mem, ptr, slice}; use syscall::PAGE_SIZE; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; -use crate::pci::{CfgAccess, Pci, PciIter}; +use crate::pci::{CfgAccess, Pci, PciAddress, PciIter}; pub const MCFG_NAME: [u8; 4] = *b"MCFG"; @@ -31,7 +31,6 @@ unsafe impl plain::Plain for Mcfg {} /// The "Memory Mapped Enhanced Configuration Space Base Address Allocation Structure" (yes, it's /// called that). - #[repr(packed)] #[derive(Clone, Copy, Debug)] pub struct PcieAlloc { @@ -45,16 +44,21 @@ unsafe impl plain::Plain for PcieAlloc {} impl Mcfg { pub fn base_addr_structs(&self) -> &[PcieAlloc] { - let total_length = mem::size_of::(); - let len = total_length - 44; + let total_length = self.length as usize; + let len = total_length - mem::size_of::(); // safe because the length cannot be changed arbitrarily - unsafe { slice::from_raw_parts(&self.base_addrs as *const PcieAlloc, len / mem::size_of::()) } + unsafe { + slice::from_raw_parts( + &self.base_addrs as *const PcieAlloc, + len / mem::size_of::(), + ) + } } } impl fmt::Debug for Mcfg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Mcfg") - .field("name", &"MCFG") + .field("name", &self.name) .field("length", &{ self.length }) .field("revision", &self.revision) .field("checksum", &self.checksum) @@ -83,37 +87,40 @@ impl Mcfgs { }) } pub fn allocs<'a>(&'a self) -> impl Iterator + 'a { - self.tables().map(|table| table.base_addr_structs().iter()).flatten() + self.tables() + .map(|table| table.base_addr_structs().iter()) + .flatten() } pub fn fetch() -> io::Result { let table_dir = fs::read_dir("acpi:tables")?; - let tables = table_dir.map(|table_direntry| -> io::Result> { - let table_direntry = table_direntry?; - let table_path = table_direntry.path(); + let mut tables = smallvec![]; + for table_direntry in table_dir { + let table_path = table_direntry?.path(); - let table_filename = match table_path.file_name() { - Some(n) => n.to_str().ok_or(io::Error::new(io::ErrorKind::InvalidData, "Non-UTF-8 ACPI table filename"))?, - None => return Ok(None), - }; - - if table_filename.starts_with("MCFG") { - Ok(Some(fs::read(table_path)?)) - } else { - Ok(None) + // Every directory entry has to have a filename unless + // the filesystem (or in this case acpid) misbehaves. + // If it misbehaves we have worse problems than pcid + // crashing. `as_encoded_bytes()` returns some superset + // of ASCII, so directly comparing it with an ASCII name + // is fine. + let table_filename = table_path.file_name().unwrap().as_encoded_bytes(); + if table_filename.get(0..4) == Some(&MCFG_NAME) { + tables.push(fs::read(table_path)?); } - }).filter_map(|result_option| result_option.transpose()).collect::, _>>()?; + } - Ok(Self { - tables, - }) + Ok(Self { tables }) } pub fn table_and_alloc_at_bus(&self, bus: u8) -> Option<(&Mcfg, &PcieAlloc)> { self.tables().find_map(|table| { - Some((table, table.base_addr_structs().iter().find(|addr_struct| { - (addr_struct.start_bus..addr_struct.end_bus).contains(&bus) - })?)) + Some(( + table, + table.base_addr_structs().iter().find(|addr_struct| { + (addr_struct.start_bus..=addr_struct.end_bus).contains(&bus) + })?, + )) }) } pub fn at_bus(&self, bus: u8) -> Option<&PcieAlloc> { @@ -137,7 +144,7 @@ impl fmt::Debug for Mcfgs { pub struct Pcie { lock: Mutex<()>, mcfgs: Mcfgs, - maps: Mutex>, + maps: Mutex>, fallback: Arc, } unsafe impl Send for Pcie {} @@ -154,34 +161,55 @@ impl Pcie { fallback, }) } - fn addr_offset_in_bytes(starting_bus: u8, bus: u8, dev: u8, func: u8, offset: u16) -> usize { + fn addr_offset_in_bytes(starting_bus: u8, address: PciAddress, offset: u16) -> usize { assert_eq!(offset & 0xFFFC, offset, "pcie offset not dword-aligned"); assert_eq!(offset & 0x0FFF, offset, "pcie offset larger than 4095"); - assert_eq!(dev & 0x1F, dev, "pcie dev number larger than 5 bits"); - assert_eq!(func & 0x7, func, "pcie func number larger than 3 bits"); - (((bus - starting_bus) as usize) << 20) | ((dev as usize) << 15) | ((func as usize) << 12) | (offset as usize) + assert_eq!( + address.segment(), + 0, + "multiple segments not yet implemented" + ); + + (((address.bus() - starting_bus) as usize) << 20) + | ((address.device() as usize) << 15) + | ((address.function() as usize) << 12) + | (offset as usize) } - fn addr_offset_in_dwords(starting_bus: u8, bus: u8, dev: u8, func: u8, offset: u16) -> usize { - Self::addr_offset_in_bytes(starting_bus, bus, dev, func, offset) / mem::size_of::() + fn addr_offset_in_dwords(starting_bus: u8, address: PciAddress, offset: u16) -> usize { + Self::addr_offset_in_bytes(starting_bus, address, offset) / mem::size_of::() } - unsafe fn with_pointer) -> T>(&self, bus: u8, dev: u8, func: u8, offset: u16, f: F) -> T { - let (base_address_phys, starting_bus) = match self.mcfgs.at_bus(bus) { + unsafe fn with_pointer) -> T>( + &self, + address: PciAddress, + offset: u16, + f: F, + ) -> T { + let (base_address_phys, starting_bus) = match self.mcfgs.at_bus(address.bus()) { Some(t) => (t.base_addr, t.start_bus), None => return f(None), }; let mut maps_lock = self.maps.lock().unwrap(); - let virt_pointer = maps_lock.entry((bus, dev, func)).or_insert_with(|| { + let virt_pointer = maps_lock.entry(address).or_insert_with(|| { common::physmap( - base_address_phys as usize + Self::addr_offset_in_bytes(starting_bus, bus, dev, func, 0), + base_address_phys as usize + Self::addr_offset_in_bytes(starting_bus, address, 0), PAGE_SIZE, - common::Prot { read: true, write: true }, + common::Prot { + read: true, + write: true, + }, common::MemoryType::Uncacheable, - ).unwrap_or_else(|error| { - panic!("failed to physmap pcie configuration space for {:2x}:{:2x}.{:2x}: {:?}", bus, dev, func, error) + ) + .unwrap_or_else(|error| { + panic!( + "failed to physmap pcie configuration space for {}: {:?}", + address, error + ) }) as *mut u32 }); - f(Some(&mut *virt_pointer.offset((offset as usize / mem::size_of::()) as isize))) + f(Some(&mut *virt_pointer.offset( + (offset as usize / mem::size_of::()) as isize, + ))) } pub fn buses<'pcie>(&'pcie self) -> PciIter<'pcie> { PciIter::new(self) @@ -189,26 +217,25 @@ impl Pcie { } impl CfgAccess for Pcie { - unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { - self.with_pointer(bus, dev, func, offset, |pointer| match pointer { + unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 { + let _guard = self.lock.lock().unwrap(); + + self.with_pointer(address, offset, |pointer| match pointer { Some(address) => ptr::read_volatile::(address), - None => self.fallback.read(bus, dev, func, offset), + None => self.fallback.read(address, offset), }) } - unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 { + + unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) { let _guard = self.lock.lock().unwrap(); - self.read_nolock(bus, dev, func, offset) - } - unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { - self.with_pointer(bus, dev, func, offset, |pointer| match pointer { + + self.with_pointer(address, offset, |pointer| match pointer { Some(address) => ptr::write_volatile::(address, value), - None => { self.fallback.read(bus, dev, func, offset); } + None => { + self.fallback.write(address, offset, value); + } }); } - unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) { - let _guard = self.lock.lock().unwrap(); - self.write_nolock(bus, dev, func, offset, value); - } } impl Drop for Pcie {