Merge branch 'pci_types' into 'master'
A bunch of pci improvements See merge request redox-os/drivers!130
This commit is contained in:
Generated
+1
@@ -956,6 +956,7 @@ name = "pcid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bit_field",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder 1.4.3",
|
||||
"common",
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ use std::{slice, usize};
|
||||
|
||||
use pcid_interface::{PciBar, PciFeature, PciFeatureInfo, PciFunction, PcidServerHandle};
|
||||
use syscall::{
|
||||
Event, Mmio, Packet, Result, SchemeBlockMut,
|
||||
Event, Mmio, Packet, Result, SchemeBlockMut,
|
||||
PAGE_SIZE,
|
||||
};
|
||||
use redox_log::{OutputBuilder, RedoxLogger};
|
||||
@@ -299,7 +299,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
.fetch_config()
|
||||
.expect("nvmed: failed to fetch config");
|
||||
|
||||
let mut scheme_name = format!("disk.pci-{:x}+{:x}+{:x}-nvme", pci_config.func.bus_num, pci_config.func.dev_num, pci_config.func.func_num);
|
||||
let scheme_name = format!("disk.pci-{}-nvme", pci_config.func.addr);
|
||||
|
||||
let _logger_ref = setup_logging(&scheme_name);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ path = "src/lib.rs"
|
||||
[dependencies]
|
||||
bincode = "1.2"
|
||||
bitflags = "1"
|
||||
bit_field = "0.10"
|
||||
byteorder = "1.2"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
|
||||
@@ -4,12 +4,12 @@ use std::{env, io};
|
||||
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
use serde::{Serialize, Deserialize, de::DeserializeOwned};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
pub use crate::pci::cap::Capability;
|
||||
pub use crate::pci::msi;
|
||||
pub use crate::pci::{PciBar, PciHeader};
|
||||
pub use crate::pci::{PciAddress, PciBar, PciHeader};
|
||||
|
||||
pub mod irq_helpers;
|
||||
|
||||
@@ -28,14 +28,8 @@ pub enum LegacyInterruptPin {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct PciFunction {
|
||||
/// Number of PCI bus
|
||||
pub bus_num: u8,
|
||||
|
||||
/// Number of PCI device
|
||||
pub dev_num: u8,
|
||||
|
||||
/// Number of PCI function
|
||||
pub func_num: u8,
|
||||
/// Address of the PCI function.
|
||||
pub addr: PciAddress,
|
||||
|
||||
/// PCI Base Address Registers
|
||||
pub bars: [PciBar; 6],
|
||||
@@ -58,7 +52,8 @@ pub struct PciFunction {
|
||||
}
|
||||
impl PciFunction {
|
||||
pub fn name(&self) -> String {
|
||||
format!("pci-{:>02X}.{:>02X}.{:>02X}", self.bus_num, self.dev_num, self.func_num)
|
||||
// FIXME stop replacing : with - once it is a valid character in scheme names
|
||||
format!("pci-{}", self.addr).replace(':', "-")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-71
@@ -10,7 +10,7 @@ use log::{debug, error, info, warn, trace};
|
||||
use redox_log::{OutputBuilder, RedoxLogger};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::pci::{CfgAccess, Pci, PciIter, PciBar, PciBus, PciClass, PciDev, PciFunc, PciHeader, PciHeaderError, PciHeaderType};
|
||||
use crate::pci::{CfgAccess, Pci, PciAddress, PciBar, PciBus, PciClass, PciFunc, PciHeader, PciHeaderError, PciHeaderType};
|
||||
use crate::pci::cap::Capability as PciCapability;
|
||||
use crate::pci::func::{ConfigReader, ConfigWriter};
|
||||
use crate::pcie::Pcie;
|
||||
@@ -34,33 +34,20 @@ struct Args {
|
||||
|
||||
pub struct DriverHandler {
|
||||
config: config::DriverConfig,
|
||||
bus_num: u8,
|
||||
dev_num: u8,
|
||||
func_num: u8,
|
||||
addr: PciAddress,
|
||||
header: PciHeader,
|
||||
capabilities: Vec<(u8, PciCapability)>,
|
||||
|
||||
state: Arc<State>,
|
||||
}
|
||||
fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(pci: &dyn CfgAccess, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T {
|
||||
let bus = PciBus {
|
||||
pci,
|
||||
num: bus_num,
|
||||
};
|
||||
let dev = PciDev {
|
||||
bus: &bus,
|
||||
num: dev_num,
|
||||
};
|
||||
fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(pci: &dyn CfgAccess, addr: PciAddress, function: F) -> T {
|
||||
let func = PciFunc {
|
||||
dev: &dev,
|
||||
num: func_num,
|
||||
pci,
|
||||
addr,
|
||||
};
|
||||
function(&func)
|
||||
}
|
||||
impl DriverHandler {
|
||||
fn with_pci_func_raw<T, F: FnOnce(&PciFunc) -> T>(&self, function: F) -> T {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, function)
|
||||
}
|
||||
fn respond(&mut self, request: driver_interface::PcidClientRequest, args: &driver_interface::SubdriverArguments) -> driver_interface::PcidClientResponse {
|
||||
use driver_interface::*;
|
||||
use crate::pci::cap::{MsiCapability, MsixCapability};
|
||||
@@ -89,7 +76,7 @@ impl DriverHandler {
|
||||
None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)),
|
||||
};
|
||||
unsafe {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| {
|
||||
capability.set_enabled(true);
|
||||
capability.write_message_control(func, offset);
|
||||
});
|
||||
@@ -102,7 +89,7 @@ impl DriverHandler {
|
||||
None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)),
|
||||
};
|
||||
unsafe {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| {
|
||||
capability.set_msix_enabled(true);
|
||||
capability.write_a(func, offset);
|
||||
});
|
||||
@@ -162,7 +149,7 @@ impl DriverHandler {
|
||||
info.set_mask_bits(mask_bits);
|
||||
}
|
||||
unsafe {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| {
|
||||
info.write_all(func, offset);
|
||||
});
|
||||
}
|
||||
@@ -174,7 +161,7 @@ impl DriverHandler {
|
||||
if let Some(mask) = function_mask {
|
||||
info.set_function_mask(mask);
|
||||
unsafe {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| {
|
||||
info.write_a(func, offset);
|
||||
});
|
||||
}
|
||||
@@ -186,7 +173,7 @@ impl DriverHandler {
|
||||
}
|
||||
PcidClientRequest::ReadConfig(offset) => {
|
||||
let value = unsafe {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| {
|
||||
func.read_u32(offset)
|
||||
})
|
||||
};
|
||||
@@ -194,7 +181,7 @@ impl DriverHandler {
|
||||
},
|
||||
PcidClientRequest::WriteConfig(offset, value) => {
|
||||
unsafe {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| {
|
||||
with_pci_func_raw(self.state.preferred_cfg_access(), self.addr, |func| {
|
||||
func.write_u32(offset, value);
|
||||
});
|
||||
}
|
||||
@@ -224,19 +211,16 @@ pub struct State {
|
||||
}
|
||||
impl State {
|
||||
fn preferred_cfg_access(&self) -> &dyn CfgAccess {
|
||||
// TODO
|
||||
//self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess)
|
||||
&*self.pci as &dyn CfgAccess
|
||||
self.pcie.as_ref().map(|pcie| pcie as &dyn CfgAccess).unwrap_or(&*self.pci as &dyn CfgAccess)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
dev_num: u8, func_num: u8, header: PciHeader) {
|
||||
fn handle_parsed_header(state: Arc<State>, config: &Config, addr: PciAddress, header: PciHeader) {
|
||||
let pci = state.preferred_cfg_access();
|
||||
|
||||
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,
|
||||
let mut string = format!("PCI {} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}",
|
||||
addr, header.vendor_id(), header.device_id(), raw_class,
|
||||
header.subclass(), header.interface(), header.revision(), header.class());
|
||||
match header.class() {
|
||||
PciClass::Legacy if header.subclass() == 1 => string.push_str(" VGA CTL"),
|
||||
@@ -327,24 +311,24 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
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);
|
||||
let mut data = pci.read(addr, 0x04);
|
||||
data |= 7;
|
||||
pci.write(bus_num, dev_num, func_num, 0x04, data);
|
||||
pci.write(addr, 0x04, data);
|
||||
}
|
||||
|
||||
// Set IRQ line to 9 if not set
|
||||
let mut irq;
|
||||
let mut interrupt_pin;
|
||||
let interrupt_pin;
|
||||
|
||||
unsafe {
|
||||
let mut data = pci.read(bus_num, dev_num, func_num, 0x3C);
|
||||
let mut data = pci.read(addr, 0x3C);
|
||||
irq = (data & 0xFF) as u8;
|
||||
interrupt_pin = ((data & 0x0000_FF00) >> 8) as u8;
|
||||
if irq == 0xFF {
|
||||
irq = 9;
|
||||
}
|
||||
data = (data & 0xFFFFFF00) | irq as u32;
|
||||
pci.write(bus_num, dev_num, func_num, 0x3C, data);
|
||||
pci.write(addr, 0x3C, data);
|
||||
};
|
||||
|
||||
// Find BAR sizes
|
||||
@@ -363,11 +347,11 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
|
||||
let offset = 0x10 + (i as u8) * 4;
|
||||
|
||||
let original = pci.read(bus_num, dev_num, func_num, offset.into());
|
||||
pci.write(bus_num, dev_num, func_num, offset.into(), 0xFFFFFFFF);
|
||||
let original = pci.read(addr, offset.into());
|
||||
pci.write(addr, offset.into(), 0xFFFFFFFF);
|
||||
|
||||
let new = pci.read(bus_num, dev_num, func_num, offset.into());
|
||||
pci.write(bus_num, dev_num, func_num, offset.into(), original);
|
||||
let new = pci.read(addr, offset.into());
|
||||
pci.write(addr, offset.into(), original);
|
||||
|
||||
let masked = if new & 1 == 1 {
|
||||
new & 0xFFFFFFFC
|
||||
@@ -385,17 +369,9 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
}
|
||||
|
||||
let capabilities = if header.status() & (1 << 4) != 0 {
|
||||
let bus = PciBus {
|
||||
pci: state.preferred_cfg_access(),
|
||||
num: bus_num,
|
||||
};
|
||||
let dev = PciDev {
|
||||
bus: &bus,
|
||||
num: dev_num
|
||||
};
|
||||
let func = PciFunc {
|
||||
dev: &dev,
|
||||
num: func_num,
|
||||
pci: state.preferred_cfg_access(),
|
||||
addr
|
||||
};
|
||||
crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(header.cap_pointer(), &func) }.collect::<Vec<_>>()
|
||||
} else {
|
||||
@@ -421,9 +397,7 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
let func = driver_interface::PciFunction {
|
||||
bars,
|
||||
bar_sizes,
|
||||
bus_num,
|
||||
dev_num,
|
||||
func_num,
|
||||
addr,
|
||||
devid: header.device_id(),
|
||||
legacy_interrupt_line: irq,
|
||||
legacy_interrupt_pin,
|
||||
@@ -439,9 +413,9 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
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),
|
||||
"$BUS" => format!("{:>02X}", addr.bus()),
|
||||
"$DEV" => format!("{:>02X}", addr.device()),
|
||||
"$FUNC" => format!("{:>02X}", addr.function()),
|
||||
"$NAME" => func.name(),
|
||||
"$BAR0" => format!("{}", bars[0]),
|
||||
"$BAR1" => format!("{}", bars[1]),
|
||||
@@ -491,15 +465,13 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
match command.envs(envs).spawn() {
|
||||
Ok(mut child) => {
|
||||
let driver_handler = DriverHandler {
|
||||
bus_num,
|
||||
dev_num,
|
||||
func_num,
|
||||
addr,
|
||||
config: driver.clone(),
|
||||
header,
|
||||
state: Arc::clone(&state),
|
||||
capabilities,
|
||||
};
|
||||
let thread = thread::spawn(move || {
|
||||
thread::spawn(move || {
|
||||
// RFLAGS are no longer kept in the relibc clone() implementation.
|
||||
unsafe { syscall::iopl(3).expect("pcid: failed to set IOPL"); }
|
||||
|
||||
@@ -613,30 +585,25 @@ fn main(args: Args) {
|
||||
|
||||
let mut bus_nums = vec![0];
|
||||
let mut bus_i = 0;
|
||||
'bus: while bus_i < bus_nums.len() {
|
||||
while bus_i < bus_nums.len() {
|
||||
let bus_num = bus_nums[bus_i];
|
||||
bus_i += 1;
|
||||
|
||||
let bus = PciBus { pci, num: bus_num };
|
||||
let bus = PciBus { num: bus_num };
|
||||
'dev: for dev in bus.devs() {
|
||||
for func in dev.funcs() {
|
||||
let func_num = func.num;
|
||||
for func in dev.funcs(pci) {
|
||||
let func_addr = func.addr;
|
||||
match PciHeader::from_reader(func) {
|
||||
Ok(header) => {
|
||||
handle_parsed_header(Arc::clone(&state), &config, bus.num, dev.num, func_num, header);
|
||||
handle_parsed_header(Arc::clone(&state), &config, func_addr, header);
|
||||
if let PciHeader::PciToPci { secondary_bus_num, .. } = header {
|
||||
bus_nums.push(secondary_bus_num);
|
||||
}
|
||||
}
|
||||
Err(PciHeaderError::NoDevice) => {
|
||||
if func_num == 0 {
|
||||
if dev.num == 0 {
|
||||
trace!("PCI {:>02X}: no bus", bus.num);
|
||||
continue 'bus;
|
||||
} else {
|
||||
if func_addr.function() == 0 {
|
||||
trace!("PCI {:>02X}/{:>02X}: no dev", bus.num, dev.num);
|
||||
continue 'dev;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(PciHeaderError::UnknownHeaderType(id)) => {
|
||||
|
||||
+19
-29
@@ -1,49 +1,39 @@
|
||||
use super::{PciDev, CfgAccess};
|
||||
use super::PciDev;
|
||||
|
||||
pub struct PciBus<'pci> {
|
||||
pub pci: &'pci dyn CfgAccess,
|
||||
pub num: u8
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PciBus {
|
||||
pub num: u8,
|
||||
}
|
||||
|
||||
impl<'pci> PciBus<'pci> {
|
||||
pub fn devs(&'pci self) -> PciBusIter<'pci> {
|
||||
impl<'pci> PciBus {
|
||||
pub fn devs(self) -> PciBusIter {
|
||||
PciBusIter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn read(&self, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
self.pci.read(self.num, dev, func, offset)
|
||||
}
|
||||
pub unsafe fn write(&self, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
self.pci.write(self.num, dev, func, offset, value)
|
||||
pub struct PciBusIter {
|
||||
bus: PciBus,
|
||||
num: u8,
|
||||
}
|
||||
|
||||
impl PciBusIter {
|
||||
pub fn new(bus: PciBus) -> Self {
|
||||
PciBusIter { bus, num: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciBusIter<'pci> {
|
||||
bus: &'pci PciBus<'pci>,
|
||||
num: u8
|
||||
}
|
||||
|
||||
impl<'pci> PciBusIter<'pci> {
|
||||
pub fn new(bus: &'pci PciBus<'pci>) -> Self {
|
||||
PciBusIter {
|
||||
bus,
|
||||
num: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'pci> Iterator for PciBusIter<'pci> {
|
||||
type Item = PciDev<'pci>;
|
||||
impl Iterator for PciBusIter {
|
||||
type Item = PciDev;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.num {
|
||||
dev_num if dev_num < 32 => {
|
||||
let dev = PciDev {
|
||||
bus: self.bus,
|
||||
num: self.num
|
||||
num: self.num,
|
||||
};
|
||||
self.num += 1;
|
||||
Some(dev)
|
||||
},
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+16
-24
@@ -1,34 +1,26 @@
|
||||
use super::{PciBus, PciFunc};
|
||||
use super::{CfgAccess, PciAddress, PciBus, PciFunc};
|
||||
|
||||
pub struct PciDev<'pci> {
|
||||
pub bus: &'pci PciBus<'pci>,
|
||||
pub num: u8
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PciDev {
|
||||
pub bus: PciBus,
|
||||
pub num: u8,
|
||||
}
|
||||
|
||||
impl<'pci> PciDev<'pci> {
|
||||
pub fn funcs(&'pci self) -> PciDevIter<'pci> {
|
||||
PciDevIter::new(self)
|
||||
}
|
||||
|
||||
pub unsafe fn read(&self, func: u8, offset: u16) -> u32 {
|
||||
self.bus.read(self.num, func, offset)
|
||||
}
|
||||
pub unsafe fn write(&self, func: u8, offset: u16, value: u32) {
|
||||
self.bus.write(self.num, func, offset, value);
|
||||
impl<'pci> PciDev {
|
||||
pub fn funcs(self, pci: &'pci dyn CfgAccess) -> PciDevIter<'pci> {
|
||||
PciDevIter::new(self, pci)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciDevIter<'pci> {
|
||||
dev: &'pci PciDev<'pci>,
|
||||
num: u8
|
||||
pci: &'pci dyn CfgAccess,
|
||||
dev: PciDev,
|
||||
num: u8,
|
||||
}
|
||||
|
||||
impl<'pci> PciDevIter<'pci> {
|
||||
pub fn new(dev: &'pci PciDev<'pci>) -> Self {
|
||||
PciDevIter {
|
||||
dev: dev,
|
||||
num: 0
|
||||
}
|
||||
pub fn new(dev: PciDev, pci: &'pci dyn CfgAccess) -> Self {
|
||||
PciDevIter { pci, dev, num: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +30,12 @@ impl<'pci> Iterator for PciDevIter<'pci> {
|
||||
match self.num {
|
||||
func_num if func_num < 8 => {
|
||||
let func = PciFunc {
|
||||
dev: self.dev,
|
||||
num: self.num
|
||||
pci: self.pci,
|
||||
addr: PciAddress::new(0, self.dev.bus.num, self.dev.num, func_num),
|
||||
};
|
||||
self.num += 1;
|
||||
Some(func)
|
||||
},
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -1,16 +1,18 @@
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
use super::PciDev;
|
||||
use super::{CfgAccess, PciAddress, PciDev};
|
||||
|
||||
pub trait ConfigReader {
|
||||
unsafe fn read_range(&self, offset: u16, len: u16) -> Vec<u8> {
|
||||
assert!(len > 3 && len % 4 == 0, "invalid range length: {}", len);
|
||||
let mut ret = Vec::with_capacity(len as usize);
|
||||
let results = (offset..offset + len).step_by(4).fold(Vec::new(), |mut acc, offset| {
|
||||
let val = self.read_u32(offset);
|
||||
acc.push(val);
|
||||
acc
|
||||
});
|
||||
let results = (offset..offset + len)
|
||||
.step_by(4)
|
||||
.fold(Vec::new(), |mut acc, offset| {
|
||||
let val = self.read_u32(offset);
|
||||
acc.push(val);
|
||||
acc
|
||||
});
|
||||
ret.set_len(len as usize);
|
||||
LittleEndian::write_u32_into(&*results, &mut ret);
|
||||
ret
|
||||
@@ -31,17 +33,17 @@ pub trait ConfigWriter {
|
||||
}
|
||||
|
||||
pub struct PciFunc<'pci> {
|
||||
pub dev: &'pci PciDev<'pci>,
|
||||
pub num: u8,
|
||||
pub pci: &'pci dyn CfgAccess,
|
||||
pub addr: PciAddress,
|
||||
}
|
||||
|
||||
impl<'pci> ConfigReader for PciFunc<'pci> {
|
||||
unsafe fn read_u32(&self, offset: u16) -> u32 {
|
||||
self.dev.read(self.num, offset)
|
||||
self.pci.read(self.addr, offset)
|
||||
}
|
||||
}
|
||||
impl<'pci> ConfigWriter for PciFunc<'pci> {
|
||||
unsafe fn write_u32(&self, offset: u16, value: u32) {
|
||||
self.dev.write(self.num, offset, value);
|
||||
self.pci.write(self.addr, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
+140
-53
@@ -1,15 +1,15 @@
|
||||
use bitflags::bitflags;
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::func::ConfigReader;
|
||||
use super::class::PciClass;
|
||||
use super::bar::PciBar;
|
||||
use super::class::PciClass;
|
||||
use super::func::ConfigReader;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum PciHeaderError {
|
||||
NoDevice,
|
||||
UnknownHeaderType(u8)
|
||||
UnknownHeaderType(u8),
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@@ -30,8 +30,7 @@ bitflags! {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PciHeader {
|
||||
General {
|
||||
pub struct SharedPciHeader {
|
||||
vendor_id: u16,
|
||||
device_id: u16,
|
||||
command: u16,
|
||||
@@ -44,6 +43,12 @@ pub enum PciHeader {
|
||||
latency_timer: u8,
|
||||
header_type: PciHeaderType,
|
||||
bist: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PciHeader {
|
||||
General {
|
||||
shared: SharedPciHeader,
|
||||
bars: [PciBar; 6],
|
||||
cardbus_cis_ptr: u32,
|
||||
subsystem_vendor_id: u16,
|
||||
@@ -53,21 +58,10 @@ pub enum PciHeader {
|
||||
interrupt_line: u8,
|
||||
interrupt_pin: u8,
|
||||
min_grant: u8,
|
||||
max_latency: u8
|
||||
max_latency: u8,
|
||||
},
|
||||
PciToPci {
|
||||
vendor_id: u16,
|
||||
device_id: u16,
|
||||
command: u16,
|
||||
status: u16,
|
||||
revision: u8,
|
||||
interface: u8,
|
||||
subclass: u8,
|
||||
class: PciClass,
|
||||
cache_line_size: u8,
|
||||
latency_timer: u8,
|
||||
header_type: PciHeaderType,
|
||||
bist: u8,
|
||||
shared: SharedPciHeader,
|
||||
bars: [PciBar; 2],
|
||||
primary_bus_num: u8,
|
||||
secondary_bus_num: u8,
|
||||
@@ -87,9 +81,9 @@ pub enum PciHeader {
|
||||
cap_pointer: u8,
|
||||
expansion_rom: u32,
|
||||
interrupt_line: u8,
|
||||
interrupt_pin : u8,
|
||||
bridge_control: u16
|
||||
}
|
||||
interrupt_pin: u8,
|
||||
bridge_control: u16,
|
||||
},
|
||||
}
|
||||
|
||||
impl PciHeader {
|
||||
@@ -111,7 +105,7 @@ impl PciHeader {
|
||||
addr |= (LittleEndian::read_u32(high_bytes) as u64) << 32;
|
||||
bars[i] = PciBar::Memory64(addr);
|
||||
i += 2;
|
||||
},
|
||||
}
|
||||
bar => {
|
||||
bars[i] = bar;
|
||||
i += 1;
|
||||
@@ -138,6 +132,21 @@ impl PciHeader {
|
||||
let latency_timer = bytes[13];
|
||||
let header_type = PciHeaderType::from_bits_truncate(bytes[14]);
|
||||
let bist = bytes[15];
|
||||
let shared = SharedPciHeader {
|
||||
vendor_id,
|
||||
device_id,
|
||||
command,
|
||||
status,
|
||||
revision,
|
||||
interface,
|
||||
subclass,
|
||||
class,
|
||||
cache_line_size,
|
||||
latency_timer,
|
||||
header_type,
|
||||
bist,
|
||||
};
|
||||
|
||||
match header_type & PciHeaderType::HEADER_TYPE {
|
||||
PciHeaderType::GENERAL => {
|
||||
let bytes = unsafe { reader.read_range(16, 48) };
|
||||
@@ -153,13 +162,19 @@ impl PciHeader {
|
||||
let min_grant = bytes[46];
|
||||
let max_latency = bytes[47];
|
||||
Ok(PciHeader::General {
|
||||
vendor_id, device_id, command, status, revision, interface,
|
||||
subclass, class, cache_line_size, latency_timer, header_type,
|
||||
bist, bars, cardbus_cis_ptr, subsystem_vendor_id, subsystem_id,
|
||||
expansion_rom_bar, cap_pointer, interrupt_line, interrupt_pin,
|
||||
min_grant, max_latency
|
||||
shared,
|
||||
bars,
|
||||
cardbus_cis_ptr,
|
||||
subsystem_vendor_id,
|
||||
subsystem_id,
|
||||
expansion_rom_bar,
|
||||
cap_pointer,
|
||||
interrupt_line,
|
||||
interrupt_pin,
|
||||
min_grant,
|
||||
max_latency,
|
||||
})
|
||||
},
|
||||
}
|
||||
PciHeaderType::PCITOPCI => {
|
||||
let bytes = unsafe { reader.read_range(16, 48) };
|
||||
let mut bars = [PciBar::None; 2];
|
||||
@@ -185,17 +200,31 @@ impl PciHeader {
|
||||
let interrupt_pin = bytes[45];
|
||||
let bridge_control = LittleEndian::read_u16(&bytes[46..48]);
|
||||
Ok(PciHeader::PciToPci {
|
||||
vendor_id, device_id, command, status, revision, interface,
|
||||
subclass, class, cache_line_size, latency_timer, header_type,
|
||||
bist, bars, primary_bus_num, secondary_bus_num, subordinate_bus_num,
|
||||
secondary_latency_timer, io_base, io_limit, secondary_status,
|
||||
mem_base, mem_limit, prefetch_base, prefetch_limit, prefetch_base_upper,
|
||||
prefetch_limit_upper, io_base_upper, io_limit_upper, cap_pointer,
|
||||
expansion_rom, interrupt_line, interrupt_pin, bridge_control
|
||||
shared,
|
||||
bars,
|
||||
primary_bus_num,
|
||||
secondary_bus_num,
|
||||
subordinate_bus_num,
|
||||
secondary_latency_timer,
|
||||
io_base,
|
||||
io_limit,
|
||||
secondary_status,
|
||||
mem_base,
|
||||
mem_limit,
|
||||
prefetch_base,
|
||||
prefetch_limit,
|
||||
prefetch_base_upper,
|
||||
prefetch_limit_upper,
|
||||
io_base_upper,
|
||||
io_limit_upper,
|
||||
cap_pointer,
|
||||
expansion_rom,
|
||||
interrupt_line,
|
||||
interrupt_pin,
|
||||
bridge_control,
|
||||
})
|
||||
|
||||
},
|
||||
id => Err(PciHeaderError::UnknownHeaderType(id.bits()))
|
||||
}
|
||||
id => Err(PciHeaderError::UnknownHeaderType(id.bits())),
|
||||
}
|
||||
} else {
|
||||
Err(PciHeaderError::NoDevice)
|
||||
@@ -205,49 +234,98 @@ impl PciHeader {
|
||||
/// Return the Header Type.
|
||||
pub fn header_type(&self) -> PciHeaderType {
|
||||
match self {
|
||||
&PciHeader::General { header_type, .. } | &PciHeader::PciToPci { header_type, .. } => header_type,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { header_type, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { header_type, .. },
|
||||
..
|
||||
} => header_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Vendor ID field.
|
||||
pub fn vendor_id(&self) -> u16 {
|
||||
match self {
|
||||
&PciHeader::General { vendor_id, .. } | &PciHeader::PciToPci { vendor_id, .. } => vendor_id,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { vendor_id, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { vendor_id, .. },
|
||||
..
|
||||
} => vendor_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Device ID field.
|
||||
pub fn device_id(&self) -> u16 {
|
||||
match self {
|
||||
&PciHeader::General { device_id, .. } | &PciHeader::PciToPci { device_id, .. } => device_id,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { device_id, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { device_id, .. },
|
||||
..
|
||||
} => device_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Revision field.
|
||||
pub fn revision(&self) -> u8 {
|
||||
match self {
|
||||
&PciHeader::General { revision, .. } | &PciHeader::PciToPci { revision, .. } => revision,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { revision, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { revision, .. },
|
||||
..
|
||||
} => revision,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Interface field.
|
||||
pub fn interface(&self) -> u8 {
|
||||
match self {
|
||||
&PciHeader::General { interface, .. } | &PciHeader::PciToPci { interface, .. } => interface,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { interface, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { interface, .. },
|
||||
..
|
||||
} => interface,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Subclass field.
|
||||
pub fn subclass(&self) -> u8 {
|
||||
match self {
|
||||
&PciHeader::General { subclass, .. } | &PciHeader::PciToPci { subclass, .. } => subclass,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { subclass, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { subclass, .. },
|
||||
..
|
||||
} => subclass,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the Class field.
|
||||
pub fn class(&self) -> PciClass {
|
||||
match self {
|
||||
&PciHeader::General { class, .. } | &PciHeader::PciToPci { class, .. } => class,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { class, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { class, .. },
|
||||
..
|
||||
} => class,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +347,7 @@ impl PciHeader {
|
||||
&PciHeader::General { bars, .. } => {
|
||||
assert!(idx < 6, "the general PCI device only has 6 BARs");
|
||||
bars[idx]
|
||||
},
|
||||
}
|
||||
&PciHeader::PciToPci { bars, .. } => {
|
||||
assert!(idx < 2, "the general PCI device only has 2 BARs");
|
||||
bars[idx]
|
||||
@@ -280,20 +358,29 @@ impl PciHeader {
|
||||
/// Return the Interrupt Line field.
|
||||
pub fn interrupt_line(&self) -> u8 {
|
||||
match self {
|
||||
&PciHeader::General { interrupt_line, .. } | &PciHeader::PciToPci { interrupt_line, .. } =>
|
||||
interrupt_line,
|
||||
&PciHeader::General { interrupt_line, .. }
|
||||
| &PciHeader::PciToPci { interrupt_line, .. } => interrupt_line,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status(&self) -> u16 {
|
||||
match self {
|
||||
&PciHeader::General { status, .. } | &PciHeader::PciToPci { status, .. } => status,
|
||||
&PciHeader::General {
|
||||
shared: SharedPciHeader { status, .. },
|
||||
..
|
||||
}
|
||||
| &PciHeader::PciToPci {
|
||||
shared: SharedPciHeader { status, .. },
|
||||
..
|
||||
} => status,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cap_pointer(&self) -> u8 {
|
||||
match self {
|
||||
&PciHeader::General { cap_pointer, .. } | &PciHeader::PciToPci { cap_pointer, .. } => cap_pointer,
|
||||
&PciHeader::General { cap_pointer, .. } | &PciHeader::PciToPci { cap_pointer, .. } => {
|
||||
cap_pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,7 +467,7 @@ mod test {
|
||||
macro_rules! read_range_should_panic {
|
||||
($name:ident, $len:expr) => {
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed: len > 3 && len % 4 == 0")]
|
||||
#[should_panic(expected = "invalid range length")]
|
||||
fn $name() {
|
||||
let _ = unsafe { (&IGB_DEV_BYTES[..]).read_range(0, $len) };
|
||||
}
|
||||
|
||||
+99
-45
@@ -1,6 +1,9 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::sync::{Mutex, Once};
|
||||
|
||||
use bit_field::BitField;
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use syscall::io::{Io as _, Pio};
|
||||
|
||||
@@ -23,11 +26,69 @@ pub mod header;
|
||||
pub mod msi;
|
||||
|
||||
pub trait CfgAccess {
|
||||
unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32;
|
||||
unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32;
|
||||
unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32;
|
||||
unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32);
|
||||
}
|
||||
|
||||
unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32);
|
||||
unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32);
|
||||
// Copied from the pci_types crate, version 0.6.1. It has been modified to add serde support.
|
||||
// FIXME If we start using it in the future use the upstream version instead.
|
||||
/// The address of a PCIe function.
|
||||
///
|
||||
/// PCIe supports 65536 segments, each with 256 buses, each with 32 slots, each with 8 possible functions. We pack this into a `u32`:
|
||||
///
|
||||
/// ```ignore
|
||||
/// 32 16 8 3 0
|
||||
/// +-------------------------------+---------------+---------+------+
|
||||
/// | segment | bus | device | func |
|
||||
/// +-------------------------------+---------------+---------+------+
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
|
||||
pub struct PciAddress(u32);
|
||||
|
||||
impl PciAddress {
|
||||
pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> PciAddress {
|
||||
let mut result = 0;
|
||||
result.set_bits(0..3, function as u32);
|
||||
result.set_bits(3..8, device as u32);
|
||||
result.set_bits(8..16, bus as u32);
|
||||
result.set_bits(16..32, segment as u32);
|
||||
PciAddress(result)
|
||||
}
|
||||
|
||||
pub fn segment(&self) -> u16 {
|
||||
self.0.get_bits(16..32) as u16
|
||||
}
|
||||
|
||||
pub fn bus(&self) -> u8 {
|
||||
self.0.get_bits(8..16) as u8
|
||||
}
|
||||
|
||||
pub fn device(&self) -> u8 {
|
||||
self.0.get_bits(3..8) as u8
|
||||
}
|
||||
|
||||
pub fn function(&self) -> u8 {
|
||||
self.0.get_bits(0..3) as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PciAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:02x}-{:02x}:{:02x}.{}",
|
||||
self.segment(),
|
||||
self.bus(),
|
||||
self.device(),
|
||||
self.function()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PciAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pci {
|
||||
@@ -51,98 +112,91 @@ impl Pci {
|
||||
// make sure that pcid is not granted io port permission unless pcie memory-mapped
|
||||
// configuration space is not available.
|
||||
info!("PCI: couldn't find or access PCIe extended configuration, and thus falling back to PCI 3.0 io ports");
|
||||
unsafe { syscall::iopl(3).expect("pcid: failed to set iopl to 3"); }
|
||||
unsafe {
|
||||
syscall::iopl(3).expect("pcid: failed to set iopl to 3");
|
||||
}
|
||||
}
|
||||
fn address(bus: u8, dev: u8, func: u8, offset: u8) -> u32 {
|
||||
fn address(address: PciAddress, offset: u8) -> u32 {
|
||||
// TODO: Find the part of pcid that uses an unaligned offset!
|
||||
//
|
||||
// assert_eq!(offset & 0xFC, offset, "pci offset is not aligned");
|
||||
//
|
||||
let offset = offset & 0xFC;
|
||||
|
||||
assert_eq!(dev & 0x1F, dev, "pci device larger than 5 bits");
|
||||
assert_eq!(func & 0x7, func, "pci func larger than 3 bits");
|
||||
assert_eq!(
|
||||
address.segment(),
|
||||
0,
|
||||
"usage of multiple segments requires PCIe extended configuration"
|
||||
);
|
||||
|
||||
0x80000000 | (u32::from(bus) << 16) | (u32::from(dev) << 11) | (u32::from(func) << 8) | u32::from(offset)
|
||||
0x80000000
|
||||
| (u32::from(address.bus()) << 16)
|
||||
| (u32::from(address.device()) << 11)
|
||||
| (u32::from(address.function()) << 8)
|
||||
| u32::from(offset)
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
impl CfgAccess for Pci {
|
||||
unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
|
||||
self.iopl_once.call_once(Self::set_iopl);
|
||||
|
||||
let offset = u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||
let address = Self::address(bus, dev, func, offset);
|
||||
let offset =
|
||||
u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||
let address = Self::address(address, offset);
|
||||
|
||||
Pio::<u32>::new(0xCF8).write(address);
|
||||
Pio::<u32>::new(0xCFC).read()
|
||||
}
|
||||
|
||||
unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
self.read_nolock(bus, dev, func, offset)
|
||||
}
|
||||
|
||||
unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
self.iopl_once.call_once(Self::set_iopl);
|
||||
|
||||
let offset = u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||
let address = Self::address(bus, dev, func, offset);
|
||||
let offset =
|
||||
u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||
let address = Self::address(address, offset);
|
||||
|
||||
Pio::<u32>::new(0xCF8).write(address);
|
||||
Pio::<u32>::new(0xCFC).write(value);
|
||||
}
|
||||
unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
self.write_nolock(bus, dev, func, offset, value)
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
impl CfgAccess for Pci {
|
||||
unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
todo!("Pci::CfgAccess::read_nolock on this architecture")
|
||||
unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
todo!("Pci::CfgAccess::read on this architecture")
|
||||
}
|
||||
|
||||
unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32) {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
self.read_nolock(bus, dev, func, offset)
|
||||
}
|
||||
|
||||
unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
todo!("Pci::CfgAccess::write_nolock on this architecture")
|
||||
}
|
||||
unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
self.write_nolock(bus, dev, func, offset, value)
|
||||
todo!("Pci::CfgAccess::write on this architecture")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciIter<'pci> {
|
||||
pci: &'pci dyn CfgAccess,
|
||||
num: Option<u8>
|
||||
num: Option<u8>,
|
||||
}
|
||||
|
||||
impl<'pci> PciIter<'pci> {
|
||||
pub fn new(pci: &'pci dyn CfgAccess) -> Self {
|
||||
PciIter {
|
||||
pci,
|
||||
num: Some(0)
|
||||
}
|
||||
PciIter { pci, num: Some(0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'pci> Iterator for PciIter<'pci> {
|
||||
type Item = PciBus<'pci>;
|
||||
type Item = PciBus;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.num {
|
||||
Some(bus_num) => {
|
||||
let bus = PciBus {
|
||||
pci: self.pci,
|
||||
num: bus_num
|
||||
};
|
||||
let bus = PciBus { num: bus_num };
|
||||
self.num = bus_num.checked_add(1);
|
||||
Some(bus)
|
||||
},
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
+83
-56
@@ -1,12 +1,12 @@
|
||||
use std::{fmt, fs, io, mem, ptr, slice};
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, fs, io, mem, ptr, slice};
|
||||
|
||||
use syscall::PAGE_SIZE;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::pci::{CfgAccess, Pci, PciIter};
|
||||
use crate::pci::{CfgAccess, Pci, PciAddress, PciIter};
|
||||
|
||||
pub const MCFG_NAME: [u8; 4] = *b"MCFG";
|
||||
|
||||
@@ -31,7 +31,6 @@ unsafe impl plain::Plain for Mcfg {}
|
||||
|
||||
/// The "Memory Mapped Enhanced Configuration Space Base Address Allocation Structure" (yes, it's
|
||||
/// called that).
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PcieAlloc {
|
||||
@@ -45,16 +44,21 @@ unsafe impl plain::Plain for PcieAlloc {}
|
||||
|
||||
impl Mcfg {
|
||||
pub fn base_addr_structs(&self) -> &[PcieAlloc] {
|
||||
let total_length = mem::size_of::<Self>();
|
||||
let len = total_length - 44;
|
||||
let total_length = self.length as usize;
|
||||
let len = total_length - mem::size_of::<Mcfg>();
|
||||
// safe because the length cannot be changed arbitrarily
|
||||
unsafe { slice::from_raw_parts(&self.base_addrs as *const PcieAlloc, len / mem::size_of::<PcieAlloc>()) }
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
&self.base_addrs as *const PcieAlloc,
|
||||
len / mem::size_of::<PcieAlloc>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for Mcfg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Mcfg")
|
||||
.field("name", &"MCFG")
|
||||
.field("name", &self.name)
|
||||
.field("length", &{ self.length })
|
||||
.field("revision", &self.revision)
|
||||
.field("checksum", &self.checksum)
|
||||
@@ -83,37 +87,40 @@ impl Mcfgs {
|
||||
})
|
||||
}
|
||||
pub fn allocs<'a>(&'a self) -> impl Iterator<Item = &'a PcieAlloc> + 'a {
|
||||
self.tables().map(|table| table.base_addr_structs().iter()).flatten()
|
||||
self.tables()
|
||||
.map(|table| table.base_addr_structs().iter())
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn fetch() -> io::Result<Self> {
|
||||
let table_dir = fs::read_dir("acpi:tables")?;
|
||||
|
||||
let tables = table_dir.map(|table_direntry| -> io::Result<Option<_>> {
|
||||
let table_direntry = table_direntry?;
|
||||
let table_path = table_direntry.path();
|
||||
let mut tables = smallvec![];
|
||||
for table_direntry in table_dir {
|
||||
let table_path = table_direntry?.path();
|
||||
|
||||
let table_filename = match table_path.file_name() {
|
||||
Some(n) => n.to_str().ok_or(io::Error::new(io::ErrorKind::InvalidData, "Non-UTF-8 ACPI table filename"))?,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
if table_filename.starts_with("MCFG") {
|
||||
Ok(Some(fs::read(table_path)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
// Every directory entry has to have a filename unless
|
||||
// the filesystem (or in this case acpid) misbehaves.
|
||||
// If it misbehaves we have worse problems than pcid
|
||||
// crashing. `as_encoded_bytes()` returns some superset
|
||||
// of ASCII, so directly comparing it with an ASCII name
|
||||
// is fine.
|
||||
let table_filename = table_path.file_name().unwrap().as_encoded_bytes();
|
||||
if table_filename.get(0..4) == Some(&MCFG_NAME) {
|
||||
tables.push(fs::read(table_path)?);
|
||||
}
|
||||
}).filter_map(|result_option| result_option.transpose()).collect::<Result<SmallVec<_>, _>>()?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
tables,
|
||||
})
|
||||
Ok(Self { tables })
|
||||
}
|
||||
pub fn table_and_alloc_at_bus(&self, bus: u8) -> Option<(&Mcfg, &PcieAlloc)> {
|
||||
self.tables().find_map(|table| {
|
||||
Some((table, table.base_addr_structs().iter().find(|addr_struct| {
|
||||
(addr_struct.start_bus..addr_struct.end_bus).contains(&bus)
|
||||
})?))
|
||||
Some((
|
||||
table,
|
||||
table.base_addr_structs().iter().find(|addr_struct| {
|
||||
(addr_struct.start_bus..=addr_struct.end_bus).contains(&bus)
|
||||
})?,
|
||||
))
|
||||
})
|
||||
}
|
||||
pub fn at_bus(&self, bus: u8) -> Option<&PcieAlloc> {
|
||||
@@ -137,7 +144,7 @@ impl fmt::Debug for Mcfgs {
|
||||
pub struct Pcie {
|
||||
lock: Mutex<()>,
|
||||
mcfgs: Mcfgs,
|
||||
maps: Mutex<BTreeMap<(u8, u8, u8), *mut u32>>,
|
||||
maps: Mutex<BTreeMap<PciAddress, *mut u32>>,
|
||||
fallback: Arc<Pci>,
|
||||
}
|
||||
unsafe impl Send for Pcie {}
|
||||
@@ -154,34 +161,55 @@ impl Pcie {
|
||||
fallback,
|
||||
})
|
||||
}
|
||||
fn addr_offset_in_bytes(starting_bus: u8, bus: u8, dev: u8, func: u8, offset: u16) -> usize {
|
||||
fn addr_offset_in_bytes(starting_bus: u8, address: PciAddress, offset: u16) -> usize {
|
||||
assert_eq!(offset & 0xFFFC, offset, "pcie offset not dword-aligned");
|
||||
assert_eq!(offset & 0x0FFF, offset, "pcie offset larger than 4095");
|
||||
assert_eq!(dev & 0x1F, dev, "pcie dev number larger than 5 bits");
|
||||
assert_eq!(func & 0x7, func, "pcie func number larger than 3 bits");
|
||||
|
||||
(((bus - starting_bus) as usize) << 20) | ((dev as usize) << 15) | ((func as usize) << 12) | (offset as usize)
|
||||
assert_eq!(
|
||||
address.segment(),
|
||||
0,
|
||||
"multiple segments not yet implemented"
|
||||
);
|
||||
|
||||
(((address.bus() - starting_bus) as usize) << 20)
|
||||
| ((address.device() as usize) << 15)
|
||||
| ((address.function() as usize) << 12)
|
||||
| (offset as usize)
|
||||
}
|
||||
fn addr_offset_in_dwords(starting_bus: u8, bus: u8, dev: u8, func: u8, offset: u16) -> usize {
|
||||
Self::addr_offset_in_bytes(starting_bus, bus, dev, func, offset) / mem::size_of::<u32>()
|
||||
fn addr_offset_in_dwords(starting_bus: u8, address: PciAddress, offset: u16) -> usize {
|
||||
Self::addr_offset_in_bytes(starting_bus, address, offset) / mem::size_of::<u32>()
|
||||
}
|
||||
unsafe fn with_pointer<T, F: FnOnce(Option<&mut u32>) -> T>(&self, bus: u8, dev: u8, func: u8, offset: u16, f: F) -> T {
|
||||
let (base_address_phys, starting_bus) = match self.mcfgs.at_bus(bus) {
|
||||
unsafe fn with_pointer<T, F: FnOnce(Option<&mut u32>) -> T>(
|
||||
&self,
|
||||
address: PciAddress,
|
||||
offset: u16,
|
||||
f: F,
|
||||
) -> T {
|
||||
let (base_address_phys, starting_bus) = match self.mcfgs.at_bus(address.bus()) {
|
||||
Some(t) => (t.base_addr, t.start_bus),
|
||||
None => return f(None),
|
||||
};
|
||||
let mut maps_lock = self.maps.lock().unwrap();
|
||||
let virt_pointer = maps_lock.entry((bus, dev, func)).or_insert_with(|| {
|
||||
let virt_pointer = maps_lock.entry(address).or_insert_with(|| {
|
||||
common::physmap(
|
||||
base_address_phys as usize + Self::addr_offset_in_bytes(starting_bus, bus, dev, func, 0),
|
||||
base_address_phys as usize + Self::addr_offset_in_bytes(starting_bus, address, 0),
|
||||
PAGE_SIZE,
|
||||
common::Prot { read: true, write: true },
|
||||
common::Prot {
|
||||
read: true,
|
||||
write: true,
|
||||
},
|
||||
common::MemoryType::Uncacheable,
|
||||
).unwrap_or_else(|error| {
|
||||
panic!("failed to physmap pcie configuration space for {:2x}:{:2x}.{:2x}: {:?}", bus, dev, func, error)
|
||||
)
|
||||
.unwrap_or_else(|error| {
|
||||
panic!(
|
||||
"failed to physmap pcie configuration space for {}: {:?}",
|
||||
address, error
|
||||
)
|
||||
}) as *mut u32
|
||||
});
|
||||
f(Some(&mut *virt_pointer.offset((offset as usize / mem::size_of::<u32>()) as isize)))
|
||||
f(Some(&mut *virt_pointer.offset(
|
||||
(offset as usize / mem::size_of::<u32>()) as isize,
|
||||
)))
|
||||
}
|
||||
pub fn buses<'pcie>(&'pcie self) -> PciIter<'pcie> {
|
||||
PciIter::new(self)
|
||||
@@ -189,26 +217,25 @@ impl Pcie {
|
||||
}
|
||||
|
||||
impl CfgAccess for Pcie {
|
||||
unsafe fn read_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
self.with_pointer(bus, dev, func, offset, |pointer| match pointer {
|
||||
unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
|
||||
self.with_pointer(address, offset, |pointer| match pointer {
|
||||
Some(address) => ptr::read_volatile::<u32>(address),
|
||||
None => self.fallback.read(bus, dev, func, offset),
|
||||
None => self.fallback.read(address, offset),
|
||||
})
|
||||
}
|
||||
unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u16) -> u32 {
|
||||
|
||||
unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
self.read_nolock(bus, dev, func, offset)
|
||||
}
|
||||
unsafe fn write_nolock(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
self.with_pointer(bus, dev, func, offset, |pointer| match pointer {
|
||||
|
||||
self.with_pointer(address, offset, |pointer| match pointer {
|
||||
Some(address) => ptr::write_volatile::<u32>(address, value),
|
||||
None => { self.fallback.read(bus, dev, func, offset); }
|
||||
None => {
|
||||
self.fallback.write(address, offset, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u16, value: u32) {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
self.write_nolock(bus, dev, func, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Pcie {
|
||||
|
||||
Reference in New Issue
Block a user