mirror of
https://github.com/rust-osdev/uefi-rs
synced 2026-08-26 18:26:05 -04:00
uefi-raw: make MemoryDescriptor layout portable across x86 targets
TL;DR: Add an explicit 32-bit padding field after `MemoryDescriptor::ty`
so UEFI memory maps can be parsed correctly from non-UEFI targets such
as kernels and bootloaders compiled for generic i686 targets.
UEFI's `EFI_MEMORY_DESCRIPTOR` places `PhysicalStart` at offset 8. This
happens implicitly on x86_64 and also on IA32 UEFI, where 64-bit values
are 8-byte aligned. EDK II enforces this for GCC IA32 builds with
`-malign-double` [0], and Rust's `i686-unknown-uefi` target explicitly
uses `i64:64` in its LLVM data layout [1].
This differs from the standard x86-32 ABI. GCC normally aligns
`long long` to one word on x86-32 and documents `-malign-double` as an
ABI-changing option [2]. Rust/LLVM follows the standard x86-32 layout
for targets such as `i686-unknown-linux-gnu`, where `u64` has 4-byte ABI
alignment [3]. Consequently, `#[repr(C)]` alone places `phys_start` at
offset 4 instead of the UEFI-required offset 8.
This matters outside UEFI applications. Kernels and bootloaders may
receive and parse a UEFI memory map while being compiled for a generic
target such as a bare-metal i686 target.
For example, the multiboot2 crate re-exports this type as
`EFIMemoryDesc` and uses it to parse EFI memory-map data [4]. The type
therefore needs to describe the UEFI binary layout independently of the
consumer's target ABI.
Reproducer without the explicit padding:
```rust
#[repr(C)]
pub struct MemoryDescriptor {
pub ty: u32,
pub phys_start: u64,
pub virt_start: u64,
pub page_count: u64,
pub att: u64,
}
const _: () = {
assert!(core::mem::offset_of!(MemoryDescriptor, phys_start) == 8);
};
```
```console
# works
cargo check --target x86_64-unknown-linux-gnu
# fails: phys_start is at offset 4
cargo check --target i686-unknown-linux-gnu
# both UEFI targets already have the required layout
cargo check --target x86_64-unknown-uefi
cargo check --target i686-unknown-uefi
```
The explicit padding makes the UEFI-defined field offsets independent
of whether the compilation target itself uses the UEFI ABI.
[0]: 2970e5699b/BaseTools/Conf/tools_def.template (L620)
[1]: 9ba81c26fe/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs (L96)
[2]: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
[3]: 9ba81c26fe/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs (L36)
[4]: 5ccb9629df/multiboot2/src/memory_map.rs (L5)
This commit is contained in:
parent
a30ecd2a52
commit
e72d1269c9
5 changed files with 40 additions and 1 deletions
|
|
@ -3,6 +3,8 @@
|
|||
## Added
|
||||
|
||||
## Changed
|
||||
- **Breaking**: `MemoryDescriptor` now has a new member to ensure correct
|
||||
layout on all non-UEFI 32-bit targets.
|
||||
|
||||
## Removed
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use crate::{
|
|||
};
|
||||
use bitflags::bitflags;
|
||||
use core::ffi::c_void;
|
||||
use core::mem::offset_of;
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
newtype_enum! {
|
||||
|
|
@ -360,10 +361,15 @@ bitflags! {
|
|||
/// [version]: MemoryDescriptor::VERSION
|
||||
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct MemoryDescriptor {
|
||||
/// Type of memory occupying this range.
|
||||
pub ty: MemoryType,
|
||||
/// Implicit padding that should be set to 0.
|
||||
///
|
||||
/// This field is required for a correct layout on non-UEFI targets to
|
||||
/// properly parse a memory map.
|
||||
pub padding: u32,
|
||||
// Implicit 32-bit padding.
|
||||
/// Starting physical address.
|
||||
pub phys_start: PhysicalAddress,
|
||||
|
|
@ -375,6 +381,18 @@ pub struct MemoryDescriptor {
|
|||
pub att: MemoryAttribute,
|
||||
}
|
||||
|
||||
// Ensure ABI guarantees for MemoryDescriptor.
|
||||
const _: () = {
|
||||
assert!(size_of::<MemoryDescriptor>() == 40);
|
||||
assert!(align_of::<MemoryDescriptor>() == 8);
|
||||
|
||||
assert!(offset_of!(MemoryDescriptor, ty) == 0);
|
||||
assert!(offset_of!(MemoryDescriptor, phys_start) == 8);
|
||||
assert!(offset_of!(MemoryDescriptor, virt_start) == 16);
|
||||
assert!(offset_of!(MemoryDescriptor, page_count) == 24);
|
||||
assert!(offset_of!(MemoryDescriptor, att) == 32);
|
||||
};
|
||||
|
||||
impl MemoryDescriptor {
|
||||
/// Memory descriptor version number.
|
||||
pub const VERSION: u32 = 1;
|
||||
|
|
@ -384,6 +402,7 @@ impl Default for MemoryDescriptor {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
ty: MemoryType::RESERVED,
|
||||
padding: 0,
|
||||
phys_start: 0,
|
||||
virt_start: 0,
|
||||
page_count: 0,
|
||||
|
|
|
|||
|
|
@ -471,6 +471,7 @@ mod tests {
|
|||
const BASE_MMAP_UNSORTED: [MemoryDescriptor; 3] = [
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x3000,
|
||||
virt_start: 0x3000,
|
||||
page_count: 1,
|
||||
|
|
@ -478,6 +479,7 @@ mod tests {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x2000,
|
||||
virt_start: 0x2000,
|
||||
page_count: 1,
|
||||
|
|
@ -485,6 +487,7 @@ mod tests {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x1000,
|
||||
virt_start: 0x1000,
|
||||
page_count: 1,
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ mod tests_mmap_artificial {
|
|||
|
||||
const BASE: MemoryDescriptor = MemoryDescriptor {
|
||||
ty: TY,
|
||||
padding: 0,
|
||||
phys_start: 0,
|
||||
virt_start: 0,
|
||||
page_count: 0,
|
||||
|
|
@ -170,6 +171,7 @@ mod tests_mmap_artificial {
|
|||
const TY: MemoryType = MemoryType::RESERVED;
|
||||
|
||||
const BASE: MemoryDescriptor = MemoryDescriptor {
|
||||
padding: 0,
|
||||
ty: TY,
|
||||
phys_start: 0,
|
||||
virt_start: 0,
|
||||
|
|
@ -274,6 +276,7 @@ mod tests_mmap_real {
|
|||
let expected = [
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::BOOT_SERVICES_CODE,
|
||||
padding: 0,
|
||||
phys_start: 0x0,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x1,
|
||||
|
|
@ -284,6 +287,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x1000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x86,
|
||||
|
|
@ -294,6 +298,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::BOOT_SERVICES_DATA,
|
||||
padding: 0,
|
||||
phys_start: 0x87000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x1,
|
||||
|
|
@ -304,6 +309,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x88000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x18,
|
||||
|
|
@ -314,6 +320,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x100000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x700,
|
||||
|
|
@ -324,6 +331,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::ACPI_NON_VOLATILE,
|
||||
padding: 0,
|
||||
phys_start: 0x800000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x8,
|
||||
|
|
@ -334,6 +342,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x808000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x3,
|
||||
|
|
@ -344,6 +353,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::ACPI_NON_VOLATILE,
|
||||
padding: 0,
|
||||
phys_start: 0x80b000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x1,
|
||||
|
|
@ -354,6 +364,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x80c000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0x4,
|
||||
|
|
@ -364,6 +375,7 @@ mod tests_mmap_real {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::ACPI_NON_VOLATILE,
|
||||
padding: 0,
|
||||
phys_start: 0x810000,
|
||||
virt_start: 0x0,
|
||||
page_count: 0xf0,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ fn parse_boot_information_efi_mmap() {
|
|||
let mut mmap_source = [
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x3000,
|
||||
virt_start: 0x3000,
|
||||
page_count: 1,
|
||||
|
|
@ -17,6 +18,7 @@ fn parse_boot_information_efi_mmap() {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x2000,
|
||||
virt_start: 0x2000,
|
||||
page_count: 1,
|
||||
|
|
@ -24,6 +26,7 @@ fn parse_boot_information_efi_mmap() {
|
|||
},
|
||||
MemoryDescriptor {
|
||||
ty: MemoryType::CONVENTIONAL,
|
||||
padding: 0,
|
||||
phys_start: 0x1000,
|
||||
virt_start: 0x1000,
|
||||
page_count: 1,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue