use std::fs::File; use std::io::{Read, Write}; use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; use std::path::Path; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::pci::{PciBarInfo, PciBarKind, PciDeviceInfo, PciLocation, PCI_HEADER_TYPE_NORMAL}; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum PciBar { None, Memory32 { addr: u32, size: u32 }, Memory64 { addr: u64, size: u64 }, Port(u16), } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct LegacyInterruptLine { pub irq: u8, pub phandled: Option<(u32, [u32; 3], usize)>, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct FullDeviceId { pub vendor_id: u16, pub device_id: u16, pub class: u8, pub subclass: u8, pub interface: u8, pub revision: u8, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PciFunction { pub segment: u16, pub bus: u8, pub device: u8, pub function: u8, pub bars: [PciBar; 6], pub rom: Option, pub legacy_interrupt_line: Option, pub full_device_id: FullDeviceId, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PciRom { pub addr: u32, pub size: u32, pub enabled: bool, } #[derive(Debug, Serialize, Deserialize)] pub struct SubdriverArguments { pub func: PciFunction, } #[derive(Debug, Serialize, Deserialize)] pub enum PcidClientRequest { EnableDevice, RequestConfig, ReadConfig(u16), WriteConfig(u16, u32), } #[derive(Debug, Serialize, Deserialize)] pub enum PcidClientResponse { EnabledDevice, Config(SubdriverArguments), ReadConfig(u32), WriteConfig, Error(String), } pub struct PcidClient { channel: File, } impl PcidClient { pub fn connect_default() -> Option { let fd_str = std::env::var("PCID_CLIENT_CHANNEL").ok()?; let fd: RawFd = fd_str.parse().ok()?; Some(Self::connect_common(fd)) } pub fn connect_by_path(device_path: &Path) -> Result { let channel_path = device_path.join("channel"); let fd = libredox::call::open( channel_path.to_str().ok_or_else(|| { std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid path") })?, libredox::flag::O_RDWR, 0, ) .map_err(|e| std::io::Error::from_raw_os_error(e.errno()))?; Ok(Self::connect_common(fd as RawFd)) } fn connect_common(channel_fd: RawFd) -> Self { let channel = unsafe { File::from_raw_fd(channel_fd) }; Self { channel } } fn send(&mut self, msg: &T) -> Result<(), std::io::Error> { let data = bincode::serialize(msg) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; self.channel.write_all(&data)?; Ok(()) } fn recv(&mut self) -> Result { let mut len_buf = [0u8; 8]; self.channel.read_exact(&mut len_buf)?; let len = u64::from_le_bytes(len_buf) as usize; if len > 0x100_000 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "response too large", )); } let mut data = vec![0u8; len]; self.channel.read_exact(&mut data)?; bincode::deserialize_from(&data[..]) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e)) } pub fn request_config(&mut self) -> Result { self.send(&PcidClientRequest::RequestConfig)?; match self.recv()? { PcidClientResponse::Config(args) => Ok(args.func), other => Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("unexpected response: {other:?}"), )), } } pub fn enable_device(&mut self) -> Result<(), std::io::Error> { self.send(&PcidClientRequest::EnableDevice)?; match self.recv()? { PcidClientResponse::EnabledDevice => Ok(()), other => Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("unexpected response: {other:?}"), )), } } pub fn read_config(&mut self, offset: u16) -> Result { self.send(&PcidClientRequest::ReadConfig(offset))?; match self.recv()? { PcidClientResponse::ReadConfig(val) => Ok(val), other => Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("unexpected response: {other:?}"), )), } } pub fn write_config(&mut self, offset: u16, value: u32) -> Result<(), std::io::Error> { self.send(&PcidClientRequest::WriteConfig(offset, value))?; match self.recv()? { PcidClientResponse::WriteConfig => Ok(()), other => Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("unexpected response: {other:?}"), )), } } pub fn into_raw_fd(self) -> RawFd { self.channel.into_raw_fd() } } impl PciFunction { pub fn location(&self) -> PciLocation { PciLocation { segment: self.segment, bus: self.bus, device: self.device, function: self.function, } } pub fn device_info(&self) -> PciDeviceInfo { PciDeviceInfo { location: self.location(), vendor_id: self.full_device_id.vendor_id, device_id: self.full_device_id.device_id, subsystem_vendor_id: 0xffff, subsystem_device_id: 0xffff, revision: self.full_device_id.revision, class_code: self.full_device_id.class, subclass: self.full_device_id.subclass, prog_if: self.full_device_id.interface, header_type: PCI_HEADER_TYPE_NORMAL, irq: self.legacy_interrupt_line.map(|irq| u32::from(irq.irq)), bars: self .bars .iter() .enumerate() .map(|(index, bar)| match *bar { PciBar::None => PciBarInfo { index, kind: PciBarKind::None, addr: 0, size: 0, prefetchable: false, }, PciBar::Memory32 { addr, size } => PciBarInfo { index, kind: PciBarKind::Memory32, addr: u64::from(addr), size: u64::from(size), prefetchable: false, }, PciBar::Memory64 { addr, size } => PciBarInfo { index, kind: PciBarKind::Memory64, addr, size, prefetchable: false, }, PciBar::Port(port) => PciBarInfo { index, kind: PciBarKind::Io, addr: u64::from(port), size: 0, prefetchable: false, }, }) .collect(), capabilities: Vec::new(), } } }