Parse some of the PCI capabilities, and setup IPC.

This commit is contained in:
4lDO2
2020-03-14 12:13:47 +01:00
parent bd860de51e
commit 7536048ceb
17 changed files with 626 additions and 34 deletions
Generated
+5 -11
View File
@@ -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)" = "<none>"
"checksum tokio-udp 0.1.3 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
"checksum tokio-uds 0.2.4 (git+https://gitlab.redox-os.org/redox-os/tokio)" = "<none>"
"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"
+3 -1
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
/target
+14 -4
View File
@@ -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"
+5 -2
View File
@@ -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<DriverConfig>
pub drivers: Vec<DriverConfig>,
}
#[derive(Debug, Default, Deserialize)]
@@ -16,5 +18,6 @@ pub struct DriverConfig {
pub vendor: Option<u16>,
pub device: Option<u16>,
pub device_id_range: Option<Range<u16>>,
pub command: Option<Vec<String>>
pub command: Option<Vec<String>>,
pub channel_name: Option<String>,
}
+132
View File
@@ -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<PciCapabilitiy>,
}
#[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<T, E = PcidClientHandleError> = std::result::Result<T, E>;
#[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: Write, T: Serialize>(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: Read, T: DeserializeOwned>(r: &mut R) -> Result<T> {
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<Self> {
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<Self> {
let pcid_to_client_fd = env::var("PCID_TO_CLIENT_FD")?.parse::<RawFd>().map_err(PcidClientHandleError::EnvValidityError)?;
let pcid_from_client_fd = env::var("PCID_FROM_CLIENT_FD")?.parse::<RawFd>().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<PcidClientResponse> {
recv(&mut self.pcid_to_client)
}
pub fn fetch_config(&mut self) -> Result<SubdriverArguments> {
self.send(&PcidClientRequest::RequestConfig)?;
match self.recv()? {
PcidClientResponse::Config(a) => Ok(a),
}
}
}
+7
View File
@@ -0,0 +1,7 @@
//! Interface to `pcid`.
#![feature(asm)]
mod driver_interface;
mod pci;
pub use driver_interface::*;
+72 -12
View File
@@ -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<usize>, pcid_from_client_read: Option<usize>, 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() {
+3 -1
View File
@@ -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),
+3
View File
@@ -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> {
+344
View File
@@ -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<Self::Item> {
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<R: ConfigReader>(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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<R: ConfigReader>(reader: &R, offset: u8) -> Self {
Self::Msi(MsiCapability::parse(reader, offset))
}
unsafe fn parse_msix<R: ConfigReader>(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<R: ConfigReader>(reader: &R, offset: u8) -> Self {
// TODO
Self::Pcie(PcieCapability {})
}
unsafe fn parse<R: ConfigReader>(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<Self::Item> {
let offset = self.inner.next()?;
Some(unsafe { Capability::parse(self.inner.reader, offset) })
}
}
+3
View File
@@ -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> {
+20 -1
View File
@@ -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<u8> {
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);
}
}
+3 -2
View File
@@ -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::<Vec<_>>());
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];
+1
View File
@@ -7,6 +7,7 @@ pub use self::header::{PciHeader, PciHeaderError, PciHeaderType};
mod bar;
mod bus;
pub mod cap;
mod class;
mod dev;
mod func;
+1
View File
@@ -23,3 +23,4 @@ serde_json = "1"
smallvec = { version = "1", features = ["serde"] }
thiserror = "1"
toml = "0.5"
pcid = { path = "../pcid" }
+9
View File
@@ -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");