#![feature(asm)] #[macro_use] extern crate bitflags; extern crate byteorder; #[macro_use] extern crate serde_derive; extern crate syscall; extern crate toml; use std::{env, i64}; use std::fs::{File, metadata, read_dir}; use std::io::Read; use std::process::Command; use syscall::iopl; use crate::config::Config; use crate::pci::{Pci, PciBar, PciClass, PciHeader, PciHeaderError, PciHeaderType}; mod config; mod pci; fn handle_parsed_header(config: &Config, pci: &Pci, bus_num: u8, dev_num: u8, func_num: u8, header: PciHeader) { let raw_class: u8 = header.class().into(); 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::Storage => match header.subclass() { 0x01 => { string.push_str(" IDE"); }, 0x06 => { string.push_str(" SATA"); }, _ => () }, PciClass::SerialBus => match header.subclass() { 0x03 => match header.interface() { 0x00 => { string.push_str(" UHCI"); }, 0x10 => { string.push_str(" OHCI"); }, 0x20 => { string.push_str(" EHCI"); }, 0x30 => { string.push_str(" XHCI"); }, _ => () }, _ => () }, _ => () } for (i, bar) in header.bars().iter().enumerate() { if !bar.is_none() { string.push_str(&format!(" {}={}", i, bar)); } } string.push('\n'); print!("{}", string); for driver in config.drivers.iter() { if let Some(class) = driver.class { if class != raw_class { continue; } } if let Some(subclass) = driver.subclass { if subclass != header.subclass() { continue; } } if let Some(interface) = driver.interface { if interface != header.interface() { continue; } } if let Some(ref ids) = driver.ids { let mut device_found = false; for (vendor, devices) in ids { let vendor_without_prefix = vendor.trim_start_matches("0x"); let vendor = i64::from_str_radix(vendor_without_prefix, 16).unwrap() as u16; if vendor != header.vendor_id() { continue; } for device in devices { if *device == header.device_id() { device_found = true; break; } } } if !device_found { continue; } } else { if let Some(vendor) = driver.vendor { if vendor != header.vendor_id() { continue; } } if let Some(device) = driver.device { if device != header.device_id() { continue; } } } if let Some(ref device_id_range) = driver.device_id_range { if header.device_id() < device_id_range.start || device_id_range.end <= header.device_id() { continue; } } if let Some(ref args) = driver.command { // Enable bus mastering, memory space, and I/O space unsafe { let mut data = pci.read(bus_num, dev_num, func_num, 0x04); data |= 7; pci.write(bus_num, dev_num, func_num, 0x04, data); } // Set IRQ line to 9 if not set let mut irq; unsafe { let mut data = pci.read(bus_num, dev_num, func_num, 0x3C); irq = (data & 0xFF) as u8; if irq == 0xFF { irq = 9; } data = (data & 0xFFFFFF00) | irq as u32; pci.write(bus_num, dev_num, func_num, 0x3C, data); } // Find BAR sizes let mut bars = [PciBar::None; 6]; let mut bar_sizes = [0; 6]; unsafe { let count = match header.header_type() { PciHeaderType::GENERAL => 6, PciHeaderType::PCITOPCI => 2, _ => 0, }; for i in 0..count { bars[i] = header.get_bar(i); let offset = 0x10 + (i as u8) * 4; let original = pci.read(bus_num, dev_num, func_num, offset); pci.write(bus_num, dev_num, func_num, offset, 0xFFFFFFFF); let new = pci.read(bus_num, dev_num, func_num, offset); pci.write(bus_num, dev_num, func_num, offset, original); let masked = if new & 1 == 1 { new & 0xFFFFFFFC } else { new & 0xFFFFFFF0 }; let size = !masked + 1; bar_sizes[i] = if size <= 1 { 0 } else { size }; } } // TODO: find a better way to pass the header data down to the // device driver, making passing the capabilities list etc // posible. let mut args = args.iter(); if let Some(program) = args.next() { let mut command = Command::new(program); for arg in args { let arg = match arg.as_str() { "$BUS" => format!("{:>02X}", bus_num), "$DEV" => format!("{:>02X}", dev_num), "$FUNC" => format!("{:>02X}", func_num), "$NAME" => format!("pci-{:>02X}.{:>02X}.{:>02X}", bus_num, dev_num, func_num), "$BAR0" => format!("{}", bars[0]), "$BAR1" => format!("{}", bars[1]), "$BAR2" => format!("{}", bars[2]), "$BAR3" => format!("{}", bars[3]), "$BAR4" => format!("{}", bars[4]), "$BAR5" => format!("{}", bars[5]), "$BARSIZE0" => format!("{:>08X}", bar_sizes[0]), "$BARSIZE1" => format!("{:>08X}", bar_sizes[1]), "$BARSIZE2" => format!("{:>08X}", bar_sizes[2]), "$BARSIZE3" => format!("{:>08X}", bar_sizes[3]), "$BARSIZE4" => format!("{:>08X}", bar_sizes[4]), "$BARSIZE5" => format!("{:>08X}", bar_sizes[5]), "$IRQ" => format!("{}", irq), "$VENID" => format!("{:>04X}", header.vendor_id()), "$DEVID" => format!("{:>04X}", header.device_id()), _ => arg.clone() }; command.arg(&arg); } 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) }, Err(err) => println!("pcid: failed to execute {:?}: {}", command, err) } } } } } fn main() { let mut config = Config::default(); let mut args = env::args().skip(1); if let Some(config_path) = args.next() { 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()); } } unsafe { iopl(3).unwrap() }; print!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV\n"); let pci = Pci::new(); for bus in pci.buses() { for dev in bus.devs() { for func in dev.funcs() { let func_num = func.num; match PciHeader::from_reader(func) { Ok(header) => { handle_parsed_header(&config, &pci, bus.num, dev.num, func_num, header); } Err(PciHeaderError::NoDevice) => {}, Err(PciHeaderError::UnknownHeaderType(id)) => { println!("pcid: unknown header type: {}", id); } } } } } }