Merge branch 'pcid-scheme2' into 'master'
Introduce a pci scheme and move driver spawning to pcid-spawner See merge request redox-os/drivers!240
This commit is contained in:
Generated
+40
-15
@@ -32,7 +32,7 @@ dependencies = [
|
||||
"parking_lot 0.12.3",
|
||||
"plain",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
"ron",
|
||||
@@ -52,7 +52,7 @@ dependencies = [
|
||||
"log",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -180,7 +180,7 @@ dependencies = [
|
||||
"fdt 0.2.0-alpha1",
|
||||
"libredox",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -405,7 +405,7 @@ dependencies = [
|
||||
"inputd",
|
||||
"libredox",
|
||||
"log",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
@@ -414,7 +414,7 @@ name = "driver-network"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
@@ -449,7 +449,7 @@ dependencies = [
|
||||
"orbclient",
|
||||
"ransid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -465,7 +465,7 @@ dependencies = [
|
||||
"orbclient",
|
||||
"ransid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -680,7 +680,7 @@ dependencies = [
|
||||
"log",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -720,7 +720,7 @@ dependencies = [
|
||||
"log",
|
||||
"orbclient",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
@@ -783,7 +783,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"libredox",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -857,7 +857,7 @@ dependencies = [
|
||||
"partitionlib",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
"smallvec 1.13.2",
|
||||
@@ -968,8 +968,23 @@ dependencies = [
|
||||
"pico-args",
|
||||
"plain",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.4.0",
|
||||
"redox_syscall",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcid-spawner"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"common",
|
||||
"log",
|
||||
"pcid",
|
||||
"pico-args",
|
||||
"redox_syscall",
|
||||
"serde",
|
||||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
@@ -1123,6 +1138,16 @@ dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox-scheme"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28d292981c8f3338cb772b6024b08bcc597d0dd81020de17080a9a7b470ebb2"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_event"
|
||||
version = "0.4.1"
|
||||
@@ -1135,9 +1160,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
@@ -1577,7 +1602,7 @@ dependencies = [
|
||||
"libredox",
|
||||
"plain",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
"xhcid",
|
||||
@@ -1652,7 +1677,7 @@ dependencies = [
|
||||
"partitionlib",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_syscall",
|
||||
"spin",
|
||||
"static_assertions",
|
||||
|
||||
@@ -4,6 +4,7 @@ members = [
|
||||
"common",
|
||||
"hwd",
|
||||
"pcid",
|
||||
"pcid-spawner",
|
||||
"vboxd",
|
||||
"xhcid",
|
||||
"usbctl",
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "pcid-spawner"
|
||||
version = "0.1.0"
|
||||
authors = ["4lDO2 <4lDO2@protonmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
log = "0.4"
|
||||
pico-args = "0.5"
|
||||
redox_syscall = "0.5.9"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
|
||||
common = { path = "../common" }
|
||||
pcid = { path = "../pcid" }
|
||||
@@ -0,0 +1,102 @@
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use pcid_interface::config::Config;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
let config_path = args
|
||||
.free_from_str::<String>()
|
||||
.expect("failed to parse --config argument");
|
||||
|
||||
common::setup_logging(
|
||||
"bus",
|
||||
"pci",
|
||||
"pci-spawner.log",
|
||||
log::LevelFilter::Info,
|
||||
log::LevelFilter::Trace,
|
||||
);
|
||||
|
||||
let config_data = if fs::metadata(&config_path)?.is_file() {
|
||||
fs::read_to_string(&config_path)?
|
||||
} else {
|
||||
let mut config_data = String::new();
|
||||
for path in fs::read_dir(&config_path)? {
|
||||
if let Ok(tmp) = fs::read_to_string(path.unwrap().path()) {
|
||||
config_data.push_str(&tmp);
|
||||
}
|
||||
}
|
||||
config_data
|
||||
};
|
||||
let config: Config = toml::from_str(&config_data)?;
|
||||
|
||||
for entry in fs::read_dir("/scheme/pci")? {
|
||||
let entry = entry.context("failed to get entry")?;
|
||||
let device_path = entry.path();
|
||||
log::trace!("ENTRY: {}", device_path.to_string_lossy());
|
||||
|
||||
let mut handle = match PciFunctionHandle::connect_by_path(&device_path) {
|
||||
Ok(handle) => handle,
|
||||
Err(err) => {
|
||||
// Either the device is gone or it is already in-use by a driver.
|
||||
log::debug!(
|
||||
"pcid-spawner: {} already in use: {err}",
|
||||
device_path.display(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let full_device_id = handle.config().func.full_device_id;
|
||||
|
||||
log::debug!(
|
||||
"pcid-spawner enumerated: PCI {} {}",
|
||||
handle.config().func.addr,
|
||||
full_device_id.display()
|
||||
);
|
||||
|
||||
let Some(driver) = config
|
||||
.drivers
|
||||
.iter()
|
||||
.find(|driver| driver.match_function(&full_device_id))
|
||||
else {
|
||||
log::debug!("no driver for {}, continuing", handle.config().func.addr);
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut args = driver.command.iter();
|
||||
|
||||
let program = args
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("driver configuration entry did not have any command!"))?;
|
||||
let program = if program.starts_with('/') {
|
||||
program.to_owned()
|
||||
} else {
|
||||
"/usr/lib/drivers/".to_owned() + program
|
||||
};
|
||||
|
||||
let mut command = Command::new(program);
|
||||
command.args(args);
|
||||
|
||||
log::info!("pcid-spawner: spawn {:?}", command);
|
||||
|
||||
handle.enable_device()?;
|
||||
|
||||
let channel_fd = handle.into_inner_fd();
|
||||
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
||||
|
||||
match command.status() {
|
||||
Ok(status) if !status.success() => {
|
||||
log::error!("pcid-spawner: driver {command:?} failed with {status}");
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(err) => log::error!("pcid-spawner: failed to execute {command:?}: {err}"),
|
||||
}
|
||||
syscall::close(channel_fd as usize).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+2
-1
@@ -20,9 +20,10 @@ pci_types = "0.10"
|
||||
pico-args = { version = "0.5", features = ["combined-flags"] }
|
||||
plain = "0.2"
|
||||
redox-daemon = "0.1"
|
||||
redox-scheme = "0.4"
|
||||
redox_syscall = "0.5.9"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
toml = "0.5"
|
||||
|
||||
common = { path = "../common" }
|
||||
libredox = "0.1.3"
|
||||
|
||||
+40
-117
@@ -1,121 +1,60 @@
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use log::{error, info};
|
||||
use pci_types::capability::{MultipleMessageSupport, PciCapability};
|
||||
use pci_types::{ConfigRegionAccess, PciAddress};
|
||||
use pci_types::{ConfigRegionAccess, EndpointHeader};
|
||||
use pcid_interface::PciFunction;
|
||||
|
||||
use crate::State;
|
||||
use crate::cfg_access::Pcie;
|
||||
|
||||
pub struct DriverHandler {
|
||||
addr: PciAddress,
|
||||
pub struct DriverHandler<'a> {
|
||||
func: PciFunction,
|
||||
endpoint_header: &'a mut EndpointHeader,
|
||||
capabilities: Vec<PciCapability>,
|
||||
|
||||
state: Arc<State>,
|
||||
pcie: &'a Pcie,
|
||||
}
|
||||
|
||||
impl DriverHandler {
|
||||
pub fn spawn(
|
||||
state: Arc<State>,
|
||||
func: pcid_interface::PciFunction,
|
||||
impl<'a> DriverHandler<'a> {
|
||||
pub fn new(
|
||||
func: PciFunction,
|
||||
endpoint_header: &'a mut EndpointHeader,
|
||||
capabilities: Vec<PciCapability>,
|
||||
args: &[String],
|
||||
) {
|
||||
let subdriver_args = pcid_interface::SubdriverArguments { func };
|
||||
|
||||
let mut args = args.iter();
|
||||
if let Some(program) = args.next() {
|
||||
let program = if program.starts_with('/') {
|
||||
program.to_owned()
|
||||
} else {
|
||||
"/usr/lib/drivers/".to_owned() + program
|
||||
};
|
||||
let mut command = Command::new(program);
|
||||
for arg in args {
|
||||
if arg.starts_with("$") {
|
||||
panic!("support for $VARIABLE has been removed. use pcid_interface instead");
|
||||
}
|
||||
command.arg(arg);
|
||||
}
|
||||
|
||||
info!("PCID SPAWN {:?}", command);
|
||||
|
||||
// TODO: libc wrapper?
|
||||
let [fds1, fds2] = unsafe {
|
||||
let mut fds1 = [0 as libc::c_int; 2];
|
||||
let mut fds2 = [0 as libc::c_int; 2];
|
||||
|
||||
assert_eq!(
|
||||
libc::pipe(fds1.as_mut_ptr()),
|
||||
0,
|
||||
"pcid: failed to create pcid->client pipe"
|
||||
);
|
||||
assert_eq!(
|
||||
libc::pipe(fds2.as_mut_ptr()),
|
||||
0,
|
||||
"pcid: failed to create client->pcid pipe"
|
||||
);
|
||||
|
||||
[fds1.map(|c| c as usize), fds2.map(|c| c as usize)]
|
||||
};
|
||||
|
||||
let [pcid_to_client_read, pcid_to_client_write] = fds1;
|
||||
let [pcid_from_client_read, pcid_from_client_write] = fds2;
|
||||
|
||||
let envs = vec![
|
||||
("PCID_TO_CLIENT_FD", format!("{}", pcid_to_client_read)),
|
||||
("PCID_FROM_CLIENT_FD", format!("{}", pcid_from_client_write)),
|
||||
];
|
||||
|
||||
match command.envs(envs).spawn() {
|
||||
Ok(mut child) => {
|
||||
let driver_handler = DriverHandler {
|
||||
addr: func.addr,
|
||||
state: state.clone(),
|
||||
capabilities,
|
||||
};
|
||||
let handle = thread::spawn(move || {
|
||||
driver_handler.handle_spawn(
|
||||
pcid_to_client_write,
|
||||
pcid_from_client_read,
|
||||
subdriver_args,
|
||||
);
|
||||
});
|
||||
state.threads.lock().unwrap().push(handle);
|
||||
match child.wait() {
|
||||
Ok(_status) => (),
|
||||
Err(err) => error!("pcid: failed to wait for {:?}: {}", command, err),
|
||||
}
|
||||
}
|
||||
Err(err) => error!("pcid: failed to execute {:?}: {}", command, err),
|
||||
}
|
||||
pcie: &'a Pcie,
|
||||
) -> Self {
|
||||
DriverHandler {
|
||||
func,
|
||||
endpoint_header,
|
||||
capabilities,
|
||||
pcie,
|
||||
}
|
||||
}
|
||||
|
||||
fn respond(
|
||||
pub fn respond(
|
||||
&mut self,
|
||||
request: pcid_interface::PcidClientRequest,
|
||||
args: &pcid_interface::SubdriverArguments,
|
||||
) -> pcid_interface::PcidClientResponse {
|
||||
use pcid_interface::*;
|
||||
|
||||
#[forbid(non_exhaustive_omitted_patterns)]
|
||||
match request {
|
||||
PcidClientRequest::EnableDevice => {
|
||||
self.func.legacy_interrupt_line =
|
||||
crate::enable_function(&self.pcie, &mut self.endpoint_header);
|
||||
|
||||
PcidClientResponse::EnabledDevice
|
||||
}
|
||||
PcidClientRequest::RequestVendorCapabilities => PcidClientResponse::VendorCapabilities(
|
||||
self.capabilities
|
||||
.iter()
|
||||
.filter_map(|capability| match capability {
|
||||
PciCapability::Vendor(addr) => unsafe {
|
||||
Some(VendorSpecificCapability::parse(*addr, &self.state.pcie))
|
||||
Some(VendorSpecificCapability::parse(*addr, self.pcie))
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
PcidClientRequest::RequestConfig => PcidClientResponse::Config(args.clone()),
|
||||
PcidClientRequest::RequestConfig => {
|
||||
PcidClientResponse::Config(SubdriverArguments { func: self.func })
|
||||
}
|
||||
PcidClientRequest::RequestFeatures => PcidClientResponse::AllFeatures(
|
||||
self.capabilities
|
||||
.iter()
|
||||
@@ -139,7 +78,7 @@ impl DriverHandler {
|
||||
{
|
||||
// If MSI-X is supported disable it before enabling MSI as they can't be
|
||||
// active at the same time.
|
||||
msix_capability.set_enabled(false, &self.state.pcie);
|
||||
msix_capability.set_enabled(false, self.pcie);
|
||||
}
|
||||
|
||||
let capability = match self.capabilities.iter_mut().find_map(|capability| {
|
||||
@@ -155,7 +94,7 @@ impl DriverHandler {
|
||||
)
|
||||
}
|
||||
};
|
||||
capability.set_enabled(true, &self.state.pcie);
|
||||
capability.set_enabled(true, self.pcie);
|
||||
PcidClientResponse::FeatureEnabled(feature)
|
||||
}
|
||||
PciFeature::MsiX => {
|
||||
@@ -169,7 +108,7 @@ impl DriverHandler {
|
||||
{
|
||||
// If MSI is supported disable it before enabling MSI-X as they can't be
|
||||
// active at the same time.
|
||||
msi_capability.set_enabled(false, &self.state.pcie);
|
||||
msi_capability.set_enabled(false, self.pcie);
|
||||
}
|
||||
|
||||
let capability = match self.capabilities.iter_mut().find_map(|capability| {
|
||||
@@ -185,7 +124,7 @@ impl DriverHandler {
|
||||
)
|
||||
}
|
||||
};
|
||||
capability.set_enabled(true, &self.state.pcie);
|
||||
capability.set_enabled(true, self.pcie);
|
||||
PcidClientResponse::FeatureEnabled(feature)
|
||||
}
|
||||
}
|
||||
@@ -268,7 +207,7 @@ impl DriverHandler {
|
||||
)
|
||||
}
|
||||
},
|
||||
&self.state.pcie,
|
||||
self.pcie,
|
||||
);
|
||||
}
|
||||
if let Some(message_addr_and_data) = info_to_set.message_address_and_data {
|
||||
@@ -279,7 +218,7 @@ impl DriverHandler {
|
||||
);
|
||||
}
|
||||
if message_addr_and_data.data
|
||||
& ((1 << info.multiple_message_enable(&self.state.pcie) as u8) - 1)
|
||||
& ((1 << info.multiple_message_enable(self.pcie) as u8) - 1)
|
||||
!= 0
|
||||
{
|
||||
return PcidClientResponse::Error(
|
||||
@@ -292,11 +231,11 @@ impl DriverHandler {
|
||||
.data
|
||||
.try_into()
|
||||
.expect("pcid: MSI message data too big"),
|
||||
&self.state.pcie,
|
||||
self.pcie,
|
||||
);
|
||||
}
|
||||
if let Some(mask_bits) = info_to_set.mask_bits {
|
||||
info.set_message_mask(mask_bits, &self.state.pcie);
|
||||
info.set_message_mask(mask_bits, self.pcie);
|
||||
}
|
||||
PcidClientResponse::SetFeatureInfo(PciFeature::Msi)
|
||||
} else {
|
||||
@@ -315,7 +254,7 @@ impl DriverHandler {
|
||||
})
|
||||
{
|
||||
if let Some(mask) = function_mask {
|
||||
info.set_function_mask(mask, &self.state.pcie);
|
||||
info.set_function_mask(mask, self.pcie);
|
||||
}
|
||||
PcidClientResponse::SetFeatureInfo(PciFeature::MsiX)
|
||||
} else {
|
||||
@@ -327,32 +266,16 @@ impl DriverHandler {
|
||||
_ => unreachable!(),
|
||||
},
|
||||
PcidClientRequest::ReadConfig(offset) => {
|
||||
let value = unsafe { self.state.pcie.read(self.addr, offset) };
|
||||
let value = unsafe { self.pcie.read(self.func.addr, offset) };
|
||||
return PcidClientResponse::ReadConfig(value);
|
||||
}
|
||||
PcidClientRequest::WriteConfig(offset, value) => {
|
||||
unsafe {
|
||||
self.state.pcie.write(self.addr, offset, value);
|
||||
self.pcie.write(self.func.addr, offset, value);
|
||||
}
|
||||
return PcidClientResponse::WriteConfig;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn handle_spawn(
|
||||
mut self,
|
||||
pcid_to_client_write: usize,
|
||||
pcid_from_client_read: usize,
|
||||
args: pcid_interface::SubdriverArguments,
|
||||
) {
|
||||
use pcid_interface::*;
|
||||
|
||||
let mut pcid_to_client = unsafe { File::from_raw_fd(pcid_to_client_write as RawFd) };
|
||||
let mut pcid_from_client = unsafe { File::from_raw_fd(pcid_from_client_read as RawFd) };
|
||||
|
||||
while let Ok(msg) = recv(&mut pcid_from_client) {
|
||||
let response = self.respond(msg, &args);
|
||||
send(&mut pcid_to_client, &response).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ 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: Vec<String>,
|
||||
}
|
||||
|
||||
impl DriverConfig {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::ptr::NonNull;
|
||||
use std::{env, io};
|
||||
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -240,6 +240,7 @@ pub enum SetFeatureInfo {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum PcidClientRequest {
|
||||
EnableDevice,
|
||||
RequestConfig,
|
||||
RequestFeatures,
|
||||
RequestVendorCapabilities,
|
||||
@@ -260,6 +261,7 @@ pub enum PcidServerResponseError {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum PcidClientResponse {
|
||||
EnabledDevice,
|
||||
Config(SubdriverArguments),
|
||||
AllFeatures(Vec<PciFeature>),
|
||||
VendorCapabilities(Vec<VendorSpecificCapability>),
|
||||
@@ -277,14 +279,9 @@ pub struct MappedBar {
|
||||
pub bar_size: usize,
|
||||
}
|
||||
|
||||
// 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 as the actual data).
|
||||
/// A handle from a `pcid` client (e.g. `ahcid`) to `pcid`.
|
||||
pub struct PciFunctionHandle {
|
||||
pcid_to_client: File,
|
||||
pcid_from_client: File,
|
||||
channel: File,
|
||||
config: SubdriverArguments,
|
||||
mapped_bars: [Option<MappedBar>; 6],
|
||||
}
|
||||
@@ -293,9 +290,7 @@ pub struct PciFunctionHandle {
|
||||
pub fn send<W: Write, T: Serialize>(w: &mut W, message: &T) -> Result<()> {
|
||||
let mut data = Vec::new();
|
||||
bincode::serialize_into(&mut data, message)?;
|
||||
let length_bytes = u64::to_le_bytes(data.len() as u64);
|
||||
w.write_all(&length_bytes)?;
|
||||
w.write_all(&data)?;
|
||||
assert_eq!(w.write(&data)?, data.len());
|
||||
Ok(())
|
||||
}
|
||||
#[doc(hidden)]
|
||||
@@ -314,42 +309,69 @@ pub fn recv<R: Read, T: DeserializeOwned>(r: &mut R) -> Result<T> {
|
||||
|
||||
impl PciFunctionHandle {
|
||||
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")?
|
||||
let channel_fd = env::var("PCID_CLIENT_CHANNEL")?
|
||||
.parse::<RawFd>()
|
||||
.map_err(PcidClientHandleError::EnvValidityError)?;
|
||||
Self::connect_common(channel_fd)
|
||||
}
|
||||
|
||||
let mut pcid_to_client = unsafe { File::from_raw_fd(pcid_to_client_fd) };
|
||||
let mut pcid_from_client = unsafe { File::from_raw_fd(pcid_from_client_fd) };
|
||||
pub fn connect_by_path(device_path: &Path) -> Result<Self> {
|
||||
let channel_fd = syscall::open(
|
||||
device_path.join("channel").to_str().unwrap(),
|
||||
syscall::O_RDWR,
|
||||
)
|
||||
.map_err(|err| {
|
||||
PcidClientHandleError::IoError(io::Error::other(format!(
|
||||
"failed to open pcid channel: {}",
|
||||
err
|
||||
)))
|
||||
})?;
|
||||
Self::connect_common(channel_fd as RawFd)
|
||||
}
|
||||
|
||||
send(&mut pcid_from_client, &PcidClientRequest::RequestConfig)?;
|
||||
let config = match recv(&mut pcid_to_client)? {
|
||||
fn connect_common(
|
||||
channel_fd: i32,
|
||||
) -> std::result::Result<PciFunctionHandle, PcidClientHandleError> {
|
||||
let mut channel = unsafe { File::from_raw_fd(channel_fd) };
|
||||
|
||||
send(&mut channel, &PcidClientRequest::RequestConfig)?;
|
||||
let config = match recv(&mut channel)? {
|
||||
PcidClientResponse::Config(a) => a,
|
||||
other => return Err(PcidClientHandleError::InvalidResponse(other)),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
pcid_to_client,
|
||||
pcid_from_client,
|
||||
channel,
|
||||
config,
|
||||
mapped_bars: [const { None }; 6],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_inner_fd(self) -> RawFd {
|
||||
self.channel.into_raw_fd()
|
||||
}
|
||||
|
||||
fn send(&mut self, req: &PcidClientRequest) -> Result<()> {
|
||||
send(&mut self.pcid_from_client, req)
|
||||
send(&mut self.channel, req)
|
||||
}
|
||||
fn recv(&mut self) -> Result<PcidClientResponse> {
|
||||
recv(&mut self.pcid_to_client)
|
||||
recv(&mut self.channel)
|
||||
}
|
||||
|
||||
pub fn config(&self) -> SubdriverArguments {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
pub fn enable_device(&mut self) -> Result<()> {
|
||||
self.send(&PcidClientRequest::EnableDevice)?;
|
||||
match self.recv()? {
|
||||
PcidClientResponse::EnabledDevice => Ok(()),
|
||||
other => Err(PcidClientHandleError::InvalidResponse(other)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vendor_capabilities(&mut self) -> Result<Vec<VendorSpecificCapability>> {
|
||||
self.send(&PcidClientRequest::RequestVendorCapabilities)?;
|
||||
|
||||
match self.recv()? {
|
||||
PcidClientResponse::VendorCapabilities(a) => Ok(a),
|
||||
other => Err(PcidClientHandleError::InvalidResponse(other)),
|
||||
|
||||
+120
-138
@@ -1,127 +1,117 @@
|
||||
#![feature(array_chunks)]
|
||||
#![feature(non_exhaustive_omitted_patterns_lint)]
|
||||
#![feature(iter_next_chunk)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
use std::fs::{metadata, read_dir, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use log::{debug, info, trace, warn};
|
||||
use pci_types::capability::PciCapability;
|
||||
use pci_types::{
|
||||
Bar as TyBar, CommandRegister, EndpointHeader, HeaderType, PciAddress,
|
||||
PciHeader as TyPciHeader, PciPciBridgeHeader,
|
||||
};
|
||||
use redox_scheme::{RequestKind, SignalBehavior};
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
use pcid_interface::{config::Config, FullDeviceId, LegacyInterruptLine, PciBar};
|
||||
use pcid_interface::{FullDeviceId, LegacyInterruptLine, PciBar, PciFunction};
|
||||
|
||||
mod cfg_access;
|
||||
mod driver_handler;
|
||||
mod scheme;
|
||||
|
||||
pub struct State {
|
||||
threads: Mutex<Vec<thread::JoinHandle<()>>>,
|
||||
pcie: Pcie,
|
||||
pub struct Func {
|
||||
inner: PciFunction,
|
||||
|
||||
capabilities: Vec<PciCapability>,
|
||||
endpoint_header: EndpointHeader,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
fn handle_parsed_header(
|
||||
state: Arc<State>,
|
||||
config: &Config,
|
||||
mut endpoint_header: EndpointHeader,
|
||||
pcie: &Pcie,
|
||||
tree: &mut BTreeMap<PciAddress, Func>,
|
||||
endpoint_header: EndpointHeader,
|
||||
full_device_id: FullDeviceId,
|
||||
) {
|
||||
for driver in config.drivers.iter() {
|
||||
if !driver.match_function(&full_device_id) {
|
||||
let mut bars = [PciBar::None; 6];
|
||||
let mut skip = false;
|
||||
for i in 0..6 {
|
||||
if skip {
|
||||
skip = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(ref args) = driver.command else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut bars = [PciBar::None; 6];
|
||||
let mut skip = false;
|
||||
for i in 0..6 {
|
||||
if skip {
|
||||
skip = false;
|
||||
continue;
|
||||
}
|
||||
match endpoint_header.bar(i, &state.pcie) {
|
||||
Some(TyBar::Io { port }) => {
|
||||
bars[i as usize] = PciBar::Port(port.try_into().unwrap())
|
||||
}
|
||||
Some(TyBar::Memory32 {
|
||||
address,
|
||||
match endpoint_header.bar(i, pcie) {
|
||||
Some(TyBar::Io { port }) => bars[i as usize] = PciBar::Port(port.try_into().unwrap()),
|
||||
Some(TyBar::Memory32 {
|
||||
address,
|
||||
size,
|
||||
prefetchable: _,
|
||||
}) => {
|
||||
bars[i as usize] = PciBar::Memory32 {
|
||||
addr: address,
|
||||
size,
|
||||
prefetchable: _,
|
||||
}) => {
|
||||
bars[i as usize] = PciBar::Memory32 {
|
||||
addr: address,
|
||||
size,
|
||||
}
|
||||
}
|
||||
Some(TyBar::Memory64 {
|
||||
address,
|
||||
}
|
||||
Some(TyBar::Memory64 {
|
||||
address,
|
||||
size,
|
||||
prefetchable: _,
|
||||
}) => {
|
||||
bars[i as usize] = PciBar::Memory64 {
|
||||
addr: address,
|
||||
size,
|
||||
prefetchable: _,
|
||||
}) => {
|
||||
bars[i as usize] = PciBar::Memory64 {
|
||||
addr: address,
|
||||
size,
|
||||
};
|
||||
skip = true; // Each 64bit memory BAR occupies two slots
|
||||
}
|
||||
None => bars[i as usize] = PciBar::None,
|
||||
};
|
||||
skip = true; // Each 64bit memory BAR occupies two slots
|
||||
}
|
||||
None => bars[i as usize] = PciBar::None,
|
||||
}
|
||||
}
|
||||
|
||||
let mut string = String::new();
|
||||
for (i, bar) in bars.iter().enumerate() {
|
||||
if !bar.is_none() {
|
||||
string.push_str(&format!(" {i}={}", bar.display()));
|
||||
}
|
||||
let mut string = String::new();
|
||||
for (i, bar) in bars.iter().enumerate() {
|
||||
if !bar.is_none() {
|
||||
string.push_str(&format!(" {i}={}", bar.display()));
|
||||
}
|
||||
}
|
||||
|
||||
if !string.is_empty() {
|
||||
info!(" BAR{}", string);
|
||||
}
|
||||
if !string.is_empty() {
|
||||
info!(" BAR{}", string);
|
||||
}
|
||||
|
||||
let legacy_interrupt_line = enable_function(&state, &mut endpoint_header);
|
||||
let capabilities = if endpoint_header.status(pcie).has_capability_list() {
|
||||
endpoint_header.capabilities(pcie).collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
debug!(
|
||||
"PCI DEVICE CAPABILITIES for {}: {:?}",
|
||||
endpoint_header.header().address(),
|
||||
capabilities
|
||||
);
|
||||
|
||||
let capabilities = if endpoint_header.status(&state.pcie).has_capability_list() {
|
||||
endpoint_header
|
||||
.capabilities(&state.pcie)
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
debug!(
|
||||
"PCI DEVICE CAPABILITIES for {}: {:?}",
|
||||
args.iter()
|
||||
.map(|string| string.as_ref())
|
||||
.nth(0)
|
||||
.unwrap_or("[unknown]"),
|
||||
capabilities
|
||||
);
|
||||
|
||||
let func = pcid_interface::PciFunction {
|
||||
let func = Func {
|
||||
inner: pcid_interface::PciFunction {
|
||||
bars,
|
||||
addr: endpoint_header.header().address(),
|
||||
legacy_interrupt_line,
|
||||
legacy_interrupt_line: None, // Will be filled in when enabling the device
|
||||
full_device_id: full_device_id.clone(),
|
||||
};
|
||||
},
|
||||
|
||||
driver_handler::DriverHandler::spawn(Arc::clone(&state), func, capabilities, args);
|
||||
}
|
||||
capabilities,
|
||||
endpoint_header,
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
tree.insert(func.inner.addr, func);
|
||||
}
|
||||
|
||||
fn enable_function(
|
||||
state: &Arc<State>,
|
||||
pcie: &Pcie,
|
||||
endpoint_header: &mut EndpointHeader,
|
||||
) -> Option<LegacyInterruptLine> {
|
||||
// Enable bus mastering, memory space, and I/O space
|
||||
endpoint_header.update_command(&state.pcie, |cmd| {
|
||||
endpoint_header.update_command(pcie, |cmd| {
|
||||
cmd | CommandRegister::BUS_MASTER_ENABLE
|
||||
| CommandRegister::MEMORY_ENABLE
|
||||
| CommandRegister::IO_ENABLE
|
||||
@@ -131,7 +121,7 @@ fn enable_function(
|
||||
let mut irq = 0xFF;
|
||||
let mut interrupt_pin = 0xFF;
|
||||
|
||||
endpoint_header.update_interrupt(&state.pcie, |(pin, mut line)| {
|
||||
endpoint_header.update_interrupt(pcie, |(pin, mut line)| {
|
||||
if line == 0xFF {
|
||||
line = 9;
|
||||
}
|
||||
@@ -156,13 +146,12 @@ fn enable_function(
|
||||
| ((pci_address.device() as u32) << 11)
|
||||
| ((pci_address.function() as u32) << 8);
|
||||
let addr = [
|
||||
dt_address & state.pcie.interrupt_map_mask[0],
|
||||
dt_address & pcie.interrupt_map_mask[0],
|
||||
0u32,
|
||||
0u32,
|
||||
interrupt_pin as u32 & state.pcie.interrupt_map_mask[3],
|
||||
interrupt_pin as u32 & pcie.interrupt_map_mask[3],
|
||||
];
|
||||
let mapping = state
|
||||
.pcie
|
||||
let mapping = pcie
|
||||
.interrupt_map
|
||||
.iter()
|
||||
.find(|x| x.addr == addr[0..3] && x.interrupt == addr[3]);
|
||||
@@ -187,34 +176,6 @@ fn enable_function(
|
||||
|
||||
fn main() {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
|
||||
let mut config = Config::default();
|
||||
|
||||
if let Ok(config_path) = args.free_from_str::<PathBuf>() {
|
||||
if metadata(&config_path).unwrap().is_file() {
|
||||
if let Ok(mut config_file) = File::open(&config_path) {
|
||||
let mut config_data = String::new();
|
||||
if let Ok(_) = config_file.read_to_string(&mut config_data) {
|
||||
config = toml::from_str(&config_data).unwrap_or(Config::default());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let paths = read_dir(&config_path).unwrap();
|
||||
|
||||
let mut config_data = String::new();
|
||||
|
||||
for path in paths {
|
||||
if let Ok(mut config_file) = File::open(&path.unwrap().path()) {
|
||||
let mut tmp = String::new();
|
||||
if let Ok(_) = config_file.read_to_string(&mut tmp) {
|
||||
config_data.push_str(&tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
config = toml::from_str(&config_data).unwrap_or(Config::default());
|
||||
}
|
||||
}
|
||||
|
||||
let verbosity = (0..).find(|_| !args.contains("-v")).unwrap_or(0);
|
||||
let log_level = match verbosity {
|
||||
0 => log::LevelFilter::Info,
|
||||
@@ -224,14 +185,12 @@ fn main() {
|
||||
|
||||
common::setup_logging("bus", "pci", "pcid", log_level, log::LevelFilter::Trace);
|
||||
|
||||
redox_daemon::Daemon::new(move |daemon| main_inner(config, daemon)).unwrap();
|
||||
redox_daemon::Daemon::new(move |daemon| main_inner(daemon)).unwrap();
|
||||
}
|
||||
|
||||
fn main_inner(config: Config, daemon: redox_daemon::Daemon) -> ! {
|
||||
let state = Arc::new(State {
|
||||
pcie: Pcie::new(),
|
||||
threads: Mutex::new(Vec::new()),
|
||||
});
|
||||
fn main_inner(daemon: redox_daemon::Daemon) -> ! {
|
||||
let pcie = Pcie::new();
|
||||
let mut tree = BTreeMap::new();
|
||||
|
||||
info!("PCI SG-BS:DV.F VEND:DEVI CL.SC.IN.RV");
|
||||
|
||||
@@ -245,22 +204,45 @@ fn main_inner(config: Config, daemon: redox_daemon::Daemon) -> ! {
|
||||
bus_i += 1;
|
||||
|
||||
for dev_num in 0..32 {
|
||||
scan_device(&config, &state, &mut bus_nums, bus_num, dev_num);
|
||||
scan_device(&mut tree, &pcie, &mut bus_nums, bus_num, dev_num);
|
||||
}
|
||||
}
|
||||
info!("Enumeration complete, now starting pci scheme");
|
||||
|
||||
let mut scheme = scheme::PciScheme::new(pcie, tree);
|
||||
let socket = redox_scheme::Socket::create("pci").expect("failed to open pci scheme socket");
|
||||
|
||||
let _ = daemon.ready();
|
||||
|
||||
loop {
|
||||
let Some(request) = socket
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("pcid: failed to get next scheme request")
|
||||
else {
|
||||
break;
|
||||
};
|
||||
match request.kind() {
|
||||
RequestKind::Call(call) => {
|
||||
let response = call.handle_scheme(&mut scheme);
|
||||
|
||||
socket
|
||||
.write_responses(&[response], SignalBehavior::Restart)
|
||||
.expect("pcid: failed to write next scheme response");
|
||||
}
|
||||
RequestKind::OnClose { id } => {
|
||||
scheme.on_close(id);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
daemon.ready().unwrap();
|
||||
|
||||
for thread in state.threads.lock().unwrap().drain(..) {
|
||||
thread.join().unwrap();
|
||||
}
|
||||
|
||||
println!("pcid: exit");
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn scan_device(
|
||||
config: &Config,
|
||||
state: &Arc<State>,
|
||||
tree: &mut BTreeMap<PciAddress, Func>,
|
||||
pcie: &Pcie,
|
||||
bus_nums: &mut Vec<u8>,
|
||||
bus_num: u8,
|
||||
dev_num: u8,
|
||||
@@ -268,7 +250,7 @@ fn scan_device(
|
||||
for func_num in 0..8 {
|
||||
let header = TyPciHeader::new(PciAddress::new(0, bus_num, dev_num, func_num));
|
||||
|
||||
let (vendor_id, device_id) = header.id(&state.pcie);
|
||||
let (vendor_id, device_id) = header.id(pcie);
|
||||
if vendor_id == 0xffff && device_id == 0xffff {
|
||||
if func_num == 0 {
|
||||
trace!("PCI {:>02X}:{:>02X}: no dev", bus_num, dev_num);
|
||||
@@ -278,7 +260,7 @@ fn scan_device(
|
||||
continue;
|
||||
}
|
||||
|
||||
let (revision, class, subclass, interface) = header.revision_and_class(&state.pcie);
|
||||
let (revision, class, subclass, interface) = header.revision_and_class(pcie);
|
||||
let full_device_id = FullDeviceId {
|
||||
vendor_id,
|
||||
device_id,
|
||||
@@ -290,20 +272,20 @@ fn scan_device(
|
||||
|
||||
info!("PCI {} {}", header.address(), full_device_id.display());
|
||||
|
||||
let has_multiple_functions = header.has_multiple_functions(&state.pcie);
|
||||
let has_multiple_functions = header.has_multiple_functions(pcie);
|
||||
|
||||
match header.header_type(&state.pcie) {
|
||||
match header.header_type(pcie) {
|
||||
HeaderType::Endpoint => {
|
||||
handle_parsed_header(
|
||||
Arc::clone(state),
|
||||
config,
|
||||
EndpointHeader::from_header(header, &state.pcie).unwrap(),
|
||||
pcie,
|
||||
tree,
|
||||
EndpointHeader::from_header(header, pcie).unwrap(),
|
||||
full_device_id,
|
||||
);
|
||||
}
|
||||
HeaderType::PciPciBridge => {
|
||||
let bridge_header = PciPciBridgeHeader::from_header(header, &state.pcie).unwrap();
|
||||
bus_nums.push(bridge_header.secondary_bus_number(&state.pcie));
|
||||
let bridge_header = PciPciBridgeHeader::from_header(header, pcie).unwrap();
|
||||
bus_nums.push(bridge_header.secondary_bus_number(pcie));
|
||||
}
|
||||
ty => {
|
||||
warn!("pcid: unknown header type: {ty:?}");
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
|
||||
use pci_types::PciAddress;
|
||||
use redox_scheme::{CallerCtx, OpenResult, Scheme};
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR, O_DIRECTORY, O_STAT};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::ENOLCK;
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
|
||||
pub struct PciScheme {
|
||||
handles: BTreeMap<usize, HandleWrapper>,
|
||||
next_id: usize,
|
||||
pcie: Pcie,
|
||||
tree: BTreeMap<PciAddress, crate::Func>,
|
||||
}
|
||||
enum Handle {
|
||||
TopLevel { entries: Vec<String> },
|
||||
Device,
|
||||
Channel { addr: PciAddress, st: ChannelState },
|
||||
}
|
||||
struct HandleWrapper {
|
||||
inner: Handle,
|
||||
stat: bool,
|
||||
}
|
||||
impl Handle {
|
||||
fn is_file(&self) -> bool {
|
||||
matches!(self, Self::Channel { .. })
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
}
|
||||
// TODO: capability rather than root
|
||||
fn requires_root(&self) -> bool {
|
||||
matches!(self, Self::Channel { .. })
|
||||
}
|
||||
}
|
||||
|
||||
enum ChannelState {
|
||||
AwaitingData,
|
||||
AwaitingResponseRead(VecDeque<u8>),
|
||||
}
|
||||
|
||||
const DEVICE_CONTENTS: &[&str] = &["channel"];
|
||||
|
||||
impl Scheme for PciScheme {
|
||||
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
log::trace!("OPEN `{}` flags {}", path, flags);
|
||||
|
||||
// TODO: Check flags are correct
|
||||
let expects_dir = path.ends_with('/') || flags & O_DIRECTORY != 0;
|
||||
|
||||
let path = path.trim_matches('/');
|
||||
|
||||
let handle = if path.is_empty() {
|
||||
Handle::TopLevel {
|
||||
entries: self
|
||||
.tree
|
||||
.iter()
|
||||
// FIXME remove replacement of : once the old scheme format is no longer supported.
|
||||
.map(|(addr, _)| format!("{}", addr).replace(':', "--"))
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
} else {
|
||||
let idx = path.find('/').unwrap_or(path.len());
|
||||
let (addr_str, after) = path.split_at(idx);
|
||||
let addr = parse_pci_addr(addr_str).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
self.parse_after_pci_addr(addr, after)?
|
||||
};
|
||||
|
||||
let stat = flags & O_STAT != 0;
|
||||
if expects_dir && handle.is_file() && !stat {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
if !expects_dir && handle.is_dir() && !stat {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
if ctx.uid != 0 && handle.requires_root() && !stat {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
|
||||
self.handles.insert(
|
||||
id,
|
||||
HandleWrapper {
|
||||
inner: handle,
|
||||
stat,
|
||||
},
|
||||
);
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
})
|
||||
}
|
||||
fn fstat(&mut self, id: usize, stat: &mut syscall::Stat) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let (len, mode) = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||
};
|
||||
stat.st_size = len as u64;
|
||||
stat.st_mode = mode;
|
||||
Ok(0)
|
||||
}
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
match handle.inner {
|
||||
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||
Handle::Device => Err(Error::new(EISDIR)),
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
}
|
||||
}
|
||||
fn getdents<'buf>(
|
||||
&mut self,
|
||||
id: usize,
|
||||
mut buf: DirentBuf<&'buf mut [u8]>,
|
||||
opaque_offset: u64,
|
||||
) -> Result<DirentBuf<&'buf mut [u8]>> {
|
||||
let Ok(offset) = usize::try_from(opaque_offset) else {
|
||||
return Ok(buf);
|
||||
};
|
||||
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let entries = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => {
|
||||
for (i, dent_name) in entries.iter().enumerate().skip(offset) {
|
||||
buf.entry(DirEntry {
|
||||
inode: 0,
|
||||
name: dent_name,
|
||||
kind: DirentKind::Unspecified,
|
||||
next_opaque_id: i as u64 + 1,
|
||||
})?;
|
||||
}
|
||||
return Ok(buf);
|
||||
}
|
||||
Handle::Device => DEVICE_CONTENTS,
|
||||
Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||
};
|
||||
|
||||
for (i, dent_name) in entries.iter().enumerate().skip(offset) {
|
||||
buf.entry(DirEntry {
|
||||
inode: 0,
|
||||
name: dent_name,
|
||||
kind: DirentKind::Unspecified,
|
||||
next_opaque_id: i as u64 + 1,
|
||||
})?;
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
match handle.inner {
|
||||
Handle::Channel { addr, ref mut st } => {
|
||||
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||
}
|
||||
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciScheme {
|
||||
pub fn on_close(&mut self, id: usize) {
|
||||
match self.handles.remove(&id) {
|
||||
Some(HandleWrapper {
|
||||
inner: Handle::Channel { addr, .. },
|
||||
..
|
||||
}) => {
|
||||
log::trace!("TODO: Support disabling device (called on {})", addr);
|
||||
if let Some(func) = self.tree.get_mut(&addr) {
|
||||
func.enabled = false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciScheme {
|
||||
pub fn new(pcie: Pcie, tree: BTreeMap<PciAddress, crate::Func>) -> Self {
|
||||
Self {
|
||||
handles: BTreeMap::new(),
|
||||
next_id: 0,
|
||||
pcie,
|
||||
tree,
|
||||
}
|
||||
}
|
||||
fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str) -> Result<Handle> {
|
||||
if after.chars().next().map_or(false, |c| c != '/') {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let func = self.tree.get_mut(&addr).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
Ok(if after.is_empty() {
|
||||
Handle::Device
|
||||
} else {
|
||||
let path = &after[1..];
|
||||
|
||||
match path {
|
||||
"channel" => {
|
||||
if func.enabled {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
func.inner.legacy_interrupt_line =
|
||||
crate::enable_function(&self.pcie, &mut func.endpoint_header);
|
||||
func.enabled = true;
|
||||
Handle::Channel {
|
||||
addr,
|
||||
st: ChannelState::AwaitingData,
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn read_channel(state: &mut ChannelState, buf: &mut [u8]) -> Result<usize> {
|
||||
match *state {
|
||||
ChannelState::AwaitingResponseRead(ref mut queue) => {
|
||||
let byte_count = std::cmp::min(queue.len(), buf.len());
|
||||
// XXX: Why can't VecDeque support dequeueing into slices?
|
||||
for (idx, byte) in queue.drain(..byte_count).enumerate() {
|
||||
buf[idx] = byte;
|
||||
}
|
||||
if queue.is_empty() {
|
||||
*state = ChannelState::AwaitingData;
|
||||
}
|
||||
Ok(byte_count)
|
||||
}
|
||||
ChannelState::AwaitingData => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
fn write_channel(
|
||||
pci_state: &Pcie,
|
||||
tree: &mut BTreeMap<PciAddress, crate::Func>,
|
||||
addr: PciAddress,
|
||||
state: &mut ChannelState,
|
||||
buf: &[u8],
|
||||
) -> Result<usize> {
|
||||
match *state {
|
||||
ChannelState::AwaitingResponseRead(_) => return Err(Error::new(EINVAL)),
|
||||
ChannelState::AwaitingData => {
|
||||
let func = tree.get_mut(&addr).unwrap();
|
||||
|
||||
let request = bincode::deserialize_from(buf).map_err(|_| Error::new(EINVAL))?;
|
||||
let response = crate::driver_handler::DriverHandler::new(
|
||||
func.inner.clone(),
|
||||
&mut func.endpoint_header,
|
||||
func.capabilities.clone(),
|
||||
&*pci_state,
|
||||
)
|
||||
.respond(request);
|
||||
|
||||
let mut output_bytes = vec![0_u8; 8];
|
||||
bincode::serialize_into(&mut output_bytes, &response)
|
||||
.map_err(|_| Error::new(EIO))?;
|
||||
let len = output_bytes.len() - 8;
|
||||
output_bytes[..8].copy_from_slice(&u64::to_le_bytes(len as u64));
|
||||
*state = ChannelState::AwaitingResponseRead(output_bytes.into());
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pci_addr(addr: &str) -> Option<PciAddress> {
|
||||
let (segment, rest) = addr.split_once('-')?;
|
||||
let segment = u16::from_str_radix(segment, 16).ok()?;
|
||||
|
||||
// FIXME use : instead of -- as separator once the old scheme format is no longer supported.
|
||||
let (bus, rest) = rest.split_once("--")?;
|
||||
let bus = u8::from_str_radix(bus, 16).ok()?;
|
||||
|
||||
let (device, function) = rest.split_once('.')?;
|
||||
let device = u8::from_str_radix(device, 16).ok()?;
|
||||
let function = u8::from_str_radix(function, 16).ok()?;
|
||||
|
||||
Some(PciAddress::new(segment, bus, device, function))
|
||||
}
|
||||
Reference in New Issue
Block a user