Skip to content

Commit 8ccdc28

Browse files
committed
Initial handling of the global lock from AML interpreter
This requires the AML interpreter to have access to the fixed hardware registers, and so changes the convenience initialization method to take a `AcpiPlatform` instead of just the tables. I think this is fine in the long run - users can still manually construct an interpreter if needed.
1 parent 528e727 commit 8ccdc28

File tree

1 file changed

+113
-10
lines changed

1 file changed

+113
-10
lines changed

src/aml/mod.rs

Lines changed: 113 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,19 @@ pub mod resource;
2020

2121
use crate::{
2222
AcpiError,
23-
AcpiTables,
2423
AmlTable,
2524
Handle,
2625
Handler,
2726
PhysicalMapping,
27+
platform::AcpiPlatform,
28+
registers::{FixedRegisters, Pm1ControlBit},
2829
sdt::{SdtHeader, facs::Facs, fadt::Fadt},
2930
};
3031
use alloc::{
3132
boxed::Box,
3233
collections::btree_map::BTreeMap,
3334
string::{String, ToString},
35+
sync::Arc,
3436
vec,
3537
vec::Vec,
3638
};
@@ -70,6 +72,7 @@ where
7072
region_handlers: Spinlock<BTreeMap<RegionSpace, Box<dyn RegionHandler>>>,
7173

7274
global_lock_mutex: Handle,
75+
registers: Arc<FixedRegisters<H>>,
7376
facs: PhysicalMapping<H, Facs>,
7477
}
7578

@@ -85,7 +88,12 @@ where
8588
{
8689
/// Construct a new `Interpreter`. This does not load any tables - if you have an `AcpiTables`
8790
/// already, use [`Interpreter::new_from_tables`] instead.
88-
pub fn new(handler: H, dsdt_revision: u8, facs: PhysicalMapping<H, Facs>) -> Interpreter<H> {
91+
pub fn new(
92+
handler: H,
93+
dsdt_revision: u8,
94+
registers: Arc<FixedRegisters<H>>,
95+
facs: PhysicalMapping<H, Facs>,
96+
) -> Interpreter<H> {
8997
info!("Initializing AML interpreter v{}", env!("CARGO_PKG_VERSION"));
9098

9199
let global_lock_mutex = handler.create_mutex();
@@ -98,13 +106,14 @@ where
98106
dsdt_revision,
99107
region_handlers: Spinlock::new(BTreeMap::new()),
100108
global_lock_mutex,
109+
registers,
101110
facs,
102111
}
103112
}
104113

105114
/// Construct a new `Interpreter` with the given set of ACPI tables. This will automatically
106115
/// load the DSDT and any SSDTs in the supplied [`AcpiTables`].
107-
pub fn new_from_tables(handler: H, tables: &AcpiTables<H>) -> Result<Interpreter<H>, AcpiError> {
116+
pub fn new_from_platform(platform: &AcpiPlatform<H>) -> Result<Interpreter<H>, AcpiError> {
108117
fn load_table(interpreter: &Interpreter<impl Handler>, table: AmlTable) -> Result<(), AcpiError> {
109118
let mapping = unsafe {
110119
interpreter.handler.map_physical_region::<SdtHeader>(table.phys_address, table.length as usize)
@@ -119,16 +128,17 @@ where
119128
Ok(())
120129
}
121130

131+
let registers = platform.registers.clone();
122132
let facs = {
123-
let fadt = tables.find_table::<Fadt>().unwrap();
124-
unsafe { handler.map_physical_region(fadt.facs_address()?, mem::size_of::<Facs>()) }
133+
let fadt = platform.tables.find_table::<Fadt>().unwrap();
134+
unsafe { platform.handler.map_physical_region(fadt.facs_address()?, mem::size_of::<Facs>()) }
125135
};
126136

127-
let dsdt = tables.dsdt()?;
128-
let interpreter = Interpreter::new(handler, dsdt.revision, facs);
137+
let dsdt = platform.tables.dsdt()?;
138+
let interpreter = Interpreter::new(platform.handler.clone(), dsdt.revision, registers, facs);
129139
load_table(&interpreter, dsdt)?;
130140

131-
for ssdt in tables.ssdts() {
141+
for ssdt in platform.tables.ssdts() {
132142
load_table(&interpreter, ssdt)?;
133143
}
134144

@@ -267,6 +277,88 @@ where
267277
info!("Initialized {} devices", num_devices_initialized);
268278
}
269279

280+
pub fn acquire_global_lock(&self, timeout: u16) -> Result<(), AmlError> {
281+
self.handler.acquire(self.global_lock_mutex, timeout)?;
282+
283+
// Now we've acquired the AML-side mutex, acquire the hardware side
284+
// TODO: count the number of times we have to go round this loop / enforce a timeout?
285+
loop {
286+
if self.try_do_acquire_firmware_lock() {
287+
break Ok(());
288+
} else {
289+
/*
290+
* The lock is owned by the firmware. We have set the pending bit - we now need to
291+
* wait for the firmware to signal it has released the lock.
292+
*
293+
* TODO: this should wait for an interrupt from the firmware. That needs more infra
294+
* so for now let's just spin round and try and acquire it again...
295+
*/
296+
self.handler.release(self.global_lock_mutex);
297+
continue;
298+
}
299+
}
300+
}
301+
302+
/// Attempt to acquire the firmware lock, setting the owned bit if the lock is free. If the
303+
/// lock is not free, sets the pending bit to instruct the firmware to alert us when we can
304+
/// attempt to take ownership of the lock again. Returns `true` if we now have ownership of the
305+
/// lock, and `false` if we need to wait for firmware to release it.
306+
fn try_do_acquire_firmware_lock(&self) -> bool {
307+
loop {
308+
let global_lock = self.facs.global_lock.load(Ordering::Relaxed);
309+
let is_owned = global_lock.get_bit(1);
310+
311+
/*
312+
* Compute the new value: either the lock is already owned, and we need to set the
313+
* pending bit and wait, or we can acquire ownership of the lock now. Either way, we
314+
* unconditionally set the owned bit and set the pending bit if the lock is already
315+
* owned.
316+
*/
317+
let mut new_value = global_lock;
318+
new_value.set_bit(0, is_owned);
319+
new_value.set_bit(1, true);
320+
321+
if self
322+
.facs
323+
.global_lock
324+
.compare_exchange(global_lock, new_value, Ordering::AcqRel, Ordering::Acquire)
325+
.is_ok()
326+
{
327+
break !is_owned;
328+
}
329+
}
330+
}
331+
332+
pub fn release_global_lock(&self) -> Result<(), AmlError> {
333+
let is_pending = self.do_release_firmware_lock();
334+
if is_pending {
335+
self.registers.pm1_control_registers.set_bit(Pm1ControlBit::GlobalLockRelease, true).unwrap();
336+
}
337+
Ok(())
338+
}
339+
340+
/// Atomically release the owned and pending bits of the global lock. Returns whether the
341+
/// pending bit was set (this means the firmware is waiting to acquire the lock, and should be
342+
/// informed we're finished with it).
343+
fn do_release_firmware_lock(&self) -> bool {
344+
loop {
345+
let global_lock = self.facs.global_lock.load(Ordering::Relaxed);
346+
let is_pending = global_lock.get_bit(0);
347+
let mut new_value = global_lock;
348+
new_value.set_bit(0, false);
349+
new_value.set_bit(1, false);
350+
351+
if self
352+
.facs
353+
.global_lock
354+
.compare_exchange(global_lock, new_value, Ordering::AcqRel, Ordering::Acquire)
355+
.is_ok()
356+
{
357+
break is_pending;
358+
}
359+
}
360+
}
361+
270362
fn do_execute_method(&self, mut context: MethodContext) -> Result<WrappedObject, AmlError> {
271363
/*
272364
* This is the main loop that executes operations. Every op is handled at the top-level of
@@ -630,16 +722,27 @@ where
630722
let timeout = context.next_u16()?;
631723

632724
// TODO: should we do something with the sync level??
633-
self.handler.acquire(mutex, timeout)?;
725+
if mutex == self.global_lock_mutex {
726+
self.acquire_global_lock(timeout)?;
727+
} else {
728+
self.handler.acquire(mutex, timeout)?;
729+
}
730+
634731
context.retire_op(op);
635732
}
636733
Opcode::Release => {
637734
let [Argument::Object(mutex)] = &op.arguments[..] else { panic!() };
638735
let Object::Mutex { mutex, sync_level } = **mutex else {
639736
Err(AmlError::InvalidOperationOnObject { op: Operation::Release, typ: mutex.typ() })?
640737
};
738+
641739
// TODO: should we do something with the sync level??
642-
self.handler.release(mutex);
740+
if mutex == self.global_lock_mutex {
741+
self.release_global_lock()?;
742+
} else {
743+
self.handler.release(mutex);
744+
}
745+
643746
context.retire_op(op);
644747
}
645748
Opcode::InternalMethodCall => {

0 commit comments

Comments
 (0)