e3a9a820ee
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
238 lines
7.3 KiB
Rust
238 lines
7.3 KiB
Rust
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<PciRom>,
|
|
pub legacy_interrupt_line: Option<LegacyInterruptLine>,
|
|
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<Self> {
|
|
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<Self, std::io::Error> {
|
|
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<T: Serialize>(&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<T: DeserializeOwned>(&mut self) -> Result<T, std::io::Error> {
|
|
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<PciFunction, std::io::Error> {
|
|
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<u32, std::io::Error> {
|
|
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(),
|
|
}
|
|
}
|
|
}
|