use crate::{fs::File, memory::MemoryManager};
use core::{
ptr,
slice,
str::{self, FromStr},
};
use hal::memory::{Flags, FrameAllocator, FrameSize, PAddr, Page, PageTable, Size4KiB, VAddr};
use hal_riscv::platform::kernel_map;
use mer::{
program::{ProgramHeader, SegmentType},
Elf,
};
use mulch::math::align_up;
use seed::boot_info::{LoadedImage, Segment};
#[derive(Clone, Debug)]
pub struct LoadedKernel {
pub entry_point: VAddr,
pub stack_top: VAddr,
pub global_pointer: VAddr,
pub next_available_address: VAddr,
}
pub fn load_kernel<P>(file: &File<'_>, page_table: &mut P, memory_manager: &MemoryManager) -> LoadedKernel
where
P: PageTable<Size4KiB>,
{
let elf = Elf::new(file.data).expect("Failed to parse kernel ELF");
let entry_point = VAddr::new(elf.entry_point());
let mut next_available_address = kernel_map::KERNEL_BASE;
for segment in elf.segments() {
match segment.segment_type() {
SegmentType::Load if segment.mem_size > 0 => {
let segment = load_segment(segment, &elf, false, memory_manager);
if (segment.virtual_address + segment.size) > next_available_address {
next_available_address =
(Page::<Size4KiB>::contains(segment.virtual_address + segment.size) + 1).start;
}
assert!(
segment.virtual_address.is_aligned(Size4KiB::SIZE),
"Segment's virtual address is not page-aligned"
);
assert!(
segment.physical_address.is_aligned(Size4KiB::SIZE),
"Segment's physical address is not frame-aligned"
);
assert!(segment.size % Size4KiB::SIZE == 0, "Segment size is not a multiple of page size!");
page_table
.map_area(
segment.virtual_address,
segment.physical_address,
segment.size,
segment.flags,
memory_manager,
)
.unwrap();
}
_ => (),
}
}
let stack_top = match elf.symbols().find(|symbol| symbol.name(&elf) == Some("_stack_top")) {
Some(symbol) => VAddr::new(symbol.value as usize),
None => panic!("Kernel does not have a '_stack_top' symbol!"),
};
let global_pointer = match elf.symbols().find(|symbol| symbol.name(&elf) == Some("__global_pointer$")) {
Some(symbol) => VAddr::new(symbol.value as usize),
None => panic!("Kernel does not have a '__global_pointer$' symbol!"),
};
let guard_page_address = match elf.symbols().find(|symbol| symbol.name(&elf) == Some("_guard_page")) {
Some(symbol) => VAddr::new(symbol.value as usize),
None => panic!("Kernel does not have a '_guard_page' symbol!"),
};
assert!(guard_page_address.is_aligned(Size4KiB::SIZE), "Guard page address is not page aligned");
page_table.unmap::<Size4KiB>(Page::starts_with(guard_page_address));
LoadedKernel { entry_point, stack_top, global_pointer, next_available_address }
}
pub fn load_image(file: &File<'_>, name: &str, memory_manager: &MemoryManager) -> LoadedImage {
let elf = Elf::new(file.data).expect("Failed to parse user task ELF");
let mut image_data = LoadedImage::default();
image_data.entry_point = VAddr::new(elf.entry_point());
image_data.name = heapless::String::from_str(name).unwrap();
for segment in elf.segments() {
match segment.segment_type() {
SegmentType::Load if segment.mem_size > 0 => {
let segment = load_segment(segment, &elf, true, memory_manager);
match image_data.segments.push(segment) {
Ok(()) => (),
Err(_) => panic!("Image for '{}' has too many load segments!", name),
}
}
_ => (),
}
}
image_data
}
fn load_segment(
segment: ProgramHeader,
elf: &Elf,
user_accessible: bool,
memory_manager: &MemoryManager,
) -> Segment {
let mem_size = align_up(segment.mem_size as usize, Size4KiB::SIZE);
let num_frames = (mem_size as usize) / Size4KiB::SIZE;
let physical_address = memory_manager.allocate_n(num_frames).start.start;
assert!(segment.file_size <= segment.mem_size, "Segment's data will not fit in requested memory");
unsafe {
slice::from_raw_parts_mut(usize::from(physical_address) as *mut u8, segment.file_size as usize)
.copy_from_slice(segment.data(&elf));
}
unsafe {
ptr::write_bytes(
(usize::from(physical_address) + (segment.file_size as usize)) as *mut u8,
0,
mem_size - (segment.file_size as usize),
);
}
Segment {
physical_address: PAddr::new(usize::from(physical_address)).unwrap(),
virtual_address: VAddr::new(segment.virtual_address as usize),
size: num_frames * Size4KiB::SIZE,
flags: Flags {
writable: segment.is_writable(),
executable: segment.is_executable(),
user_accessible,
..Default::default()
},
}
}