feat: userspace linker script at 0x10000, proper ELF segment loading, full disk boot pipeline working

- Userspace linker script: apps/servers link at 0x10000 (user VA)
- Adapter linker script: kernel links at 0x80200000 (S-mode)
- Each uses build.rs to emit its own -T flag, global config only sets panic=abort
- ELF loader: page-aligns segment mappings, handles non-aligned vaddrs
- Stack: 4 pages (16K) mapped below program
- SP set to top of stack (0x10000), not bottom
- FAT16 disk image forced (mformat was creating FAT32)
- End-to-end verified: VirtIO → FAT16 → ELF → userspace → ecall → exit
This commit is contained in:
2026-10-18 04:51:07 +02:00
parent 924baede9e
commit 29cd0e7dfe
7 changed files with 122 additions and 33 deletions

View File

@@ -0,0 +1,5 @@
fn main() {
let ld = std::path::Path::new("linker.ld").canonicalize().unwrap();
println!("cargo:rustc-link-arg=-T{}", ld.display());
println!("cargo:rerun-if-changed=linker.ld");
}

View File

@@ -113,46 +113,100 @@ fn load_init_from_disk(uart: &Uart, kernel_pt: &mmu::PageTable) -> usize {
}
};
// Parse ELF
// Parse ELF and map segments
let entry_va = match elf::parse(&init_buf[..file_size]) {
Ok(elf_info) => {
uart.write_str("k-os: ELF entry point = ");
uart.write_hex(elf_info.entry_point);
uart.put_char(b'\n');
// Map each segment into user page table
let pages = (file_size + 4095) / 4096;
let _user_pt = mmu::create_user_page_table(
// Create a user page table with kernel mappings
let user_pt = mmu::create_user_page_table(
kernel_pt,
types::address::PhysicalAddress::from_raw(init_base),
pages,
types::address::VirtualAddress::from_raw(INIT_LOAD_VA),
types::address::PhysicalAddress::from_raw(0),
0,
types::address::VirtualAddress::from_raw(0x10000),
);
use ::platform::Platform;
kernel_ref().platform_mut().configure_protection(
// Map each PT_LOAD segment at its virtual address
for segment in elf_info.segments() {
let page_start = segment.vaddr & !0xFFF;
let offset_in_page = segment.vaddr & 0xFFF;
let total_size = offset_in_page + segment.mem_size;
let seg_pages = (total_size + 4095) / 4096;
for p in 0..seg_pages {
let page = mmu::allocator::alloc_zeroed_page();
let page_va_start = page_start + p * 4096;
let seg_start_in_file = if p == 0 { 0 } else { p * 4096 - offset_in_page };
let dest_offset = if p == 0 { offset_in_page } else { 0 };
let copy_len = if seg_start_in_file < segment.file_size {
(segment.file_size - seg_start_in_file).min(4096 - dest_offset)
} else {
0
};
if copy_len > 0 {
let src = segment.file_offset + seg_start_in_file;
if src + copy_len <= file_size {
unsafe {
core::ptr::copy_nonoverlapping(
init_buf.as_ptr().add(src),
(page as *mut u8).add(dest_offset),
copy_len,
);
}
}
}
let flags = if segment.flags.execute {
mmu::Sv39Flags::user_rx()
} else if segment.flags.write {
mmu::Sv39Flags::user_rw()
} else {
mmu::Sv39Flags::user_rx()
};
mmu::map(user_pt, mmu::Mapping {
va: types::address::VirtualAddress::from_raw(page_va_start),
pa: types::address::PhysicalAddress::from_raw(page),
pages: 1,
flags,
});
}
uart.write_str("k-os: mapped segment at ");
uart.write_hex(segment.vaddr);
uart.write_str(" (");
uart.write_dec(seg_pages);
uart.write_str(" pages)\n");
}
// Map stack pages below the lowest segment (4 pages = 16K)
let stack_pages = 4;
for i in 0..stack_pages {
let stack_page = mmu::allocator::alloc_zeroed_page();
let stack_va = 0x10000 - (stack_pages - i) * 4096;
mmu::map(user_pt, mmu::Mapping {
va: types::address::VirtualAddress::from_raw(stack_va),
pa: types::address::PhysicalAddress::from_raw(stack_page),
pages: 1,
flags: mmu::Sv39Flags::user_rw(),
});
}
// Store page table for init process
let kern = kernel_ref();
let pt_addr = user_pt as *const mmu::PageTable as usize;
kern.platform_mut().set_page_table(
types::id::ProcessId::from_raw(0),
&[],
pt_addr,
);
elf_info.entry_point
}
Err(_) => {
uart.write_str("k-os: not valid ELF — loading as raw binary\n");
let _user_pt = mmu::create_user_page_table(
kernel_pt,
types::address::PhysicalAddress::from_raw(init_base),
(file_size + 4095) / 4096,
types::address::VirtualAddress::from_raw(INIT_LOAD_VA),
);
use ::platform::Platform;
kernel_ref().platform_mut().configure_protection(
types::id::ProcessId::from_raw(0),
&[],
);
INIT_LOAD_VA
load_fallback_init(kernel_pt)
}
};
@@ -209,7 +263,7 @@ fn launch_init(uart: &Uart, entry_va: usize) -> ! {
if let Some(ctx) = kern.thread_context_mut(tid) {
ctx.set_pc(entry_va);
ctx.registers_mut()[2] = INIT_LOAD_VA - 4096;
ctx.registers_mut()[2] = INIT_LOAD_VA;
let mut sstatus: usize = 0;
sstatus |= 1 << 5; // SPIE
ctx.set_status(sstatus);
@@ -229,7 +283,7 @@ fn launch_init(uart: &Uart, entry_va: usize) -> ! {
unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) };
trap::set_kernel_stack(kernel_sp);
let user_sp = INIT_LOAD_VA - 4096;
let user_sp = INIT_LOAD_VA;
unsafe {
core::arch::asm!(
"csrc sstatus, {spp}",

View File

@@ -30,6 +30,10 @@ impl RiscVPlatform {
self.page_tables[pid.raw() as usize]
}
pub fn set_page_table(&mut self, pid: ProcessId, addr: usize) {
self.page_tables[pid.raw() as usize] = addr;
}
fn ensure_page_table(&mut self, pid: ProcessId) -> &mut mmu::PageTable {
let idx = pid.raw() as usize;
if self.page_tables[idx] == 0 {