mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
Avoid segment overlapping in EFI stub
This commit is contained in:
parent
a64fa94404
commit
5633263182
@ -1,5 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
use xmas_elf::program::{ProgramHeader, SegmentData};
|
use xmas_elf::program::{ProgramHeader, SegmentData};
|
||||||
|
|
||||||
/// Load the kernel ELF payload to memory.
|
/// Load the kernel ELF payload to memory.
|
||||||
@ -24,12 +26,7 @@ fn load_segment(file: &xmas_elf::ElfFile, program: &xmas_elf::program::ProgramHe
|
|||||||
panic!("[setup] Unexpected segment data type!");
|
panic!("[setup] Unexpected segment data type!");
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: This can be unsafe if the memory region overlaps with allocated memory or memory
|
let dst_slice = crate::x86::alloc_at(program.physical_addr as usize, program.mem_size as usize);
|
||||||
// reserved by the firmware. We need to query UEFI or check the memory map ourselves to prevent
|
|
||||||
// this from happening.
|
|
||||||
let dst_slice = unsafe {
|
|
||||||
core::slice::from_raw_parts_mut(program.physical_addr as *mut u8, program.mem_size as usize)
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "debug_print")]
|
#[cfg(feature = "debug_print")]
|
||||||
crate::println!(
|
crate::println!(
|
||||||
@ -39,6 +36,6 @@ fn load_segment(file: &xmas_elf::ElfFile, program: &xmas_elf::program::ProgramHe
|
|||||||
);
|
);
|
||||||
|
|
||||||
let (left, right) = dst_slice.split_at_mut(program.file_size as usize);
|
let (left, right) = dst_slice.split_at_mut(program.file_size as usize);
|
||||||
left.copy_from_slice(segment_data);
|
left.write_copy_of_slice(segment_data);
|
||||||
right.fill(0);
|
MaybeUninit::fill(right, 0);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(maybe_uninit_fill)]
|
||||||
|
#![feature(maybe_uninit_write_slice)]
|
||||||
|
|
||||||
mod console;
|
mod console;
|
||||||
mod loader;
|
mod loader;
|
||||||
|
34
ostd/libs/linux-bzimage/setup/src/x86/amd64_efi/alloc.rs
Normal file
34
ostd/libs/linux-bzimage/setup/src/x86/amd64_efi/alloc.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
pub fn alloc_at(addr: usize, size: usize) -> &'static mut [MaybeUninit<u8>] {
|
||||||
|
assert_ne!(addr, 0, "the address to allocate is zero");
|
||||||
|
assert!(
|
||||||
|
size <= isize::MAX as usize,
|
||||||
|
"the size to allocate exceeds `isize::MAX`"
|
||||||
|
);
|
||||||
|
|
||||||
|
addr.checked_add(size)
|
||||||
|
.expect("the range to allocate overflows");
|
||||||
|
|
||||||
|
let allocated = uefi::boot::allocate_pages(
|
||||||
|
uefi::boot::AllocateType::Address(addr as u64),
|
||||||
|
uefi::boot::MemoryType::LOADER_DATA,
|
||||||
|
size.div_ceil(super::efi::PAGE_SIZE as usize),
|
||||||
|
)
|
||||||
|
.expect("the UEFI allocation fails");
|
||||||
|
assert_eq!(
|
||||||
|
allocated.as_ptr() as usize,
|
||||||
|
addr,
|
||||||
|
"the allocated address is not the request address"
|
||||||
|
);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// 1. The address is not zero and the size is reasonable (there are less the `isize::MAX` bytes
|
||||||
|
// and the range won't overflow the address space), as asserted above.
|
||||||
|
// 2. The memory region is allocated via the UEFI firmware, so it is valid for reading and
|
||||||
|
// writing. We will not deallocate it, so it live for `'static`.
|
||||||
|
// 3. The type alignment is 1 and the type can contain uninitialized data.
|
||||||
|
unsafe { core::slice::from_raw_parts_mut(addr as *mut MaybeUninit<u8>, size) }
|
||||||
|
}
|
@ -6,7 +6,7 @@ use uefi_raw::table::system::SystemTable;
|
|||||||
|
|
||||||
use super::decoder::decode_payload;
|
use super::decoder::decode_payload;
|
||||||
|
|
||||||
const PAGE_SIZE: u64 = 4096;
|
pub(super) const PAGE_SIZE: u64 = 4096;
|
||||||
|
|
||||||
#[export_name = "main_efi_handover64"]
|
#[export_name = "main_efi_handover64"]
|
||||||
extern "sysv64" fn main_efi_handover64(
|
extern "sysv64" fn main_efi_handover64(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
pub(super) mod alloc;
|
||||||
mod decoder;
|
mod decoder;
|
||||||
mod efi;
|
mod efi;
|
||||||
|
|
||||||
|
89
ostd/libs/linux-bzimage/setup/src/x86/legacy_i386/alloc.rs
Normal file
89
ostd/libs/linux-bzimage/setup/src/x86/legacy_i386/alloc.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::{mem::MaybeUninit, ops::Range};
|
||||||
|
|
||||||
|
use crate::sync::Mutex;
|
||||||
|
|
||||||
|
const NUM_USED_RANGES: usize = 16;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
e820: &'static [linux_boot_params::BootE820Entry],
|
||||||
|
used: [Range<usize>; NUM_USED_RANGES],
|
||||||
|
}
|
||||||
|
|
||||||
|
static STATE: Mutex<Option<State>> = Mutex::new(None);
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that the E820 entries in the boot parameters correctly represent the
|
||||||
|
/// current memory map.
|
||||||
|
pub(super) unsafe fn init(boot_params: &'static linux_boot_params::BootParams) {
|
||||||
|
let mut state = STATE.lock();
|
||||||
|
|
||||||
|
assert!(state.is_none());
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn __executable_start();
|
||||||
|
fn __executable_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut used = core::array::from_fn(|_| 0..0);
|
||||||
|
used[0] = (__executable_start as usize)..(__executable_end as usize);
|
||||||
|
used[1] = {
|
||||||
|
let params_addr = core::ptr::from_ref(boot_params).addr();
|
||||||
|
params_addr..(params_addr + core::mem::size_of::<linux_boot_params::BootParams>())
|
||||||
|
};
|
||||||
|
|
||||||
|
*state = Some(State {
|
||||||
|
e820: &boot_params.e820_table[..(boot_params.e820_entries as usize)],
|
||||||
|
used,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_at(addr: usize, size: usize) -> &'static mut [MaybeUninit<u8>] {
|
||||||
|
let mut state = STATE.lock();
|
||||||
|
let state = state.as_mut().unwrap();
|
||||||
|
|
||||||
|
assert_ne!(addr, 0, "the address to allocate is zero");
|
||||||
|
assert!(
|
||||||
|
size <= isize::MAX as usize,
|
||||||
|
"the size to allocate exceeds `isize::MAX`"
|
||||||
|
);
|
||||||
|
|
||||||
|
let range = addr..addr
|
||||||
|
.checked_add(size)
|
||||||
|
.expect("the range to allocate overflows");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
state.e820.iter().any(|entry| {
|
||||||
|
let typ = entry.typ;
|
||||||
|
typ == linux_boot_params::E820Type::Ram
|
||||||
|
&& entry.addr as usize <= range.start
|
||||||
|
&& range.end <= (entry.addr + entry.size) as usize
|
||||||
|
}),
|
||||||
|
"the range to allocate is not usable"
|
||||||
|
);
|
||||||
|
|
||||||
|
let overlapped = state
|
||||||
|
.used
|
||||||
|
.iter()
|
||||||
|
.find(|used| used.start < range.end && range.start < used.end);
|
||||||
|
assert!(overlapped.is_none(), "the range to allocate is used");
|
||||||
|
|
||||||
|
let empty = state
|
||||||
|
.used
|
||||||
|
.iter_mut()
|
||||||
|
// Use fully qualified syntax to avoid collisions with the unstable method
|
||||||
|
// `ExactSizeIterator::is_empty`. See <https://github.com/rust-lang/rust/issues/86682>.
|
||||||
|
.find(|used| Range::is_empty(used))
|
||||||
|
.expect("the allocated ranges are full");
|
||||||
|
*empty = range;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// 1. The address is not zero and the size is reasonable (there are less the `isize::MAX` bytes
|
||||||
|
// and the range won't overflow the address space), as asserted above.
|
||||||
|
// 2. The memory region is usable and just allocated, so it is valid for reading and writing.
|
||||||
|
// We will not deallocate it, so it live for `'static`.
|
||||||
|
// 3. The type alignment is 1 and the type can contain uninitialized data.
|
||||||
|
unsafe { core::slice::from_raw_parts_mut(addr as *mut MaybeUninit<u8>, size) }
|
||||||
|
}
|
@ -4,6 +4,8 @@ use core::arch::{asm, global_asm};
|
|||||||
|
|
||||||
use linux_boot_params::BootParams;
|
use linux_boot_params::BootParams;
|
||||||
|
|
||||||
|
pub(super) mod alloc;
|
||||||
|
|
||||||
global_asm!(include_str!("setup.S"));
|
global_asm!(include_str!("setup.S"));
|
||||||
|
|
||||||
const ASTER_ENTRY_POINT: *const () = 0x8001000 as _;
|
const ASTER_ENTRY_POINT: *const () = 0x8001000 as _;
|
||||||
@ -15,6 +17,13 @@ extern "cdecl" fn main_legacy32(boot_params_ptr: *mut BootParams) -> ! {
|
|||||||
crate::x86::image_load_offset(),
|
crate::x86::image_load_offset(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// SAFETY: We get boot parameters from the boot loader, so by contract the pointer is valid and
|
||||||
|
// the underlying memory is initialized. We never mutate the boot parameters, so we can create
|
||||||
|
// an immutable reference of the plain-old-data type.
|
||||||
|
let boot_params = unsafe { &*boot_params_ptr };
|
||||||
|
// SAFETY: We get boot parameters from the boot loader. By contract they are correct.
|
||||||
|
unsafe { alloc::init(boot_params) };
|
||||||
|
|
||||||
crate::println!("[setup] Loading the payload as an ELF file");
|
crate::println!("[setup] Loading the payload as an ELF file");
|
||||||
crate::loader::load_elf(crate::x86::payload());
|
crate::loader::load_elf(crate::x86::payload());
|
||||||
|
|
||||||
|
@ -24,6 +24,19 @@ load_address:
|
|||||||
// Set up the stack.
|
// Set up the stack.
|
||||||
mov esp, offset __stack_top
|
mov esp, offset __stack_top
|
||||||
|
|
||||||
|
// Load the GDT.
|
||||||
|
push 8 // 32-bit code
|
||||||
|
mov eax, offset gdt_loaded
|
||||||
|
push eax
|
||||||
|
lgdt [gdtr]
|
||||||
|
retf
|
||||||
|
gdt_loaded:
|
||||||
|
mov ax, 16 // 32-bit data
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
|
||||||
// Call the Rust main routine.
|
// Call the Rust main routine.
|
||||||
push esi
|
push esi
|
||||||
call main_legacy32
|
call main_legacy32
|
||||||
@ -39,6 +52,20 @@ halt:
|
|||||||
hlt
|
hlt
|
||||||
jmp halt
|
jmp halt
|
||||||
|
|
||||||
|
// GDT. We define GDT ourselves to ensure that the GDT page will not be
|
||||||
|
// accidentally overwritten by the allocated memory.
|
||||||
|
.rodata
|
||||||
|
.align 16
|
||||||
|
gdtr:
|
||||||
|
.word gdt_end - gdt - 1
|
||||||
|
.long gdt
|
||||||
|
.align 16
|
||||||
|
gdt:
|
||||||
|
.quad 0x0000000000000000 // 0: null descriptor
|
||||||
|
.quad 0x00cf9a000000ffff // 8: 32-bit code segment
|
||||||
|
.quad 0x00cf92000000ffff // 16: 32-bit data segment
|
||||||
|
gdt_end:
|
||||||
|
|
||||||
// A small stack for the setup code.
|
// A small stack for the setup code.
|
||||||
.bss
|
.bss
|
||||||
.align 8
|
.align 8
|
||||||
|
@ -6,10 +6,14 @@ cfg_if::cfg_if! {
|
|||||||
if #[cfg(target_arch = "x86_64")] {
|
if #[cfg(target_arch = "x86_64")] {
|
||||||
mod amd64_efi;
|
mod amd64_efi;
|
||||||
|
|
||||||
|
pub use amd64_efi::alloc::alloc_at;
|
||||||
|
|
||||||
const CFG_TARGET_ARCH_X86_64: usize = 1;
|
const CFG_TARGET_ARCH_X86_64: usize = 1;
|
||||||
} else if #[cfg(target_arch = "x86")] {
|
} else if #[cfg(target_arch = "x86")] {
|
||||||
mod legacy_i386;
|
mod legacy_i386;
|
||||||
|
|
||||||
|
pub use legacy_i386::alloc::alloc_at;
|
||||||
|
|
||||||
const CFG_TARGET_ARCH_X86_64: usize = 0;
|
const CFG_TARGET_ARCH_X86_64: usize = 0;
|
||||||
} else {
|
} else {
|
||||||
compile_error!("unsupported target architecture");
|
compile_error!("unsupported target architecture");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user