Compare commits

..

4 Commits

39 changed files with 2898 additions and 997 deletions
Generated
+22 -1
View File
@@ -2,6 +2,13 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "client"
version = "0.1.0"
dependencies = [
"dusk-sys",
]
[[package]] [[package]]
name = "dusk" name = "dusk"
version = "0.1.0" version = "0.1.0"
@@ -10,11 +17,25 @@ dependencies = [
] ]
[[package]] [[package]]
name = "init" name = "dusk-sys"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "echo"
version = "0.1.0"
dependencies = [
"dusk-sys",
]
[[package]] [[package]]
name = "limine" name = "limine"
version = "0.6.5" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29363c0f37e66e18575fadf7141c56ee7ea04ae5fecbeb25eff303f77af203a9" checksum = "29363c0f37e66e18575fadf7141c56ee7ea04ae5fecbeb25eff303f77af203a9"
[[package]]
name = "omega3"
version = "0.1.0"
dependencies = [
"dusk-sys",
]
+1 -1
View File
@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[workspace] [workspace]
members = [".", "userspace/init"] members = [".", "userspace/*"]
[dependencies] [dependencies]
limine = "0.6.5" limine = "0.6.5"
+7 -3
View File
@@ -67,10 +67,14 @@ prepare-bin-files:
mkdir -p ${INITRAMFS_PATH} mkdir -p ${INITRAMFS_PATH}
compile-user: compile-user:
RUSTFLAGS="-C relocation-model=static" cargo build --package init ${USERSPACE_CARGO_OPTS} RUSTFLAGS="-C relocation-model=static" cargo build --package omega3 ${USERSPACE_CARGO_OPTS}
RUSTFLAGS="-C relocation-model=static" cargo build --package client ${USERSPACE_CARGO_OPTS}
RUSTFLAGS="-C relocation-model=static" cargo build --package echo ${USERSPACE_CARGO_OPTS}
copy-initramfs-files: compile-user copy-initramfs-files: compile-user
cp -v target/${ARCH}-unknown-none/${MODE}/init ${INITRAMFS_PATH}/init.elf cp -v target/${ARCH}-unknown-none/${MODE}/omega3 ${INITRAMFS_PATH}/omega3.elf
cp -v target/${ARCH}-unknown-none/${MODE}/client ${INITRAMFS_PATH}/client.elf
cp -v target/${ARCH}-unknown-none/${MODE}/echo ${INITRAMFS_PATH}/echo.elf
compile-initramfs: copy-initramfs-files compile-initramfs: copy-initramfs-files
(cd ${INITRAMFS_PATH} && find . -mindepth 1 | cpio -o -H newc) > ${ARTIFACTS_PATH}/initramfs.img (cd ${INITRAMFS_PATH} && find . -mindepth 1 | cpio -o -H newc) > ${ARTIFACTS_PATH}/initramfs.img
@@ -133,7 +137,7 @@ compile-binaries:
ovmf-x86_64: ovmf-x86_64:
mkdir -p ovmf/ovmf-x86_64 mkdir -p ovmf/ovmf-x86_64
@if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \ @if [ ! -f "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \ cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
fi fi
-2
View File
@@ -36,7 +36,6 @@ const APIC_TIMER_INITIAL_COUNT: u32 = 0x380;
const APIC_TIMER_CURRENT_COUNT: u32 = 0x390; const APIC_TIMER_CURRENT_COUNT: u32 = 0x390;
const APIC_TIMER_DIVIDE_CONFIG: u32 = 0x3E0; const APIC_TIMER_DIVIDE_CONFIG: u32 = 0x3E0;
#[derive(Debug)]
enum LocalApicAccess { enum LocalApicAccess {
X2Apic, X2Apic,
XApic, XApic,
@@ -88,7 +87,6 @@ pub enum LocalApicError {
NotBootSystemProcessor, NotBootSystemProcessor,
} }
#[derive(Debug)]
pub struct LocalApic { pub struct LocalApic {
id: u32, id: u32,
access: LocalApicAccess, access: LocalApicAccess,
+1 -1
View File
@@ -103,7 +103,7 @@ pub enum CpuFeaturesError {
InvalidVirtualAddressWidth, InvalidVirtualAddressWidth,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub(crate) struct CpuFeatures { pub(crate) struct CpuFeatures {
pub nx_supported: bool, pub nx_supported: bool,
pub nx_enabled: bool, pub nx_enabled: bool,
+29 -24
View File
@@ -1,35 +1,40 @@
use crate::arch::{ use super::idt::{self, InterruptFrame, stub_no_err};
apic, timer, use crate::arch::{apic, timer};
x86_64::interrupts::idt::{self, InterruptStackFrame},
};
pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1; pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1;
pub const APIC_TIMER_VECTOR: u8 = 0xFD; pub const APIC_TIMER_VECTOR: u8 = 0xFD;
pub const APIC_ERROR_VECTOR: u8 = 0xFE; pub const APIC_ERROR_VECTOR: u8 = 0xFE;
pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF; pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF;
extern "x86-interrupt" fn error_handler(_frame: InterruptStackFrame) { stub_no_err!(stub_pit_calibration, 0xF1);
apic::record_error(); stub_no_err!(stub_apic_timer, 0xFD);
apic::end_of_interrupt(); stub_no_err!(stub_apic_error, 0xFE);
} stub_no_err!(stub_apic_spurious, 0xFF);
extern "x86-interrupt" fn timer_handler(_frame: InterruptStackFrame) { pub(super) fn handle(frame: &mut InterruptFrame) {
apic::record_timer(); match frame.vector as u8 {
apic::end_of_interrupt(); PIT_CALIBRATION_VECTOR => {
} timer::record_pit_calibration();
apic::end_of_interrupt();
extern "x86-interrupt" fn pit_calibration_handler(_frame: InterruptStackFrame) { }
timer::record_pit_calibration(); APIC_TIMER_VECTOR => {
apic::end_of_interrupt(); apic::record_timer();
} apic::end_of_interrupt();
}
extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame) { APIC_ERROR_VECTOR => {
// No EOI apic::record_error();
apic::end_of_interrupt();
}
APIC_SPURIOUS_VECTOR => {
// No EOI
}
_ => {}
}
} }
pub(super) fn install(idt: &mut idt::Idt) { pub(super) fn install(idt: &mut idt::Idt) {
idt.set_handler(PIT_CALIBRATION_VECTOR, pit_calibration_handler, 0); idt.set_handler(PIT_CALIBRATION_VECTOR, stub_pit_calibration, 0);
idt.set_handler(APIC_ERROR_VECTOR, error_handler, 0); idt.set_handler(APIC_ERROR_VECTOR, stub_apic_error, 0);
idt.set_handler(APIC_TIMER_VECTOR, timer_handler, 0); idt.set_handler(APIC_TIMER_VECTOR, stub_apic_timer, 0);
idt.set_handler(APIC_SPURIOUS_VECTOR, spurious_handler, 0); idt.set_handler(APIC_SPURIOUS_VECTOR, stub_apic_spurious, 0);
} }
+101 -65
View File
@@ -1,79 +1,115 @@
use core::arch::asm; use core::arch::asm;
use super::idt::{self, InterruptStackFrame}; use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err};
use crate::{hcf, println}; use crate::{
hcf, println,
task::tcb::{ExitReason, Fault},
};
macro_rules! fatal_without_error_code { stub_no_err!(stub_divide_error, 0);
($handler:ident, $name:literal) => { stub_no_err!(stub_debug, 1);
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) { stub_no_err!(stub_non_maskable_interrupt, 2);
fatal_exception($name, &frame, None); stub_no_err!(stub_breakpoint, 3);
stub_no_err!(stub_invalid_opcode, 6);
stub_no_err!(stub_device_not_available, 7);
stub_err!(stub_double_fault, 8);
stub_err!(stub_invalid_tss, 10);
stub_err!(stub_segment_not_present, 11);
stub_err!(stub_stack_segment_fault, 12);
stub_err!(stub_general_protection, 13);
stub_err!(stub_page_fault, 14);
stub_no_err!(stub_x87_floating_point, 16);
stub_err!(stub_alignment_check, 17);
stub_no_err!(stub_machine_check, 18);
stub_no_err!(stub_simd_floating_point, 19);
stub_no_err!(stub_user_test_exit, 0x80);
const EXCEPTION_NAMES: [&str; 32] = [
"DIVIDE ERROR",
"DEBUG",
"NON-MASKABLE INTERRUPT",
"BREAKPOINT",
"OVERFLOW",
"BOUND RANGE EXCEEDED",
"INVALID OPCODE",
"DEVICE NOT AVAILABLE",
"DOUBLE FAULT",
"COPROCESSOR SEGMENT OVERRUN",
"INVALID TSS",
"SEGMENT NOT PRESENT",
"STACK-SEGMENT FAULT",
"GENERAL PROTECTION FAULT",
"PAGE FAULT",
"RESERVED",
"x87 FLOATING-POINT EXCEPTION",
"ALIGNMENT CHECK",
"MACHINE CHECK",
"SIMD FLOATING-POINT EXCEPTION",
"VIRTUALIZATION EXCEPTION",
"CONTROL PROTECTION EXCEPTION",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"RESERVED",
"HYPERVISOR INJECTION EXCEPTION",
"VMM COMMUNICATION EXCEPTION",
"SECURITY EXCEPTION",
"RESERVED",
];
pub(super) fn handle(frame: &mut InterruptFrame) {
let is_user = frame.stack_frame.code_segment & 0b11 == 3;
let vector = frame.vector as u8;
let name = EXCEPTION_NAMES
.get(vector as usize)
.copied()
.unwrap_or("UNKNOWN EXCEPTION");
if !is_user {
if vector == 14 {
report_exception(name, &frame.stack_frame, Some(frame.error_code));
println!("Faulting address: {:#X}", read_cr2());
print_page_fault_error(frame.error_code);
hcf();
} }
};
}
macro_rules! fatal_with_error_code { fatal_exception(name, &frame.stack_frame, Some(frame.error_code));
($handler:ident, $name:literal) => {
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) {
fatal_exception($name, &frame, Some(error_code));
}
};
}
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
report_exception("BREAKPOINT", &frame, None);
}
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
report_exception("PAGE FAULT", &frame, Some(error_code));
println!("Faulting address: {:#X}", read_cr2());
print_page_fault_error(error_code);
hcf();
}
fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR");
fatal_without_error_code!(debug_handler, "DEBUG EXCEPTION");
fatal_without_error_code!(non_maskable_interrupt_handler, "NON-MASKABLE INTERRUPT");
fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE");
fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE");
fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION");
fatal_without_error_code!(machine_check_handler, "MACHINE CHECK");
fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION");
fatal_with_error_code!(double_fault_handler, "DOUBLE FAULT");
fatal_with_error_code!(invalid_tss_handler, "INVALID TSS");
fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT");
fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
fatal_with_error_code!(general_protection_handler, "GENERAL PROTECTION FAULT");
fatal_with_error_code!(alignment_check_handler, "ALIGNMENT CHECK");
extern "x86-interrupt" fn user_test_exit_handler(frame: InterruptStackFrame) {
if frame.code_segment & 0b11 != 3 {
panic!("user_test_exit_handler called from kernel");
} }
println!("User test exit"); let fault = match vector {
hcf(); // Page fault, GPF, Stack/Segment faults -> SegmentationFault
11 | 12 | 13 | 14 => Fault::SegmentationFault,
// Invalid Opcode -> IllegalInstruction
6 => Fault::IllegalInstruction,
// Divide by zero, Alignment check, SIMD/x87 -> Abort
0 | 16 | 17 | 19 => Fault::Abort,
_ => Fault::Abort,
};
crate::task::scheduler::exit_current(ExitReason::Fault(fault));
} }
pub(super) fn install(idt: &mut idt::Idt) { pub(super) fn install(idt: &mut idt::Idt) {
idt.set_handler(0, divide_error_handler, 0); idt.set_handler(0, stub_divide_error, 0);
idt.set_handler(1, debug_handler, 0); idt.set_handler(1, stub_debug, 0);
idt.set_handler(2, non_maskable_interrupt_handler, 0); idt.set_handler(2, stub_non_maskable_interrupt, 0);
idt.set_user_handler(3, breakpoint_handler, 0); idt.set_user_handler(3, stub_breakpoint, 0);
idt.set_handler(6, invalid_opcode_handler, 0); idt.set_handler(6, stub_invalid_opcode, 0);
idt.set_handler(7, device_not_available_handler, 0); idt.set_handler(7, stub_device_not_available, 0);
idt.set_error_code_handler(8, double_fault_handler, 1); idt.set_handler(8, stub_double_fault, 1);
idt.set_error_code_handler(10, invalid_tss_handler, 0); idt.set_handler(10, stub_invalid_tss, 0);
idt.set_error_code_handler(11, segment_not_present_handler, 0); idt.set_handler(11, stub_segment_not_present, 0);
idt.set_error_code_handler(12, stack_segment_fault_handler, 0); idt.set_handler(12, stub_stack_segment_fault, 0);
idt.set_error_code_handler(13, general_protection_handler, 0); idt.set_handler(13, stub_general_protection, 0);
idt.set_error_code_handler(14, page_fault_handler, 0); idt.set_handler(14, stub_page_fault, 0);
idt.set_handler(16, x87_floating_point_handler, 0); idt.set_handler(16, stub_x87_floating_point, 0);
idt.set_error_code_handler(17, alignment_check_handler, 0); idt.set_handler(17, stub_alignment_check, 0);
idt.set_handler(18, machine_check_handler, 0); idt.set_handler(18, stub_machine_check, 0);
idt.set_handler(19, simd_floating_point_handler, 0); idt.set_handler(19, stub_simd_floating_point, 0);
idt.set_user_handler(0x80, user_test_exit_handler, 0); idt.set_user_handler(0x80, stub_user_test_exit, 0);
} }
fn read_cr2() -> u64 { fn read_cr2() -> u64 {
+133 -16
View File
@@ -18,7 +18,7 @@ struct IdtEntry {
impl IdtEntry { impl IdtEntry {
const fn missing() -> Self { const fn missing() -> Self {
return Self { Self {
offset_low: 0, offset_low: 0,
code_selector: 0, code_selector: 0,
ist: 0, ist: 0,
@@ -26,7 +26,7 @@ impl IdtEntry {
offset_middle: 0, offset_middle: 0,
offset_high: 0, offset_high: 0,
reserved: 0, reserved: 0,
}; }
} }
} }
@@ -38,7 +38,7 @@ struct IdtPointer {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub(super) struct InterruptStackFrame { pub struct InterruptStackFrame {
pub instruction_pointer: VirtualAddr, pub instruction_pointer: VirtualAddr,
pub code_segment: u64, pub code_segment: u64,
pub cpu_flags: u64, pub cpu_flags: u64,
@@ -46,14 +46,36 @@ pub(super) struct InterruptStackFrame {
pub stack_segment: u64, pub stack_segment: u64,
} }
#[repr(C)]
#[derive(Debug)]
pub struct InterruptFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rsi: u64,
pub rdi: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub rbx: u64,
pub rbp: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub vector: u64,
pub error_code: u64,
pub stack_frame: InterruptStackFrame,
}
const INTERRUPT_GATE: u8 = 0b1110; const INTERRUPT_GATE: u8 = 0b1110;
const PRESENT: u8 = 1 << 7; const PRESENT: u8 = 1 << 7;
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE; const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
const USER_DPL: u8 = 3 << 5; const USER_DPL: u8 = 3 << 5;
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE; const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame); pub(super) type RawHandler = unsafe extern "C" fn();
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
pub(super) struct Idt { pub(super) struct Idt {
entries: [IdtEntry; 256], entries: [IdtEntry; 256],
@@ -66,20 +88,11 @@ impl Idt {
} }
} }
pub(super) fn set_handler(&mut self, vector: u8, handler: Handler, ist: u8) { pub(super) fn set_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE); self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
} }
pub(super) fn set_error_code_handler( pub(super) fn set_user_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
&mut self,
vector: u8,
handler: ErrorCodeHandler,
ist: u8,
) {
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
}
pub(super) fn set_user_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE); self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE);
} }
@@ -101,6 +114,110 @@ static mut IDT: Idt = Idt::new();
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16); const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10); const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40); const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
const _: () = assert!(core::mem::size_of::<InterruptFrame>() == 176);
const _: () = assert!(core::mem::offset_of!(InterruptFrame, stack_frame) == 136);
#[unsafe(naked)]
pub(super) unsafe extern "C" fn interrupt_common() {
core::arch::naked_asm!(
"push r15",
"push r14",
"push r13",
"push r12",
"push rbp",
"push rbx",
"push r11",
"push r10",
"push r9",
"push r8",
"push rdi",
"push rsi",
"push rdx",
"push rcx",
"push rax",
// Check CS: bit 0 and 1 are CPL. If CPL != 0 (user mode), swapgs
"test byte ptr [rsp + 144], 3",
"jz 1f",
"swapgs",
"1:",
"mov rdi, rsp",
"cld",
"call {dispatch}",
// Check CS: if returning to user mode, swapgs
"test byte ptr [rsp + 144], 3",
"jz 2f",
"swapgs",
"2:",
"pop rax",
"pop rcx",
"pop rdx",
"pop rsi",
"pop rdi",
"pop r8",
"pop r9",
"pop r10",
"pop r11",
"pop rbx",
"pop rbp",
"pop r12",
"pop r13",
"pop r14",
"pop r15",
"add rsp, 16",
"iretq",
dispatch = sym interrupt_dispatch,
);
}
extern "C" fn interrupt_dispatch(frame: &mut InterruptFrame) {
let vector = frame.vector as u8;
match vector {
0..=31 | 0x80 => exceptions::handle(frame),
apic_vectors::PIT_CALIBRATION_VECTOR
| apic_vectors::APIC_TIMER_VECTOR
| apic_vectors::APIC_ERROR_VECTOR
| apic_vectors::APIC_SPURIOUS_VECTOR => apic_vectors::handle(frame),
_ => {
crate::println!("Unhandled interrupt vector: {:#X}", vector);
}
}
}
macro_rules! stub_no_err {
($name:ident, $vec:literal) => {
#[unsafe(naked)]
pub(super) unsafe extern "C" fn $name() {
core::arch::naked_asm!(
"push 0",
concat!("push ", stringify!($vec)),
"jmp {common}",
common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common,
);
}
};
}
macro_rules! stub_err {
($name:ident, $vec:literal) => {
#[unsafe(naked)]
pub(super) unsafe extern "C" fn $name() {
core::arch::naked_asm!(
concat!("push ", stringify!($vec)),
"jmp {common}",
common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common,
);
}
};
}
pub(super) use stub_err;
pub(super) use stub_no_err;
pub fn idt_init() { pub fn idt_init() {
let mut idt = Idt::new(); let mut idt = Idt::new();
-1
View File
@@ -31,7 +31,6 @@ pub struct RedirectionConfig {
pub trigger: TriggerMode, pub trigger: TriggerMode,
} }
#[derive(Debug)]
pub struct IoApic { pub struct IoApic {
base: VirtualAddr, base: VirtualAddr,
global_interrupt_base: u32, global_interrupt_base: u32,
-3
View File
@@ -64,7 +64,6 @@ pub enum InterruptInitError {
PitNotHandled, PitNotHandled,
} }
#[derive(Debug)]
pub struct InterruptController { pub struct InterruptController {
local_apic: apic::LocalApic, local_apic: apic::LocalApic,
io_apic: io_apic::IoApic, io_apic: io_apic::IoApic,
@@ -164,8 +163,6 @@ pub unsafe fn enter_user(
user_instruction_pointer: VirtualAddr, user_instruction_pointer: VirtualAddr,
user_stack_pointer: VirtualAddr, user_stack_pointer: VirtualAddr,
) -> ! { ) -> ! {
println!("Entering user mode");
unsafe { unsafe {
asm!( asm!(
"mov ds, {user_data_selector:x}", "mov ds, {user_data_selector:x}",
+67 -8
View File
@@ -4,14 +4,14 @@ use crate::{
arch::x86_64::cpu::CpuFeatures, arch::x86_64::cpu::CpuFeatures,
memory::{ memory::{
CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions, CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions,
PhysicalAddr, VirtualAddr, PageTableMapping, PhysicalAddr, VirtualAddr,
}, },
}; };
pub const PAGE_SIZE: usize = 4096; pub const PAGE_SIZE: usize = 4096;
pub const PAGE_TABLE_ENTRIES: usize = 512; pub const PAGE_TABLE_ENTRIES: usize = 512;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct PagingConfig { pub struct PagingConfig {
physical_address_bits: u8, physical_address_bits: u8,
global_pages: bool, global_pages: bool,
@@ -42,7 +42,7 @@ impl PagingConfig {
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
enum PagingMode { enum PagingMode {
FourLevel, FourLevel,
FiveLevel, FiveLevel,
@@ -77,7 +77,7 @@ impl PagingMode {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum PageTableLevel { enum PageTableLevel {
Pml5, Pml5,
Pml4, Pml4,
@@ -106,14 +106,13 @@ impl PageTableLevel {
} }
} }
#[derive(Debug)]
enum PageTableEntryError { enum PageTableEntryError {
PhysicalAddressTooLarge, PhysicalAddressTooLarge,
NoExecuteUnsupported, NoExecuteUnsupported,
} }
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
struct PageTableEntry(u64); struct PageTableEntry(u64);
impl PageTableEntry { impl PageTableEntry {
@@ -200,6 +199,14 @@ impl PageTableEntry {
self.0 & Self::PRESENT != 0 self.0 & Self::PRESENT != 0
} }
fn writable(&self) -> bool {
self.0 & Self::WRITABLE != 0
}
fn executable(&self) -> bool {
self.0 & Self::NX == 0
}
fn is_user_accessible(&self) -> bool { fn is_user_accessible(&self) -> bool {
self.0 & Self::USER_ACCESSIBLE != 0 self.0 & Self::USER_ACCESSIBLE != 0
} }
@@ -227,9 +234,16 @@ impl PageTableEntry {
FrameAddr::from_start_address(self.physical_address(config)) FrameAddr::from_start_address(self.physical_address(config))
} }
fn permissions(&self) -> PagePermissions {
PagePermissions::new(
self.writable(),
self.executable(),
self.is_user_accessible(),
)
}
} }
#[derive(Debug)]
pub(crate) enum MapError { pub(crate) enum MapError {
InvalidVirtualAddress, InvalidVirtualAddress,
VirtualAddressUnaligned, VirtualAddressUnaligned,
@@ -264,7 +278,6 @@ pub(crate) enum PageTableCreateError {
OutOfFrames, OutOfFrames,
} }
#[derive(Debug)]
pub struct PageTable { pub struct PageTable {
pub direct_map: DirectMap, pub direct_map: DirectMap,
config: PagingConfig, config: PagingConfig,
@@ -366,6 +379,52 @@ impl PageTable {
self.direct_map.translate(addr) self.direct_map.translate(addr)
} }
pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option<PageTableMapping> {
let address = virtual_addr.as_usize();
if !self.is_canonical(address) {
return None;
}
let mut table_frame = self.frame.frame_address();
let mut permissions = PagePermissions::new(true, true, true);
for &level in self.config.mode.intermediate_levels() {
let table = self.table(table_frame)?;
let entry = table[level.index(address)];
if !entry.is_present() {
return None;
}
let entry_permissions = entry.permissions();
permissions.writable &= entry_permissions.writable;
permissions.user_accessible &= entry_permissions.user_accessible;
permissions.executable &= entry_permissions.executable;
if entry.is_huge() {
level.large_page_size()?;
return Some(PageTableMapping { permissions });
}
table_frame = entry.table_frame(self.config)?;
}
let page_table = self.table(table_frame)?;
let entry = page_table[p1_index(address)];
if !entry.is_present() {
return None;
}
let entry_permissions = entry.permissions();
permissions.writable &= entry_permissions.writable;
permissions.user_accessible &= entry_permissions.user_accessible;
permissions.executable &= entry_permissions.executable;
Some(PageTableMapping { permissions })
}
fn get_next_level( fn get_next_level(
&self, &self,
parent: FrameAddr, parent: FrameAddr,
-1
View File
@@ -15,7 +15,6 @@ const IA32_KERNEL_GS_BASE: u32 = 0xC000_0102;
const RFLAGS_MASK: u64 = 0x257FD5; // Clear IF, TF, DF, IOPL, NT, AC const RFLAGS_MASK: u64 = 0x257FD5; // Clear IF, TF, DF, IOPL, NT, AC
#[repr(C)] #[repr(C)]
#[derive(Debug)]
struct SyscallFrame { struct SyscallFrame {
pub r15: u64, pub r15: u64,
pub r14: u64, pub r14: u64,
+4
View File
@@ -38,6 +38,10 @@ pub fn find_file<'a>(archive: &'a [u8], target: &str) -> Option<&'a [u8]> {
let mut offset = 0; let mut offset = 0;
while offset + core::mem::size_of::<Header>() <= archive.len() { while offset + core::mem::size_of::<Header>() <= archive.len() {
if offset + core::mem::size_of::<Header>() > archive.len() {
return None;
}
let header = Header::from_bytes(&archive[offset..])?; let header = Header::from_bytes(&archive[offset..])?;
let header_start = offset; let header_start = offset;
offset += core::mem::size_of::<Header>(); offset += core::mem::size_of::<Header>();
+82 -400
View File
@@ -1,423 +1,105 @@
#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ElfError;
pub enum ElfIsa {
None,
Sparc,
X86,
Mips,
Ppc,
Arm,
SuperH,
Ia64,
Amd64,
AArch64,
Riscv,
}
impl ElfIsa { #[cfg(target_arch = "x86_64")]
fn from_u16(value: u16) -> Result<Self, ElfError> { const MACHINE: u16 = 62;
match value { #[cfg(target_arch = "aarch64")]
0x00 => Ok(Self::None), const MACHINE: u16 = 183;
0x02 => Ok(Self::Sparc), #[cfg(target_arch = "riscv64")]
0x03 => Ok(Self::X86), const MACHINE: u16 = 243;
0x08 => Ok(Self::Mips),
0x14 => Ok(Self::Ppc),
0x28 => Ok(Self::Arm),
0x2A => Ok(Self::SuperH),
0x32 => Ok(Self::Ia64),
0x3E => Ok(Self::Amd64),
0xB7 => Ok(Self::AArch64),
0xF3 => Ok(Self::Riscv),
_ => Err(ElfError::InvalidElf),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ElfClass {
Elf32,
Elf64,
}
impl ElfClass {
fn from_u8(value: u8) -> Result<Self, ElfError> {
match value {
1 => Ok(Self::Elf32),
2 => Ok(Self::Elf64),
_ => Err(ElfError::InvalidElf),
}
}
const fn header_size(self) -> u16 {
match self {
Self::Elf32 => 52,
Self::Elf64 => 64,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Endianness {
Little,
Big,
}
impl Endianness {
fn from_u8(value: u8) -> Result<Self, ElfError> {
match value {
1 => Ok(Self::Little),
2 => Ok(Self::Big),
_ => Err(ElfError::InvalidElf),
}
}
}
#[repr(u16)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ElfType {
Relocatable = 1,
Executable = 2,
SharedObject = 3,
Core = 4,
}
impl ElfType {
fn from_u16(value: u16) -> Result<Self, ElfError> {
match value {
1 => Ok(Self::Relocatable),
2 => Ok(Self::Executable),
3 => Ok(Self::SharedObject),
4 => Ok(Self::Core),
_ => Err(ElfError::InvalidElf),
}
}
}
#[derive(Debug)]
#[allow(unused)]
pub struct ElfHeader {
magic: [u8; 4],
pub class: ElfClass,
endianness: Endianness,
version: u8,
os_abi: u8,
_reserved: [u8; 8],
pub object_type: ElfType,
pub machine: ElfIsa,
version_1: u32,
entry: u64, // 2115136
program_header_offset: u64, // 64
section_header_offset: u64, // 2759752
flags: u32, // 0
header_size: u16, // 64
program_header_entry_size: u16, // 56
program_header_count: u16, // 6
section_header_entry_size: u16, // 64
section_header_count: u16, // 17
section_name_index: u16, // 15
}
#[derive(Debug)]
pub enum ElfError {
InvalidElf,
}
impl ElfHeader {
pub fn parse(bytes: &[u8]) -> Result<Self, ElfError> {
let mut reader = Reader::new(bytes);
let magic = reader.read_array()?;
if magic != *b"\x7fELF" {
return Err(ElfError::InvalidElf);
}
let class = ElfClass::from_u8(reader.read_u8()?)?;
let endianness = Endianness::from_u8(reader.read_u8()?)?;
reader.set_endianness(endianness);
let version = reader.read_u8()?;
let os_abi = reader.read_u8()?;
let reserved = reader.read_array()?;
let object_type = ElfType::from_u16(reader.read_u16()?)?;
let machine = ElfIsa::from_u16(reader.read_u16()?)?;
let version_1 = reader.read_u32()?;
let entry = reader.read_word(class)?;
let program_header_offset = reader.read_word(class)?;
let section_header_offset = reader.read_word(class)?;
let flags = reader.read_u32()?;
let header_size = reader.read_u16()?;
let program_header_entry_size = reader.read_u16()?;
let program_header_count = reader.read_u16()?;
let section_header_entry_size = reader.read_u16()?;
let section_header_count = reader.read_u16()?;
let section_name_index = reader.read_u16()?;
if header_size != class.header_size() {
return Err(ElfError::InvalidElf);
}
Ok(Self {
magic,
class,
endianness,
version,
os_abi,
_reserved: reserved,
object_type,
machine,
version_1,
entry,
program_header_offset,
section_header_offset,
flags,
header_size,
program_header_entry_size,
program_header_count,
section_header_entry_size,
section_header_count,
section_name_index,
})
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProgramHeaderType {
Null = 0,
Load = 1,
Dynamic = 2,
Interpreter = 3,
Note = 4,
Shlib = 5,
Phdr = 6,
GnuStack = 0x6474e551,
Relro = 0x6474e552,
Other(u32),
}
impl ProgramHeaderType {
fn from_u32(value: u32) -> Self {
match value {
0 => Self::Null,
1 => Self::Load,
2 => Self::Dynamic,
3 => Self::Interpreter,
4 => Self::Note,
5 => Self::Shlib,
6 => Self::Phdr,
0x6474e551 => Self::GnuStack,
0x6474e552 => Self::Relro,
_ => Self::Other(value),
}
}
}
#[derive(Debug)]
pub struct ProgramHeader {
pub segment_type: ProgramHeaderType,
pub flags: u32,
pub file_offset: u64,
pub virtual_address: u64,
_physical_address: u64,
pub file_size: u64,
pub memory_size: u64,
pub alignment: u64,
}
impl ProgramHeader {
pub fn parse(bytes: &[u8], class: ElfClass, endianness: Endianness) -> Result<Self, ElfError> {
let mut reader = Reader::new(bytes);
reader.set_endianness(endianness);
let segment_type = ProgramHeaderType::from_u32(reader.read_u32()?);
let flags = if class == ElfClass::Elf64 {
reader.read_u32()?
} else {
0
};
let file_offset = reader.read_word(class)?;
let virtual_address = reader.read_word(class)?;
let physical_address = reader.read_word(class)?;
let file_size = reader.read_word(class)?;
let memory_size = reader.read_word(class)?;
let flags = if class == ElfClass::Elf32 {
reader.read_u32()?
} else {
flags
};
let alignment = reader.read_word(class)?;
Ok(Self {
segment_type,
flags,
file_offset,
virtual_address,
_physical_address: physical_address,
file_size,
memory_size,
alignment,
})
}
}
pub struct Elf<'a> { pub struct Elf<'a> {
bytes: &'a [u8], bytes: &'a [u8],
header: ElfHeader, headers: &'a [u8],
pub entry: usize,
}
pub struct Segment<'a> {
pub data: &'a [u8],
pub address: usize,
pub memory_size: usize,
pub writable: bool,
pub executable: bool,
} }
impl<'a> Elf<'a> { impl<'a> Elf<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> { pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> {
let header = ElfHeader::parse(bytes)?; let header = bytes.get(..64).ok_or(ElfError)?;
// Bootstrap images are static ELF64 executables in the native ISA, always LE.
Ok(Self { bytes, header }) if &header[..7] != b"\x7fELF\x02\x01\x01"
} || u16_at(header, 16) != 2
|| u16_at(header, 18) != MACHINE
pub fn program_headers(&self) -> Result<ProgramHeaders<'_>, ElfError> { || u32_at(header, 20) != 1
let offset = || u16_at(header, 52) != 64
usize::try_from(self.header.program_header_offset).map_err(|_| ElfError::InvalidElf)?; || u16_at(header, 54) != 56
let entry_size = usize::from(self.header.program_header_entry_size); {
let count = usize::from(self.header.program_header_count); return Err(ElfError);
let expected_entry_size = match self.header.class {
ElfClass::Elf32 => 32,
ElfClass::Elf64 => 56,
};
if entry_size != expected_entry_size {
return Err(ElfError::InvalidElf);
} }
let table_size = entry_size.checked_mul(count).ok_or(ElfError::InvalidElf)?; let offset = usize_at(header, 32);
let table_end = offset.checked_add(table_size).ok_or(ElfError::InvalidElf)?; let count = usize::from(u16_at(header, 56));
let bytes = self let end = offset.checked_add(count * 56).ok_or(ElfError)?;
.bytes let headers = bytes.get(offset..end).ok_or(ElfError)?;
.get(offset..table_end) if headers
.ok_or(ElfError::InvalidElf)?; .chunks_exact(56)
.any(|h| matches!(u32_at(h, 0), 2 | 3))
{
// There is no dynamic linker or relocation processing during bootstrap.
return Err(ElfError);
}
Ok(ProgramHeaders { Ok(Self {
bytes, bytes,
class: self.header.class, headers,
endianness: self.header.endianness, entry: usize_at(header, 24),
entry_size,
remaining: count,
}) })
} }
pub fn bytes(&self) -> &[u8] { pub fn segments(&self) -> impl Iterator<Item = Result<Segment<'a>, ElfError>> + '_ {
self.bytes self.headers
} .chunks_exact(56)
.filter(|h| u32_at(h, 0) == 1)
pub fn machine(&self) -> ElfIsa { .map(|h| {
self.header.machine let offset = usize_at(h, 8);
} let address = usize_at(h, 16);
let file_size = usize_at(h, 32);
pub fn entry(&self) -> usize { let memory_size = usize_at(h, 40);
self.header.entry as usize let alignment = usize_at(h, 48);
if file_size > memory_size
|| (alignment > 1
&& (!alignment.is_power_of_two()
|| address % alignment != offset % alignment))
{
return Err(ElfError);
}
let end = offset.checked_add(file_size).ok_or(ElfError)?;
let data = self.bytes.get(offset..end).ok_or(ElfError)?;
let flags = u32_at(h, 4);
Ok(Segment {
data,
address,
memory_size,
writable: flags & 2 != 0,
executable: flags & 1 != 0,
})
})
} }
} }
pub struct ProgramHeaders<'a> { // Callers only read fixed offsets within already bounds-checked headers.
bytes: &'a [u8], fn u16_at(bytes: &[u8], offset: usize) -> u16 {
class: ElfClass, let mut value = [0; 2];
endianness: Endianness, value.copy_from_slice(&bytes[offset..offset + 2]);
entry_size: usize, u16::from_le_bytes(value)
remaining: usize,
} }
impl<'a> Iterator for ProgramHeaders<'a> { fn u32_at(bytes: &[u8], offset: usize) -> u32 {
type Item = Result<ProgramHeader, ElfError>; let mut value = [0; 4];
value.copy_from_slice(&bytes[offset..offset + 4]);
fn next(&mut self) -> Option<Self::Item> { u32::from_le_bytes(value)
if self.remaining == 0 {
return None;
}
let entry = match self.bytes.get(..self.entry_size) {
Some(entry) => entry,
None => {
self.remaining = 0;
return None;
}
};
self.bytes = &self.bytes[self.entry_size..];
self.remaining -= 1;
Some(ProgramHeader::parse(entry, self.class, self.endianness))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
} }
impl ExactSizeIterator for ProgramHeaders<'_> {} fn usize_at(bytes: &[u8], offset: usize) -> usize {
let mut value = [0; 8];
struct Reader<'a> { value.copy_from_slice(&bytes[offset..offset + 8]);
bytes: &'a [u8], u64::from_le_bytes(value) as usize
offset: usize,
endianness: Endianness,
}
impl<'a> Reader<'a> {
const fn new(bytes: &'a [u8]) -> Self {
Self {
bytes,
offset: 0,
endianness: Endianness::Little,
}
}
fn set_endianness(&mut self, endianness: Endianness) {
self.endianness = endianness;
}
fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ElfError> {
let end = self.offset.checked_add(N).ok_or(ElfError::InvalidElf)?;
let bytes = self
.bytes
.get(self.offset..end)
.ok_or(ElfError::InvalidElf)?;
self.offset = end;
bytes.try_into().map_err(|_| ElfError::InvalidElf)
}
fn read_u8(&mut self) -> Result<u8, ElfError> {
Ok(self.read_array::<1>()?[0])
}
fn read_u16(&mut self) -> Result<u16, ElfError> {
let bytes = self.read_array()?;
Ok(match self.endianness {
Endianness::Little => u16::from_le_bytes(bytes),
Endianness::Big => u16::from_be_bytes(bytes),
})
}
fn read_u32(&mut self) -> Result<u32, ElfError> {
let bytes = self.read_array()?;
Ok(match self.endianness {
Endianness::Little => u32::from_le_bytes(bytes),
Endianness::Big => u32::from_be_bytes(bytes),
})
}
fn read_u64(&mut self) -> Result<u64, ElfError> {
let bytes = self.read_array()?;
Ok(match self.endianness {
Endianness::Little => u64::from_le_bytes(bytes),
Endianness::Big => u64::from_be_bytes(bytes),
})
}
fn read_word(&mut self, class: ElfClass) -> Result<u64, ElfError> {
match class {
ElfClass::Elf32 => Ok(u64::from(self.read_u32()?)),
ElfClass::Elf64 => self.read_u64(),
}
}
} }
+12 -48
View File
@@ -1,4 +1,3 @@
#![feature(abi_x86_interrupt)]
#![allow(clippy::needless_return)] #![allow(clippy::needless_return)]
#![no_std] #![no_std]
#![no_main] #![no_main]
@@ -12,12 +11,9 @@ mod platform;
mod syscall; mod syscall;
mod task; mod task;
use core::arch::global_asm;
use crate::{ use crate::{
debug::serial, debug::serial,
memory::{AddressSpace, KernelStackPool, MemoryRegionKind, UserStack}, memory::{AddressSpace, MemoryRegionKind, init_frame_allocator, init_kernel_address_space},
task::tcb::Tcb,
}; };
pub struct KernelHandoff { pub struct KernelHandoff {
@@ -25,7 +21,6 @@ pub struct KernelHandoff {
address_space: AddressSpace, address_space: AddressSpace,
direct_map: memory::DirectMap, direct_map: memory::DirectMap,
boot_info: boot::BootInfo, boot_info: boot::BootInfo,
kernel_stack_pool: KernelStackPool,
handoff_frame: memory::OwnedFrame, handoff_frame: memory::OwnedFrame,
} }
@@ -54,11 +49,9 @@ pub extern "C" fn _start() -> ! {
println!("Entering kernel main..."); println!("Entering kernel main...");
let mut kernel_stack_pool = KernelStackPool::new(); let kernel_stack =
crate::task::scheduler::allocate_kernel_stack(&mut address_space, &mut allocator)
let kernel_stack = kernel_stack_pool .expect("failed to allocate bootstrap stack");
.allocate(&mut address_space, &mut allocator)
.expect("failed to allocate bootstrap stack");
let handoff_frame = allocator let handoff_frame = allocator
.alloc() .alloc()
@@ -73,7 +66,6 @@ pub extern "C" fn _start() -> ! {
address_space, address_space,
direct_map, direct_map,
boot_info, boot_info,
kernel_stack_pool,
handoff_frame, handoff_frame,
}; };
@@ -91,21 +83,13 @@ pub extern "C" fn _start() -> ! {
} }
pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! { pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
let ( let (mut allocator, mut address_space, direct_map, boot_info, handoff_frame) = unsafe {
mut allocator,
mut address_space,
direct_map,
boot_info,
mut kernel_stack_pool,
handoff_frame,
) = unsafe {
let handoff = handoff.read(); let handoff = handoff.read();
( (
handoff.allocator, handoff.allocator,
handoff.address_space, handoff.address_space,
handoff.direct_map, handoff.direct_map,
handoff.boot_info, handoff.boot_info,
handoff.kernel_stack_pool,
handoff.handoff_frame, handoff.handoff_frame,
) )
}; };
@@ -119,9 +103,6 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
MemoryRegionKind::BootloaderReclaimable, MemoryRegionKind::BootloaderReclaimable,
); );
let init_code = format::cpio::find_file(boot_info.initramfs.data(), "init.elf")
.expect("Failed to load init program from initramfs");
println!("Initializing local ACPI...",); println!("Initializing local ACPI...",);
let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI"); let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
@@ -135,39 +116,22 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
println!("Initializing interrupt controller..."); println!("Initializing interrupt controller...");
let interrupt_controller = let _interrupt_controller =
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space) arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
.expect("failed to initialize interrupt controller"); .expect("failed to initialize interrupt controller");
let user_kernel_stack = kernel_stack_pool task::bootstrap::spawn(
.allocate(&mut address_space, &mut allocator) "omega3.elf",
.expect("failed to allocate task kernel stack"); &boot_info.initramfs,
&mut address_space,
let mut user_address_space = address_space
.new_user(&mut allocator)
.expect("failed to create user address space");
let image = task::loader::load_elf(
init_code,
&mut user_address_space,
&mut allocator, &mut allocator,
direct_map, direct_map,
); );
let user_stack = UserStack::allocate(&mut user_address_space, &mut allocator) init_frame_allocator(allocator);
.expect("failed to allocate user stack"); init_kernel_address_space(address_space);
let task = Tcb::new_user(
0, // overwritten by add_task for now
user_address_space,
user_kernel_stack,
image.expect("Failed to load elf"),
user_stack.top(),
);
task::scheduler::add_task(task).expect("scheduler is full");
task::scheduler::start(); task::scheduler::start();
hcf();
} }
#[panic_handler] #[panic_handler]
+117 -2
View File
@@ -1,3 +1,5 @@
use core::cell::UnsafeCell;
use crate::{ use crate::{
arch::{PageTable, PageTableCreateError, PageTableMapError, PageTableUnmapError, PagingConfig}, arch::{PageTable, PageTableCreateError, PageTableMapError, PageTableUnmapError, PagingConfig},
memory::{ memory::{
@@ -6,6 +8,111 @@ use crate::{
}, },
}; };
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct AddressSpaceId(usize);
const MAX_ADDRESS_SPACES: usize = 32;
struct AddressSpaceTable {
entries: [Option<AddressSpace>; MAX_ADDRESS_SPACES],
}
impl AddressSpaceTable {
const fn new() -> Self {
Self {
entries: [const { None }; MAX_ADDRESS_SPACES],
}
}
fn insert(&mut self, address_space: AddressSpace) -> Result<AddressSpaceId, AddressSpace> {
for (i, slot) in self.entries.iter_mut().enumerate() {
if slot.is_none() {
*slot = Some(address_space);
return Ok(AddressSpaceId(i));
}
}
Err(address_space)
}
fn get(&self, id: AddressSpaceId) -> Option<&AddressSpace> {
self.entries.get(id.0).and_then(Option::as_ref)
}
fn get_mut(&mut self, id: AddressSpaceId) -> Option<&mut AddressSpace> {
self.entries.get_mut(id.0).and_then(Option::as_mut)
}
fn remove(&mut self, id: AddressSpaceId) -> Option<AddressSpace> {
self.entries.get_mut(id.0).and_then(Option::take)
}
}
struct GlobalAddressSpaceTable(UnsafeCell<AddressSpaceTable>);
unsafe impl Sync for GlobalAddressSpaceTable {}
static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable =
GlobalAddressSpaceTable(UnsafeCell::new(AddressSpaceTable::new()));
pub fn insert_address_space(address_space: AddressSpace) -> Result<AddressSpaceId, AddressSpace> {
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
table.insert(address_space)
}
pub fn remove_address_space(id: AddressSpaceId) -> Option<AddressSpace> {
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
table.remove(id)
}
pub fn with_address_space<R>(id: AddressSpaceId, f: impl FnOnce(&AddressSpace) -> R) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() };
let res = table.get(id).map(f);
crate::arch::restore_interrupts(interrupt_state);
res
}
pub fn with_address_space_mut<R>(
id: AddressSpaceId,
f: impl FnOnce(&mut AddressSpace) -> R,
) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
let res = table.get_mut(id).map(f);
crate::arch::restore_interrupts(interrupt_state);
res
}
struct GlobalKernelAddressSpace(UnsafeCell<Option<AddressSpace>>);
unsafe impl Sync for GlobalKernelAddressSpace {}
static KERNEL_ADDRESS_SPACE: GlobalKernelAddressSpace =
GlobalKernelAddressSpace(UnsafeCell::new(None));
pub fn init_kernel_address_space(address_space: AddressSpace) {
let interrupt_state = crate::arch::disable_interrupts_and_save();
unsafe {
*KERNEL_ADDRESS_SPACE.0.get() = Some(address_space);
}
crate::arch::restore_interrupts(interrupt_state);
}
pub fn with_kernel_address_space<R>(f: impl FnOnce(&mut AddressSpace) -> R) -> R {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let space = unsafe {
(&mut *KERNEL_ADDRESS_SPACE.0.get())
.as_mut()
.expect("kernel address space not initialized")
};
let res = f(space);
crate::arch::restore_interrupts(interrupt_state);
res
}
#[derive(Debug)] #[derive(Debug)]
pub enum MapError { pub enum MapError {
InvalidVirtualAddress, InvalidVirtualAddress,
@@ -82,13 +189,17 @@ impl From<PageTableCreateError> for AddressSpaceCreateError {
} }
} }
#[derive(Debug, PartialEq, Eq)] pub struct PageTableMapping {
pub permissions: PagePermissions,
}
#[derive(PartialEq, Eq)]
enum AddressSpaceKind { enum AddressSpaceKind {
Kernel, Kernel,
User, User,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub struct AddressSpace { pub struct AddressSpace {
root: PageTable, root: PageTable,
kind: AddressSpaceKind, kind: AddressSpaceKind,
@@ -326,6 +437,10 @@ impl AddressSpace {
self.root.to_virtual(physical_addr) self.root.to_virtual(physical_addr)
} }
pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option<PageTableMapping> {
self.root.mapping(virtual_addr)
}
pub unsafe fn activate(&self) { pub unsafe fn activate(&self) {
unsafe { self.root.activate() } unsafe { self.root.activate() }
} }
+46 -4
View File
@@ -1,3 +1,5 @@
use core::cell::UnsafeCell;
use crate::memory::{DirectMap, MemoryRegion, MemoryRegionKind, PhysicalAddr, VirtualAddr}; use crate::memory::{DirectMap, MemoryRegion, MemoryRegionKind, PhysicalAddr, VirtualAddr};
pub const FRAME_SIZE: usize = 4096; pub const FRAME_SIZE: usize = 4096;
@@ -12,15 +14,57 @@ pub fn align_down_to_frame(addr: usize) -> usize {
} }
#[repr(u8)] #[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum FrameState { enum FrameState {
Reserved = 0b00, Reserved = 0b00,
Free = 0b01, Free = 0b01,
Allocated = 0b10, Allocated = 0b10,
} }
struct GlobalFrameAllocator(UnsafeCell<Option<FrameAllocator>>);
unsafe impl Sync for GlobalFrameAllocator {}
static FRAME_ALLOCATOR: GlobalFrameAllocator = GlobalFrameAllocator(UnsafeCell::new(None));
pub fn init_global(allocator: FrameAllocator) {
let interrupt_state = crate::arch::disable_interrupts_and_save();
unsafe {
*FRAME_ALLOCATOR.0.get() = Some(allocator);
}
crate::arch::restore_interrupts(interrupt_state);
}
pub fn alloc_frame() -> Option<OwnedFrame> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let allocator = unsafe { &mut *FRAME_ALLOCATOR.0.get() };
let frame = allocator.as_mut().and_then(|a| a.alloc());
crate::arch::restore_interrupts(interrupt_state);
frame
}
pub unsafe fn dealloc_frame(frame: OwnedFrame) {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let allocator = unsafe { &mut *FRAME_ALLOCATOR.0.get() };
if let Some(a) = allocator.as_mut() {
unsafe { a.dealloc(frame) };
}
crate::arch::restore_interrupts(interrupt_state);
}
#[allow(unused)]
pub fn with_allocator<R>(f: impl FnOnce(&mut FrameAllocator) -> R) -> R {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let allocator = unsafe {
(&mut *FRAME_ALLOCATOR.0.get())
.as_mut()
.expect("frame allocator not initialized")
};
let result = f(allocator);
crate::arch::restore_interrupts(interrupt_state);
result
}
// 64 KiB per GiB // 64 KiB per GiB
#[derive(Debug)]
struct Bitmap { struct Bitmap {
start: VirtualAddr, start: VirtualAddr,
frame_count: usize, frame_count: usize,
@@ -70,7 +114,6 @@ pub enum FrameAllocatorInitError {
} }
// very very simple bitmap frame/page allocator // very very simple bitmap frame/page allocator
#[derive(Debug)]
pub struct FrameAllocator { pub struct FrameAllocator {
bitmap: Bitmap, bitmap: Bitmap,
next_search: usize, next_search: usize,
@@ -327,7 +370,6 @@ impl FrameAddr {
} }
// specifically not Clone or Copy // specifically not Clone or Copy
#[derive(Debug)]
pub struct OwnedFrame { pub struct OwnedFrame {
frame: FrameAddr, frame: FrameAddr,
} }
+14 -9
View File
@@ -6,8 +6,15 @@ mod user;
use core::ops::Add; use core::ops::Add;
#[allow(unused)] #[allow(unused)]
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError}; pub use address_space::{
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame}; AddressSpace, AddressSpaceCreateError, AddressSpaceId, MapError, PageTableMapping, UnmapError,
init_kernel_address_space, insert_address_space, remove_address_space, with_address_space,
with_address_space_mut, with_kernel_address_space,
};
pub use frame::{
FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame,
init_global as init_frame_allocator, with_allocator,
};
#[allow(unused)] #[allow(unused)]
pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack}; pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack};
#[allow(unused)] #[allow(unused)]
@@ -31,8 +38,6 @@ impl<const N: usize> BootString<N> {
} }
} }
const MAX_MODULE_PATH_LENGTH: usize = 256;
pub struct InitramfsImage { pub struct InitramfsImage {
pub start: VirtualAddr, pub start: VirtualAddr,
pub length: usize, pub length: usize,
@@ -55,7 +60,7 @@ pub struct KernelMemoryLayout {
pub segments: [KernelSegment; 3], pub segments: [KernelSegment; 3],
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub struct PagePermissions { pub struct PagePermissions {
pub writable: bool, pub writable: bool,
pub executable: bool, pub executable: bool,
@@ -117,7 +122,7 @@ impl Add<usize> for VirtualAddr {
} }
#[repr(u8)] #[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum MemoryRegionKind { pub enum MemoryRegionKind {
Usable, Usable,
Reserved, Reserved,
@@ -130,20 +135,20 @@ pub enum MemoryRegionKind {
MappedReserved, MappedReserved,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub enum CachePolicy { pub enum CachePolicy {
Uncacheable, Uncacheable,
WriteBack, WriteBack,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub struct MemoryRegion { pub struct MemoryRegion {
pub start: PhysicalAddr, pub start: PhysicalAddr,
pub length: usize, pub length: usize,
pub kind: MemoryRegionKind, pub kind: MemoryRegionKind,
} }
#[derive(Debug, Clone, Copy)] #[derive(Clone, Copy)]
pub struct DirectMap { pub struct DirectMap {
offset: usize, offset: usize,
} }
+7 -2
View File
@@ -215,7 +215,7 @@ pub struct KernelStackPool {
} }
impl KernelStackPool { impl KernelStackPool {
pub fn new() -> Self { pub const fn new() -> Self {
Self { free_slots: 0 } Self { free_slots: 0 }
} }
@@ -237,8 +237,13 @@ impl KernelStackPool {
Err(StackCreateError::OutOfStacks) Err(StackCreateError::OutOfStacks)
} }
pub fn free(&mut self, stack: &KernelStack) { pub fn free(&mut self, stack: KernelStack) {
let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1; let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1;
crate::memory::with_kernel_address_space(|kernel_as| {
crate::memory::with_allocator(|allocator| unsafe {
stack.destroy(kernel_as, allocator)
});
});
self.free_slots &= !(1 << slot); self.free_slots &= !(1 << slot);
} }
} }
+58 -6
View File
@@ -1,9 +1,51 @@
use crate::{memory::VirtualAddr, syscall::Status}; use crate::{
memory::{FRAME_SIZE, VirtualAddr, address_space::AddressSpaceId, with_address_space},
syscall::Status,
};
pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000); pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000);
pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> { pub fn validate_user_range(
// TODO: guard against unmapped pages as_id: AddressSpaceId,
start: VirtualAddr,
len: usize,
writable: bool,
) -> Result<(), Status> {
let start_addr = start.as_usize();
let end_addr = start_addr.checked_add(len).ok_or(Status::BadAddress)?;
if start_addr >= USER_SPACE_END.as_usize() || end_addr > USER_SPACE_END.as_usize() {
return Err(Status::BadAddress);
}
if len == 0 {
return Ok(());
}
let page_start = start_addr & !(FRAME_SIZE - 1);
for page in (page_start..end_addr).step_by(FRAME_SIZE) {
let is_valid = with_address_space(as_id, |address_space| {
address_space
.mapping(VirtualAddr::new(page))
.is_some_and(|mapping| {
mapping.permissions.user_accessible
&& (!writable || mapping.permissions.writable)
})
})
.ok_or(Status::BadAddress)?;
if !is_valid {
return Err(Status::BadAddress);
}
}
Ok(())
}
/// # Safety
///
/// The caller must ensure that the user address range is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]).
pub unsafe fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
let end = src let end = src
.as_usize() .as_usize()
.checked_add(dst.len()) .checked_add(dst.len())
@@ -19,7 +61,10 @@ pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
Ok(()) Ok(())
} }
pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> { /// # Safety
///
/// The caller must ensure that the user address range is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]).
pub unsafe fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
let end = dst let end = dst
.as_usize() .as_usize()
.checked_add(src.len()) .checked_add(src.len())
@@ -35,7 +80,10 @@ pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
Ok(()) Ok(())
} }
pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> { /// # Safety
///
/// The caller must ensure that the user address is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]).
pub unsafe fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> {
if dst.as_usize() % core::mem::align_of::<T>() != 0 { if dst.as_usize() % core::mem::align_of::<T>() != 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
@@ -54,7 +102,11 @@ pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status
Ok(()) Ok(())
} }
pub fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> { /// # Safety
///
/// The caller must ensure that the user address is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]).
#[allow(unused)]
pub unsafe fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> {
if src.as_usize() % core::mem::align_of::<T>() != 0 { if src.as_usize() % core::mem::align_of::<T>() != 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
+16 -21
View File
@@ -12,7 +12,6 @@ pub enum AcpiError {
MultipleIoApicsUnsupported, MultipleIoApicsUnsupported,
} }
#[derive(Debug)]
pub struct AcpiTables { pub struct AcpiTables {
direct_map: DirectMap, direct_map: DirectMap,
root: RootTable, root: RootTable,
@@ -215,7 +214,6 @@ impl AcpiTables {
} }
} }
#[derive(Debug)]
enum RootTable { enum RootTable {
Rsdt(Sdt), Rsdt(Sdt),
Xsdt(Sdt), Xsdt(Sdt),
@@ -251,7 +249,7 @@ impl RootTable {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
struct Rsdp { struct Rsdp {
signature: [u8; 8], signature: [u8; 8],
checksum: u8, checksum: u8,
@@ -261,7 +259,7 @@ struct Rsdp {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
struct Xsdp { struct Xsdp {
rsdp: Rsdp, rsdp: Rsdp,
length: u32, length: u32,
@@ -271,7 +269,7 @@ struct Xsdp {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
struct SDTHeader { struct SDTHeader {
signature: [u8; 4], signature: [u8; 4],
length: u32, length: u32,
@@ -284,14 +282,12 @@ struct SDTHeader {
creator_revision: u32, creator_revision: u32,
} }
#[derive(Debug)]
pub struct Sdt { pub struct Sdt {
physical_addr: PhysicalAddr, physical_addr: PhysicalAddr,
length: usize, length: usize,
signature: [u8; 4], signature: [u8; 4],
} }
#[derive(Debug)]
#[allow(unused)] #[allow(unused)]
pub struct Madt<'a> { pub struct Madt<'a> {
acpi: &'a AcpiTables, acpi: &'a AcpiTables,
@@ -301,7 +297,7 @@ pub struct Madt<'a> {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
struct MadtBody { struct MadtBody {
local_apic_address: u32, local_apic_address: u32,
flags: u32, flags: u32,
@@ -543,21 +539,20 @@ impl<'a> Iterator for MadtEntries<'a> {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct MadtEntryHeader { pub struct MadtEntryHeader {
kind: u8, kind: u8,
length: u8, length: u8,
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct LocalApicEntry { pub struct LocalApicEntry {
processor_id: u8, processor_id: u8,
id: u8, id: u8,
flags: u32, flags: u32,
} }
#[derive(Debug)]
pub struct IoApicInfo { pub struct IoApicInfo {
pub id: u8, pub id: u8,
pub apic_address: PhysicalAddr, pub apic_address: PhysicalAddr,
@@ -565,7 +560,7 @@ pub struct IoApicInfo {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct IoApicEntry { pub struct IoApicEntry {
id: u8, id: u8,
reserved: u8, reserved: u8,
@@ -574,7 +569,7 @@ pub struct IoApicEntry {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct InterruptSourceOverride { pub struct InterruptSourceOverride {
bus: u8, bus: u8,
source: u8, source: u8,
@@ -582,19 +577,19 @@ pub struct InterruptSourceOverride {
flags: u16, flags: u16,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub enum InterruptPolarity { pub enum InterruptPolarity {
ActiveHigh, ActiveHigh,
ActiveLow, ActiveLow,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub enum TriggerMode { pub enum TriggerMode {
Edge, Edge,
Level, Level,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct IsaIrqRoute { pub struct IsaIrqRoute {
pub gsi: u32, pub gsi: u32,
pub polarity: InterruptPolarity, pub polarity: InterruptPolarity,
@@ -602,7 +597,7 @@ pub struct IsaIrqRoute {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct IoApicNmiEntry { pub struct IoApicNmiEntry {
nmi_source: u8, nmi_source: u8,
reserved: u8, reserved: u8,
@@ -611,7 +606,7 @@ pub struct IoApicNmiEntry {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct LocalApicNmiEntry { pub struct LocalApicNmiEntry {
processor_id: u8, processor_id: u8,
flags: u16, flags: u16,
@@ -619,14 +614,14 @@ pub struct LocalApicNmiEntry {
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct LocalApicAddressOverride { pub struct LocalApicAddressOverride {
reserved: u16, reserved: u16,
local_apic_address: u64, local_apic_address: u64,
} }
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
pub struct LocalX2ApicEntry { pub struct LocalX2ApicEntry {
reserved: u16, reserved: u16,
local_x2apic_id: u32, local_x2apic_id: u32,
@@ -634,7 +629,7 @@ pub struct LocalX2ApicEntry {
acpi_processor_uid: u32, acpi_processor_uid: u32,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy)]
#[allow(unused)] #[allow(unused)]
pub enum MadtEntry { pub enum MadtEntry {
LocalApic(LocalApicEntry), LocalApic(LocalApicEntry),
+45 -5
View File
@@ -2,6 +2,8 @@ mod table;
use table::*; use table::*;
use crate::task::tcb::{ExitReason, Fault};
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u64)] #[repr(u64)]
pub enum Status { pub enum Status {
@@ -11,37 +13,75 @@ pub enum Status {
BadFileDescriptor = 3, // EBADF BadFileDescriptor = 3, // EBADF
NoSuchTask = 4, // ESRCH NoSuchTask = 4, // ESRCH
OutOfMemory = 5, // ENOMEM OutOfMemory = 5, // ENOMEM
BadHandle = 6, // EBADH
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u64)] #[repr(u64)]
pub enum SyscallNumber { pub enum SyscallNumber {
Yield = 1, Yield = 1,
Exit = 2, Exit = 2,
Write = 3, Write = 3,
Send = 4,
Recv = 5,
FrameAlloc = 6,
FrameDealloc = 7,
AsCreate = 8,
Map = 9,
Unmap = 10,
TaskCreate = 11,
} }
impl TryFrom<u64> for SyscallNumber { impl TryFrom<u64> for SyscallNumber {
type Error = Status; type Error = ();
fn try_from(val: u64) -> Result<Self, Self::Error> { fn try_from(val: u64) -> Result<Self, Self::Error> {
match val { match val {
1 => Ok(Self::Yield), 1 => Ok(Self::Yield),
2 => Ok(Self::Exit), 2 => Ok(Self::Exit),
3 => Ok(Self::Write), 3 => Ok(Self::Write),
_ => Err(Status::InvalidArgument), 4 => Ok(Self::Send),
5 => Ok(Self::Recv),
6 => Ok(Self::FrameAlloc),
7 => Ok(Self::FrameDealloc),
8 => Ok(Self::AsCreate),
9 => Ok(Self::Map),
10 => Ok(Self::Unmap),
11 => Ok(Self::TaskCreate),
_ => Err(()),
} }
} }
} }
pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64, _arg5: u64) -> u64 { pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, _arg5: u64) -> u64 {
let result = (|| -> Result<(), Status> { let result = (|| -> Result<(), Status> {
let syscall = SyscallNumber::try_from(num)?; let syscall = SyscallNumber::try_from(num).unwrap_or_else(|_| {
crate::task::scheduler::exit_current(ExitReason::Fault(Fault::BadSystemCall))
});
match syscall { match syscall {
SyscallNumber::Yield => sys_yield(), SyscallNumber::Yield => sys_yield(),
SyscallNumber::Exit => sys_exit(arg0 as usize), SyscallNumber::Exit => sys_exit(arg0 as usize),
SyscallNumber::Write => { SyscallNumber::Write => {
sys_write(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize) sys_write(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
} }
SyscallNumber::Send => sys_send(arg0 as usize, arg1 as usize, arg2 as usize),
SyscallNumber::Recv => {
sys_recv(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
}
SyscallNumber::FrameAlloc => sys_frame_alloc(arg0 as usize),
SyscallNumber::FrameDealloc => sys_frame_dealloc(arg0 as usize),
SyscallNumber::AsCreate => sys_as_create(arg0 as usize),
SyscallNumber::Map => sys_map(
arg0 as usize,
arg1 as usize,
arg2 as usize,
arg3 as usize,
arg4 as usize,
),
SyscallNumber::Unmap => sys_unmap(arg0 as usize),
SyscallNumber::TaskCreate => {
sys_task_create(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
}
} }
})(); })();
+618 -4
View File
@@ -1,4 +1,14 @@
use crate::memory::{VirtualAddr, copy_from_user, copy_val_to_user}; use crate::{
memory::{
FRAME_SIZE, MapError, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user,
copy_to_user, copy_val_to_user, validate_user_range,
},
println,
task::{
scheduler::TaskId,
tcb::{BlockReason, ExitReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights},
},
};
use super::Status; use super::Status;
@@ -8,7 +18,7 @@ pub fn sys_yield() -> Result<(), Status> {
} }
pub fn sys_exit(exit_code: usize) -> ! { pub fn sys_exit(exit_code: usize) -> ! {
crate::task::scheduler::exit_current(exit_code); crate::task::scheduler::exit_current(ExitReason::Exited(exit_code));
} }
pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Result<(), Status> { pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Result<(), Status> {
@@ -16,18 +26,622 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul
return Err(Status::BadFileDescriptor); return Err(Status::BadFileDescriptor);
} }
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(current_task.as_id, VirtualAddr::new(buf_ptr), len, false)?;
if out_ptr != 0 {
if out_ptr % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_ptr),
core::mem::size_of::<usize>(),
true,
)?;
}
Ok(())
})
.expect("failed to resolve self task")?;
let mut chunk = [0u8; 128]; let mut chunk = [0u8; 128];
let mut written = 0; let mut written = 0;
while written < len { while written < len {
let n = (len - written).min(chunk.len()); let n = (len - written).min(chunk.len());
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?; unsafe {
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?;
}
crate::debug::serial::write_bytes(&chunk[..n]); crate::debug::serial::write_bytes(&chunk[..n]);
written += n; written += n;
} }
if out_ptr != 0 { if out_ptr != 0 {
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?; unsafe {
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
}
} }
Ok(()) Ok(())
} }
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> {
let dest_task_id = TaskId::new(dest_task_id);
if len > MAX_MSG_SIZE {
return Err(Status::InvalidArgument);
}
let sender = crate::task::scheduler::current();
crate::task::scheduler::with_task(sender, |current_task| {
validate_user_range(current_task.as_id, VirtualAddr::new(msg_ptr), len, false)
})
.expect("failed to resolve self task")?;
let mut msg_buf = [0u8; MAX_MSG_SIZE];
unsafe { copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])? };
let msg = Message {
sender,
length: len,
data: msg_buf,
};
let should_unblock = crate::task::scheduler::with_task_mut(dest_task_id, |dest_task| {
if !dest_task.mailbox.push(msg) {
return Err(Status::OutOfMemory);
}
Ok(matches!(
dest_task.state,
crate::task::tcb::ThreadState::Blocked(BlockReason::Recv)
))
})
.ok_or(Status::NoSuchTask)??;
if should_unblock {
crate::task::scheduler::unblock(dest_task_id);
}
Ok(())
}
pub fn sys_recv(
out_ptr: usize,
max_len: usize,
out_actual_len: usize,
out_sender: usize,
) -> Result<(), Status> {
if out_ptr == 0 {
return Err(Status::InvalidArgument);
}
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
if current_task.mailbox.len == 0 {
crate::task::scheduler::block_current(BlockReason::Recv);
}
validate_user_range(current_task.as_id, VirtualAddr::new(out_ptr), max_len, true)?;
if out_actual_len != 0 {
if out_actual_len % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_actual_len),
core::mem::size_of::<usize>(),
true,
)?;
}
if out_sender != 0 {
if out_sender % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_sender),
core::mem::size_of::<usize>(),
true,
)?;
}
Ok(())
})
.expect("failed to resolve self task")?;
let msg =
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
current_task.mailbox.pop().ok_or(Status::NoSuchTask)
})
.expect("failed to resolve self task")?;
unsafe {
copy_to_user(
VirtualAddr::new(out_ptr),
&msg.data[..msg.length.min(max_len)],
)?;
if out_actual_len != 0 {
copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?;
}
if out_sender != 0 {
copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?;
}
}
Ok(())
}
pub fn sys_frame_alloc(out_handle: usize) -> Result<(), Status> {
if out_handle == 0 {
return Err(Status::InvalidArgument);
}
if out_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_handle),
core::mem::size_of::<usize>(),
true,
)
})
.expect("failed to resolve self task")?;
let frame = crate::memory::alloc_frame().ok_or(Status::OutOfMemory)?;
let handle = Handle {
object: KernelObject::Frame(frame),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE | Rights::MAP,
};
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
let handle_id = match current_task.handles.push(handle) {
Ok(id) => id,
Err(handle) => {
let frame = match handle.object {
KernelObject::Frame(frame) => frame,
_ => unreachable!("pushed handle was not a frame"),
};
unsafe { crate::memory::dealloc_frame(frame) };
return Err(Status::OutOfMemory);
}
};
unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
})
.expect("failed to resolve self task")
}
pub fn sys_frame_dealloc(frame_handle_id: usize) -> Result<(), Status> {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
let frame_handle = task
.handles
.take(frame_handle_id)
.ok_or(Status::BadHandle)?;
match frame_handle.object {
KernelObject::Frame(frame_addr) => {
unsafe { crate::memory::dealloc_frame(frame_addr) };
Ok(())
}
_ => {
// Wrong-type operations must not consume the handle
match task.handles.put(frame_handle_id, frame_handle) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
return Err(Status::InvalidArgument);
}
}
})
.expect("failed to resolve self task")
}
pub fn sys_as_create(out_handle: usize) -> Result<(), Status> {
if out_handle == 0 {
return Err(Status::InvalidArgument);
}
if out_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
let new_as =
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_handle),
core::mem::size_of::<usize>(),
true,
)?;
let new_as = match crate::memory::with_address_space(current_task.as_id, |caller_as| {
crate::memory::with_allocator(|allocator| caller_as.new_user(allocator))
}) {
Some(Ok(as_space)) => as_space,
_ => return Err(Status::OutOfMemory),
};
Ok(new_as)
})
.expect("failed to resolve self task")?;
let as_id = match crate::memory::insert_address_space(new_as) {
Ok(id) => id,
Err(addr_space) => {
crate::memory::with_allocator(|allocator| unsafe { addr_space.destroy(allocator) });
return Err(Status::OutOfMemory);
}
};
let handle = Handle {
object: KernelObject::AddressSpace(as_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
};
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
let handle_id = match current_task.handles.push(handle) {
Ok(id) => id,
Err(handle) => {
let address_space = match handle.object {
KernelObject::AddressSpace(as_id) => crate::memory::remove_address_space(as_id)
.expect("address space was just inserted"),
_ => unreachable!("pushed handle was not an address space"),
};
crate::memory::with_allocator(|allocator| unsafe {
address_space.destroy(allocator)
});
return Err(Status::OutOfMemory);
}
};
unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
})
.expect("failed to resolve self task")
}
pub fn sys_map(
as_handle: usize,
frame_handle: usize,
virtual_addr: usize,
permissions: usize,
out_handle: usize,
) -> Result<(), Status> {
if out_handle == 0 || out_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
if virtual_addr % FRAME_SIZE != 0 || permissions & !0b11 != 0 {
return Err(Status::InvalidArgument);
}
let writable = permissions & (1 << 0) != 0;
let executable = permissions & (1 << 1) != 0;
let end = virtual_addr
.checked_add(FRAME_SIZE)
.ok_or(Status::InvalidArgument)?;
if end > USER_SPACE_END.as_usize() {
return Err(Status::InvalidArgument);
}
let current_task = crate::task::scheduler::current();
let as_id = crate::task::scheduler::with_task(current_task, |task| {
validate_user_range(
task.as_id,
VirtualAddr::new(out_handle),
core::mem::size_of::<usize>(),
true,
)?;
let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?;
let as_id = match as_handle.object {
KernelObject::AddressSpace(as_id) => as_id,
_ => return Err(Status::InvalidArgument),
};
if as_handle.rights.0 & Rights::WRITE.0 == 0 {
return Err(Status::InvalidArgument);
}
let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?;
if !matches!(frame_handle.object, KernelObject::Frame(_)) {
return Err(Status::InvalidArgument);
}
let mut required_rights = Rights::READ | Rights::MAP;
if writable {
required_rights = required_rights | Rights::WRITE;
}
if executable {
required_rights = required_rights | Rights::EXECUTE;
}
if frame_handle.rights.0 & required_rights.0 != required_rights.0 {
return Err(Status::InvalidArgument);
}
Ok(as_id)
})
.expect("failed to resolve self task")?;
let handle = crate::task::scheduler::with_task_mut(current_task, |task| {
task.handles.take(frame_handle).ok_or(Status::BadHandle)
})
.expect("failed to resolve self task")?;
let Handle { object, rights } = handle;
let KernelObject::Frame(frame) = object else {
panic!("validated frame handle changed before it was taken");
};
let permissions = PagePermissions::new(writable, executable, true);
let virtual_addr = VirtualAddr::new(virtual_addr);
let map_result = crate::memory::with_address_space_mut(as_id, |target_as| {
crate::memory::with_allocator(|allocator| {
target_as.map(
frame.frame_address().start_address(),
virtual_addr,
permissions,
allocator,
crate::memory::CachePolicy::WriteBack,
)
})
})
.expect("failed to resolve self address space");
match map_result {
Ok(_) => {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
match task.handles.put(
frame_handle,
Handle {
object: KernelObject::Mapping {
frame,
address_space: as_id,
virtual_addr,
},
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
});
unsafe {
copy_val_to_user(VirtualAddr::new(out_handle), &frame_handle)
.expect("out_handle has already been checked")
}
Ok(())
}
Err(err) => {
crate::task::scheduler::with_task_mut(current_task, |task| {
match task.handles.put(
frame_handle,
Handle {
object: KernelObject::Frame(frame),
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
})
.expect("failed to resolve self task");
match err {
MapError::AlreadyMapped | MapError::UnsupportedPermissions => {
Err(Status::InvalidArgument)
}
MapError::OutOfMemory => Err(Status::OutOfMemory),
MapError::InvalidVirtualAddress
| MapError::VirtualAddressUnaligned
| MapError::PhysicalAddressTooLarge
| MapError::PhysicalAddressUnaligned
| MapError::RangeLengthUnaligned
| MapError::AddressOverflow
| MapError::MappingConflict
| MapError::PageTableUnavailable
| MapError::CorruptedPageTable
| MapError::InvalidUserAddress
| MapError::InvalidUserMap => {
panic!("validated user mapping failed with an impossible error: {err:?}")
}
}
}
}
}
pub fn sys_unmap(mapping_handle: usize) -> Result<(), Status> {
let handle = crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
task.handles.take(mapping_handle).ok_or(Status::BadHandle)
})
.expect("failed to resolve self task")?;
let Handle { object, rights } = handle;
let KernelObject::Mapping {
frame,
address_space,
virtual_addr,
} = object
else {
// Wrong-type operations must not consume the handle
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| match task
.handles
.put(mapping_handle, Handle { object, rights })
{
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
})
.expect("failed to resolve self task");
return Err(Status::InvalidArgument);
};
let unmap_result = crate::memory::with_address_space_mut(address_space, |target_as| {
crate::memory::with_allocator(|allocator| unsafe {
target_as.unmap(virtual_addr, allocator)
})
});
match unmap_result {
Some(Ok(unmapped_frame)) => {
if unmapped_frame == frame.frame_address() {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
match task.handles.put(
mapping_handle,
Handle {
object: KernelObject::Frame(frame),
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
});
Ok(())
} else {
panic!("unmap resulted in a frame that was not the one we expected")
}
}
Some(Err(err)) => {
// every unmapping error should be impossible to occur
panic!("failed to unmap: {err:?}");
}
None => {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
match task.handles.put(
mapping_handle,
Handle {
object: KernelObject::Mapping {
frame: frame,
address_space,
virtual_addr,
},
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
});
Err(Status::BadHandle)
}
}
}
pub fn sys_task_create(
as_handle: usize,
entry: usize,
user_stack: usize,
out_task_handle: usize,
) -> Result<(), Status> {
if entry == 0 || user_stack == 0 || out_task_handle == 0 {
return Err(Status::InvalidArgument);
}
if entry >= USER_SPACE_END.as_usize() || user_stack > USER_SPACE_END.as_usize() {
return Err(Status::BadAddress);
}
if out_task_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
let as_id =
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_task_handle),
core::mem::size_of::<usize>(),
true,
)?;
let as_handle = current_task
.handles
.get(as_handle)
.ok_or(Status::BadHandle)?;
let as_id = match as_handle.object {
KernelObject::AddressSpace(as_id) => as_id,
_ => return Err(Status::InvalidArgument),
};
if as_handle.rights.0 & Rights::EXECUTE.0 == 0 {
return Err(Status::InvalidArgument);
}
Ok(as_id)
})
.expect("failed to resolve self task")?;
if crate::memory::with_address_space(as_id, |_| ()).is_none() {
return Err(Status::BadHandle);
}
let stack_probe = user_stack.checked_sub(1).ok_or(Status::BadAddress)?;
validate_user_range(as_id, VirtualAddr::new(stack_probe), 1, true)?;
let entry_is_valid = crate::memory::with_address_space(as_id, |address_space| {
address_space
.mapping(VirtualAddr::new(entry))
.is_some_and(|mapping| {
mapping.permissions.user_accessible && mapping.permissions.executable
})
})
.ok_or(Status::BadHandle)?;
if !entry_is_valid {
return Err(Status::BadAddress);
}
let kernel_stack = match crate::memory::with_kernel_address_space(|kernel_as| {
crate::memory::with_allocator(|allocator| {
crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
})
}) {
Ok(stack) => stack,
Err(err) => {
println!("Failed to allocate kernel stack: {:?}", err);
return Err(Status::OutOfMemory);
}
};
let new_tcb = crate::task::tcb::Tcb::new_user(
as_id,
kernel_stack,
VirtualAddr::new(entry),
VirtualAddr::new(user_stack),
);
let new_task_id = match crate::task::scheduler::add_task(new_tcb) {
Ok(id) => id,
Err(_) => return Err(Status::OutOfMemory),
};
let handle = Handle {
object: KernelObject::Thread(new_task_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
};
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
let handle_id = match current_task.handles.push(handle) {
Ok(id) => id,
Err(_) => {
crate::task::scheduler::remove_task(new_task_id);
return Err(Status::OutOfMemory);
}
};
unsafe { copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id) }
})
.expect("failed to resolve self task")
}
+140
View File
@@ -0,0 +1,140 @@
use crate::{
format,
memory::{
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions,
UserStack, VirtualAddr,
},
task::{scheduler::TaskId, tcb::Tcb},
};
pub fn spawn(
name: &str,
initramfs: &InitramfsImage,
kernel_as: &mut AddressSpace,
allocator: &mut FrameAllocator,
direct_map: DirectMap,
) -> TaskId {
let bytes = format::cpio::find_file(initramfs.data(), name)
.unwrap_or_else(|| panic!("{name} missing from initramfs"));
let kernel_stack = crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
.expect("kernel stack allocation failed");
let initramfs_physical_addr = kernel_as
.to_physical(initramfs.start)
.expect("failed to translate initramfs start address");
let mut address_space = kernel_as
.new_user(allocator)
.expect("address space allocation failed");
address_space
.map_range(
initramfs_physical_addr,
VirtualAddr::new(0x4000_0000),
((initramfs.length) + 0xFFF) & !0xFFF,
PagePermissions::new(true, false, true),
allocator,
memory::CachePolicy::WriteBack,
)
.expect("failed to map initramfs");
let user_stack =
UserStack::allocate(&mut address_space, allocator).expect("user stack allocation failed");
let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF");
let as_id = match crate::memory::insert_address_space(address_space) {
Ok(id) => id,
Err(_) => {
panic!("address space table is full");
}
};
let task = Tcb::new_user(as_id, kernel_stack, entry, user_stack.top());
crate::task::scheduler::add_task(task).expect("scheduler is full")
}
#[derive(Debug)]
enum ElfLoadError {
AddressTranslationFailed,
FailedToMapSegment,
OutOfMemory,
InvalidElf,
}
fn load_elf(
bytes: &[u8],
user_address_space: &mut AddressSpace,
allocator: &mut FrameAllocator,
direct_map: DirectMap,
) -> Result<VirtualAddr, ElfLoadError> {
let program = format::elf::Elf::parse(bytes).map_err(|_| ElfLoadError::InvalidElf)?;
let mut executable_entry = false;
for segment in program.segments() {
let segment = segment.map_err(|_| ElfLoadError::InvalidElf)?;
let end = segment
.address
.checked_add(segment.memory_size)
.filter(|&end| end <= memory::USER_SPACE_END.as_usize())
.ok_or(ElfLoadError::InvalidElf)?;
if segment.memory_size == 0 {
continue;
}
executable_entry |= segment.executable && (segment.address..end).contains(&program.entry);
let page_start = segment.address & !(FRAME_SIZE - 1);
let file_end = segment.address + segment.data.len();
let permissions = PagePermissions::new(segment.writable, segment.executable, true);
// Overlapping segment pages are rejected by map(), including stack/archive collisions.
for page in (page_start..end).step_by(FRAME_SIZE) {
let frame = allocator.alloc_nozero().ok_or(ElfLoadError::OutOfMemory)?;
let physical = frame.frame_address().start_address();
let Some(destination) = direct_map.translate(physical) else {
unsafe { allocator.dealloc(frame) };
return Err(ElfLoadError::AddressTranslationFailed);
};
let copy_start = page.max(segment.address).min(page + FRAME_SIZE);
let copy_end = (page + FRAME_SIZE).min(file_end).max(copy_start);
let prefix = copy_start - page;
let copied = copy_end - copy_start;
unsafe {
let destination = destination.as_mut_ptr::<u8>();
// Initialize padding and BSS, but don't zero bytes we're about to overwrite.
core::ptr::write_bytes(destination, 0, prefix);
if copied != 0 {
core::ptr::copy_nonoverlapping(
segment.data.as_ptr().add(copy_start - segment.address),
destination.add(prefix),
copied,
);
}
core::ptr::write_bytes(
destination.add(prefix + copied),
0,
FRAME_SIZE - prefix - copied,
);
}
if user_address_space
.map(
physical,
VirtualAddr::new(page),
permissions,
allocator,
memory::CachePolicy::WriteBack,
)
.is_err()
{
unsafe { allocator.dealloc(frame) };
return Err(ElfLoadError::FailedToMapSegment);
}
let _ = frame.into_raw();
}
}
if !executable_entry {
return Err(ElfLoadError::InvalidElf);
}
Ok(VirtualAddr::new(program.entry))
}
-212
View File
@@ -1,212 +0,0 @@
use crate::{
format,
memory::{
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, OwnedFrame, PagePermissions,
VirtualAddr,
},
println,
};
const MAX_LOAD_SEGMENTS: usize = 32;
#[derive(Debug)]
struct LoadedRegion {
start: VirtualAddr,
mapped_pages: usize,
}
#[derive(Debug)]
pub struct LoadedImage {
pub entry: VirtualAddr,
regions: [LoadedRegion; MAX_LOAD_SEGMENTS],
region_count: usize,
}
impl LoadedImage {
/// # Safety
/// The supplied address space must contain this image's original mappings.
/// Its frames must be exclusively owned by this image and no longer in use.
pub unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
for region in self.regions[..self.region_count].iter().rev() {
for page in (0..region.mapped_pages).rev() {
let address = VirtualAddr::new(region.start.as_usize() + page * FRAME_SIZE);
let frame = unsafe {
address_space
.unmap(address, allocator)
.expect("loaded image mapping was unexpectedly missing")
};
unsafe { allocator.dealloc(OwnedFrame::from_raw(frame)) };
}
}
}
}
#[derive(Debug)]
pub enum ElfLoadError {
AddressTranslationFailed,
FailedToMapSegment,
AddressOverflow,
InvalidStack,
OutOfMemory,
InvalidElf,
TooManyLoadSegments,
}
#[cfg(target_arch = "x86_64")]
fn is_loadable(elf: &format::elf::Elf) -> bool {
// on x86_64, we only support ELFs that are either 32 bit x86 or 64 bit x86
matches!(
elf.machine(),
format::elf::ElfIsa::X86 | format::elf::ElfIsa::Amd64
)
}
#[cfg(not(target_arch = "x86_64"))]
fn is_loadable(elf: &format::elf::Elf) -> bool {
false
}
pub fn load_elf(
bytes: &[u8],
user_address_space: &mut AddressSpace,
allocator: &mut FrameAllocator,
direct_map: DirectMap,
) -> Result<LoadedImage, ElfLoadError> {
let program = format::elf::Elf::parse(bytes).map_err(|_| ElfLoadError::InvalidElf)?;
if !is_loadable(&program) {
return Err(ElfLoadError::InvalidElf);
}
let mut image = LoadedImage {
entry: VirtualAddr::new(program.entry()),
regions: core::array::from_fn(|_| LoadedRegion {
start: VirtualAddr::new(0),
mapped_pages: 0,
}),
region_count: 0,
};
let result = (|| {
for header in program
.program_headers()
.map_err(|_| ElfLoadError::InvalidElf)?
{
println!("Processing program header: {:?}", header);
let header = header.map_err(|_| ElfLoadError::InvalidElf)?;
if header.file_size > header.memory_size {
return Err(ElfLoadError::InvalidElf);
}
match header.segment_type {
format::elf::ProgramHeaderType::Load => {
if image.region_count == MAX_LOAD_SEGMENTS {
return Err(ElfLoadError::TooManyLoadSegments);
}
// TODO: give a fuck about alignment
// TODO: handle program segments that overlap
let segment_start = header.virtual_address as usize;
if (program.entry() >= segment_start
&& program.entry() < segment_start + header.memory_size as usize)
&& header.flags & 0x01 == 0
{
// entry is within NX segment
return Err(ElfLoadError::InvalidElf);
}
let page_start = segment_start & !(FRAME_SIZE - 1);
let page_offset = segment_start - page_start;
let mapped_length = page_offset
.checked_add(header.memory_size as usize)
.ok_or(ElfLoadError::AddressOverflow)?
.div_ceil(FRAME_SIZE)
* FRAME_SIZE;
let frame_count = mapped_length / FRAME_SIZE;
let executable = header.flags & 0x01 != 0;
let writable = header.flags & 0x02 != 0;
// TODO: support only-executable segments
// let readable = header.flags & 0x04 != 0;
let region = &mut image.regions[image.region_count];
region.start = VirtualAddr::new(page_start);
image.region_count += 1;
for i in 0..frame_count {
let frame = allocator.alloc().ok_or(ElfLoadError::OutOfMemory)?;
println!(
"Mapping code frame: {:X?} to {:X?}",
frame,
page_start + i * FRAME_SIZE
);
if user_address_space
.map(
frame.frame_address().start_address(),
VirtualAddr::new(page_start + i * FRAME_SIZE),
PagePermissions::new(writable, executable, true),
allocator,
memory::CachePolicy::WriteBack,
)
.is_err()
{
unsafe { allocator.dealloc(frame) };
return Err(ElfLoadError::FailedToMapSegment);
}
let _ = frame.into_raw();
region.mapped_pages += 1;
}
let mut copied = 0;
while copied < header.file_size as usize {
let destination = VirtualAddr::new(segment_start + copied);
let physical = user_address_space
.to_physical(destination)
.ok_or(ElfLoadError::AddressTranslationFailed)?;
let direct_mapped = direct_map
.translate(physical)
.ok_or(ElfLoadError::AddressTranslationFailed)?;
let page_remaining = FRAME_SIZE - destination.as_usize() % FRAME_SIZE;
let copy_length = page_remaining.min(header.file_size as usize - copied);
unsafe {
core::ptr::copy_nonoverlapping(
program
.bytes()
.as_ptr()
.add(header.file_offset as usize + copied),
direct_mapped.as_mut_ptr(),
copy_length,
);
}
copied += copy_length;
}
}
format::elf::ProgramHeaderType::GnuStack => {
// if the stack is NOT R/W NX, we refuse to map it
if header.flags != 6 {
return Err(ElfLoadError::InvalidStack);
}
}
_ => {}
}
}
Ok(())
})();
if let Err(error) = result {
// Only pages created by this load are recorded; none have been handed to a task.
unsafe { image.destroy(user_address_space, allocator) };
return Err(error);
}
Ok(image)
}
+1 -1
View File
@@ -1,3 +1,3 @@
pub mod loader; pub mod bootstrap;
pub mod scheduler; pub mod scheduler;
pub mod tcb; pub mod tcb;
+166 -24
View File
@@ -2,19 +2,31 @@ use core::cell::UnsafeCell;
use crate::{ use crate::{
arch::ThreadContext, arch::ThreadContext,
memory::{AddressSpace, VirtualAddr}, memory::{
AddressSpace, AddressSpaceId, FrameAllocator, KernelStack, KernelStackPool,
StackCreateError, VirtualAddr,
},
println, println,
task::tcb::{ExitReason, Tcb, ThreadState}, task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState},
}; };
const MAX_TASKS: usize = 32; const MAX_TASKS: usize = 32;
type TaskId = usize; #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct TaskId(usize);
impl TaskId {
pub const fn new(id: usize) -> Self {
Self(id)
}
}
struct Scheduler { struct Scheduler {
current: Option<TaskId>, current: Option<TaskId>,
tasks: [Option<Tcb>; MAX_TASKS], tasks: [Option<Tcb>; MAX_TASKS],
ready: ReadyQueue, ready: ReadyQueue,
stacks: KernelStackPool,
} }
impl Scheduler { impl Scheduler {
@@ -23,27 +35,28 @@ impl Scheduler {
current: None, current: None,
tasks: [const { None }; MAX_TASKS], tasks: [const { None }; MAX_TASKS],
ready: ReadyQueue::new(), ready: ReadyQueue::new(),
stacks: KernelStackPool::new(),
} }
} }
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch { fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
assert_ne!(current_id, next_id); assert_ne!(current_id, next_id);
let current = self.tasks[current_id].as_mut().unwrap(); let current = self.tasks[current_id.0].as_mut().unwrap();
let prev_ctx = &mut current.context as *mut ThreadContext; let prev_ctx = &mut current.context as *mut ThreadContext;
let prev_addr_space = &current.address_space as *const AddressSpace; let prev_as_id = current.as_id;
let next = self.tasks[next_id].as_ref().unwrap(); let next = self.tasks[next_id.0].as_ref().unwrap();
let next_ctx = &next.context as *const ThreadContext; let next_ctx = &next.context as *const ThreadContext;
let next_addr_space = &next.address_space as *const AddressSpace; let next_as_id = next.as_id;
let next_kernel_stack = next.kernel_stack.top(); let next_kernel_stack = next.kernel_stack.top();
Switch { Switch {
previous_context: prev_ctx, previous_context: prev_ctx,
next_context: next_ctx, next_context: next_ctx,
next_address_space: next_addr_space, next_as_id,
next_kernel_stack: next_kernel_stack, next_kernel_stack,
activate_address_space: unsafe { *next_addr_space != *prev_addr_space }, activate_address_space: next_as_id != prev_as_id,
} }
} }
} }
@@ -51,7 +64,7 @@ impl Scheduler {
struct Switch { struct Switch {
previous_context: *mut ThreadContext, previous_context: *mut ThreadContext,
next_context: *const ThreadContext, next_context: *const ThreadContext,
next_address_space: *const AddressSpace, next_as_id: AddressSpaceId,
next_kernel_stack: VirtualAddr, next_kernel_stack: VirtualAddr,
activate_address_space: bool, activate_address_space: bool,
} }
@@ -59,9 +72,9 @@ struct Switch {
impl Switch { impl Switch {
unsafe fn perform(self) { unsafe fn perform(self) {
if self.activate_address_space { if self.activate_address_space {
unsafe { crate::memory::with_address_space(self.next_as_id, |as_ref| unsafe {
(&*self.next_address_space).activate(); as_ref.activate();
} });
} }
crate::arch::set_kernel_stack(self.next_kernel_stack); crate::arch::set_kernel_stack(self.next_kernel_stack);
@@ -81,7 +94,7 @@ struct ReadyQueue {
impl ReadyQueue { impl ReadyQueue {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
entries: [0; MAX_TASKS], entries: [TaskId(0); MAX_TASKS],
head: 0, head: 0,
len: 0, len: 0,
} }
@@ -110,6 +123,23 @@ impl ReadyQueue {
Some(task) Some(task)
} }
pub fn remove(&mut self, task: TaskId) -> bool {
for i in 0..self.len {
let idx = (self.head + i) % MAX_TASKS;
if self.entries[idx] == task {
for j in i..(self.len - 1) {
let from = (self.head + j + 1) % MAX_TASKS;
let to = (self.head + j) % MAX_TASKS;
self.entries[to] = self.entries[from];
}
self.len -= 1;
return true;
}
}
false
}
} }
struct GlobalScheduler(UnsafeCell<Scheduler>); struct GlobalScheduler(UnsafeCell<Scheduler>);
@@ -126,9 +156,20 @@ pub fn add_task(mut task: Tcb) -> Result<TaskId, Tcb> {
match scheduler.tasks.iter().position(Option::is_none) { match scheduler.tasks.iter().position(Option::is_none) {
Some(id) => { Some(id) => {
let id = TaskId(id);
task.id = id; task.id = id;
match task.handles.push(Handle {
object: KernelObject::Thread(id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
}) {
Ok(_) => {}
Err(_) => unreachable!("Cant push root thread handle"),
}
task.state = ThreadState::Ready; task.state = ThreadState::Ready;
scheduler.tasks[id] = Some(task); scheduler.tasks[id.0] = Some(task);
assert!(scheduler.ready.push_back(id)); assert!(scheduler.ready.push_back(id));
Ok(id) Ok(id)
} }
@@ -149,7 +190,7 @@ pub fn start() -> ! {
let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let scheduler = unsafe { &mut *SCHEDULER.0.get() };
let next_id = scheduler.ready.pop_front().expect("no tasks to run"); let next_id = scheduler.ready.pop_front().expect("no tasks to run");
let next = scheduler.tasks[next_id] let next = scheduler.tasks[next_id.0]
.as_mut() .as_mut()
.expect("ready task is missing"); .expect("ready task is missing");
@@ -159,7 +200,7 @@ pub fn start() -> ! {
Switch { Switch {
previous_context: &mut bootstrap_context, previous_context: &mut bootstrap_context,
next_context: &next.context, next_context: &next.context,
next_address_space: &next.address_space, next_as_id: next.as_id,
next_kernel_stack: next.kernel_stack.top(), next_kernel_stack: next.kernel_stack.top(),
activate_address_space: true, activate_address_space: true,
} }
@@ -172,6 +213,107 @@ pub fn start() -> ! {
panic!("scheduler returned to bootstrap context"); panic!("scheduler returned to bootstrap context");
} }
pub fn allocate_kernel_stack(
address_space: &mut AddressSpace,
allocator: &mut FrameAllocator,
) -> Result<KernelStack, StackCreateError> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
let result = scheduler.stacks.allocate(address_space, allocator);
crate::arch::restore_interrupts(interrupt_state);
result
}
pub fn remove_task(id: TaskId) -> bool {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let result = {
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
if scheduler.current == Some(id) {
false
} else if let Some(task) = scheduler.tasks.get_mut(id.0).and_then(Option::take) {
scheduler.ready.remove(id);
scheduler.stacks.free(task.kernel_stack);
true
} else {
false
}
};
crate::arch::restore_interrupts(interrupt_state);
result
}
pub fn with_task<R>(id: TaskId, f: impl FnOnce(&Tcb) -> R) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let scheduler = unsafe { &*SCHEDULER.0.get() };
let res = scheduler.tasks.get(id.0).and_then(Option::as_ref).map(f);
crate::arch::restore_interrupts(interrupt_state);
res
}
pub fn with_task_mut<R>(id: TaskId, f: impl FnOnce(&mut Tcb) -> R) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
let res = scheduler
.tasks
.get_mut(id.0)
.and_then(Option::as_mut)
.map(f);
crate::arch::restore_interrupts(interrupt_state);
res
}
pub fn current() -> TaskId {
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
scheduler.current.expect("no current task")
}
pub fn block_current(reason: BlockReason) {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let switch = {
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
let Some(next_id) = scheduler.ready.pop_front() else {
println!("Deadlock: all tasks blocked");
crate::hcf();
};
let current_id = scheduler.current.expect("no current task");
scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Blocked(reason);
// explicitly do NOT push back the current task, because it is not ready
scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
scheduler.current = Some(next_id);
scheduler.make_switch(current_id, next_id)
};
unsafe {
switch.perform();
}
// this runs when this task is selected to run again
crate::arch::restore_interrupts(interrupt_state);
}
pub fn unblock(id: TaskId) {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
if let Some(task) = scheduler.tasks[id.0].as_mut() {
if matches!(task.state, ThreadState::Blocked(_)) {
task.state = ThreadState::Ready;
assert!(scheduler.ready.push_back(id));
}
}
crate::arch::restore_interrupts(interrupt_state);
}
pub fn yield_current() { pub fn yield_current() {
let interrupt_state = crate::arch::disable_interrupts_and_save(); let interrupt_state = crate::arch::disable_interrupts_and_save();
@@ -185,10 +327,10 @@ pub fn yield_current() {
let current_id = scheduler.current.expect("no current task"); let current_id = scheduler.current.expect("no current task");
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Ready; scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Ready;
assert!(scheduler.ready.push_back(current_id)); assert!(scheduler.ready.push_back(current_id));
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
scheduler.current = Some(next_id); scheduler.current = Some(next_id);
scheduler.make_switch(current_id, next_id) scheduler.make_switch(current_id, next_id)
@@ -202,7 +344,7 @@ pub fn yield_current() {
crate::arch::restore_interrupts(interrupt_state); crate::arch::restore_interrupts(interrupt_state);
} }
pub fn exit_current(exit_code: usize) -> ! { pub fn exit_current(reason: ExitReason) -> ! {
crate::arch::disable_interrupts(); crate::arch::disable_interrupts();
let switch = { let switch = {
@@ -213,10 +355,10 @@ pub fn exit_current(exit_code: usize) -> ! {
crate::hcf(); crate::hcf();
}; };
let current = scheduler.tasks[current_id].as_mut().unwrap(); let current = scheduler.tasks[current_id.0].as_mut().unwrap();
current.state = ThreadState::Dead(ExitReason::Exited(exit_code)); current.state = ThreadState::Dead(reason);
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
scheduler.current = Some(next_id); scheduler.current = Some(next_id);
scheduler.make_switch(current_id, next_id) scheduler.make_switch(current_id, next_id)
+185 -15
View File
@@ -1,52 +1,222 @@
use core::ops::BitOr;
use crate::{ use crate::{
arch::ThreadContext, arch::ThreadContext,
memory::{AddressSpace, KernelStack, VirtualAddr}, memory::{AddressSpaceId, KernelStack, OwnedFrame, VirtualAddr},
task::loader::LoadedImage, task::scheduler::TaskId,
}; };
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Fault {
SegmentationFault,
IllegalInstruction,
Abort,
BadSystemCall,
}
// Thread Control Block // Thread Control Block
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExitReason { pub enum ExitReason {
Exited(usize), Exited(usize),
Killed, Killed,
Fault, Fault(Fault),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlockReason {
Recv,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ThreadState { pub enum ThreadState {
Ready, Ready,
Running, Running,
Blocked, Blocked(BlockReason),
Dead(ExitReason), Dead(ExitReason),
} }
#[derive(Debug)] pub const MAX_MSG_SIZE: usize = 128;
pub const MAILBOX_CAPACITY: usize = 4;
#[derive(Clone, Copy)]
pub struct Message {
pub sender: TaskId,
pub length: usize,
pub data: [u8; MAX_MSG_SIZE],
}
pub struct Mailbox {
pub messages: [Option<Message>; MAILBOX_CAPACITY],
pub head: usize,
pub len: usize,
}
impl Mailbox {
const fn new() -> Self {
Self {
messages: [None; MAILBOX_CAPACITY],
head: 0,
len: 0,
}
}
pub fn pop(&mut self) -> Option<Message> {
if self.len == 0 {
return None;
}
let msg = self.messages[self.head];
self.head = (self.head + 1) % MAILBOX_CAPACITY;
self.len -= 1;
msg
}
pub fn push(&mut self, msg: Message) -> bool {
if self.len == MAILBOX_CAPACITY {
return false;
}
let tail = (self.head + self.len) % MAILBOX_CAPACITY;
self.messages[tail] = Some(msg);
self.len += 1;
true
}
}
const MAX_HANDLES: usize = 32;
pub enum KernelObject {
AddressSpace(AddressSpaceId),
Frame(OwnedFrame),
Mapping {
frame: OwnedFrame,
address_space: AddressSpaceId,
virtual_addr: VirtualAddr,
},
Thread(TaskId),
}
pub struct Handle {
pub object: KernelObject,
pub rights: Rights,
}
#[derive(Clone, Copy)]
pub struct Rights(pub u32);
impl Rights {
pub const READ: Self = Self(1 << 0);
pub const WRITE: Self = Self(1 << 1);
pub const EXECUTE: Self = Self(1 << 2);
pub const MAP: Self = Self(1 << 3);
}
impl BitOr for Rights {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
pub struct HandleTable {
handles: [Option<Handle>; MAX_HANDLES],
}
impl HandleTable {
pub const fn new() -> Self {
Self {
handles: [const { None }; MAX_HANDLES],
}
}
pub fn push(&mut self, handle: Handle) -> Result<usize, Handle> {
for (i, slot) in self.handles.iter_mut().enumerate() {
if slot.is_none() {
*slot = Some(handle);
return Ok(i);
}
}
Err(handle)
}
pub fn remove(&mut self, id: usize) -> Option<Handle> {
self.handles.get_mut(id).and_then(Option::take)
}
pub fn take(&mut self, id: usize) -> Option<Handle> {
self.handles.get_mut(id)?.take()
}
pub fn put(&mut self, id: usize, handle: Handle) -> Result<(), Handle> {
let Some(slot) = self.handles.get_mut(id) else {
return Err(handle);
};
if slot.is_some() {
return Err(handle);
}
*slot = Some(handle);
Ok(())
}
pub fn get(&self, id: usize) -> Option<&Handle> {
self.handles.get(id).and_then(Option::as_ref)
}
pub fn get_mut(&mut self, id: usize) -> Option<&mut Handle> {
self.handles.get_mut(id).and_then(Option::as_mut)
}
}
pub struct Tcb { pub struct Tcb {
pub id: usize, pub id: TaskId,
pub as_id: AddressSpaceId,
pub state: ThreadState, pub state: ThreadState,
pub kernel_stack: KernelStack, pub kernel_stack: KernelStack,
pub context: ThreadContext, pub context: ThreadContext,
pub address_space: AddressSpace, pub mailbox: Mailbox,
pub image: LoadedImage, pub handles: HandleTable,
}
impl core::fmt::Debug for Tcb {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("Tcb")
.field("id", &self.id)
.field("as_id", &self.as_id)
.field("state", &self.state)
.finish_non_exhaustive()
}
} }
impl Tcb { impl Tcb {
pub fn new_user( pub fn new_user(
id: usize, as_id: AddressSpaceId,
address_space: AddressSpace,
kernel_stack: KernelStack, kernel_stack: KernelStack,
image: LoadedImage, entry: VirtualAddr,
user_stack: VirtualAddr, user_stack: VirtualAddr,
) -> Self { ) -> Self {
let context = ThreadContext::new(image.entry, user_stack, kernel_stack.top()); let context = ThreadContext::new(entry, user_stack, kernel_stack.top());
let mut handles = HandleTable::new();
match handles.push(Handle {
object: KernelObject::AddressSpace(as_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
}) {
Ok(_) => {}
Err(_) => unreachable!("Cant push root address space handle"),
};
Self { Self {
id, id: TaskId::new(0),
as_id,
state: ThreadState::Ready, state: ThreadState::Ready,
kernel_stack, kernel_stack,
context, context,
address_space, mailbox: Mailbox::new(),
image, handles,
} }
} }
} }
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "client"
version = "0.1.0"
edition = "2024"
[dependencies]
dusk-sys = { path = "../dusk-sys" }
[[bin]]
name = "client"
test = false
bench = false
+27
View File
@@ -0,0 +1,27 @@
#![no_std]
#![no_main]
use dusk_sys::{println, sys_exit, sys_recv, sys_send};
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
let msg = "Hello from client!";
println!("[client] Sent: {}", msg);
// TODO: we assume the echo server is task 1 (spawned by omega3)
sys_send(1, msg.as_bytes()).unwrap();
let mut out = [0u8; 128];
let (actual_len, _) = sys_recv(&mut out).unwrap();
println!(
"[client] Received: {}",
core::str::from_utf8(&out[..actual_len]).unwrap()
);
sys_exit(0);
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("{info}");
sys_exit(1);
}
@@ -1,9 +1,8 @@
[package] [package]
name = "init" name = "dusk-sys"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[[bin]] [lib]
name = "init"
test = false test = false
bench = false bench = false
+327
View File
@@ -0,0 +1,327 @@
#![no_std]
use core::arch::asm;
#[derive(Debug, PartialEq, Eq)]
pub enum Status {
// Success = 0,
InvalidArgument = 1,
BadAddress = 2,
BadFileDescriptor = 3,
NoSuchTask = 4,
OutOfMemory = 5,
BadHandle = 6,
}
// Opaque handle type
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AddressSpaceHandle(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FrameHandle(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MappingHandle(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ThreadHandle(usize);
// our own address space and thread handle are always given to us
pub const SELF_AS: AddressSpaceHandle = AddressSpaceHandle(0);
pub const SELF_THREAD: ThreadHandle = ThreadHandle(1);
impl From<usize> for Status {
fn from(value: usize) -> Self {
match value {
1 => Self::InvalidArgument,
2 => Self::BadAddress,
3 => Self::BadFileDescriptor,
4 => Self::NoSuchTask,
5 => Self::OutOfMemory,
6 => Self::BadHandle,
_ => Self::InvalidArgument,
}
}
}
#[repr(u64)]
pub enum SyscallNumber {
Yield = 1,
Exit = 2,
Write = 3,
Send = 4,
Recv = 5,
FrameAlloc = 6,
FrameDealloc = 7,
AsCreate = 8,
Map = 9,
Unmap = 10,
TaskCreate = 11,
}
pub fn sys_yield() {
unsafe {
asm!(
"syscall",
in("rax") 1usize,
lateout("rcx") _,
lateout("r11") _,
);
}
}
fn debug_write(buf: &str) -> Result<(), Status> {
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") 1,
in("rsi") buf.as_ptr(),
in("rdx") buf.len(),
in("r10") 0,
inlateout("rax") SyscallNumber::Write as usize => status,
lateout("rcx") _,
lateout("r11") _
);
if status != 0 {
Err(status.into())
} else {
Ok(())
}
}
}
struct DebugWriter;
impl core::fmt::Write for DebugWriter {
fn write_str(&mut self, value: &str) -> core::fmt::Result {
debug_write(value).map_err(|_| core::fmt::Error)
}
}
#[doc(hidden)]
pub fn __print(arguments: core::fmt::Arguments<'_>) {
use core::fmt::Write;
let _ = DebugWriter.write_fmt(arguments);
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {{
$crate::__print(core::format_args!($($arg)*));
}};
}
#[macro_export]
macro_rules! println {
() => {{
$crate::print!("\n");
}};
($($arg:tt)*) => {{
$crate::print!("{}\n", core::format_args!($($arg)*));
}};
}
pub fn sys_exit(exit_code: usize) -> ! {
unsafe {
asm!(
"syscall",
in("rdi") exit_code,
in("rax") SyscallNumber::Exit as usize,
options(noreturn)
);
}
}
pub fn sys_send(dest_task_id: usize, msg: &[u8]) -> Result<(), Status> {
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") dest_task_id,
in("rsi") msg.as_ptr(),
in("rdx") msg.len(),
inlateout("rax") SyscallNumber::Send as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(())
}
}
}
pub fn sys_recv(buf: &mut [u8]) -> Result<(usize, usize), Status> {
let mut actual_len: usize = 0;
let mut sender: usize = 0;
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") buf.as_mut_ptr(),
in("rsi") buf.len(),
in("rdx") &raw mut actual_len as usize,
in("r10") &raw mut sender as usize,
inlateout("rax") SyscallNumber::Recv as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok((actual_len, sender))
}
}
}
pub fn sys_frame_alloc() -> Result<FrameHandle, Status> {
let mut handle: usize = 0;
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") &raw mut handle as usize,
inlateout("rax") SyscallNumber::FrameAlloc as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(FrameHandle(handle))
}
}
}
pub fn sys_frame_dealloc(frame_handle: FrameHandle) -> Result<(), Status> {
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") frame_handle.0,
inlateout("rax") SyscallNumber::FrameDealloc as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(())
}
}
}
pub fn sys_as_create() -> Result<AddressSpaceHandle, Status> {
let mut handle: usize = 0;
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") &raw mut handle as usize,
inlateout("rax") SyscallNumber::AsCreate as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(AddressSpaceHandle(handle))
}
}
}
pub fn sys_map(
as_handle: AddressSpaceHandle,
frame_handle: FrameHandle,
virtual_addr: usize,
permissions: usize,
) -> Result<MappingHandle, Status> {
let mut handle: usize = 0;
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") as_handle.0,
in("rsi") frame_handle.0,
in("rdx") virtual_addr,
in("r10") permissions,
in("r8") &raw mut handle as usize,
inlateout("rax") SyscallNumber::Map as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(MappingHandle(handle))
}
}
}
pub fn sys_unmap(mapping_handle: MappingHandle) -> Result<(), Status> {
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") mapping_handle.0,
inlateout("rax") SyscallNumber::Unmap as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(())
}
}
}
pub fn sys_task_create(
as_handle: AddressSpaceHandle,
entry: usize,
user_stack: usize,
) -> Result<ThreadHandle, Status> {
let mut handle: usize = 0;
unsafe {
let status: usize;
asm!(
"syscall",
in("rdi") as_handle.0,
in("rsi") entry,
in("rdx") user_stack,
in("r10") &raw mut handle as usize,
inlateout("rax") SyscallNumber::TaskCreate as usize => status,
lateout("rcx") _,
lateout("r11") _,
);
if status != 0 {
Err(status.into())
} else {
Ok(ThreadHandle(handle))
}
}
}
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "echo"
version = "0.1.0"
edition = "2024"
[dependencies]
dusk-sys = { path = "../dusk-sys" }
[[bin]]
name = "echo"
test = false
bench = false
+23
View File
@@ -0,0 +1,23 @@
#![no_std]
#![no_main]
use dusk_sys::{println, sys_exit, sys_recv, sys_send};
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
let mut out = [0u8; 128];
loop {
let (actual_len, sender) = sys_recv(&mut out).unwrap();
println!(
"[echo] Received: {}",
core::str::from_utf8(&out[..actual_len]).unwrap()
);
sys_send(sender, &out[..actual_len]).unwrap();
}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("{info}");
sys_exit(1);
}
-115
View File
@@ -1,115 +0,0 @@
#![no_std]
#![no_main]
use core::arch::asm;
use core::cell::UnsafeCell;
use core::fmt::Write;
fn sys_yield() {
unsafe {
asm!("mov rax, 1", "syscall");
}
}
fn sys_write(fd: usize, buf: &str) -> Result<(), usize> {
unsafe {
let status;
asm!(
"mov rax, 3",
"syscall",
in("rdi") fd,
in("rsi") buf.as_ptr(),
in("rdx") buf.len(),
in("r10") 0,
lateout("rax") status,
);
if status != 0 { Err(status) } else { Ok(()) }
}
}
struct Writer;
impl core::fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
sys_write(1, s).map_err(|_| core::fmt::Error)?;
Ok(())
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => (let _ = $crate::Writer.write_fmt(format_args!($($arg)*)););
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
pub fn sys_exit(exit_code: usize) -> ! {
unsafe {
asm!(
"mov rax, 2",
"syscall",
in("rdi") exit_code,
options(noreturn)
);
}
}
struct Heap {
pub data: UnsafeCell<[u8; 1024]>,
}
impl Heap {
const fn new() -> Self {
Self {
data: UnsafeCell::new([0; 1024]),
}
}
fn len(&self) -> usize {
unsafe { (*self.data.get()).len() }
}
}
impl core::ops::Deref for Heap {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { (*self.data.get()).as_ref() }
}
}
unsafe impl Sync for Heap {}
static HEAP: Heap = Heap::new();
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
(0..100).for_each(|i| {
println!("Hello {}", i);
sys_yield();
});
let len = HEAP.len();
for i in 0..len {
unsafe { HEAP.data.get().as_mut().unwrap()[i] = i as u8 };
}
(0..1024).for_each(|i| {
println!("{}", unsafe { HEAP.data.get().as_ref().unwrap()[i] });
sys_yield();
});
sys_exit(0);
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("{info}");
sys_exit(1);
}
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "omega3"
version = "0.1.0"
edition = "2024"
[dependencies]
dusk-sys = { path = "../dusk-sys" }
[[bin]]
name = "omega3"
test = false
bench = false
+73
View File
@@ -0,0 +1,73 @@
// CPIO newc archive parser
#[repr(C)]
struct Header {
pub c_magic: [u8; 6],
pub c_ino: [u8; 8],
pub c_mode: [u8; 8],
pub c_uid: [u8; 8],
pub c_gid: [u8; 8],
pub c_nlink: [u8; 8],
pub c_mtime: [u8; 8],
pub c_filesize: [u8; 8],
pub c_devmajor: [u8; 8],
pub c_devminor: [u8; 8],
pub c_rdevmajor: [u8; 8],
pub c_rdevminor: [u8; 8],
pub c_namesize: [u8; 8],
pub c_check: [u8; 8],
}
impl Header {
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() < core::mem::size_of::<Header>() {
return None;
}
let header: Header = unsafe { core::ptr::read(bytes.as_ptr() as *const Header) };
if header.c_magic != *b"070701" {
return None;
}
Some(header)
}
}
pub fn find_file<'a>(archive: *const u8, target: &str) -> Option<&'a [u8]> {
let mut offset = 0;
loop {
let header = Header::from_bytes(&unsafe {
core::slice::from_raw_parts(archive.add(offset), core::mem::size_of::<Header>())
})?;
let header_start = offset;
offset += core::mem::size_of::<Header>();
let file_len =
usize::from_str_radix(core::str::from_utf8(&header.c_filesize).ok()?, 16).ok()?;
let name_len =
usize::from_str_radix(core::str::from_utf8(&header.c_namesize).ok()?, 16).ok()?;
let name_bytes = &unsafe { core::slice::from_raw_parts(archive.add(offset), name_len) };
let name = core::str::from_utf8(name_bytes)
.ok()?
.trim_end_matches('\0');
if name == "TRAILER!!!" {
break;
}
let data_start = header_start + ((core::mem::size_of::<Header>() + name_len + 3) & !3);
if name == target {
return Some(&unsafe {
core::slice::from_raw_parts(archive.add(data_start), file_len)
});
}
offset = data_start + ((file_len + 3) & !3);
}
None
}
+425
View File
@@ -0,0 +1,425 @@
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ElfIsa {
None,
Sparc,
X86,
Mips,
Ppc,
Arm,
SuperH,
Ia64,
Amd64,
AArch64,
Riscv,
}
impl ElfIsa {
fn from_u16(value: u16) -> Result<Self, ElfError> {
match value {
0x00 => Ok(Self::None),
0x02 => Ok(Self::Sparc),
0x03 => Ok(Self::X86),
0x08 => Ok(Self::Mips),
0x14 => Ok(Self::Ppc),
0x28 => Ok(Self::Arm),
0x2A => Ok(Self::SuperH),
0x32 => Ok(Self::Ia64),
0x3E => Ok(Self::Amd64),
0xB7 => Ok(Self::AArch64),
0xF3 => Ok(Self::Riscv),
_ => Err(ElfError::InvalidElf),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ElfClass {
Elf32,
Elf64,
}
impl ElfClass {
fn from_u8(value: u8) -> Result<Self, ElfError> {
match value {
1 => Ok(Self::Elf32),
2 => Ok(Self::Elf64),
_ => Err(ElfError::InvalidElf),
}
}
const fn header_size(self) -> u16 {
match self {
Self::Elf32 => 52,
Self::Elf64 => 64,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Endianness {
Little,
Big,
}
impl Endianness {
fn from_u8(value: u8) -> Result<Self, ElfError> {
match value {
1 => Ok(Self::Little),
2 => Ok(Self::Big),
_ => Err(ElfError::InvalidElf),
}
}
}
#[repr(u16)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ElfType {
Relocatable = 1,
Executable = 2,
SharedObject = 3,
Core = 4,
}
impl ElfType {
fn from_u16(value: u16) -> Result<Self, ElfError> {
match value {
1 => Ok(Self::Relocatable),
2 => Ok(Self::Executable),
3 => Ok(Self::SharedObject),
4 => Ok(Self::Core),
_ => Err(ElfError::InvalidElf),
}
}
}
#[derive(Debug)]
#[allow(unused)]
pub struct ElfHeader {
magic: [u8; 4],
pub class: ElfClass,
endianness: Endianness,
version: u8,
os_abi: u8,
_reserved: [u8; 8],
pub object_type: ElfType,
pub machine: ElfIsa,
version_1: u32,
entry: u64, // 2115136
program_header_offset: u64, // 64
section_header_offset: u64, // 2759752
flags: u32, // 0
header_size: u16, // 64
program_header_entry_size: u16, // 56
program_header_count: u16, // 6
section_header_entry_size: u16, // 64
section_header_count: u16, // 17
section_name_index: u16, // 15
}
#[derive(Debug)]
pub enum ElfError {
InvalidElf,
}
impl ElfHeader {
pub fn parse(bytes: &[u8]) -> Result<Self, ElfError> {
let mut reader = Reader::new(bytes);
let magic = reader.read_array()?;
if magic != *b"\x7fELF" {
return Err(ElfError::InvalidElf);
}
let class = ElfClass::from_u8(reader.read_u8()?)?;
let endianness = Endianness::from_u8(reader.read_u8()?)?;
reader.set_endianness(endianness);
let version = reader.read_u8()?;
let os_abi = reader.read_u8()?;
let reserved = reader.read_array()?;
let object_type = ElfType::from_u16(reader.read_u16()?)?;
let machine = ElfIsa::from_u16(reader.read_u16()?)?;
let version_1 = reader.read_u32()?;
let entry = reader.read_word(class)?;
let program_header_offset = reader.read_word(class)?;
let section_header_offset = reader.read_word(class)?;
let flags = reader.read_u32()?;
let header_size = reader.read_u16()?;
let program_header_entry_size = reader.read_u16()?;
let program_header_count = reader.read_u16()?;
let section_header_entry_size = reader.read_u16()?;
let section_header_count = reader.read_u16()?;
let section_name_index = reader.read_u16()?;
if header_size != class.header_size() {
return Err(ElfError::InvalidElf);
}
Ok(Self {
magic,
class,
endianness,
version,
os_abi,
_reserved: reserved,
object_type,
machine,
version_1,
entry,
program_header_offset,
section_header_offset,
flags,
header_size,
program_header_entry_size,
program_header_count,
section_header_entry_size,
section_header_count,
section_name_index,
})
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProgramHeaderType {
Null = 0,
Load = 1,
Dynamic = 2,
Interpreter = 3,
Note = 4,
Shlib = 5,
Phdr = 6,
GnuStack = 0x6474e551,
Relro = 0x6474e552,
Other(u32),
}
impl ProgramHeaderType {
fn from_u32(value: u32) -> Self {
match value {
0 => Self::Null,
1 => Self::Load,
2 => Self::Dynamic,
3 => Self::Interpreter,
4 => Self::Note,
5 => Self::Shlib,
6 => Self::Phdr,
0x6474e551 => Self::GnuStack,
0x6474e552 => Self::Relro,
_ => Self::Other(value),
}
}
}
#[derive(Debug)]
#[allow(unused)]
pub struct ProgramHeader {
pub segment_type: ProgramHeaderType,
pub flags: u32,
pub file_offset: u64,
pub virtual_address: u64,
_physical_address: u64,
pub file_size: u64,
pub memory_size: u64,
pub alignment: u64,
}
impl ProgramHeader {
pub fn parse(bytes: &[u8], class: ElfClass, endianness: Endianness) -> Result<Self, ElfError> {
let mut reader = Reader::new(bytes);
reader.set_endianness(endianness);
let segment_type = ProgramHeaderType::from_u32(reader.read_u32()?);
let flags = if class == ElfClass::Elf64 {
reader.read_u32()?
} else {
0
};
let file_offset = reader.read_word(class)?;
let virtual_address = reader.read_word(class)?;
let physical_address = reader.read_word(class)?;
let file_size = reader.read_word(class)?;
let memory_size = reader.read_word(class)?;
let flags = if class == ElfClass::Elf32 {
reader.read_u32()?
} else {
flags
};
let alignment = reader.read_word(class)?;
Ok(Self {
segment_type,
flags,
file_offset,
virtual_address,
_physical_address: physical_address,
file_size,
memory_size,
alignment,
})
}
}
pub struct Elf<'a> {
bytes: &'a [u8],
header: ElfHeader,
}
impl<'a> Elf<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> {
let header = ElfHeader::parse(bytes)?;
Ok(Self { bytes, header })
}
pub fn program_headers(&self) -> Result<ProgramHeaders<'_>, ElfError> {
let offset =
usize::try_from(self.header.program_header_offset).map_err(|_| ElfError::InvalidElf)?;
let entry_size = usize::from(self.header.program_header_entry_size);
let count = usize::from(self.header.program_header_count);
let expected_entry_size = match self.header.class {
ElfClass::Elf32 => 32,
ElfClass::Elf64 => 56,
};
if entry_size != expected_entry_size {
return Err(ElfError::InvalidElf);
}
let table_size = entry_size.checked_mul(count).ok_or(ElfError::InvalidElf)?;
let table_end = offset.checked_add(table_size).ok_or(ElfError::InvalidElf)?;
let bytes = self
.bytes
.get(offset..table_end)
.ok_or(ElfError::InvalidElf)?;
Ok(ProgramHeaders {
bytes,
class: self.header.class,
endianness: self.header.endianness,
entry_size,
remaining: count,
})
}
pub fn bytes(&self) -> &[u8] {
self.bytes
}
#[allow(unused)]
pub fn machine(&self) -> ElfIsa {
self.header.machine
}
pub fn entry(&self) -> usize {
self.header.entry as usize
}
}
pub struct ProgramHeaders<'a> {
bytes: &'a [u8],
class: ElfClass,
endianness: Endianness,
entry_size: usize,
remaining: usize,
}
impl<'a> Iterator for ProgramHeaders<'a> {
type Item = Result<ProgramHeader, ElfError>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let entry = match self.bytes.get(..self.entry_size) {
Some(entry) => entry,
None => {
self.remaining = 0;
return None;
}
};
self.bytes = &self.bytes[self.entry_size..];
self.remaining -= 1;
Some(ProgramHeader::parse(entry, self.class, self.endianness))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl ExactSizeIterator for ProgramHeaders<'_> {}
struct Reader<'a> {
bytes: &'a [u8],
offset: usize,
endianness: Endianness,
}
impl<'a> Reader<'a> {
const fn new(bytes: &'a [u8]) -> Self {
Self {
bytes,
offset: 0,
endianness: Endianness::Little,
}
}
fn set_endianness(&mut self, endianness: Endianness) {
self.endianness = endianness;
}
fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ElfError> {
let end = self.offset.checked_add(N).ok_or(ElfError::InvalidElf)?;
let bytes = self
.bytes
.get(self.offset..end)
.ok_or(ElfError::InvalidElf)?;
self.offset = end;
bytes.try_into().map_err(|_| ElfError::InvalidElf)
}
fn read_u8(&mut self) -> Result<u8, ElfError> {
Ok(self.read_array::<1>()?[0])
}
fn read_u16(&mut self) -> Result<u16, ElfError> {
let bytes = self.read_array()?;
Ok(match self.endianness {
Endianness::Little => u16::from_le_bytes(bytes),
Endianness::Big => u16::from_be_bytes(bytes),
})
}
fn read_u32(&mut self) -> Result<u32, ElfError> {
let bytes = self.read_array()?;
Ok(match self.endianness {
Endianness::Little => u32::from_le_bytes(bytes),
Endianness::Big => u32::from_be_bytes(bytes),
})
}
fn read_u64(&mut self) -> Result<u64, ElfError> {
let bytes = self.read_array()?;
Ok(match self.endianness {
Endianness::Little => u64::from_le_bytes(bytes),
Endianness::Big => u64::from_be_bytes(bytes),
})
}
fn read_word(&mut self, class: ElfClass) -> Result<u64, ElfError> {
match class {
ElfClass::Elf32 => Ok(u64::from(self.read_u32()?)),
ElfClass::Elf64 => self.read_u64(),
}
}
}
+113
View File
@@ -0,0 +1,113 @@
#![no_std]
#![no_main]
mod cpio;
mod elf;
use dusk_sys::{
AddressSpaceHandle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map,
sys_task_create, sys_unmap, sys_yield,
};
// Mapped into the root task's address space by the kernel.
static INITRAMFS_START: usize = 0x4000_0000;
const SCRATCH_PAGE: usize = 0x8000_0000;
const STACK_TOP: usize = 0x0000_7FFF_FFFF_F000;
const STACK_PAGES: usize = 4;
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
println!(r#"-----------------------------"#);
println!(r#" .d88888888b. .d88888b. "#);
println!(r#" d88P" "Y88b 88P" "Y88 "#);
println!(r#" 888 888 .od88P "#);
println!(r#" Y88b d88P "Y88b "#);
println!(r#" "88bo od88" 88b d88 "#);
println!(r#" d88888 88888b "Y88888P" "#);
println!(r#"----- Omega3 Dusk Root Server"#);
let echo_bytes = cpio::find_file(INITRAMFS_START as *const u8, "echo.elf").unwrap();
let echo_elf = elf::Elf::parse(echo_bytes).unwrap();
let echo_as = sys_as_create().unwrap();
let echo_entry = load_elf(&echo_elf, echo_as);
map_stack(echo_as, STACK_TOP, STACK_PAGES);
let _ = sys_task_create(echo_as, echo_entry, STACK_TOP).unwrap();
let client_bytes = cpio::find_file(INITRAMFS_START as *const u8, "client.elf").unwrap();
let client_elf = elf::Elf::parse(client_bytes).unwrap();
let client_as = sys_as_create().unwrap();
let client_entry = load_elf(&client_elf, client_as);
map_stack(client_as, STACK_TOP, STACK_PAGES);
let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap();
// call a bogus system call
unsafe {
core::arch::asm!("syscall", in("rax") 134);
}
sys_exit(0);
}
fn load_elf(elf: &elf::Elf, target_as: AddressSpaceHandle) -> usize {
for header in elf.program_headers().unwrap() {
let header = header.unwrap();
if header.segment_type != elf::ProgramHeaderType::Load || header.memory_size == 0 {
continue;
}
let mut perms = 0;
if header.flags & 2 != 0 {
perms |= 1 << 0;
}
if header.flags & 1 != 0 {
perms |= 1 << 1;
}
let segment_start = header.virtual_address as usize;
let segment_end = segment_start + header.memory_size as usize;
let file_end = segment_start + header.file_size as usize;
let page_start = segment_start & !0xFFF;
for page in (page_start..segment_end).step_by(0x1000) {
let frame = sys_frame_alloc().unwrap();
let scratch_handle = sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap();
unsafe {
core::ptr::write_bytes(SCRATCH_PAGE as *mut u8, 0, 0x1000);
let copy_start = page.max(segment_start).min(page + 0x1000);
let copy_end = (page + 0x1000).min(file_end).max(copy_start);
if copy_end > copy_start {
let page_offset = copy_start - page;
let file_offset = header.file_offset as usize + (copy_start - segment_start);
let len = copy_end - copy_start;
core::ptr::copy_nonoverlapping(
elf.bytes().as_ptr().add(file_offset),
(SCRATCH_PAGE as *mut u8).add(page_offset),
len,
);
}
}
sys_unmap(scratch_handle).unwrap();
sys_map(target_as, frame, page, perms).unwrap();
}
}
elf.entry()
}
fn map_stack(target_as: AddressSpaceHandle, stack_top: usize, pages: usize) {
for i in 1..=pages {
let frame = sys_frame_alloc().unwrap();
let page_addr = stack_top - i * 0x1000;
sys_map(target_as, frame, page_addr, 0b01).unwrap();
}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("{info}");
sys_exit(1);
}