Parse some of the PCI capabilities, and setup IPC.
This commit is contained in:
Generated
+5
-11
@@ -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
@@ -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"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/target
|
||||
+14
-4
@@ -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
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//! Interface to `pcid`.
|
||||
|
||||
#![feature(asm)]
|
||||
|
||||
mod driver_interface;
|
||||
mod pci;
|
||||
pub use driver_interface::*;
|
||||
+72
-12
@@ -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
@@ -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),
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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) })
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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,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];
|
||||
|
||||
@@ -7,6 +7,7 @@ pub use self::header::{PciHeader, PciHeaderError, PciHeaderType};
|
||||
|
||||
mod bar;
|
||||
mod bus;
|
||||
pub mod cap;
|
||||
mod class;
|
||||
mod dev;
|
||||
mod func;
|
||||
|
||||
@@ -23,3 +23,4 @@ serde_json = "1"
|
||||
smallvec = { version = "1", features = ["serde"] }
|
||||
thiserror = "1"
|
||||
toml = "0.5"
|
||||
pcid = { path = "../pcid" }
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user