feat: P0-P6 kernel scheduler + relibc threading comprehensive implementation
P0-P2: Barrier SMP, sigmask/pthread_kill races, robust mutexes, RT scheduling, POSIX sched API P3: PerCpuSched struct, per-CPU wiring, work stealing, load balancing, initial placement P4: 64-shard futex table, REQUEUE, PI futexes (LOCK_PI/UNLOCK_PI/TRYLOCK_PI), robust futexes, vruntime tracking, min-vruntime SCHED_OTHER selection P5: setpriority/getpriority, pthread_setaffinity_np, pthread_setname_np, pthread_setschedparam (Redox) P6: Cache-affine scheduling (last_cpu + vruntime bonus), NUMA topology kernel hints + numad userspace daemon Stability fixes: make_consistent stores 0 (dead TID fix), cond.rs error propagation, SPIN_COUNT adaptive spinning, Sys::open &str fix, PI futex CAS race, proc.rs lock ordering, barrier destroy Patches: 33 kernel + 58 relibc patches, all tracked in recipes Docs: KERNEL-SCHEDULER-MULTITHREAD-IMPROVEMENT-PLAN.md updated, SCHEDULER-REVIEW-FINAL.md created Architecture: NUMA topology parsing stays userspace (numad daemon), kernel stores lightweight NumaTopology hints
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||
index ce55b33f..c06bdec4 100644
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -21,6 +21,10 @@ enum Handle {
|
||||
Access,
|
||||
Device,
|
||||
Channel { addr: PciAddress, st: ChannelState },
|
||||
+ // Uevent surface for hotplug consumers. Opening uevent returns an object
|
||||
+ // from which device add/remove events can be read. Since pcid currently
|
||||
+ // only scans at startup, this surface is ready for hotplug polling consumers.
|
||||
+ Uevent,
|
||||
SchemeRoot,
|
||||
/// Represents an open handle to a device's bind endpoint
|
||||
Bind { addr: PciAddress },
|
||||
@@ -34,7 +38,7 @@ struct HandleWrapper {
|
||||
}
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. })
|
||||
+ matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. } | Self::Uevent)
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
@@ -96,6 +100,8 @@ impl SchemeSync for PciScheme {
|
||||
}
|
||||
} else if path == "access" {
|
||||
Handle::Access
|
||||
+ } else if path == "uevent" {
|
||||
+ Handle::Uevent
|
||||
} else {
|
||||
let idx = path.find('/').unwrap_or(path.len());
|
||||
let (addr_str, after) = path.split_at(idx);
|
||||
@@ -140,6 +146,7 @@ impl SchemeSync for PciScheme {
|
||||
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Uevent => (0, MODE_CHR | 0o644),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
stat.st_size = len as u64;
|
||||
@@ -164,6 +171,12 @@ impl SchemeSync for PciScheme {
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
+ Handle::Uevent => {
|
||||
+ // Uevent surface is ready for hotplug polling consumers.
|
||||
+ // pcid currently only scans at startup, so return empty (EAGAIN would indicate no data available).
|
||||
+ // Consumers can poll and re-read to check for new events.
|
||||
+ Ok(0)
|
||||
+ }
|
||||
Handle::SchemeRoot | Handle::Bind { .. } => Err(Error::new(EBADF)),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -199,7 +212,7 @@ impl SchemeSync for PciScheme {
|
||||
}
|
||||
Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } | Handle::Uevent => return Err(Error::new(ENOTDIR)),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
for (i, dent_name) in entries.iter().enumerate().skip(offset) {
|
||||
@@ -0,0 +1,20 @@
|
||||
diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
index f1c6d08e..a3f2e15c 100644
|
||||
--- a/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
+++ b/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
@@ -904,6 +904,7 @@ impl<const N: usize> Xhci<N> {
|
||||
match self.spawn_drivers(port_id) {
|
||||
Ok(()) => {
|
||||
info!("xhcid: uevent add device usb/{}", port_id.root_hub_port_num());
|
||||
+ // NOTE: driver-manager hotplug loop detects new USB devices via this log
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to spawn driver for port {}: `{}`", port_id, err)
|
||||
@@ -974,6 +975,8 @@ impl<const N: usize> Xhci<N> {
|
||||
info!("xhcid: uevent remove device usb/{}", port_id.root_hub_port_num());
|
||||
result
|
||||
} else {
|
||||
+ // NOTE: driver-manager hotplug loop detects USB device removal via this log
|
||||
debug!(
|
||||
"Attempted to detach from port {}, which wasn't previously attached.",
|
||||
port_id
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,844 @@
|
||||
diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs
|
||||
index 94a1eb17..c8919290 100644
|
||||
--- a/drivers/acpid/src/acpi.rs
|
||||
+++ b/drivers/acpid/src/acpi.rs
|
||||
@@ -52,9 +52,7 @@ impl SdtHeader {
|
||||
}
|
||||
}
|
||||
pub fn length(&self) -> usize {
|
||||
- self.length
|
||||
- .try_into()
|
||||
- .expect("expected usize to be at least 32 bits")
|
||||
+ self.length as usize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +130,9 @@ impl Drop for PhysmapGuard {
|
||||
pub struct Sdt(Arc<[u8]>);
|
||||
|
||||
impl Sdt {
|
||||
+ // SDT validation is split between parser and caller policy:
|
||||
+ // - this parser only decides whether a given byte slice is structurally valid,
|
||||
+ // - callers decide whether rejection is fatal (root [R|X]SDT) or degradable (child tables).
|
||||
pub fn new(slice: Arc<[u8]>) -> Result<Self, InvalidSdtError> {
|
||||
let header = match plain::from_bytes::<SdtHeader>(&slice) {
|
||||
Ok(header) => header,
|
||||
@@ -233,6 +234,177 @@ impl fmt::Debug for Sdt {
|
||||
pub struct Dsdt(Sdt);
|
||||
pub struct Ssdt(Sdt);
|
||||
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+pub enum AmlBootstrapMethod {
|
||||
+ HwdEnv,
|
||||
+ X86BiosFallback,
|
||||
+}
|
||||
+impl AmlBootstrapMethod {
|
||||
+ fn as_str(self) -> &'static str {
|
||||
+ match self {
|
||||
+ Self::HwdEnv => "hwd RSDP_ADDR/RSDP_SIZE handoff",
|
||||
+ Self::X86BiosFallback => "x86 BIOS fallback",
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Clone, Debug)]
|
||||
+pub struct AmlBootstrap {
|
||||
+ rsdp_addr: usize,
|
||||
+ rsdp_size: Option<usize>,
|
||||
+ method: AmlBootstrapMethod,
|
||||
+}
|
||||
+impl AmlBootstrap {
|
||||
+ pub fn from_env() -> Result<Self, Box<dyn Error>> {
|
||||
+ let rsdp_addr = usize::from_str_radix(&std::env::var("RSDP_ADDR")?, 16)?;
|
||||
+ let rsdp_size = match std::env::var("RSDP_SIZE") {
|
||||
+ Ok(size) => Some(usize::from_str_radix(&size, 16)?),
|
||||
+ Err(std::env::VarError::NotPresent) => None,
|
||||
+ Err(err) => return Err(Box::new(err)),
|
||||
+ };
|
||||
+
|
||||
+ Ok(Self {
|
||||
+ rsdp_addr,
|
||||
+ rsdp_size,
|
||||
+ method: AmlBootstrapMethod::HwdEnv,
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ pub fn x86_bios_fallback() -> Result<Option<Self>, Box<dyn Error>> {
|
||||
+ if let Some(rsdp_addr) = search_x86_bios_rsdp()? {
|
||||
+ return Ok(Some(Self {
|
||||
+ rsdp_addr,
|
||||
+ rsdp_size: None,
|
||||
+ method: AmlBootstrapMethod::X86BiosFallback,
|
||||
+ }));
|
||||
+ }
|
||||
+
|
||||
+ Ok(None)
|
||||
+ }
|
||||
+
|
||||
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
+ pub fn x86_bios_fallback() -> Result<Option<Self>, Box<dyn Error>> {
|
||||
+ Ok(None)
|
||||
+ }
|
||||
+
|
||||
+ pub fn log_bootstrap(&self) {
|
||||
+ log::info!(
|
||||
+ "acpid: AML bootstrap via {} (RSDP at {:#X})",
|
||||
+ self.method.as_str(),
|
||||
+ self.rsdp_addr
|
||||
+ );
|
||||
+
|
||||
+ if let Some(rsdp_size) = self.rsdp_size {
|
||||
+ log::debug!("acpid: AML bootstrap RSDP_SIZE={:#X}", rsdp_size);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+const RSDP_SIGNATURE: &[u8; 8] = b"RSD PTR ";
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+fn search_x86_bios_rsdp() -> Result<Option<usize>, Box<dyn Error>> {
|
||||
+ let ebda_segment = read_u16_physical(0x40E)?;
|
||||
+ let ebda_addr = usize::from(ebda_segment) << 4;
|
||||
+
|
||||
+ if ebda_addr != 0 {
|
||||
+ if let Some(rsdp_addr) = search_rsdp_region(ebda_addr, 1024)? {
|
||||
+ return Ok(Some(rsdp_addr));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ search_rsdp_region(0xE0000, 0x20000).map_err(Into::into)
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+fn read_u16_physical(physaddr: usize) -> std::io::Result<u16> {
|
||||
+ let start_page = physaddr / PAGE_SIZE * PAGE_SIZE;
|
||||
+ let page_offset = physaddr % PAGE_SIZE;
|
||||
+ let map = PhysmapGuard::map(start_page, 1)?;
|
||||
+ let bytes = map
|
||||
+ .get(page_offset..page_offset + mem::size_of::<u16>())
|
||||
+ .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "short BIOS map"))?;
|
||||
+
|
||||
+ Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+fn search_rsdp_region(physaddr: usize, length: usize) -> std::io::Result<Option<usize>> {
|
||||
+ let start_page = physaddr / PAGE_SIZE * PAGE_SIZE;
|
||||
+ let page_offset = physaddr % PAGE_SIZE;
|
||||
+ let mapped_len = page_offset + length;
|
||||
+ let page_count = mapped_len.div_ceil(PAGE_SIZE);
|
||||
+ let map = PhysmapGuard::map(start_page, page_count)?;
|
||||
+ let region = map.get(page_offset..page_offset + length).ok_or_else(|| {
|
||||
+ std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "short BIOS RSDP search window")
|
||||
+ })?;
|
||||
+
|
||||
+ for candidate_offset in (0..=length.saturating_sub(20)).step_by(16) {
|
||||
+ if region
|
||||
+ .get(candidate_offset..candidate_offset + RSDP_SIGNATURE.len())
|
||||
+ != Some(&RSDP_SIGNATURE[..])
|
||||
+ {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if rsdp_candidate_valid(®ion[candidate_offset..]) {
|
||||
+ return Ok(Some(physaddr + candidate_offset));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Ok(None)
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+fn rsdp_candidate_valid(candidate: &[u8]) -> bool {
|
||||
+ if candidate.len() < 20 || &candidate[..RSDP_SIGNATURE.len()] != RSDP_SIGNATURE {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if checksum_is_zero(&candidate[..20]).is_err() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ let revision = candidate[15];
|
||||
+ if revision < 2 {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ if candidate.len() < 36 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ let declared_length = u32::from_le_bytes([candidate[20], candidate[21], candidate[22], candidate[23]])
|
||||
+ as usize;
|
||||
+ if declared_length < 36 || candidate.len() < declared_length {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ checksum_is_zero(&candidate[..declared_length]).is_ok()
|
||||
+}
|
||||
+
|
||||
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+fn checksum_is_zero(bytes: &[u8]) -> Result<(), ()> {
|
||||
+ let checksum = bytes
|
||||
+ .iter()
|
||||
+ .copied()
|
||||
+ .fold(0_u8, |current_sum, item| current_sum.wrapping_add(item));
|
||||
+
|
||||
+ if checksum == 0 {
|
||||
+ Ok(())
|
||||
+ } else {
|
||||
+ Err(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+struct SleepTypeData {
|
||||
+ slp_typa: u16,
|
||||
+ slp_typb: u16,
|
||||
+}
|
||||
+
|
||||
// Current AML implementation builds the aml_context.namespace at startup,
|
||||
// but the cache for symbols is lazy-loaded when someone
|
||||
// reads from the acpi:/symbols scheme.
|
||||
@@ -245,15 +417,20 @@ pub struct AmlSymbols {
|
||||
symbol_cache: FxHashMap<String, String>,
|
||||
page_cache: Arc<Mutex<AmlPageCache>>,
|
||||
aml_region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
||||
+ aml_bootstrap: Option<AmlBootstrap>,
|
||||
}
|
||||
|
||||
impl AmlSymbols {
|
||||
- pub fn new(aml_region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler>)>) -> Self {
|
||||
+ pub fn new(
|
||||
+ aml_bootstrap: Option<AmlBootstrap>,
|
||||
+ aml_region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
||||
+ ) -> Self {
|
||||
Self {
|
||||
aml_context: None,
|
||||
symbol_cache: FxHashMap::default(),
|
||||
page_cache: Arc::new(Mutex::new(AmlPageCache::default())),
|
||||
aml_region_handlers,
|
||||
+ aml_bootstrap,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,9 +441,12 @@ impl AmlSymbols {
|
||||
let format_err = |err| format!("{:?}", err);
|
||||
let handler = AmlPhysMemHandler::new(pci_fd, Arc::clone(&self.page_cache));
|
||||
//TODO: use these parsed tables for the rest of acpid
|
||||
- let rsdp_address = usize::from_str_radix(&std::env::var("RSDP_ADDR")?, 16)?;
|
||||
+ let bootstrap = self
|
||||
+ .aml_bootstrap
|
||||
+ .as_ref()
|
||||
+ .ok_or_else(|| std::io::Error::other("AML bootstrap unavailable"))?;
|
||||
let tables =
|
||||
- unsafe { AcpiTables::from_rsdp(handler.clone(), rsdp_address).map_err(format_err)? };
|
||||
+ unsafe { AcpiTables::from_rsdp(handler.clone(), bootstrap.rsdp_addr).map_err(format_err)? };
|
||||
let platform = AcpiPlatform::new(tables, handler).map_err(format_err)?;
|
||||
let interpreter = Interpreter::new_from_platform(&platform).map_err(format_err)?;
|
||||
for (region, handler) in self.aml_region_handlers.drain(..) {
|
||||
@@ -316,7 +496,7 @@ impl AmlSymbols {
|
||||
.namespace
|
||||
.lock()
|
||||
.traverse(|level_aml_name, level| {
|
||||
- for (child_seg, handle) in level.values.iter() {
|
||||
+ for (child_seg, _handle) in level.values.iter() {
|
||||
if let Ok(aml_name) =
|
||||
AmlName::from_name_seg(child_seg.to_owned()).resolve(level_aml_name)
|
||||
{
|
||||
@@ -379,6 +559,7 @@ pub struct AcpiContext {
|
||||
tables: Vec<Sdt>,
|
||||
dsdt: Option<Dsdt>,
|
||||
fadt: Option<Fadt>,
|
||||
+ shutdown_s5: RwLock<Option<SleepTypeData>>,
|
||||
|
||||
aml_symbols: RwLock<AmlSymbols>,
|
||||
|
||||
@@ -426,27 +607,56 @@ impl AcpiContext {
|
||||
|
||||
pub fn init(
|
||||
rxsdt_physaddrs: impl Iterator<Item = u64>,
|
||||
+ aml_bootstrap: Option<AmlBootstrap>,
|
||||
ec: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
||||
) -> Self {
|
||||
- let tables = rxsdt_physaddrs
|
||||
- .map(|physaddr| {
|
||||
- let physaddr: usize = physaddr
|
||||
- .try_into()
|
||||
- .expect("expected ACPI addresses to be compatible with the current word size");
|
||||
-
|
||||
- log::trace!("TABLE AT {:#>08X}", physaddr);
|
||||
-
|
||||
- Sdt::load_from_physical(physaddr).expect("failed to load physical SDT")
|
||||
- })
|
||||
- .collect::<Vec<Sdt>>();
|
||||
+ // Child-table validation policy:
|
||||
+ // - checksum/length failures are degradable: warn, skip the table, continue boot,
|
||||
+ // - malformed FADT is handled separately as "raw-table-only" mode for ACPI control paths,
|
||||
+ // - MADT subtable interpretation is delegated to consumers, which must skip unknown entry
|
||||
+ // types instead of treating them as daemon-fatal.
|
||||
+ let mut tables = Vec::new();
|
||||
+ for physaddr in rxsdt_physaddrs {
|
||||
+ let physaddr: usize = match physaddr.try_into() {
|
||||
+ Ok(physaddr) => physaddr,
|
||||
+ Err(_) => {
|
||||
+ log::warn!(
|
||||
+ "acpid: skipping ACPI table at {:#X}: physical address out of range",
|
||||
+ physaddr
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ match Sdt::load_from_physical(physaddr) {
|
||||
+ Ok(table) => {
|
||||
+ log::debug!(
|
||||
+ "acpid: accepted ACPI table {} at {:#X}",
|
||||
+ String::from_utf8_lossy(&table.signature),
|
||||
+ physaddr
|
||||
+ );
|
||||
+ tables.push(table);
|
||||
+ }
|
||||
+ Err(TablePhysLoadError::Validity(InvalidSdtError::BadChecksum)) => {
|
||||
+ log::warn!(
|
||||
+ "acpid: skipping ACPI table at {:#X}: checksum validation failed",
|
||||
+ physaddr
|
||||
+ );
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ log::warn!("acpid: skipping ACPI table at {:#X}: {}", physaddr, err);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
let mut this = Self {
|
||||
tables,
|
||||
dsdt: None,
|
||||
fadt: None,
|
||||
+ shutdown_s5: RwLock::new(None),
|
||||
|
||||
// Temporary values
|
||||
- aml_symbols: RwLock::new(AmlSymbols::new(ec)),
|
||||
+ aml_symbols: RwLock::new(AmlSymbols::new(aml_bootstrap, ec)),
|
||||
|
||||
next_ctx: RwLock::new(0),
|
||||
|
||||
@@ -581,55 +791,26 @@ impl AcpiContext {
|
||||
let port = fadt.pm1a_control_block as u16;
|
||||
let mut val = 1 << 13;
|
||||
|
||||
- let aml_symbols = self.aml_symbols.read();
|
||||
-
|
||||
- let s5_aml_name = match acpi::aml::namespace::AmlName::from_str("\\_S5") {
|
||||
- Ok(aml_name) => aml_name,
|
||||
- Err(error) => {
|
||||
- log::error!("Could not build AmlName for \\_S5, {:?}", error);
|
||||
- return;
|
||||
- }
|
||||
- };
|
||||
-
|
||||
- let s5 = match &aml_symbols.aml_context {
|
||||
- Some(aml_context) => match aml_context.namespace.lock().get(s5_aml_name) {
|
||||
- Ok(s5) => s5,
|
||||
- Err(error) => {
|
||||
- log::error!("Cannot set S-state, missing \\_S5, {:?}", error);
|
||||
- return;
|
||||
+ if self.shutdown_s5.read().is_none() {
|
||||
+ match self.cache_shutdown_s5_from_ready_aml("existing AML context") {
|
||||
+ Ok(true) | Ok(false) => {}
|
||||
+ Err(err) => {
|
||||
+ log::warn!("acpid: _S5 was not ready at shutdown: {}", err);
|
||||
}
|
||||
- },
|
||||
- None => {
|
||||
- log::error!("Cannot set S-state, AML context not initialized");
|
||||
- return;
|
||||
}
|
||||
- };
|
||||
-
|
||||
- let package = match s5.deref() {
|
||||
- acpi::aml::object::Object::Package(package) => package,
|
||||
- _ => {
|
||||
- log::error!("Cannot set S-state, \\_S5 is not a package");
|
||||
- return;
|
||||
- }
|
||||
- };
|
||||
+ }
|
||||
|
||||
- let slp_typa = match package[0].deref() {
|
||||
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
|
||||
- _ => {
|
||||
- log::error!("typa is not an Integer");
|
||||
- return;
|
||||
- }
|
||||
- };
|
||||
- let slp_typb = match package[1].deref() {
|
||||
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
|
||||
- _ => {
|
||||
- log::error!("typb is not an Integer");
|
||||
- return;
|
||||
- }
|
||||
+ let Some(sleep_types) = *self.shutdown_s5.read() else {
|
||||
+ log::error!("Cannot set S-state, missing derived \\_S5 sleep types");
|
||||
+ return;
|
||||
};
|
||||
|
||||
- log::trace!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", slp_typa, slp_typb);
|
||||
- val |= slp_typa as u16;
|
||||
+ log::trace!(
|
||||
+ "Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}",
|
||||
+ sleep_types.slp_typa,
|
||||
+ sleep_types.slp_typb
|
||||
+ );
|
||||
+ val |= sleep_types.slp_typa;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
@@ -652,6 +833,86 @@ impl AcpiContext {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
+
|
||||
+ pub fn prime_shutdown_s5(&self, pci_fd: Option<&libredox::Fd>, source: &'static str) {
|
||||
+ match self.cache_shutdown_s5(pci_fd, source) {
|
||||
+ Ok(()) => {}
|
||||
+ Err(err) => {
|
||||
+ log::warn!("acpid: unable to derive _S5 from {}: {}", source, err);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn cache_shutdown_s5(
|
||||
+ &self,
|
||||
+ pci_fd: Option<&libredox::Fd>,
|
||||
+ source: &'static str,
|
||||
+ ) -> Result<(), String> {
|
||||
+ if self.shutdown_s5.read().is_some() {
|
||||
+ return Ok(());
|
||||
+ }
|
||||
+
|
||||
+ let mut aml_symbols = self.aml_symbols.write();
|
||||
+ let aml_context = aml_symbols
|
||||
+ .aml_context_mut(pci_fd)
|
||||
+ .map_err(|err| format!("AML not ready: {err}"))?;
|
||||
+ let sleep_types = extract_s5_sleep_types(aml_context)?;
|
||||
+
|
||||
+ *self.shutdown_s5.write() = Some(sleep_types);
|
||||
+ log::info!("acpid: _S5 derived from {}", source);
|
||||
+ Ok(())
|
||||
+ }
|
||||
+
|
||||
+ fn cache_shutdown_s5_from_ready_aml(&self, source: &'static str) -> Result<bool, String> {
|
||||
+ if self.shutdown_s5.read().is_some() {
|
||||
+ return Ok(true);
|
||||
+ }
|
||||
+
|
||||
+ let aml_symbols = self.aml_symbols.read();
|
||||
+ let Some(aml_context) = aml_symbols.aml_context.as_ref() else {
|
||||
+ return Ok(false);
|
||||
+ };
|
||||
+
|
||||
+ let sleep_types = extract_s5_sleep_types(aml_context)?;
|
||||
+ drop(aml_symbols);
|
||||
+
|
||||
+ *self.shutdown_s5.write() = Some(sleep_types);
|
||||
+ log::info!("acpid: _S5 derived from {}", source);
|
||||
+ Ok(true)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn extract_s5_sleep_types(
|
||||
+ aml_context: &Interpreter<AmlPhysMemHandler>,
|
||||
+) -> Result<SleepTypeData, String> {
|
||||
+ let s5_aml_name = acpi::aml::namespace::AmlName::from_str("\\_S5")
|
||||
+ .map_err(|error| format!("failed to build \\_S5 name: {error:?}"))?;
|
||||
+ let s5 = aml_context
|
||||
+ .namespace
|
||||
+ .lock()
|
||||
+ .get(s5_aml_name)
|
||||
+ .map_err(|error| format!("missing \\_S5: {error:?}"))?;
|
||||
+ let package = match s5.deref() {
|
||||
+ acpi::aml::object::Object::Package(package) => package,
|
||||
+ _ => return Err("\\_S5 is not a package".into()),
|
||||
+ };
|
||||
+
|
||||
+ let slp_typa = extract_sleep_type(package.get(0), "SLP_TYPa")?;
|
||||
+ let slp_typb = extract_sleep_type(package.get(1), "SLP_TYPb")?;
|
||||
+
|
||||
+ Ok(SleepTypeData { slp_typa, slp_typb })
|
||||
+}
|
||||
+
|
||||
+fn extract_sleep_type(value: Option<&WrappedObject>, label: &'static str) -> Result<u16, String> {
|
||||
+ let Some(value) = value else {
|
||||
+ return Err(format!("missing {label} in \\_S5 package"));
|
||||
+ };
|
||||
+
|
||||
+ match value.deref() {
|
||||
+ acpi::aml::object::Object::Integer(i) => u16::try_from(*i)
|
||||
+ .map_err(|_| format!("{label} out of range for PM1 control register")),
|
||||
+ _ => Err(format!("{label} is not an Integer")),
|
||||
+ }
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
@@ -760,45 +1021,66 @@ impl Deref for Fadt {
|
||||
type Target = FadtStruct;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
- plain::from_bytes::<FadtStruct>(&self.0 .0)
|
||||
- .expect("expected FADT struct to already be validated in Deref impl")
|
||||
+ match plain::from_bytes::<FadtStruct>(&self.0 .0) {
|
||||
+ Ok(fadt) => fadt,
|
||||
+ Err(plain::Error::TooShort) => unreachable!(
|
||||
+ "Fadt::new validates the minimum FADT size before constructing Fadt"
|
||||
+ ),
|
||||
+ Err(plain::Error::BadAlignment) => unreachable!(
|
||||
+ "plain::from_bytes reported bad alignment, but FadtStruct is #[repr(packed)]"
|
||||
+ ),
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
impl Fadt {
|
||||
pub fn new(sdt: Sdt) -> Option<Fadt> {
|
||||
- if sdt.signature != *b"FACP" || sdt.length() < mem::size_of::<Fadt>() {
|
||||
+ if sdt.signature != *b"FACP" || sdt.length() < mem::size_of::<FadtStruct>() {
|
||||
return None;
|
||||
}
|
||||
Some(Fadt(sdt))
|
||||
}
|
||||
|
||||
pub fn init(context: &mut AcpiContext) {
|
||||
- let fadt_sdt = context
|
||||
- .take_single_sdt(*b"FACP")
|
||||
- .expect("expected ACPI to always have a FADT");
|
||||
+ // FADT policy: this table is mandatory for ACPI control services such as shutdown/reboot.
|
||||
+ // If it is missing or malformed, acpid stays alive for diagnostics/raw tables but degrades
|
||||
+ // into raw-table-only mode instead of crashing the boot.
|
||||
+ let Some(fadt_sdt) = context.take_single_sdt(*b"FACP") else {
|
||||
+ log::error!("acpid: missing FADT; booting without ACPI control services");
|
||||
+ return;
|
||||
+ };
|
||||
|
||||
let fadt = match Fadt::new(fadt_sdt) {
|
||||
Some(fadt) => fadt,
|
||||
None => {
|
||||
- log::error!("Failed to find FADT");
|
||||
+ log::error!("acpid: corrupt FADT; booting without ACPI control services");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let dsdt_ptr = match fadt.acpi_2_struct() {
|
||||
- Some(fadt2) => usize::try_from(fadt2.x_dsdt).unwrap_or_else(|_| {
|
||||
- usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize")
|
||||
- }),
|
||||
- None => usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize"),
|
||||
+ Some(fadt2) if fadt2.x_dsdt != 0 => match usize::try_from(fadt2.x_dsdt) {
|
||||
+ Ok(dsdt_ptr) => dsdt_ptr,
|
||||
+ Err(_) => {
|
||||
+ log::warn!(
|
||||
+ "acpid: x_dsdt address out of range; falling back to 32-bit DSDT pointer"
|
||||
+ );
|
||||
+ fadt.dsdt as usize
|
||||
+ }
|
||||
+ },
|
||||
+ _ => fadt.dsdt as usize,
|
||||
};
|
||||
|
||||
log::debug!("FACP at {:X}", { dsdt_ptr });
|
||||
|
||||
- let dsdt_sdt = match Sdt::load_from_physical(fadt.dsdt as usize) {
|
||||
+ let dsdt_sdt = match Sdt::load_from_physical(dsdt_ptr) {
|
||||
Ok(dsdt) => dsdt,
|
||||
Err(error) => {
|
||||
- log::error!("Failed to load DSDT: {}", error);
|
||||
+ log::error!(
|
||||
+ "acpid: corrupt FADT/DSDT linkage (DSDT at {:#X}): booting without ACPI control services: {}",
|
||||
+ dsdt_ptr,
|
||||
+ error
|
||||
+ );
|
||||
return;
|
||||
}
|
||||
};
|
||||
diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs
|
||||
index 059254b3..25566553 100644
|
||||
--- a/drivers/acpid/src/main.rs
|
||||
+++ b/drivers/acpid/src/main.rs
|
||||
@@ -3,6 +3,7 @@ use std::fs::File;
|
||||
use std::mem;
|
||||
use std::ops::ControlFlow;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
+use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ::acpi::aml::op_region::{RegionHandler, RegionSpace};
|
||||
@@ -28,94 +29,206 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
|
||||
log::info!("acpid start");
|
||||
|
||||
- let rxsdt_raw_data: Arc<[u8]> = std::fs::read("/scheme/kernel.acpi/rxsdt")
|
||||
- .expect("acpid: failed to read `/scheme/kernel.acpi/rxsdt`")
|
||||
- .into();
|
||||
+ let rxsdt_raw_data: Arc<[u8]> = match std::fs::read("/scheme/kernel.acpi/rxsdt") {
|
||||
+ Ok(data) => data.into(),
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to read `/scheme/kernel.acpi/rxsdt`: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
if rxsdt_raw_data.is_empty() {
|
||||
log::info!("System doesn't use ACPI");
|
||||
daemon.ready();
|
||||
- std::process::exit(0);
|
||||
+ process::exit(0);
|
||||
}
|
||||
|
||||
- let sdt = self::acpi::Sdt::new(rxsdt_raw_data).expect("acpid: failed to parse [RX]SDT");
|
||||
+ // Root-table policy: if the kernel-provided [R|X]SDT is malformed, acpid cannot enumerate any
|
||||
+ // firmware tables at all. That is fatal to this daemon, but it must fail with a logged exit
|
||||
+ // rather than a panic on malformed firmware input.
|
||||
+ let sdt = match self::acpi::Sdt::new(rxsdt_raw_data) {
|
||||
+ Ok(sdt) => sdt,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to parse kernel [R|X]SDT: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ // AML bootstrap contract:
|
||||
+ // - preferred path: RSDP_ADDR[/RSDP_SIZE] inherited into acpid by the boot path,
|
||||
+ // - x86 fallback: bounded BIOS RSDP search when that explicit handoff is absent or unusable.
|
||||
+ let aml_bootstrap = match self::acpi::AmlBootstrap::from_env() {
|
||||
+ Ok(bootstrap) => {
|
||||
+ bootstrap.log_bootstrap();
|
||||
+ Some(bootstrap)
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ log::warn!(
|
||||
+ "acpid: explicit AML bootstrap handoff unavailable ({}); trying x86 BIOS fallback",
|
||||
+ err
|
||||
+ );
|
||||
|
||||
- let mut thirty_two_bit;
|
||||
- let mut sixty_four_bit;
|
||||
+ match self::acpi::AmlBootstrap::x86_bios_fallback() {
|
||||
+ Ok(Some(bootstrap)) => {
|
||||
+ bootstrap.log_bootstrap();
|
||||
+ Some(bootstrap)
|
||||
+ }
|
||||
+ Ok(None) => {
|
||||
+ log::warn!(
|
||||
+ "acpid: AML bootstrap unavailable; continuing without AML-backed ACPI services"
|
||||
+ );
|
||||
+ None
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ log::warn!(
|
||||
+ "acpid: x86 BIOS AML bootstrap fallback failed ({}); continuing without AML-backed ACPI services",
|
||||
+ err
|
||||
+ );
|
||||
+ None
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
|
||||
- let physaddrs_iter = match &sdt.signature {
|
||||
+ let physaddrs = match &sdt.signature {
|
||||
b"RSDT" => {
|
||||
- thirty_two_bit = sdt
|
||||
- .data()
|
||||
- .chunks(mem::size_of::<u32>())
|
||||
- // TODO: With const generics, the compiler has some way of doing this for static sizes.
|
||||
- .map(|chunk| <[u8; mem::size_of::<u32>()]>::try_from(chunk).unwrap())
|
||||
- .map(|chunk| u32::from_le_bytes(chunk))
|
||||
- .map(u64::from);
|
||||
-
|
||||
- &mut thirty_two_bit as &mut dyn Iterator<Item = u64>
|
||||
+ let chunks = sdt.data().chunks_exact(mem::size_of::<u32>());
|
||||
+ if !chunks.remainder().is_empty() {
|
||||
+ log::error!("acpid: malformed RSDT payload length {}", sdt.data().len());
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+
|
||||
+ chunks
|
||||
+ .map(|chunk| {
|
||||
+ let chunk = <[u8; mem::size_of::<u32>()]>::try_from(chunk)
|
||||
+ .map_err(|_| "invalid 32-bit RSDT entry width")?;
|
||||
+ Ok(u64::from(u32::from_le_bytes(chunk)))
|
||||
+ })
|
||||
+ .collect::<Result<Vec<u64>, &str>>()
|
||||
}
|
||||
b"XSDT" => {
|
||||
- sixty_four_bit = sdt
|
||||
- .data()
|
||||
- .chunks(mem::size_of::<u64>())
|
||||
- .map(|chunk| <[u8; mem::size_of::<u64>()]>::try_from(chunk).unwrap())
|
||||
- .map(|chunk| u64::from_le_bytes(chunk));
|
||||
+ let chunks = sdt.data().chunks_exact(mem::size_of::<u64>());
|
||||
+ if !chunks.remainder().is_empty() {
|
||||
+ log::error!("acpid: malformed XSDT payload length {}", sdt.data().len());
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- &mut sixty_four_bit as &mut dyn Iterator<Item = u64>
|
||||
+ chunks
|
||||
+ .map(|chunk| {
|
||||
+ let chunk = <[u8; mem::size_of::<u64>()]>::try_from(chunk)
|
||||
+ .map_err(|_| "invalid 64-bit XSDT entry width")?;
|
||||
+ Ok(u64::from_le_bytes(chunk))
|
||||
+ })
|
||||
+ .collect::<Result<Vec<u64>, &str>>()
|
||||
+ }
|
||||
+ _ => {
|
||||
+ log::error!(
|
||||
+ "acpid: expected kernel root table to be RSDT or XSDT, got {}",
|
||||
+ String::from_utf8_lossy(&sdt.signature)
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let physaddrs = match physaddrs {
|
||||
+ Ok(physaddrs) => physaddrs,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to decode root table pointers: {}", err);
|
||||
+ process::exit(1);
|
||||
}
|
||||
- _ => panic!("acpid: expected [RX]SDT from kernel to be either of those"),
|
||||
};
|
||||
|
||||
let region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler + 'static>)> = vec![
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
(RegionSpace::EmbeddedControl, Box::new(ec::Ec::new())),
|
||||
];
|
||||
- let acpi_context = self::acpi::AcpiContext::init(physaddrs_iter, region_handlers);
|
||||
+ let acpi_context = self::acpi::AcpiContext::init(physaddrs.into_iter(), aml_bootstrap, region_handlers);
|
||||
|
||||
// TODO: I/O permission bitmap?
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
- common::acquire_port_io_rights().expect("acpid: failed to set I/O privilege level to Ring 3");
|
||||
+ if let Err(err) = common::acquire_port_io_rights() {
|
||||
+ log::error!(
|
||||
+ "acpid: failed to set I/O privilege level to Ring 3: {}",
|
||||
+ err
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- let shutdown_pipe = File::open("/scheme/kernel.acpi/kstop")
|
||||
- .expect("acpid: failed to open `/scheme/kernel.acpi/kstop`");
|
||||
+ let shutdown_pipe = match File::open("/scheme/kernel.acpi/kstop") {
|
||||
+ Ok(file) => file,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to open `/scheme/kernel.acpi/kstop`: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
- let mut event_queue = RawEventQueue::new().expect("acpid: failed to create event queue");
|
||||
- let socket = Socket::nonblock().expect("acpid: failed to create disk scheme");
|
||||
+ let mut event_queue = match RawEventQueue::new() {
|
||||
+ Ok(event_queue) => event_queue,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to create event queue: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let socket = match Socket::nonblock() {
|
||||
+ Ok(socket) => socket,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to create acpi scheme socket: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket);
|
||||
let mut handler = Blocking::new(&socket, 16);
|
||||
|
||||
- event_queue
|
||||
- .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ)
|
||||
- .expect("acpid: failed to register shutdown pipe for event queue");
|
||||
- event_queue
|
||||
- .subscribe(socket.inner().raw(), 1, EventFlags::READ)
|
||||
- .expect("acpid: failed to register scheme socket for event queue");
|
||||
+ if let Err(err) = event_queue.subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ)
|
||||
+ {
|
||||
+ log::error!(
|
||||
+ "acpid: failed to register shutdown pipe for event queue: {}",
|
||||
+ err
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ if let Err(err) = event_queue.subscribe(socket.inner().raw(), 1, EventFlags::READ) {
|
||||
+ log::error!(
|
||||
+ "acpid: failed to register scheme socket for event queue: {}",
|
||||
+ err
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
- register_sync_scheme(&socket, "acpi", &mut scheme)
|
||||
- .expect("acpid: failed to register acpi scheme to namespace");
|
||||
+ if let Err(err) = register_sync_scheme(&socket, "acpi", &mut scheme) {
|
||||
+ log::error!("acpid: failed to register acpi scheme to namespace: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
daemon.ready();
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace");
|
||||
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||
+ log::error!("acpid: failed to enter null namespace: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
|
||||
let mut mounted = true;
|
||||
while mounted {
|
||||
- let Some(event) = event_queue
|
||||
- .next()
|
||||
- .transpose()
|
||||
- .expect("acpid: failed to read event file")
|
||||
- else {
|
||||
+ let event = match event_queue.next().transpose() {
|
||||
+ Ok(event) => event,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to read event file: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ let Some(event) = event else {
|
||||
break;
|
||||
};
|
||||
|
||||
if event.fd == socket.inner().raw() {
|
||||
loop {
|
||||
- match handler
|
||||
- .process_requests_nonblocking(&mut scheme)
|
||||
- .expect("acpid: failed to process requests")
|
||||
- {
|
||||
+ match match handler.process_requests_nonblocking(&mut scheme) {
|
||||
+ Ok(flow) => flow,
|
||||
+ Err(err) => {
|
||||
+ log::error!("acpid: failed to process requests: {}", err);
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ } {
|
||||
ControlFlow::Continue(()) => {}
|
||||
ControlFlow::Break(()) => break,
|
||||
}
|
||||
diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs
|
||||
index 5a5040c3..6e57624a 100644
|
||||
--- a/drivers/acpid/src/scheme.rs
|
||||
+++ b/drivers/acpid/src/scheme.rs
|
||||
@@ -474,6 +474,8 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
return Err(Error::new(EINVAL));
|
||||
} else {
|
||||
self.pci_fd = Some(new_fd);
|
||||
+ self.ctx
|
||||
+ .prime_shutdown_s5(self.pci_fd.as_ref(), "PCI-backed AML handoff");
|
||||
}
|
||||
|
||||
Ok(num_fds)
|
||||
@@ -0,0 +1,398 @@
|
||||
--- a/drivers/pcid/src/cfg_access/mod.rs
|
||||
+++ b/drivers/pcid/src/cfg_access/mod.rs
|
||||
@@ -349,6 +349,10 @@
|
||||
let bus_addr = self.bus_addr(address.segment(), address.bus())?;
|
||||
Some(unsafe { bus_addr.add(Self::bus_addr_offset_in_dwords(address, offset)) })
|
||||
}
|
||||
+
|
||||
+ pub fn has_extended_config(&self, address: PciAddress) -> bool {
|
||||
+ self.mmio_addr(address, 0x100).is_some()
|
||||
+ }
|
||||
}
|
||||
|
||||
impl ConfigRegionAccess for Pcie {
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -5,12 +5,61 @@
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use scheme_utils::HandleMap;
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
-use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EALREADY};
|
||||
+use syscall::error::{
|
||||
+ Error, Result, EACCES, EALREADY, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EROFS,
|
||||
+};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR, O_DIRECTORY, O_STAT};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::ENOLCK;
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
+
|
||||
+const PCIE_EXTENDED_CAPABILITY_AER: u16 = 0x0001;
|
||||
+
|
||||
+#[derive(Clone, Copy)]
|
||||
+enum AerRegisterName {
|
||||
+ UncorStatus,
|
||||
+ UncorMask,
|
||||
+ UncorSeverity,
|
||||
+ CorStatus,
|
||||
+ CorMask,
|
||||
+ Cap,
|
||||
+ HeaderLog,
|
||||
+}
|
||||
+
|
||||
+impl AerRegisterName {
|
||||
+ fn from_path(path: &str) -> Option<Self> {
|
||||
+ Some(match path {
|
||||
+ "uncor_status" => Self::UncorStatus,
|
||||
+ "uncor_mask" => Self::UncorMask,
|
||||
+ "uncor_severity" => Self::UncorSeverity,
|
||||
+ "cor_status" => Self::CorStatus,
|
||||
+ "cor_mask" => Self::CorMask,
|
||||
+ "cap" => Self::Cap,
|
||||
+ "header_log" => Self::HeaderLog,
|
||||
+ _ => return None,
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ const fn offset(self) -> u16 {
|
||||
+ match self {
|
||||
+ Self::UncorStatus => 0x00,
|
||||
+ Self::UncorMask => 0x04,
|
||||
+ Self::UncorSeverity => 0x08,
|
||||
+ Self::CorStatus => 0x0C,
|
||||
+ Self::CorMask => 0x10,
|
||||
+ Self::Cap => 0x14,
|
||||
+ Self::HeaderLog => 0x18,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const fn len(self) -> usize {
|
||||
+ match self {
|
||||
+ Self::HeaderLog => 16,
|
||||
+ _ => 4,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
pub struct PciScheme {
|
||||
handles: HandleMap<HandleWrapper>,
|
||||
@@ -20,13 +69,27 @@
|
||||
binds: HashMap<String, u32>,
|
||||
}
|
||||
enum Handle {
|
||||
- TopLevel { entries: Vec<String> },
|
||||
+ TopLevel {
|
||||
+ entries: Vec<String>,
|
||||
+ },
|
||||
Access,
|
||||
- Device,
|
||||
- Channel { addr: PciAddress, st: ChannelState },
|
||||
+ Device {
|
||||
+ addr: PciAddress,
|
||||
+ },
|
||||
+ Channel {
|
||||
+ addr: PciAddress,
|
||||
+ st: ChannelState,
|
||||
+ },
|
||||
SchemeRoot,
|
||||
/// Represents an open handle to a device's bind endpoint
|
||||
- Bind { addr: PciAddress },
|
||||
+ Bind {
|
||||
+ addr: PciAddress,
|
||||
+ },
|
||||
+ AerDir,
|
||||
+ Aer {
|
||||
+ addr: PciAddress,
|
||||
+ register: AerRegisterName,
|
||||
+ },
|
||||
/// Uevent surface for hotplug consumers. Opening uevent returns an object
|
||||
/// from which device add/remove events can be read. Since pcid currently
|
||||
/// only scans at startup, this surface is ready for hotplug polling consumers.
|
||||
@@ -38,13 +101,23 @@
|
||||
}
|
||||
impl Handle {
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. } | Self::Uevent)
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access
|
||||
+ | Self::Channel { .. }
|
||||
+ | Self::Bind { .. }
|
||||
+ | Self::Aer { .. }
|
||||
+ | Self::Uevent
|
||||
+ )
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
}
|
||||
fn requires_root(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. })
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access | Self::Channel { .. } | Self::Bind { .. }
|
||||
+ )
|
||||
}
|
||||
fn is_scheme_root(&self) -> bool {
|
||||
matches!(self, Self::SchemeRoot)
|
||||
@@ -57,6 +130,16 @@
|
||||
}
|
||||
|
||||
const DEVICE_CONTENTS: &[&str] = &["channel", "bind"];
|
||||
+const DEVICE_AER_CONTENTS: &[&str] = &["channel", "bind", "aer"];
|
||||
+const AER_CONTENTS: &[&str] = &[
|
||||
+ "uncor_status",
|
||||
+ "uncor_mask",
|
||||
+ "uncor_severity",
|
||||
+ "cor_status",
|
||||
+ "cor_mask",
|
||||
+ "cap",
|
||||
+ "header_log",
|
||||
+];
|
||||
|
||||
impl PciScheme {
|
||||
pub fn access(&mut self) -> usize {
|
||||
@@ -141,7 +224,12 @@
|
||||
|
||||
let (len, mode) = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
- Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
+ Handle::Device { addr } => (
|
||||
+ Self::device_entries(&self.pcie, addr).len(),
|
||||
+ MODE_DIR | 0o755,
|
||||
+ ),
|
||||
+ Handle::AerDir => (AER_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
+ Handle::Aer { register, .. } => (register.len(), MODE_CHR | 0o444),
|
||||
Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => (0, MODE_CHR | 0o600),
|
||||
Handle::Uevent => (0, MODE_CHR | 0o644),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
@@ -154,7 +242,7 @@
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
- _offset: u64,
|
||||
+ offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
@@ -166,11 +254,14 @@
|
||||
|
||||
match handle.inner {
|
||||
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||
- Handle::Device => Err(Error::new(EISDIR)),
|
||||
+ Handle::Device { .. } | Handle::AerDir => Err(Error::new(EISDIR)),
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
+ Handle::Aer { addr, register } => {
|
||||
+ Self::read_aer_register(&self.pcie, addr, register, buf, offset)
|
||||
+ }
|
||||
Handle::Uevent => {
|
||||
// Uevent surface is ready for hotplug polling consumers.
|
||||
// pcid currently only scans at startup, so return empty (EAGAIN would indicate no data available).
|
||||
@@ -209,8 +300,15 @@
|
||||
}
|
||||
return Ok(buf);
|
||||
}
|
||||
- Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } | Handle::Uevent => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Device { addr } => Self::device_entries(&self.pcie, addr),
|
||||
+ Handle::AerDir => AER_CONTENTS,
|
||||
+ Handle::Access
|
||||
+ | Handle::Channel { .. }
|
||||
+ | Handle::Bind { .. }
|
||||
+ | Handle::Aer { .. }
|
||||
+ | Handle::Uevent => {
|
||||
+ return Err(Error::new(ENOTDIR));
|
||||
+ }
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
@@ -243,6 +341,7 @@
|
||||
Handle::Channel { addr, ref mut st } => {
|
||||
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||
}
|
||||
+ Handle::Aer { .. } => Err(Error::new(EROFS)),
|
||||
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -357,45 +456,151 @@
|
||||
binds: HashMap::new(),
|
||||
}
|
||||
}
|
||||
- fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str, ctx: &CallerCtx) -> Result<Handle> {
|
||||
+ fn device_entries(pcie: &Pcie, addr: PciAddress) -> &'static [&'static str] {
|
||||
+ if Self::find_pcie_extended_capability(pcie, addr, PCIE_EXTENDED_CAPABILITY_AER).is_some() {
|
||||
+ DEVICE_AER_CONTENTS
|
||||
+ } else {
|
||||
+ DEVICE_CONTENTS
|
||||
+ }
|
||||
+ }
|
||||
+ fn find_pcie_extended_capability(
|
||||
+ pcie: &Pcie,
|
||||
+ addr: PciAddress,
|
||||
+ capability_id: u16,
|
||||
+ ) -> Option<u16> {
|
||||
+ if !pcie.has_extended_config(addr) {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let mut offset = 0x100_u16;
|
||||
+
|
||||
+ while offset <= 0xFFC {
|
||||
+ let header = unsafe { pcie.read(addr, offset) };
|
||||
+ if header == 0 || header == u32::MAX {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ if (header & 0xFFFF) as u16 == capability_id {
|
||||
+ return Some(offset);
|
||||
+ }
|
||||
+
|
||||
+ let next = ((header >> 20) & 0xFFF) as u16;
|
||||
+ if next < 0x100 || next <= offset || next > 0xFFC || next % 4 != 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+ offset = next;
|
||||
+ }
|
||||
+
|
||||
+ None
|
||||
+ }
|
||||
+ fn read_file_bytes(data: &[u8], buf: &mut [u8], offset: u64) -> Result<usize> {
|
||||
+ let Ok(offset) = usize::try_from(offset) else {
|
||||
+ return Ok(0);
|
||||
+ };
|
||||
+ if offset >= data.len() {
|
||||
+ return Ok(0);
|
||||
+ }
|
||||
+
|
||||
+ let count = std::cmp::min(buf.len(), data.len() - offset);
|
||||
+ buf[..count].copy_from_slice(&data[offset..offset + count]);
|
||||
+ Ok(count)
|
||||
+ }
|
||||
+ fn read_aer_register(
|
||||
+ pcie: &Pcie,
|
||||
+ addr: PciAddress,
|
||||
+ register: AerRegisterName,
|
||||
+ buf: &mut [u8],
|
||||
+ offset: u64,
|
||||
+ ) -> Result<usize> {
|
||||
+ let Some(aer_base) =
|
||||
+ Self::find_pcie_extended_capability(pcie, addr, PCIE_EXTENDED_CAPABILITY_AER)
|
||||
+ else {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ };
|
||||
+
|
||||
+ let mut data = [0_u8; 16];
|
||||
+ for (index, chunk) in data[..register.len()].chunks_exact_mut(4).enumerate() {
|
||||
+ let index = u16::try_from(index).map_err(|_| Error::new(EIO))?;
|
||||
+ let value = unsafe { pcie.read(addr, aer_base + register.offset() + index * 4) };
|
||||
+ chunk.copy_from_slice(&value.to_le_bytes());
|
||||
+ }
|
||||
+
|
||||
+ Self::read_file_bytes(&data[..register.len()], buf, offset)
|
||||
+ }
|
||||
+ fn parse_after_pci_addr(
|
||||
+ &mut self,
|
||||
+ addr: PciAddress,
|
||||
+ after: &str,
|
||||
+ ctx: &CallerCtx,
|
||||
+ ) -> Result<Handle> {
|
||||
if after.chars().next().map_or(false, |c| c != '/') {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let func = self.tree.get_mut(&addr).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
Ok(if after.is_empty() {
|
||||
- Handle::Device
|
||||
+ Handle::Device { addr }
|
||||
} else {
|
||||
let path = &after[1..];
|
||||
|
||||
- match path {
|
||||
- "channel" => {
|
||||
- if func.enabled {
|
||||
- return Err(Error::new(ENOLCK));
|
||||
+ if path == "aer" {
|
||||
+ if Self::find_pcie_extended_capability(
|
||||
+ &self.pcie,
|
||||
+ addr,
|
||||
+ PCIE_EXTENDED_CAPABILITY_AER,
|
||||
+ )
|
||||
+ .is_none()
|
||||
+ {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::AerDir
|
||||
+ } else if let Some(register_name) = path.strip_prefix("aer/") {
|
||||
+ let register =
|
||||
+ AerRegisterName::from_path(register_name).ok_or(Error::new(ENOENT))?;
|
||||
+ if Self::find_pcie_extended_capability(
|
||||
+ &self.pcie,
|
||||
+ addr,
|
||||
+ PCIE_EXTENDED_CAPABILITY_AER,
|
||||
+ )
|
||||
+ .is_none()
|
||||
+ {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::Aer { addr, register }
|
||||
+ } else {
|
||||
+ match path {
|
||||
+ "channel" => {
|
||||
+ if func.enabled {
|
||||
+ return Err(Error::new(ENOLCK));
|
||||
+ }
|
||||
+ func.inner.legacy_interrupt_line = crate::enable_function(
|
||||
+ &self.pcie,
|
||||
+ &mut func.endpoint_header,
|
||||
+ &mut func.capabilities,
|
||||
+ );
|
||||
+ func.enabled = true;
|
||||
+ Handle::Channel {
|
||||
+ addr,
|
||||
+ st: ChannelState::AwaitingData,
|
||||
+ }
|
||||
}
|
||||
- func.inner.legacy_interrupt_line = crate::enable_function(
|
||||
- &self.pcie,
|
||||
- &mut func.endpoint_header,
|
||||
- &mut func.capabilities,
|
||||
- );
|
||||
- func.enabled = true;
|
||||
- Handle::Channel {
|
||||
- addr,
|
||||
- st: ChannelState::AwaitingData,
|
||||
+ "bind" => {
|
||||
+ let addr_str = format!("{}", addr);
|
||||
+ if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
+ log::info!(
|
||||
+ "pcid: device {} already bound by pid {}",
|
||||
+ addr_str,
|
||||
+ owner_pid
|
||||
+ );
|
||||
+ return Err(Error::new(EALREADY));
|
||||
+ }
|
||||
+ let caller_pid = u32::try_from(ctx.pid).map_err(|_| Error::new(EINVAL))?;
|
||||
+ self.binds.insert(addr_str.clone(), caller_pid);
|
||||
+ log::info!("pcid: device {} bound by pid {}", addr_str, caller_pid);
|
||||
+ Handle::Bind { addr }
|
||||
}
|
||||
- }
|
||||
- "bind" => {
|
||||
- let addr_str = format!("{}", addr);
|
||||
- if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
- log::info!("pcid: device {} already bound by pid {}", addr_str, owner_pid);
|
||||
- return Err(Error::new(EALREADY));
|
||||
- }
|
||||
- let caller_pid = ctx.pid;
|
||||
- self.binds.insert(addr_str.clone(), caller_pid);
|
||||
- log::info!("pcid: device {} bound by pid {}", addr_str, caller_pid);
|
||||
- Handle::Bind { addr }
|
||||
- }
|
||||
- _ => return Err(Error::new(ENOENT)),
|
||||
+ _ => return Err(Error::new(ENOENT)),
|
||||
+ }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||
index bb9f39a3..06be6267 100644
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -1,11 +1,11 @@
|
||||
-use std::collections::{BTreeMap, VecDeque};
|
||||
+use std::collections::{BTreeMap, HashMap, VecDeque};
|
||||
|
||||
use pci_types::{ConfigRegionAccess, PciAddress};
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use scheme_utils::HandleMap;
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
-use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR};
|
||||
+use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EALREADY};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR, O_DIRECTORY, O_STAT};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::ENOLCK;
|
||||
@@ -16,6 +16,8 @@ pub struct PciScheme {
|
||||
handles: HandleMap<HandleWrapper>,
|
||||
pub pcie: Pcie,
|
||||
pub tree: BTreeMap<PciAddress, crate::Func>,
|
||||
+ /// Maps device address string (e.g. "0000:00:14.0") to owning PID
|
||||
+ binds: HashMap<String, u32>,
|
||||
}
|
||||
enum Handle {
|
||||
TopLevel { entries: Vec<String> },
|
||||
@@ -23,6 +25,12 @@ enum Handle {
|
||||
Device,
|
||||
Channel { addr: PciAddress, st: ChannelState },
|
||||
SchemeRoot,
|
||||
+ /// Represents an open handle to a device's bind endpoint
|
||||
+ Bind { addr: PciAddress },
|
||||
+ /// Uevent surface for hotplug consumers. Opening uevent returns an object
|
||||
+ /// from which device add/remove events can be read. Since pcid currently
|
||||
+ /// only scans at startup, this surface is ready for hotplug polling consumers.
|
||||
+ Uevent,
|
||||
}
|
||||
struct HandleWrapper {
|
||||
inner: Handle,
|
||||
@@ -30,14 +38,13 @@ struct HandleWrapper {
|
||||
}
|
||||
impl Handle {
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. })
|
||||
+ matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. } | Self::Uevent)
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
}
|
||||
- // TODO: capability rather than root
|
||||
fn requires_root(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. })
|
||||
+ matches!(self, Self::Access | Self::Channel { .. } | Self::Bind { .. })
|
||||
}
|
||||
fn is_scheme_root(&self) -> bool {
|
||||
matches!(self, Self::SchemeRoot)
|
||||
@@ -49,7 +56,7 @@ enum ChannelState {
|
||||
AwaitingResponseRead(VecDeque<u8>),
|
||||
}
|
||||
|
||||
-const DEVICE_CONTENTS: &[&str] = &["channel"];
|
||||
+const DEVICE_CONTENTS: &[&str] = &["channel", "bind"];
|
||||
|
||||
impl PciScheme {
|
||||
pub fn access(&mut self) -> usize {
|
||||
@@ -88,22 +95,25 @@ impl SchemeSync for PciScheme {
|
||||
let path = path.trim_matches('/');
|
||||
|
||||
let handle = if path.is_empty() {
|
||||
- Handle::TopLevel {
|
||||
- entries: self
|
||||
- .tree
|
||||
- .iter()
|
||||
- // FIXME remove replacement of : once the old scheme format is no longer supported.
|
||||
- .map(|(addr, _)| format!("{}", addr).replace(':', "--"))
|
||||
- .collect::<Vec<_>>(),
|
||||
- }
|
||||
+ let mut entries: Vec<String> = self
|
||||
+ .tree
|
||||
+ .iter()
|
||||
+ // FIXME remove replacement of : once the old scheme format is no longer supported.
|
||||
+ .map(|(addr, _)| format!("{}", addr).replace(':', "--"))
|
||||
+ .collect();
|
||||
+ entries.push(String::from("uevent"));
|
||||
+ entries.push(String::from("access"));
|
||||
+ Handle::TopLevel { entries }
|
||||
} else if path == "access" {
|
||||
Handle::Access
|
||||
+ } else if path == "uevent" {
|
||||
+ Handle::Uevent
|
||||
} else {
|
||||
let idx = path.find('/').unwrap_or(path.len());
|
||||
let (addr_str, after) = path.split_at(idx);
|
||||
let addr = parse_pci_addr(addr_str).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
- self.parse_after_pci_addr(addr, after)?
|
||||
+ self.parse_after_pci_addr(addr, after, ctx)?
|
||||
};
|
||||
|
||||
let stat = flags & O_STAT != 0;
|
||||
@@ -132,7 +142,8 @@ impl SchemeSync for PciScheme {
|
||||
let (len, mode) = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
- Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Uevent => (0, MODE_CHR | 0o644),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
stat.st_size = len as u64;
|
||||
@@ -160,7 +171,13 @@ impl SchemeSync for PciScheme {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
- Handle::SchemeRoot => Err(Error::new(EBADF)),
|
||||
+ Handle::Uevent => {
|
||||
+ // Uevent surface is ready for hotplug polling consumers.
|
||||
+ // pcid currently only scans at startup, so return empty (EAGAIN would indicate no data available).
|
||||
+ // Consumers can poll and re-read to check for new events.
|
||||
+ Ok(0)
|
||||
+ }
|
||||
+ Handle::SchemeRoot | Handle::Bind { .. } => Err(Error::new(EBADF)),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
@@ -193,7 +210,7 @@ impl SchemeSync for PciScheme {
|
||||
return Ok(buf);
|
||||
}
|
||||
Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } | Handle::Uevent => return Err(Error::new(ENOTDIR)),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
@@ -316,6 +333,16 @@ impl SchemeSync for PciScheme {
|
||||
func.enabled = false;
|
||||
}
|
||||
}
|
||||
+ Some(HandleWrapper {
|
||||
+ inner: Handle::Bind { addr },
|
||||
+ ..
|
||||
+ }) => {
|
||||
+ let addr_str = format!("{}", addr);
|
||||
+ if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
+ log::info!("pcid: device {} unbound by pid {}", addr_str, owner_pid);
|
||||
+ }
|
||||
+ self.binds.remove(&addr_str);
|
||||
+ }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -327,9 +354,10 @@ impl PciScheme {
|
||||
handles: HandleMap::new(),
|
||||
pcie,
|
||||
tree: BTreeMap::new(),
|
||||
+ binds: HashMap::new(),
|
||||
}
|
||||
}
|
||||
- fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str) -> Result<Handle> {
|
||||
+ fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str, ctx: &CallerCtx) -> Result<Handle> {
|
||||
if after.chars().next().map_or(false, |c| c != '/') {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
@@ -356,6 +384,17 @@ impl PciScheme {
|
||||
st: ChannelState::AwaitingData,
|
||||
}
|
||||
}
|
||||
+ "bind" => {
|
||||
+ let addr_str = format!("{}", addr);
|
||||
+ if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
+ log::info!("pcid: device {} already bound by pid {}", addr_str, owner_pid);
|
||||
+ return Err(Error::new(EALREADY));
|
||||
+ }
|
||||
+ let caller_pid = ctx.pid;
|
||||
+ self.binds.insert(addr_str.clone(), caller_pid);
|
||||
+ log::info!("pcid: device {} bound by pid {}", addr_str, caller_pid);
|
||||
+ Handle::Bind { addr }
|
||||
+ }
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
}
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user