1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! Loads the kernel in high memory

use multiboot2::BootInformation;
use crate::bootstrap_logging::Serial;
use core::fmt::Write;
use core::slice;
use xmas_elf::ElfFile;
use xmas_elf::program::{ProgramHeader, Type::Load, SegmentData};
use crate::paging::{PagingOffPageSet, PAGE_SIZE, PageTablesSet, EntryFlags};
use crate::address::VirtualAddress;
use sunrise_libutils::align_up;
use crate::frame_alloc::FrameAllocator;

/// Loads the kernel in high memory
/// Returns address of entry point
pub fn load_kernel(page_table: &mut PagingOffPageSet, multiboot_info: &BootInformation) -> usize {
    let module = multiboot_info.module_tags()
        .next().expect("Multiboot module tag for kernel not found");

    let kernel_ptr = module.start_address();
    let kernel_len = module.end_address() - module.start_address();

    let kernel_elf = ElfFile::new(unsafe { slice::from_raw_parts(kernel_ptr as usize as *const u8, kernel_len as usize) })
        .expect("Failed parsing multiboot module as elf");

    // load all segments
    for ph in kernel_elf.program_iter().filter(|ph|
        ph.get_type().expect("Failed to get type of elf program header") == Load)
    {
        load_segment(page_table, ph, &kernel_elf);
    }

    // return the entry point
    let entry_point = kernel_elf.header.pt2.entry_point();
    let _ = writeln!(Serial, "Entry point : {:#x?}", entry_point);
    entry_point as usize
}

/// Loads an elf segment by coping file_size bytes to the right address,
/// and filling remaining with 0s.
/// This is used by NOBITS sections (.bss), this way we initialize them to 0.
#[allow(clippy::match_bool)] // more readable
fn load_segment(page_table: &mut PagingOffPageSet, segment: ProgramHeader<'_>, elf_file: &ElfFile<'_>) {
    // Map the segment memory
    let mem_size_total = align_up(segment.mem_size() as usize, PAGE_SIZE);
    let vaddr = segment.virtual_addr() as usize;

    let flags = if !segment.flags().is_write() {
        EntryFlags::empty()
    } else {
        EntryFlags::WRITABLE
    };

    let phys_addr = FrameAllocator::alloc_contiguous_frames(mem_size_total / PAGE_SIZE);

    page_table.map_range(phys_addr,
        VirtualAddress(vaddr),
        mem_size_total / PAGE_SIZE,
        flags
    );

    // Copy the segment data
    match segment.get_data(elf_file).expect("Error geting elf segment data")
    {
        SegmentData::Undefined(elf_data) =>
        {
            let dest_ptr = phys_addr.addr() as *mut u8;
            let dest = unsafe { slice::from_raw_parts_mut(dest_ptr, mem_size_total) };
            let (dest_data, dest_pad) = dest.split_at_mut(segment.file_size() as usize);

            // Copy elf data
            dest_data.copy_from_slice(elf_data);

            // Fill remaining with 0s
            for byte in dest_pad.iter_mut() {
                *byte = 0x00;
            }
        },
        x => { panic ! ("Unexpected Segment data {:?}", x) }
    }

    let _ = writeln!(Serial, "Loaded segment - VirtAddr {:#010x}, FileSize {:#010x}, MemSize {:#010x} {}{}{}",
        segment.virtual_addr(), segment.file_size(), segment.mem_size(),
        match segment.flags().is_read()    { true => 'R', false => ' '},
        match segment.flags().is_write()   { true => 'W', false => ' '},
        match segment.flags().is_execute() { true => 'X', false => ' '},
    );
}