Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements

This commit is contained in:
2026-04-26 22:31:07 +01:00
parent d4a6b356eb
commit 70a84cefee
3416 changed files with 1360518 additions and 10522 deletions
+9 -484
View File
@@ -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