diff --git a/pcid/src/cfg_access/fallback.rs b/pcid/src/cfg_access/fallback.rs new file mode 100644 index 0000000000..90023961f7 --- /dev/null +++ b/pcid/src/cfg_access/fallback.rs @@ -0,0 +1,99 @@ +use std::cell::Cell; +use std::convert::TryFrom; +use std::sync::Mutex; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use syscall::io::{Io as _, Pio}; + +use log::info; + +use crate::pci::{CfgAccess, PciAddress}; + +pub(crate) struct Pci { + lock: Mutex<()>, +} + +impl Pci { + pub(crate) fn new() -> Self { + Self { + lock: Mutex::new(()), + } + } + + fn set_iopl() { + // The IO privilege level is per-thread, so we need to do the initialization on every thread. + thread_local! { + static IOPL_ONCE: Cell = Cell::new(false); + } + + IOPL_ONCE.with(|iopl_once| { + if !iopl_once.replace(true) { + // 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"); + } + } + }); + } + + fn address(address: PciAddress, offset: u8) -> u32 { + assert_eq!( + address.segment(), + 0, + "usage of multiple segments requires PCIe extended configuration" + ); + + assert_eq!(offset & 0xFC, offset, "pci offset is not aligned"); + + 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(&self, address: PciAddress, offset: u16) -> u32 { + let _guard = self.lock.lock().unwrap(); + + Self::set_iopl(); + + 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 write(&self, address: PciAddress, offset: u16, value: u32) { + let _guard = self.lock.lock().unwrap(); + + Self::set_iopl(); + + 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); + } +} +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +impl CfgAccess for Pci { + unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 { + let _guard = self.lock.lock().unwrap(); + todo!("Pci::CfgAccess::read on this architecture") + } + + unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32) { + let _guard = self.lock.lock().unwrap(); + todo!("Pci::CfgAccess::write on this architecture") + } +} diff --git a/pcid/src/pcie/mod.rs b/pcid/src/cfg_access/mod.rs similarity index 88% rename from pcid/src/pcie/mod.rs rename to pcid/src/cfg_access/mod.rs index a5f41014ad..0925b21d03 100644 --- a/pcid/src/pcie/mod.rs +++ b/pcid/src/cfg_access/mod.rs @@ -1,7 +1,12 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Mutex; use std::{fmt, fs, io, mem, ptr, slice}; -use crate::pci::{CfgAccess, Pci, PciAddress}; +use log::info; + +use crate::pci::{CfgAccess, PciAddress}; +use fallback::Pci; + +mod fallback; pub const MCFG_NAME: [u8; 4] = *b"MCFG"; @@ -120,14 +125,14 @@ impl fmt::Debug for Mcfg { pub struct Pcie { lock: Mutex<()>, bus_maps: Vec>, - fallback: Arc, + fallback: Pci, } unsafe impl Send for Pcie {} unsafe impl Sync for Pcie {} impl Pcie { - pub fn new(fallback: Arc) -> io::Result { - Mcfg::with(|mcfg| { + pub fn new() -> Self { + match Mcfg::with(|mcfg| { let alloc_maps = (0..=255) .map(|bus| { if let Some(alloc) = mcfg.at_bus(bus) { @@ -136,14 +141,24 @@ impl Pcie { None } }) - .collect(); + .collect::>(); Ok(Self { lock: Mutex::new(()), bus_maps: alloc_maps, - fallback, + fallback: Pci::new(), }) - }) + }) { + Ok(pcie) => pcie, + Err(error) => { + info!("Couldn't retrieve PCIe info, perhaps the kernel is not compiled with acpi? Using the PCI 3.0 configuration space instead. Error: {:?}", error); + Self { + lock: Mutex::new(()), + bus_maps: vec![], + fallback: Pci::new(), + } + } + } } unsafe fn physmap_pcie_bus(alloc: &PcieAlloc, bus: u8) -> (*mut u32, usize) { @@ -190,9 +205,11 @@ impl Pcie { "multiple segments not yet implemented" ); - let bus_addr = match self.bus_maps[address.bus() as usize] { - Some(bus_addr) => bus_addr, - None => return f(None), + assert_eq!(offset & 0xFC, offset, "pci offset is not aligned"); + + let bus_addr = match self.bus_maps.get(address.bus() as usize) { + Some(Some(bus_addr)) => bus_addr, + Some(None) | None => return f(None), }; let virt_pointer = unsafe { // FIXME use byte_add once stable diff --git a/pcid/src/main.rs b/pcid/src/main.rs index 83d7ef2ce7..5de73c48cc 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -9,16 +9,16 @@ use structopt::StructOpt; use log::{debug, error, info, warn, trace}; use redox_log::{OutputBuilder, RedoxLogger}; +use crate::cfg_access::Pcie; use crate::config::Config; -use crate::pci::{CfgAccess, Pci, PciAddress, PciBar, PciBus, PciClass, PciFunc, PciHeader, PciHeaderError, PciHeaderType}; +use crate::pci::{CfgAccess, PciAddress, PciBar, PciClass, PciFunc, PciHeader, PciHeaderError, PciHeaderType}; use crate::pci::cap::Capability as PciCapability; use crate::pci::func::{ConfigReader, ConfigWriter}; -use crate::pcie::Pcie; +mod cfg_access; mod config; mod driver_interface; mod pci; -mod pcie; #[derive(StructOpt)] #[structopt(about)] @@ -206,12 +206,11 @@ impl DriverHandler { pub struct State { threads: Mutex>>, - pci: Arc, - pcie: Option, + pcie: Pcie, } impl State { fn preferred_cfg_access(&self) -> &dyn CfgAccess { - self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess) + &self.pcie } } @@ -471,12 +470,11 @@ fn handle_parsed_header(state: Arc, config: &Config, addr: PciAddress, he state: Arc::clone(&state), capabilities, }; - thread::spawn(move || { - // RFLAGS are no longer kept in the relibc clone() implementation. - unsafe { syscall::iopl(3).expect("pcid: failed to set IOPL"); } - + let _handle = thread::spawn(move || { driver_handler.handle_spawn(pcid_to_client_write, pcid_from_client_read, subdriver_args); }); + // FIXME this currently deadlocks as pcid doesn't daemonize + //state.threads.lock().unwrap().push(handle); match child.wait() { Ok(_status) => (), Err(err) => error!("pcid: failed to wait for {:?}: {}", command, err), @@ -565,17 +563,8 @@ fn main(args: Args) { let _logger_ref = setup_logging(args.verbose); - let pci = Arc::new(Pci::new()); - let state = Arc::new(State { - pci: Arc::clone(&pci), - pcie: match Pcie::new(Arc::clone(&pci)) { - Ok(pcie) => Some(pcie), - Err(error) => { - info!("Couldn't retrieve PCIe info, perhaps the kernel is not compiled with acpi? Using the PCI 3.0 configuration space instead. Error: {:?}", error); - None - } - }, + pcie: Pcie::new(), threads: Mutex::new(Vec::new()), }); @@ -592,10 +581,10 @@ fn main(args: Args) { let bus_num = bus_nums[bus_i]; bus_i += 1; - let bus = PciBus { num: bus_num }; - 'dev: for dev in bus.devs() { - for func in dev.funcs(pci) { - let func_addr = func.addr; + 'dev: for dev_num in 0..32 { + for func_num in 0..8 { + let func_addr = PciAddress::new(0, bus_num, dev_num, func_num); + let func = PciFunc { pci, addr: func_addr }; match PciHeader::from_reader(func) { Ok(header) => { handle_parsed_header(Arc::clone(&state), &config, func_addr, header); @@ -605,7 +594,7 @@ fn main(args: Args) { } Err(PciHeaderError::NoDevice) => { if func_addr.function() == 0 { - trace!("PCI {:>02X}/{:>02X}: no dev", bus.num, dev.num); + trace!("PCI {:>02X}:{:>02X}: no dev", bus_num, dev_num); continue 'dev; } }, diff --git a/pcid/src/pci/bus.rs b/pcid/src/pci/bus.rs deleted file mode 100644 index 9900d118d9..0000000000 --- a/pcid/src/pci/bus.rs +++ /dev/null @@ -1,40 +0,0 @@ -use super::PciDev; - -#[derive(Copy, Clone)] -pub struct PciBus { - pub num: u8, -} - -impl<'pci> PciBus { - pub fn devs(self) -> PciBusIter { - PciBusIter::new(self) - } -} - -pub struct PciBusIter { - bus: PciBus, - num: u8, -} - -impl PciBusIter { - pub fn new(bus: PciBus) -> Self { - PciBusIter { bus, num: 0 } - } -} - -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, - }; - self.num += 1; - Some(dev) - } - _ => None, - } - } -} diff --git a/pcid/src/pci/cap.rs b/pcid/src/pci/cap.rs index 99fc3e09b2..5e852a528f 100644 --- a/pcid/src/pci/cap.rs +++ b/pcid/src/pci/cap.rs @@ -153,18 +153,6 @@ impl Capability { _ => None, } } - pub fn into_msi(self) -> Option { - match self { - Self::Msi(msi) => Some(msi), - _ => None, - } - } - pub fn into_msix(self) -> Option { - match self { - Self::MsiX(msix) => Some(msix), - _ => None, - } - } unsafe fn parse_msi(reader: &R, offset: u8) -> Self { Self::Msi(MsiCapability::parse(reader, offset)) } diff --git a/pcid/src/pci/dev.rs b/pcid/src/pci/dev.rs deleted file mode 100644 index b64eeb1034..0000000000 --- a/pcid/src/pci/dev.rs +++ /dev/null @@ -1,42 +0,0 @@ -use super::{CfgAccess, PciAddress, PciBus, PciFunc}; - -#[derive(Copy, Clone)] -pub struct PciDev { - pub bus: PciBus, - pub num: u8, -} - -impl<'pci> PciDev { - pub fn funcs(self, pci: &'pci dyn CfgAccess) -> PciDevIter<'pci> { - PciDevIter::new(self, pci) - } -} - -pub struct PciDevIter<'pci> { - pci: &'pci dyn CfgAccess, - dev: PciDev, - num: u8, -} - -impl<'pci> PciDevIter<'pci> { - pub fn new(dev: PciDev, pci: &'pci dyn CfgAccess) -> Self { - PciDevIter { pci, dev, num: 0 } - } -} - -impl<'pci> Iterator for PciDevIter<'pci> { - type Item = PciFunc<'pci>; - fn next(&mut self) -> Option { - match self.num { - func_num if func_num < 8 => { - let func = PciFunc { - 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 6625542db9..d6c7e93b70 100644 --- a/pcid/src/pci/func.rs +++ b/pcid/src/pci/func.rs @@ -1,6 +1,6 @@ use byteorder::{ByteOrder, LittleEndian}; -use super::{CfgAccess, PciAddress, PciDev}; +use super::{CfgAccess, PciAddress}; pub trait ConfigReader { unsafe fn read_range(&self, offset: u16, len: u16) -> Vec { diff --git a/pcid/src/pci/mod.rs b/pcid/src/pci/mod.rs index 84baf13f53..6d39cbdb8f 100644 --- a/pcid/src/pci/mod.rs +++ b/pcid/src/pci/mod.rs @@ -1,26 +1,16 @@ -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}; pub use self::bar::PciBar; -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, PciHeaderError, PciHeaderType}; -use log::info; - mod bar; -mod bus; pub mod cap; mod class; -mod dev; pub mod func; pub mod header; pub mod msi; @@ -90,114 +80,3 @@ impl fmt::Debug for PciAddress { write!(f, "{}", self) } } - -pub struct Pci { - lock: Mutex<()>, - iopl_once: Once, -} - -impl Pci { - pub fn new() -> Self { - Self { - lock: Mutex::new(()), - iopl_once: Once::new(), - } - } - - pub fn buses<'pci>(&'pci self) -> PciIter<'pci> { - PciIter::new(self) - } - - fn set_iopl() { - // 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"); - } - } - 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!( - address.segment(), - 0, - "usage of multiple segments requires PCIe extended configuration" - ); - - 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(&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(address, offset); - - Pio::::new(0xCF8).write(address); - Pio::::new(0xCFC).read() - } - - unsafe fn write(&self, address: PciAddress, offset: u16, value: 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(address, offset); - - Pio::::new(0xCF8).write(address); - Pio::::new(0xCFC).write(value); - } -} -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -impl CfgAccess for Pci { - unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 { - let _guard = self.lock.lock().unwrap(); - todo!("Pci::CfgAccess::read on this architecture") - } - - unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32) { - let _guard = self.lock.lock().unwrap(); - todo!("Pci::CfgAccess::write on this architecture") - } -} - -pub struct PciIter<'pci> { - pci: &'pci dyn CfgAccess, - num: Option, -} - -impl<'pci> PciIter<'pci> { - pub fn new(pci: &'pci dyn CfgAccess) -> Self { - PciIter { pci, num: Some(0) } - } -} - -impl<'pci> Iterator for PciIter<'pci> { - type Item = PciBus; - fn next(&mut self) -> Option { - match self.num { - Some(bus_num) => { - let bus = PciBus { num: bus_num }; - self.num = bus_num.checked_add(1); - Some(bus) - } - None => None, - } - } -}