Skip to content

Commit

Permalink
try to load elf file
Browse files Browse the repository at this point in the history
  • Loading branch information
0x6D70 committed Feb 22, 2024
1 parent 08b2e14 commit f829996
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 70 deletions.
1 change: 1 addition & 0 deletions xernel/kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ libxernel = { path = "../../crates/libxernel", features = ["kernel"] }
linked_list_allocator = { version = "0.10.3", features = [] }
paste = "1.0.14"
seq-macro = "0.3.5"
elf = { version="0.7.4", features = ["nightly"], default-features = false}
1 change: 1 addition & 0 deletions xernel/kernel/c.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gcc -O3 -ffreestanding -nostdlib -fno-builtin -mno-red-zone -no-pie -Wall -Werror -pedantic -o test-elfloader test-elfloader.c
8 changes: 5 additions & 3 deletions xernel/kernel/src/arch/amd64/idt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::arch::amd64::ports::outb;
use crate::sched::context::CpuContext;
use crate::{arch::amd64::ports::outb, mem::mmap::handle_page_fault};
use core::arch::asm;
use core::mem::size_of;
use core::ptr::addr_of;
Expand Down Expand Up @@ -243,11 +243,13 @@ fn page_fault_handler(frame: CpuContext) {
let addr = read_cr2();
let error_code = PageFaultErrorCode::from_bits_truncate(frame.error_code);

let handled_successfully = handle_page_fault(addr, error_code);
// TODO: this does currently not work when there is no process currently executing ==> we need to propagate the option and don't panic in executing_thread
// NOTE: this might be fixed in the scheduler branch
let handled_successfully = false; // handle_page_fault(addr, error_code);

if !handled_successfully {
dbg!("EXCEPTION: PAGE FAULT");
dbg!("Accessed Address: {:?}", read_cr2());
dbg!("Accessed Address: {:?}", addr);
dbg!("Error Code: {:?}", frame.error_code);
dbg!("{:#?}", frame);

Expand Down
60 changes: 3 additions & 57 deletions xernel/kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,13 @@ use x86_64::instructions::interrupts;
use arch::amd64::gdt;
use arch::amd64::idt;

use x86_64::structures::paging::Page;
use x86_64::structures::paging::PageTableFlags;
use x86_64::structures::paging::Size2MiB;
use x86_64::VirtAddr;

use crate::acpi::hpet;
use crate::arch::amd64::apic;
use crate::cpu::register_cpu;
use crate::cpu::wait_until_cpus_registered;
use crate::cpu::CPU_COUNT;
use crate::fs::vfs;
use crate::fs::vfs::VFS;
use crate::mem::frame::FRAME_ALLOCATOR;
use crate::mem::paging::KERNEL_PAGE_MAPPER;
use crate::sched::process::Process;
use crate::sched::process::KERNEL_PROCESS;
Expand Down Expand Up @@ -161,43 +155,14 @@ extern "C" fn kernel_main() -> ! {

let process = Arc::new(Spinlock::new(Process::new(Some(KERNEL_PROCESS.clone()))));

let _user_task = Thread::new_user_thread(process.clone(), VirtAddr::new(0x200000));

let page = FRAME_ALLOCATOR.lock().allocate_frame::<Size2MiB>().unwrap();
KERNEL_PAGE_MAPPER.lock().map(
page,
Page::from_start_address(VirtAddr::new(0x200000)).unwrap(),
PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE | PageTableFlags::PRESENT,
true,
);
let mut pm = process.lock().get_page_table().unwrap();
pm.map(
page,
Page::from_start_address(VirtAddr::new(0x200000)).unwrap(),
PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE | PageTableFlags::PRESENT,
true,
);

unsafe {
let start_address_fn = test_userspace_fn as usize;

// the `test_userspace_fn` is very small and should fit in 512 bytes
for i in 0..512 {
let ptr = (0x200000 + i) as *mut u8;
let val = (start_address_fn + i) as *mut u8;

ptr.write_volatile(val.read_volatile());
}
}

let test_elf = include_bytes!("../test-elfloader");
let user_task = Thread::new_user_thread_from_elf(process.clone(), test_elf);
let main_task = Thread::kernel_thread_from_fn(kernel_main_task);

let kernel_task = Thread::kernel_thread_from_fn(task1);

let kernel_task2 = Thread::kernel_thread_from_fn(task2);

Scheduler::add_thread_balanced(Arc::new(Spinlock::new(main_task)));
//Scheduler::add_task_balanced(Arc::new(Spinlock::new(user_task)));
Scheduler::add_thread_balanced(Arc::new(Spinlock::new(user_task)));
Scheduler::add_thread_balanced(Arc::new(Spinlock::new(kernel_task)));
Scheduler::add_thread_balanced(Arc::new(Spinlock::new(kernel_task2)));

Expand Down Expand Up @@ -227,25 +192,6 @@ pub fn kernel_main_task() {
}
}

#[naked]
pub extern "C" fn test_userspace_fn() {
//loop {
unsafe {
asm!(
"\
mov rax, 0
mov rdi, 2
mov rsi, 3
mov rdx, 4
syscall
mov rax, 0
",
options(noreturn)
);
}
//}
}

#[no_mangle]
fn task1() {
let mut var = 1;
Expand Down
9 changes: 2 additions & 7 deletions xernel/kernel/src/mem/paging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ impl Pagemap {
self.flush(virt.start_address());
}
}
dbg!("end map");
}

pub fn flush(&self, addr: VirtAddr) {
Expand Down Expand Up @@ -329,7 +330,7 @@ impl Pagemap {
}
}

fn deallocate_pt(pt: *mut PageTable, level: u8) {
pub fn deallocate_pt(pt: *mut PageTable, level: u8) {
let mut frame_allocator = FRAME_ALLOCATOR.lock();
if level == 4 {
for i in 0..256 {
Expand Down Expand Up @@ -367,12 +368,6 @@ impl Pagemap {
}
}

impl Drop for Pagemap {
fn drop(&mut self) {
Self::deallocate_pt(self.page_table, 4);
}
}

pub fn init() {
unsafe {
// create new pagetable and map the kernel + all memory maps in higher half
Expand Down
9 changes: 8 additions & 1 deletion xernel/kernel/src/sched/process.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::sync::Weak;
use core::sync::atomic::{AtomicUsize, Ordering};
use libxernel::syscall::{MapFlags, ProtectionFlags};
use x86_64::structures::paging::{Page, PageSize, PageTableFlags, Size4KiB};
use x86_64::structures::paging::{Page, PageSize, PageTable, PageTableFlags, Size4KiB};
use x86_64::VirtAddr;

use crate::fs::file::File;
Expand Down Expand Up @@ -154,5 +154,12 @@ impl Process {
impl Drop for Process {
fn drop(&mut self) {
self.vm.clean_up();

match &self.page_table {
Some(pt) => {
Pagemap::deallocate_pt(pt.pml4().as_u64() as *mut PageTable, 4);
}
None => {}
}
}
}
115 changes: 114 additions & 1 deletion xernel/kernel/src/sched/thread.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use alloc::sync::Arc;
use elf::abi::{ET_EXEC, PT_LOAD};
use elf::endian::LittleEndian;
use elf::ElfBytes;
use libxernel::sync::Spinlock;
use x86_64::structures::paging::{Page, PageSize, PhysFrame, Size4KiB};
use x86_64::structures::paging::{Page, PageSize, PageTableFlags, PhysFrame, Size4KiB};

use crate::allocator::align_up;
use crate::mem::frame::FRAME_ALLOCATOR;
use crate::mem::paging::KERNEL_PAGE_MAPPER;
use crate::mem::STACK_SIZE;
Expand All @@ -10,6 +14,7 @@ use super::context::CpuContext;
use super::process::{Process, KERNEL_PROCESS};

use core::pin::Pin;
use core::ptr::write_bytes;

use alloc::boxed::Box;
use alloc::sync::Weak;
Expand Down Expand Up @@ -151,6 +156,114 @@ impl Thread {
}
}

pub fn new_user_thread_from_elf(parent_process: Arc<Spinlock<Process>>, elf_data: &[u8]) -> Self {
let thread_stack = parent_process.lock().new_user_stack();
let kernel_stack_end = parent_process.lock().new_kernel_stack();

let mut ctx = CpuContext::new();

ctx.ss = 0x2b; // user stack segment
ctx.cs = 0x33; // user code segment
ctx.rsp = thread_stack as u64;
ctx.rflags = 0x202;

let mut parent = parent_process.lock();

let mut thread = Self {
id: parent.next_tid(),
thread_stack,
process: Arc::downgrade(&parent_process),
status: ThreadStatus::Ready,
priority: ThreadPriority::Normal,
context: ctx,
kernel_stack: Some(Box::pin(KernelStack {
user_space_stack: 0,
kernel_stack_top: kernel_stack_end,
})),
};

parent.unlock();

thread.load_elf(elf_data);

thread
}

fn load_elf(&mut self, elf_data: &[u8]) {
let file = ElfBytes::<LittleEndian>::minimal_parse(elf_data).expect("opening elf file failed");

self.context.rip = file.ehdr.e_entry;

if file.ehdr.e_type != ET_EXEC {
panic!("elf: not an executable");
}

let segments = file.segments().expect("parsing segments failed");

for segment in segments {
if segment.p_type == PT_LOAD {
dbg!("{:#x?}", segment);
let file_size = segment.p_filesz as usize;
let mem_size = segment.p_memsz as usize;
let vaddr = segment.p_vaddr as usize;

assert!(file_size <= mem_size);
assert!(vaddr % Size4KiB::SIZE as usize == 0);

let mem_size = align_up(mem_size, Size4KiB::SIZE as usize);

// TODO: add mappings to Vm
for page in (vaddr..vaddr + mem_size).step_by(Size4KiB::SIZE as usize) {
let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(page as u64)).unwrap();
let frame = FRAME_ALLOCATOR.lock().allocate_frame::<Size4KiB>().unwrap();

let process = self.get_process().unwrap();
let process = process.lock();

dbg!("before mapping");
process.get_page_table().unwrap().map(
frame,
page,
Self::elf_flags_to_pt_flags(segment.p_flags)
| PageTableFlags::PRESENT
| PageTableFlags::USER_ACCESSIBLE
| PageTableFlags::WRITABLE, // TODO: make this depend on the elf flags, currently we need it to write the content of the segment ==> make it read-only after writing
true,
);
dbg!("after mapping");

unsafe {
write_bytes(page.start_address().as_mut_ptr::<u8>(), 0, Size4KiB::SIZE as usize);
}
}

unsafe {
core::ptr::copy_nonoverlapping(
elf_data.as_ptr().add(segment.p_offset as usize),
vaddr as *mut u8,
file_size,
);
}

dbg!("segment loaded");
}
}
}

pub fn elf_flags_to_pt_flags(flags: u32) -> PageTableFlags {
let mut ret = PageTableFlags::empty();

if flags & 1 == 0 {
ret |= PageTableFlags::NO_EXECUTE;
}

if flags & 2 == 2 {
ret |= PageTableFlags::WRITABLE;
}

ret
}

pub fn new_idle_thread() -> Self {
// TODO: don't use a normal kernel task as a huge stack is allocated
let mut thread = Self::kernel_thread_from_fn(idle_thread_fn);
Expand Down
2 changes: 1 addition & 1 deletion xernel/kernel/src/syscall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ extern "sysv64" fn general_syscall_handler(data: SyscallData) -> i64 {
}
None => Err(SyscallError::InvalidArgument),
}
},
}
_ => {
unimplemented!("unknown syscall: {:x?}", data);
}
Expand Down
Binary file added xernel/kernel/test-elfloader
Binary file not shown.
24 changes: 24 additions & 0 deletions xernel/kernel/test-elfloader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

char *message = "hello from userspace";

int syscall(long syscall_number, char *arg) {
int ret;

asm volatile(
"syscall"
: "=a" (ret)
: "a" (syscall_number), "D" (arg)
);

return ret;
}

void _start() {
while (1) {
for (int i = 0; i < 1000000; i++) {
asm volatile("nop");
}

syscall(5, message);
}
}

0 comments on commit f829996

Please sign in to comment.