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

@@ -1,3 +1,3 @@
[target.riscv64gc-unknown-none-elf] [target.riscv64gc-unknown-none-elf]
runner = "qemu-system-riscv64 -machine virt -nographic -bios default -kernel" runner = "qemu-system-riscv64 -machine virt -nographic -bios default -kernel"
rustflags = ["-C", "link-arg=-Tcrates/adapter-riscv/linker.ld", "-C", "panic=abort"] rustflags = ["-C", "panic=abort"]

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]) { let entry_va = match elf::parse(&init_buf[..file_size]) {
Ok(elf_info) => { Ok(elf_info) => {
uart.write_str("k-os: ELF entry point = "); uart.write_str("k-os: ELF entry point = ");
uart.write_hex(elf_info.entry_point); uart.write_hex(elf_info.entry_point);
uart.put_char(b'\n'); uart.put_char(b'\n');
// Map each segment into user page table // Create a user page table with kernel mappings
let pages = (file_size + 4095) / 4096; let user_pt = mmu::create_user_page_table(
let _user_pt = mmu::create_user_page_table(
kernel_pt, kernel_pt,
types::address::PhysicalAddress::from_raw(init_base), types::address::PhysicalAddress::from_raw(0),
pages, 0,
types::address::VirtualAddress::from_raw(INIT_LOAD_VA), types::address::VirtualAddress::from_raw(0x10000),
); );
use ::platform::Platform; // Map each PT_LOAD segment at its virtual address
kernel_ref().platform_mut().configure_protection( 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), types::id::ProcessId::from_raw(0),
&[], pt_addr,
); );
elf_info.entry_point elf_info.entry_point
} }
Err(_) => { Err(_) => {
uart.write_str("k-os: not valid ELF — loading as raw binary\n"); uart.write_str("k-os: not valid ELF — loading as raw binary\n");
let _user_pt = mmu::create_user_page_table( load_fallback_init(kernel_pt)
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
} }
}; };
@@ -209,7 +263,7 @@ fn launch_init(uart: &Uart, entry_va: usize) -> ! {
if let Some(ctx) = kern.thread_context_mut(tid) { if let Some(ctx) = kern.thread_context_mut(tid) {
ctx.set_pc(entry_va); 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; let mut sstatus: usize = 0;
sstatus |= 1 << 5; // SPIE sstatus |= 1 << 5; // SPIE
ctx.set_status(sstatus); 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) }; unsafe { core::arch::asm!("mv {0}, sp", out(reg) kernel_sp) };
trap::set_kernel_stack(kernel_sp); trap::set_kernel_stack(kernel_sp);
let user_sp = INIT_LOAD_VA - 4096; let user_sp = INIT_LOAD_VA;
unsafe { unsafe {
core::arch::asm!( core::arch::asm!(
"csrc sstatus, {spp}", "csrc sstatus, {spp}",

View File

@@ -30,6 +30,10 @@ impl RiscVPlatform {
self.page_tables[pid.raw() as usize] 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 { fn ensure_page_table(&mut self, pid: ProcessId) -> &mut mmu::PageTable {
let idx = pid.raw() as usize; let idx = pid.raw() as usize;
if self.page_tables[idx] == 0 { if self.page_tables[idx] == 0 {

View File

@@ -7,10 +7,6 @@ pub mod shutdown;
pub mod supervisor; pub mod supervisor;
pub fn run() -> ! { pub fn run() -> ! {
let create_cap = types::bounded::CapSlot::new(0) // Phase 1: just prove we can run from disk and exit cleanly
.unwrap_or_else(|| unreachable!()); userspace::syscall::exit();
let result = boot::boot(&manifest::SNAKE_MANIFEST, create_cap);
let mut sup = boot::create_supervisor(&manifest::SNAKE_MANIFEST, &result);
sup.run(create_cap);
} }

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

@@ -0,0 +1,25 @@
OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS {
. = 0x10000;
.text : {
*(.text.entry)
*(.text .text.*)
}
. = ALIGN(4096);
.rodata : { *(.rodata .rodata.*) }
. = ALIGN(4096);
.data : { *(.data .data.*) }
. = ALIGN(4096);
.bss : {
*(.bss .bss.*)
*(.sbss .sbss.*)
}
/DISCARD/ : { *(.eh_frame) }
}