Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements
This commit is contained in:
@@ -29,18 +29,3 @@ diff -urN a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2004,8 +2008,12 @@
|
||||
|
||||
context.tables.push(dsdt_sdt);
|
||||
|
||||
- if let Err(error) = context.refresh_s5_values() {
|
||||
- log::warn!("Failed to evaluate \\_S5 during FADT init: {error}");
|
||||
+ if context.pci_ready() {
|
||||
+ if let Err(error) = context.refresh_s5_values() {
|
||||
+ log::warn!("Failed to evaluate \\_S5 during FADT init: {error}");
|
||||
+ }
|
||||
+ } else {
|
||||
+ log::debug!("Deferring \\_S5 evaluation until PCI registration");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,11 +280,19 @@ index 9e776232..8cecf423 100644
|
||||
@@ -63,6 +74,7 @@ members = [
|
||||
"drivers/storage/usbscsid",
|
||||
"drivers/storage/virtio-blkd",
|
||||
|
||||
|
||||
+ "drivers/usb/ucsid",
|
||||
"drivers/usb/xhcid",
|
||||
"drivers/usb/usbctl",
|
||||
"drivers/usb/usbhubd",
|
||||
@@ -82,6 +93,7 @@ fdt = "0.1.5"
|
||||
edid = "0.3.0" #TODO: edid is abandoned, fork it and maintain?
|
||||
fdt = "0.1.5"
|
||||
+nom = "3.2.0" # transitive dep via edid; needed to match on IResult variants
|
||||
libc = "0.2.181"
|
||||
log = "0.4"
|
||||
libredox = "0.1.16"
|
||||
parking_lot = "0.12"
|
||||
diff --git a/drivers/acpi-resource/Cargo.toml b/drivers/acpi-resource/Cargo.toml
|
||||
new file mode 100644
|
||||
index 00000000..f30c6d02
|
||||
@@ -998,79 +1006,6 @@ index 00000000..57ae4b4b
|
||||
+ assert!(matches!(&resources[0], ResourceDescriptor::GpioInt(_)));
|
||||
+ }
|
||||
+}
|
||||
diff --git a/drivers/acpid/Cargo.toml b/drivers/acpid/Cargo.toml
|
||||
index f03a4ccb..712b6d6e 100644
|
||||
--- a/drivers/acpid/Cargo.toml
|
||||
+++ b/drivers/acpid/Cargo.toml
|
||||
@@ -8,6 +8,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
+acpi-resource = { path = "../acpi-resource" }
|
||||
acpi = { git = "https://github.com/jackpot51/acpi.git" }
|
||||
arrayvec = "0.7.6"
|
||||
log.workspace = true
|
||||
diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs
|
||||
index e388cd46..3b0deeab 100644
|
||||
--- a/drivers/acpid/src/main.rs
|
||||
+++ b/drivers/acpid/src/main.rs
|
||||
@@ -15,8 +15,9 @@ mod aml_physmem;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
mod ec;
|
||||
|
||||
-mod sleep;
|
||||
+mod resources;
|
||||
mod scheme;
|
||||
+mod sleep;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum StartupError {
|
||||
@@ -179,15 +180,18 @@ fn run_acpid(daemon: daemon::Daemon) -> Result<(), StartupError> {
|
||||
while mounted {
|
||||
let Some(event) = event_queue.next().transpose().map_err(|error| {
|
||||
StartupError::runtime("failed to read event file", error.to_string())
|
||||
- })? else {
|
||||
+ })?
|
||||
+ else {
|
||||
break;
|
||||
};
|
||||
|
||||
if event.fd == socket.inner().raw() {
|
||||
loop {
|
||||
- match handler.process_requests_nonblocking(&mut scheme).map_err(|error| {
|
||||
- StartupError::runtime("failed to process requests", error.to_string())
|
||||
- })? {
|
||||
+ match handler
|
||||
+ .process_requests_nonblocking(&mut scheme)
|
||||
+ .map_err(|error| {
|
||||
+ StartupError::runtime("failed to process requests", error.to_string())
|
||||
+ })? {
|
||||
ControlFlow::Continue(()) => {}
|
||||
ControlFlow::Break(()) => break,
|
||||
}
|
||||
@@ -204,7 +208,10 @@ fn run_acpid(daemon: daemon::Daemon) -> Result<(), StartupError> {
|
||||
drop(event_queue);
|
||||
|
||||
acpi_context.set_global_s_state(5).map_err(|error| {
|
||||
- StartupError::runtime("failed to shut down after kernel request", error.to_string())
|
||||
+ StartupError::runtime(
|
||||
+ "failed to shut down after kernel request",
|
||||
+ error.to_string(),
|
||||
+ )
|
||||
})
|
||||
}
|
||||
|
||||
@@ -289,8 +296,8 @@ mod tests {
|
||||
#[test]
|
||||
fn xsdt_physaddrs_parse_without_panic() {
|
||||
let payload = [
|
||||
- 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
|
||||
- 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99,
|
||||
+ 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB,
|
||||
+ 0xAA, 0x99,
|
||||
];
|
||||
let sdt = parse_root_sdt(make_sdt(*b"XSDT", &payload))
|
||||
.unwrap()
|
||||
diff --git a/drivers/acpid/src/resources.rs b/drivers/acpid/src/resources.rs
|
||||
new file mode 100644
|
||||
index 00000000..86bcfd1e
|
||||
@@ -1078,416 +1013,6 @@ index 00000000..86bcfd1e
|
||||
+++ b/drivers/acpid/src/resources.rs
|
||||
@@ -0,0 +1 @@
|
||||
+pub use acpi_resource::{decode_resource_template, ResourceDescriptor};
|
||||
diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs
|
||||
index 7070e8b9..4fe3b8d8 100644
|
||||
--- a/drivers/acpid/src/scheme.rs
|
||||
+++ b/drivers/acpid/src/scheme.rs
|
||||
@@ -15,7 +15,9 @@ use syscall::FobtainFdFlags;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{Error, Result};
|
||||
-use syscall::error::{EACCES, EAGAIN, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EOPNOTSUPP};
|
||||
+use syscall::error::{
|
||||
+ EACCES, EAGAIN, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EOPNOTSUPP,
|
||||
+};
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDONLY, O_STAT, O_SYMLINK};
|
||||
use syscall::{EOVERFLOW, EPERM};
|
||||
@@ -24,6 +26,7 @@ use crate::acpi::{
|
||||
AcpiBattery, AcpiContext, AcpiPowerAdapter, AcpiPowerSnapshot, AmlSymbols, DmiInfo,
|
||||
SdtSignature,
|
||||
};
|
||||
+use crate::resources::{decode_resource_template, ResourceDescriptor};
|
||||
|
||||
pub struct AcpiScheme<'acpi, 'sock> {
|
||||
ctx: &'acpi AcpiContext,
|
||||
@@ -42,6 +45,8 @@ enum HandleKind<'a> {
|
||||
Table(SdtSignature),
|
||||
Symbols(RwLockReadGuard<'a, AmlSymbols>),
|
||||
Symbol { name: String, description: String },
|
||||
+ ResourcesDir,
|
||||
+ Resources(String),
|
||||
Reboot,
|
||||
DmiDir,
|
||||
Dmi(String),
|
||||
@@ -197,6 +202,7 @@ fn top_level_entries(power_available: bool) -> Vec<(&'static str, DirentKind)> {
|
||||
let mut entries = vec![
|
||||
("tables", DirentKind::Directory),
|
||||
("symbols", DirentKind::Directory),
|
||||
+ ("resources", DirentKind::Directory),
|
||||
("dmi", DirentKind::Directory),
|
||||
("reboot", DirentKind::Regular),
|
||||
];
|
||||
@@ -206,6 +212,37 @@ fn top_level_entries(power_available: bool) -> Vec<(&'static str, DirentKind)> {
|
||||
entries
|
||||
}
|
||||
|
||||
+fn resource_symbol_path(path: &str) -> Option<String> {
|
||||
+ let normalized = path.trim_matches('/').trim_start_matches('\\');
|
||||
+ if normalized.is_empty() {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let normalized = normalized.replace('/', ".");
|
||||
+ if normalized.is_empty() {
|
||||
+ None
|
||||
+ } else {
|
||||
+ Some(format!("{normalized}._CRS"))
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+fn resource_entry_name(symbol: &str) -> Option<String> {
|
||||
+ symbol
|
||||
+ .strip_suffix("._CRS")
|
||||
+ .map(str::to_string)
|
||||
+ .filter(|path| !path.is_empty())
|
||||
+}
|
||||
+
|
||||
+fn resource_dir_entries<'a>(symbols: impl IntoIterator<Item = &'a str>) -> Vec<String> {
|
||||
+ let mut entries = symbols
|
||||
+ .into_iter()
|
||||
+ .filter_map(resource_entry_name)
|
||||
+ .collect::<Vec<_>>();
|
||||
+ entries.sort_unstable();
|
||||
+ entries.dedup();
|
||||
+ entries
|
||||
+}
|
||||
+
|
||||
impl HandleKind<'_> {
|
||||
fn is_dir(&self) -> bool {
|
||||
match self {
|
||||
@@ -214,6 +251,8 @@ impl HandleKind<'_> {
|
||||
Self::Table(_) => false,
|
||||
Self::Symbols(_) => true,
|
||||
Self::Symbol { .. } => false,
|
||||
+ Self::ResourcesDir => true,
|
||||
+ Self::Resources(_) => false,
|
||||
Self::Reboot => false,
|
||||
Self::DmiDir => true,
|
||||
Self::Dmi(_) => false,
|
||||
@@ -235,12 +274,14 @@ impl HandleKind<'_> {
|
||||
.ok_or(Error::new(EBADFD))?
|
||||
.length(),
|
||||
Self::Symbol { description, .. } => description.len(),
|
||||
+ Self::Resources(contents) => contents.len(),
|
||||
Self::Reboot => 0,
|
||||
Self::Dmi(contents) => contents.len(),
|
||||
Self::PowerFile(contents) => contents.len(),
|
||||
// Directories
|
||||
Self::TopLevel
|
||||
| Self::Symbols(_)
|
||||
+ | Self::ResourcesDir
|
||||
| Self::Tables
|
||||
| Self::DmiDir
|
||||
| Self::PowerDir
|
||||
@@ -280,6 +321,58 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> {
|
||||
matches!(self.ctx.power_snapshot(), Ok(_))
|
||||
}
|
||||
|
||||
+ fn resources_handle(&self, path: &str) -> Result<HandleKind<'acpi>> {
|
||||
+ if !self.ctx.pci_ready() {
|
||||
+ let display_path = if path.is_empty() { "resources" } else { path };
|
||||
+ log::warn!(
|
||||
+ "Deferring ACPI resource lookup for {display_path} until PCI registration is ready"
|
||||
+ );
|
||||
+ return Err(Error::new(EAGAIN));
|
||||
+ }
|
||||
+
|
||||
+ let normalized = path.trim_matches('/');
|
||||
+ if normalized.is_empty() {
|
||||
+ return Ok(HandleKind::ResourcesDir);
|
||||
+ }
|
||||
+
|
||||
+ let symbol_path = resource_symbol_path(normalized).ok_or(Error::new(ENOENT))?;
|
||||
+ if self.ctx.aml_lookup(&symbol_path).is_none() {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+
|
||||
+ let aml_name =
|
||||
+ AmlName::from_str(&format!("\\{symbol_path}")).map_err(|_| Error::new(ENOENT))?;
|
||||
+ let buffer = match self.ctx.aml_eval(aml_name, Vec::new()) {
|
||||
+ Ok(AmlSerdeValue::Buffer(bytes)) => bytes,
|
||||
+ Ok(other) => {
|
||||
+ log::debug!(
|
||||
+ "Skipping ACPI resources for {normalized} due to unexpected _CRS value: {:?}",
|
||||
+ other
|
||||
+ );
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Err(error) => {
|
||||
+ log::debug!(
|
||||
+ "Failed to evaluate ACPI resources for {symbol_path}: {:?}",
|
||||
+ error
|
||||
+ );
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let descriptors: Vec<ResourceDescriptor> =
|
||||
+ decode_resource_template(&buffer).map_err(|error| {
|
||||
+ log::warn!("Failed to decode ACPI _CRS for {symbol_path}: {error}");
|
||||
+ Error::new(EIO)
|
||||
+ })?;
|
||||
+ let serialized = ron::ser::to_string(&descriptors).map_err(|error| {
|
||||
+ log::warn!("Failed to serialize decoded ACPI resources for {symbol_path}: {error}");
|
||||
+ Error::new(EIO)
|
||||
+ })?;
|
||||
+
|
||||
+ Ok(HandleKind::Resources(serialized))
|
||||
+ }
|
||||
+
|
||||
fn power_handle(&self, path: &str) -> Result<HandleKind<'acpi>> {
|
||||
let normalized = path.trim_matches('/');
|
||||
self.power_snapshot()?;
|
||||
@@ -464,76 +557,85 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
|
||||
let kind = match handle.kind {
|
||||
HandleKind::SchemeRoot => {
|
||||
- // TODO: arrayvec
|
||||
- let components = {
|
||||
- let mut v = arrayvec::ArrayVec::<&str, 4>::new();
|
||||
- let it = path.split('/');
|
||||
- for component in it.take(4) {
|
||||
- v.push(component);
|
||||
- }
|
||||
-
|
||||
- v
|
||||
- };
|
||||
-
|
||||
- match &*components {
|
||||
- [""] => HandleKind::TopLevel,
|
||||
- ["reboot"] => HandleKind::Reboot,
|
||||
- ["dmi"] => {
|
||||
- if flag_dir || flag_stat || path.ends_with('/') {
|
||||
- HandleKind::DmiDir
|
||||
- } else {
|
||||
- HandleKind::Dmi(
|
||||
- dmi_contents(self.ctx.dmi_info(), "match_all")
|
||||
- .expect("match_all should always resolve"),
|
||||
- )
|
||||
+ if path == "resources" || path == "resources/" {
|
||||
+ self.resources_handle("")?
|
||||
+ } else if let Some(rest) = path.strip_prefix("resources/") {
|
||||
+ self.resources_handle(rest)?
|
||||
+ } else {
|
||||
+ // TODO: arrayvec
|
||||
+ let components = {
|
||||
+ let mut v = arrayvec::ArrayVec::<&str, 4>::new();
|
||||
+ let it = path.split('/');
|
||||
+ for component in it.take(4) {
|
||||
+ v.push(component);
|
||||
}
|
||||
- }
|
||||
- ["dmi", ""] => HandleKind::DmiDir,
|
||||
- ["dmi", field] => HandleKind::Dmi(
|
||||
- dmi_contents(self.ctx.dmi_info(), field).ok_or(Error::new(ENOENT))?,
|
||||
- ),
|
||||
- ["power"] => self.power_handle("")?,
|
||||
- ["power", tail] => self.power_handle(tail)?,
|
||||
- ["power", a, b] => self.power_handle(&format!("{a}/{b}"))?,
|
||||
- ["power", a, b, c] => self.power_handle(&format!("{a}/{b}/{c}"))?,
|
||||
- ["register_pci"] => HandleKind::RegisterPci,
|
||||
- ["tables"] => HandleKind::Tables,
|
||||
-
|
||||
- ["tables", table] => {
|
||||
- let signature = parse_table(table.as_bytes()).ok_or(Error::new(ENOENT))?;
|
||||
- HandleKind::Table(signature)
|
||||
- }
|
||||
|
||||
- ["symbols"] => {
|
||||
- if !self.ctx.pci_ready() {
|
||||
- log::warn!("Deferring AML symbol scan until PCI registration is ready");
|
||||
- return Err(Error::new(EAGAIN));
|
||||
+ v
|
||||
+ };
|
||||
+
|
||||
+ match &*components {
|
||||
+ [""] => HandleKind::TopLevel,
|
||||
+ ["reboot"] => HandleKind::Reboot,
|
||||
+ ["dmi"] => {
|
||||
+ if flag_dir || flag_stat || path.ends_with('/') {
|
||||
+ HandleKind::DmiDir
|
||||
+ } else {
|
||||
+ HandleKind::Dmi(
|
||||
+ dmi_contents(self.ctx.dmi_info(), "match_all")
|
||||
+ .expect("match_all should always resolve"),
|
||||
+ )
|
||||
+ }
|
||||
}
|
||||
- if let Ok(aml_symbols) = self.ctx.aml_symbols() {
|
||||
- HandleKind::Symbols(aml_symbols)
|
||||
- } else {
|
||||
- return Err(Error::new(EIO));
|
||||
+ ["dmi", ""] => HandleKind::DmiDir,
|
||||
+ ["dmi", field] => HandleKind::Dmi(
|
||||
+ dmi_contents(self.ctx.dmi_info(), field).ok_or(Error::new(ENOENT))?,
|
||||
+ ),
|
||||
+ ["power"] => self.power_handle("")?,
|
||||
+ ["power", tail] => self.power_handle(tail)?,
|
||||
+ ["power", a, b] => self.power_handle(&format!("{a}/{b}"))?,
|
||||
+ ["power", a, b, c] => self.power_handle(&format!("{a}/{b}/{c}"))?,
|
||||
+ ["register_pci"] => HandleKind::RegisterPci,
|
||||
+ ["tables"] => HandleKind::Tables,
|
||||
+
|
||||
+ ["tables", table] => {
|
||||
+ let signature =
|
||||
+ parse_table(table.as_bytes()).ok_or(Error::new(ENOENT))?;
|
||||
+ HandleKind::Table(signature)
|
||||
}
|
||||
- }
|
||||
|
||||
- ["symbols", symbol] => {
|
||||
- if !self.ctx.pci_ready() {
|
||||
- log::warn!(
|
||||
- "Deferring AML symbol lookup for {symbol} until PCI registration is ready"
|
||||
- );
|
||||
- return Err(Error::new(EAGAIN));
|
||||
+ ["symbols"] => {
|
||||
+ if !self.ctx.pci_ready() {
|
||||
+ log::warn!(
|
||||
+ "Deferring AML symbol scan until PCI registration is ready"
|
||||
+ );
|
||||
+ return Err(Error::new(EAGAIN));
|
||||
+ }
|
||||
+ if let Ok(aml_symbols) = self.ctx.aml_symbols() {
|
||||
+ HandleKind::Symbols(aml_symbols)
|
||||
+ } else {
|
||||
+ return Err(Error::new(EIO));
|
||||
+ }
|
||||
}
|
||||
- if let Some(description) = self.ctx.aml_lookup(symbol) {
|
||||
- HandleKind::Symbol {
|
||||
- name: (*symbol).to_owned(),
|
||||
- description,
|
||||
+
|
||||
+ ["symbols", symbol] => {
|
||||
+ if !self.ctx.pci_ready() {
|
||||
+ log::warn!(
|
||||
+ "Deferring AML symbol lookup for {symbol} until PCI registration is ready"
|
||||
+ );
|
||||
+ return Err(Error::new(EAGAIN));
|
||||
+ }
|
||||
+ if let Some(description) = self.ctx.aml_lookup(symbol) {
|
||||
+ HandleKind::Symbol {
|
||||
+ name: (*symbol).to_owned(),
|
||||
+ description,
|
||||
+ }
|
||||
+ } else {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
}
|
||||
- } else {
|
||||
- return Err(Error::new(ENOENT));
|
||||
}
|
||||
- }
|
||||
|
||||
- _ => return Err(Error::new(ENOENT)),
|
||||
+ _ => return Err(Error::new(ENOENT)),
|
||||
+ }
|
||||
}
|
||||
}
|
||||
HandleKind::DmiDir => {
|
||||
@@ -545,6 +647,7 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
)
|
||||
}
|
||||
}
|
||||
+ HandleKind::ResourcesDir => self.resources_handle(path)?,
|
||||
HandleKind::Symbols(ref aml_symbols) => {
|
||||
if let Some(description) = aml_symbols.lookup(path) {
|
||||
HandleKind::Symbol {
|
||||
@@ -646,6 +749,7 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
.ok_or(Error::new(EBADFD))?
|
||||
.as_slice(),
|
||||
HandleKind::Symbol { description, .. } => description.as_bytes(),
|
||||
+ HandleKind::Resources(contents) => contents.as_bytes(),
|
||||
HandleKind::Dmi(contents) => contents.as_bytes(),
|
||||
HandleKind::PowerFile(contents) => contents.as_bytes(),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
@@ -698,6 +802,19 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
})?;
|
||||
}
|
||||
}
|
||||
+ HandleKind::ResourcesDir => {
|
||||
+ let aml_symbols = self.ctx.aml_symbols().map_err(|_| Error::new(EIO))?;
|
||||
+ let entries =
|
||||
+ resource_dir_entries(aml_symbols.symbols_cache().keys().map(String::as_str));
|
||||
+ for (idx, name) in entries.iter().enumerate().skip(opaque_offset as usize) {
|
||||
+ buf.entry(DirEntry {
|
||||
+ inode: 0,
|
||||
+ next_opaque_id: idx as u64 + 1,
|
||||
+ name,
|
||||
+ kind: DirentKind::Regular,
|
||||
+ })?;
|
||||
+ }
|
||||
+ }
|
||||
HandleKind::PowerDir => {
|
||||
const POWER_ROOT_ENTRIES: &[(&str, DirentKind)] = &[
|
||||
("on_battery", DirentKind::Regular),
|
||||
@@ -958,7 +1075,7 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
- use super::{dmi_contents, top_level_entries};
|
||||
+ use super::{dmi_contents, resource_dir_entries, resource_symbol_path, top_level_entries};
|
||||
use crate::acpi::DmiInfo;
|
||||
use syscall::dirent::DirentKind;
|
||||
|
||||
@@ -994,9 +1111,9 @@ mod tests {
|
||||
#[test]
|
||||
fn top_level_entries_always_include_reboot() {
|
||||
let entries = top_level_entries(false);
|
||||
- assert!(entries.iter().any(|(name, kind)| {
|
||||
- *name == "reboot" && matches!(kind, DirentKind::Regular)
|
||||
- }));
|
||||
+ assert!(entries
|
||||
+ .iter()
|
||||
+ .any(|(name, kind)| { *name == "reboot" && matches!(kind, DirentKind::Regular) }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1005,8 +1122,40 @@ mod tests {
|
||||
let visible = top_level_entries(true);
|
||||
|
||||
assert!(!hidden.iter().any(|(name, _)| *name == "power"));
|
||||
- assert!(visible.iter().any(|(name, kind)| {
|
||||
- *name == "power" && matches!(kind, DirentKind::Directory)
|
||||
- }));
|
||||
+ assert!(visible
|
||||
+ .iter()
|
||||
+ .any(|(name, kind)| { *name == "power" && matches!(kind, DirentKind::Directory) }));
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn top_level_entries_always_include_resources() {
|
||||
+ let entries = top_level_entries(false);
|
||||
+ assert!(entries
|
||||
+ .iter()
|
||||
+ .any(|(name, kind)| { *name == "resources" && matches!(kind, DirentKind::Directory) }));
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn resource_symbol_path_accepts_dotted_and_slash_paths() {
|
||||
+ assert_eq!(
|
||||
+ resource_symbol_path("_SB.PCI0.I2C0.TPD0").as_deref(),
|
||||
+ Some("_SB.PCI0.I2C0.TPD0._CRS")
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ resource_symbol_path("\\_SB/PCI0/I2C0/TPD0").as_deref(),
|
||||
+ Some("_SB.PCI0.I2C0.TPD0._CRS")
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn resource_dir_entries_list_devices_with_crs_suffix() {
|
||||
+ assert_eq!(
|
||||
+ resource_dir_entries([
|
||||
+ "_SB.PCI0.I2C0.TPD0._CRS",
|
||||
+ "_SB.PCI0.I2C1.TPD1._HID",
|
||||
+ "_SB.PCI0.I2C0.TPD0._CRS",
|
||||
+ ]),
|
||||
+ vec!["_SB.PCI0.I2C0.TPD0".to_string()]
|
||||
+ );
|
||||
}
|
||||
}
|
||||
diff --git a/drivers/acpid/src/sleep.rs b/drivers/acpid/src/sleep.rs
|
||||
new file mode 100644
|
||||
index 00000000..e5d6ab22
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,291 +18,3 @@ index 82ec2bd0..a531edd9 100644
|
||||
Err(error) => eprintln!("Failed to create {logfile_base}.ansi.log: {}", error),
|
||||
}
|
||||
|
||||
diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs
|
||||
index a968f4d4..a39b2af8 100644
|
||||
--- a/drivers/pcid-spawner/src/main.rs
|
||||
+++ b/drivers/pcid-spawner/src/main.rs
|
||||
@@ -1,11 +1,40 @@
|
||||
+use std::env;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
+use std::thread;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use pcid_interface::config::Config;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
+fn strict_usb_boot() -> bool {
|
||||
+ matches!(
|
||||
+ env::var("REDBEAR_STRICT_USB_BOOT")
|
||||
+ .ok()
|
||||
+ .as_deref()
|
||||
+ .map(str::to_ascii_lowercase)
|
||||
+ .as_deref(),
|
||||
+ Some("1" | "true" | "yes" | "on")
|
||||
+ )
|
||||
+}
|
||||
+
|
||||
+fn should_detach_in_initfs(initfs: bool, class: u8, subclass: u8, strict_usb_boot: bool) -> bool {
|
||||
+ if !initfs {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if class == 0x01 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if strict_usb_boot && class == 0x0C && subclass == 0x03 {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ true
|
||||
+}
|
||||
+
|
||||
fn main() -> Result<()> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
let initfs = args.contains("--initfs");
|
||||
@@ -30,6 +59,12 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
let config: Config = toml::from_str(&config_data)?;
|
||||
+ let strict_usb_boot = strict_usb_boot();
|
||||
+
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: starting (initfs={}, strict_usb_boot={})",
|
||||
+ initfs, strict_usb_boot
|
||||
+ );
|
||||
|
||||
for entry in fs::read_dir("/scheme/pci")? {
|
||||
let entry = entry.context("failed to get entry")?;
|
||||
@@ -55,10 +90,11 @@ fn main() -> Result<()> {
|
||||
};
|
||||
|
||||
let full_device_id = handle.config().func.full_device_id;
|
||||
+ let device_addr = handle.config().func.addr;
|
||||
|
||||
log::debug!(
|
||||
"pcid-spawner enumerated: PCI {} {}",
|
||||
- handle.config().func.addr,
|
||||
+ device_addr,
|
||||
full_device_id.display()
|
||||
);
|
||||
|
||||
@@ -67,7 +103,7 @@ fn main() -> Result<()> {
|
||||
.iter()
|
||||
.find(|driver| driver.match_function(&full_device_id))
|
||||
else {
|
||||
- log::debug!("no driver for {}, continuing", handle.config().func.addr);
|
||||
+ log::debug!("no driver for {}, continuing", device_addr);
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -85,16 +121,93 @@ fn main() -> Result<()> {
|
||||
let mut command = Command::new(program);
|
||||
command.args(args);
|
||||
|
||||
- log::info!("pcid-spawner: spawn {:?}", command);
|
||||
-
|
||||
- handle.enable_device();
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: matched {} to driver {:?}",
|
||||
+ device_addr,
|
||||
+ driver.command
|
||||
+ );
|
||||
+ log::info!("pcid-spawner: enabling {} before spawn", device_addr);
|
||||
+
|
||||
+ if let Err(err) = handle.try_enable_device() {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to enable {} before spawn: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
let channel_fd = handle.into_inner_fd();
|
||||
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
||||
|
||||
+ log::info!("pcid-spawner: spawn {:?}", command);
|
||||
#[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- daemon::Daemon::spawn(command);
|
||||
- syscall::close(channel_fd as usize).unwrap();
|
||||
+ if should_detach_in_initfs(
|
||||
+ initfs,
|
||||
+ full_device_id.class,
|
||||
+ full_device_id.subclass,
|
||||
+ strict_usb_boot,
|
||||
+ ) {
|
||||
+ log::warn!(
|
||||
+ "pcid-spawner: detached initfs spawn for {} to avoid blocking early boot",
|
||||
+ device_addr
|
||||
+ );
|
||||
+
|
||||
+ let device_addr = device_addr.to_string();
|
||||
+ thread::spawn(move || {
|
||||
+ #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
+ if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ }
|
||||
+ if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
+ channel_fd,
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ });
|
||||
+ } else {
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: blocking on storage driver spawn for {} (class={:#04x})",
|
||||
+ device_addr,
|
||||
+ full_device_id.class
|
||||
+ );
|
||||
+ #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
+ if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ } else {
|
||||
+ log::info!(
|
||||
+ "pcid-spawner: storage driver ready for {}",
|
||||
+ device_addr
|
||||
+ );
|
||||
+ }
|
||||
+ if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
+ log::error!(
|
||||
+ "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
+ channel_fd,
|
||||
+ device_addr,
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
diff --git a/init/src/main.rs b/init/src/main.rs
|
||||
index 5682cf44..ed436619 100644
|
||||
--- a/init/src/main.rs
|
||||
+++ b/init/src/main.rs
|
||||
@@ -117,6 +117,8 @@ fn main() {
|
||||
let mut unit_store = UnitStore::new();
|
||||
let mut scheduler = Scheduler::new();
|
||||
|
||||
+ eprintln!("init: phase 1 — initfs boot");
|
||||
+
|
||||
switch_root(
|
||||
&mut unit_store,
|
||||
&mut init_config,
|
||||
@@ -125,6 +127,7 @@ fn main() {
|
||||
);
|
||||
|
||||
// Start logd first such that we can pass /scheme/log as stdio to all other services
|
||||
+ eprintln!("init: starting logd");
|
||||
scheduler
|
||||
.schedule_start_and_report_errors(&mut unit_store, UnitId("00_logd.service".to_owned()));
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
@@ -132,14 +135,18 @@ fn main() {
|
||||
eprintln!("init: failed to switch stdio to '/scheme/log': {err}");
|
||||
}
|
||||
|
||||
+ eprintln!("init: starting runtime target");
|
||||
let runtime_target = UnitId("00_runtime.target".to_owned());
|
||||
scheduler.schedule_start_and_report_errors(&mut unit_store, runtime_target.clone());
|
||||
unit_store.set_runtime_target(runtime_target);
|
||||
|
||||
+ eprintln!("init: starting initfs drivers target");
|
||||
scheduler
|
||||
.schedule_start_and_report_errors(&mut unit_store, UnitId("90_initfs.target".to_owned()));
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
+ eprintln!("init: initfs drivers target step() complete");
|
||||
|
||||
+ eprintln!("init: phase 2 — switchroot to /usr");
|
||||
switch_root(
|
||||
&mut unit_store,
|
||||
&mut init_config,
|
||||
@@ -162,23 +169,64 @@ fn main() {
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
- return;
|
||||
+ Vec::new()
|
||||
}
|
||||
};
|
||||
+ eprintln!("init: scheduling {} rootfs units", entries.len());
|
||||
for entry in entries {
|
||||
+ let name = match entry.file_name().and_then(|n| n.to_str()) {
|
||||
+ Some(name) => name,
|
||||
+ None => {
|
||||
+ eprintln!("init: skipping config entry with non-UTF-8 filename");
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
scheduler.schedule_start_and_report_errors(
|
||||
&mut unit_store,
|
||||
- UnitId(entry.file_name().unwrap().to_str().unwrap().to_owned()),
|
||||
+ UnitId(name.to_owned()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
+ eprintln!("init: phase 3 — rootfs services started");
|
||||
+
|
||||
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||
+ eprintln!("init: failed to enter null namespace: {err}");
|
||||
+ }
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("init: failed to enter null namespace");
|
||||
+ eprintln!("init: boot complete — entering waitpid loop");
|
||||
+
|
||||
+ let mut respawn_map: BTreeMap<u32, UnitId> = BTreeMap::new();
|
||||
+ for (unit_id, pid) in scheduler.respawn_pids {
|
||||
+ respawn_map.insert(pid, unit_id);
|
||||
+ }
|
||||
|
||||
loop {
|
||||
let mut status = 0;
|
||||
- libredox::call::waitpid(0, &mut status, 0).unwrap();
|
||||
+ match libredox::call::waitpid(0, &mut status, 0) {
|
||||
+ Ok(pid) => {
|
||||
+ if let Some(unit_id) = respawn_map.remove(&(pid as u32)) {
|
||||
+ eprintln!("init: respawning {} (pid {} exited)", unit_id.0, pid);
|
||||
+ let mut resp_scheduler = Scheduler::new();
|
||||
+ resp_scheduler.schedule_start_and_report_errors(
|
||||
+ &mut unit_store,
|
||||
+ unit_id.clone(),
|
||||
+ );
|
||||
+ resp_scheduler.step(&mut unit_store, &mut init_config);
|
||||
+ for (uid, new_pid) in resp_scheduler.respawn_pids {
|
||||
+ respawn_map.insert(new_pid, uid);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ // EAGAIN is normal (no child exited yet). Other errors are
|
||||
+ // unexpected but init must never crash — log and continue.
|
||||
+ if err.errno() != syscall::EAGAIN {
|
||||
+ eprintln!("init: waitpid error: {err}");
|
||||
+ }
|
||||
+ std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
diff --git a/drivers/hwd/src/backend/acpi.rs b/drivers/hwd/src/backend/acpi.rs
|
||||
index c24dfc4b..12d26261 100644
|
||||
--- a/drivers/hwd/src/backend/acpi.rs
|
||||
+++ b/drivers/hwd/src/backend/acpi.rs
|
||||
@@ -1,27 +1,36 @@
|
||||
@@ -21,7 +20,7 @@ index c24dfc4b..12d26261 100644
|
||||
- // Spawn acpid
|
||||
- //TODO: pass rxsdt data to acpid?
|
||||
- #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- let _ = daemon::Daemon::spawn(Command::new("acpid"));
|
||||
- daemon::Daemon::spawn(Command::new("acpid"));
|
||||
-
|
||||
- Ok(Self { rxsdt })
|
||||
+ Ok(Self { _rxsdt: rxsdt })
|
||||
@@ -48,7 +47,7 @@ index c24dfc4b..12d26261 100644
|
||||
// TODO: Reimplement with getdents?
|
||||
let symbols_fd = libredox::Fd::open(
|
||||
"/scheme/acpi/symbols",
|
||||
@@ -100,12 +109,103 @@ impl Backend for AcpiBackend {
|
||||
@@ -100,12 +109,103 @@
|
||||
"PNP0C0F" => "PCI interrupt link",
|
||||
"PNP0C50" => "I2C HID",
|
||||
"PNP0F13" => "PS/2 port for PS/2-style mouse",
|
||||
@@ -152,8 +151,8 @@ index c24dfc4b..12d26261 100644
|
||||
+fn is_non_hid_i2c_input_id(id: &str) -> bool {
|
||||
+ is_elan_touchpad_id(id) || is_cypress_touchpad_id(id) || is_synaptics_rmi_id(id)
|
||||
+}
|
||||
|
||||
diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs
|
||||
index e41caee0..31a4af5a 100644
|
||||
--- a/drivers/pcid-spawner/src/main.rs
|
||||
+++ b/drivers/pcid-spawner/src/main.rs
|
||||
@@ -1,11 +1,40 @@
|
||||
@@ -197,7 +196,7 @@ index e41caee0..31a4af5a 100644
|
||||
fn main() -> Result<()> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
let initfs = args.contains("--initfs");
|
||||
@@ -30,6 +59,7 @@ fn main() -> Result<()> {
|
||||
@@ -30,6 +59,7 @@
|
||||
}
|
||||
|
||||
let config: Config = toml::from_str(&config_data)?;
|
||||
@@ -205,18 +204,20 @@ index e41caee0..31a4af5a 100644
|
||||
|
||||
for entry in fs::read_dir("/scheme/pci")? {
|
||||
let entry = entry.context("failed to get entry")?;
|
||||
@@ -107,24 +137,61 @@ fn main() -> Result<()> {
|
||||
@@ -87,14 +117,70 @@
|
||||
|
||||
log::info!("pcid-spawner: spawn {:?}", command);
|
||||
|
||||
+ let device_addr = handle.config().func.addr;
|
||||
+
|
||||
handle.enable_device();
|
||||
|
||||
let channel_fd = handle.into_inner_fd();
|
||||
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
||||
|
||||
#[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- if let Err(err) = daemon::Daemon::spawn(command) {
|
||||
- log::error!(
|
||||
- "pcid-spawner: spawn/readiness failed for {}: {}",
|
||||
- device_addr,
|
||||
- err
|
||||
- );
|
||||
- log::error!(
|
||||
- "pcid-spawner: {} remains enabled without a confirmed ready driver",
|
||||
- daemon::Daemon::spawn(command);
|
||||
- syscall::close(channel_fd as usize).unwrap();
|
||||
+ if should_detach_in_initfs(
|
||||
+ initfs,
|
||||
+ full_device_id.class,
|
||||
@@ -225,16 +226,8 @@ index e41caee0..31a4af5a 100644
|
||||
+ ) {
|
||||
+ log::warn!(
|
||||
+ "pcid-spawner: detached initfs spawn for {} to avoid blocking early boot",
|
||||
device_addr
|
||||
);
|
||||
- }
|
||||
- if let Err(err) = syscall::close(channel_fd as usize) {
|
||||
- log::error!(
|
||||
- "pcid-spawner: failed to close channel fd {} for {}: {}",
|
||||
- channel_fd,
|
||||
- device_addr,
|
||||
- err
|
||||
- );
|
||||
+ device_addr
|
||||
+ );
|
||||
+
|
||||
+ let device_addr = device_addr.to_string();
|
||||
+ thread::spawn(move || {
|
||||
@@ -280,14 +273,15 @@ index e41caee0..31a4af5a 100644
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
}
|
||||
+ }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs
|
||||
index 61cd9a78..6da034ef 100644
|
||||
--- a/drivers/pcid/src/main.rs
|
||||
+++ b/drivers/pcid/src/main.rs
|
||||
@@ -12,6 +12,7 @@ use pci_types::{
|
||||
@@ -12,6 +12,7 @@
|
||||
};
|
||||
use redox_scheme::scheme::register_sync_scheme;
|
||||
use scheme_utils::Blocking;
|
||||
@@ -295,7 +289,7 @@ index 61cd9a78..6da034ef 100644
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
use pcid_interface::{FullDeviceId, LegacyInterruptLine, PciBar, PciFunction, PciRom};
|
||||
@@ -262,14 +263,13 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
@@ -262,14 +263,13 @@
|
||||
let access_fd = socket
|
||||
.create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0)
|
||||
.expect("failed to issue this resource");
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
# P2-boot-runtime-noise-and-net-race.patch
|
||||
#
|
||||
# Reduce expected boot-time warning noise and harden netstack startup ordering:
|
||||
# - procmgr: unknown cancellation is trace-level (benign race)
|
||||
# - acpid: warn once for unsupported power surface
|
||||
# - ahcid: SATAPI probe failures are informational on empty media
|
||||
# - netstack: retry network adapter discovery during early boot races
|
||||
|
||||
diff --git a/bootstrap/src/procmgr.rs b/bootstrap/src/procmgr.rs
|
||||
--- a/bootstrap/src/procmgr.rs
|
||||
+++ b/bootstrap/src/procmgr.rs
|
||||
@@ -296,7 +296,7 @@ fn handle_scheme<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
- log::warn!("Cancellation for unknown id {:?}", req.id);
|
||||
+ log::trace!("Cancellation for unknown id {:?}", req.id);
|
||||
Pending
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs
|
||||
--- a/drivers/acpid/src/scheme.rs
|
||||
+++ b/drivers/acpid/src/scheme.rs
|
||||
@@ -8,6 +8,7 @@ use ron::de::SpannedError;
|
||||
use scheme_utils::HandleMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::str::FromStr;
|
||||
+use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::FobtainFdFlags;
|
||||
@@ -29,6 +30,8 @@ use crate::acpi::{
|
||||
};
|
||||
use crate::resources::{decode_resource_template, ResourceDescriptor};
|
||||
|
||||
+static POWER_SURFACE_UNAVAILABLE_WARNED: AtomicBool = AtomicBool::new(false);
|
||||
+
|
||||
pub struct AcpiScheme<'acpi, 'sock> {
|
||||
ctx: &'acpi AcpiContext,
|
||||
handles: HandleMap<Handle<'acpi>>,
|
||||
@@ -307,7 +310,9 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> {
|
||||
self.ctx.power_snapshot().map_err(|error| match error {
|
||||
crate::acpi::AmlEvalError::NotInitialized => Error::new(EAGAIN),
|
||||
crate::acpi::AmlEvalError::Unsupported(message) => {
|
||||
- log::warn!("ACPI power surface unavailable: {message}");
|
||||
+ if !POWER_SURFACE_UNAVAILABLE_WARNED.swap(true, Ordering::Relaxed) {
|
||||
+ log::warn!("ACPI power surface unavailable: {message}");
|
||||
+ }
|
||||
Error::new(EOPNOTSUPP)
|
||||
}
|
||||
other => {
|
||||
|
||||
diff --git a/drivers/storage/ahcid/src/ahci/mod.rs b/drivers/storage/ahcid/src/ahci/mod.rs
|
||||
--- a/drivers/storage/ahcid/src/ahci/mod.rs
|
||||
+++ b/drivers/storage/ahcid/src/ahci/mod.rs
|
||||
@@ -64,7 +64,7 @@ pub fn disks(base: usize, name: &str) -> (&'static mut HbaMem, Vec<AnyDisk>) {
|
||||
HbaPortType::SATAPI => match DiskATAPI::new(i, port) {
|
||||
Ok(disk) => Some(AnyDisk::Atapi(disk)),
|
||||
Err(err) => {
|
||||
- error!("{}: {}", i, err);
|
||||
+ info!("{}: {}", i, err);
|
||||
None
|
||||
}
|
||||
},
|
||||
|
||||
diff --git a/netstack/src/main.rs b/netstack/src/main.rs
|
||||
--- a/netstack/src/main.rs
|
||||
+++ b/netstack/src/main.rs
|
||||
@@ -6,6 +6,8 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
use event::{EventFlags, EventQueue};
|
||||
use libredox::flag::{O_NONBLOCK, O_RDWR};
|
||||
use libredox::Fd;
|
||||
+use std::thread;
|
||||
+use std::time::Duration;
|
||||
|
||||
use redox_scheme::Socket;
|
||||
use scheme::Smolnetd;
|
||||
@@ -22,34 +24,47 @@ mod scheme;
|
||||
fn get_network_adapter() -> Result<String> {
|
||||
use std::fs;
|
||||
|
||||
- let mut adapters = vec![];
|
||||
+ const MAX_ATTEMPTS: u32 = 50;
|
||||
+ const RETRY_DELAY: Duration = Duration::from_millis(100);
|
||||
|
||||
- for entry_res in fs::read_dir("/scheme")? {
|
||||
- let Ok(entry) = entry_res else {
|
||||
- continue;
|
||||
- };
|
||||
+ for attempt in 1..=MAX_ATTEMPTS {
|
||||
+ let mut adapters = vec![];
|
||||
|
||||
- let Ok(scheme) = entry.file_name().into_string() else {
|
||||
- continue;
|
||||
- };
|
||||
+ for entry_res in fs::read_dir("/scheme")? {
|
||||
+ let Ok(entry) = entry_res else {
|
||||
+ continue;
|
||||
+ };
|
||||
|
||||
- if !scheme.starts_with("network") {
|
||||
- continue;
|
||||
- }
|
||||
+ let Ok(scheme) = entry.file_name().into_string() else {
|
||||
+ continue;
|
||||
+ };
|
||||
|
||||
- adapters.push(scheme);
|
||||
- }
|
||||
+ if !scheme.starts_with("network") {
|
||||
+ continue;
|
||||
+ }
|
||||
|
||||
- if adapters.is_empty() {
|
||||
- bail!("no network adapter found");
|
||||
- } else {
|
||||
- let adapter = adapters.remove(0);
|
||||
+ adapters.push(scheme);
|
||||
+ }
|
||||
+
|
||||
if !adapters.is_empty() {
|
||||
- // FIXME allow using multiple network adapters at the same time
|
||||
- warn!("Multiple network adapters found. Only {adapter} will be used");
|
||||
+ let adapter = adapters.remove(0);
|
||||
+ if !adapters.is_empty() {
|
||||
+ // FIXME allow using multiple network adapters at the same time
|
||||
+ warn!("Multiple network adapters found. Only {adapter} will be used");
|
||||
+ }
|
||||
+ return Ok(adapter);
|
||||
+ }
|
||||
+
|
||||
+ if attempt < MAX_ATTEMPTS {
|
||||
+ warn!(
|
||||
+ "no network adapter found yet (attempt {attempt}/{MAX_ATTEMPTS}), waiting {} ms",
|
||||
+ RETRY_DELAY.as_millis()
|
||||
+ );
|
||||
+ thread::sleep(RETRY_DELAY);
|
||||
}
|
||||
- Ok(adapter)
|
||||
}
|
||||
+
|
||||
+ bail!("no network adapter found")
|
||||
}
|
||||
@@ -1,50 +1,3 @@
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
index 9e776232..36d87870 100644
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -20,8 +20,17 @@ members = [
|
||||
"drivers/common",
|
||||
"drivers/executor",
|
||||
|
||||
+ "drivers/acpi-resource",
|
||||
"drivers/acpid",
|
||||
+ "drivers/gpio/gpiod",
|
||||
+ "drivers/gpio/i2c-gpio-expanderd",
|
||||
+ "drivers/gpio/intel-gpiod",
|
||||
"drivers/hwd",
|
||||
+ "drivers/i2c/amd-mp2-i2cd",
|
||||
+ "drivers/i2c/dw-acpi-i2cd",
|
||||
+ "drivers/i2c/i2c-interface",
|
||||
+ "drivers/i2c/i2cd",
|
||||
+ "drivers/i2c/intel-lpss-i2cd",
|
||||
"drivers/pcid",
|
||||
"drivers/pcid-spawner",
|
||||
"drivers/rtcd",
|
||||
@@ -43,6 +52,8 @@ members = [
|
||||
"drivers/graphics/virtio-gpud",
|
||||
|
||||
"drivers/input/ps2d",
|
||||
+ "drivers/input/i2c-hidd",
|
||||
+ "drivers/input/intel-thc-hidd",
|
||||
"drivers/input/usbhidd",
|
||||
|
||||
"drivers/net/driver-network",
|
||||
@@ -63,6 +74,7 @@ members = [
|
||||
"drivers/storage/usbscsid",
|
||||
"drivers/storage/virtio-blkd",
|
||||
|
||||
+ "drivers/usb/ucsid",
|
||||
"drivers/usb/xhcid",
|
||||
"drivers/usb/usbctl",
|
||||
"drivers/usb/usbhubd",
|
||||
@@ -81,6 +93,7 @@ drm = "0.15.0"
|
||||
drm-sys = "0.8.1"
|
||||
edid = "0.3.0" #TODO: edid is abandoned, fork it and maintain?
|
||||
fdt = "0.1.5"
|
||||
+nom = "3.2.0" # transitive dep via edid; needed to match on IResult variants
|
||||
libc = "0.2.181"
|
||||
log = "0.4"
|
||||
libredox = "0.1.16"
|
||||
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
||||
index 9f507221..a0ba9d88 100644
|
||||
--- a/daemon/src/lib.rs
|
||||
@@ -2432,7 +2385,7 @@ index 22a985ee..075502a2 100644
|
||||
self.control_queue.send(command).await;
|
||||
- assert!(response.header.ty == CommandTy::RespOkDisplayInfo);
|
||||
+ if response.header.ty != CommandTy::RespOkDisplayInfo {
|
||||
+ return Err(Error::QueueSetup("unexpected response type for display info"));
|
||||
+ return Err(Error::Probe("unexpected response type for display info"));
|
||||
+ }
|
||||
|
||||
Ok(response)
|
||||
@@ -2443,7 +2396,7 @@ index 22a985ee..075502a2 100644
|
||||
self.control_queue.send(command).await;
|
||||
- assert!(response.header.ty == CommandTy::RespOkEdid);
|
||||
+ if response.header.ty != CommandTy::RespOkEdid {
|
||||
+ return Err(Error::QueueSetup("unexpected response type for EDID"));
|
||||
+ return Err(Error::Probe("unexpected response type for EDID"));
|
||||
+ }
|
||||
|
||||
Ok(response)
|
||||
@@ -3262,7 +3215,7 @@ index d42a4e57..32f5076f 100644
|
||||
- if !unit_store.unit(&unit_id).conditions_met() {
|
||||
+ if unit_store
|
||||
+ .unit(&unit_id)
|
||||
+ .map_or(false, |u| u.conditions_met())
|
||||
+ .map_or(false, |u| !u.conditions_met())
|
||||
+ {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
||||
--- a/daemon/src/lib.rs
|
||||
+++ b/daemon/src/lib.rs
|
||||
@@ -52,7 +52,11 @@
|
||||
# P2-daemon-ready-graceful.patch
|
||||
#
|
||||
# Replace unwrap() in Daemon::ready() with graceful error handling.
|
||||
# When hwd spawns pcid fire-and-forget (dropping the pipe's read end
|
||||
# before pcid signals readiness), the unwrap() causes a BrokenPipe panic
|
||||
# that kills pcid and cascades into total boot failure.
|
||||
#
|
||||
# Also adds the log crate to daemon's dependencies for the debug message.
|
||||
#
|
||||
diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml
|
||||
--- a/daemon/Cargo.toml
|
||||
+++ b/daemon/Cargo.toml
|
||||
@@ -7,6 +7,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
libc.workspace = true
|
||||
libredox.workspace = true
|
||||
+log.workspace = true
|
||||
redox-scheme.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
|
||||
/// Notify the process that the daemon is ready to accept requests.
|
||||
pub fn ready(mut self) {
|
||||
- self.write_pipe.write_all(&[0]).unwrap();
|
||||
+ if let Err(err) = self.write_pipe.write_all(&[0]) {
|
||||
+ if err.kind() != io::ErrorKind::BrokenPipe {
|
||||
+ eprintln!("daemon::ready write failed: {err}");
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
/// Executes `Command` as a child process.
|
||||
diff --git a/randd/src/main.rs b/randd/src/main.rs
|
||||
--- a/randd/src/main.rs
|
||||
+++ b/randd/src/main.rs
|
||||
@@ -83,7 +83,7 @@
|
||||
} // TODO integrate alternative entropy sources
|
||||
if !have_seeded {
|
||||
- println!("randd: Seeding failed, no entropy source. Random numbers on this platform are NOT SECURE");
|
||||
+ eprintln!("randd: no hardware entropy source, random numbers are NOT SECURE");
|
||||
}
|
||||
rng
|
||||
}
|
||||
|
||||
@@ -1,64 +1,18 @@
|
||||
# P2-hwd-misc.patch
|
||||
# Extract hwd (hardware daemon) Cargo.toml and main.rs improvements.
|
||||
#
|
||||
# Files: drivers/hwd/Cargo.toml, drivers/hwd/src/main.rs
|
||||
# Keep hwd focused on hardware probing. Init owns boot-time pcid startup.
|
||||
|
||||
diff --git a/drivers/hwd/Cargo.toml b/drivers/hwd/Cargo.toml
|
||||
index 3d37cfb3..40b51a1b 100644
|
||||
--- a/drivers/hwd/Cargo.toml
|
||||
+++ b/drivers/hwd/Cargo.toml
|
||||
@@ -6,6 +6,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
fdt.workspace = true
|
||||
+libc.workspace = true
|
||||
log.workspace = true
|
||||
ron.workspace = true
|
||||
libredox = { workspace = true, default-features = false, features = ["std", "call"] }
|
||||
diff --git a/drivers/hwd/src/main.rs b/drivers/hwd/src/main.rs
|
||||
index 79360e34..a0462f51 100644
|
||||
index 79360e34..4de3d9f3 100644
|
||||
--- a/drivers/hwd/src/main.rs
|
||||
+++ b/drivers/hwd/src/main.rs
|
||||
@@ -1,3 +1,5 @@
|
||||
+use std::os::fd::AsRawFd;
|
||||
+use std::os::unix::process::CommandExt;
|
||||
use std::process;
|
||||
|
||||
mod backend;
|
||||
@@ -37,8 +39,34 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
@@ -37,11 +37,6 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
|
||||
//TODO: launch pcid based on backend information?
|
||||
// Must launch after acpid but before probe calls /scheme/acpi/symbols
|
||||
- #[allow(deprecated, reason = "we can't yet move this to init")]
|
||||
- daemon::Daemon::spawn(process::Command::new("pcid"));
|
||||
+ // Fire-and-forget: daemon::Daemon::spawn blocks until pcid signals readiness,
|
||||
+ // but pcid only signals after full PCI enumeration. If enumeration hangs on
|
||||
+ // real hardware (unresponsive device, complex AML), hwd deadlocks initfs.
|
||||
+ {
|
||||
+ match std::io::pipe() {
|
||||
+ Ok((_read_end, write_end)) => {
|
||||
+ let write_fd: std::os::fd::OwnedFd = write_end.into();
|
||||
+ let raw_fd = write_fd.as_raw_fd();
|
||||
+ let mut cmd = std::process::Command::new("pcid");
|
||||
+ cmd.env("INIT_NOTIFY", raw_fd.to_string());
|
||||
+ unsafe {
|
||||
+ cmd.pre_exec(move || {
|
||||
+ if libc::fcntl(raw_fd, libc::F_SETFD, 0) == -1 {
|
||||
+ return Err(std::io::Error::last_os_error());
|
||||
+ }
|
||||
+ Ok(())
|
||||
+ });
|
||||
+ }
|
||||
+ match cmd.spawn() {
|
||||
+ Ok(_) => {}
|
||||
+ Err(err) => log::error!("hwd: failed to spawn pcid: {}", err),
|
||||
+ }
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ log::error!("hwd: failed to create pcid notification pipe: {}", err);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
-
|
||||
daemon.ready();
|
||||
|
||||
//TODO: HWD is meant to locate PCI/XHCI/etc devices in ACPI and DeviceTree definitions and start their drivers
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,17 @@
|
||||
# P2-init-acpid-wiring.patch
|
||||
#
|
||||
# Init service acpid dependency wiring: add 41_acpid.service as a weak
|
||||
# dependency to the drivers target, hardware manager, and PCI spawner so
|
||||
# acpid starts reliably during boot.
|
||||
#
|
||||
# Covers:
|
||||
# - 40_drivers.target: add 41_acpid.service to requires_weak
|
||||
# - 40_hwd.service: add 41_acpid.service to requires_weak
|
||||
# - 40_pcid-spawner-initfs.service: add 41_acpid.service to requires_weak
|
||||
#
|
||||
diff --git a/init.initfs.d/41_acpid.service b/init.initfs.d/41_acpid.service
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/init.initfs.d/41_acpid.service
|
||||
@@ -0,0 +1,7 @@
|
||||
+[unit]
|
||||
+description = "ACPI daemon"
|
||||
+default_dependencies = false
|
||||
+
|
||||
+[service]
|
||||
+cmd = "acpid"
|
||||
+inherit_envs = ["RSDP_ADDR", "RSDP_SIZE"]
|
||||
+type = "notify"
|
||||
diff --git a/init.initfs.d/40_drivers.target b/init.initfs.d/40_drivers.target
|
||||
index 8ddb4795..029583a1 100644
|
||||
--- a/init.initfs.d/40_drivers.target
|
||||
+++ b/init.initfs.d/40_drivers.target
|
||||
@@ -7,4 +7,5 @@ requires_weak = [
|
||||
@@ -20,7 +21,6 @@ index 8ddb4795..029583a1 100644
|
||||
+ "41_acpid.service",
|
||||
]
|
||||
diff --git a/init.initfs.d/40_hwd.service b/init.initfs.d/40_hwd.service
|
||||
index cba12dde..cf34a51b 100644
|
||||
--- a/init.initfs.d/40_hwd.service
|
||||
+++ b/init.initfs.d/40_hwd.service
|
||||
@@ -1,6 +1,6 @@
|
||||
@@ -31,15 +31,3 @@ index cba12dde..cf34a51b 100644
|
||||
|
||||
[service]
|
||||
cmd = "hwd"
|
||||
diff --git a/init.initfs.d/40_pcid-spawner-initfs.service b/init.initfs.d/40_pcid-spawner-initfs.service
|
||||
index 6945b9ea..ba1ee0bb 100644
|
||||
--- a/init.initfs.d/40_pcid-spawner-initfs.service
|
||||
+++ b/init.initfs.d/40_pcid-spawner-initfs.service
|
||||
@@ -1,6 +1,6 @@
|
||||
[unit]
|
||||
description = "PCI driver spawner"
|
||||
-requires_weak = ["10_inputd.service", "20_graphics.target", "40_hwd.service"]
|
||||
+requires_weak = ["10_inputd.service", "20_graphics.target", "40_hwd.service", "41_acpid.service"]
|
||||
|
||||
[service]
|
||||
cmd = "pcid-spawner"
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
# P2-init-subsystems.patch
|
||||
# Extract init subsystem hardening: service respawn, unit store Option returns,
|
||||
# scheduler PID tracking, and service spawn error handling.
|
||||
#
|
||||
# Files: init/src/scheduler.rs, init/src/service.rs, init/src/unit.rs
|
||||
|
||||
diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs
|
||||
index d42a4e57..64e64e1e 100644
|
||||
--- a/init/src/scheduler.rs
|
||||
+++ b/init/src/scheduler.rs
|
||||
@@ -5,6 +5,7 @@ use crate::unit::{Unit, UnitId, UnitKind, UnitStore};
|
||||
|
||||
pub struct Scheduler {
|
||||
pending: VecDeque<Job>,
|
||||
+ pub respawn_pids: Vec<(UnitId, u32)>,
|
||||
}
|
||||
|
||||
struct Job {
|
||||
@@ -20,6 +21,7 @@ impl Scheduler {
|
||||
pub fn new() -> Scheduler {
|
||||
Scheduler {
|
||||
pending: VecDeque::new(),
|
||||
+ respawn_pids: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +45,10 @@ impl Scheduler {
|
||||
) {
|
||||
let loaded_units = unit_store.load_units(unit_id.clone(), errors);
|
||||
for unit_id in loaded_units {
|
||||
- if !unit_store.unit(&unit_id).conditions_met() {
|
||||
+ let Some(unit) = unit_store.unit(&unit_id) else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ if !unit.conditions_met() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -62,7 +67,10 @@ impl Scheduler {
|
||||
|
||||
match job.kind {
|
||||
JobKind::Start => {
|
||||
- let unit = unit_store.unit_mut(&job.unit);
|
||||
+ let Some(unit) = unit_store.unit_mut(&job.unit) else {
|
||||
+ eprintln!("init: unit {} not found in store, skipping", job.unit.0);
|
||||
+ continue 'a;
|
||||
+ };
|
||||
|
||||
for dep in &unit.info.requires_weak {
|
||||
for pending_job in &self.pending {
|
||||
@@ -73,14 +81,17 @@ impl Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
- run(unit, init_config);
|
||||
+ let pid = run(unit, init_config);
|
||||
+ if let Some(pid) = pid {
|
||||
+ self.respawn_pids.push((job.unit.clone(), pid));
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-fn run(unit: &mut Unit, config: &mut InitConfig) {
|
||||
+fn run(unit: &mut Unit, config: &mut InitConfig) -> Option<u32> {
|
||||
match &unit.kind {
|
||||
UnitKind::LegacyScript { script } => {
|
||||
for cmd in script.clone() {
|
||||
@@ -92,25 +103,30 @@ fn run(unit: &mut Unit, config: &mut InitConfig) {
|
||||
}
|
||||
UnitKind::Service { service } => {
|
||||
if config.skip_cmd.contains(&service.cmd) {
|
||||
- eprintln!("Skipping '{} {}'", service.cmd, service.args.join(" "));
|
||||
- return;
|
||||
+ eprintln!("init: skipping {} {}", service.cmd, service.args.join(" "));
|
||||
+ return None;
|
||||
}
|
||||
- if config.log_debug {
|
||||
+ eprintln!(
|
||||
+ "init: starting {} ({})",
|
||||
+ unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||
+ service.cmd,
|
||||
+ );
|
||||
+ let pid = service.spawn(&config.envs);
|
||||
+ if pid.is_some() {
|
||||
eprintln!(
|
||||
- "Starting {} ({})",
|
||||
+ "init: started {} (pid {})",
|
||||
unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||
- service.cmd,
|
||||
+ pid.unwrap_or(0),
|
||||
);
|
||||
}
|
||||
- service.spawn(&config.envs);
|
||||
+ return pid;
|
||||
}
|
||||
UnitKind::Target {} => {
|
||||
- if config.log_debug {
|
||||
- eprintln!(
|
||||
- "Reached target {}",
|
||||
- unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||
- );
|
||||
- }
|
||||
+ eprintln!(
|
||||
+ "init: reached target {}",
|
||||
+ unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||
+ );
|
||||
}
|
||||
}
|
||||
+ None
|
||||
}
|
||||
diff --git a/init/src/service.rs b/init/src/service.rs
|
||||
index ed0023e9..cc95d02b 100644
|
||||
--- a/init/src/service.rs
|
||||
+++ b/init/src/service.rs
|
||||
@@ -22,6 +22,8 @@ pub struct Service {
|
||||
pub inherit_envs: BTreeSet<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: ServiceType,
|
||||
+ #[serde(default)]
|
||||
+ pub respawn: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
@@ -35,7 +37,7 @@ pub enum ServiceType {
|
||||
}
|
||||
|
||||
impl Service {
|
||||
- pub fn spawn(&self, base_envs: &BTreeMap<String, OsString>) {
|
||||
+ pub fn spawn(&self, base_envs: &BTreeMap<String, OsString>) -> Option<u32> {
|
||||
let mut command = Command::new(&self.cmd);
|
||||
command.args(self.args.iter().map(|arg| subst_env(arg)));
|
||||
command.env_clear();
|
||||
@@ -46,20 +48,28 @@ impl Service {
|
||||
}
|
||||
command.envs(base_envs).envs(&self.envs);
|
||||
|
||||
- let (mut read_pipe, write_pipe) = io::pipe().unwrap();
|
||||
+ let (mut read_pipe, write_pipe) = match io::pipe() {
|
||||
+ Ok(pair) => pair,
|
||||
+ Err(err) => {
|
||||
+ eprintln!("init: failed to create readiness pipe for {:?}: {err}", command);
|
||||
+ return None;
|
||||
+ }
|
||||
+ };
|
||||
unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) };
|
||||
|
||||
let mut child = match command.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(err) => {
|
||||
eprintln!("init: failed to execute {:?}: {}", command, err);
|
||||
- return;
|
||||
+ return None;
|
||||
}
|
||||
};
|
||||
|
||||
match &self.type_ {
|
||||
ServiceType::Notify => match read_pipe.read_exact(&mut [0]) {
|
||||
- Ok(()) => {}
|
||||
+ Ok(()) => {
|
||||
+ eprintln!("init: {} ready (notify)", self.cmd);
|
||||
+ }
|
||||
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
||||
eprintln!("init: {command:?} exited without notifying readiness");
|
||||
}
|
||||
@@ -81,23 +91,34 @@ impl Service {
|
||||
}) => continue,
|
||||
Ok(0) => {
|
||||
eprintln!("init: {command:?} exited without notifying readiness");
|
||||
- return;
|
||||
+ return None;
|
||||
}
|
||||
Ok(1) => break,
|
||||
Ok(n) => {
|
||||
eprintln!("init: incorrect amount of fds {n} returned");
|
||||
- return;
|
||||
+ return None;
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("init: failed to wait for {command:?}: {err}");
|
||||
- return;
|
||||
+ return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- let current_namespace_fd = libredox::call::getns().expect("TODO");
|
||||
- libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
|
||||
- .expect("TODO");
|
||||
+ let current_namespace_fd = match libredox::call::getns() {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(err) => {
|
||||
+ eprintln!("init: failed to get current namespace for {command:?}: {err}");
|
||||
+ return None;
|
||||
+ }
|
||||
+ };
|
||||
+ if let Err(err) =
|
||||
+ libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
|
||||
+ {
|
||||
+ eprintln!("init: failed to register scheme {scheme:?} for {command:?}: {err}");
|
||||
+ } else {
|
||||
+ eprintln!("init: {} ready (scheme {})", self.cmd, scheme);
|
||||
+ }
|
||||
}
|
||||
ServiceType::Oneshot => {
|
||||
drop(read_pipe);
|
||||
@@ -105,6 +126,8 @@ impl Service {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
eprintln!("init: {command:?} failed with {exit_status}");
|
||||
+ } else {
|
||||
+ eprintln!("init: {} done (oneshot)", self.cmd);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -112,8 +135,13 @@ impl Service {
|
||||
}
|
||||
}
|
||||
}
|
||||
- ServiceType::OneshotAsync => {}
|
||||
+ ServiceType::OneshotAsync => {
|
||||
+ if self.respawn {
|
||||
+ return Some(child.id());
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ None
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/init/src/unit.rs b/init/src/unit.rs
|
||||
index 98053cb2..a58bfb96 100644
|
||||
--- a/init/src/unit.rs
|
||||
+++ b/init/src/unit.rs
|
||||
@@ -23,8 +23,14 @@ impl UnitStore {
|
||||
}
|
||||
|
||||
pub fn set_runtime_target(&mut self, unit_id: UnitId) {
|
||||
- assert!(self.runtime_target.is_none());
|
||||
- assert!(self.units.contains_key(&unit_id));
|
||||
+ if self.runtime_target.is_some() {
|
||||
+ eprintln!("init: runtime target already set, ignoring {}", unit_id.0);
|
||||
+ return;
|
||||
+ }
|
||||
+ if !self.units.contains_key(&unit_id) {
|
||||
+ eprintln!("init: runtime target {} not found in unit store", unit_id.0);
|
||||
+ return;
|
||||
+ }
|
||||
self.runtime_target = Some(unit_id);
|
||||
}
|
||||
|
||||
@@ -85,8 +91,10 @@ impl UnitStore {
|
||||
let unit = self.load_single_unit(unit_id, errors);
|
||||
if let Some(unit) = unit {
|
||||
loaded_units.push(unit.clone());
|
||||
- for dep in &self.unit(&unit).info.requires_weak {
|
||||
- pending_units.push(dep.clone());
|
||||
+ if let Some(u) = self.unit(&unit) {
|
||||
+ for dep in &u.info.requires_weak {
|
||||
+ pending_units.push(dep.clone());
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,12 +102,12 @@ impl UnitStore {
|
||||
loaded_units
|
||||
}
|
||||
|
||||
- pub fn unit(&self, unit: &UnitId) -> &Unit {
|
||||
- self.units.get(unit).unwrap()
|
||||
+ pub fn unit(&self, unit: &UnitId) -> Option<&Unit> {
|
||||
+ self.units.get(unit)
|
||||
}
|
||||
|
||||
- pub fn unit_mut(&mut self, unit: &UnitId) -> &mut Unit {
|
||||
- self.units.get_mut(unit).unwrap()
|
||||
+ pub fn unit_mut(&mut self, unit: &UnitId) -> Option<&mut Unit> {
|
||||
+ self.units.get_mut(unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +188,7 @@ impl Unit {
|
||||
) -> io::Result<Self> {
|
||||
let config = fs::read_to_string(config_path)?;
|
||||
|
||||
- let Some(ext) = config_path.extension().map(|ext| ext.to_str().unwrap()) else {
|
||||
+ let Some(ext) = config_path.extension().and_then(|ext| ext.to_str()) else {
|
||||
let script = Script::from_str(&config, errors)?;
|
||||
return Ok(Unit {
|
||||
id,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
diff --git a/init.initfs.d/40_pcid.service b/init.initfs.d/40_pcid.service
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/init.initfs.d/40_pcid.service
|
||||
@@ -0,0 +1,7 @@
|
||||
+[unit]
|
||||
+description = "PCI daemon"
|
||||
+requires_weak = ["41_acpid.service"]
|
||||
+
|
||||
+[service]
|
||||
+cmd = "pcid"
|
||||
+type = "notify"
|
||||
diff --git a/init.initfs.d/40_drivers.target b/init.initfs.d/40_drivers.target
|
||||
--- a/init.initfs.d/40_drivers.target
|
||||
+++ b/init.initfs.d/40_drivers.target
|
||||
@@ -3,6 +3,7 @@ description = "Initfs drivers"
|
||||
requires_weak = [
|
||||
"10_lived.service",
|
||||
"20_graphics.target",
|
||||
+ "40_pcid.service",
|
||||
"40_ps2d.service",
|
||||
"40_bcm2835-sdhcid.service",
|
||||
"40_hwd.service",
|
||||
diff --git a/init.initfs.d/40_hwd.service b/init.initfs.d/40_hwd.service
|
||||
--- a/init.initfs.d/40_hwd.service
|
||||
+++ b/init.initfs.d/40_hwd.service
|
||||
@@ -1,6 +1,6 @@
|
||||
[unit]
|
||||
description = "Hardware manager"
|
||||
-requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target", "41_acpid.service"]
|
||||
+requires_weak = ["10_inputd.service", "10_lived.service", "20_graphics.target", "40_pcid.service", "41_acpid.service"]
|
||||
|
||||
[service]
|
||||
cmd = "hwd"
|
||||
diff --git a/init.initfs.d/40_pcid-spawner-initfs.service b/init.initfs.d/40_pcid-spawner-initfs.service
|
||||
--- a/init.initfs.d/40_pcid-spawner-initfs.service
|
||||
+++ b/init.initfs.d/40_pcid-spawner-initfs.service
|
||||
@@ -1,6 +1,6 @@
|
||||
[unit]
|
||||
description = "PCI driver spawner"
|
||||
-requires_weak = ["10_inputd.service", "20_graphics.target", "40_hwd.service"]
|
||||
+requires_weak = ["10_inputd.service", "20_graphics.target", "40_pcid.service"]
|
||||
|
||||
[service]
|
||||
cmd = "pcid-spawner"
|
||||
@@ -1,896 +0,0 @@
|
||||
# P2-inputd.patch
|
||||
# Extract inputd improvements: named producers, device consumers, hotplug events,
|
||||
# error handling, and input scheme extensions.
|
||||
#
|
||||
# Files: drivers/inputd/src/lib.rs, drivers/inputd/src/main.rs
|
||||
|
||||
diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs
|
||||
index b68e8211..f07a411d 100644
|
||||
--- a/drivers/inputd/src/lib.rs
|
||||
+++ b/drivers/inputd/src/lib.rs
|
||||
@@ -64,25 +64,53 @@ impl ConsumerHandle {
|
||||
let fd = self.0.as_raw_fd();
|
||||
let written = libredox::call::fpath(fd as usize, &mut buffer)?;
|
||||
|
||||
- assert!(written <= buffer.len());
|
||||
-
|
||||
- let mut display_path = PathBuf::from(
|
||||
- std::str::from_utf8(&buffer[..written])
|
||||
- .expect("init: display path UTF-8 check failed")
|
||||
- .to_owned(),
|
||||
- );
|
||||
- display_path.set_file_name(format!(
|
||||
- "v2/{}",
|
||||
- display_path.file_name().unwrap().to_str().unwrap()
|
||||
- ));
|
||||
- let display_path = display_path.to_str().unwrap();
|
||||
+ if written > buffer.len() {
|
||||
+ return Err(io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ "inputd: display path exceeded buffer size",
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ let path_str = std::str::from_utf8(&buffer[..written]).map_err(|e| {
|
||||
+ io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ format!("inputd: display path is not valid UTF-8: {e}"),
|
||||
+ )
|
||||
+ })?;
|
||||
+ let mut display_path = PathBuf::from(path_str.to_owned());
|
||||
+
|
||||
+ let file_name = display_path
|
||||
+ .file_name()
|
||||
+ .and_then(|n| n.to_str())
|
||||
+ .ok_or_else(|| {
|
||||
+ io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ format!(
|
||||
+ "inputd: display path has no valid file name: {}",
|
||||
+ display_path.display()
|
||||
+ ),
|
||||
+ )
|
||||
+ })?;
|
||||
+ display_path.set_file_name(format!("v2/{file_name}"));
|
||||
+ let display_path_str = display_path.to_str().ok_or_else(|| {
|
||||
+ io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ format!(
|
||||
+ "inputd: constructed display path is not valid UTF-8: {}",
|
||||
+ display_path.display()
|
||||
+ ),
|
||||
+ )
|
||||
+ })?;
|
||||
|
||||
let display_file =
|
||||
- libredox::call::open(display_path, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
|
||||
+ libredox::call::open(display_path_str, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
|
||||
.map(|socket| unsafe { File::from_raw_fd(socket as RawFd) })
|
||||
- .unwrap_or_else(|err| {
|
||||
- panic!("failed to open display {}: {}", display_path, err);
|
||||
- });
|
||||
+ .map_err(|err| {
|
||||
+ io::Error::new(
|
||||
+ io::ErrorKind::Other,
|
||||
+ format!("inputd: failed to open display {display_path_str}: {err}"),
|
||||
+ )
|
||||
+ })?;
|
||||
|
||||
Ok(display_file)
|
||||
}
|
||||
@@ -152,8 +180,12 @@ impl DisplayHandle {
|
||||
|
||||
if nread == 0 {
|
||||
Ok(None)
|
||||
+ } else if nread != size_of::<VtEvent>() {
|
||||
+ Err(io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ format!("inputd: partial vt event read: got {nread}, expected {}", size_of::<VtEvent>()),
|
||||
+ ))
|
||||
} else {
|
||||
- assert_eq!(nread, size_of::<VtEvent>());
|
||||
Ok(Some(event))
|
||||
}
|
||||
}
|
||||
@@ -171,13 +203,11 @@ impl ControlHandle {
|
||||
Ok(Self(File::open(path)?))
|
||||
}
|
||||
|
||||
- /// Sent to Handle::Display
|
||||
pub fn activate_vt(&mut self, vt: usize) -> io::Result<usize> {
|
||||
let cmd = ControlEvent::from(VtActivate { vt });
|
||||
self.0.write(unsafe { any_as_u8_slice(&cmd) })
|
||||
}
|
||||
|
||||
- /// Sent to Handle::Producer
|
||||
pub fn activate_keymap(&mut self, keymap: usize) -> io::Result<usize> {
|
||||
let cmd = ControlEvent::from(KeymapActivate { keymap });
|
||||
self.0.write(unsafe { any_as_u8_slice(&cmd) })
|
||||
@@ -209,3 +239,195 @@ impl ProducerHandle {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+
|
||||
+pub struct NamedProducerHandle(File);
|
||||
+
|
||||
+impl NamedProducerHandle {
|
||||
+ pub fn new(name: &str) -> io::Result<Self> {
|
||||
+ let path = format!("/scheme/input/producer/{name}");
|
||||
+ Ok(Self(File::open(path)?))
|
||||
+ }
|
||||
+
|
||||
+ pub fn write_event(&mut self, event: orbclient::Event) -> io::Result<()> {
|
||||
+ self.0.write(&event)?;
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Convenience wrapper that tries a named producer first,
|
||||
+/// falling back to the legacy anonymous producer on failure.
|
||||
+pub enum InputProducer {
|
||||
+ Named(NamedProducerHandle),
|
||||
+ Legacy(ProducerHandle),
|
||||
+}
|
||||
+
|
||||
+impl InputProducer {
|
||||
+ /// Open a named producer (`/scheme/input/producer/{name}`).
|
||||
+ /// If the named path is unavailable, fall back to the legacy
|
||||
+ /// `/scheme/input/producer` path so the driver keeps working on
|
||||
+ /// older inputd builds or degraded schemes.
|
||||
+ pub fn new_named_or_fallback(name: &str) -> io::Result<Self> {
|
||||
+ match NamedProducerHandle::new(name) {
|
||||
+ Ok(named) => Ok(InputProducer::Named(named)),
|
||||
+ Err(named_err) => {
|
||||
+ log::debug!(
|
||||
+ "inputd: named producer '{}' unavailable ({}), falling back to legacy",
|
||||
+ name,
|
||||
+ named_err
|
||||
+ );
|
||||
+ ProducerHandle::new().map(InputProducer::Legacy)
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Open the legacy anonymous producer directly.
|
||||
+ pub fn new_legacy() -> io::Result<Self> {
|
||||
+ ProducerHandle::new().map(InputProducer::Legacy)
|
||||
+ }
|
||||
+
|
||||
+ pub fn write_event(&mut self, event: orbclient::Event) -> io::Result<()> {
|
||||
+ match self {
|
||||
+ InputProducer::Named(h) => h.write_event(event),
|
||||
+ InputProducer::Legacy(h) => h.write_event(event),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub struct DeviceConsumerHandle(File);
|
||||
+
|
||||
+pub enum DeviceConsumerHandleEvent<'a> {
|
||||
+ Events(&'a [Event]),
|
||||
+}
|
||||
+
|
||||
+impl DeviceConsumerHandle {
|
||||
+ pub fn new(device_name: &str) -> io::Result<Self> {
|
||||
+ let path = format!("/scheme/input/{device_name}");
|
||||
+ Ok(Self(File::open(path)?))
|
||||
+ }
|
||||
+
|
||||
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
|
||||
+ self.0.as_fd()
|
||||
+ }
|
||||
+
|
||||
+ pub fn read_events<'a>(
|
||||
+ &self,
|
||||
+ events: &'a mut [Event],
|
||||
+ ) -> io::Result<DeviceConsumerHandleEvent<'a>> {
|
||||
+ match read_to_slice(self.0.as_fd(), events) {
|
||||
+ Ok(count) => Ok(DeviceConsumerHandleEvent::Events(&events[..count])),
|
||||
+ Err(err) => Err(err.into()),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Debug, Clone)]
|
||||
+#[repr(C)]
|
||||
+pub struct HotplugEventHeader {
|
||||
+ pub kind: u32,
|
||||
+ pub device_id: u32,
|
||||
+ pub name_len: u32,
|
||||
+ pub reserved: u32,
|
||||
+}
|
||||
+
|
||||
+#[derive(Debug, Clone)]
|
||||
+pub struct HotplugEvent {
|
||||
+ pub kind: u32,
|
||||
+ pub device_id: u32,
|
||||
+ pub name: String,
|
||||
+}
|
||||
+
|
||||
+pub struct HotplugHandle {
|
||||
+ file: File,
|
||||
+ partial: Vec<u8>,
|
||||
+}
|
||||
+
|
||||
+impl HotplugHandle {
|
||||
+ pub fn new() -> io::Result<Self> {
|
||||
+ let file = File::open("/scheme/input/events")?;
|
||||
+ Ok(Self {
|
||||
+ file,
|
||||
+ partial: Vec::new(),
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
|
||||
+ self.file.as_fd()
|
||||
+ }
|
||||
+
|
||||
+ pub fn read_event(&mut self) -> io::Result<Option<HotplugEvent>> {
|
||||
+ let mut tmp = [0u8; 256];
|
||||
+ match self.file.read(&mut tmp) {
|
||||
+ Ok(0) => {}
|
||||
+ Ok(n) => self.partial.extend_from_slice(&tmp[..n]),
|
||||
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
|
||||
+ Err(e) => return Err(e),
|
||||
+ }
|
||||
+
|
||||
+ if self.partial.len() < 16 {
|
||||
+ return Ok(None);
|
||||
+ }
|
||||
+
|
||||
+ let header = HotplugEventHeader {
|
||||
+ kind: u32::from_ne_bytes(self.partial[0..4].try_into().map_err(|_| {
|
||||
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
|
||||
+ })?),
|
||||
+ device_id: u32::from_ne_bytes(self.partial[4..8].try_into().map_err(|_| {
|
||||
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
|
||||
+ })?),
|
||||
+ name_len: u32::from_ne_bytes(self.partial[8..12].try_into().map_err(|_| {
|
||||
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
|
||||
+ })?),
|
||||
+ reserved: 0,
|
||||
+ };
|
||||
+
|
||||
+ let total_len = 16 + header.name_len as usize;
|
||||
+ if self.partial.len() < total_len {
|
||||
+ return Ok(None);
|
||||
+ }
|
||||
+
|
||||
+ let name = String::from_utf8(self.partial[16..total_len].to_vec()).map_err(|e| {
|
||||
+ io::Error::new(io::ErrorKind::InvalidData, format!("invalid UTF-8: {e}"))
|
||||
+ })?;
|
||||
+
|
||||
+ self.partial.drain(..total_len);
|
||||
+
|
||||
+ Ok(Some(HotplugEvent {
|
||||
+ kind: header.kind,
|
||||
+ device_id: header.device_id,
|
||||
+ name,
|
||||
+ }))
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub const RESERVED_DEVICE_NAMES: &[&str] = &[
|
||||
+ "producer",
|
||||
+ "consumer",
|
||||
+ "consumer_bootlog",
|
||||
+ "events",
|
||||
+ "handle",
|
||||
+ "handle_early",
|
||||
+ "control",
|
||||
+];
|
||||
+
|
||||
+pub struct InputDeviceLister;
|
||||
+
|
||||
+impl InputDeviceLister {
|
||||
+ pub fn list() -> io::Result<Vec<String>> {
|
||||
+ let mut dir = std::fs::read_dir("/scheme/input/")?;
|
||||
+ let mut devices = Vec::new();
|
||||
+ loop {
|
||||
+ match dir.next() {
|
||||
+ Some(Ok(entry)) => {
|
||||
+ if let Some(name) = entry.file_name().to_str() {
|
||||
+ if !RESERVED_DEVICE_NAMES.contains(&name) {
|
||||
+ devices.push(name.to_owned());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ Some(Err(e)) => return Err(e),
|
||||
+ None => break,
|
||||
+ }
|
||||
+ }
|
||||
+ Ok(devices)
|
||||
+ }
|
||||
+}
|
||||
diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs
|
||||
index 07aa943e..89018568 100644
|
||||
--- a/drivers/inputd/src/main.rs
|
||||
+++ b/drivers/inputd/src/main.rs
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
use core::mem::size_of;
|
||||
use std::borrow::Cow;
|
||||
-use std::collections::BTreeSet;
|
||||
+use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::mem::transmute;
|
||||
use std::ops::ControlFlow;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
@@ -26,8 +26,9 @@ use redox_scheme::{CallerCtx, OpenResult, Response, SignalBehavior, Socket};
|
||||
|
||||
use orbclient::{Event, EventOption};
|
||||
use scheme_utils::{Blocking, FpathWriter, HandleMap};
|
||||
+use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
-use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EEXIST, EINVAL};
|
||||
+use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EEXIST, EINVAL, ENOENT, ENOTDIR};
|
||||
|
||||
pub mod keymap;
|
||||
|
||||
@@ -35,8 +36,57 @@ use keymap::KeymapKind;
|
||||
|
||||
use crate::keymap::KeymapData;
|
||||
|
||||
+const DEVICE_ADD: u32 = 1;
|
||||
+const DEVICE_REMOVE: u32 = 2;
|
||||
+
|
||||
+fn validate_producer_name(name: &str) -> Result<(), SysError> {
|
||||
+ if name.is_empty() || name.contains('/') {
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
+ if inputd::RESERVED_DEVICE_NAMES.contains(&name) {
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+fn serialize_hotplug(kind: u32, device_id: u32, name: &str) -> Vec<u8> {
|
||||
+ let name_bytes = name.as_bytes();
|
||||
+ let header = HotplugHeader {
|
||||
+ kind,
|
||||
+ device_id,
|
||||
+ name_len: name_bytes.len() as u32,
|
||||
+ _reserved: 0,
|
||||
+ };
|
||||
+ let mut out = Vec::with_capacity(16 + name_bytes.len());
|
||||
+ out.extend_from_slice(&header.to_bytes());
|
||||
+ out.extend_from_slice(name_bytes);
|
||||
+ out
|
||||
+}
|
||||
+
|
||||
+#[repr(C)]
|
||||
+struct HotplugHeader {
|
||||
+ kind: u32,
|
||||
+ device_id: u32,
|
||||
+ name_len: u32,
|
||||
+ _reserved: u32,
|
||||
+}
|
||||
+
|
||||
+impl HotplugHeader {
|
||||
+ fn to_bytes(&self) -> [u8; 16] {
|
||||
+ let mut buf = [0u8; 16];
|
||||
+ buf[0..4].copy_from_slice(&self.kind.to_ne_bytes());
|
||||
+ buf[4..8].copy_from_slice(&self.device_id.to_ne_bytes());
|
||||
+ buf[8..12].copy_from_slice(&self.name_len.to_ne_bytes());
|
||||
+ buf[12..16].copy_from_slice(&self._reserved.to_ne_bytes());
|
||||
+ buf
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
enum Handle {
|
||||
Producer,
|
||||
+ NamedProducer {
|
||||
+ name: String,
|
||||
+ },
|
||||
Consumer {
|
||||
events: EventFlags,
|
||||
pending: Vec<u8>,
|
||||
@@ -46,6 +96,17 @@ enum Handle {
|
||||
notified: bool,
|
||||
vt: usize,
|
||||
},
|
||||
+ DeviceConsumer {
|
||||
+ device_name: String,
|
||||
+ events: EventFlags,
|
||||
+ pending: Vec<u8>,
|
||||
+ notified: bool,
|
||||
+ },
|
||||
+ HotplugEvents {
|
||||
+ events: EventFlags,
|
||||
+ pending: Vec<u8>,
|
||||
+ notified: bool,
|
||||
+ },
|
||||
Display {
|
||||
events: EventFlags,
|
||||
pending: Vec<VtEvent>,
|
||||
@@ -72,6 +133,9 @@ struct InputScheme {
|
||||
rshift: bool,
|
||||
|
||||
has_new_events: bool,
|
||||
+
|
||||
+ devices: BTreeMap<String, u32>,
|
||||
+ next_device_id: AtomicUsize,
|
||||
}
|
||||
|
||||
impl InputScheme {
|
||||
@@ -90,9 +154,28 @@ impl InputScheme {
|
||||
lshift: false,
|
||||
rshift: false,
|
||||
has_new_events: false,
|
||||
+
|
||||
+ devices: BTreeMap::new(),
|
||||
+ next_device_id: AtomicUsize::new(1),
|
||||
}
|
||||
}
|
||||
|
||||
+ fn emit_hotplug(&mut self, kind: u32, device_id: u32, name: &str) {
|
||||
+ let record = serialize_hotplug(kind, device_id, name);
|
||||
+ for handle in self.handles.values_mut() {
|
||||
+ if let Handle::HotplugEvents {
|
||||
+ pending,
|
||||
+ notified,
|
||||
+ ..
|
||||
+ } = handle
|
||||
+ {
|
||||
+ pending.extend_from_slice(&record);
|
||||
+ *notified = false;
|
||||
+ }
|
||||
+ }
|
||||
+ self.has_new_events = true;
|
||||
+ }
|
||||
+
|
||||
fn switch_vt(&mut self, new_active: usize) {
|
||||
if let Some(active_vt) = self.active_vt {
|
||||
if new_active == active_vt {
|
||||
@@ -146,6 +229,43 @@ impl InputScheme {
|
||||
|
||||
self.active_keymap = KeymapData::new(new_active.into());
|
||||
}
|
||||
+
|
||||
+ fn deliver_to_legacy_consumers(&mut self, buf: &[u8]) {
|
||||
+ if let Some(active_vt) = self.active_vt {
|
||||
+ for handle in self.handles.values_mut() {
|
||||
+ if let Handle::Consumer {
|
||||
+ pending,
|
||||
+ notified,
|
||||
+ vt,
|
||||
+ ..
|
||||
+ } = handle
|
||||
+ {
|
||||
+ if *vt != active_vt {
|
||||
+ continue;
|
||||
+ }
|
||||
+ pending.extend_from_slice(buf);
|
||||
+ *notified = false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn deliver_to_device_consumers(&mut self, name: &str, buf: &[u8]) {
|
||||
+ for handle in self.handles.values_mut() {
|
||||
+ if let Handle::DeviceConsumer {
|
||||
+ device_name,
|
||||
+ pending,
|
||||
+ notified,
|
||||
+ ..
|
||||
+ } = handle
|
||||
+ {
|
||||
+ if device_name == name {
|
||||
+ pending.extend_from_slice(buf);
|
||||
+ *notified = false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
impl SchemeSync for InputScheme {
|
||||
@@ -170,7 +290,23 @@ impl SchemeSync for InputScheme {
|
||||
let command = path_parts.next().ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
let handle_ty = match command {
|
||||
- "producer" => Handle::Producer,
|
||||
+ "producer" => {
|
||||
+ if let Some(name) = path_parts.next() {
|
||||
+ validate_producer_name(name)?;
|
||||
+ if self.devices.contains_key(name) {
|
||||
+ return Err(SysError::new(EEXIST));
|
||||
+ }
|
||||
+ let device_id = self.next_device_id.fetch_add(1, Ordering::SeqCst) as u32;
|
||||
+ self.devices.insert(name.to_owned(), device_id);
|
||||
+ let handle = Handle::NamedProducer {
|
||||
+ name: name.to_owned(),
|
||||
+ };
|
||||
+ self.emit_hotplug(DEVICE_ADD, device_id, name);
|
||||
+ handle
|
||||
+ } else {
|
||||
+ Handle::Producer
|
||||
+ }
|
||||
+ }
|
||||
"consumer" => {
|
||||
let vt = self.next_vt_id.fetch_add(1, Ordering::Relaxed);
|
||||
self.vts.insert(vt);
|
||||
@@ -253,9 +389,23 @@ impl SchemeSync for InputScheme {
|
||||
}
|
||||
"control" => Handle::Control,
|
||||
|
||||
- _ => {
|
||||
- log::error!("invalid path '{path}'");
|
||||
- return Err(SysError::new(EINVAL));
|
||||
+ "events" => Handle::HotplugEvents {
|
||||
+ events: EventFlags::empty(),
|
||||
+ pending: Vec::new(),
|
||||
+ notified: false,
|
||||
+ },
|
||||
+
|
||||
+ // dynamic device consumer: must be a currently registered device
|
||||
+ name => {
|
||||
+ if !self.devices.contains_key(name) {
|
||||
+ return Err(SysError::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::DeviceConsumer {
|
||||
+ device_name: name.to_owned(),
|
||||
+ events: EventFlags::empty(),
|
||||
+ pending: Vec::new(),
|
||||
+ notified: false,
|
||||
+ }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -274,7 +424,7 @@ impl SchemeSync for InputScheme {
|
||||
let handle = self.handles.get(id)?;
|
||||
|
||||
if let Handle::Consumer { vt, .. } = handle {
|
||||
- write!(w, "{vt}").unwrap();
|
||||
+ write!(w, "{vt}").map_err(|_| SysError::new(EINVAL))?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SysError::new(EINVAL))
|
||||
@@ -282,6 +432,50 @@ impl SchemeSync for InputScheme {
|
||||
})
|
||||
}
|
||||
|
||||
+ fn getdents<'buf>(
|
||||
+ &mut self,
|
||||
+ id: usize,
|
||||
+ mut buf: DirentBuf<&'buf mut [u8]>,
|
||||
+ opaque_offset: u64,
|
||||
+ ) -> syscall::Result<DirentBuf<&'buf mut [u8]>> {
|
||||
+ let handle = self.handles.get(id)?;
|
||||
+ if !matches!(handle, Handle::SchemeRoot) {
|
||||
+ return Err(SysError::new(ENOTDIR));
|
||||
+ }
|
||||
+
|
||||
+ let static_entries: &[&str] = &[
|
||||
+ "producer",
|
||||
+ "consumer",
|
||||
+ "consumer_bootlog",
|
||||
+ "events",
|
||||
+ "handle",
|
||||
+ "handle_early",
|
||||
+ "control",
|
||||
+ ];
|
||||
+
|
||||
+ let device_names: Vec<&str> = self.devices.keys().map(|s| s.as_str()).collect();
|
||||
+
|
||||
+ let all_entries: Vec<(&str, DirentKind)> = static_entries
|
||||
+ .iter()
|
||||
+ .map(|&name| (name, DirentKind::Directory))
|
||||
+ .chain(device_names.iter().map(|&name| (name, DirentKind::Unspecified)))
|
||||
+ .collect();
|
||||
+
|
||||
+ for (idx, (name, kind)) in all_entries
|
||||
+ .iter()
|
||||
+ .enumerate()
|
||||
+ .skip(opaque_offset as usize)
|
||||
+ {
|
||||
+ buf.entry(DirEntry {
|
||||
+ inode: 0,
|
||||
+ next_opaque_id: idx as u64 + 1,
|
||||
+ name,
|
||||
+ kind: *kind,
|
||||
+ })?;
|
||||
+ }
|
||||
+ Ok(buf)
|
||||
+ }
|
||||
+
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
@@ -313,6 +507,22 @@ impl SchemeSync for InputScheme {
|
||||
Ok(copy)
|
||||
}
|
||||
|
||||
+ Handle::DeviceConsumer { pending, .. } => {
|
||||
+ let copy = core::cmp::min(pending.len(), buf.len());
|
||||
+ for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
+ buf[i] = byte;
|
||||
+ }
|
||||
+ Ok(copy)
|
||||
+ }
|
||||
+
|
||||
+ Handle::HotplugEvents { pending, .. } => {
|
||||
+ let copy = core::cmp::min(pending.len(), buf.len());
|
||||
+ for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
+ buf[i] = byte;
|
||||
+ }
|
||||
+ Ok(copy)
|
||||
+ }
|
||||
+
|
||||
Handle::Display { pending, .. } => {
|
||||
if buf.len() % size_of::<VtEvent>() == 0 {
|
||||
let copy = core::cmp::min(pending.len(), buf.len() / size_of::<VtEvent>());
|
||||
@@ -334,6 +544,10 @@ impl SchemeSync for InputScheme {
|
||||
log::error!("producer tried to read");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
+ Handle::NamedProducer { .. } => {
|
||||
+ log::error!("named producer tried to read");
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
Handle::Control => {
|
||||
log::error!("control tried to read");
|
||||
return Err(SysError::new(EINVAL));
|
||||
@@ -379,11 +593,20 @@ impl SchemeSync for InputScheme {
|
||||
log::error!("consumer tried to write");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
+ Handle::DeviceConsumer { .. } => {
|
||||
+ log::error!("device consumer tried to write");
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
+ Handle::HotplugEvents { .. } => {
|
||||
+ log::error!("hotplug events tried to write");
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
Handle::Display { .. } => {
|
||||
log::error!("display tried to write");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
Handle::Producer => {}
|
||||
+ Handle::NamedProducer { .. } => {}
|
||||
Handle::SchemeRoot => return Err(SysError::new(EBADF)),
|
||||
}
|
||||
|
||||
@@ -397,6 +620,11 @@ impl SchemeSync for InputScheme {
|
||||
buf.len() / size_of::<Event>(),
|
||||
)
|
||||
});
|
||||
+ let producer_name = match self.handles.get(id)? {
|
||||
+ Handle::NamedProducer { ref name } => Some(name.clone()),
|
||||
+ Handle::Producer => None,
|
||||
+ _ => return Err(SysError::new(EBADF)),
|
||||
+ };
|
||||
|
||||
for i in 0..events.len() {
|
||||
let mut new_active_opt = None;
|
||||
@@ -437,38 +665,21 @@ impl SchemeSync for InputScheme {
|
||||
}
|
||||
}
|
||||
|
||||
- let handle = self.handles.get_mut(id)?;
|
||||
- assert!(matches!(handle, Handle::Producer));
|
||||
-
|
||||
- let buf = unsafe {
|
||||
+ let serialized = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
(events.as_ptr()) as *const u8,
|
||||
events.len() * size_of::<Event>(),
|
||||
)
|
||||
};
|
||||
|
||||
- if let Some(active_vt) = self.active_vt {
|
||||
- for handle in self.handles.values_mut() {
|
||||
- match handle {
|
||||
- Handle::Consumer {
|
||||
- pending,
|
||||
- notified,
|
||||
- vt,
|
||||
- ..
|
||||
- } => {
|
||||
- if *vt != active_vt {
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- pending.extend_from_slice(buf);
|
||||
- *notified = false;
|
||||
- }
|
||||
- _ => continue,
|
||||
- }
|
||||
- }
|
||||
+ if let Some(ref name) = producer_name {
|
||||
+ self.deliver_to_device_consumers(name, serialized);
|
||||
}
|
||||
|
||||
- Ok(buf.len())
|
||||
+ // named producers also feed the legacy path; legacy producers only feed legacy
|
||||
+ self.deliver_to_legacy_consumers(serialized);
|
||||
+
|
||||
+ Ok(serialized.len())
|
||||
}
|
||||
|
||||
fn fevent(
|
||||
@@ -487,6 +698,24 @@ impl SchemeSync for InputScheme {
|
||||
*notified = false;
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
+ Handle::DeviceConsumer {
|
||||
+ ref mut events,
|
||||
+ ref mut notified,
|
||||
+ ..
|
||||
+ } => {
|
||||
+ *events = flags;
|
||||
+ *notified = false;
|
||||
+ Ok(EventFlags::empty())
|
||||
+ }
|
||||
+ Handle::HotplugEvents {
|
||||
+ ref mut events,
|
||||
+ ref mut notified,
|
||||
+ ..
|
||||
+ } => {
|
||||
+ *events = flags;
|
||||
+ *notified = false;
|
||||
+ Ok(EventFlags::empty())
|
||||
+ }
|
||||
Handle::Display {
|
||||
ref mut events,
|
||||
ref mut notified,
|
||||
@@ -496,7 +725,7 @@ impl SchemeSync for InputScheme {
|
||||
*notified = false;
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
- Handle::Producer | Handle::Control => {
|
||||
+ Handle::Producer | Handle::NamedProducer { .. } | Handle::Control => {
|
||||
log::error!("producer or control tried to use an event queue");
|
||||
Err(SysError::new(EINVAL))
|
||||
}
|
||||
@@ -505,8 +734,8 @@ impl SchemeSync for InputScheme {
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
- match self.handles.remove(id).unwrap() {
|
||||
- Handle::Consumer { vt, .. } => {
|
||||
+ match self.handles.remove(id) {
|
||||
+ Some(Handle::Consumer { vt, .. }) => {
|
||||
self.vts.remove(&vt);
|
||||
if self.active_vt == Some(vt) {
|
||||
if let Some(&new_vt) = self.vts.last() {
|
||||
@@ -516,7 +745,15 @@ impl SchemeSync for InputScheme {
|
||||
}
|
||||
}
|
||||
}
|
||||
- _ => {}
|
||||
+ Some(Handle::NamedProducer { name, .. }) => {
|
||||
+ if let Some(device_id) = self.devices.remove(&name) {
|
||||
+ self.emit_hotplug(DEVICE_REMOVE, device_id, &name);
|
||||
+ }
|
||||
+ }
|
||||
+ Some(_) => {}
|
||||
+ None => {
|
||||
+ log::warn!("inputd: on_close called with unknown handle id {id}");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,6 +801,39 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> {
|
||||
|
||||
*notified = true;
|
||||
}
|
||||
+ Handle::DeviceConsumer {
|
||||
+ events,
|
||||
+ pending,
|
||||
+ ref mut notified,
|
||||
+ ..
|
||||
+ } => {
|
||||
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ socket_file.write_response(
|
||||
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
|
||||
+ SignalBehavior::Restart,
|
||||
+ )?;
|
||||
+
|
||||
+ *notified = true;
|
||||
+ }
|
||||
+ Handle::HotplugEvents {
|
||||
+ events,
|
||||
+ pending,
|
||||
+ ref mut notified,
|
||||
+ } => {
|
||||
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ socket_file.write_response(
|
||||
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
|
||||
+ SignalBehavior::Restart,
|
||||
+ )?;
|
||||
+
|
||||
+ *notified = true;
|
||||
+ }
|
||||
Handle::Display {
|
||||
events,
|
||||
pending,
|
||||
@@ -589,8 +859,11 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! {
|
||||
- deamon(daemon).unwrap();
|
||||
- unreachable!();
|
||||
+ if let Err(err) = deamon(daemon) {
|
||||
+ log::error!("inputd: scheme daemon failed: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ unreachable!()
|
||||
}
|
||||
|
||||
const HELP: &str = r#"
|
||||
@@ -608,13 +881,26 @@ fn main() {
|
||||
match val.as_ref() {
|
||||
// Activates a VT.
|
||||
"-A" => {
|
||||
- let vt = args.next().unwrap().parse::<usize>().unwrap();
|
||||
+ let vt_str = args.next().unwrap_or_else(|| {
|
||||
+ eprintln!("inputd: -A requires a VT number argument");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
+ let vt = vt_str.parse::<usize>().unwrap_or_else(|_| {
|
||||
+ eprintln!("inputd: invalid VT number: {vt_str}");
|
||||
+ std::process::exit(1);
|
||||
+ });
|
||||
|
||||
- let mut handle =
|
||||
- inputd::ControlHandle::new().expect("inputd: failed to open control handle");
|
||||
- handle
|
||||
- .activate_vt(vt)
|
||||
- .expect("inputd: failed to activate VT");
|
||||
+ let mut handle = match inputd::ControlHandle::new() {
|
||||
+ Ok(h) => h,
|
||||
+ Err(e) => {
|
||||
+ eprintln!("inputd: failed to open control handle: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ if let Err(e) = handle.activate_vt(vt) {
|
||||
+ eprintln!("inputd: failed to activate VT {vt}: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
}
|
||||
// Activates a keymap.
|
||||
"-K" => {
|
||||
@@ -630,11 +916,17 @@ fn main() {
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
- let mut handle =
|
||||
- inputd::ControlHandle::new().expect("inputd: failed to open control handle");
|
||||
- handle
|
||||
- .activate_keymap(vt as usize)
|
||||
- .expect("inputd: failed to activate keymap");
|
||||
+ let mut handle = match inputd::ControlHandle::new() {
|
||||
+ Ok(h) => h,
|
||||
+ Err(e) => {
|
||||
+ eprintln!("inputd: failed to open control handle: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
+ if let Err(e) = handle.activate_keymap(vt as usize) {
|
||||
+ eprintln!("inputd: failed to activate keymap: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
}
|
||||
// List available keymaps
|
||||
"--keymaps" => {
|
||||
@@ -647,7 +939,10 @@ fn main() {
|
||||
println!("{}", HELP);
|
||||
}
|
||||
|
||||
- _ => panic!("inputd: invalid argument: {}", val),
|
||||
+ _ => {
|
||||
+ eprintln!("inputd: invalid argument: {val}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
}
|
||||
} else {
|
||||
common::setup_logging(
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
# P2-logd.patch
|
||||
# Extract logd hardening: optional kernel debug/syslog handles, graceful error
|
||||
# handling, and resilient request processing loop.
|
||||
#
|
||||
# Files: logd/src/main.rs, logd/src/scheme.rs
|
||||
|
||||
diff --git a/logd/src/main.rs b/logd/src/main.rs
|
||||
index 3636f1fa..559d8993 100644
|
||||
--- a/logd/src/main.rs
|
||||
+++ b/logd/src/main.rs
|
||||
@@ -6,18 +6,30 @@ use crate::scheme::LogScheme;
|
||||
mod scheme;
|
||||
|
||||
fn daemon(daemon: daemon::SchemeDaemon) -> ! {
|
||||
- let socket = Socket::create().expect("logd: failed to create log scheme");
|
||||
+ let socket = match Socket::create() {
|
||||
+ Ok(s) => s,
|
||||
+ Err(e) => {
|
||||
+ eprintln!("logd: failed to create log scheme: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let mut scheme = LogScheme::new(&socket);
|
||||
let handler = Blocking::new(&socket, 16);
|
||||
|
||||
let _ = daemon.ready_sync_scheme(&socket, &mut scheme);
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("logd: failed to enter null namespace");
|
||||
-
|
||||
- handler
|
||||
- .process_requests_blocking(scheme)
|
||||
- .expect("logd: failed to process requests");
|
||||
+ if let Err(e) = libredox::call::setrens(0, 0) {
|
||||
+ eprintln!("logd: failed to enter null namespace: {e}");
|
||||
+ }
|
||||
+
|
||||
+ match handler.process_requests_blocking(scheme) {
|
||||
+ Ok(never) => match never {},
|
||||
+ Err(e) => {
|
||||
+ eprintln!("logd: failed to process requests: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs
|
||||
index 070de3d6..ef3e175c 100644
|
||||
--- a/logd/src/scheme.rs
|
||||
+++ b/logd/src/scheme.rs
|
||||
@@ -22,7 +22,7 @@ pub enum LogHandle {
|
||||
|
||||
pub struct LogScheme<'sock> {
|
||||
socket: &'sock Socket,
|
||||
- kernel_debug: File,
|
||||
+ kernel_debug: Option<File>,
|
||||
output_tx: Sender<OutputCmd>,
|
||||
handles: HandleMap<LogHandle>,
|
||||
}
|
||||
@@ -34,12 +34,24 @@ enum OutputCmd {
|
||||
|
||||
impl<'sock> LogScheme<'sock> {
|
||||
pub fn new(socket: &'sock Socket) -> Self {
|
||||
- let kernel_debug = OpenOptions::new()
|
||||
+ let kernel_debug = match OpenOptions::new()
|
||||
.write(true)
|
||||
.open("/scheme/debug")
|
||||
- .unwrap();
|
||||
+ {
|
||||
+ Ok(f) => Some(f),
|
||||
+ Err(e) => {
|
||||
+ eprintln!("logd: failed to open /scheme/debug: {e}");
|
||||
+ None
|
||||
+ }
|
||||
+ };
|
||||
|
||||
- let mut kernel_sys_log = std::fs::File::open("/scheme/sys/log").unwrap();
|
||||
+ let kernel_sys_log = match std::fs::File::open("/scheme/sys/log") {
|
||||
+ Ok(f) => Some(f),
|
||||
+ Err(e) => {
|
||||
+ eprintln!("logd: failed to open /scheme/sys/log: {e}");
|
||||
+ None
|
||||
+ }
|
||||
+ };
|
||||
|
||||
let (output_tx, output_rx) = mpsc::channel::<OutputCmd>();
|
||||
|
||||
@@ -72,20 +84,28 @@ impl<'sock> LogScheme<'sock> {
|
||||
}
|
||||
});
|
||||
|
||||
- let output_tx2 = output_tx.clone();
|
||||
- std::thread::spawn(move || {
|
||||
- let mut handle_buf = vec![];
|
||||
- let mut buf = [0; 4096];
|
||||
- buf[.."kernel: ".len()].copy_from_slice(b"kernel: ");
|
||||
- loop {
|
||||
- let n = kernel_sys_log.read(&mut buf["kernel: ".len()..]).unwrap();
|
||||
- if n == 0 {
|
||||
- // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
|
||||
- break;
|
||||
+ if let Some(mut kernel_sys_log) = kernel_sys_log {
|
||||
+ let output_tx2 = output_tx.clone();
|
||||
+ std::thread::spawn(move || {
|
||||
+ let mut handle_buf = vec![];
|
||||
+ let mut buf = [0; 4096];
|
||||
+ buf[.."kernel: ".len()].copy_from_slice(b"kernel: ");
|
||||
+ loop {
|
||||
+ let n = match kernel_sys_log.read(&mut buf["kernel: ".len()..]) {
|
||||
+ Ok(n) => n,
|
||||
+ Err(e) => {
|
||||
+ eprintln!("logd: error reading kernel log: {e}");
|
||||
+ break;
|
||||
+ }
|
||||
+ };
|
||||
+ if n == 0 {
|
||||
+ // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue
|
||||
+ break;
|
||||
+ }
|
||||
+ Self::write_logs(&output_tx2, &mut handle_buf, "kernel", &buf, None);
|
||||
}
|
||||
- Self::write_logs(&output_tx2, &mut handle_buf, "kernel", &buf, None);
|
||||
- }
|
||||
- });
|
||||
+ });
|
||||
+ }
|
||||
|
||||
LogScheme {
|
||||
socket,
|
||||
@@ -120,9 +140,9 @@ impl<'sock> LogScheme<'sock> {
|
||||
let _ = kernel_debug.flush();
|
||||
}
|
||||
|
||||
- output_tx
|
||||
- .send(OutputCmd::Log(mem::take(handle_buf)))
|
||||
- .unwrap();
|
||||
+ if let Err(e) = output_tx.send(OutputCmd::Log(mem::take(handle_buf))) {
|
||||
+ eprintln!("logd: failed to send log output: {e}");
|
||||
+ }
|
||||
}
|
||||
|
||||
i += 1;
|
||||
@@ -196,7 +216,7 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
|
||||
handle_buf,
|
||||
context,
|
||||
buf,
|
||||
- Some(&mut self.kernel_debug),
|
||||
+ self.kernel_debug.as_mut(),
|
||||
);
|
||||
|
||||
Ok(buf.len())
|
||||
@@ -217,7 +237,10 @@ impl<'sock> SchemeSync for LogScheme<'sock> {
|
||||
) {
|
||||
return Err(e);
|
||||
}
|
||||
- self.output_tx.send(OutputCmd::AddSink(new_fd)).unwrap();
|
||||
+ if let Err(e) = self.output_tx.send(OutputCmd::AddSink(new_fd)) {
|
||||
+ eprintln!("logd: failed to add log sink: {e}");
|
||||
+ return Err(Error::new(EIO));
|
||||
+ }
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# P2-misc-daemon-fixes.patch
|
||||
#
|
||||
# Various daemon error handling and robustness fixes:
|
||||
# graceful degradation when audio hardware is absent, InputProducer migration
|
||||
# for USB HID, zerod argument handling and request loop resilience.
|
||||
# graceful degradation when audio hardware is absent,
|
||||
# zerod argument handling and request loop resilience.
|
||||
#
|
||||
# Covers:
|
||||
# - audiod/main.rs: handle ENODEV when no audio hardware present
|
||||
# - usbhidd/main.rs: migrate ProducerHandle→InputProducer with named producer
|
||||
# - zerod/main.rs: derive Copy for Ty, graceful argument parsing, resilient request loop
|
||||
#
|
||||
diff --git a/audiod/src/main.rs b/audiod/src/main.rs
|
||||
@@ -29,98 +28,3 @@ index 51b103af..2354cf5f 100644
|
||||
|
||||
let socket = Socket::create().context("failed to create scheme")?;
|
||||
|
||||
|
||||
diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs
|
||||
index 15c5b778..706c4008 100644
|
||||
--- a/drivers/input/usbhidd/src/main.rs
|
||||
+++ b/drivers/input/usbhidd/src/main.rs
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use std::{env, thread, time};
|
||||
|
||||
-use inputd::ProducerHandle;
|
||||
+use inputd::InputProducer;
|
||||
use orbclient::KeyEvent as OrbKeyEvent;
|
||||
use rehid::{
|
||||
report_desc::{ReportTy, REPORT_DESC_TY},
|
||||
@@ -15,7 +15,7 @@ use xhcid_interface::{
|
||||
|
||||
mod reqs;
|
||||
|
||||
-fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) {
|
||||
+fn send_key_event(display: &mut InputProducer, usage_page: u16, usage: u16, pressed: bool) {
|
||||
let scancode = match usage_page {
|
||||
0x07 => match usage {
|
||||
0x04 => orbclient::K_A,
|
||||
@@ -272,7 +272,9 @@ fn main() -> Result<()> {
|
||||
let report_ty = ReportTy::Input;
|
||||
let report_id = 0;
|
||||
|
||||
- let mut display = ProducerHandle::new().context("Failed to open input socket")?;
|
||||
+ let producer_name = format!("usb-{}-if{}", port, interface_num);
|
||||
+ let mut display = InputProducer::new_named_or_fallback(&producer_name)
|
||||
+ .context("Failed to open input socket")?;
|
||||
let mut endpoint_opt = match endp_desc_opt {
|
||||
Some((endp_num, _endp_desc)) => match handle.open_endpoint(endp_num as u8) {
|
||||
Ok(ok) => Some(ok),
|
||||
|
||||
diff --git a/zerod/src/main.rs b/zerod/src/main.rs
|
||||
index c9bd1465..59f6b97c 100644
|
||||
--- a/zerod/src/main.rs
|
||||
+++ b/zerod/src/main.rs
|
||||
@@ -5,6 +5,7 @@ use scheme_utils::Blocking;
|
||||
|
||||
mod scheme;
|
||||
|
||||
+#[derive(Clone, Copy)]
|
||||
enum Ty {
|
||||
Null,
|
||||
Zero,
|
||||
@@ -15,21 +16,36 @@ fn main() {
|
||||
}
|
||||
|
||||
fn daemon(daemon: daemon::SchemeDaemon) -> ! {
|
||||
- let ty = match &*std::env::args().nth(1).unwrap() {
|
||||
- "null" => Ty::Null,
|
||||
- "zero" => Ty::Zero,
|
||||
- _ => panic!("needs to be called with either null or zero as argument"),
|
||||
+ let ty = match std::env::args().nth(1).as_deref() {
|
||||
+ Some("null") => Ty::Null,
|
||||
+ Some("zero") | None => Ty::Zero,
|
||||
+ Some(other) => {
|
||||
+ eprintln!("zerod: unknown argument '{other}', use 'null' or 'zero'");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
};
|
||||
|
||||
- let socket = Socket::create().expect("zerod: failed to create zero scheme");
|
||||
+ let socket = match Socket::create() {
|
||||
+ Ok(s) => s,
|
||||
+ Err(e) => {
|
||||
+ eprintln!("zerod: failed to create zero scheme: {e}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
+ };
|
||||
let mut zero_scheme = ZeroScheme(ty);
|
||||
- let zero_handler = Blocking::new(&socket, 16);
|
||||
|
||||
let _ = daemon.ready_sync_scheme(&socket, &mut zero_scheme);
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("zerod: failed to enter null namespace");
|
||||
-
|
||||
- zero_handler
|
||||
- .process_requests_blocking(zero_scheme)
|
||||
- .expect("zerod: failed to process events from zero scheme");
|
||||
+ if let Err(e) = libredox::call::setrens(0, 0) {
|
||||
+ eprintln!("zerod: failed to enter null namespace: {e}");
|
||||
+ }
|
||||
+
|
||||
+ loop {
|
||||
+ let zero_handler = Blocking::new(&socket, 16);
|
||||
+ let scheme = ZeroScheme(ty);
|
||||
+ match zero_handler.process_requests_blocking(scheme) {
|
||||
+ Ok(never) => never,
|
||||
+ Err(e) => eprintln!("zerod: error processing requests: {e}"),
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
# P2-ps2d-improvements.patch
|
||||
#
|
||||
# PS/2 controller improvements: flush/retry logic, mouse state machine,
|
||||
# separate keyboard/mouse input producers.
|
||||
# PS/2 controller improvements: flush/retry logic, mouse state machine fixes.
|
||||
#
|
||||
# Covers:
|
||||
# - ps2d/controller.rs: flush stale bytes, self-test with retry, AUX port test
|
||||
# - ps2d/main.rs: separate InputProducer for keyboard and mouse
|
||||
# - ps2d/mouse.rs: ACK/RESEND/BAT constant names, resend handling, state machine fixes
|
||||
# - ps2d/state.rs: dual InputProducer fields, non-fatal init error handling
|
||||
# - ps2d/state.rs: non-fatal init error handling
|
||||
#
|
||||
diff --git a/drivers/input/ps2d/src/controller.rs b/drivers/input/ps2d/src/controller.rs
|
||||
index d7af4cba..638b7cc1 100644
|
||||
@@ -31,7 +29,7 @@ index d7af4cba..638b7cc1 100644
|
||||
@@ -271,6 +279,50 @@ impl Ps2 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+ /// Drain all pending bytes from the controller output buffer.
|
||||
+ /// Borrowed from Linux i8042_flush(): stale firmware/BIOS bytes can be
|
||||
+ /// misinterpreted as device responses during initialization.
|
||||
@@ -78,10 +76,10 @@ index d7af4cba..638b7cc1 100644
|
||||
+
|
||||
pub fn init_keyboard(&mut self) -> Result<(), Error> {
|
||||
let mut b;
|
||||
|
||||
|
||||
@@ -308,66 +360,125 @@ impl Ps2 {
|
||||
}
|
||||
|
||||
|
||||
pub fn init(&mut self) -> Result<(), Error> {
|
||||
+ // Linux i8042_controller_check(): verify controller is present by
|
||||
+ // flushing any stale data. A stuck output buffer means no controller.
|
||||
@@ -115,7 +113,7 @@ index d7af4cba..638b7cc1 100644
|
||||
+ warn!("disable second port failed: {:?}", err);
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
- // Disable clocks, disable interrupts, and disable translate
|
||||
+ // Flush again after disabling — firmware may have queued more bytes
|
||||
+ self.flush();
|
||||
@@ -131,7 +129,7 @@ index d7af4cba..638b7cc1 100644
|
||||
| ConfigFlags::SECOND_DISABLED;
|
||||
self.set_config(config)?;
|
||||
}
|
||||
|
||||
|
||||
- // The keyboard seems to still collect bytes even when we disable
|
||||
- // the port, so we must disable the keyboard too
|
||||
+ // Linux i8042_controller_selftest(): retry up to 5 times with delay.
|
||||
@@ -184,7 +182,7 @@ index d7af4cba..638b7cc1 100644
|
||||
-
|
||||
Ok(b)
|
||||
})?;
|
||||
|
||||
|
||||
- {
|
||||
- // Perform the self test
|
||||
- self.command(Command::TestController)?;
|
||||
@@ -199,7 +197,7 @@ index d7af4cba..638b7cc1 100644
|
||||
error!("failed to initialize keyboard: {:?}", err);
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
|
||||
- // Enable second device
|
||||
- let enable_mouse = match self.command(Command::EnableSecond) {
|
||||
- Ok(()) => true,
|
||||
@@ -223,7 +221,7 @@ index d7af4cba..638b7cc1 100644
|
||||
+ info!("skipping mouse init: aux port test did not pass");
|
||||
+ false
|
||||
};
|
||||
|
||||
|
||||
{
|
||||
- // Enable keyboard data reporting
|
||||
- // Use inner function to prevent retries
|
||||
@@ -233,41 +231,7 @@ index d7af4cba..638b7cc1 100644
|
||||
- //TODO: fix by using interrupts?
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/drivers/input/ps2d/src/main.rs b/drivers/input/ps2d/src/main.rs
|
||||
index db17de2a..1ae055e4 100644
|
||||
--- a/drivers/input/ps2d/src/main.rs
|
||||
+++ b/drivers/input/ps2d/src/main.rs
|
||||
@@ -11,7 +11,7 @@ use std::process;
|
||||
|
||||
use common::acquire_port_io_rights;
|
||||
use event::{user_data, EventQueue};
|
||||
-use inputd::ProducerHandle;
|
||||
+use inputd::InputProducer;
|
||||
|
||||
use crate::state::Ps2d;
|
||||
|
||||
@@ -31,7 +31,10 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
|
||||
acquire_port_io_rights().expect("ps2d: failed to get I/O permission");
|
||||
|
||||
- let input = ProducerHandle::new().expect("ps2d: failed to open input producer");
|
||||
+ let keyboard_input = InputProducer::new_named_or_fallback("ps2-keyboard")
|
||||
+ .expect("ps2d: failed to open input producer");
|
||||
+ let mouse_input = InputProducer::new_named_or_fallback("ps2-mouse")
|
||||
+ .expect("ps2d: failed to open input producer");
|
||||
|
||||
user_data! {
|
||||
enum Source {
|
||||
@@ -93,7 +96,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
|
||||
daemon.ready();
|
||||
|
||||
- let mut ps2d = Ps2d::new(input, time_file);
|
||||
+ let mut ps2d = Ps2d::new(keyboard_input, mouse_input, time_file);
|
||||
|
||||
let mut data = [0; 256];
|
||||
for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) {
|
||||
|
||||
diff --git a/drivers/input/ps2d/src/mouse.rs b/drivers/input/ps2d/src/mouse.rs
|
||||
index 9e95ab88..8087c8c4 100644
|
||||
--- a/drivers/input/ps2d/src/mouse.rs
|
||||
@@ -275,7 +239,7 @@ index 9e95ab88..8087c8c4 100644
|
||||
@@ -5,6 +5,11 @@ pub const RESET_RETRIES: usize = 10;
|
||||
pub const RESET_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
pub const COMMAND_TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
|
||||
+const CMD_ACK: u8 = 0xFA;
|
||||
+const CMD_RESEND: u8 = 0xFE;
|
||||
+const BAT_COMPLETE: u8 = 0xAA;
|
||||
@@ -285,7 +249,7 @@ index 9e95ab88..8087c8c4 100644
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
@@ -58,9 +63,11 @@ impl MouseTx {
|
||||
|
||||
|
||||
fn handle(&mut self, data: u8, ps2: &mut Ps2) -> Result<bool, ()> {
|
||||
if self.write_i < self.write.len() {
|
||||
- if data == 0xFA {
|
||||
@@ -405,112 +369,16 @@ diff --git a/drivers/input/ps2d/src/state.rs b/drivers/input/ps2d/src/state.rs
|
||||
index 9018dc6b..da304e05 100644
|
||||
--- a/drivers/input/ps2d/src/state.rs
|
||||
+++ b/drivers/input/ps2d/src/state.rs
|
||||
@@ -1,4 +1,4 @@
|
||||
-use inputd::ProducerHandle;
|
||||
+use inputd::InputProducer;
|
||||
use log::{error, warn};
|
||||
use orbclient::{ButtonEvent, KeyEvent, MouseEvent, MouseRelativeEvent, ScrollEvent};
|
||||
use std::{
|
||||
@@ -44,7 +44,8 @@ pub struct Ps2d {
|
||||
ps2: Ps2,
|
||||
vmmouse: bool,
|
||||
vmmouse_relative: bool,
|
||||
- input: ProducerHandle,
|
||||
+ keyboard_input: InputProducer,
|
||||
+ mouse_input: InputProducer,
|
||||
time_file: File,
|
||||
extended: bool,
|
||||
mouse_x: i32,
|
||||
@@ -59,9 +60,11 @@ pub struct Ps2d {
|
||||
}
|
||||
|
||||
impl Ps2d {
|
||||
- pub fn new(input: ProducerHandle, time_file: File) -> Self {
|
||||
+ pub fn new(keyboard_input: InputProducer, mouse_input: InputProducer, time_file: File) -> Self {
|
||||
@@ -61,9 +61,11 @@ impl Ps2d {
|
||||
pub fn new(input: ProducerHandle, time_file: File) -> Self {
|
||||
let mut ps2 = Ps2::new();
|
||||
- ps2.init().expect("failed to initialize");
|
||||
+ if let Err(err) = ps2.init() {
|
||||
+ log::error!("ps2d: controller init failed: {:?}", err);
|
||||
+ }
|
||||
|
||||
|
||||
// FIXME add an option for orbital to disable this when an app captures the mouse.
|
||||
let vmmouse_relative = false;
|
||||
@@ -77,7 +80,8 @@ impl Ps2d {
|
||||
ps2,
|
||||
vmmouse,
|
||||
vmmouse_relative,
|
||||
- input,
|
||||
+ keyboard_input,
|
||||
+ mouse_input,
|
||||
time_file,
|
||||
extended: false,
|
||||
mouse_x: 0,
|
||||
@@ -273,7 +277,7 @@ impl Ps2d {
|
||||
};
|
||||
|
||||
if scancode != 0 {
|
||||
- self.input
|
||||
+ self.keyboard_input
|
||||
.write_event(
|
||||
KeyEvent {
|
||||
character: '\0',
|
||||
@@ -304,7 +308,7 @@ impl Ps2d {
|
||||
|
||||
if self.vmmouse_relative {
|
||||
if dx != 0 || dy != 0 {
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(
|
||||
MouseRelativeEvent {
|
||||
dx: dx as i32,
|
||||
@@ -320,14 +324,14 @@ impl Ps2d {
|
||||
if x != self.mouse_x || y != self.mouse_y {
|
||||
self.mouse_x = x;
|
||||
self.mouse_y = y;
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(MouseEvent { x, y }.to_event())
|
||||
.expect("ps2d: failed to write mouse event");
|
||||
}
|
||||
};
|
||||
|
||||
if dz != 0 {
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(
|
||||
ScrollEvent {
|
||||
x: 0,
|
||||
@@ -348,7 +352,7 @@ impl Ps2d {
|
||||
self.mouse_left = left;
|
||||
self.mouse_middle = middle;
|
||||
self.mouse_right = right;
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(
|
||||
ButtonEvent {
|
||||
left,
|
||||
@@ -441,13 +445,13 @@ impl Ps2d {
|
||||
}
|
||||
|
||||
if dx != 0 || dy != 0 {
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(MouseRelativeEvent { dx, dy }.to_event())
|
||||
.expect("ps2d: failed to write mouse event");
|
||||
}
|
||||
|
||||
if dz != 0 {
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(ScrollEvent { x: 0, y: dz }.to_event())
|
||||
.expect("ps2d: failed to write scroll event");
|
||||
}
|
||||
@@ -462,7 +466,7 @@ impl Ps2d {
|
||||
self.mouse_left = left;
|
||||
self.mouse_middle = middle;
|
||||
self.mouse_right = right;
|
||||
- self.input
|
||||
+ self.mouse_input
|
||||
.write_event(
|
||||
ButtonEvent {
|
||||
left,
|
||||
let vmmouse = vm::enable(vmmouse_relative);
|
||||
|
||||
// TODO: QEMU hack, maybe do this when Init timed out?
|
||||
|
||||
@@ -379,34 +379,6 @@ index 74b9f732..493e79df 100644
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
||||
index ac492d5b..310fe51f 100644
|
||||
--- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
||||
+++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
||||
@@ -633,7 +633,10 @@ impl<const N: usize> Xhci<N> {
|
||||
pub fn with_ring<T, F: FnOnce(&Ring) -> T>(&self, id: RingId, function: F) -> Option<T> {
|
||||
use super::RingOrStreams;
|
||||
|
||||
- let slot_state = self.port_states.get(&id.port)?;
|
||||
+ let slot_state = self
|
||||
+ .port_states
|
||||
+ .get(&id.port)
|
||||
+ .or_else(|| self.staged_port_states.get(&id.port))?;
|
||||
let endpoint_state = slot_state.endpoint_states.get(&id.endpoint_num)?;
|
||||
|
||||
let ring_ref = match endpoint_state.transfer {
|
||||
@@ -650,7 +653,10 @@ impl<const N: usize> Xhci<N> {
|
||||
) -> Option<T> {
|
||||
use super::RingOrStreams;
|
||||
|
||||
- let mut slot_state = self.port_states.get_mut(&id.port)?;
|
||||
+ let mut slot_state = self
|
||||
+ .port_states
|
||||
+ .get_mut(&id.port)
|
||||
+ .or_else(|| self.staged_port_states.get_mut(&id.port))?;
|
||||
let mut endpoint_state = slot_state.endpoint_states.get_mut(&id.endpoint_num)?;
|
||||
|
||||
let ring_ref = match endpoint_state.transfer {
|
||||
diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
index f2143676..0d2ec432 100644
|
||||
--- a/drivers/usb/xhcid/src/xhci/mod.rs
|
||||
|
||||
@@ -2144,7 +2144,7 @@ index 94a1eb17..a7cde5d6 100644
|
||||
Ok(dsdt) => dsdt,
|
||||
Err(error) => {
|
||||
log::error!("Failed to load DSDT: {}", error);
|
||||
@@ -805,8 +2001,20 @@ impl Fadt {
|
||||
@@ -805,8 +2001,12 @@ impl Fadt {
|
||||
|
||||
context.fadt = Some(fadt.clone());
|
||||
context.dsdt = Some(Dsdt(dsdt_sdt.clone()));
|
||||
@@ -2154,14 +2154,6 @@ index 94a1eb17..a7cde5d6 100644
|
||||
+ context.reset_value = reset_value;
|
||||
|
||||
context.tables.push(dsdt_sdt);
|
||||
+
|
||||
+ if context.pci_ready() {
|
||||
+ if let Err(error) = context.refresh_s5_values() {
|
||||
+ log::warn!("Failed to evaluate \\_S5 during FADT init: {error}");
|
||||
+ }
|
||||
+ } else {
|
||||
+ log::debug!("Deferring \\_S5 evaluation until PCI registration");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user