Merge pull request #2036 from rust-osdev/fix

uefi-raw: make MemoryDescriptor layout portable across x86 targets
This commit is contained in:
Philipp Schuster 2026-08-19 19:22:30 +00:00 committed by GitHub
commit 2fa6c6f1e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 91 additions and 24 deletions

View file

@ -4,7 +4,7 @@
## Changed ## Changed
- **Breaking**: `MemoryDescriptor` now has a new member to ensure correct - **Breaking**: `MemoryDescriptor` now has a new member to ensure correct
padding on 32-bit targets. layout on all non-UEFI 32-bit targets.
## Removed ## Removed

View file

@ -9,6 +9,7 @@ use crate::{
}; };
use bitflags::bitflags; use bitflags::bitflags;
use core::ffi::c_void; use core::ffi::c_void;
use core::mem::offset_of;
use core::ops::RangeInclusive; use core::ops::RangeInclusive;
newtype_enum! { newtype_enum! {
@ -360,12 +361,15 @@ bitflags! {
/// [version]: MemoryDescriptor::VERSION /// [version]: MemoryDescriptor::VERSION
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059 /// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)] #[repr(C, align(8))]
pub struct MemoryDescriptor { pub struct MemoryDescriptor {
/// Type of memory occupying this range. /// Type of memory occupying this range.
pub ty: MemoryType, pub ty: MemoryType,
/// Reserved field that must be set to 0. /// Implicit padding that should be set to 0.
pub reserved: u32, ///
/// 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. // Implicit 32-bit padding.
/// Starting physical address. /// Starting physical address.
pub phys_start: PhysicalAddress, pub phys_start: PhysicalAddress,
@ -377,6 +381,18 @@ pub struct MemoryDescriptor {
pub att: MemoryAttribute, 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 { impl MemoryDescriptor {
/// Memory descriptor version number. /// Memory descriptor version number.
pub const VERSION: u32 = 1; pub const VERSION: u32 = 1;
@ -386,7 +402,7 @@ impl Default for MemoryDescriptor {
fn default() -> Self { fn default() -> Self {
Self { Self {
ty: MemoryType::RESERVED, ty: MemoryType::RESERVED,
reserved: 0, padding: 0,
phys_start: 0, phys_start: 0,
virt_start: 0, virt_start: 0,
page_count: 0, page_count: 0,

View file

@ -471,7 +471,7 @@ mod tests {
const BASE_MMAP_UNSORTED: [MemoryDescriptor; 3] = [ const BASE_MMAP_UNSORTED: [MemoryDescriptor; 3] = [
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x3000, phys_start: 0x3000,
virt_start: 0x3000, virt_start: 0x3000,
page_count: 1, page_count: 1,
@ -479,7 +479,7 @@ mod tests {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x2000, phys_start: 0x2000,
virt_start: 0x2000, virt_start: 0x2000,
page_count: 1, page_count: 1,
@ -487,7 +487,7 @@ mod tests {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x1000, phys_start: 0x1000,
virt_start: 0x1000, virt_start: 0x1000,
page_count: 1, page_count: 1,

View file

@ -133,7 +133,7 @@ mod tests_mmap_artificial {
const BASE: MemoryDescriptor = MemoryDescriptor { const BASE: MemoryDescriptor = MemoryDescriptor {
ty: TY, ty: TY,
reserved: 0, padding: 0,
phys_start: 0, phys_start: 0,
virt_start: 0, virt_start: 0,
page_count: 0, page_count: 0,
@ -171,7 +171,7 @@ mod tests_mmap_artificial {
const TY: MemoryType = MemoryType::RESERVED; const TY: MemoryType = MemoryType::RESERVED;
const BASE: MemoryDescriptor = MemoryDescriptor { const BASE: MemoryDescriptor = MemoryDescriptor {
reserved: 0, padding: 0,
ty: TY, ty: TY,
phys_start: 0, phys_start: 0,
virt_start: 0, virt_start: 0,
@ -276,7 +276,7 @@ mod tests_mmap_real {
let expected = [ let expected = [
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::BOOT_SERVICES_CODE, ty: MemoryType::BOOT_SERVICES_CODE,
reserved: 0, padding: 0,
phys_start: 0x0, phys_start: 0x0,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x1, page_count: 0x1,
@ -287,7 +287,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x1000, phys_start: 0x1000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x86, page_count: 0x86,
@ -298,7 +298,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::BOOT_SERVICES_DATA, ty: MemoryType::BOOT_SERVICES_DATA,
reserved: 0, padding: 0,
phys_start: 0x87000, phys_start: 0x87000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x1, page_count: 0x1,
@ -309,7 +309,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x88000, phys_start: 0x88000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x18, page_count: 0x18,
@ -320,7 +320,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x100000, phys_start: 0x100000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x700, page_count: 0x700,
@ -331,7 +331,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::ACPI_NON_VOLATILE, ty: MemoryType::ACPI_NON_VOLATILE,
reserved: 0, padding: 0,
phys_start: 0x800000, phys_start: 0x800000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x8, page_count: 0x8,
@ -342,7 +342,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x808000, phys_start: 0x808000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x3, page_count: 0x3,
@ -353,7 +353,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::ACPI_NON_VOLATILE, ty: MemoryType::ACPI_NON_VOLATILE,
reserved: 0, padding: 0,
phys_start: 0x80b000, phys_start: 0x80b000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x1, page_count: 0x1,
@ -364,7 +364,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x80c000, phys_start: 0x80c000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0x4, page_count: 0x4,
@ -375,7 +375,7 @@ mod tests_mmap_real {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::ACPI_NON_VOLATILE, ty: MemoryType::ACPI_NON_VOLATILE,
reserved: 0, padding: 0,
phys_start: 0x810000, phys_start: 0x810000,
virt_start: 0x0, virt_start: 0x0,
page_count: 0xf0, page_count: 0xf0,

View file

@ -10,7 +10,7 @@ fn parse_boot_information_efi_mmap() {
let mut mmap_source = [ let mut mmap_source = [
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x3000, phys_start: 0x3000,
virt_start: 0x3000, virt_start: 0x3000,
page_count: 1, page_count: 1,
@ -18,7 +18,7 @@ fn parse_boot_information_efi_mmap() {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x2000, phys_start: 0x2000,
virt_start: 0x2000, virt_start: 0x2000,
page_count: 1, page_count: 1,
@ -26,7 +26,7 @@ fn parse_boot_information_efi_mmap() {
}, },
MemoryDescriptor { MemoryDescriptor {
ty: MemoryType::CONVENTIONAL, ty: MemoryType::CONVENTIONAL,
reserved: 0, padding: 0,
phys_start: 0x1000, phys_start: 0x1000,
virt_start: 0x1000, virt_start: 0x1000,
page_count: 1, page_count: 1,

View file

@ -323,7 +323,12 @@ fn check_fields(fields: &Punctuated<Field, Comma>, src: &Path) -> Result<(), Err
} }
/// List with allowed combinations of representations (see [`Repr`]). /// List with allowed combinations of representations (see [`Repr`]).
const ALLOWED_REPRS: &[&[Repr]] = &[&[Repr::C], &[Repr::C, Repr::Packed], &[Repr::Transparent]]; const ALLOWED_REPRS: &[&[Repr]] = &[
&[Repr::C],
&[Repr::C, Repr::Packed],
&[Repr::Align(8), Repr::C],
&[Repr::Transparent],
];
fn check_type_attrs(attrs: &[Attribute], spanned: &dyn Spanned, src: &Path) -> Result<(), Error> { fn check_type_attrs(attrs: &[Attribute], spanned: &dyn Spanned, src: &Path) -> Result<(), Error> {
let attrs = parse_attrs(attrs, src)?; let attrs = parse_attrs(attrs, src)?;
@ -398,9 +403,17 @@ fn check_macro(item: &ItemMacro, src: &Path) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// True if the item is an anonymous compile-time assertion.
fn is_anonymous_unit_const(item: &ItemConst) -> bool {
item.ident == "_" && matches!(&*item.ty, Type::Tuple(tuple) if tuple.elems.is_empty())
}
/// Validate a top-level item. /// Validate a top-level item.
fn check_item(item: &Item, src: &Path) -> Result<(), Error> { fn check_item(item: &Item, src: &Path) -> Result<(), Error> {
match item { match item {
Item::Const(item) if is_anonymous_unit_const(item) => {
// Allow compile-time assertions such as ABI layout checks.
}
Item::Const(ItemConst { vis, ty, .. }) => { Item::Const(ItemConst { vis, ty, .. }) => {
if !is_pub(vis) { if !is_pub(vis) {
return Err(Error::new(ErrorKind::MissingPub, src, item)); return Err(Error::new(ErrorKind::MissingPub, src, item));
@ -513,6 +526,30 @@ mod tests {
); );
} }
#[test]
fn test_anonymous_unit_const() {
// Compile-time assertions do not form part of the public API.
assert!(
check_item(
&parse_quote! {
const _: () = {
assert!(true);
};
},
src(),
)
.is_ok()
);
// Named constants must remain public.
check_item_err(
parse_quote! {
const PRIVATE: () = ();
},
ErrorKind::MissingPub,
);
}
#[test] #[test]
fn test_macro() { fn test_macro() {
// bitflags `repr` must be transparent. // bitflags `repr` must be transparent.
@ -606,6 +643,20 @@ mod tests {
.is_ok() .is_ok()
); );
// Valid `repr(C, align(8))` struct.
assert!(
check_struct(
&parse_quote! {
#[repr(C, align(8))]
pub struct S {
pub f: u32,
}
},
src(),
)
.is_ok()
);
// Missing `pub` on struct. // Missing `pub` on struct.
check_item_err( check_item_err(
parse_quote! { parse_quote! {