Files
RedBear-OS/pcid/src/main.rs
T
2024-06-16 15:23:32 +02:00

318 lines
9.7 KiB
Rust

use std::fs::{metadata, read_dir, File};
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::thread;
use log::{debug, info, trace, warn};
use pci_types::{
Bar as TyBar, CommandRegister, EndpointHeader, HeaderType, PciAddress,
PciHeader as TyPciHeader, PciPciBridgeHeader,
};
use redox_log::{OutputBuilder, RedoxLogger};
use structopt::StructOpt;
use crate::cfg_access::Pcie;
use crate::config::Config;
use crate::driver_interface::{FullDeviceId, LegacyInterruptLine, PciBar};
mod cfg_access;
mod config;
mod driver_handler;
mod driver_interface;
#[derive(StructOpt)]
#[structopt(about)]
struct Args {
#[structopt(
short,
long,
help = "Increase logging level once for each arg.",
parse(from_occurrences)
)]
verbose: u8,
#[structopt(
help = "A path to a pcid config file or a directory that contains pcid config files."
)]
config_path: Option<String>,
}
pub struct State {
threads: Mutex<Vec<thread::JoinHandle<()>>>,
pcie: Pcie,
}
fn handle_parsed_header(
state: Arc<State>,
config: &Config,
mut endpoint_header: EndpointHeader,
full_device_id: FullDeviceId,
) {
for driver in config.drivers.iter() {
if !driver.match_function(&full_device_id) {
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,
size,
prefetchable: _,
}) => {
bars[i as usize] = PciBar::Memory32 {
addr: address,
size,
}
}
Some(TyBar::Memory64 {
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,
}
}
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);
}
// Enable bus mastering, memory space, and I/O space
endpoint_header.update_command(&state.pcie, |cmd| {
cmd | CommandRegister::BUS_MASTER_ENABLE
| CommandRegister::MEMORY_ENABLE
| CommandRegister::IO_ENABLE
});
// Set IRQ line to 9 if not set
let mut irq = 0xFF;
let mut interrupt_pin = 0xFF;
endpoint_header.update_interrupt(&state.pcie, |(pin, mut line)| {
if line == 0xFF {
line = 9;
}
irq = line;
interrupt_pin = pin;
(pin, line)
});
let legacy_interrupt_enabled = match interrupt_pin {
0 => false,
1 | 2 | 3 | 4 => true,
other => {
warn!("pcid: invalid interrupt pin: {}", other);
false
}
};
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 = driver_interface::PciFunction {
bars,
addr: endpoint_header.header().address(),
legacy_interrupt_line: if legacy_interrupt_enabled {
Some(LegacyInterruptLine(irq))
} else {
None
},
full_device_id: full_device_id.clone(),
};
driver_handler::DriverHandler::spawn(Arc::clone(&state), func, capabilities, args);
}
}
fn setup_logging(verbosity: u8) -> Option<&'static RedoxLogger> {
let log_level = match verbosity {
0 => log::LevelFilter::Info,
1 => log::LevelFilter::Debug,
_ => log::LevelFilter::Trace,
};
let mut logger = RedoxLogger::new().with_output(
OutputBuilder::stderr()
.with_ansi_escape_codes()
.with_filter(log_level)
.flush_on_newline(true)
.build(),
);
#[cfg(target_os = "redox")]
{
match OutputBuilder::in_redox_logging_scheme("bus", "pci", "pcid.log") {
Ok(b) => {
logger = logger.with_output(
b.with_filter(log::LevelFilter::Trace)
.flush_on_newline(true)
.build(),
)
}
Err(error) => eprintln!("pcid: failed to open pcid.log"),
}
match OutputBuilder::in_redox_logging_scheme("bus", "pci", "pcid.ansi.log") {
Ok(b) => {
logger = logger.with_output(
b.with_filter(log::LevelFilter::Trace)
.with_ansi_escape_codes()
.flush_on_newline(true)
.build(),
)
}
Err(error) => eprintln!("pcid: failed to open pcid.ansi.log"),
}
}
match logger.enable() {
Ok(logger_ref) => {
eprintln!("pcid: enabled logger");
Some(logger_ref)
}
Err(error) => {
eprintln!("pcid: failed to set default logger: {}", error);
None
}
}
}
#[paw::main]
fn main(args: Args) {
let mut config = Config::default();
if let Some(config_path) = args.config_path {
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 _logger_ref = setup_logging(args.verbose);
let state = Arc::new(State {
pcie: Pcie::new(),
threads: Mutex::new(Vec::new()),
});
info!("PCI SG-BS:DV.F VEND:DEVI CL.SC.IN.RV");
// FIXME Use full ACPI for enumerating the host bridges. MCFG only describes the first
// host bridge, while multi-processor systems likely have a host bridge for each CPU.
// See also https://www.kernel.org/doc/html/latest/PCI/acpi-info.html
let mut bus_nums = vec![0];
let mut bus_i = 0;
while bus_i < bus_nums.len() {
let bus_num = bus_nums[bus_i];
bus_i += 1;
'dev: for dev_num in 0..32 {
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);
if vendor_id == 0xffff && device_id == 0xffff {
if func_num == 0 {
trace!("PCI {:>02X}:{:>02X}: no dev", bus_num, dev_num);
continue 'dev;
}
continue;
}
let (revision, class, subclass, interface) = header.revision_and_class(&state.pcie);
let full_device_id = FullDeviceId {
vendor_id,
device_id,
class,
subclass,
interface,
revision,
};
info!("PCI {} {}", header.address(), full_device_id.display());
match header.header_type(&state.pcie) {
HeaderType::Endpoint => {
handle_parsed_header(
Arc::clone(&state),
&config,
EndpointHeader::from_header(header, &state.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));
}
ty => {
warn!("pcid: unknown header type: {ty:?}");
}
}
}
}
}
for thread in state.threads.lock().unwrap().drain(..) {
thread.join().unwrap();
}
}