Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -220,18 +220,40 @@ otherwise pay to find again:
map leaf of 104 bytes for its entry in its parent and the arena at its
doubled capacity: an interpreter filled with field units refuses with
11,111,460 bytes of heap held, and one filled with devices of one child
with 8,900,612. The real machine's tables have not been loaded since the
meter came to count a node's slot, entry and record, a wide field's read
came to hold its buffer and the arena's move to cost steps: that reading
is owed before the server links the crate, and with it the slots the
arena has free after the last table, since a method that defines more
names than that moves the arena each time it runs, at a step for every
64 bytes of it. Owner: the power-off stage, which gives the server its
memory. **Exit**: the server states its interpreter's bound in heap
bytes, and a host test under a counting allocator holds the most a load
and an evaluation hold while they run to it; and the real machine's
tables load under the meter as it stands, the arena's free slots after
the last read with them.
with 8,900,612. The real machine's tables, loaded outside the tree under
the meter as it stands, with the one word of memory answered and every
function answering as Linux reads it: all 14 load in 39,419 steps, 32,946 of them the
DSDT's, and leave the meter at 2,412,886 bytes over 1,819,038 of heap by
a counting allocator, in 6,891 nodes. Nothing reads the arena's
capacity: by its doubling it is 8,192 slots, 1,301 of them free, and a
method that defines more names than are free moves the arena each time
it runs, at a step for every 64 bytes of it. Owner: the power-off stage,
which gives the server its memory. **Exit**: the server states its
interpreter's bound in heap bytes, and a host test under a counting
allocator holds the most a load and an evaluation hold while they run to
it; and the arena's capacity after the real machine's last table is
read, not derived.
- **The T14's tables are not in name order.** The interpreter's walk of
the namespace (`Interpreter::walk`, `userland/acpiserver/aml/src/lib.rs`)
reads siblings as their tables declared them. Of the 267 objects of the
T14's tables that hold others, 217 declare them against the order their
names sort in. What its initialisation needs of the order was read only
so far: the tables define 45 `_INI`, all methods; a dry run that answered
every read zero but one word of memory and each function's header found
two of them writing anything, the PCI root bridge's and the embedded
controller's; and that controller's device is below that bridge, so
under those answers the two run parent before child whichever order
siblings take. What the 45 do under the machine's own answers, and
whether any hangs on a sibling's, nothing has read.
- **An evaluation may ask to wait ten seconds, and no caller names another
limit.** The limit counts the time asked of Sleep, Stall and Wait, a
load's too (`MAX_WAIT_US`, `userland/acpiserver/aml/src/lib.rs`), and
`Interpreter::usage` says what a call asked, the request that was refused
included. The T14's 14 tables asked for none while they loaded, and no
method of them has been read refused at it. Owner: this stage. **Exit**:
the slice that carries a reading of a method refused at ten seconds, on
the T14 and by `usage`, brings the limit its caller names with that
reading; until one does, ten seconds stands and nothing names more.

What the server's load of the tables, on the T14 and through the kernel's
mediated access, leaves open:
Expand Down
6 changes: 4 additions & 2 deletions userland/acpiserver/aml/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::object::{
Object, Ref, Slot, Unresolved, Width,
};
use crate::stream::{starts_name, Cursor};
use crate::{Error, Host, MAX_DEPTH, MAX_NESTING, MAX_STEPS, MAX_WAIT_US, REVISION, WINDOWS, WORK_PER_STEP};
use crate::{Error, Host, Usage, MAX_DEPTH, MAX_NESTING, MAX_STEPS, MAX_WAIT_US, REVISION, WINDOWS, WORK_PER_STEP};

pub(crate) struct Machine<'a> {
pub(crate) ns: &'a mut Namespace,
Expand Down Expand Up @@ -239,7 +239,9 @@ impl<'a> Machine<'a> {
/// Ends an evaluation: whatever it still holds is let go, and holding
/// anything at its end is itself a refusal (§19.6.88: "the top-level
/// control method cannot exit while still holding ownership of a Mutex").
pub(crate) fn finish<T>(mut self, r: Result<T, Error>) -> Result<T, Error> {
/// What it took goes to `took`, whichever way it ends.
pub(crate) fn finish<T>(mut self, r: Result<T, Error>, took: &mut Usage) -> Result<T, Error> {
(took.steps, took.waited_us) = (self.steps, self.waited_us);
let held = !self.held.is_empty();
for m in self.held.drain(..) {
m.held.set(0);
Expand Down
151 changes: 138 additions & 13 deletions userland/acpiserver/aml/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
//! what an interpreter holds of tables, namespace and objects is bounded in
//! sum, in bytes of heap ([`MAX_LIVE`]). A load refused leaves the namespace
//! without anything that table created, and the interpreter holding what it
//! held before.
//! held before. What the last load or evaluation took of those bounds is
//! [`Interpreter::usage`]'s, a refused one's too.
//!
//! [`Interpreter::walk`] reads the namespace an interpreter holds, and runs
//! nothing of it: which objects a caller then evaluates, and in which order,
//! is the caller's.
//!
//! The library touches no hardware. An operation region's field is read and
//! written through the [`Host`] the caller passes, in SystemMemory,
Expand Down Expand Up @@ -193,12 +198,105 @@ pub enum Value {
Reference(String),
}

/// What a load or an evaluation took of its bounds, a refused one as well.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Usage {
/// The steps it took: terms, arguments, loop iterations, field access
/// units, and the bytes it made, copied, compared or walked.
pub steps: u64,
/// The time it asked to Sleep, Stall and Wait, in µs, the request its
/// limit refused included.
pub waited_us: u64,
/// The heap the interpreter held at its end, of [`MAX_LIVE`].
pub live: usize,
}

/// What a named object is: the types of Table 19.36, a predefined scope,
/// which has none, and a reference a Name holds.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Uninitialized,
Integer,
String,
Buffer,
Package,
FieldUnit,
Device,
Event,
Method,
Mutex,
OperationRegion,
PowerResource,
/// The Processor object ACPI 6.4 deprecated (ACPI 6.3A §19.6.108).
Processor,
ThermalZone,
BufferField,
/// `\_SB` and the other scopes of §5.3.1: descended, and no device.
Scope,
Reference,
}

/// One object of a [`Walk`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Entry {
/// How many names its path has: 1 directly below the root. Everything
/// below an object follows it at a greater depth, so a caller leaves a
/// subtree out by skipping to the next entry no deeper than its top.
pub depth: u32,
pub name: [u8; 4],
pub kind: Kind,
}

/// The namespace read from the root down ([`Interpreter::walk`]). A table
/// chooses how deep it nests, and a path is as long as its depth: an entry
/// is as small at any depth, and is what a caller keeps of a whole
/// namespace, where the paths of one nested as deep as [`MAX_LIVE`] admits
/// are gigabytes.
pub struct Walk<'a>(namespace::Walk<'a>);

impl Walk<'_> {
/// The absolute path of the entry last returned, as
/// [`Interpreter::evaluate`] takes it.
pub fn path(&self) -> &str {
self.0.path()
}
}

impl Iterator for Walk<'_> {
type Item = Entry;

fn next(&mut self) -> Option<Entry> {
let (depth, seg, object) = self.0.step()?;
let kind = match object {
Object::Uninit => Kind::Uninitialized,
Object::Int(_) => Kind::Integer,
Object::Str(_) => Kind::String,
Object::Buf(_) => Kind::Buffer,
Object::Pkg(_) => Kind::Package,
Object::Field(_) => Kind::FieldUnit,
Object::BufField(_) => Kind::BufferField,
Object::Ref(_) | Object::Lazy(_) => Kind::Reference,
Object::Scope => Kind::Scope,
Object::Device => Kind::Device,
Object::Processor => Kind::Processor,
Object::ThermalZone => Kind::ThermalZone,
Object::PowerResource => Kind::PowerResource,
Object::Method(_) => Kind::Method,
Object::Mutex(_) => Kind::Mutex,
Object::Event(_) => Kind::Event,
Object::Region(_) => Kind::OperationRegion,
};
Some(Entry { depth, name: seg.0, kind })
}
}

/// One machine's namespace, and what loaded it.
pub struct Interpreter {
ns: Namespace,
meter: Rc<Meter>,
/// Set by the DSDT's revision (§19.6.29), for every table after it.
width: Option<Width>,
last: Usage,
}

impl Default for Interpreter {
Expand Down Expand Up @@ -228,12 +326,20 @@ impl Interpreter {
let os = meter.bytes(b"Microsoft Windows NT".to_vec()).expect(held);
put(b"_OS_", Object::Str(os));
put(b"_REV", Object::Int(2));
Interpreter { ns, meter, width: None }
let last = Usage { live: meter.live(), ..Usage::default() };
Interpreter { ns, meter, width: None, last }
}

/// Loads a DSDT or SSDT (§5.4.2): the DSDT first, then each SSDT. The
/// table's header, length and checksum are [`toyos_acpi::Table::open`]'s.
pub fn load<P: toyos_acpi::Phys>(&mut self, host: &mut dyn Host, table: &toyos_acpi::Table<P>) -> Result<(), Error> {
self.last = Usage::default();
let r = self.load_in(host, table);
self.last.live = self.meter.live();
r
}

fn load_in<P: toyos_acpi::Phys>(&mut self, host: &mut dyn Host, table: &toyos_acpi::Table<P>) -> Result<(), Error> {
let bytes: Vec<u8> = (0..table.len()).map_while(|i| table.byte(i)).collect();
let w = match (&bytes[..4], self.width) {
(b"DSDT", None) => Width { bits: if bytes[toyos_acpi::SDT_REVISION] < 2 { 32 } else { 64 } },
Expand All @@ -253,22 +359,25 @@ impl Interpreter {
exec::Flow::Next => Ok(()),
_ => Err(Error::Rule("a Return, Break or Continue at definition block level")),
});
let r = m.finish(r);
match r {
Ok(()) => {
self.width = Some(w);
Ok(())
}
Err(e) => {
self.ns.unwind(made);
Err(e)
}
let r = m.finish(r, &mut self.last);
if r.is_ok() {
self.width = Some(w);
} else {
self.ns.unwind(made);
}
r
}

/// Evaluates the object at an absolute path, written `\_SB.PCI0._STA`: a
/// method is invoked with `args`, anything else is its value.
pub fn evaluate(&mut self, host: &mut dyn Host, path: &str, args: &[Value]) -> Result<Value, Error> {
self.last = Usage::default();
let r = self.evaluate_in(host, path, args);
self.last.live = self.meter.live();
r
}

fn evaluate_in(&mut self, host: &mut dyn Host, path: &str, args: &[Value]) -> Result<Value, Error> {
let w = self.width.ok_or(Error::Table("nothing is loaded"))?;
let id = self.named(path)?;
let args = args.iter().map(|a| self.object_of(a, w, 0)).collect::<Result<Vec<_>, _>>()?;
Expand All @@ -278,7 +387,23 @@ impl Interpreter {
let r = m.evaluate(id, args).and_then(|o| value_of(&mut m, &meter, &mut handed, o, 0));
// The value is the caller's from here, and no longer this interpreter's.
meter.give(handed);
m.finish(r)
m.finish(r, &mut self.last)
}

/// What the last load or evaluation took, and what the interpreter held
/// at its end.
pub fn usage(&self) -> Usage {
self.last
}

/// Every object the loaded tables and this interpreter defined, read-only
/// and without evaluating any: each after the object it is in, siblings
/// in the order they were declared, a table's after those of the tables
/// loaded before it. An Alias is not among them; what it names is, where
/// it was defined. The walk is held against [`MAX_LIVE`] while it lasts,
/// and refused where that has no room for it.
pub fn walk(&self) -> Result<Walk<'_>, Error> {
self.ns.walk().map(Walk)
}

fn object_of(&self, v: &Value, w: Width, depth: usize) -> Result<Object, Error> {
Expand Down
104 changes: 104 additions & 0 deletions userland/acpiserver/aml/src/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
//! and a parent whose last child it took no map. The arena therefore moves
//! as often as a method that grows it runs, and a move pays the [`Toll`] for
//! the bytes it copies.
//!
//! The arena's order is the order its nodes were declared in, and a [`Walk`]
//! reads sibling order from it alone: a parent's map keeps its children by
//! name.

use alloc::collections::BTreeMap;
use alloc::rc::Rc;
Expand Down Expand Up @@ -241,3 +245,103 @@ impl Namespace {
Ok(crate::name::text(&Path { root: true, up: 0, segs }))
}
}

/// Every object below the root, each after its parent and siblings in the
/// order they were declared, an Alias left out: its source is met at its own
/// place, and an Alias of an ancestor would otherwise never end.
///
/// It is held against the meter while it lasts, at eight bytes a node and
/// five a level of the deepest: both are as many as the tables chose.
pub(crate) struct Walk<'a> {
ns: &'a Namespace,
/// A node's first child and its next sibling, by index; 0, the root's,
/// is none.
first: Vec<u32>,
next: Vec<u32>,
at: u32,
depth: u32,
path: String,
held: usize,
}

/// The bytes of a path `depth` names long, `\AAAA.BBBB`.
const fn path_len(depth: usize) -> usize {
5 * depth
}

impl Namespace {
pub(crate) fn walk(&self) -> Result<Walk<'_>, Error> {
let count = self.nodes.len();
let links = 2 * count * core::mem::size_of::<u32>();
self.meter.take(links)?;
let mut walk = Walk { ns: self, first: Vec::new(), next: Vec::new(), at: 0, depth: 1, path: String::new(), held: links };
for links in [&mut walk.first, &mut walk.next] {
links.reserve_exact(count);
links.resize(count, 0);
}
// A parent is older than its child, so its depth is known first:
// `next` holds each node's depth until the links are made.
let mut deepest = 0;
for (i, n) in self.nodes.iter().enumerate() {
if let Some(p) = n.parent {
walk.next[i] = walk.next[p as usize] + 1;
deepest = deepest.max(walk.next[i]);
}
}
let path = path_len(deepest as usize);
self.meter.take(path)?;
walk.held += path;
walk.path.reserve_exact(path);
// Newest first, each node goes to the front of its parent's list,
// which leaves the oldest there.
for (i, n) in self.nodes.iter().enumerate().rev() {
walk.next[i] = 0;
if let (Some(p), None) = (n.parent, n.alias) {
walk.next[i] = walk.first[p as usize];
walk.first[p as usize] = i as u32;
}
}
walk.at = walk.first[0];
Ok(walk)
}
}

impl Drop for Walk<'_> {
fn drop(&mut self) {
self.ns.meter.give(self.held);
}
}

impl Walk<'_> {
/// The next object's depth, a child of the root's being 1, its name and
/// the object.
pub(crate) fn step(&mut self) -> Option<(u32, Seg, &Object)> {
let i = self.at as usize;
if i == 0 {
return None;
}
let (n, depth) = (&self.ns.nodes[i], self.depth);
self.path.truncate(path_len(depth as usize - 1));
self.path.push(if depth == 1 { '\\' } else { '.' });
self.path.extend(n.seg.0.iter().map(|&c| char::from(c)));
if self.first[i] != 0 {
self.at = self.first[i];
self.depth += 1;
} else {
// The next sibling of this node or of the nearest ancestor that
// has one: each node is climbed from once in a whole walk.
let mut up = i;
while up != 0 && self.next[up] == 0 {
up = self.ns.nodes[up].parent.map_or(0, |p| p as usize);
self.depth -= 1;
}
self.at = self.next[up];
}
Some((depth, n.seg, &n.object))
}

/// The absolute path of the object last stepped to.
pub(crate) fn path(&self) -> &str {
&self.path
}
}
Loading
Loading