Expose AML evaluation through acpi scheme

This commit is contained in:
aarch
2025-10-24 17:48:29 +00:00
committed by Jeremy Soller
parent 57ca3be975
commit 63bad63321
7 changed files with 297 additions and 46 deletions
Generated
+1
View File
@@ -50,6 +50,7 @@ dependencies = [
"redox_syscall",
"ron",
"rustc-hash",
"serde",
"thiserror",
]
+2 -1
View File
@@ -24,4 +24,5 @@ amlserde = { path = "../amlserde" }
common = { path = "../common" }
libredox = "0.1.3"
redox-scheme = "0.6.2"
arrayvec = "0.7.6"
arrayvec = "0.7.6"
serde = { version = "1.0.228", features = ["derive"] }
+49 -1
View File
@@ -1,3 +1,4 @@
use acpi::aml::object::{Object, WrappedObject};
use rustc_hash::FxHashMap;
use std::convert::{TryFrom, TryInto};
use std::ops::Deref;
@@ -18,7 +19,7 @@ use acpi::{
AcpiTables,
};
use amlserde::aml_serde_name::aml_to_symbol;
use amlserde::AmlSerde;
use amlserde::{AmlSerde, AmlSerdeValue};
#[cfg(target_arch = "x86_64")]
pub mod dmar;
@@ -328,6 +329,21 @@ impl AmlSymbols {
}
}
#[derive(Debug, Error)]
pub enum AmlEvalError {
#[error("AML error")]
AmlError(AmlError),
#[error("Failed to serialize argument")]
SerializationError,
#[error("Failed to deserialize")]
DeserializationError,
}
impl From<AmlError> for AmlEvalError {
fn from(value: AmlError) -> Self {
AmlEvalError::AmlError(value)
}
}
pub struct AcpiContext {
tables: Vec<Sdt>,
dsdt: Option<Dsdt>,
@@ -344,6 +360,38 @@ pub struct AcpiContext {
}
impl AcpiContext {
pub fn aml_eval(
&self,
symbol: AmlName,
args: Vec<AmlSerdeValue>,
) -> Result<AmlSerdeValue, AmlEvalError> {
let interpreter = &mut self.aml_symbols.write().aml_context;
interpreter.acquire_global_lock(16)?;
let args = args
.into_iter()
.map(|aml_serde_value| {
aml_serde_value
.to_aml_object()
.map(Object::wrap)
.ok_or(AmlEvalError::DeserializationError)
})
.collect::<Result<Vec<WrappedObject>, AmlEvalError>>()?;
let result = interpreter.evaluate(symbol, args);
interpreter
.release_global_lock()
.expect("Failed to release GIL!"); //TODO: check if this should panic
result
.map_err(AmlEvalError::from)
.map(|object| {
AmlSerdeValue::from_aml_value(object.deref())
.ok_or(AmlEvalError::SerializationError)
})
.flatten()
}
pub fn init(rxsdt_physaddrs: impl Iterator<Item = u64>) -> Self {
let tables = rxsdt_physaddrs
.map(|physaddr| {
+1 -1
View File
@@ -358,7 +358,7 @@ impl acpi::Handler for AmlPhysMemHandler {
}
fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), AmlError> {
log::warn!("TODO: Handler::aquire");
log::warn!("TODO: Handler::acquire");
Ok(())
}
+1
View File
@@ -10,6 +10,7 @@ use syscall::{EAGAIN, EWOULDBLOCK};
mod acpi;
mod aml_physmem;
mod scheme;
fn daemon(daemon: redox_daemon::Daemon) -> ! {
+64 -9
View File
@@ -1,9 +1,14 @@
use acpi::aml::namespace::AmlName;
use amlserde::aml_serde_name::to_aml_format;
use amlserde::AmlSerdeValue;
use core::str;
use parking_lot::RwLockReadGuard;
use redox_scheme::scheme::SchemeSync;
use redox_scheme::{CallerCtx, OpenResult};
use ron::de::SpannedError;
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
use syscall::schemev2::NewFdFlags;
@@ -12,7 +17,7 @@ use syscall::error::{Error, Result};
use syscall::error::{EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR};
use syscall::flag::{MODE_DIR, MODE_FILE};
use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDONLY, O_STAT, O_SYMLINK};
use syscall::EOPNOTSUPP;
use syscall::{EOPNOTSUPP, EOVERFLOW, EPERM};
use crate::acpi::{AcpiContext, AmlSymbols, SdtSignature};
@@ -25,13 +30,14 @@ pub struct AcpiScheme<'acpi> {
struct Handle<'a> {
kind: HandleKind<'a>,
stat: bool,
allowed_to_eval: bool,
}
enum HandleKind<'a> {
TopLevel,
Tables,
Table(SdtSignature),
Symbols(RwLockReadGuard<'a, AmlSymbols>),
Symbol(String),
Symbol { name: String, description: String },
}
impl HandleKind<'_> {
@@ -41,7 +47,7 @@ impl HandleKind<'_> {
Self::Tables => true,
Self::Table(_) => false,
Self::Symbols(_) => true,
Self::Symbol(_) => false,
Self::Symbol { .. } => false,
}
}
fn len(&self, acpi_ctx: &AcpiContext) -> Result<usize> {
@@ -51,7 +57,7 @@ impl HandleKind<'_> {
.sdt_from_signature(signature)
.ok_or(Error::new(EBADFD))?
.length(),
Self::Symbol(description) => description.len(),
Self::Symbol { description, .. } => description.len(),
// Directories
Self::TopLevel | Self::Symbols(_) | Self::Tables => 0,
})
@@ -143,7 +149,7 @@ fn parse_table(table: &[u8]) -> Option<SdtSignature> {
}
impl SchemeSync for AcpiScheme<'_> {
fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result<OpenResult> {
fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
let path = path.trim_start_matches('/');
let flag_stat = flags & O_STAT == O_STAT;
@@ -179,7 +185,10 @@ impl SchemeSync for AcpiScheme<'_> {
["symbols", symbol] => {
if let Some(description) = self.ctx.aml_lookup(symbol) {
HandleKind::Symbol(description)
HandleKind::Symbol {
name: (*symbol).to_owned(),
description,
}
} else {
return Err(Error::new(ENOENT));
}
@@ -194,9 +203,13 @@ impl SchemeSync for AcpiScheme<'_> {
return Err(Error::new(ENOTDIR));
}
if flags & O_ACCMODE != O_RDONLY && !flag_stat {
let allowed_to_eval = if flags & O_ACCMODE == O_RDONLY || flag_stat {
false
} else if ctx.uid != 0 {
true
} else {
return Err(Error::new(EINVAL));
}
};
if flags & O_SYMLINK == O_SYMLINK && !flag_stat {
return Err(Error::new(EINVAL));
@@ -210,6 +223,7 @@ impl SchemeSync for AcpiScheme<'_> {
Handle {
stat: flag_stat,
kind,
allowed_to_eval,
},
);
@@ -259,7 +273,7 @@ impl SchemeSync for AcpiScheme<'_> {
.sdt_from_signature(signature)
.ok_or(Error::new(EBADFD))?
.as_slice(),
HandleKind::Symbol(description) => description.as_bytes(),
HandleKind::Symbol { description, .. } => description.as_bytes(),
_ => return Err(Error::new(EINVAL)),
};
@@ -358,6 +372,47 @@ impl SchemeSync for AcpiScheme<'_> {
) -> Result<usize> {
Err(Error::new(EBADF))
}
fn call(&mut self, id: usize, payload: &mut [u8], _metadata: &[u64]) -> Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
if !handle.allowed_to_eval {
return Err(Error::new(EPERM));
}
let Ok(args): Result<Vec<AmlSerdeValue>, SpannedError> = ron::de::from_bytes(payload)
else {
return Err(Error::new(EINVAL));
};
let HandleKind::Symbol { name, .. } = &handle.kind else {
return Err(Error::new(EBADF));
};
let Ok(aml_name) = AmlName::from_str(&to_aml_format(name)) else {
log::error!("Failed to convert symbol name: \"{name}\" to aml name!");
return Err(Error::new(EBADF));
};
let Ok(result) = self.ctx.aml_eval(aml_name, args) else {
return Err(Error::new(EINVAL));
};
let Ok(serialized_result) = ron::ser::to_string(&result) else {
log::error!("Failed to serialize aml result!");
return Err(Error::new(EINVAL));
};
let byte_result = serialized_result.as_bytes();
let result_len = byte_result.len();
if result_len > payload.len() {
return Err(Error::new(EOVERFLOW));
}
payload[..result_len].copy_from_slice(byte_result);
Ok(result_len)
}
}
impl AcpiScheme<'_> {
+179 -34
View File
@@ -1,14 +1,24 @@
use acpi::{
aml::{
namespace::AmlName,
object::{FieldAccessType, FieldUnitKind, FieldUpdateRule, Object, ReferenceKind},
op_region::RegionSpace,
object::{
FieldAccessType, FieldFlags, FieldUnit, FieldUnitKind, FieldUpdateRule, MethodFlags,
Object, ReferenceKind, WrappedObject,
},
op_region::{OpRegion, RegionSpace},
Interpreter,
},
Handler,
Handle, Handler,
};
use serde::{Deserialize, Serialize};
use std::{ops::Deref, sync::atomic::Ordering};
use std::{
ops::{Deref, Shl},
str::FromStr,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
};
#[derive(Debug, Serialize, Deserialize)]
pub struct AmlSerde {
@@ -25,7 +35,7 @@ pub enum AmlSerdeValue {
region: AmlSerdeRegionSpace,
offset: u64,
length: u64,
parent_device: Option<String>,
parent_device: String,
},
Field {
kind: AmlSerdeFieldKind,
@@ -44,7 +54,7 @@ pub enum AmlSerdeValue {
BufferField {
offset: u64,
length: u64,
data: Option<Box<AmlSerdeValue>>,
data: Box<AmlSerdeValue>,
},
Processor {
id: u8,
@@ -57,7 +67,7 @@ pub enum AmlSerdeValue {
},
Reference {
kind: AmlSerdeReferenceKind,
inner: Option<Box<AmlSerdeValue>>,
inner: Box<AmlSerdeValue>,
},
Package {
contents: Vec<AmlSerdeValue>,
@@ -90,41 +100,53 @@ pub enum AmlSerdeRegionSpace {
#[derive(Debug, Serialize, Deserialize)]
pub enum AmlSerdeFieldKind {
Normal {
region: Option<Box<AmlSerdeValue>>,
region: Box<AmlSerdeValue>,
},
Bank {
region: Option<Box<AmlSerdeValue>>,
bank: Option<Box<AmlSerdeValue>>,
region: Box<AmlSerdeValue>,
bank: Box<AmlSerdeValue>,
bank_value: u64,
},
Index {
index: Option<Box<AmlSerdeValue>>,
data: Option<Box<AmlSerdeValue>>,
index: Box<AmlSerdeValue>,
data: Box<AmlSerdeValue>,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AmlSerdeFieldFlags {
pub access_type: AmlSerdeFieldAccessType,
pub lock_rule: bool,
pub lock_rule: bool, // bit 4
pub update_rule: AmlSerdeFieldUpdateRule,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum AmlSerdeFieldAccessType {
Any,
Byte,
Word,
DWord,
QWord,
Buffer,
impl Into<u8> for AmlSerdeFieldFlags {
fn into(self) -> u8 {
// bits 0..4
(self.access_type as u8) +
// bit 4
(self.lock_rule as u8).shl(4) +
// bits 5..7
(self.update_rule as u8).shl(5)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[repr(u8)]
pub enum AmlSerdeFieldAccessType {
Any = 0,
Byte = 1,
Word = 2,
DWord = 3,
QWord = 4,
Buffer = 5,
}
#[derive(Debug, Serialize, Deserialize)]
#[repr(u8)]
pub enum AmlSerdeFieldUpdateRule {
Preserve,
WriteAsOnes,
WriteAsZeros,
Preserve = 0,
WriteAsOnes = 1,
WriteAsZeros = 2,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -168,7 +190,7 @@ impl AmlSerdeValue {
AmlSerdeValue::String("".to_owned())
}
fn from_aml_value(aml_value: &Object) -> Option<Self> {
pub fn from_aml_value(aml_value: &Object) -> Option<Self> {
Some(match aml_value {
Object::Uninitialized => AmlSerdeValue::Uninitialized,
Object::Integer(n) => AmlSerdeValue::Integer(n.to_owned()),
@@ -190,25 +212,25 @@ impl AmlSerdeValue {
},
offset: region.base,
length: region.length,
parent_device: Some(region.parent_device_path.to_string()),
parent_device: region.parent_device_path.to_string(),
},
Object::FieldUnit(field) => AmlSerdeValue::Field {
kind: match &field.kind {
FieldUnitKind::Normal { region } => AmlSerdeFieldKind::Normal {
region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new),
region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new)?,
},
FieldUnitKind::Bank {
region,
bank,
bank_value,
} => AmlSerdeFieldKind::Bank {
region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new),
bank: AmlSerdeValue::from_aml_value(bank.deref()).map(Box::new),
region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new)?,
bank: AmlSerdeValue::from_aml_value(bank.deref()).map(Box::new)?,
bank_value: bank_value.to_owned(),
},
FieldUnitKind::Index { index, data } => AmlSerdeFieldKind::Index {
index: AmlSerdeValue::from_aml_value(index.deref()).map(Box::new),
data: AmlSerdeValue::from_aml_value(data.deref()).map(Box::new),
index: AmlSerdeValue::from_aml_value(index.deref()).map(Box::new)?,
data: AmlSerdeValue::from_aml_value(data.deref()).map(Box::new)?,
},
},
flags: AmlSerdeFieldFlags {
@@ -252,7 +274,7 @@ impl AmlSerdeValue {
} => AmlSerdeValue::BufferField {
offset: offset.to_owned() as u64,
length: length.to_owned() as u64,
data: AmlSerdeValue::from_aml_value(buffer.deref()).map(Box::new),
data: AmlSerdeValue::from_aml_value(buffer.deref()).map(Box::new)?,
},
Object::Processor {
proc_id,
@@ -273,7 +295,7 @@ impl AmlSerdeValue {
ReferenceKind::LocalOrArg => AmlSerdeReferenceKind::LocalOrArg,
ReferenceKind::Unresolved => AmlSerdeReferenceKind::Unresolved,
},
inner: AmlSerdeValue::from_aml_value(inner.deref()).map(Box::new),
inner: AmlSerdeValue::from_aml_value(inner.deref()).map(Box::new)?,
},
Object::Package(aml_contents) => AmlSerdeValue::Package {
contents: aml_contents
@@ -293,6 +315,129 @@ impl AmlSerdeValue {
Object::Debug => AmlSerdeValue::Debug,
})
}
pub fn to_aml_object(self) -> Option<Object> {
Some(match self {
AmlSerdeValue::Uninitialized => Object::Uninitialized,
AmlSerdeValue::Integer(n) => Object::Integer(n),
AmlSerdeValue::String(s) => Object::String(s),
AmlSerdeValue::OpRegion {
region,
offset,
length,
parent_device,
} => Object::OpRegion(OpRegion {
space: match region {
AmlSerdeRegionSpace::PciConfig => RegionSpace::PciConfig,
AmlSerdeRegionSpace::EmbeddedControl => RegionSpace::EmbeddedControl,
AmlSerdeRegionSpace::SMBus => RegionSpace::SmBus,
AmlSerdeRegionSpace::SystemCmos => RegionSpace::SystemCmos,
AmlSerdeRegionSpace::PciBarTarget => RegionSpace::PciBarTarget,
AmlSerdeRegionSpace::IPMI => RegionSpace::Ipmi,
AmlSerdeRegionSpace::GeneralPurposeIo => RegionSpace::GeneralPurposeIo,
AmlSerdeRegionSpace::GenericSerialBus => RegionSpace::GenericSerialBus,
AmlSerdeRegionSpace::SystemMemory => RegionSpace::SystemMemory,
AmlSerdeRegionSpace::SystemIo => RegionSpace::SystemIO,
AmlSerdeRegionSpace::Pcc => RegionSpace::Pcc,
AmlSerdeRegionSpace::OemDefined(n) => RegionSpace::Oem(n),
},
base: offset,
length,
//
parent_device_path: AmlName::from_str(&parent_device).ok()?, // TODO: Error value hidden
}),
AmlSerdeValue::Field {
kind,
flags,
offset,
length,
} => Object::FieldUnit(FieldUnit {
kind: match kind {
AmlSerdeFieldKind::Normal { region } => FieldUnitKind::Normal {
region: region.to_aml_object()?.wrap(),
},
AmlSerdeFieldKind::Bank {
region,
bank,
bank_value,
} => FieldUnitKind::Bank {
region: region.to_aml_object()?.wrap(),
bank: bank.to_aml_object()?.wrap(),
bank_value: bank_value.to_owned(),
},
AmlSerdeFieldKind::Index { index, data } => FieldUnitKind::Index {
index: index.to_aml_object()?.wrap(),
data: data.to_aml_object()?.wrap(),
},
},
flags: FieldFlags(flags.into()),
bit_index: offset as usize,
bit_length: length as usize,
}),
AmlSerdeValue::Device => Object::Device,
AmlSerdeValue::Event(event) => Object::Event(Arc::new(AtomicU64::new(event))),
AmlSerdeValue::Method {
arg_count,
serialize,
sync_level,
} => Object::Method {
code: (return None), //TODO figure out what to do here
//TODO check specs to see if all bit patterns are allowed
flags: MethodFlags(
(arg_count as u8).clamp(0, 7)
+ (serialize as u8).shl(3)
+ sync_level.clamp(0, 15).shl(4),
),
},
//TODO: handle native method?
AmlSerdeValue::Buffer(buffer_data) => Object::Buffer(buffer_data),
AmlSerdeValue::BufferField {
data,
offset,
length,
} => Object::BufferField {
offset: offset as usize,
length: length as usize,
buffer: data.to_aml_object()?.wrap(),
},
AmlSerdeValue::Processor {
id,
pblk_address,
pblk_len,
} => Object::Processor {
proc_id: id,
pblk_address,
pblk_length: pblk_len,
},
AmlSerdeValue::Mutex { mutex, sync_level } => Object::Mutex {
mutex: Handle(mutex),
sync_level: sync_level,
},
AmlSerdeValue::Reference { kind, inner } => Object::Reference {
kind: match kind {
AmlSerdeReferenceKind::RefOf => ReferenceKind::RefOf,
AmlSerdeReferenceKind::LocalOrArg => ReferenceKind::LocalOrArg,
AmlSerdeReferenceKind::Unresolved => ReferenceKind::Unresolved,
},
inner: inner.to_aml_object()?.wrap(),
},
AmlSerdeValue::Package { contents } => Object::Package(
contents
.into_iter()
.map(|item| item.to_aml_object().map(Object::wrap)) // TODO: see if errors should be ignored here
.collect::<Option<Vec<WrappedObject>>>()?,
),
AmlSerdeValue::PowerResource {
system_level,
resource_order,
} => Object::PowerResource {
system_level: system_level.to_owned(),
resource_order: resource_order.to_owned(),
},
AmlSerdeValue::RawDataBuffer => Object::RawDataBuffer,
AmlSerdeValue::ThermalZone => Object::ThermalZone,
AmlSerdeValue::Debug => Object::Debug,
})
}
}
pub mod aml_serde_name {