Skip to main content

kernel/sched/loader/
load.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! ELF loading logic
3//!
4//! Authors: MarioS271
5
6use crate::lib::addr::VirtAddr;
7use crate::mm::mm_error::MmResult;
8use crate::mm::pmm::FRAME_SIZE;
9use crate::mm::vmm::Vmm;
10use crate::mm::vmm::address_space::AddressSpace;
11use crate::mm::vmm::traits::VmmPaging;
12use crate::mm::vmm::vma::VmaFlags;
13use crate::sched::loader::defs::phdrs::ElfPhdr;
14use crate::state::kstate::KSTATE;
15
16/// Map VMAs and pages according to the ELF phdrs and copy the ELF binary into the
17/// freshly mapped memory
18pub fn map_phdrs_and_copy_elf(addr_space: &mut AddressSpace, phdrs: &[ElfPhdr], elf: &[u8]) -> MmResult<()> {
19    for phdr in phdrs {
20        if phdr.p_type != ElfPhdr::PT_LOAD {
21            continue;
22        }
23
24        let vaddr_start = VirtAddr::new(phdr.p_vaddr).align_down(FRAME_SIZE);
25        let vaddr_end = VirtAddr::new(phdr.p_vaddr + phdr.p_memsz).align_up(FRAME_SIZE);
26        let size = vaddr_end.as_u64() - vaddr_start.as_u64();
27
28        let mut vma_flags = VmaFlags::USER;
29        if phdr.p_flags & ElfPhdr::PF_R != 0 { vma_flags |= VmaFlags::READ; }
30        if phdr.p_flags & ElfPhdr::PF_W != 0 { vma_flags |= VmaFlags::WRITE; }
31        if phdr.p_flags & ElfPhdr::PF_X != 0 { vma_flags |= VmaFlags::EXEC; }
32
33        // Safety: the PMM was already initialized in stage 2
34        let mut pmm = unsafe { KSTATE.mm.pmm().lock() };
35
36        Vmm::map_region(
37            &mut pmm,
38            addr_space,
39            vaddr_start,
40            size,
41            vma_flags
42        )?;
43
44        drop(pmm);
45
46        // Safety: map_region correctly mapped memory in this code path and nothing is running
47        // in parallel
48        unsafe { copy_segment_data(addr_space, phdr, elf) };
49    }
50    Ok(())
51}
52
53/// Copies an ELF segment into the process's address space
54///
55/// # Safety
56/// The caller must ensure all the following things:
57/// - `p_vaddr` to `p_vaddr + p_filesz` needs to be fully mapped in `addr_space`
58/// - No references/pointers to the target frames should exist and no other CPU should be
59///   able to concurrently operate on this data
60unsafe fn copy_segment_data(addr_space: &AddressSpace, phdr: &ElfPhdr, elf: &[u8]) {
61    let mut copied = 0u64;
62
63    while copied < phdr.p_filesz {
64        let dst_vaddr = phdr.p_vaddr + copied;
65        let page_base = VirtAddr::new(dst_vaddr).align_down(FRAME_SIZE);
66        let offset_in_page = dst_vaddr - page_base.as_u64();
67
68        let phys = Vmm::translate(addr_space.page_ptr(), page_base).unwrap();
69        let remaining_space_in_page = FRAME_SIZE - offset_in_page;
70        let remaining_to_copy = phdr.p_filesz - copied;
71        let chunk = remaining_space_in_page.min(remaining_to_copy);
72
73        unsafe {
74            let src = elf.as_ptr().add((phdr.p_offset + copied) as usize);
75            core::ptr::copy_nonoverlapping(
76                src,
77                phys.as_mut_hhdm_ptr::<u8>().add(offset_in_page as usize),
78                chunk as usize
79            );
80        }
81
82        copied += chunk;
83    }
84}
85
86/// Set up a stack for the user process
87pub fn setup_user_stack(addr_space: &mut AddressSpace) -> MmResult<VirtAddr> {
88    let stack_size = FRAME_SIZE * 4;    // TODO: dynamically do this
89    let stack_top = crate::mm::layout::USER_STACK_TOP;
90    let stack_bottom = stack_top - stack_size;
91
92    // Safety: the PMM was already initialized in stage 2
93    let mut pmm = unsafe { KSTATE.mm.pmm().lock() };
94
95    // TODO: lazily map this (needs user pf handler)
96    Vmm::map_region(
97        &mut pmm,
98        addr_space,
99        stack_bottom,
100        stack_size,
101        VmaFlags::USER | VmaFlags::READ | VmaFlags::WRITE
102    )?;
103
104    Ok(stack_top)
105}