Compare commits
4 Commits
dd596d5378
..
trunk
| Author | SHA1 | Date | |
|---|---|---|---|
|
e5c2889acc
|
|||
|
bf1a81cf82
|
|||
|
3308fd2959
|
|||
|
028ac8fb13
|
Generated
+22
-1
@@ -2,6 +2,13 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dusk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dusk"
|
||||
version = "0.1.0"
|
||||
@@ -10,11 +17,25 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "init"
|
||||
name = "dusk-sys"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "echo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dusk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limine"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29363c0f37e66e18575fadf7141c56ee7ea04ae5fecbeb25eff303f77af203a9"
|
||||
|
||||
[[package]]
|
||||
name = "omega3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dusk-sys",
|
||||
]
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
members = [".", "userspace/init"]
|
||||
members = [".", "userspace/*"]
|
||||
|
||||
[dependencies]
|
||||
limine = "0.6.5"
|
||||
|
||||
@@ -67,10 +67,14 @@ prepare-bin-files:
|
||||
mkdir -p ${INITRAMFS_PATH}
|
||||
|
||||
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
|
||||
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
|
||||
(cd ${INITRAMFS_PATH} && find . -mindepth 1 | cpio -o -H newc) > ${ARTIFACTS_PATH}/initramfs.img
|
||||
@@ -133,7 +137,7 @@ compile-binaries:
|
||||
|
||||
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; \
|
||||
fi
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ const APIC_TIMER_INITIAL_COUNT: u32 = 0x380;
|
||||
const APIC_TIMER_CURRENT_COUNT: u32 = 0x390;
|
||||
const APIC_TIMER_DIVIDE_CONFIG: u32 = 0x3E0;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum LocalApicAccess {
|
||||
X2Apic,
|
||||
XApic,
|
||||
@@ -88,7 +87,6 @@ pub enum LocalApicError {
|
||||
NotBootSystemProcessor,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalApic {
|
||||
id: u32,
|
||||
access: LocalApicAccess,
|
||||
|
||||
@@ -103,7 +103,7 @@ pub enum CpuFeaturesError {
|
||||
InvalidVirtualAddressWidth,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct CpuFeatures {
|
||||
pub nx_supported: bool,
|
||||
pub nx_enabled: bool,
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
use crate::arch::{
|
||||
apic, timer,
|
||||
x86_64::interrupts::idt::{self, InterruptStackFrame},
|
||||
};
|
||||
use super::idt::{self, InterruptFrame, stub_no_err};
|
||||
use crate::arch::{apic, timer};
|
||||
|
||||
pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1;
|
||||
pub const APIC_TIMER_VECTOR: u8 = 0xFD;
|
||||
pub const APIC_ERROR_VECTOR: u8 = 0xFE;
|
||||
pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF;
|
||||
|
||||
extern "x86-interrupt" fn error_handler(_frame: InterruptStackFrame) {
|
||||
apic::record_error();
|
||||
apic::end_of_interrupt();
|
||||
}
|
||||
stub_no_err!(stub_pit_calibration, 0xF1);
|
||||
stub_no_err!(stub_apic_timer, 0xFD);
|
||||
stub_no_err!(stub_apic_error, 0xFE);
|
||||
stub_no_err!(stub_apic_spurious, 0xFF);
|
||||
|
||||
extern "x86-interrupt" fn timer_handler(_frame: InterruptStackFrame) {
|
||||
apic::record_timer();
|
||||
apic::end_of_interrupt();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn pit_calibration_handler(_frame: InterruptStackFrame) {
|
||||
pub(super) fn handle(frame: &mut InterruptFrame) {
|
||||
match frame.vector as u8 {
|
||||
PIT_CALIBRATION_VECTOR => {
|
||||
timer::record_pit_calibration();
|
||||
apic::end_of_interrupt();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame) {
|
||||
APIC_TIMER_VECTOR => {
|
||||
apic::record_timer();
|
||||
apic::end_of_interrupt();
|
||||
}
|
||||
APIC_ERROR_VECTOR => {
|
||||
apic::record_error();
|
||||
apic::end_of_interrupt();
|
||||
}
|
||||
APIC_SPURIOUS_VECTOR => {
|
||||
// No EOI
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn install(idt: &mut idt::Idt) {
|
||||
idt.set_handler(PIT_CALIBRATION_VECTOR, pit_calibration_handler, 0);
|
||||
idt.set_handler(APIC_ERROR_VECTOR, error_handler, 0);
|
||||
idt.set_handler(APIC_TIMER_VECTOR, timer_handler, 0);
|
||||
idt.set_handler(APIC_SPURIOUS_VECTOR, spurious_handler, 0);
|
||||
idt.set_handler(PIT_CALIBRATION_VECTOR, stub_pit_calibration, 0);
|
||||
idt.set_handler(APIC_ERROR_VECTOR, stub_apic_error, 0);
|
||||
idt.set_handler(APIC_TIMER_VECTOR, stub_apic_timer, 0);
|
||||
idt.set_handler(APIC_SPURIOUS_VECTOR, stub_apic_spurious, 0);
|
||||
}
|
||||
|
||||
@@ -1,79 +1,115 @@
|
||||
use core::arch::asm;
|
||||
|
||||
use super::idt::{self, InterruptStackFrame};
|
||||
use crate::{hcf, println};
|
||||
|
||||
macro_rules! fatal_without_error_code {
|
||||
($handler:ident, $name:literal) => {
|
||||
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) {
|
||||
fatal_exception($name, &frame, None);
|
||||
}
|
||||
use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err};
|
||||
use crate::{
|
||||
hcf, println,
|
||||
task::tcb::{ExitReason, Fault},
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! fatal_with_error_code {
|
||||
($handler:ident, $name:literal) => {
|
||||
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) {
|
||||
fatal_exception($name, &frame, Some(error_code));
|
||||
}
|
||||
};
|
||||
}
|
||||
stub_no_err!(stub_divide_error, 0);
|
||||
stub_no_err!(stub_debug, 1);
|
||||
stub_no_err!(stub_non_maskable_interrupt, 2);
|
||||
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);
|
||||
|
||||
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
|
||||
report_exception("BREAKPOINT", &frame, None);
|
||||
}
|
||||
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",
|
||||
];
|
||||
|
||||
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
|
||||
report_exception("PAGE FAULT", &frame, Some(error_code));
|
||||
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(error_code);
|
||||
print_page_fault_error(frame.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");
|
||||
fatal_exception(name, &frame.stack_frame, Some(frame.error_code));
|
||||
}
|
||||
|
||||
println!("User test exit");
|
||||
hcf();
|
||||
let fault = match vector {
|
||||
// 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) {
|
||||
idt.set_handler(0, divide_error_handler, 0);
|
||||
idt.set_handler(1, debug_handler, 0);
|
||||
idt.set_handler(2, non_maskable_interrupt_handler, 0);
|
||||
idt.set_user_handler(3, breakpoint_handler, 0);
|
||||
idt.set_handler(6, invalid_opcode_handler, 0);
|
||||
idt.set_handler(7, device_not_available_handler, 0);
|
||||
idt.set_error_code_handler(8, double_fault_handler, 1);
|
||||
idt.set_error_code_handler(10, invalid_tss_handler, 0);
|
||||
idt.set_error_code_handler(11, segment_not_present_handler, 0);
|
||||
idt.set_error_code_handler(12, stack_segment_fault_handler, 0);
|
||||
idt.set_error_code_handler(13, general_protection_handler, 0);
|
||||
idt.set_error_code_handler(14, page_fault_handler, 0);
|
||||
idt.set_handler(16, x87_floating_point_handler, 0);
|
||||
idt.set_error_code_handler(17, alignment_check_handler, 0);
|
||||
idt.set_handler(18, machine_check_handler, 0);
|
||||
idt.set_handler(19, simd_floating_point_handler, 0);
|
||||
idt.set_handler(0, stub_divide_error, 0);
|
||||
idt.set_handler(1, stub_debug, 0);
|
||||
idt.set_handler(2, stub_non_maskable_interrupt, 0);
|
||||
idt.set_user_handler(3, stub_breakpoint, 0);
|
||||
idt.set_handler(6, stub_invalid_opcode, 0);
|
||||
idt.set_handler(7, stub_device_not_available, 0);
|
||||
idt.set_handler(8, stub_double_fault, 1);
|
||||
idt.set_handler(10, stub_invalid_tss, 0);
|
||||
idt.set_handler(11, stub_segment_not_present, 0);
|
||||
idt.set_handler(12, stub_stack_segment_fault, 0);
|
||||
idt.set_handler(13, stub_general_protection, 0);
|
||||
idt.set_handler(14, stub_page_fault, 0);
|
||||
idt.set_handler(16, stub_x87_floating_point, 0);
|
||||
idt.set_handler(17, stub_alignment_check, 0);
|
||||
idt.set_handler(18, stub_machine_check, 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 {
|
||||
|
||||
@@ -18,7 +18,7 @@ struct IdtEntry {
|
||||
|
||||
impl IdtEntry {
|
||||
const fn missing() -> Self {
|
||||
return Self {
|
||||
Self {
|
||||
offset_low: 0,
|
||||
code_selector: 0,
|
||||
ist: 0,
|
||||
@@ -26,7 +26,7 @@ impl IdtEntry {
|
||||
offset_middle: 0,
|
||||
offset_high: 0,
|
||||
reserved: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ struct IdtPointer {
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(super) struct InterruptStackFrame {
|
||||
pub struct InterruptStackFrame {
|
||||
pub instruction_pointer: VirtualAddr,
|
||||
pub code_segment: u64,
|
||||
pub cpu_flags: u64,
|
||||
@@ -46,14 +46,36 @@ pub(super) struct InterruptStackFrame {
|
||||
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 PRESENT: u8 = 1 << 7;
|
||||
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
|
||||
const USER_DPL: u8 = 3 << 5;
|
||||
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
|
||||
|
||||
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame);
|
||||
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
|
||||
pub(super) type RawHandler = unsafe extern "C" fn();
|
||||
|
||||
pub(super) struct Idt {
|
||||
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);
|
||||
}
|
||||
|
||||
pub(super) fn set_error_code_handler(
|
||||
&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) {
|
||||
pub(super) fn set_user_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
|
||||
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::<IdtPointer>() == 10);
|
||||
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() {
|
||||
let mut idt = Idt::new();
|
||||
|
||||
@@ -31,7 +31,6 @@ pub struct RedirectionConfig {
|
||||
pub trigger: TriggerMode,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IoApic {
|
||||
base: VirtualAddr,
|
||||
global_interrupt_base: u32,
|
||||
|
||||
@@ -64,7 +64,6 @@ pub enum InterruptInitError {
|
||||
PitNotHandled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptController {
|
||||
local_apic: apic::LocalApic,
|
||||
io_apic: io_apic::IoApic,
|
||||
@@ -164,8 +163,6 @@ pub unsafe fn enter_user(
|
||||
user_instruction_pointer: VirtualAddr,
|
||||
user_stack_pointer: VirtualAddr,
|
||||
) -> ! {
|
||||
println!("Entering user mode");
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov ds, {user_data_selector:x}",
|
||||
|
||||
@@ -4,14 +4,14 @@ use crate::{
|
||||
arch::x86_64::cpu::CpuFeatures,
|
||||
memory::{
|
||||
CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions,
|
||||
PhysicalAddr, VirtualAddr,
|
||||
PageTableMapping, PhysicalAddr, VirtualAddr,
|
||||
},
|
||||
};
|
||||
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
pub const PAGE_TABLE_ENTRIES: usize = 512;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PagingConfig {
|
||||
physical_address_bits: u8,
|
||||
global_pages: bool,
|
||||
@@ -42,7 +42,7 @@ impl PagingConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
enum PagingMode {
|
||||
FourLevel,
|
||||
FiveLevel,
|
||||
@@ -77,7 +77,7 @@ impl PagingMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum PageTableLevel {
|
||||
Pml5,
|
||||
Pml4,
|
||||
@@ -106,14 +106,13 @@ impl PageTableLevel {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PageTableEntryError {
|
||||
PhysicalAddressTooLarge,
|
||||
NoExecuteUnsupported,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
struct PageTableEntry(u64);
|
||||
|
||||
impl PageTableEntry {
|
||||
@@ -200,6 +199,14 @@ impl PageTableEntry {
|
||||
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 {
|
||||
self.0 & Self::USER_ACCESSIBLE != 0
|
||||
}
|
||||
@@ -227,9 +234,16 @@ impl PageTableEntry {
|
||||
|
||||
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 {
|
||||
InvalidVirtualAddress,
|
||||
VirtualAddressUnaligned,
|
||||
@@ -264,7 +278,6 @@ pub(crate) enum PageTableCreateError {
|
||||
OutOfFrames,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PageTable {
|
||||
pub direct_map: DirectMap,
|
||||
config: PagingConfig,
|
||||
@@ -366,6 +379,52 @@ impl PageTable {
|
||||
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(
|
||||
&self,
|
||||
parent: FrameAddr,
|
||||
|
||||
@@ -15,7 +15,6 @@ const IA32_KERNEL_GS_BASE: u32 = 0xC000_0102;
|
||||
const RFLAGS_MASK: u64 = 0x257FD5; // Clear IF, TF, DF, IOPL, NT, AC
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct SyscallFrame {
|
||||
pub r15: u64,
|
||||
pub r14: u64,
|
||||
|
||||
@@ -38,6 +38,10 @@ pub fn find_file<'a>(archive: &'a [u8], target: &str) -> Option<&'a [u8]> {
|
||||
let mut offset = 0;
|
||||
|
||||
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_start = offset;
|
||||
offset += core::mem::size_of::<Header>();
|
||||
|
||||
+80
-398
@@ -1,423 +1,105 @@
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ElfIsa {
|
||||
None,
|
||||
Sparc,
|
||||
X86,
|
||||
Mips,
|
||||
Ppc,
|
||||
Arm,
|
||||
SuperH,
|
||||
Ia64,
|
||||
Amd64,
|
||||
AArch64,
|
||||
Riscv,
|
||||
}
|
||||
pub struct ElfError;
|
||||
|
||||
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)]
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const MACHINE: u16 = 62;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const MACHINE: u16 = 183;
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
const MACHINE: u16 = 243;
|
||||
|
||||
pub struct Elf<'a> {
|
||||
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> {
|
||||
pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> {
|
||||
let header = ElfHeader::parse(bytes)?;
|
||||
|
||||
Ok(Self { bytes, header })
|
||||
let header = bytes.get(..64).ok_or(ElfError)?;
|
||||
// Bootstrap images are static ELF64 executables in the native ISA, always LE.
|
||||
if &header[..7] != b"\x7fELF\x02\x01\x01"
|
||||
|| u16_at(header, 16) != 2
|
||||
|| u16_at(header, 18) != MACHINE
|
||||
|| u32_at(header, 20) != 1
|
||||
|| u16_at(header, 52) != 64
|
||||
|| u16_at(header, 54) != 56
|
||||
{
|
||||
return Err(ElfError);
|
||||
}
|
||||
|
||||
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 offset = usize_at(header, 32);
|
||||
let count = usize::from(u16_at(header, 56));
|
||||
let end = offset.checked_add(count * 56).ok_or(ElfError)?;
|
||||
let headers = bytes.get(offset..end).ok_or(ElfError)?;
|
||||
if headers
|
||||
.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);
|
||||
}
|
||||
|
||||
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 {
|
||||
Ok(Self {
|
||||
bytes,
|
||||
class: self.header.class,
|
||||
endianness: self.header.endianness,
|
||||
entry_size,
|
||||
remaining: count,
|
||||
headers,
|
||||
entry: usize_at(header, 24),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
self.bytes
|
||||
pub fn segments(&self) -> impl Iterator<Item = Result<Segment<'a>, ElfError>> + '_ {
|
||||
self.headers
|
||||
.chunks_exact(56)
|
||||
.filter(|h| u32_at(h, 0) == 1)
|
||||
.map(|h| {
|
||||
let offset = usize_at(h, 8);
|
||||
let address = usize_at(h, 16);
|
||||
let file_size = usize_at(h, 32);
|
||||
let memory_size = usize_at(h, 40);
|
||||
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);
|
||||
}
|
||||
|
||||
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),
|
||||
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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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),
|
||||
})
|
||||
// Callers only read fixed offsets within already bounds-checked headers.
|
||||
fn u16_at(bytes: &[u8], offset: usize) -> u16 {
|
||||
let mut value = [0; 2];
|
||||
value.copy_from_slice(&bytes[offset..offset + 2]);
|
||||
u16::from_le_bytes(value)
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
fn u32_at(bytes: &[u8], offset: usize) -> u32 {
|
||||
let mut value = [0; 4];
|
||||
value.copy_from_slice(&bytes[offset..offset + 4]);
|
||||
u32::from_le_bytes(value)
|
||||
}
|
||||
|
||||
fn usize_at(bytes: &[u8], offset: usize) -> usize {
|
||||
let mut value = [0; 8];
|
||||
value.copy_from_slice(&bytes[offset..offset + 8]);
|
||||
u64::from_le_bytes(value) as usize
|
||||
}
|
||||
|
||||
+11
-47
@@ -1,4 +1,3 @@
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![allow(clippy::needless_return)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
@@ -12,12 +11,9 @@ mod platform;
|
||||
mod syscall;
|
||||
mod task;
|
||||
|
||||
use core::arch::global_asm;
|
||||
|
||||
use crate::{
|
||||
debug::serial,
|
||||
memory::{AddressSpace, KernelStackPool, MemoryRegionKind, UserStack},
|
||||
task::tcb::Tcb,
|
||||
memory::{AddressSpace, MemoryRegionKind, init_frame_allocator, init_kernel_address_space},
|
||||
};
|
||||
|
||||
pub struct KernelHandoff {
|
||||
@@ -25,7 +21,6 @@ pub struct KernelHandoff {
|
||||
address_space: AddressSpace,
|
||||
direct_map: memory::DirectMap,
|
||||
boot_info: boot::BootInfo,
|
||||
kernel_stack_pool: KernelStackPool,
|
||||
handoff_frame: memory::OwnedFrame,
|
||||
}
|
||||
|
||||
@@ -54,10 +49,8 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
println!("Entering kernel main...");
|
||||
|
||||
let mut kernel_stack_pool = KernelStackPool::new();
|
||||
|
||||
let kernel_stack = kernel_stack_pool
|
||||
.allocate(&mut address_space, &mut allocator)
|
||||
let kernel_stack =
|
||||
crate::task::scheduler::allocate_kernel_stack(&mut address_space, &mut allocator)
|
||||
.expect("failed to allocate bootstrap stack");
|
||||
|
||||
let handoff_frame = allocator
|
||||
@@ -73,7 +66,6 @@ pub extern "C" fn _start() -> ! {
|
||||
address_space,
|
||||
direct_map,
|
||||
boot_info,
|
||||
kernel_stack_pool,
|
||||
handoff_frame,
|
||||
};
|
||||
|
||||
@@ -91,21 +83,13 @@ pub extern "C" fn _start() -> ! {
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
let (
|
||||
mut allocator,
|
||||
mut address_space,
|
||||
direct_map,
|
||||
boot_info,
|
||||
mut kernel_stack_pool,
|
||||
handoff_frame,
|
||||
) = unsafe {
|
||||
let (mut allocator, mut address_space, direct_map, boot_info, handoff_frame) = unsafe {
|
||||
let handoff = handoff.read();
|
||||
(
|
||||
handoff.allocator,
|
||||
handoff.address_space,
|
||||
handoff.direct_map,
|
||||
handoff.boot_info,
|
||||
handoff.kernel_stack_pool,
|
||||
handoff.handoff_frame,
|
||||
)
|
||||
};
|
||||
@@ -119,9 +103,6 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
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...",);
|
||||
|
||||
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...");
|
||||
|
||||
let interrupt_controller =
|
||||
let _interrupt_controller =
|
||||
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
|
||||
.expect("failed to initialize interrupt controller");
|
||||
|
||||
let user_kernel_stack = kernel_stack_pool
|
||||
.allocate(&mut address_space, &mut allocator)
|
||||
.expect("failed to allocate task kernel stack");
|
||||
|
||||
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,
|
||||
task::bootstrap::spawn(
|
||||
"omega3.elf",
|
||||
&boot_info.initramfs,
|
||||
&mut address_space,
|
||||
&mut allocator,
|
||||
direct_map,
|
||||
);
|
||||
|
||||
let user_stack = UserStack::allocate(&mut user_address_space, &mut allocator)
|
||||
.expect("failed to allocate user stack");
|
||||
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(),
|
||||
);
|
||||
init_frame_allocator(allocator);
|
||||
init_kernel_address_space(address_space);
|
||||
|
||||
task::scheduler::add_task(task).expect("scheduler is full");
|
||||
task::scheduler::start();
|
||||
|
||||
hcf();
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
|
||||
+117
-2
@@ -1,3 +1,5 @@
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
use crate::{
|
||||
arch::{PageTable, PageTableCreateError, PageTableMapError, PageTableUnmapError, PagingConfig},
|
||||
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)]
|
||||
pub enum MapError {
|
||||
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 {
|
||||
Kernel,
|
||||
User,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct AddressSpace {
|
||||
root: PageTable,
|
||||
kind: AddressSpaceKind,
|
||||
@@ -326,6 +437,10 @@ impl AddressSpace {
|
||||
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) {
|
||||
unsafe { self.root.activate() }
|
||||
}
|
||||
|
||||
+46
-4
@@ -1,3 +1,5 @@
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
use crate::memory::{DirectMap, MemoryRegion, MemoryRegionKind, PhysicalAddr, VirtualAddr};
|
||||
|
||||
pub const FRAME_SIZE: usize = 4096;
|
||||
@@ -12,15 +14,57 @@ pub fn align_down_to_frame(addr: usize) -> usize {
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum FrameState {
|
||||
Reserved = 0b00,
|
||||
Free = 0b01,
|
||||
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
|
||||
#[derive(Debug)]
|
||||
struct Bitmap {
|
||||
start: VirtualAddr,
|
||||
frame_count: usize,
|
||||
@@ -70,7 +114,6 @@ pub enum FrameAllocatorInitError {
|
||||
}
|
||||
|
||||
// very very simple bitmap frame/page allocator
|
||||
#[derive(Debug)]
|
||||
pub struct FrameAllocator {
|
||||
bitmap: Bitmap,
|
||||
next_search: usize,
|
||||
@@ -327,7 +370,6 @@ impl FrameAddr {
|
||||
}
|
||||
|
||||
// specifically not Clone or Copy
|
||||
#[derive(Debug)]
|
||||
pub struct OwnedFrame {
|
||||
frame: FrameAddr,
|
||||
}
|
||||
|
||||
+14
-9
@@ -6,8 +6,15 @@ mod user;
|
||||
use core::ops::Add;
|
||||
|
||||
#[allow(unused)]
|
||||
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
||||
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame};
|
||||
pub use address_space::{
|
||||
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)]
|
||||
pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack};
|
||||
#[allow(unused)]
|
||||
@@ -31,8 +38,6 @@ impl<const N: usize> BootString<N> {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_MODULE_PATH_LENGTH: usize = 256;
|
||||
|
||||
pub struct InitramfsImage {
|
||||
pub start: VirtualAddr,
|
||||
pub length: usize,
|
||||
@@ -55,7 +60,7 @@ pub struct KernelMemoryLayout {
|
||||
pub segments: [KernelSegment; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PagePermissions {
|
||||
pub writable: bool,
|
||||
pub executable: bool,
|
||||
@@ -117,7 +122,7 @@ impl Add<usize> for VirtualAddr {
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MemoryRegionKind {
|
||||
Usable,
|
||||
Reserved,
|
||||
@@ -130,20 +135,20 @@ pub enum MemoryRegionKind {
|
||||
MappedReserved,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CachePolicy {
|
||||
Uncacheable,
|
||||
WriteBack,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct MemoryRegion {
|
||||
pub start: PhysicalAddr,
|
||||
pub length: usize,
|
||||
pub kind: MemoryRegionKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DirectMap {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
+7
-2
@@ -215,7 +215,7 @@ pub struct KernelStackPool {
|
||||
}
|
||||
|
||||
impl KernelStackPool {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self { free_slots: 0 }
|
||||
}
|
||||
|
||||
@@ -237,8 +237,13 @@ impl KernelStackPool {
|
||||
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;
|
||||
crate::memory::with_kernel_address_space(|kernel_as| {
|
||||
crate::memory::with_allocator(|allocator| unsafe {
|
||||
stack.destroy(kernel_as, allocator)
|
||||
});
|
||||
});
|
||||
self.free_slots &= !(1 << slot);
|
||||
}
|
||||
}
|
||||
|
||||
+58
-6
@@ -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 fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
|
||||
// TODO: guard against unmapped pages
|
||||
pub fn validate_user_range(
|
||||
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
|
||||
.as_usize()
|
||||
.checked_add(dst.len())
|
||||
@@ -19,7 +61,10 @@ pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
|
||||
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
|
||||
.as_usize()
|
||||
.checked_add(src.len())
|
||||
@@ -35,7 +80,10 @@ pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
|
||||
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 {
|
||||
return Err(Status::InvalidArgument);
|
||||
}
|
||||
@@ -54,7 +102,11 @@ pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status
|
||||
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 {
|
||||
return Err(Status::InvalidArgument);
|
||||
}
|
||||
|
||||
+16
-21
@@ -12,7 +12,6 @@ pub enum AcpiError {
|
||||
MultipleIoApicsUnsupported,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AcpiTables {
|
||||
direct_map: DirectMap,
|
||||
root: RootTable,
|
||||
@@ -215,7 +214,6 @@ impl AcpiTables {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum RootTable {
|
||||
Rsdt(Sdt),
|
||||
Xsdt(Sdt),
|
||||
@@ -251,7 +249,7 @@ impl RootTable {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct Rsdp {
|
||||
signature: [u8; 8],
|
||||
checksum: u8,
|
||||
@@ -261,7 +259,7 @@ struct Rsdp {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct Xsdp {
|
||||
rsdp: Rsdp,
|
||||
length: u32,
|
||||
@@ -271,7 +269,7 @@ struct Xsdp {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct SDTHeader {
|
||||
signature: [u8; 4],
|
||||
length: u32,
|
||||
@@ -284,14 +282,12 @@ struct SDTHeader {
|
||||
creator_revision: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Sdt {
|
||||
physical_addr: PhysicalAddr,
|
||||
length: usize,
|
||||
signature: [u8; 4],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub struct Madt<'a> {
|
||||
acpi: &'a AcpiTables,
|
||||
@@ -301,7 +297,7 @@ pub struct Madt<'a> {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct MadtBody {
|
||||
local_apic_address: u32,
|
||||
flags: u32,
|
||||
@@ -543,21 +539,20 @@ impl<'a> Iterator for MadtEntries<'a> {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MadtEntryHeader {
|
||||
kind: u8,
|
||||
length: u8,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LocalApicEntry {
|
||||
processor_id: u8,
|
||||
id: u8,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IoApicInfo {
|
||||
pub id: u8,
|
||||
pub apic_address: PhysicalAddr,
|
||||
@@ -565,7 +560,7 @@ pub struct IoApicInfo {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IoApicEntry {
|
||||
id: u8,
|
||||
reserved: u8,
|
||||
@@ -574,7 +569,7 @@ pub struct IoApicEntry {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct InterruptSourceOverride {
|
||||
bus: u8,
|
||||
source: u8,
|
||||
@@ -582,19 +577,19 @@ pub struct InterruptSourceOverride {
|
||||
flags: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum InterruptPolarity {
|
||||
ActiveHigh,
|
||||
ActiveLow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TriggerMode {
|
||||
Edge,
|
||||
Level,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IsaIrqRoute {
|
||||
pub gsi: u32,
|
||||
pub polarity: InterruptPolarity,
|
||||
@@ -602,7 +597,7 @@ pub struct IsaIrqRoute {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IoApicNmiEntry {
|
||||
nmi_source: u8,
|
||||
reserved: u8,
|
||||
@@ -611,7 +606,7 @@ pub struct IoApicNmiEntry {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LocalApicNmiEntry {
|
||||
processor_id: u8,
|
||||
flags: u16,
|
||||
@@ -619,14 +614,14 @@ pub struct LocalApicNmiEntry {
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LocalApicAddressOverride {
|
||||
reserved: u16,
|
||||
local_apic_address: u64,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LocalX2ApicEntry {
|
||||
reserved: u16,
|
||||
local_x2apic_id: u32,
|
||||
@@ -634,7 +629,7 @@ pub struct LocalX2ApicEntry {
|
||||
acpi_processor_uid: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(unused)]
|
||||
pub enum MadtEntry {
|
||||
LocalApic(LocalApicEntry),
|
||||
|
||||
+45
-5
@@ -2,6 +2,8 @@ mod table;
|
||||
|
||||
use table::*;
|
||||
|
||||
use crate::task::tcb::{ExitReason, Fault};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u64)]
|
||||
pub enum Status {
|
||||
@@ -11,37 +13,75 @@ pub enum Status {
|
||||
BadFileDescriptor = 3, // EBADF
|
||||
NoSuchTask = 4, // ESRCH
|
||||
OutOfMemory = 5, // ENOMEM
|
||||
BadHandle = 6, // EBADH
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl TryFrom<u64> for SyscallNumber {
|
||||
type Error = Status;
|
||||
type Error = ();
|
||||
fn try_from(val: u64) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
1 => Ok(Self::Yield),
|
||||
2 => Ok(Self::Exit),
|
||||
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 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 {
|
||||
SyscallNumber::Yield => sys_yield(),
|
||||
SyscallNumber::Exit => sys_exit(arg0 as usize),
|
||||
SyscallNumber::Write => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
+616
-2
@@ -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;
|
||||
|
||||
@@ -8,7 +18,7 @@ pub fn sys_yield() -> Result<(), Status> {
|
||||
}
|
||||
|
||||
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> {
|
||||
@@ -16,18 +26,622 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul
|
||||
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 written = 0;
|
||||
while written < len {
|
||||
let n = (len - written).min(chunk.len());
|
||||
unsafe {
|
||||
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?;
|
||||
}
|
||||
crate::debug::serial::write_bytes(&chunk[..n]);
|
||||
written += n;
|
||||
}
|
||||
|
||||
if out_ptr != 0 {
|
||||
unsafe {
|
||||
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
@@ -1,3 +1,3 @@
|
||||
pub mod loader;
|
||||
pub mod bootstrap;
|
||||
pub mod scheduler;
|
||||
pub mod tcb;
|
||||
|
||||
+166
-24
@@ -2,19 +2,31 @@ use core::cell::UnsafeCell;
|
||||
|
||||
use crate::{
|
||||
arch::ThreadContext,
|
||||
memory::{AddressSpace, VirtualAddr},
|
||||
memory::{
|
||||
AddressSpace, AddressSpaceId, FrameAllocator, KernelStack, KernelStackPool,
|
||||
StackCreateError, VirtualAddr,
|
||||
},
|
||||
println,
|
||||
task::tcb::{ExitReason, Tcb, ThreadState},
|
||||
task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState},
|
||||
};
|
||||
|
||||
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 {
|
||||
current: Option<TaskId>,
|
||||
tasks: [Option<Tcb>; MAX_TASKS],
|
||||
ready: ReadyQueue,
|
||||
stacks: KernelStackPool,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
@@ -23,27 +35,28 @@ impl Scheduler {
|
||||
current: None,
|
||||
tasks: [const { None }; MAX_TASKS],
|
||||
ready: ReadyQueue::new(),
|
||||
stacks: KernelStackPool::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
|
||||
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_addr_space = ¤t.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_addr_space = &next.address_space as *const AddressSpace;
|
||||
let next_as_id = next.as_id;
|
||||
let next_kernel_stack = next.kernel_stack.top();
|
||||
|
||||
Switch {
|
||||
previous_context: prev_ctx,
|
||||
next_context: next_ctx,
|
||||
next_address_space: next_addr_space,
|
||||
next_kernel_stack: next_kernel_stack,
|
||||
activate_address_space: unsafe { *next_addr_space != *prev_addr_space },
|
||||
next_as_id,
|
||||
next_kernel_stack,
|
||||
activate_address_space: next_as_id != prev_as_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +64,7 @@ impl Scheduler {
|
||||
struct Switch {
|
||||
previous_context: *mut ThreadContext,
|
||||
next_context: *const ThreadContext,
|
||||
next_address_space: *const AddressSpace,
|
||||
next_as_id: AddressSpaceId,
|
||||
next_kernel_stack: VirtualAddr,
|
||||
activate_address_space: bool,
|
||||
}
|
||||
@@ -59,9 +72,9 @@ struct Switch {
|
||||
impl Switch {
|
||||
unsafe fn perform(self) {
|
||||
if self.activate_address_space {
|
||||
unsafe {
|
||||
(&*self.next_address_space).activate();
|
||||
}
|
||||
crate::memory::with_address_space(self.next_as_id, |as_ref| unsafe {
|
||||
as_ref.activate();
|
||||
});
|
||||
}
|
||||
|
||||
crate::arch::set_kernel_stack(self.next_kernel_stack);
|
||||
@@ -81,7 +94,7 @@ struct ReadyQueue {
|
||||
impl ReadyQueue {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
entries: [0; MAX_TASKS],
|
||||
entries: [TaskId(0); MAX_TASKS],
|
||||
head: 0,
|
||||
len: 0,
|
||||
}
|
||||
@@ -110,6 +123,23 @@ impl ReadyQueue {
|
||||
|
||||
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>);
|
||||
@@ -126,9 +156,20 @@ pub fn add_task(mut task: Tcb) -> Result<TaskId, Tcb> {
|
||||
|
||||
match scheduler.tasks.iter().position(Option::is_none) {
|
||||
Some(id) => {
|
||||
let id = TaskId(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;
|
||||
scheduler.tasks[id] = Some(task);
|
||||
scheduler.tasks[id.0] = Some(task);
|
||||
assert!(scheduler.ready.push_back(id));
|
||||
Ok(id)
|
||||
}
|
||||
@@ -149,7 +190,7 @@ pub fn start() -> ! {
|
||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||
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()
|
||||
.expect("ready task is missing");
|
||||
|
||||
@@ -159,7 +200,7 @@ pub fn start() -> ! {
|
||||
Switch {
|
||||
previous_context: &mut bootstrap_context,
|
||||
next_context: &next.context,
|
||||
next_address_space: &next.address_space,
|
||||
next_as_id: next.as_id,
|
||||
next_kernel_stack: next.kernel_stack.top(),
|
||||
activate_address_space: true,
|
||||
}
|
||||
@@ -172,6 +213,107 @@ pub fn start() -> ! {
|
||||
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() {
|
||||
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");
|
||||
|
||||
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));
|
||||
|
||||
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.make_switch(current_id, next_id)
|
||||
@@ -202,7 +344,7 @@ pub fn yield_current() {
|
||||
crate::arch::restore_interrupts(interrupt_state);
|
||||
}
|
||||
|
||||
pub fn exit_current(exit_code: usize) -> ! {
|
||||
pub fn exit_current(reason: ExitReason) -> ! {
|
||||
crate::arch::disable_interrupts();
|
||||
|
||||
let switch = {
|
||||
@@ -213,10 +355,10 @@ pub fn exit_current(exit_code: usize) -> ! {
|
||||
crate::hcf();
|
||||
};
|
||||
|
||||
let current = scheduler.tasks[current_id].as_mut().unwrap();
|
||||
current.state = ThreadState::Dead(ExitReason::Exited(exit_code));
|
||||
let current = scheduler.tasks[current_id.0].as_mut().unwrap();
|
||||
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.make_switch(current_id, next_id)
|
||||
|
||||
+185
-15
@@ -1,52 +1,222 @@
|
||||
use core::ops::BitOr;
|
||||
|
||||
use crate::{
|
||||
arch::ThreadContext,
|
||||
memory::{AddressSpace, KernelStack, VirtualAddr},
|
||||
task::loader::LoadedImage,
|
||||
memory::{AddressSpaceId, KernelStack, OwnedFrame, VirtualAddr},
|
||||
task::scheduler::TaskId,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Fault {
|
||||
SegmentationFault,
|
||||
IllegalInstruction,
|
||||
Abort,
|
||||
BadSystemCall,
|
||||
}
|
||||
|
||||
// Thread Control Block
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ExitReason {
|
||||
Exited(usize),
|
||||
Killed,
|
||||
Fault,
|
||||
Fault(Fault),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BlockReason {
|
||||
Recv,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ThreadState {
|
||||
Ready,
|
||||
Running,
|
||||
Blocked,
|
||||
Blocked(BlockReason),
|
||||
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 id: usize,
|
||||
pub id: TaskId,
|
||||
pub as_id: AddressSpaceId,
|
||||
pub state: ThreadState,
|
||||
pub kernel_stack: KernelStack,
|
||||
pub context: ThreadContext,
|
||||
pub address_space: AddressSpace,
|
||||
pub image: LoadedImage,
|
||||
pub mailbox: Mailbox,
|
||||
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 {
|
||||
pub fn new_user(
|
||||
id: usize,
|
||||
address_space: AddressSpace,
|
||||
as_id: AddressSpaceId,
|
||||
kernel_stack: KernelStack,
|
||||
image: LoadedImage,
|
||||
entry: VirtualAddr,
|
||||
user_stack: VirtualAddr,
|
||||
) -> 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 {
|
||||
id,
|
||||
id: TaskId::new(0),
|
||||
as_id,
|
||||
state: ThreadState::Ready,
|
||||
kernel_stack,
|
||||
context,
|
||||
address_space,
|
||||
image,
|
||||
mailbox: Mailbox::new(),
|
||||
handles,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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]
|
||||
name = "init"
|
||||
name = "dusk-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "init"
|
||||
[lib]
|
||||
test = false
|
||||
bench = false
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user