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
26 changes: 0 additions & 26 deletions src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,32 +218,6 @@ where
}
}
}

/// Ignores the GAS access size and does a 16-bit read.
pub fn read_u16(&self, byte_offset: u64) -> u16 {
match self.gas.address_space {
AddressSpace::SystemMemory => unsafe {
let addr = self.mapping.as_ref().unwrap().as_mut_ptr().cast::<u16>();
addr.byte_offset(byte_offset as isize).read_unaligned()
},
AddressSpace::SystemIo => self.handler.read_io_u16(self.gas.address as u16 + byte_offset as u16),
address_space => todo!("{address_space:?}"),
}
}

/// Ignores the GAS access size and does a 16-bit write.
pub fn write_u16(&self, byte_offset: u64, value: u16) {
match self.gas.address_space {
AddressSpace::SystemMemory => unsafe {
let addr = self.mapping.as_ref().unwrap().as_mut_ptr().cast::<u16>();
addr.byte_offset(byte_offset as isize).write_unaligned(value)
},
AddressSpace::SystemIo => {
self.handler.write_io_u16(self.gas.address as u16 + byte_offset as u16, value)
}
address_space => todo!("{address_space:?}"),
}
}
}

/// Returns the access size that should be made for a given `GenericAddress`, in bits.
Expand Down
7 changes: 4 additions & 3 deletions src/aml/interrupt_model_used.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ use crate::{
#[derive(Debug, Clone, Copy)]
pub enum InterruptModelUsed {
/// 0 - PIC mode
PicMode,
PicMode = 0,
/// 1 - APIC mode
ApicMode,
ApicMode = 1,
/// 2 - SAPIC mode
SapicMode,
SapicMode = 2,
// NOTE: 3 is reserved
/// 4 – GIC model
GicModel = 4,
/// 5 – LPIC model
Expand Down
2 changes: 1 addition & 1 deletion src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl<H: Handler, A: Allocator + Clone> AcpiPlatform<H, A> {
/*
* Disable all fixed event interrupts to start, including the global flag.
*/
self.registers.pm1_event_registers.set_enable_flags(Pm1EventFlags::empty());
self.registers.pm1_event_registers.set_enable_flags(Pm1EventFlags::empty())?;

// TODO: deal with GPEs

Expand Down
59 changes: 38 additions & 21 deletions src/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,26 @@ where
{
pub fn new(fadt: &Fadt, handler: H) -> Result<FixedRegisters<H>, AcpiError> {
let pm1_event_registers = {
let pm1a = unsafe { MappedGas::map_gas(fadt.pm1a_event_block()?, handler.clone())? };
let pm1b = match fadt.pm1b_event_block()? {
Some(gas) => Some(unsafe { MappedGas::map_gas(gas, handler.clone())? }),
None => None,
let (pm1a_sts, pm1a_en) = {
let (sts, en) = fadt.pm1a_event_block()?;
unsafe { (MappedGas::map_gas(sts, handler.clone())?, MappedGas::map_gas(en, handler.clone())?) }
};
let (pm1b_sts, pm1b_en) = match fadt.pm1b_event_block()? {
Some((sts, en)) => unsafe {
(
Some(MappedGas::map_gas(sts, handler.clone())?),
Some(MappedGas::map_gas(en, handler.clone())?),
)
},
None => (None, None),
};
Pm1EventRegisterBlock { pm1_event_length: fadt.pm1_event_length as usize, pm1a, pm1b }
Pm1EventRegisterBlock {
pm1_event_length: fadt.pm1_event_length as usize,
pm1a_sts,
pm1a_en,
pm1b_sts,
pm1b_en,
}
};
let pm1_control_registers = {
let pm1a = unsafe { MappedGas::map_gas(fadt.pm1a_control_block()?, handler.clone())? };
Expand All @@ -43,8 +57,10 @@ where
/// control whether an event should fire.
pub struct Pm1EventRegisterBlock<H: Handler> {
pub pm1_event_length: usize,
pub pm1a: MappedGas<H>,
pub pm1b: Option<MappedGas<H>>,
pub pm1a_sts: MappedGas<H>,
pub pm1a_en: MappedGas<H>,
pub pm1b_sts: Option<MappedGas<H>>,
pub pm1b_en: Option<MappedGas<H>>,
}

bitflags! {
Expand All @@ -70,32 +86,33 @@ where
/// Note that global enable (GBL_EN) must be set for any SCI interrupts to be fired.
/// For most flags you set to enable and clear to disable.
/// But for PCIe wake the flag is set to disable and clear to enable.
pub fn set_enable_flags(&self, events: Pm1EventFlags) {
let enable_offset = self.pm1_event_length as u64 / 2;
pub fn set_enable_flags(&self, events: Pm1EventFlags) -> Result<(), AcpiError> {
let bits = events.known_bits();
self.pm1a.write_u16(enable_offset, bits);
if let Some(pm1b) = &self.pm1b {
pm1b.write_u16(enable_offset, bits);
self.pm1a_en.write(bits as u64)?;
if let Some(pm1b_en) = &self.pm1b_en {
pm1b_en.write(bits as u64)?;
}
Ok(())
}

pub fn pending_events(&self) -> Pm1EventFlags {
pub fn pending_events(&self) -> Result<Pm1EventFlags, AcpiError> {
let bits = {
let mut bits = self.pm1a.read_u16(0);
if let Some(pm1b) = &self.pm1b {
bits |= pm1b.read_u16(0);
let mut bits = self.pm1a_sts.read()?;
if let Some(pm1b_sts) = &self.pm1b_sts {
bits |= pm1b_sts.read()?;
}
bits
};
Pm1EventFlags::from_bits_retain(bits)
Ok(Pm1EventFlags::from_bits_retain(bits as u16))
}

pub fn clear_events(&self, events: Pm1EventFlags) {
pub fn clear_events(&self, events: Pm1EventFlags) -> Result<(), AcpiError> {
let bits = events.known_bits();
self.pm1a.write_u16(0, bits);
if let Some(pm1b) = &self.pm1b {
pm1b.write_u16(0, bits);
self.pm1a_sts.write(bits as u64)?;
if let Some(pm1b_sts) = &self.pm1b_sts {
pm1b_sts.write(bits as u64)?;
}
Ok(())
}
}

Expand Down
171 changes: 131 additions & 40 deletions src/sdt/fadt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,39 +169,88 @@ impl Fadt {
}
}

pub fn pm1a_event_block(&self) -> Result<GenericAddress, AcpiError> {
/// Construct two `GenericAddress`s for the two registers within the PM1a event block -
/// `PM1a_STS` and `PM1a_EN`. Each register consists of half of the overall event register
/// block.
pub fn pm1a_event_block(&self) -> Result<(GenericAddress, GenericAddress), AcpiError> {
if let Some(raw) = unsafe { self.x_pm1a_event_block.access(self.header().revision) }
&& raw.address != 0x0
{
return GenericAddress::from_raw(raw);
let block = GenericAddress::from_raw(raw)?;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: block.bit_width / 2,

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.

Is there a minimum width specified in the spec? (I can't see one...) What happens if the width ends up less than the size of PM1a_STS? (Also the same question for the other registers)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think yes - it's mentioned in §4.8.1.1 of the 6.6 spec that "The PM1 event register block has a minimum size of 4 bytes", so each register will have a minimum size of the 16 currently-defined bits.

We could add some extra checks around this (?assume block is 4 bytes if size seems incorrect etc.) to deal with especially broken FADTs but could wait for a broken firmware to demonstrate that's an issue?

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.

it's mentioned in §4.8.1.1 of the 6.6 spec that "The PM1 event register block has a minimum size of 4 bytes"

Good spot! Think I saw that and read it as "minimum size of 4 bits" 🤦

I think waiting for a broken firmware is fair - at some point you have to trust the firmware isn't completely broken!

bit_offset: 0,
access_size: 0,
address: block.address,
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: block.address + (block.bit_width / 2 / 8) as u64,
};
Ok((sts, en))
} else {

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.

Nice bit of extra compatibility, like it 😄

// Calculate individual register width **in bytes** - half the block is STS, half EN
let reg_width = self.pm1_event_length / 2;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.pm1a_event_block),
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.pm1a_event_block) + reg_width as u64,
};
Ok((sts, en))
}

Ok(GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: self.pm1_event_length * 8,
bit_offset: 0,
access_size: 0,
address: self.pm1a_event_block.into(),
})
}

pub fn pm1b_event_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
pub fn pm1b_event_block(&self) -> Result<Option<(GenericAddress, GenericAddress)>, AcpiError> {
if let Some(raw) = unsafe { self.x_pm1b_event_block.access(self.header().revision) }
&& raw.address != 0x0
{
return Ok(Some(GenericAddress::from_raw(raw)?));
}

if self.pm1b_event_block != 0 {
Ok(Some(GenericAddress {
let block = GenericAddress::from_raw(raw)?;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: self.pm1_event_length * 8,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: self.pm1b_event_block.into(),
}))
address: block.address,
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: block.address + (block.bit_width / 2 / 8) as u64,
};
Ok(Some((sts, en)))
} else {
Ok(None)
// Calculate individual register width **in bytes** - half the block is STS, half EN
let reg_width = self.pm1_event_length / 2;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.pm1b_event_block),
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.pm1b_event_block) + reg_width as u64,
};
Ok(Some((sts, en)))
}
}

Expand Down Expand Up @@ -288,43 +337,85 @@ impl Fadt {
}
}

pub fn gpe0_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
pub fn gpe0_block(&self) -> Result<Option<(GenericAddress, GenericAddress)>, AcpiError> {
if let Some(raw) = unsafe { self.x_gpe0_block.access(self.header().revision) }
&& raw.address != 0x0
{
return Ok(Some(GenericAddress::from_raw(raw)?));
}

if self.gpe0_block != 0 {
Ok(Some(GenericAddress {
let block = GenericAddress::from_raw(raw)?;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: self.gpe0_block_length * 8,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: self.gpe0_block.into(),
}))
address: block.address,
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: block.address + (block.bit_width / 2 / 8) as u64,
};
Ok(Some((sts, en)))
} else {
Ok(None)
// Calculate individual register width **in bytes** - half the block is STS, half EN
let reg_width = self.gpe0_block_length / 2;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.gpe0_block),
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.gpe0_block) + reg_width as u64,
};
Ok(Some((sts, en)))
}
}

pub fn gpe1_block(&self) -> Result<Option<GenericAddress>, AcpiError> {
pub fn gpe1_block(&self) -> Result<Option<(GenericAddress, GenericAddress)>, AcpiError> {
if let Some(raw) = unsafe { self.x_gpe1_block.access(self.header().revision) }
&& raw.address != 0x0
{
return Ok(Some(GenericAddress::from_raw(raw)?));
}

if self.gpe1_block != 0 {
Ok(Some(GenericAddress {
let block = GenericAddress::from_raw(raw)?;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: self.gpe1_block_length * 8,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: self.gpe1_block.into(),
}))
address: block.address,
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: block.bit_width / 2,
bit_offset: 0,
access_size: 0,
address: block.address + (block.bit_width / 2 / 8) as u64,
};
Ok(Some((sts, en)))
} else {
Ok(None)
// Calculate individual register width **in bytes** - half the block is STS, half EN
let reg_width = self.gpe1_block_length / 2;
let sts = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.gpe1_block),
};
let en = GenericAddress {
address_space: AddressSpace::SystemIo,
bit_width: reg_width * 8,
bit_offset: 0,
access_size: 0,
address: u64::from(self.gpe1_block) + reg_width as u64,
};
Ok(Some((sts, en)))
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/sdt/madt.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
//! The Multiple APIC Description Table (MADT) is a static table that describes the platform's
//! interrupt configuration. Despite the name, it supports description of interrupt models for
//! different platforms:
//! - The PC-AT dual i8259 interrupt controller
//! - The Advanced Programmable Interrupt Controller (APIC)
//! - The Intel Streamlined Advanced Interrupt Controller (SAPIC)
//! - The Generic Interrupt Controller (GIC) on ARM
//! - The LoongArch Programmable Interrupt Controller (LPIC) on LoongArch
//! - The RISC-V Interrupt Controller (RINTC)
//!
//! Please note that support for the various interrupt controller descriptions are in different
//! stages in this crate.
//!
//! The MADT is a variably-sized structure that contains a header, followed by a number of entries.
//! It is therefore `!Unpin` - you can enumerate the entries like so:
//! ```rust,ignore
//! let madt = acpi_tables.find_table::<Madt>().unwrap();
//! for entry in madt.get().entries() {
//! ...
//! }
//! ```

use crate::{
AcpiError,
AcpiTable,
Expand Down
Loading
Loading