Skip to content

Commit

Permalink
stage0: add a wrapper for unaligned 64bit address (project-oak#4580)
Browse files Browse the repository at this point in the history
Entry addresses of XSDT are placed from offset 0x24, and an entry
address has 8 bytes. If the XSDT table header is 4-byte aligned, there
is only 50% chance that the entry addresses are 8-byte aligned.

In debug mode, it is observed that Xsdt::entries() function causes the
following panic:

```
stage0 ERROR: panicked at /path/to/.rustup/toolchains/
nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/
library/core/src/panicking.rs:155:5 :
unsafe precondition(s) violated: slice::from_raw_parts requires the
pointer to be aligned and non-null, and the total size of the slice
not to exceed `isize::MAX`
```

This patch adds a pointer wrapper to handle the alignment issue.
This wrapper itself is 8 bytes but only 1-byte aligned.
Trait Deref is implemented for this wrapper.

Signed-off-by: Changyuan Lyu <[email protected]>
  • Loading branch information
Lencerf authored and tiziano88 committed Jan 9, 2024
1 parent c8b67df commit fef1cae
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 10 deletions.
2 changes: 1 addition & 1 deletion FORCE_CI
Original file line number Diff line number Diff line change
@@ -1 +1 @@
25
26
47 changes: 38 additions & 9 deletions stage0/src/acpi_tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

use crate::acpi::{EBDA, EBDA_SIZE};
use bitflags::bitflags;
use core::{mem::size_of, slice};
use core::{marker::PhantomData, mem::size_of, ops::Deref, slice};
use x86_64::VirtAddr;
use zerocopy::{AsBytes, FromBytes, FromZeroes};

Expand Down Expand Up @@ -259,6 +259,33 @@ impl Rsdt {
}
}

/// A wrapper for entry addresses in XSDT table.
///
/// An entry address in XSDT has 8 bytes, but they are placed from table offset
/// 36 and not 8-byte aligned. This wrapper handles the unaligned access.
#[repr(C)]
pub struct XsdtEntryPtr<'a> {
addr: [u8; 8],
_phantom: PhantomData<&'a DescriptionHeader>,
}

impl<'a> XsdtEntryPtr<'a> {
pub fn raw_val(&self) -> u64 {
// As per Section 5.2 in the ACPI specification 6.5,
// Address is little endian.
u64::from_le_bytes(self.addr)
}
}

impl<'a> Deref for XsdtEntryPtr<'a> {
type Target = DescriptionHeader;

fn deref(&self) -> &DescriptionHeader {
// Safety: the address has been validated in Xsdt::validate().
unsafe { &*(self.raw_val() as *const DescriptionHeader) }
}
}

/// Extended System Description Table.
///
/// See Section 5.2.8 in the ACPI specification for more details.
Expand Down Expand Up @@ -293,32 +320,34 @@ impl Xsdt {

// Safety: we're never dereferencing the pointer.
let ebda_base = unsafe { EBDA.as_ptr() } as usize;
for &entry in self.entries() {
let ptr = entry as *const _ as usize;
for entry in self.entries() {
let ptr = entry.raw_val() as usize;
if !(ebda_base..ebda_base + EBDA_SIZE).contains(&ptr) {
return Err("XSDT invalid: entry points outside EBDA");
}
}
Ok(())
}

pub fn entries(&self) -> &[&'static DescriptionHeader] {
pub fn entries(&self) -> &[XsdtEntryPtr] {
let entries_base = self as *const _ as usize + size_of::<DescriptionHeader>();
// Safety: we've validated that the address and length makes sense in `validate()`.
// XsdtEntryPtr is 1-byte aligned.
unsafe {
slice::from_raw_parts(
entries_base as *const &DescriptionHeader,
(self.header.length as usize - size_of::<DescriptionHeader>()) / size_of::<usize>(),
entries_base as *const XsdtEntryPtr,
(self.header.length as usize - size_of::<DescriptionHeader>())
/ size_of::<XsdtEntryPtr>(),
)
}
}

/// Finds a table based on the signature, if it is present.
pub fn get(&self, table: &[u8; 4]) -> Option<&'static DescriptionHeader> {
pub fn get(&self, table: &[u8; 4]) -> Option<&DescriptionHeader> {
self.entries()
.iter()
.find(|&&entry| entry.signature == *table)
.copied()
.find(|entry| entry.signature == *table)
.map(|p| &**p)
}
}

Expand Down

0 comments on commit fef1cae

Please sign in to comment.