feat(kernel): implement ELF64 loader, user space paging, and Ring 3 jump via iretq
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
/* opencoreC - Ring 3 User Mode Transition (IRETQ) */
|
||||||
|
.global enter_user_mode
|
||||||
|
.extern set_kernel_stack
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
/*
|
||||||
|
* void enter_user_mode(uint64_t entry_rip, uint64_t user_rsp, uint64_t pml4_paddr, uint64_t kernel_stack_top);
|
||||||
|
* RDI = entry_rip
|
||||||
|
* RSI = user_rsp
|
||||||
|
* RDX = pml4_paddr
|
||||||
|
* RCX = kernel_stack_top
|
||||||
|
*/
|
||||||
|
enter_user_mode:
|
||||||
|
/* 1. Save kernel stack in TSS (RSP0) for future interrupts/syscalls */
|
||||||
|
push rdi
|
||||||
|
push rsi
|
||||||
|
push rdx
|
||||||
|
mov rdi, rcx
|
||||||
|
call set_kernel_stack
|
||||||
|
pop rdx
|
||||||
|
pop rsi
|
||||||
|
pop rdi
|
||||||
|
|
||||||
|
/* 2. Switch to User Address Space (PML4) */
|
||||||
|
mov cr3, rdx
|
||||||
|
|
||||||
|
/* 3. Build IRETQ Stack Frame:
|
||||||
|
* [RSP + 32] = SS (User Data: 0x18 | 3 = 0x1B)
|
||||||
|
* [RSP + 24] = RSP (User Stack Pointer)
|
||||||
|
* [RSP + 16] = RFLAGS (Interrupt Flag Enabled: 0x202)
|
||||||
|
* [RSP + 8] = CS (User Code: 0x20 | 3 = 0x23)
|
||||||
|
* [RSP + 0] = RIP (User Entry Point)
|
||||||
|
*/
|
||||||
|
mov ax, 0x1B
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
|
||||||
|
push 0x1B /* User SS */
|
||||||
|
push rsi /* User RSP */
|
||||||
|
push 0x0202 /* RFLAGS (IF enabled) */
|
||||||
|
push 0x23 /* User CS */
|
||||||
|
push rdi /* User RIP */
|
||||||
|
|
||||||
|
/* 4. Clear general-purpose registers */
|
||||||
|
xor rax, rax
|
||||||
|
xor rbx, rbx
|
||||||
|
xor rcx, rcx
|
||||||
|
xor rdx, rdx
|
||||||
|
xor rsi, rsi
|
||||||
|
xor rdi, rdi
|
||||||
|
xor rbp, rbp
|
||||||
|
xor r8, r8
|
||||||
|
xor r9, r9
|
||||||
|
xor r10, r10
|
||||||
|
xor r11, r11
|
||||||
|
xor r12, r12
|
||||||
|
xor r13, r13
|
||||||
|
xor r14, r14
|
||||||
|
xor r15, r15
|
||||||
|
|
||||||
|
/* 5. Jump to Ring 3 */
|
||||||
|
iretq
|
||||||
@@ -94,7 +94,8 @@ pub unsafe fn init() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_kernel_stack(stack_top: u64) {
|
#[no_mangle]
|
||||||
|
pub extern "C" fn set_kernel_stack(stack_top: u64) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let tss_ptr = core::ptr::addr_of_mut!(TSS);
|
let tss_ptr = core::ptr::addr_of_mut!(TSS);
|
||||||
(*tss_ptr).rsp0 = stack_top;
|
(*tss_ptr).rsp0 = stack_top;
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
//! ELF64 Executable Binary Parser and Loader for Ring 3 User Space
|
||||||
|
|
||||||
|
use crate::mm::vmm::{VMM, PAGE_PRESENT, PAGE_WRITABLE, PAGE_USER};
|
||||||
|
use crate::mm::pfa::{PFA, PAGE_SIZE};
|
||||||
|
use crate::kprintln;
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Elf64Header {
|
||||||
|
pub ident: [u8; 16],
|
||||||
|
pub elf_type: u16,
|
||||||
|
pub machine: u16,
|
||||||
|
pub version: u32,
|
||||||
|
pub entry: u64,
|
||||||
|
pub phoff: u64,
|
||||||
|
pub shoff: u64,
|
||||||
|
pub flags: u32,
|
||||||
|
pub ehsize: u16,
|
||||||
|
pub phentsize: u16,
|
||||||
|
pub phnum: u16,
|
||||||
|
pub shentsize: u16,
|
||||||
|
pub shnum: u16,
|
||||||
|
pub shstrndx: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Elf64ProgramHeader {
|
||||||
|
pub p_type: u32,
|
||||||
|
pub p_flags: u32,
|
||||||
|
pub p_offset: u64,
|
||||||
|
pub p_vaddr: u64,
|
||||||
|
pub p_paddr: u64,
|
||||||
|
pub p_filesz: u64,
|
||||||
|
pub p_memsz: u64,
|
||||||
|
pub p_align: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PT_LOAD: u32 = 1;
|
||||||
|
pub const PF_X: u32 = 1 << 0;
|
||||||
|
pub const PF_W: u32 = 1 << 1;
|
||||||
|
pub const PF_R: u32 = 1 << 2;
|
||||||
|
|
||||||
|
pub struct LoadedProcess {
|
||||||
|
pub entry_rip: u64,
|
||||||
|
pub user_rsp: u64,
|
||||||
|
pub pml4_paddr: u64,
|
||||||
|
pub kernel_stack_top: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn load_elf(elf_bytes: &[u8], hhdm_offset: u64) -> Result<LoadedProcess, &'static str> {
|
||||||
|
if elf_bytes.len() < core::mem::size_of::<Elf64Header>() {
|
||||||
|
return Err("ELF file too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = &*(elf_bytes.as_ptr() as *const Elf64Header);
|
||||||
|
|
||||||
|
// Validate ELF Magic "\x7fELF"
|
||||||
|
if header.ident[0] != 0x7F || header.ident[1] != b'E' || header.ident[2] != b'L' || header.ident[3] != b'F' {
|
||||||
|
return Err("Invalid ELF magic");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be 64-bit (class 2) and x86_64 machine (0x3E)
|
||||||
|
if header.ident[4] != 2 || header.machine != 0x3E {
|
||||||
|
return Err("Not a 64-bit x86_64 ELF");
|
||||||
|
}
|
||||||
|
|
||||||
|
let vmm_ptr = core::ptr::addr_of_mut!(VMM);
|
||||||
|
let pml4_paddr = (*vmm_ptr).create_user_address_space().ok_or("Failed to allocate user PML4")?;
|
||||||
|
|
||||||
|
let phoff = header.phoff as usize;
|
||||||
|
let phentsize = header.phentsize as usize;
|
||||||
|
let phnum = header.phnum as usize;
|
||||||
|
let entry_rip = header.entry;
|
||||||
|
|
||||||
|
for i in 0..phnum {
|
||||||
|
let phdr_ptr = elf_bytes.as_ptr().add(phoff + i * phentsize) as *const Elf64ProgramHeader;
|
||||||
|
let phdr = &*phdr_ptr;
|
||||||
|
|
||||||
|
if phdr.p_type == PT_LOAD {
|
||||||
|
let vaddr_start = phdr.p_vaddr;
|
||||||
|
let memsz = phdr.p_memsz as usize;
|
||||||
|
let filesz = phdr.p_filesz as usize;
|
||||||
|
let file_offset = phdr.p_offset as usize;
|
||||||
|
|
||||||
|
let mut flags = PAGE_PRESENT | PAGE_USER;
|
||||||
|
if (phdr.p_flags & PF_W) != 0 {
|
||||||
|
flags |= PAGE_WRITABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map and copy segment page by page
|
||||||
|
let page_count = (memsz + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||||
|
for page in 0..page_count {
|
||||||
|
let page_vaddr = vaddr_start + (page * PAGE_SIZE) as u64;
|
||||||
|
let page_paddr = (*core::ptr::addr_of_mut!(PFA)).alloc_frame().ok_or("Out of memory for ELF segment")?;
|
||||||
|
|
||||||
|
// Map in user page table
|
||||||
|
(*vmm_ptr).map_page(pml4_paddr, page_vaddr, page_paddr, flags)
|
||||||
|
.map_err(|_| "Failed to map user ELF page")?;
|
||||||
|
|
||||||
|
// Copy data from file
|
||||||
|
let dest = (page_paddr + hhdm_offset) as *mut u8;
|
||||||
|
let page_file_start = page * PAGE_SIZE;
|
||||||
|
if page_file_start < filesz {
|
||||||
|
let copy_len = core::cmp::min(PAGE_SIZE, filesz - page_file_start);
|
||||||
|
let src = elf_bytes.as_ptr().add(file_offset + page_file_start);
|
||||||
|
core::ptr::copy_nonoverlapping(src, dest, copy_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate User Stack: 4 pages at 0x00007FFF_0000_0000
|
||||||
|
let user_stack_base = 0x00007FFF_0000_0000u64;
|
||||||
|
let user_stack_pages = 4;
|
||||||
|
for page in 0..user_stack_pages {
|
||||||
|
let page_vaddr = user_stack_base + (page * PAGE_SIZE) as u64;
|
||||||
|
let page_paddr = (*core::ptr::addr_of_mut!(PFA)).alloc_frame().ok_or("Out of memory for user stack")?;
|
||||||
|
(*vmm_ptr).map_page(pml4_paddr, page_vaddr, page_paddr, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER)
|
||||||
|
.map_err(|_| "Failed to map user stack")?;
|
||||||
|
}
|
||||||
|
let user_rsp = user_stack_base + (user_stack_pages * PAGE_SIZE) as u64 - 16;
|
||||||
|
|
||||||
|
// Allocate Kernel Stack (TSS RSP0): 4 pages
|
||||||
|
let kstack_frame = (*core::ptr::addr_of_mut!(PFA)).alloc_frame().ok_or("Out of memory for kernel stack")?;
|
||||||
|
let kernel_stack_top = kstack_frame + hhdm_offset + (PAGE_SIZE as u64) - 16;
|
||||||
|
|
||||||
|
kprintln!("[LOADER] ELF Loaded: Entry RIP: {:#x}, User RSP: {:#x}, User PML4: {:#x}",
|
||||||
|
entry_rip, user_rsp, pml4_paddr);
|
||||||
|
|
||||||
|
Ok(LoadedProcess {
|
||||||
|
entry_rip,
|
||||||
|
user_rsp,
|
||||||
|
pml4_paddr,
|
||||||
|
kernel_stack_top,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod elf;
|
||||||
+41
-21
@@ -7,6 +7,7 @@ pub mod arch;
|
|||||||
pub mod mm;
|
pub mod mm;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
pub mod sched;
|
pub mod sched;
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
use limine_requests::*;
|
use limine_requests::*;
|
||||||
@@ -14,6 +15,16 @@ use limine_requests::*;
|
|||||||
// Include assembly trampolines directly into the binary
|
// Include assembly trampolines directly into the binary
|
||||||
core::arch::global_asm!(include_str!("../asm/context.S"));
|
core::arch::global_asm!(include_str!("../asm/context.S"));
|
||||||
core::arch::global_asm!(include_str!("../asm/syscall_entry.S"));
|
core::arch::global_asm!(include_str!("../asm/syscall_entry.S"));
|
||||||
|
core::arch::global_asm!(include_str!("../asm/ring3_enter.S"));
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn enter_user_mode(
|
||||||
|
entry_rip: u64,
|
||||||
|
user_rsp: u64,
|
||||||
|
pml4_paddr: u64,
|
||||||
|
kernel_stack_top: u64,
|
||||||
|
) -> !;
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn _start() -> ! {
|
pub extern "C" fn _start() -> ! {
|
||||||
@@ -64,26 +75,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
// 7. Initialize Scheduler
|
// 7. Initialize Scheduler
|
||||||
sched::init();
|
sched::init();
|
||||||
|
|
||||||
// 8. Inspect Boot Modules (Initial Userspace Servers)
|
// 8. Self-Test Fast-Path IPC Ping
|
||||||
unsafe {
|
|
||||||
let mod_resp = MODULE_REQUEST.response;
|
|
||||||
if !mod_resp.is_null() {
|
|
||||||
let count = (*mod_resp).module_count;
|
|
||||||
kprintln!("[BOOT] Found {} initial userspace module(s):", count);
|
|
||||||
for i in 0..count {
|
|
||||||
let mod_file = *(*mod_resp).modules.add(i as usize);
|
|
||||||
let path_cstr = (*mod_file).path;
|
|
||||||
let path_len = (0..256).find(|&j| *path_cstr.add(j) == 0).unwrap_or(0);
|
|
||||||
let path = core::str::from_utf8(core::slice::from_raw_parts(path_cstr, path_len)).unwrap_or("unknown");
|
|
||||||
kprintln!(" - Module [{}]: {} (size: {} bytes, addr: {:p})",
|
|
||||||
i, path, (*mod_file).size, (*mod_file).address);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
kprintln!("[BOOT] No external modules passed by bootloader.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. Kernel Self-Test: Fast-Path IPC Ping
|
|
||||||
kprintln!("[TEST] Executing Kernel Fast-Path IPC Self-Test...");
|
kprintln!("[TEST] Executing Kernel Fast-Path IPC Self-Test...");
|
||||||
let (status, resp_op, val) = ipc::fastpath::handle_fastpath_call(
|
let (status, resp_op, val) = ipc::fastpath::handle_fastpath_call(
|
||||||
ipc::fastpath::CAP_KERNEL_CONTROL,
|
ipc::fastpath::CAP_KERNEL_CONTROL,
|
||||||
@@ -92,7 +84,35 @@ pub extern "C" fn _start() -> ! {
|
|||||||
);
|
);
|
||||||
kprintln!("[TEST] IPC Fast-Path Ping Result: status={}, opcode={:#x}, val={}", status, resp_op, val);
|
kprintln!("[TEST] IPC Fast-Path Ping Result: status={}, opcode={:#x}, val={}", status, resp_op, val);
|
||||||
|
|
||||||
kprintln!("[KERNEL] Initialization complete. Entering kernel idle loop.\n");
|
// 9. Inspect and Load Initial Userspace Process (Ring 3 Transition)
|
||||||
|
unsafe {
|
||||||
|
let mod_resp = MODULE_REQUEST.response;
|
||||||
|
if !mod_resp.is_null() && (*mod_resp).module_count > 0 {
|
||||||
|
kprintln!("[BOOT] Loading root userspace module [0] into Ring 3...");
|
||||||
|
let mod_file = *(*mod_resp).modules;
|
||||||
|
let elf_slice = core::slice::from_raw_parts((*mod_file).address, (*mod_file).size as usize);
|
||||||
|
|
||||||
|
match loader::elf::load_elf(elf_slice, hhdm_offset) {
|
||||||
|
Ok(proc) => {
|
||||||
|
kprintln!("[BOOT] Transitioning CPU to Ring 3 (User Mode) via IRETQ...");
|
||||||
|
kprintln!("-------------------------------------------------------");
|
||||||
|
enter_user_mode(
|
||||||
|
proc.entry_rip,
|
||||||
|
proc.user_rsp,
|
||||||
|
proc.pml4_paddr,
|
||||||
|
proc.kernel_stack_top,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
kprintln!("[ERROR] Failed to load ELF process: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
kprintln!("[BOOT] No userspace modules detected. Staying in Ring 0 idle loop.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kprintln!("[KERNEL] Entering idle loop.\n");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|||||||
+38
-3
@@ -29,10 +29,43 @@ impl VirtualMemoryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn phys_to_virt<T>(&self, paddr: u64) -> *mut T {
|
pub fn phys_to_virt<T>(&self, paddr: u64) -> *mut T {
|
||||||
(paddr + self.hhdm_offset) as *mut T
|
(paddr + self.hhdm_offset) as *mut T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_cr3(&self) -> u64 {
|
||||||
|
let cr3: u64;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("mov {0}, cr3", out(reg) cr3, options(nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
cr3
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn load_cr3(&self, pml4_paddr: u64) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("mov cr3, {0}", in(reg) pml4_paddr, options(nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new user PML4 with higher-half kernel space cloned
|
||||||
|
pub unsafe fn create_user_address_space(&self) -> Option<u64> {
|
||||||
|
let pfa_ptr = core::ptr::addr_of_mut!(PFA);
|
||||||
|
let user_pml4_paddr = (*pfa_ptr).alloc_frame()?;
|
||||||
|
let user_pml4 = self.phys_to_virt::<PageTable>(user_pml4_paddr);
|
||||||
|
|
||||||
|
let current_cr3 = self.read_cr3() & PAGE_MASK;
|
||||||
|
let kernel_pml4 = self.phys_to_virt::<PageTable>(current_cr3);
|
||||||
|
|
||||||
|
// Copy kernel mappings (higher half: 256..512)
|
||||||
|
for i in 256..512 {
|
||||||
|
(*user_pml4).entries[i] = (*kernel_pml4).entries[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(user_pml4_paddr)
|
||||||
|
}
|
||||||
|
|
||||||
/// Map a single 4KiB page into the specified PML4
|
/// Map a single 4KiB page into the specified PML4
|
||||||
pub unsafe fn map_page(
|
pub unsafe fn map_page(
|
||||||
&self,
|
&self,
|
||||||
@@ -63,8 +96,10 @@ impl VirtualMemoryManager {
|
|||||||
// 4. Map the physical page in the PT
|
// 4. Map the physical page in the PT
|
||||||
(*pt).entries[pt_idx] = (paddr & PAGE_MASK) | flags | PAGE_PRESENT;
|
(*pt).entries[pt_idx] = (paddr & PAGE_MASK) | flags | PAGE_PRESENT;
|
||||||
|
|
||||||
// Invalidate TLB for this virtual address
|
// Invalidate TLB for this virtual address if current address space
|
||||||
core::arch::asm!("invlpg [{0}]", in(reg) vaddr, options(nostack, preserves_flags));
|
if (self.read_cr3() & PAGE_MASK) == pml4_paddr {
|
||||||
|
core::arch::asm!("invlpg [{0}]", in(reg) vaddr, options(nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user