uefi_loader/elf/
loader.rs1extern crate alloc;
4
5use crate::elf::parser::{ElfHeader, PFlags};
6use alloc::vec::Vec;
7use core::ptr;
8use kernel_info::memory::{KERNEL_BASE, PHYS_LOAD};
9use kernel_vmem::addresses::{
10    MemoryAddress, PageSize, PhysicalAddress, PhysicalPage, Size4K, VirtualPage,
11};
12use uefi::Status;
13use uefi::boot::{self, AllocateType, MemoryType};
14
15#[derive(Debug, thiserror::Error)]
16pub enum ElfLoaderError {
17    #[error("A pointer arithmetic operation failed due to an underflow or overflow")]
18    PointerArithmetic,
19    #[error("A provided memory address is out of bounds for the architecture")]
20    AddressOutOfBounds,
21    #[error("An physical memory allocation failed")]
22    PhysicalAllocationFailed(#[source] uefi::Error),
23    #[error("The ELF segment size does not match the program size")]
24    ElfSizeMismatch,
25}
26
27impl From<ElfLoaderError> for Status {
28    fn from(value: ElfLoaderError) -> Self {
29        match value {
30            ElfLoaderError::PhysicalAllocationFailed(_) => Self::BUFFER_TOO_SMALL,
31            ElfLoaderError::PointerArithmetic
32            | ElfLoaderError::AddressOutOfBounds
33            | ElfLoaderError::ElfSizeMismatch => Self::BAD_BUFFER_SIZE,
34        }
35    }
36}
37
38pub fn load_pt_load_segments_hi(
41    elf_bytes: &[u8],
42    hdr: &ElfHeader,
43) -> Result<Vec<LoadedSegMap>, ElfLoaderError> {
44    let mut maps = Vec::new();
45
46    for seg in &hdr.segments {
47        if seg.memsz == 0 {
48            continue;
49        }
50
51        let seg_vaddr = seg.vaddr;
53        let lma = seg_vaddr
54            .as_u64()
55            .checked_sub(KERNEL_BASE)
56            .ok_or(ElfLoaderError::PointerArithmetic)?;
57
58        let phys_start = PHYS_LOAD
59            .checked_add(lma)
60            .ok_or(ElfLoaderError::PointerArithmetic)?;
61        let phys_end = phys_start
62            .checked_add(seg.memsz)
63            .ok_or(ElfLoaderError::PointerArithmetic)?;
64
65        let alloc_start = MemoryAddress::new(phys_start)
67            .align_down::<Size4K>()
68            .as_u64();
69        let alloc_end = align_up_u64(phys_end, Size4K::SIZE);
70        let pages = ((alloc_end - alloc_start) / Size4K::SIZE) as usize;
71
72        let mem_type = if seg.flags.execute() {
73            MemoryType::LOADER_CODE
74        } else {
75            MemoryType::LOADER_DATA
76        };
77
78        let ptr = boot::allocate_pages(AllocateType::Address(alloc_start), mem_type, pages)
80            .map_err(ElfLoaderError::PhysicalAllocationFailed)?;
81        let phys_base = PhysicalAddress::from_nonnull(ptr);
83
84        let mem_len = usize::try_from(seg.memsz).map_err(|_| ElfLoaderError::AddressOutOfBounds)?;
86        let in_seg_off = phys_start - alloc_start; let dst = (phys_base.as_u64() + in_seg_off) as *mut u8;
88        unsafe {
89            ptr::write_bytes(dst, 0, mem_len);
90        }
91
92        if seg.filesz != 0 {
94            let src_off =
95                usize::try_from(seg.offset).map_err(|_| ElfLoaderError::AddressOutOfBounds)?;
96            let file_len =
97                usize::try_from(seg.filesz).map_err(|_| ElfLoaderError::AddressOutOfBounds)?;
98            let src_end = src_off
99                .checked_add(file_len)
100                .ok_or(ElfLoaderError::PointerArithmetic)?;
101            if src_end > elf_bytes.len() {
102                return Err(ElfLoaderError::ElfSizeMismatch);
103            }
104            unsafe {
105                ptr::copy_nonoverlapping(elf_bytes.as_ptr().add(src_off), dst, file_len);
106            }
107        }
108
109        let vaddr_page = seg_vaddr.page::<Size4K>(); let vaddr_end_u64 = seg_vaddr
112            .as_u64()
113            .checked_add(seg.memsz)
114            .ok_or(ElfLoaderError::PointerArithmetic)?;
115        let vaddr_end_aligned = align_up_u64(vaddr_end_u64, Size4K::SIZE);
116        let map_len = vaddr_end_aligned - vaddr_page.base().as_u64();
117
118        maps.push(LoadedSegMap {
119            vaddr_page,
120            phys_page: PhysicalPage::<Size4K>::from_addr(phys_base),
121            map_len,
122            flags: seg.flags,
123        });
124    }
125
126    Ok(maps)
127}
128
129#[derive(Clone, Copy)]
130pub struct LoadedSegMap {
131    pub vaddr_page: VirtualPage<Size4K>,
133    pub phys_page: PhysicalPage<Size4K>,
135    pub map_len: u64,
137    pub flags: PFlags,
139}
140
141#[inline]
142const fn align_up_u64(x: u64, a: u64) -> u64 {
143    (x + (a - 1)) & !(a - 1)
145}