Skip to content
950 changes: 579 additions & 371 deletions src/aml/mod.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be some work on AmlError? At present it looks to me like it always uses the Global allocator

Large diffs are not rendered by default.

456 changes: 272 additions & 184 deletions src/aml/namespace.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rustfmt disagrees with some of the formatting (say at line 238) - did you run it?

(I'm terrible at remembering to run the formatter, so no judgements 😆)

Large diffs are not rendered by default.

285 changes: 195 additions & 90 deletions src/aml/object.rs

Large diffs are not rendered by default.

37 changes: 25 additions & 12 deletions src/aml/op_region.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
use crate::aml::{AmlError, namespace::AmlName};
use alloc::alloc::Global;
use core::alloc::Allocator;

#[derive(Clone, Debug)]
pub struct OpRegion {
#[derive(Clone)]
pub struct OpRegion<A: core::alloc::Allocator + Clone = Global> {
pub space: RegionSpace,
pub base: u64,
pub length: u64,
pub parent_device_path: AmlName,
pub parent_device_path: AmlName<A>,
}

pub trait RegionHandler {
fn read_u8(&self, region: &OpRegion) -> Result<u8, AmlError>;
fn read_u16(&self, region: &OpRegion) -> Result<u16, AmlError>;
fn read_u32(&self, region: &OpRegion) -> Result<u32, AmlError>;
fn read_u64(&self, region: &OpRegion) -> Result<u64, AmlError>;
impl<A: core::alloc::Allocator + Clone> core::fmt::Debug for OpRegion<A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("OpRegion")
.field("space", &self.space)
.field("base", &self.base)
.field("length", &self.length)
.field("parent_device_path", &self.parent_device_path)
.finish()
}
}

pub trait RegionHandler<A: Allocator + Clone = Global> {
fn read_u8(&self, region: &OpRegion<A>) -> Result<u8, AmlError>;
fn read_u16(&self, region: &OpRegion<A>) -> Result<u16, AmlError>;
fn read_u32(&self, region: &OpRegion<A>) -> Result<u32, AmlError>;
fn read_u64(&self, region: &OpRegion<A>) -> Result<u64, AmlError>;

fn write_u8(&self, region: &OpRegion, value: u8) -> Result<(), AmlError>;
fn write_u16(&self, region: &OpRegion, value: u16) -> Result<(), AmlError>;
fn write_u32(&self, region: &OpRegion, value: u32) -> Result<(), AmlError>;
fn write_u64(&self, region: &OpRegion, value: u64) -> Result<(), AmlError>;
fn write_u8(&self, region: &OpRegion<A>, value: u8) -> Result<(), AmlError>;
fn write_u16(&self, region: &OpRegion<A>, value: u16) -> Result<(), AmlError>;
fn write_u32(&self, region: &OpRegion<A>, value: u32) -> Result<(), AmlError>;
fn write_u64(&self, region: &OpRegion<A>, value: u64) -> Result<(), AmlError>;
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
Expand Down
20 changes: 12 additions & 8 deletions src/aml/pci_routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::aml::{
resource::{self, InterruptPolarity, InterruptTrigger, Resource},
};
use alloc::{vec, vec::Vec};
use core::alloc::Allocator;
use bit_field::BitField;
use core::str::FromStr;

pub use crate::aml::resource::IrqDescriptor;

Expand Down Expand Up @@ -58,13 +58,13 @@ impl PciRoutingTable {
/// `AmlError::InvalidOperationOnObject` if the value passed is not a package, or if any of the
/// values within it are not packages. Returns the various `AmlError::Prt*` errors if the
/// internal structure of the entries is invalid.
pub fn from_prt_path(
prt_path: AmlName,
interpreter: &Interpreter<impl Handler>,
pub fn from_prt_path<A: Allocator + Clone>(
prt_path: AmlName<A>,
interpreter: &Interpreter<impl Handler, A>,
) -> Result<PciRoutingTable, AmlError> {
let mut entries = Vec::new();

let prt = interpreter.evaluate(prt_path.clone(), vec![])?;
let prt = interpreter.evaluate(prt_path.clone(), Vec::new_in(interpreter.alloc.clone()))?;

if let Object::Package(ref inner_values) = *prt {
for value in inner_values {
Expand Down Expand Up @@ -117,10 +117,14 @@ impl PciRoutingTable {
* so search from the scope the name appeared in, rather than resolving it.
*/
Object::NamePath { ref name, ref scope } => {
Some(interpreter.namespace.lock().search_for_level(name, scope)?)
Some(interpreter.namespace.lock().search_for_level(name, scope)?.to_global())
}
Object::String(ref name) => Some(
interpreter.namespace.lock().search_for_level(&AmlName::from_str(name)?, &prt_path)?,
interpreter
.namespace
.lock()
.search_for_level(&AmlName::parse_in(name.as_str(), interpreter.alloc.clone())?, &prt_path)?
.to_global(),
),
_ => None,
};
Expand Down Expand Up @@ -194,7 +198,7 @@ impl PciRoutingTable {
irq: gsi,
}),
PciRouteType::LinkObject(ref name) => {
let path = AmlName::from_str("_CRS").unwrap().resolve(name)?;
let path = AmlName::parse_in("_CRS", interpreter.alloc.clone()).unwrap().resolve(name)?;
let link_crs = interpreter.evaluate(path, vec![])?;

let resources = resource::resource_descriptor_list(link_crs)?;
Expand Down
166 changes: 166 additions & 0 deletions src/aml/string.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a new type instead of using string_alloc?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did mention it didn't ya... my gold fish memory forgot about it 😅

That being said though, I was in a 'crate scare' for the past few weeks where https://rustsec.org/advisories/RUSTSEC-2026-0260 almost made it into a project through very legitimate crate dependencies (saved by the lock file, otherwise it'd have ran the build).

Nonetheless string_alloc seems to be a better option indeed. Is there a specific version you'd prefer we pin?

I wonder when nightly's native string will finally become allocator backed, that would be the best.

Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//! Allocator-aware string storage for AML objects.
//!
//! `alloc::string::String` is not parameterised over an allocator - there is no
//! `String<A>` and no `String::new_in` - so an allocator-aware [`Object`] cannot
//! store its strings in one. [`AmlString`] is a thin newtype over `Vec<u8, A>`
//! that upholds a UTF-8 invariant and provides the subset of `String` the
//! interpreter actually uses.
//!
//! [`Object`]: super::object::Object

use alloc::{alloc::Global, vec::Vec};
use core::{alloc::Allocator, fmt};

/// An allocator-aware, UTF-8 string.
///
/// Every construction path either starts from a `&str` (already valid UTF-8) or
/// appends through [`push_str`](Self::push_str) / [`push`](Self::push), so the
/// invariant holds by construction. [`as_bytes_mut`](Self::as_bytes_mut) is the
/// only way to break it, and is `unsafe` for that reason.
pub struct AmlString<A: Allocator + Clone = Global>(Vec<u8, A>);

impl<A: Allocator + Clone> AmlString<A> {
pub fn new_in(alloc: A) -> Self {
Self(Vec::new_in(alloc))
}

pub fn from_str_in(s: &str, alloc: A) -> Self {
let mut bytes = Vec::with_capacity_in(s.len(), alloc);
bytes.extend_from_slice(s.as_bytes());
Self(bytes)
}

/// Build a string from bytes that are not necessarily valid UTF-8, replacing
/// each invalid sequence with `U+FFFD`.
///
/// This is the allocator-aware counterpart of `String::from_utf8_lossy`,
/// which would otherwise allocate through `Global`.
pub fn from_utf8_lossy_in(bytes: &[u8], alloc: A) -> Self {
let mut string = Self(Vec::with_capacity_in(bytes.len(), alloc));
let mut rest = bytes;

while !rest.is_empty() {
match core::str::from_utf8(rest) {
Ok(valid) => {
string.push_str(valid);
break;
}
Err(error) => {
let (valid, after) = rest.split_at(error.valid_up_to());
// SAFETY: `valid_up_to` is by definition the length of the
// longest valid UTF-8 prefix of `rest`.
string.push_str(unsafe { core::str::from_utf8_unchecked(valid) });
string.push(char::REPLACEMENT_CHARACTER);

match error.error_len() {
// An invalid sequence of `len` bytes: skip past it.
Some(len) => rest = &after[len..],
// An unexpected end of input: nothing valid remains.
None => break,
}
}
}
}

string
}

#[inline]
pub fn as_str(&self) -> &str {
// SAFETY: the UTF-8 invariant is maintained by every safe constructor
// and mutator on this type.
unsafe { core::str::from_utf8_unchecked(&self.0) }
}

#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}

/// # Safety
/// The caller must leave the returned slice as valid UTF-8. Breaking that
/// makes subsequent [`as_str`](Self::as_str) calls unsound.
#[inline]
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
self.0.as_mut_slice()
}

pub fn push_str(&mut self, s: &str) {
self.0.extend_from_slice(s.as_bytes());
}

pub fn push(&mut self, c: char) {
let mut buf = [0u8; 4];
self.0.extend_from_slice(c.encode_utf8(&mut buf).as_bytes());
}

pub fn clear(&mut self) {
self.0.clear();
}

#[inline]
pub fn len(&self) -> usize {
self.0.len()
}

#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn parse<F: core::str::FromStr>(&self) -> Result<F, F::Err> {
self.as_str().parse::<F>()
}
}

impl<A: Allocator + Clone> Clone for AmlString<A> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

/*
* These are written out rather than derived because `derive` would bound
* `A: PartialEq`, while `Vec`'s own comparison works across differing
* allocators. Comparing two `AmlString`s with different allocators is
* meaningful, so the impl is generic over both.
*/
impl<A: Allocator + Clone, A2: Allocator + Clone> PartialEq<AmlString<A2>> for AmlString<A> {
fn eq(&self, other: &AmlString<A2>) -> bool {
self.as_bytes() == other.as_bytes()
}
}

impl<A: Allocator + Clone> Eq for AmlString<A> {}

impl<A: Allocator + Clone> PartialEq<str> for AmlString<A> {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}

impl<A: Allocator + Clone> PartialEq<&str> for AmlString<A> {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}

impl<A: Allocator + Clone> fmt::Display for AmlString<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}

impl<A: Allocator + Clone> fmt::Debug for AmlString<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}

/// Lets `write!(string, "...")` append without routing through `Global`.
impl<A: Allocator + Clone> fmt::Write for AmlString<A> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s);
Ok(())
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

#![no_std]
#![feature(allocator_api)]
#![feature(btreemap_alloc)]

#[cfg_attr(test, macro_use)]
#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion tools/aml_test_tools/src/handlers/logging_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ where
self.next_handler.release(mutex);
}

fn handle_debug(&self, object: &Object) {
fn handle_debug(&self, object: &Object<std::alloc::Global>) {
info!("Debug store: {}", object);
self.next_handler.handle_debug(object);
}
Expand Down
Loading