From 7536048ceb5115c5fe883bb6824e9aa48a070b7f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 14 Mar 2020 12:13:47 +0100 Subject: [PATCH] Parse some of the PCI capabilities, and setup IPC. --- Cargo.lock | 16 +- initfs.toml | 4 +- pcid/.gitignore | 1 + pcid/Cargo.toml | 18 +- pcid/src/config.rs | 7 +- pcid/src/driver_interface.rs | 132 ++++++++++++++ pcid/src/lib.rs | 7 + pcid/src/main.rs | 84 +++++++-- pcid/src/pci/bar.rs | 4 +- pcid/src/pci/bus.rs | 3 + pcid/src/pci/cap.rs | 344 +++++++++++++++++++++++++++++++++++ pcid/src/pci/dev.rs | 3 + pcid/src/pci/func.rs | 21 ++- pcid/src/pci/header.rs | 5 +- pcid/src/pci/mod.rs | 1 + xhcid/Cargo.toml | 1 + xhcid/src/main.rs | 9 + 17 files changed, 626 insertions(+), 34 deletions(-) create mode 100644 pcid/.gitignore create mode 100644 pcid/src/driver_interface.rs create mode 100644 pcid/src/lib.rs create mode 100644 pcid/src/pci/cap.rs diff --git a/Cargo.lock b/Cargo.lock index c41a1e0178..aa3a88a748 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,10 +686,12 @@ version = "0.1.0" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1303,14 +1305,6 @@ dependencies = [ "tokio-reactor 0.1.7 (git+https://gitlab.redox-os.org/redox-os/tokio)", ] -[[package]] -name = "toml" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "toml" version = "0.5.6" @@ -1524,6 +1518,7 @@ version = "0.1.0" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pcid 0.1.0", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_event 0.1.0 (git+https://gitlab.redox-os.org/redox-os/event.git)", "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)", @@ -1671,7 +1666,6 @@ dependencies = [ "checksum tokio-timer 0.2.8 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" "checksum tokio-udp 0.1.3 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" "checksum tokio-uds 0.2.4 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" diff --git a/initfs.toml b/initfs.toml index b604eec197..6861491750 100644 --- a/initfs.toml +++ b/initfs.toml @@ -22,7 +22,7 @@ vendor = 33006 device = 48879 command = ["bgad", "$NAME", "$BAR0"] -#nvmed +# nvmed [[drivers]] name = "NVME storage" class = 1 @@ -37,9 +37,11 @@ vendor = 33006 device = 51966 command = ["vboxd", "$NAME", "$BAR0", "$BAR1", "$IRQ"] +# xhcid [[drivers]] name = "XHCI" class = 12 subclass = 3 interface = 48 command = ["xhcid", "$NAME", "$BAR0", "$IRQ"] +channel_name = "pcid-xhcid" diff --git a/pcid/.gitignore b/pcid/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/pcid/.gitignore @@ -0,0 +1 @@ +/target diff --git a/pcid/Cargo.toml b/pcid/Cargo.toml index e35303f094..a899c78fa4 100644 --- a/pcid/Cargo.toml +++ b/pcid/Cargo.toml @@ -3,10 +3,20 @@ name = "pcid" version = "0.1.0" edition = "2018" +[[bin]] +name = "pcid" +path = "src/main.rs" + +[lib] +name = "pcid_interface" +path = "src/lib.rs" + [dependencies] -bitflags = "1.0" +bitflags = "1" byteorder = "1.2" +libc = "0.2" redox_syscall = "0.1" -serde = "1.0" -serde_derive = "1.0" -toml = "0.4" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" +toml = "0.5" diff --git a/pcid/src/config.rs b/pcid/src/config.rs index 74ba8b4eaa..b57ef14dd3 100644 --- a/pcid/src/config.rs +++ b/pcid/src/config.rs @@ -1,9 +1,11 @@ use std::collections::BTreeMap; use std::ops::Range; +use serde::Deserialize; + #[derive(Debug, Default, Deserialize)] pub struct Config { - pub drivers: Vec + pub drivers: Vec, } #[derive(Debug, Default, Deserialize)] @@ -16,5 +18,6 @@ pub struct DriverConfig { pub vendor: Option, pub device: Option, pub device_id_range: Option>, - pub command: Option> + pub command: Option>, + pub channel_name: Option, } diff --git a/pcid/src/driver_interface.rs b/pcid/src/driver_interface.rs new file mode 100644 index 0000000000..7a514b8426 --- /dev/null +++ b/pcid/src/driver_interface.rs @@ -0,0 +1,132 @@ +use std::fs::{File, OpenOptions}; +use std::io::prelude::*; +use std::{env, io}; + +use std::os::unix::io::{FromRawFd, RawFd}; + +use serde::{Serialize, Deserialize, de::DeserializeOwned}; +use thiserror::Error; + +use crate::pci; + +#[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, + /// PCI Base Address Registers + pub bars: [pci::PciBar; 6], + /// BAR sizes + pub bar_sizes: [u32; 6], + /// Legacy IRQ line + // TODO: Stop using legacy IRQ lines, and physical pins, but MSI/MSI-X instead. + pub legacy_interrupt_line: u8, + /// Vendor ID + pub venid: u16, + /// Device ID + pub devid: u16, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SubdriverArguments { + pub func: PciFunction, + pub capabilities: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum PciCapabilitiy { + Msi, + MsiX, +} + +#[derive(Debug, Error)] +pub enum PcidClientHandleError { + #[error("i/o error: {0}")] + IoError(#[from] io::Error), + + #[error("JSON ser/de error: {0}")] + SerializationError(#[from] serde_json::Error), + + #[error("environment variable error: {0}")] + EnvError(#[from] env::VarError), + + #[error("malformed fd: {0}")] + EnvValidityError(std::num::ParseIntError), + + #[error("invalid response: {0:?}")] + InvalidResponse(PcidClientResponse), +} +pub type Result = std::result::Result; + +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum PcidClientRequest { + RequestConfig, +} + +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum PcidClientResponse { + Config(SubdriverArguments), +} + +// TODO: Ideally, pcid might have its own scheme, like lots of other Redox drivers, where this kind of IPC is done. Otherwise, instead of writing serde messages over +// a channel, the communication could potentially be done via mmap, using a channel +// very similar to crossbeam-channel or libstd's mpsc (except the cycle, enqueue and dequeue fields +// are stored in the same buffer). +/// A handle from a `pcid` client (e.g. `ahcid`) to `pcid`. +pub struct PcidServerHandle { + pcid_to_client: File, + pcid_from_client: File, +} + +pub(crate) fn send(w: &mut W, message: &T) -> Result<()> { + // TODO: Use bincode. + let data = serde_json::to_vec(message)?; + let length_bytes = u64::to_le_bytes(data.len() as u64); + w.write_all(&length_bytes)?; + w.write_all(&data)?; + Ok(()) +} +pub(crate) fn recv(r: &mut R) -> Result { + let mut length_bytes = [0u8; 8]; + r.read_exact(&mut length_bytes)?; + let length = u64::from_le_bytes(length_bytes); + if length > 0x100_000 { + panic!("pcid_interface: Too large buffer"); + } + let mut data = vec! [0u8; length as usize]; + r.read_exact(&mut data)?; + + Ok(serde_json::from_slice(&data)?) +} + +impl PcidServerHandle { + pub fn connect(pcid_to_client: RawFd, pcid_from_client: RawFd) -> Result { + Ok(Self { + pcid_to_client: unsafe { File::from_raw_fd(pcid_to_client) }, + pcid_from_client: unsafe { File::from_raw_fd(pcid_from_client) }, + }) + } + pub fn connect_default() -> Result { + let pcid_to_client_fd = env::var("PCID_TO_CLIENT_FD")?.parse::().map_err(PcidClientHandleError::EnvValidityError)?; + let pcid_from_client_fd = env::var("PCID_FROM_CLIENT_FD")?.parse::().map_err(PcidClientHandleError::EnvValidityError)?; + + Self::connect(pcid_to_client_fd, pcid_from_client_fd) + } + pub(crate) fn send(&mut self, req: &PcidClientRequest) -> Result<()> { + send(&mut self.pcid_from_client, req) + } + pub(crate) fn recv(&mut self) -> Result { + recv(&mut self.pcid_to_client) + } + pub fn fetch_config(&mut self) -> Result { + self.send(&PcidClientRequest::RequestConfig)?; + match self.recv()? { + PcidClientResponse::Config(a) => Ok(a), + } + } +} diff --git a/pcid/src/lib.rs b/pcid/src/lib.rs new file mode 100644 index 0000000000..e03e5bfccf --- /dev/null +++ b/pcid/src/lib.rs @@ -0,0 +1,7 @@ +//! Interface to `pcid`. + +#![feature(asm)] + +mod driver_interface; +mod pci; +pub use driver_interface::*; diff --git a/pcid/src/main.rs b/pcid/src/main.rs index 88d85e07c0..34e4876973 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -1,21 +1,24 @@ #![feature(asm)] -#[macro_use] extern crate bitflags; +extern crate bitflags; extern crate byteorder; -#[macro_use] extern crate serde_derive; extern crate syscall; extern crate toml; -use std::{env, i64}; +use std::{env, io, i64}; use std::fs::{File, metadata, read_dir}; -use std::io::Read; +use std::io::prelude::*; +use std::os::unix::io::{FromRawFd, RawFd}; use std::process::Command; use syscall::iopl; +use std::os::unix::process::CommandExt; + use crate::config::Config; use crate::pci::{Pci, PciBar, PciClass, PciHeader, PciHeaderError, PciHeaderType}; mod config; +mod driver_interface; mod pci; fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, @@ -24,14 +27,16 @@ fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, 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::Legacy if header.subclass() == 1 => string.push_str(" VGA CTL"), PciClass::Storage => match header.subclass() { 0x01 => { string.push_str(" IDE"); }, - 0x06 => { - string.push_str(" SATA"); + 0x06 => if header.interface() == 0 { + string.push_str(" SATA VND"); + } else if header.interface() == 1 { + string.push_str(" SATA AHCI"); }, _ => () }, @@ -166,6 +171,23 @@ fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, } } + let func = driver_interface::PciFunction { + bars, + bar_sizes, + bus_num, + dev_num, + func_num, + devid: header.device_id(), + legacy_interrupt_line: irq, + venid: header.vendor_id(), + }; + let capabilities = Vec::new(); + + let subdriver_args = driver_interface::SubdriverArguments { + capabilities, + func, + }; + // TODO: find a better way to pass the header data down to the // device driver, making passing the capabilities list etc // posible. @@ -199,16 +221,54 @@ fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, } 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) - }, + + let (pcid_to_client_write, pcid_from_client_read, envs) = if driver.channel_name.is_some() { + let mut fds1 = [0usize; 2]; + let mut fds2 = [0usize; 2]; + + syscall::pipe2(&mut fds1, 0).expect("pcid: failed to create pcid->client pipe"); + syscall::pipe2(&mut fds2, 0).expect("pcid: failed to create client->pcid pipe"); + + let [pcid_to_client_read, pcid_to_client_write] = fds1; + let [pcid_from_client_read, pcid_from_client_write] = fds2; + + (Some(pcid_to_client_write), Some(pcid_from_client_read), vec! [("PCID_TO_CLIENT_FD", format!("{}", pcid_to_client_read)), ("PCID_FROM_CLIENT_FD", format!("{}", pcid_from_client_write))]) + } else { + (None, None, vec! []) + }; + + match command.envs(envs).spawn() { + Ok(mut child) => { + handle_spawn(pcid_to_client_write, pcid_from_client_read, subdriver_args); + match child.wait() { + Ok(_status) => (), + Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err), + } + } Err(err) => println!("pcid: failed to execute {:?}: {}", command, err) } } } } + fn handle_spawn(pcid_to_client_write: Option, pcid_from_client_read: Option, args: driver_interface::SubdriverArguments) { + use driver_interface::*; + + // TODO: Instead of relying on the subdriver to correctly close the pipe, there should be a + // dedicated thread responsible for this. Or alternatively, a thread pool with Futures. + + if let (Some(pcid_to_client_fd), Some(pcid_from_client_fd)) = (pcid_to_client_write, pcid_from_client_read) { + let mut pcid_to_client = unsafe { File::from_raw_fd(pcid_to_client_fd as RawFd) }; + let mut pcid_from_client = unsafe { File::from_raw_fd(pcid_from_client_fd as RawFd) }; + + if let Ok(msg) = recv(&mut pcid_from_client) { + match msg { + PcidClientRequest::RequestConfig => { + send(&mut pcid_to_client, &PcidClientResponse::Config(args.clone())).unwrap(); + } + } + } + } + } } fn main() { diff --git a/pcid/src/pci/bar.rs b/pcid/src/pci/bar.rs index b1efde2e38..1ff92f9739 100644 --- a/pcid/src/pci/bar.rs +++ b/pcid/src/pci/bar.rs @@ -1,6 +1,8 @@ use std::fmt; -#[derive(Clone, Copy, Debug, PartialEq)] +use serde::{Serialize, Deserialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub enum PciBar { None, Memory(u32), diff --git a/pcid/src/pci/bus.rs b/pcid/src/pci/bus.rs index 120fa458f9..388e796e61 100644 --- a/pcid/src/pci/bus.rs +++ b/pcid/src/pci/bus.rs @@ -13,6 +13,9 @@ impl<'pci> PciBus<'pci> { pub unsafe fn read(&self, dev: u8, func: u8, offset: u8) -> u32 { self.pci.read(self.num, dev, func, offset) } + pub unsafe fn write(&self, dev: u8, func: u8, offset: u8, value: u32) { + self.pci.write(self.num, dev, func, offset, value) + } } pub struct PciBusIter<'pci> { diff --git a/pcid/src/pci/cap.rs b/pcid/src/pci/cap.rs new file mode 100644 index 0000000000..7ac6c3a07a --- /dev/null +++ b/pcid/src/pci/cap.rs @@ -0,0 +1,344 @@ +use super::func::{ConfigReader, ConfigWriter}; + +pub struct CapabilityOffsetsIter<'a, R> { + offset: u8, + reader: &'a R, +} +impl<'a, R> CapabilityOffsetsIter<'a, R> { + pub fn new(offset: u8, reader: &'a R) -> Self { + Self { + offset, + reader, + } + } +} +impl<'a, R> Iterator for CapabilityOffsetsIter<'a, R> +where + R: ConfigReader +{ + type Item = u8; + + fn next(&mut self) -> Option { + unsafe { + assert_eq!(self.offset & 0xF8, self.offset, "capability must be dword aligned"); + + if self.offset == 0 { return None }; + + let first_dword = dbg!(self.reader.read_u32(dbg!(self.offset))); + let next = ((first_dword >> 8) & 0xFF) as u8; + + let offset = self.offset; + self.offset = next; + + Some(offset) + } + } +} + +#[repr(u8)] +pub enum CapabilityId { + Msi = 0x05, + MsiX = 0x11, + Pcie = 0x10, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum MsiCapability { + _32BitAddress { + message_control: u32, + message_address: u32, + message_data: u32, + }, + _64BitAddress { + message_control: u32, + message_address_lo: u32, + message_address_hi: u32, + message_data: u32, + }, + _32BitAddressWithPvm { + message_control: u32, + message_address: u32, + message_data: u32, + mask_bits: u32, + pending_bits: u32, + }, + _64BitAddressWithPvm { + message_control: u32, + message_address_lo: u32, + message_address_hi: u32, + message_data: u32, + mask_bits: u32, + pending_bits: u32, + }, +} + +impl MsiCapability { + pub const MC_PVT_CAPABLE_BIT: u16 = 1 << 8; + pub const MC_64_BIT_ADDR_BIT: u16 = 1 << 7; + + pub const MC_MULTI_MESSAGE_MASK: u16 = 0x000E; + pub const MC_MULTI_MESSAGE_SHIFT: u8 = 1; + + pub const MC_MULTI_MESSAGE_ENABLE_MASK: u16 = 0x0070; + pub const MC_MULTI_MESSAGE_ENABLE_SHIFT: u8 = 4; + + pub const MC_MSI_ENABLED_BIT: u16 = 1; + + pub unsafe fn parse(reader: &R, offset: u8) -> Self { + let dword = reader.read_u32(offset); + + let message_control = (dword >> 16) as u16; + + if message_control & Self::MC_PVT_CAPABLE_BIT != 0 { + if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { + Self::_64BitAddressWithPvm { + message_control: dword, + message_address_lo: reader.read_u32(offset + 4), + message_address_hi: reader.read_u32(offset + 8), + message_data: reader.read_u32(offset + 12), + mask_bits: reader.read_u32(offset + 16), + pending_bits: reader.read_u32(offset + 20), + } + } else { + Self::_32BitAddressWithPvm { + message_control: dword, + message_address: reader.read_u32(offset + 4), + message_data: reader.read_u32(offset + 8), + mask_bits: reader.read_u32(offset + 12), + pending_bits: reader.read_u32(offset + 16), + } + } + } else { + if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { + Self::_64BitAddress { + message_control: dword, + message_address_lo: reader.read_u32(offset + 4), + message_address_hi: reader.read_u32(offset + 8), + message_data: reader.read_u32(offset + 12), + } + } else { + Self::_32BitAddress { + message_control: dword, + message_address: reader.read_u32(offset + 4), + message_data: reader.read_u32(offset + 8), + } + } + } + } + + fn message_control_raw(&self) -> u32 { + match self { + Self::_32BitAddress { message_control, .. } | Self::_64BitAddress { message_control, .. } | Self::_32BitAddressWithPvm { message_control, .. } | Self::_64BitAddressWithPvm { message_control, .. } => *message_control, + } + } + pub fn message_control(&self) -> u16 { + self.message_control_raw() as u16 + } + pub unsafe fn set_message_control(&mut self, writer: &mut W, offset: u8, value: u16) { + let mut new_message_control = self.message_control_raw(); + new_message_control &= 0x0000_FFFF; + new_message_control |= u32::from(value) << 16; + writer.write_u32(offset, new_message_control); + + match self { + Self::_32BitAddress { ref mut message_control, .. } + | Self::_64BitAddress { ref mut message_control, .. } + | Self::_32BitAddressWithPvm { ref mut message_control, .. } + | Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control, + } + } + pub fn is_pvt_capable(&self) -> bool { + self.message_control() & Self::MC_PVT_CAPABLE_BIT != 0 + } + pub fn has_64_bit_addr(&self) -> bool { + self.message_control() & Self::MC_64_BIT_ADDR_BIT != 0 + } + pub fn enabled(&self) -> bool { + self.message_control() & Self::MC_MSI_ENABLED_BIT != 0 + } + pub unsafe fn set_enabled(&mut self, writer: &mut W, offset: u8, enabled: bool) { + let mut new_message_control = self.message_control() & (!Self::MC_MSI_ENABLED_BIT); + new_message_control |= u16::from(enabled); + self.set_message_control(writer, offset, new_message_control) + } + pub fn multi_message_capable(&self) -> u8 { + ((self.message_control() & Self::MC_MULTI_MESSAGE_MASK) >> Self::MC_MULTI_MESSAGE_SHIFT) as u8 + } + pub fn multi_message_enabled(&self) -> u8 { + ((self.message_control() & Self::MC_MULTI_MESSAGE_ENABLE_MASK) >> Self::MC_MULTI_MESSAGE_ENABLE_MASK) as u8 + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct PcieCapability { +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct MsixCapability { + pub a: u32, + pub b: u32, + pub c: u32, +} + +impl MsixCapability { + pub const MC_MSIX_ENABLED_BIT: u16 = 1 << 15; + pub const MC_MSIX_ENABLED_SHIFT: u8 = 15; + pub const MC_FUNCTION_MASK_BIT: u16 = 1 << 14; + pub const MC_FUNCTION_MASK_SHIFT: u8 = 14; + pub const MC_TABLE_SIZE_MASK: u16 = 0x03FF; + + /// The Message Control field, containing the enabled and function mask bits, as well as the + /// table size. + pub const fn message_control(&self) -> u16 { + (self.a >> 16) as u16 + } + pub fn set_message_control(&mut self, message_control: u16) { + self.a &= 0x0000_FFFF; + self.a |= u32::from(message_control) << 16; + } + /// Returns the MSI-X table size, subtracted by one. + pub const fn table_size_raw(&self) -> u16 { + self.message_control() & Self::MC_TABLE_SIZE_MASK + } + /// Returns the MSI-X table size. + pub const fn table_size(&self) -> u16 { + self.table_size_raw() + 1 + } + /// Returns the MSI-X enabled bit, which enables MSI-X if the MSI enable bit is also set in the + /// MSI capability structure. + pub const fn msix_enabled(&self) -> bool { + self.message_control() & Self::MC_MSIX_ENABLED_BIT != 0 + } + /// The MSI-X function mask, which overrides each of the vectors' mask bit, when set. + pub const fn function_mask(&self) -> bool { + self.message_control() & Self::MC_FUNCTION_MASK_BIT != 0 + } + + pub fn set_msix_enabled(&mut self, enabled: bool) { + let mut new_message_control = self.message_control(); + new_message_control &= !(Self::MC_MSIX_ENABLED_BIT); + new_message_control |= u16::from(enabled) << Self::MC_MSIX_ENABLED_SHIFT; + self.set_message_control(new_message_control); + } + + pub fn set_function_mask(&mut self, function_mask: bool) { + let mut new_message_control = self.message_control(); + new_message_control &= !(Self::MC_FUNCTION_MASK_BIT); + new_message_control |= u16::from(function_mask) << Self::MC_FUNCTION_MASK_SHIFT; + self.set_message_control(new_message_control); + } + pub const TABLE_OFFSET_MASK: u32 = 0xFFFF_FFF8; + pub const TABLE_BIR_MASK: u32 = 0x0000_0007; + + /// The table offset is guaranteed to be QWORD aligned (8 bytes). + pub const fn table_offset(&self) -> u32 { + self.b & Self::TABLE_OFFSET_MASK + } + /// The table BIR, which is used to map the offset to a memory location. + pub const fn table_bir(&self) -> u8 { + (self.b & Self::TABLE_BIR_MASK) as u8 + } + + pub fn set_table_offset(&mut self, offset: u32) { + assert_eq!(offset & Self::TABLE_OFFSET_MASK, offset, "MSI-X table offset has to be QWORD aligned"); + self.b &= !Self::TABLE_OFFSET_MASK; + self.b |= offset; + } + pub const PBA_OFFSET_MASK: u32 = 0xFFFF_FFF8; + pub const PBA_BIR_MASK: u32 = 0x0000_0007; + + /// The Pending Bit Array offset is guaranteed to be QWORD aligned (8 bytes). + pub const fn pba_offset(&self) -> u32 { + self.b & Self::PBA_OFFSET_MASK + } + /// The Pending Bit Array BIR, which is used to map the offset to a memory location. + pub const fn pba_bir(&self) -> u8 { + (self.b & Self::PBA_BIR_MASK) as u8 + } + + pub fn set_pba_offset(&mut self, offset: u32) { + assert_eq!(offset & Self::PBA_OFFSET_MASK, offset, "MSI-X Pending Bit Array offset has to be QWORD aligned"); + self.c &= !Self::PBA_OFFSET_MASK; + self.c |= offset; + } + + /// Write the first DWORD into configuration space (containing the partially modifiable Message + /// Control field). + pub unsafe fn write_a(&self, writer: &W, offset: u8) { + writer.write_u32(offset, self.a) + } + /// Write the second DWORD into configuration space (containing the modifiable table + /// offset and the readonly table BIR). + pub unsafe fn write_b(&self, writer: &W, offset: u8) { + writer.write_u32(offset + 4, self.a) + } + /// Write the third DWORD into configuration space (containing the modifiable pending bit array + /// offset, and the readonly PBA BIR). + pub unsafe fn write_c(&self, writer: &W, offset: u8) { + writer.write_u32(offset + 8, self.a) + } + /// Write this capability structure back to configuration space. + pub unsafe fn write_all(&self, writer: &W, offset: u8) { + self.write_a(writer, offset); + self.write_b(writer, offset); + self.write_c(writer, offset); + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Capability { + Msi(MsiCapability), + MsiX(MsixCapability), + Pcie(PcieCapability), + Other(u8), +} + +impl Capability { + unsafe fn parse_msi(reader: &R, offset: u8) -> Self { + Self::Msi(MsiCapability::parse(reader, offset)) + } + unsafe fn parse_msix(reader: &R, offset: u8) -> Self { + Self::MsiX(MsixCapability { + a: reader.read_u32(offset), + b: reader.read_u32(offset + 4), + c: reader.read_u32(offset + 8), + }) + } + unsafe fn parse_pcie(reader: &R, offset: u8) -> Self { + // TODO + Self::Pcie(PcieCapability {}) + } + unsafe fn parse(reader: &R, offset: u8) -> Self { + assert_eq!(offset & 0xF8, offset, "capability must be dword aligned"); + + let dword = reader.read_u32(offset); + let capability_id = (dword & 0xFF) as u8; + + if capability_id == CapabilityId::Msi as u8 { + Self::parse_msi(reader, offset) + } else if capability_id == CapabilityId::MsiX as u8 { + Self::parse_msix(reader, offset) + } else if capability_id == CapabilityId::Pcie as u8 { + Self::parse_pcie(reader, offset) + } else { + Self::Other(capability_id) + //panic!("unimplemented or malformed capability id: {}", capability_id) + } + } +} + +pub struct CapabilitiesIter<'a, R> { + pub inner: CapabilityOffsetsIter<'a, R>, +} + +impl<'a, R> Iterator for CapabilitiesIter<'a, R> +where + R: ConfigReader +{ + type Item = Capability; + + fn next(&mut self) -> Option { + let offset = self.inner.next()?; + Some(unsafe { Capability::parse(self.inner.reader, offset) }) + } +} diff --git a/pcid/src/pci/dev.rs b/pcid/src/pci/dev.rs index 05088886e8..6d021994e5 100644 --- a/pcid/src/pci/dev.rs +++ b/pcid/src/pci/dev.rs @@ -13,6 +13,9 @@ impl<'pci> PciDev<'pci> { pub unsafe fn read(&self, func: u8, offset: u8) -> u32 { self.bus.read(self.num, func, offset) } + pub unsafe fn write(&self, func: u8, offset: u8, value: u32) { + self.bus.write(self.num, func, offset, value); + } } pub struct PciDevIter<'pci> { diff --git a/pcid/src/pci/func.rs b/pcid/src/pci/func.rs index 5c206f3607..0a3be3c874 100644 --- a/pcid/src/pci/func.rs +++ b/pcid/src/pci/func.rs @@ -2,6 +2,9 @@ use byteorder::{LittleEndian, ByteOrder}; use super::PciDev; +// TODO: PCI Express Configuration Space, which uses a flat memory buffer, rather than IN/OUT +// instructions. + pub trait ConfigReader { unsafe fn read_range(&self, offset: u8, len: u8) -> Vec { assert!(len > 3 && len % 4 == 0); @@ -17,11 +20,22 @@ pub trait ConfigReader { } unsafe fn read_u32(&self, offset: u8) -> u32; + + unsafe fn read_u8(&self, offset: u8) -> u8 { + let dword_offset = (offset / 4) * 4; + let dword = self.read_u32(dword_offset); + + let shift = (offset % 4) * 8; + ((dword >> shift) & 0xFF) as u8 + } +} +pub trait ConfigWriter { + unsafe fn write_u32(&self, offset: u8, value: u32); } pub struct PciFunc<'pci> { pub dev: &'pci PciDev<'pci>, - pub num: u8 + pub num: u8, } impl<'pci> ConfigReader for PciFunc<'pci> { @@ -29,3 +43,8 @@ impl<'pci> ConfigReader for PciFunc<'pci> { self.dev.read(self.num, offset) } } +impl<'pci> ConfigWriter for PciFunc<'pci> { + unsafe fn write_u32(&self, offset: u8, value: u32) { + self.dev.write(self.num, offset, value); + } +} diff --git a/pcid/src/pci/header.rs b/pcid/src/pci/header.rs index 272d917b1f..e4197436a9 100644 --- a/pcid/src/pci/header.rs +++ b/pcid/src/pci/header.rs @@ -3,6 +3,7 @@ use byteorder::{LittleEndian, ByteOrder}; use super::func::ConfigReader; use super::class::PciClass; use super::bar::PciBar; +use bitflags::bitflags; #[derive(Debug, PartialEq)] pub enum PciHeaderError { @@ -123,8 +124,9 @@ impl PciHeader { 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]; + println!("PCI DEVICE CAPABILITIES: {:?}", crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(cap_pointer, &reader) }.collect::>()); + let interrupt_line = bytes[44]; let interrupt_pin = bytes[45]; let min_grant = bytes[46]; @@ -158,7 +160,6 @@ impl PciHeader { 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]; diff --git a/pcid/src/pci/mod.rs b/pcid/src/pci/mod.rs index e70048fd84..8209e09a45 100644 --- a/pcid/src/pci/mod.rs +++ b/pcid/src/pci/mod.rs @@ -7,6 +7,7 @@ pub use self::header::{PciHeader, PciHeaderError, PciHeaderType}; mod bar; mod bus; +pub mod cap; mod class; mod dev; mod func; diff --git a/xhcid/Cargo.toml b/xhcid/Cargo.toml index b67f291149..0ef92d5fd7 100644 --- a/xhcid/Cargo.toml +++ b/xhcid/Cargo.toml @@ -23,3 +23,4 @@ serde_json = "1" smallvec = { version = "1", features = ["serde"] } thiserror = "1" toml = "0.5" +pcid = { path = "../pcid" } diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 2cc823886d..0de08d86e9 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -23,6 +23,15 @@ mod usb; mod xhci; fn main() { + println!("xhcid started"); + let mut pcid_handle = pcid_interface::PcidServerHandle::connect_default().expect("xhcid: failed to setup channel to pcid"); + dbg!(); + println!("XHCI from PCI config: {:?}", pcid_handle.fetch_config().expect("xhcid: failed to fetch config")); + + // Close the pipe, allowing pcid to continue. + drop(pcid_handle); + + let mut args = env::args().skip(1); let mut name = args.next().expect("xhcid: no name provided");