refactor: large code cleanup and safety pass
This commit is contained in:
@@ -137,7 +137,7 @@ compile-binaries:
|
|||||||
|
|
||||||
ovmf-x86_64:
|
ovmf-x86_64:
|
||||||
mkdir -p ovmf/ovmf-x86_64
|
mkdir -p ovmf/ovmf-x86_64
|
||||||
@if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
|
@if [ ! -f "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
|
||||||
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
|
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,40 @@
|
|||||||
use crate::arch::{
|
use super::idt::{self, InterruptFrame, stub_no_err};
|
||||||
apic, timer,
|
use crate::arch::{apic, timer};
|
||||||
x86_64::interrupts::idt::{self, InterruptStackFrame},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1;
|
pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1;
|
||||||
pub const APIC_TIMER_VECTOR: u8 = 0xFD;
|
pub const APIC_TIMER_VECTOR: u8 = 0xFD;
|
||||||
pub const APIC_ERROR_VECTOR: u8 = 0xFE;
|
pub const APIC_ERROR_VECTOR: u8 = 0xFE;
|
||||||
pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF;
|
pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF;
|
||||||
|
|
||||||
extern "x86-interrupt" fn error_handler(_frame: InterruptStackFrame) {
|
stub_no_err!(stub_pit_calibration, 0xF1);
|
||||||
apic::record_error();
|
stub_no_err!(stub_apic_timer, 0xFD);
|
||||||
apic::end_of_interrupt();
|
stub_no_err!(stub_apic_error, 0xFE);
|
||||||
}
|
stub_no_err!(stub_apic_spurious, 0xFF);
|
||||||
|
|
||||||
extern "x86-interrupt" fn timer_handler(_frame: InterruptStackFrame) {
|
pub(super) fn handle(frame: &mut InterruptFrame) {
|
||||||
apic::record_timer();
|
match frame.vector as u8 {
|
||||||
apic::end_of_interrupt();
|
PIT_CALIBRATION_VECTOR => {
|
||||||
}
|
timer::record_pit_calibration();
|
||||||
|
apic::end_of_interrupt();
|
||||||
extern "x86-interrupt" fn pit_calibration_handler(_frame: InterruptStackFrame) {
|
}
|
||||||
timer::record_pit_calibration();
|
APIC_TIMER_VECTOR => {
|
||||||
apic::end_of_interrupt();
|
apic::record_timer();
|
||||||
}
|
apic::end_of_interrupt();
|
||||||
|
}
|
||||||
extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame) {
|
APIC_ERROR_VECTOR => {
|
||||||
// No EOI
|
apic::record_error();
|
||||||
|
apic::end_of_interrupt();
|
||||||
|
}
|
||||||
|
APIC_SPURIOUS_VECTOR => {
|
||||||
|
// No EOI
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn install(idt: &mut idt::Idt) {
|
pub(super) fn install(idt: &mut idt::Idt) {
|
||||||
idt.set_handler(PIT_CALIBRATION_VECTOR, pit_calibration_handler, 0);
|
idt.set_handler(PIT_CALIBRATION_VECTOR, stub_pit_calibration, 0);
|
||||||
idt.set_handler(APIC_ERROR_VECTOR, error_handler, 0);
|
idt.set_handler(APIC_ERROR_VECTOR, stub_apic_error, 0);
|
||||||
idt.set_handler(APIC_TIMER_VECTOR, timer_handler, 0);
|
idt.set_handler(APIC_TIMER_VECTOR, stub_apic_timer, 0);
|
||||||
idt.set_handler(APIC_SPURIOUS_VECTOR, spurious_handler, 0);
|
idt.set_handler(APIC_SPURIOUS_VECTOR, stub_apic_spurious, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,87 @@
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
use super::idt::{self, InterruptStackFrame};
|
use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err};
|
||||||
use crate::{hcf, println};
|
use crate::{hcf, println};
|
||||||
|
|
||||||
macro_rules! fatal_without_error_code {
|
stub_no_err!(stub_divide_error, 0);
|
||||||
($handler:ident, $name:literal) => {
|
stub_no_err!(stub_debug, 1);
|
||||||
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) {
|
stub_no_err!(stub_non_maskable_interrupt, 2);
|
||||||
fatal_exception($name, &frame, None);
|
stub_no_err!(stub_breakpoint, 3);
|
||||||
|
stub_no_err!(stub_invalid_opcode, 6);
|
||||||
|
stub_no_err!(stub_device_not_available, 7);
|
||||||
|
stub_err!(stub_double_fault, 8);
|
||||||
|
stub_err!(stub_invalid_tss, 10);
|
||||||
|
stub_err!(stub_segment_not_present, 11);
|
||||||
|
stub_err!(stub_stack_segment_fault, 12);
|
||||||
|
stub_err!(stub_general_protection, 13);
|
||||||
|
stub_err!(stub_page_fault, 14);
|
||||||
|
stub_no_err!(stub_x87_floating_point, 16);
|
||||||
|
stub_err!(stub_alignment_check, 17);
|
||||||
|
stub_no_err!(stub_machine_check, 18);
|
||||||
|
stub_no_err!(stub_simd_floating_point, 19);
|
||||||
|
stub_no_err!(stub_user_test_exit, 0x80);
|
||||||
|
|
||||||
|
pub(super) fn handle(frame: &mut InterruptFrame) {
|
||||||
|
match frame.vector as u8 {
|
||||||
|
0 => fatal_exception("DIVIDE ERROR", &frame.stack_frame, None),
|
||||||
|
1 => fatal_exception("DEBUG EXCEPTION", &frame.stack_frame, None),
|
||||||
|
2 => fatal_exception("NON-MASKABLE INTERRUPT", &frame.stack_frame, None),
|
||||||
|
3 => report_exception("BREAKPOINT", &frame.stack_frame, None),
|
||||||
|
6 => fatal_exception("INVALID OPCODE", &frame.stack_frame, None),
|
||||||
|
7 => fatal_exception("DEVICE NOT AVAILABLE", &frame.stack_frame, None),
|
||||||
|
8 => fatal_exception("DOUBLE FAULT", &frame.stack_frame, Some(frame.error_code)),
|
||||||
|
10 => fatal_exception("INVALID TSS", &frame.stack_frame, Some(frame.error_code)),
|
||||||
|
11 => fatal_exception("SEGMENT NOT PRESENT", &frame.stack_frame, Some(frame.error_code)),
|
||||||
|
12 => fatal_exception("STACK-SEGMENT FAULT", &frame.stack_frame, Some(frame.error_code)),
|
||||||
|
13 => fatal_exception(
|
||||||
|
"GENERAL PROTECTION FAULT",
|
||||||
|
&frame.stack_frame,
|
||||||
|
Some(frame.error_code),
|
||||||
|
),
|
||||||
|
14 => {
|
||||||
|
report_exception("PAGE FAULT", &frame.stack_frame, Some(frame.error_code));
|
||||||
|
println!("Faulting address: {:#X}", read_cr2());
|
||||||
|
print_page_fault_error(frame.error_code);
|
||||||
|
hcf();
|
||||||
}
|
}
|
||||||
};
|
16 => fatal_exception("X87 FLOATING-POINT EXCEPTION", &frame.stack_frame, None),
|
||||||
}
|
17 => fatal_exception("ALIGNMENT CHECK", &frame.stack_frame, Some(frame.error_code)),
|
||||||
|
18 => fatal_exception("MACHINE CHECK", &frame.stack_frame, None),
|
||||||
|
19 => fatal_exception("SIMD FLOATING-POINT EXCEPTION", &frame.stack_frame, None),
|
||||||
|
0x80 => {
|
||||||
|
if frame.stack_frame.code_segment & 0b11 != 3 {
|
||||||
|
panic!("user_test_exit_handler called from kernel");
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! fatal_with_error_code {
|
println!("User test exit");
|
||||||
($handler:ident, $name:literal) => {
|
hcf();
|
||||||
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) {
|
}
|
||||||
fatal_exception($name, &frame, Some(error_code));
|
_ => {
|
||||||
|
println!("Unhandled exception vector: {:#X}", frame.vector);
|
||||||
|
hcf();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
|
|
||||||
report_exception("BREAKPOINT", &frame, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
|
|
||||||
report_exception("PAGE FAULT", &frame, Some(error_code));
|
|
||||||
println!("Faulting address: {:#X}", read_cr2());
|
|
||||||
print_page_fault_error(error_code);
|
|
||||||
hcf();
|
|
||||||
}
|
|
||||||
|
|
||||||
fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR");
|
|
||||||
fatal_without_error_code!(debug_handler, "DEBUG EXCEPTION");
|
|
||||||
fatal_without_error_code!(non_maskable_interrupt_handler, "NON-MASKABLE INTERRUPT");
|
|
||||||
fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE");
|
|
||||||
fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE");
|
|
||||||
fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION");
|
|
||||||
fatal_without_error_code!(machine_check_handler, "MACHINE CHECK");
|
|
||||||
fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION");
|
|
||||||
|
|
||||||
fatal_with_error_code!(double_fault_handler, "DOUBLE FAULT");
|
|
||||||
fatal_with_error_code!(invalid_tss_handler, "INVALID TSS");
|
|
||||||
fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT");
|
|
||||||
fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
|
|
||||||
fatal_with_error_code!(general_protection_handler, "GENERAL PROTECTION FAULT");
|
|
||||||
fatal_with_error_code!(alignment_check_handler, "ALIGNMENT CHECK");
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn user_test_exit_handler(frame: InterruptStackFrame) {
|
|
||||||
if frame.code_segment & 0b11 != 3 {
|
|
||||||
panic!("user_test_exit_handler called from kernel");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("User test exit");
|
|
||||||
hcf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn install(idt: &mut idt::Idt) {
|
pub(super) fn install(idt: &mut idt::Idt) {
|
||||||
idt.set_handler(0, divide_error_handler, 0);
|
idt.set_handler(0, stub_divide_error, 0);
|
||||||
idt.set_handler(1, debug_handler, 0);
|
idt.set_handler(1, stub_debug, 0);
|
||||||
idt.set_handler(2, non_maskable_interrupt_handler, 0);
|
idt.set_handler(2, stub_non_maskable_interrupt, 0);
|
||||||
idt.set_user_handler(3, breakpoint_handler, 0);
|
idt.set_user_handler(3, stub_breakpoint, 0);
|
||||||
idt.set_handler(6, invalid_opcode_handler, 0);
|
idt.set_handler(6, stub_invalid_opcode, 0);
|
||||||
idt.set_handler(7, device_not_available_handler, 0);
|
idt.set_handler(7, stub_device_not_available, 0);
|
||||||
idt.set_error_code_handler(8, double_fault_handler, 1);
|
idt.set_handler(8, stub_double_fault, 1);
|
||||||
idt.set_error_code_handler(10, invalid_tss_handler, 0);
|
idt.set_handler(10, stub_invalid_tss, 0);
|
||||||
idt.set_error_code_handler(11, segment_not_present_handler, 0);
|
idt.set_handler(11, stub_segment_not_present, 0);
|
||||||
idt.set_error_code_handler(12, stack_segment_fault_handler, 0);
|
idt.set_handler(12, stub_stack_segment_fault, 0);
|
||||||
idt.set_error_code_handler(13, general_protection_handler, 0);
|
idt.set_handler(13, stub_general_protection, 0);
|
||||||
idt.set_error_code_handler(14, page_fault_handler, 0);
|
idt.set_handler(14, stub_page_fault, 0);
|
||||||
idt.set_handler(16, x87_floating_point_handler, 0);
|
idt.set_handler(16, stub_x87_floating_point, 0);
|
||||||
idt.set_error_code_handler(17, alignment_check_handler, 0);
|
idt.set_handler(17, stub_alignment_check, 0);
|
||||||
idt.set_handler(18, machine_check_handler, 0);
|
idt.set_handler(18, stub_machine_check, 0);
|
||||||
idt.set_handler(19, simd_floating_point_handler, 0);
|
idt.set_handler(19, stub_simd_floating_point, 0);
|
||||||
|
|
||||||
idt.set_user_handler(0x80, user_test_exit_handler, 0);
|
idt.set_user_handler(0x80, stub_user_test_exit, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_cr2() -> u64 {
|
fn read_cr2() -> u64 {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ struct IdtEntry {
|
|||||||
|
|
||||||
impl IdtEntry {
|
impl IdtEntry {
|
||||||
const fn missing() -> Self {
|
const fn missing() -> Self {
|
||||||
return Self {
|
Self {
|
||||||
offset_low: 0,
|
offset_low: 0,
|
||||||
code_selector: 0,
|
code_selector: 0,
|
||||||
ist: 0,
|
ist: 0,
|
||||||
@@ -26,7 +26,7 @@ impl IdtEntry {
|
|||||||
offset_middle: 0,
|
offset_middle: 0,
|
||||||
offset_high: 0,
|
offset_high: 0,
|
||||||
reserved: 0,
|
reserved: 0,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ struct IdtPointer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(super) struct InterruptStackFrame {
|
pub struct InterruptStackFrame {
|
||||||
pub instruction_pointer: VirtualAddr,
|
pub instruction_pointer: VirtualAddr,
|
||||||
pub code_segment: u64,
|
pub code_segment: u64,
|
||||||
pub cpu_flags: u64,
|
pub cpu_flags: u64,
|
||||||
@@ -46,14 +46,36 @@ pub(super) struct InterruptStackFrame {
|
|||||||
pub stack_segment: u64,
|
pub stack_segment: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InterruptFrame {
|
||||||
|
pub rax: u64,
|
||||||
|
pub rcx: u64,
|
||||||
|
pub rdx: u64,
|
||||||
|
pub rsi: u64,
|
||||||
|
pub rdi: u64,
|
||||||
|
pub r8: u64,
|
||||||
|
pub r9: u64,
|
||||||
|
pub r10: u64,
|
||||||
|
pub r11: u64,
|
||||||
|
pub rbx: u64,
|
||||||
|
pub rbp: u64,
|
||||||
|
pub r12: u64,
|
||||||
|
pub r13: u64,
|
||||||
|
pub r14: u64,
|
||||||
|
pub r15: u64,
|
||||||
|
pub vector: u64,
|
||||||
|
pub error_code: u64,
|
||||||
|
pub stack_frame: InterruptStackFrame,
|
||||||
|
}
|
||||||
|
|
||||||
const INTERRUPT_GATE: u8 = 0b1110;
|
const INTERRUPT_GATE: u8 = 0b1110;
|
||||||
const PRESENT: u8 = 1 << 7;
|
const PRESENT: u8 = 1 << 7;
|
||||||
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
|
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
|
||||||
const USER_DPL: u8 = 3 << 5;
|
const USER_DPL: u8 = 3 << 5;
|
||||||
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
|
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
|
||||||
|
|
||||||
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame);
|
pub(super) type RawHandler = unsafe extern "C" fn();
|
||||||
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
|
|
||||||
|
|
||||||
pub(super) struct Idt {
|
pub(super) struct Idt {
|
||||||
entries: [IdtEntry; 256],
|
entries: [IdtEntry; 256],
|
||||||
@@ -66,20 +88,11 @@ impl Idt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
|
pub(super) fn set_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
|
||||||
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
|
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_error_code_handler(
|
pub(super) fn set_user_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
|
||||||
&mut self,
|
|
||||||
vector: u8,
|
|
||||||
handler: ErrorCodeHandler,
|
|
||||||
ist: u8,
|
|
||||||
) {
|
|
||||||
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn set_user_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
|
|
||||||
self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE);
|
self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +114,110 @@ static mut IDT: Idt = Idt::new();
|
|||||||
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
|
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
|
||||||
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
|
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
|
||||||
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
|
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
|
||||||
|
const _: () = assert!(core::mem::size_of::<InterruptFrame>() == 176);
|
||||||
|
const _: () = assert!(core::mem::offset_of!(InterruptFrame, stack_frame) == 136);
|
||||||
|
|
||||||
|
#[unsafe(naked)]
|
||||||
|
pub(super) unsafe extern "C" fn interrupt_common() {
|
||||||
|
core::arch::naked_asm!(
|
||||||
|
"push r15",
|
||||||
|
"push r14",
|
||||||
|
"push r13",
|
||||||
|
"push r12",
|
||||||
|
"push rbp",
|
||||||
|
"push rbx",
|
||||||
|
"push r11",
|
||||||
|
"push r10",
|
||||||
|
"push r9",
|
||||||
|
"push r8",
|
||||||
|
"push rdi",
|
||||||
|
"push rsi",
|
||||||
|
"push rdx",
|
||||||
|
"push rcx",
|
||||||
|
"push rax",
|
||||||
|
|
||||||
|
// Check CS: bit 0 and 1 are CPL. If CPL != 0 (user mode), swapgs
|
||||||
|
"test byte ptr [rsp + 144], 3",
|
||||||
|
"jz 1f",
|
||||||
|
"swapgs",
|
||||||
|
"1:",
|
||||||
|
|
||||||
|
"mov rdi, rsp",
|
||||||
|
"cld",
|
||||||
|
"call {dispatch}",
|
||||||
|
|
||||||
|
// Check CS: if returning to user mode, swapgs
|
||||||
|
"test byte ptr [rsp + 144], 3",
|
||||||
|
"jz 2f",
|
||||||
|
"swapgs",
|
||||||
|
"2:",
|
||||||
|
|
||||||
|
"pop rax",
|
||||||
|
"pop rcx",
|
||||||
|
"pop rdx",
|
||||||
|
"pop rsi",
|
||||||
|
"pop rdi",
|
||||||
|
"pop r8",
|
||||||
|
"pop r9",
|
||||||
|
"pop r10",
|
||||||
|
"pop r11",
|
||||||
|
"pop rbx",
|
||||||
|
"pop rbp",
|
||||||
|
"pop r12",
|
||||||
|
"pop r13",
|
||||||
|
"pop r14",
|
||||||
|
"pop r15",
|
||||||
|
|
||||||
|
"add rsp, 16",
|
||||||
|
"iretq",
|
||||||
|
|
||||||
|
dispatch = sym interrupt_dispatch,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn interrupt_dispatch(frame: &mut InterruptFrame) {
|
||||||
|
let vector = frame.vector as u8;
|
||||||
|
match vector {
|
||||||
|
0..=31 | 0x80 => exceptions::handle(frame),
|
||||||
|
apic_vectors::PIT_CALIBRATION_VECTOR
|
||||||
|
| apic_vectors::APIC_TIMER_VECTOR
|
||||||
|
| apic_vectors::APIC_ERROR_VECTOR
|
||||||
|
| apic_vectors::APIC_SPURIOUS_VECTOR => apic_vectors::handle(frame),
|
||||||
|
_ => {
|
||||||
|
crate::println!("Unhandled interrupt vector: {:#X}", vector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! stub_no_err {
|
||||||
|
($name:ident, $vec:literal) => {
|
||||||
|
#[unsafe(naked)]
|
||||||
|
pub(super) unsafe extern "C" fn $name() {
|
||||||
|
core::arch::naked_asm!(
|
||||||
|
"push 0",
|
||||||
|
concat!("push ", stringify!($vec)),
|
||||||
|
"jmp {common}",
|
||||||
|
common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! stub_err {
|
||||||
|
($name:ident, $vec:literal) => {
|
||||||
|
#[unsafe(naked)]
|
||||||
|
pub(super) unsafe extern "C" fn $name() {
|
||||||
|
core::arch::naked_asm!(
|
||||||
|
concat!("push ", stringify!($vec)),
|
||||||
|
"jmp {common}",
|
||||||
|
common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) use stub_err;
|
||||||
|
pub(super) use stub_no_err;
|
||||||
|
|
||||||
pub fn idt_init() {
|
pub fn idt_init() {
|
||||||
let mut idt = Idt::new();
|
let mut idt = Idt::new();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
arch::x86_64::cpu::CpuFeatures,
|
arch::x86_64::cpu::CpuFeatures,
|
||||||
memory::{
|
memory::{
|
||||||
CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions,
|
CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions,
|
||||||
PhysicalAddr, VirtualAddr,
|
PageTableMapping, PhysicalAddr, VirtualAddr,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,6 +199,14 @@ impl PageTableEntry {
|
|||||||
self.0 & Self::PRESENT != 0
|
self.0 & Self::PRESENT != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn writable(&self) -> bool {
|
||||||
|
self.0 & Self::WRITABLE != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn executable(&self) -> bool {
|
||||||
|
self.0 & Self::NX == 0
|
||||||
|
}
|
||||||
|
|
||||||
fn is_user_accessible(&self) -> bool {
|
fn is_user_accessible(&self) -> bool {
|
||||||
self.0 & Self::USER_ACCESSIBLE != 0
|
self.0 & Self::USER_ACCESSIBLE != 0
|
||||||
}
|
}
|
||||||
@@ -226,6 +234,14 @@ impl PageTableEntry {
|
|||||||
|
|
||||||
FrameAddr::from_start_address(self.physical_address(config))
|
FrameAddr::from_start_address(self.physical_address(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn permissions(&self) -> PagePermissions {
|
||||||
|
PagePermissions::new(
|
||||||
|
self.writable(),
|
||||||
|
self.executable(),
|
||||||
|
self.is_user_accessible(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum MapError {
|
pub(crate) enum MapError {
|
||||||
@@ -363,6 +379,52 @@ impl PageTable {
|
|||||||
self.direct_map.translate(addr)
|
self.direct_map.translate(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option<PageTableMapping> {
|
||||||
|
let address = virtual_addr.as_usize();
|
||||||
|
|
||||||
|
if !self.is_canonical(address) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut table_frame = self.frame.frame_address();
|
||||||
|
let mut permissions = PagePermissions::new(true, true, true);
|
||||||
|
|
||||||
|
for &level in self.config.mode.intermediate_levels() {
|
||||||
|
let table = self.table(table_frame)?;
|
||||||
|
let entry = table[level.index(address)];
|
||||||
|
|
||||||
|
if !entry.is_present() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry_permissions = entry.permissions();
|
||||||
|
permissions.writable &= entry_permissions.writable;
|
||||||
|
permissions.user_accessible &= entry_permissions.user_accessible;
|
||||||
|
permissions.executable &= entry_permissions.executable;
|
||||||
|
|
||||||
|
if entry.is_huge() {
|
||||||
|
level.large_page_size()?;
|
||||||
|
return Some(PageTableMapping { permissions });
|
||||||
|
}
|
||||||
|
|
||||||
|
table_frame = entry.table_frame(self.config)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let page_table = self.table(table_frame)?;
|
||||||
|
let entry = page_table[p1_index(address)];
|
||||||
|
|
||||||
|
if !entry.is_present() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entry_permissions = entry.permissions();
|
||||||
|
permissions.writable &= entry_permissions.writable;
|
||||||
|
permissions.user_accessible &= entry_permissions.user_accessible;
|
||||||
|
permissions.executable &= entry_permissions.executable;
|
||||||
|
|
||||||
|
Some(PageTableMapping { permissions })
|
||||||
|
}
|
||||||
|
|
||||||
fn get_next_level(
|
fn get_next_level(
|
||||||
&self,
|
&self,
|
||||||
parent: FrameAddr,
|
parent: FrameAddr,
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ pub fn find_file<'a>(archive: &'a [u8], target: &str) -> Option<&'a [u8]> {
|
|||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
while offset + core::mem::size_of::<Header>() <= archive.len() {
|
while offset + core::mem::size_of::<Header>() <= archive.len() {
|
||||||
|
if offset + core::mem::size_of::<Header>() > archive.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let header = Header::from_bytes(&archive[offset..])?;
|
let header = Header::from_bytes(&archive[offset..])?;
|
||||||
let header_start = offset;
|
let header_start = offset;
|
||||||
offset += core::mem::size_of::<Header>();
|
offset += core::mem::size_of::<Header>();
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#![feature(abi_x86_interrupt)]
|
|
||||||
#![allow(clippy::needless_return)]
|
#![allow(clippy::needless_return)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|||||||
+29
-15
@@ -8,6 +8,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct AddressSpaceId(usize);
|
||||||
|
|
||||||
const MAX_ADDRESS_SPACES: usize = 32;
|
const MAX_ADDRESS_SPACES: usize = 32;
|
||||||
struct AddressSpaceTable {
|
struct AddressSpaceTable {
|
||||||
entries: [Option<AddressSpace>; MAX_ADDRESS_SPACES],
|
entries: [Option<AddressSpace>; MAX_ADDRESS_SPACES],
|
||||||
@@ -20,27 +24,27 @@ impl AddressSpaceTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, address_space: AddressSpace) -> Option<usize> {
|
fn insert(&mut self, address_space: AddressSpace) -> Result<AddressSpaceId, AddressSpace> {
|
||||||
for (i, slot) in self.entries.iter_mut().enumerate() {
|
for (i, slot) in self.entries.iter_mut().enumerate() {
|
||||||
if slot.is_none() {
|
if slot.is_none() {
|
||||||
*slot = Some(address_space);
|
*slot = Some(address_space);
|
||||||
return Some(i);
|
return Ok(AddressSpaceId(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
Err(address_space)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, id: usize) -> Option<&AddressSpace> {
|
fn get(&self, id: AddressSpaceId) -> Option<&AddressSpace> {
|
||||||
self.entries.get(id).and_then(Option::as_ref)
|
self.entries.get(id.0).and_then(Option::as_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mut(&mut self, id: usize) -> Option<&mut AddressSpace> {
|
fn get_mut(&mut self, id: AddressSpaceId) -> Option<&mut AddressSpace> {
|
||||||
self.entries.get_mut(id).and_then(Option::as_mut)
|
self.entries.get_mut(id.0).and_then(Option::as_mut)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&mut self, id: usize) -> Option<AddressSpace> {
|
fn remove(&mut self, id: AddressSpaceId) -> Option<AddressSpace> {
|
||||||
self.entries.get_mut(id).and_then(Option::take)
|
self.entries.get_mut(id.0).and_then(Option::take)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,20 +55,19 @@ unsafe impl Sync for GlobalAddressSpaceTable {}
|
|||||||
static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable =
|
static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable =
|
||||||
GlobalAddressSpaceTable(UnsafeCell::new(AddressSpaceTable::new()));
|
GlobalAddressSpaceTable(UnsafeCell::new(AddressSpaceTable::new()));
|
||||||
|
|
||||||
pub fn insert_address_space(address_space: AddressSpace) -> Option<usize> {
|
pub fn insert_address_space(address_space: AddressSpace) -> Result<AddressSpaceId, AddressSpace> {
|
||||||
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
|
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
|
||||||
|
|
||||||
let id = table.insert(address_space);
|
table.insert(address_space)
|
||||||
id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_address_space(id: usize) -> Option<AddressSpace> {
|
pub fn remove_address_space(id: AddressSpaceId) -> Option<AddressSpace> {
|
||||||
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
|
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
|
||||||
|
|
||||||
table.remove(id)
|
table.remove(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_address_space<R>(id: usize, f: impl FnOnce(&AddressSpace) -> R) -> Option<R> {
|
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 interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
let table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() };
|
let table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() };
|
||||||
let res = table.get(id).map(f);
|
let res = table.get(id).map(f);
|
||||||
@@ -72,7 +75,10 @@ pub fn with_address_space<R>(id: usize, f: impl FnOnce(&AddressSpace) -> R) -> O
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_address_space_mut<R>(id: usize, f: impl FnOnce(&mut AddressSpace) -> R) -> Option<R> {
|
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 interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
|
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
|
||||||
let res = table.get_mut(id).map(f);
|
let res = table.get_mut(id).map(f);
|
||||||
@@ -183,6 +189,10 @@ impl From<PageTableCreateError> for AddressSpaceCreateError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PageTableMapping {
|
||||||
|
pub permissions: PagePermissions,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
enum AddressSpaceKind {
|
enum AddressSpaceKind {
|
||||||
Kernel,
|
Kernel,
|
||||||
@@ -427,6 +437,10 @@ impl AddressSpace {
|
|||||||
self.root.to_virtual(physical_addr)
|
self.root.to_virtual(physical_addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option<PageTableMapping> {
|
||||||
|
self.root.mapping(virtual_addr)
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn activate(&self) {
|
pub unsafe fn activate(&self) {
|
||||||
unsafe { self.root.activate() }
|
unsafe { self.root.activate() }
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -7,9 +7,9 @@ use core::ops::Add;
|
|||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub use address_space::{
|
pub use address_space::{
|
||||||
AddressSpace, AddressSpaceCreateError, MapError, UnmapError, init_kernel_address_space,
|
AddressSpace, AddressSpaceCreateError, AddressSpaceId, MapError, PageTableMapping, UnmapError,
|
||||||
insert_address_space, remove_address_space, with_address_space, with_address_space_mut,
|
init_kernel_address_space, insert_address_space, remove_address_space, with_address_space,
|
||||||
with_kernel_address_space,
|
with_address_space_mut, with_kernel_address_space,
|
||||||
};
|
};
|
||||||
pub use frame::{
|
pub use frame::{
|
||||||
FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame,
|
FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame,
|
||||||
|
|||||||
+6
-1
@@ -237,8 +237,13 @@ impl KernelStackPool {
|
|||||||
Err(StackCreateError::OutOfStacks)
|
Err(StackCreateError::OutOfStacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(&mut self, stack: &KernelStack) {
|
pub fn free(&mut self, stack: KernelStack) {
|
||||||
let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1;
|
let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1;
|
||||||
|
crate::memory::with_kernel_address_space(|kernel_as| {
|
||||||
|
crate::memory::with_allocator(|allocator| unsafe {
|
||||||
|
stack.destroy(kernel_as, allocator)
|
||||||
|
});
|
||||||
|
});
|
||||||
self.free_slots &= !(1 << slot);
|
self.free_slots &= !(1 << slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-6
@@ -1,9 +1,51 @@
|
|||||||
use crate::{memory::VirtualAddr, syscall::Status};
|
use crate::{
|
||||||
|
memory::{FRAME_SIZE, VirtualAddr, address_space::AddressSpaceId, with_address_space},
|
||||||
|
syscall::Status,
|
||||||
|
};
|
||||||
|
|
||||||
pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000);
|
pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000);
|
||||||
|
|
||||||
pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
|
pub fn validate_user_range(
|
||||||
// TODO: guard against unmapped pages
|
as_id: AddressSpaceId,
|
||||||
|
start: VirtualAddr,
|
||||||
|
len: usize,
|
||||||
|
writable: bool,
|
||||||
|
) -> Result<(), Status> {
|
||||||
|
let start_addr = start.as_usize();
|
||||||
|
let end_addr = start_addr.checked_add(len).ok_or(Status::BadAddress)?;
|
||||||
|
|
||||||
|
if start_addr >= USER_SPACE_END.as_usize() || end_addr > USER_SPACE_END.as_usize() {
|
||||||
|
return Err(Status::BadAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if len == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let page_start = start_addr & !(FRAME_SIZE - 1);
|
||||||
|
for page in (page_start..end_addr).step_by(FRAME_SIZE) {
|
||||||
|
let is_valid = with_address_space(as_id, |address_space| {
|
||||||
|
address_space
|
||||||
|
.mapping(VirtualAddr::new(page))
|
||||||
|
.is_some_and(|mapping| {
|
||||||
|
mapping.permissions.user_accessible
|
||||||
|
&& (!writable || mapping.permissions.writable)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.ok_or(Status::BadAddress)?;
|
||||||
|
|
||||||
|
if !is_valid {
|
||||||
|
return Err(Status::BadAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that the user address range is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]).
|
||||||
|
pub unsafe fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
|
||||||
let end = src
|
let end = src
|
||||||
.as_usize()
|
.as_usize()
|
||||||
.checked_add(dst.len())
|
.checked_add(dst.len())
|
||||||
@@ -19,7 +61,10 @@ pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that the user address range is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]).
|
||||||
|
pub unsafe fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
|
||||||
let end = dst
|
let end = dst
|
||||||
.as_usize()
|
.as_usize()
|
||||||
.checked_add(src.len())
|
.checked_add(src.len())
|
||||||
@@ -35,7 +80,10 @@ pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> {
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that the user address is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]).
|
||||||
|
pub unsafe fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> {
|
||||||
if dst.as_usize() % core::mem::align_of::<T>() != 0 {
|
if dst.as_usize() % core::mem::align_of::<T>() != 0 {
|
||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
@@ -54,7 +102,11 @@ pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> {
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that the user address is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]).
|
||||||
|
#[allow(unused)]
|
||||||
|
pub unsafe fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> {
|
||||||
if src.as_usize() % core::mem::align_of::<T>() != 0 {
|
if src.as_usize() % core::mem::align_of::<T>() != 0 {
|
||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-6
@@ -2,7 +2,7 @@ mod table;
|
|||||||
|
|
||||||
use table::*;
|
use table::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
// Status::Success = 0
|
// Status::Success = 0
|
||||||
@@ -50,7 +50,7 @@ impl TryFrom<u64> for SyscallNumber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64, _arg5: u64) -> u64 {
|
pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, _arg5: u64) -> u64 {
|
||||||
let result = (|| -> Result<(), Status> {
|
let result = (|| -> Result<(), Status> {
|
||||||
let syscall = SyscallNumber::try_from(num)?;
|
let syscall = SyscallNumber::try_from(num)?;
|
||||||
match syscall {
|
match syscall {
|
||||||
@@ -66,10 +66,14 @@ pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64,
|
|||||||
SyscallNumber::FrameAlloc => sys_frame_alloc(arg0 as usize),
|
SyscallNumber::FrameAlloc => sys_frame_alloc(arg0 as usize),
|
||||||
SyscallNumber::FrameDealloc => sys_frame_dealloc(arg0 as usize),
|
SyscallNumber::FrameDealloc => sys_frame_dealloc(arg0 as usize),
|
||||||
SyscallNumber::AsCreate => sys_as_create(arg0 as usize),
|
SyscallNumber::AsCreate => sys_as_create(arg0 as usize),
|
||||||
SyscallNumber::Map => {
|
SyscallNumber::Map => sys_map(
|
||||||
sys_map(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
|
arg0 as usize,
|
||||||
}
|
arg1 as usize,
|
||||||
SyscallNumber::Unmap => sys_unmap(arg0 as usize, arg1 as usize),
|
arg2 as usize,
|
||||||
|
arg3 as usize,
|
||||||
|
arg4 as usize,
|
||||||
|
),
|
||||||
|
SyscallNumber::Unmap => sys_unmap(arg0 as usize),
|
||||||
SyscallNumber::TaskCreate => {
|
SyscallNumber::TaskCreate => {
|
||||||
sys_task_create(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
|
sys_task_create(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
|
||||||
}
|
}
|
||||||
|
|||||||
+448
-159
@@ -1,10 +1,13 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
memory::{
|
memory::{
|
||||||
FRAME_SIZE, OwnedFrame, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user,
|
FRAME_SIZE, MapError, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user,
|
||||||
copy_to_user, copy_val_to_user,
|
copy_to_user, copy_val_to_user, validate_user_range,
|
||||||
},
|
},
|
||||||
println,
|
println,
|
||||||
task::tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights},
|
task::{
|
||||||
|
scheduler::TaskId,
|
||||||
|
tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Status;
|
use super::Status;
|
||||||
@@ -23,32 +26,60 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul
|
|||||||
return Err(Status::BadFileDescriptor);
|
return Err(Status::BadFileDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
|
||||||
|
validate_user_range(current_task.as_id, VirtualAddr::new(buf_ptr), len, false)?;
|
||||||
|
|
||||||
|
if out_ptr != 0 {
|
||||||
|
if out_ptr % core::mem::align_of::<usize>() != 0 {
|
||||||
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
|
validate_user_range(
|
||||||
|
current_task.as_id,
|
||||||
|
VirtualAddr::new(out_ptr),
|
||||||
|
core::mem::size_of::<usize>(),
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.expect("failed to resolve self task")?;
|
||||||
|
|
||||||
let mut chunk = [0u8; 128];
|
let mut chunk = [0u8; 128];
|
||||||
let mut written = 0;
|
let mut written = 0;
|
||||||
while written < len {
|
while written < len {
|
||||||
let n = (len - written).min(chunk.len());
|
let n = (len - written).min(chunk.len());
|
||||||
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?;
|
unsafe {
|
||||||
|
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?;
|
||||||
|
}
|
||||||
crate::debug::serial::write_bytes(&chunk[..n]);
|
crate::debug::serial::write_bytes(&chunk[..n]);
|
||||||
written += n;
|
written += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
if out_ptr != 0 {
|
if out_ptr != 0 {
|
||||||
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
|
unsafe {
|
||||||
|
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> {
|
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 {
|
if len > MAX_MSG_SIZE {
|
||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut msg_buf = [0u8; MAX_MSG_SIZE];
|
|
||||||
copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])?;
|
|
||||||
|
|
||||||
let dest_task = crate::task::scheduler::get_task_mut(dest_task_id).ok_or(Status::NoSuchTask)?;
|
|
||||||
let sender = crate::task::scheduler::current();
|
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 {
|
let msg = Message {
|
||||||
sender,
|
sender,
|
||||||
@@ -56,14 +87,18 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S
|
|||||||
data: msg_buf,
|
data: msg_buf,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !dest_task.mailbox.push(msg) {
|
let should_unblock = crate::task::scheduler::with_task_mut(dest_task_id, |dest_task| {
|
||||||
return Err(Status::OutOfMemory);
|
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 matches!(
|
if should_unblock {
|
||||||
dest_task.state,
|
|
||||||
crate::task::tcb::ThreadState::Blocked(BlockReason::Recv { .. })
|
|
||||||
) {
|
|
||||||
crate::task::scheduler::unblock(dest_task_id);
|
crate::task::scheduler::unblock(dest_task_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,28 +115,58 @@ pub fn sys_recv(
|
|||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current())
|
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
|
||||||
.ok_or(Status::NoSuchTask)?;
|
if current_task.mailbox.len == 0 {
|
||||||
|
crate::task::scheduler::block_current(BlockReason::Recv);
|
||||||
|
}
|
||||||
|
|
||||||
if current_task.mailbox.len == 0 {
|
validate_user_range(current_task.as_id, VirtualAddr::new(out_ptr), max_len, true)?;
|
||||||
crate::task::scheduler::block_current(BlockReason::Recv { ep: 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we blocked, we will wake up when the mailbox is non-empty
|
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,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current())
|
if out_sender != 0 {
|
||||||
.ok_or(Status::NoSuchTask)?;
|
if out_sender % core::mem::align_of::<usize>() != 0 {
|
||||||
let msg = current_task.mailbox.pop().ok_or(Status::NoSuchTask)?;
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
|
validate_user_range(
|
||||||
|
current_task.as_id,
|
||||||
|
VirtualAddr::new(out_sender),
|
||||||
|
core::mem::size_of::<usize>(),
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
copy_to_user(
|
Ok(())
|
||||||
VirtualAddr::new(out_ptr),
|
})
|
||||||
&msg.data[..msg.length.min(max_len)],
|
.expect("failed to resolve self task")?;
|
||||||
)?;
|
|
||||||
if out_actual_len != 0 {
|
let msg =
|
||||||
copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?;
|
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
|
||||||
}
|
current_task.mailbox.pop().ok_or(Status::NoSuchTask)
|
||||||
if out_sender != 0 {
|
})
|
||||||
copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?;
|
.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(())
|
Ok(())
|
||||||
@@ -112,49 +177,69 @@ pub fn sys_frame_alloc(out_handle: usize) -> Result<(), Status> {
|
|||||||
return Err(Status::InvalidArgument);
|
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 frame = crate::memory::alloc_frame().ok_or(Status::OutOfMemory)?;
|
||||||
|
|
||||||
let task_id = crate::task::scheduler::current();
|
|
||||||
let task = match crate::task::scheduler::get_task_mut(task_id) {
|
|
||||||
Some(task) => task,
|
|
||||||
None => {
|
|
||||||
unsafe { crate::memory::dealloc_frame(frame) };
|
|
||||||
return Err(Status::NoSuchTask);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let frame_addr = frame.into_raw();
|
|
||||||
let handle = Handle {
|
let handle = Handle {
|
||||||
object: KernelObject::Frame(frame_addr),
|
object: KernelObject::Frame(frame),
|
||||||
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE | Rights::MAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_id = match task.handles.push(handle) {
|
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
|
||||||
Some(id) => id,
|
let handle_id = match current_task.handles.push(handle) {
|
||||||
None => {
|
Ok(id) => id,
|
||||||
unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame_addr)) };
|
Err(handle) => {
|
||||||
return Err(Status::OutOfMemory);
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
copy_val_to_user(VirtualAddr::new(out_handle), &handle_id)?;
|
unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
|
||||||
|
})
|
||||||
Ok(())
|
.expect("failed to resolve self task")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_frame_dealloc(frame_handle: usize) -> Result<(), Status> {
|
pub fn sys_frame_dealloc(frame_handle_id: usize) -> Result<(), Status> {
|
||||||
let task = crate::task::scheduler::current();
|
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
|
||||||
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?;
|
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) };
|
||||||
|
|
||||||
let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?;
|
Ok(())
|
||||||
let frame = match frame_handle.object {
|
}
|
||||||
KernelObject::Frame(frame_addr) => frame_addr,
|
_ => {
|
||||||
_ => return Err(Status::InvalidArgument),
|
// 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"),
|
||||||
|
}
|
||||||
|
|
||||||
unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame)) };
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
Ok(())
|
}
|
||||||
|
})
|
||||||
|
.expect("failed to resolve self task")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_as_create(out_handle: usize) -> Result<(), Status> {
|
pub fn sys_as_create(out_handle: usize) -> Result<(), Status> {
|
||||||
@@ -162,34 +247,62 @@ pub fn sys_as_create(out_handle: usize) -> Result<(), Status> {
|
|||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let task_id = crate::task::scheduler::current();
|
if out_handle % core::mem::align_of::<usize>() != 0 {
|
||||||
let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?;
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
let new_as = match crate::memory::with_address_space(task.as_id, |caller_as| {
|
let new_as =
|
||||||
crate::memory::with_allocator(|allocator| caller_as.new_user(allocator))
|
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
|
||||||
}) {
|
validate_user_range(
|
||||||
Some(Ok(as_space)) => as_space,
|
current_task.as_id,
|
||||||
_ => return Err(Status::OutOfMemory),
|
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 as_id = crate::memory::insert_address_space(new_as).ok_or(Status::OutOfMemory)?;
|
|
||||||
|
|
||||||
let handle = Handle {
|
let handle = Handle {
|
||||||
object: KernelObject::AddressSpace(as_id),
|
object: KernelObject::AddressSpace(as_id),
|
||||||
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_id = match task.handles.push(handle) {
|
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
|
||||||
Some(id) => id,
|
let handle_id = match current_task.handles.push(handle) {
|
||||||
None => {
|
Ok(id) => id,
|
||||||
crate::memory::remove_address_space(as_id);
|
Err(handle) => {
|
||||||
return Err(Status::OutOfMemory);
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
copy_val_to_user(VirtualAddr::new(out_handle), &handle_id)?;
|
unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
|
||||||
|
})
|
||||||
Ok(())
|
.expect("failed to resolve self task")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_map(
|
pub fn sys_map(
|
||||||
@@ -197,41 +310,81 @@ pub fn sys_map(
|
|||||||
frame_handle: usize,
|
frame_handle: usize,
|
||||||
virtual_addr: usize,
|
virtual_addr: usize,
|
||||||
permissions: usize,
|
permissions: usize,
|
||||||
|
out_handle: usize,
|
||||||
) -> Result<(), Status> {
|
) -> Result<(), Status> {
|
||||||
if virtual_addr % FRAME_SIZE != 0 {
|
if out_handle == 0 || out_handle % core::mem::align_of::<usize>() != 0 {
|
||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
if virtual_addr >= USER_SPACE_END.as_usize() {
|
if virtual_addr % FRAME_SIZE != 0 || permissions & !0b11 != 0 {
|
||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let task = crate::task::scheduler::current();
|
let writable = permissions & (1 << 0) != 0;
|
||||||
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?;
|
let executable = permissions & (1 << 1) != 0;
|
||||||
|
|
||||||
let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?;
|
let end = virtual_addr
|
||||||
let as_id = match as_handle.object {
|
.checked_add(FRAME_SIZE)
|
||||||
KernelObject::AddressSpace(as_id) => as_id,
|
.ok_or(Status::InvalidArgument)?;
|
||||||
_ => return Err(Status::InvalidArgument),
|
if end > USER_SPACE_END.as_usize() {
|
||||||
};
|
return Err(Status::InvalidArgument);
|
||||||
|
}
|
||||||
let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?;
|
|
||||||
let frame = match frame_handle.object {
|
let current_task = crate::task::scheduler::current();
|
||||||
KernelObject::Frame(frame_addr) => frame_addr,
|
let as_id = crate::task::scheduler::with_task(current_task, |task| {
|
||||||
_ => return Err(Status::InvalidArgument),
|
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 virtual_addr = VirtualAddr::new(virtual_addr);
|
||||||
let permissions = PagePermissions::new(
|
|
||||||
permissions & (1 << 0) != 0,
|
|
||||||
permissions & (1 << 1) != 0,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let map_result = crate::memory::with_address_space_mut(as_id, |target_as| {
|
let map_result = crate::memory::with_address_space_mut(as_id, |target_as| {
|
||||||
crate::memory::with_allocator(|allocator| {
|
crate::memory::with_allocator(|allocator| {
|
||||||
target_as.map(
|
target_as.map(
|
||||||
frame.start_address(),
|
frame.frame_address().start_address(),
|
||||||
virtual_addr,
|
virtual_addr,
|
||||||
permissions,
|
permissions,
|
||||||
allocator,
|
allocator,
|
||||||
@@ -239,48 +392,151 @@ pub fn sys_map(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.ok_or(Status::BadHandle)?;
|
.expect("failed to resolve self address space");
|
||||||
|
|
||||||
map_result.map_err(|_| Status::OutOfMemory)?;
|
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"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
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(as_handle: usize, virtual_addr: usize) -> Result<(), Status> {
|
pub fn sys_unmap(mapping_handle: usize) -> Result<(), Status> {
|
||||||
if virtual_addr % FRAME_SIZE != 0 {
|
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);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
|
||||||
|
|
||||||
if virtual_addr >= USER_SPACE_END.as_usize() {
|
|
||||||
return Err(Status::InvalidArgument);
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = crate::task::scheduler::current();
|
|
||||||
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?;
|
|
||||||
|
|
||||||
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),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let virtual_addr = VirtualAddr::new(virtual_addr);
|
let unmap_result = crate::memory::with_address_space_mut(address_space, |target_as| {
|
||||||
|
|
||||||
let map_result = crate::memory::with_address_space_mut(as_id, |target_as| {
|
|
||||||
if target_as.to_physical(virtual_addr).is_none() {
|
|
||||||
return Err(Status::InvalidArgument);
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::memory::with_allocator(|allocator| unsafe {
|
crate::memory::with_allocator(|allocator| unsafe {
|
||||||
target_as.unmap(virtual_addr, allocator)
|
target_as.unmap(virtual_addr, allocator)
|
||||||
})
|
})
|
||||||
.map_err(|_| Status::BadAddress)
|
});
|
||||||
})
|
|
||||||
.ok_or(Status::BadHandle)?;
|
|
||||||
|
|
||||||
map_result.map_err(|_| Status::OutOfMemory)?;
|
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(())
|
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(
|
pub fn sys_task_create(
|
||||||
@@ -289,31 +545,64 @@ pub fn sys_task_create(
|
|||||||
user_stack: usize,
|
user_stack: usize,
|
||||||
out_task_handle: usize,
|
out_task_handle: usize,
|
||||||
) -> Result<(), Status> {
|
) -> Result<(), Status> {
|
||||||
if out_task_handle == 0 || entry == 0 || user_stack == 0 {
|
if entry == 0 || user_stack == 0 || out_task_handle == 0 {
|
||||||
return Err(Status::InvalidArgument);
|
return Err(Status::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry >= 0x0000_8000_0000_0000 || user_stack >= 0x0000_8000_0000_0000 {
|
if entry >= USER_SPACE_END.as_usize() || user_stack > USER_SPACE_END.as_usize() {
|
||||||
return Err(Status::BadAddress);
|
return Err(Status::BadAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
let task_id = crate::task::scheduler::current();
|
if out_task_handle % core::mem::align_of::<usize>() != 0 {
|
||||||
let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?;
|
|
||||||
|
|
||||||
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::EXECUTE.0 == 0 {
|
|
||||||
return Err(Status::InvalidArgument);
|
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() {
|
if crate::memory::with_address_space(as_id, |_| ()).is_none() {
|
||||||
return Err(Status::BadHandle);
|
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| {
|
let kernel_stack = match crate::memory::with_kernel_address_space(|kernel_as| {
|
||||||
crate::memory::with_allocator(|allocator| {
|
crate::memory::with_allocator(|allocator| {
|
||||||
crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
|
crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
|
||||||
@@ -327,7 +616,6 @@ pub fn sys_task_create(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let new_tcb = crate::task::tcb::Tcb::new_user(
|
let new_tcb = crate::task::tcb::Tcb::new_user(
|
||||||
0, // assigned by scheduler::add_task
|
|
||||||
as_id,
|
as_id,
|
||||||
kernel_stack,
|
kernel_stack,
|
||||||
VirtualAddr::new(entry),
|
VirtualAddr::new(entry),
|
||||||
@@ -344,15 +632,16 @@ pub fn sys_task_create(
|
|||||||
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle_id = match task.handles.push(handle) {
|
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
|
||||||
Some(id) => id,
|
let handle_id = match current_task.handles.push(handle) {
|
||||||
None => {
|
Ok(id) => id,
|
||||||
crate::task::scheduler::remove_task(new_task_id);
|
Err(_) => {
|
||||||
return Err(Status::OutOfMemory);
|
crate::task::scheduler::remove_task(new_task_id);
|
||||||
}
|
return Err(Status::OutOfMemory);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id)?;
|
unsafe { copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id) }
|
||||||
|
})
|
||||||
Ok(())
|
.expect("failed to resolve self task")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions,
|
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions,
|
||||||
UserStack, VirtualAddr,
|
UserStack, VirtualAddr,
|
||||||
},
|
},
|
||||||
task::tcb::Tcb,
|
task::{scheduler::TaskId, tcb::Tcb},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn spawn(
|
pub fn spawn(
|
||||||
@@ -13,7 +13,7 @@ pub fn spawn(
|
|||||||
kernel_as: &mut AddressSpace,
|
kernel_as: &mut AddressSpace,
|
||||||
allocator: &mut FrameAllocator,
|
allocator: &mut FrameAllocator,
|
||||||
direct_map: DirectMap,
|
direct_map: DirectMap,
|
||||||
) -> usize {
|
) -> TaskId {
|
||||||
let bytes = format::cpio::find_file(initramfs.data(), name)
|
let bytes = format::cpio::find_file(initramfs.data(), name)
|
||||||
.unwrap_or_else(|| panic!("{name} missing from initramfs"));
|
.unwrap_or_else(|| panic!("{name} missing from initramfs"));
|
||||||
let kernel_stack = crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
|
let kernel_stack = crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
|
||||||
@@ -41,10 +41,14 @@ pub fn spawn(
|
|||||||
|
|
||||||
let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF");
|
let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF");
|
||||||
|
|
||||||
let as_id =
|
let as_id = match crate::memory::insert_address_space(address_space) {
|
||||||
crate::memory::insert_address_space(address_space).expect("address space table is full");
|
Ok(id) => id,
|
||||||
|
Err(_) => {
|
||||||
|
panic!("address space table is full");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let task = Tcb::new_user(0, as_id, kernel_stack, entry, user_stack.top());
|
let task = Tcb::new_user(as_id, kernel_stack, entry, user_stack.top());
|
||||||
crate::task::scheduler::add_task(task).expect("scheduler is full")
|
crate::task::scheduler::add_task(task).expect("scheduler is full")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+52
-28
@@ -3,7 +3,8 @@ use core::cell::UnsafeCell;
|
|||||||
use crate::{
|
use crate::{
|
||||||
arch::ThreadContext,
|
arch::ThreadContext,
|
||||||
memory::{
|
memory::{
|
||||||
AddressSpace, FrameAllocator, KernelStack, KernelStackPool, StackCreateError, VirtualAddr,
|
AddressSpace, AddressSpaceId, FrameAllocator, KernelStack, KernelStackPool,
|
||||||
|
StackCreateError, VirtualAddr,
|
||||||
},
|
},
|
||||||
println,
|
println,
|
||||||
task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState},
|
task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState},
|
||||||
@@ -11,7 +12,15 @@ use crate::{
|
|||||||
|
|
||||||
const MAX_TASKS: usize = 32;
|
const MAX_TASKS: usize = 32;
|
||||||
|
|
||||||
type TaskId = usize;
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct TaskId(usize);
|
||||||
|
|
||||||
|
impl TaskId {
|
||||||
|
pub const fn new(id: usize) -> Self {
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Scheduler {
|
struct Scheduler {
|
||||||
current: Option<TaskId>,
|
current: Option<TaskId>,
|
||||||
@@ -33,11 +42,11 @@ impl Scheduler {
|
|||||||
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
|
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
|
||||||
assert_ne!(current_id, next_id);
|
assert_ne!(current_id, next_id);
|
||||||
|
|
||||||
let current = self.tasks[current_id].as_mut().unwrap();
|
let current = self.tasks[current_id.0].as_mut().unwrap();
|
||||||
let prev_ctx = &mut current.context as *mut ThreadContext;
|
let prev_ctx = &mut current.context as *mut ThreadContext;
|
||||||
let prev_as_id = current.as_id;
|
let prev_as_id = current.as_id;
|
||||||
|
|
||||||
let next = self.tasks[next_id].as_ref().unwrap();
|
let next = self.tasks[next_id.0].as_ref().unwrap();
|
||||||
let next_ctx = &next.context as *const ThreadContext;
|
let next_ctx = &next.context as *const ThreadContext;
|
||||||
let next_as_id = next.as_id;
|
let next_as_id = next.as_id;
|
||||||
let next_kernel_stack = next.kernel_stack.top();
|
let next_kernel_stack = next.kernel_stack.top();
|
||||||
@@ -55,7 +64,7 @@ impl Scheduler {
|
|||||||
struct Switch {
|
struct Switch {
|
||||||
previous_context: *mut ThreadContext,
|
previous_context: *mut ThreadContext,
|
||||||
next_context: *const ThreadContext,
|
next_context: *const ThreadContext,
|
||||||
next_as_id: usize,
|
next_as_id: AddressSpaceId,
|
||||||
next_kernel_stack: VirtualAddr,
|
next_kernel_stack: VirtualAddr,
|
||||||
activate_address_space: bool,
|
activate_address_space: bool,
|
||||||
}
|
}
|
||||||
@@ -85,7 +94,7 @@ struct ReadyQueue {
|
|||||||
impl ReadyQueue {
|
impl ReadyQueue {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
entries: [0; MAX_TASKS],
|
entries: [TaskId(0); MAX_TASKS],
|
||||||
head: 0,
|
head: 0,
|
||||||
len: 0,
|
len: 0,
|
||||||
}
|
}
|
||||||
@@ -147,15 +156,20 @@ pub fn add_task(mut task: Tcb) -> Result<TaskId, Tcb> {
|
|||||||
|
|
||||||
match scheduler.tasks.iter().position(Option::is_none) {
|
match scheduler.tasks.iter().position(Option::is_none) {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
|
let id = TaskId(id);
|
||||||
|
|
||||||
task.id = id;
|
task.id = id;
|
||||||
|
|
||||||
task.handles.push(Handle {
|
match task.handles.push(Handle {
|
||||||
object: KernelObject::Thread(id),
|
object: KernelObject::Thread(id),
|
||||||
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
||||||
});
|
}) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => unreachable!("Cant push root thread handle"),
|
||||||
|
}
|
||||||
|
|
||||||
task.state = ThreadState::Ready;
|
task.state = ThreadState::Ready;
|
||||||
scheduler.tasks[id] = Some(task);
|
scheduler.tasks[id.0] = Some(task);
|
||||||
assert!(scheduler.ready.push_back(id));
|
assert!(scheduler.ready.push_back(id));
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
@@ -176,7 +190,7 @@ pub fn start() -> ! {
|
|||||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
let next_id = scheduler.ready.pop_front().expect("no tasks to run");
|
let next_id = scheduler.ready.pop_front().expect("no tasks to run");
|
||||||
|
|
||||||
let next = scheduler.tasks[next_id]
|
let next = scheduler.tasks[next_id.0]
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("ready task is missing");
|
.expect("ready task is missing");
|
||||||
|
|
||||||
@@ -210,20 +224,20 @@ pub fn allocate_kernel_stack(
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_task(id: TaskId) -> Option<Tcb> {
|
pub fn remove_task(id: TaskId) -> bool {
|
||||||
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
|
|
||||||
if scheduler.current == Some(id) {
|
if scheduler.current == Some(id) {
|
||||||
None
|
false
|
||||||
} else if let Some(task) = scheduler.tasks.get_mut(id).and_then(Option::take) {
|
} else if let Some(task) = scheduler.tasks.get_mut(id.0).and_then(Option::take) {
|
||||||
scheduler.ready.remove(id);
|
scheduler.ready.remove(id);
|
||||||
scheduler.stacks.free(&task.kernel_stack);
|
scheduler.stacks.free(task.kernel_stack);
|
||||||
Some(task)
|
true
|
||||||
} else {
|
} else {
|
||||||
None
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -231,14 +245,24 @@ pub fn remove_task(id: TaskId) -> Option<Tcb> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_task(id: TaskId) -> Option<&'static Tcb> {
|
pub fn with_task<R>(id: TaskId, f: impl FnOnce(&Tcb) -> R) -> Option<R> {
|
||||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
scheduler.tasks.get(id).and_then(Option::as_ref)
|
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 get_task_mut(id: TaskId) -> Option<&'static mut Tcb> {
|
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 scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
scheduler.tasks.get_mut(id).and_then(Option::as_mut)
|
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 {
|
pub fn current() -> TaskId {
|
||||||
@@ -259,10 +283,10 @@ pub fn block_current(reason: BlockReason) {
|
|||||||
|
|
||||||
let current_id = scheduler.current.expect("no current task");
|
let current_id = scheduler.current.expect("no current task");
|
||||||
|
|
||||||
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Blocked(reason);
|
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
|
// explicitly do NOT push back the current task, because it is not ready
|
||||||
|
|
||||||
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running;
|
scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
|
||||||
scheduler.current = Some(next_id);
|
scheduler.current = Some(next_id);
|
||||||
|
|
||||||
scheduler.make_switch(current_id, next_id)
|
scheduler.make_switch(current_id, next_id)
|
||||||
@@ -280,7 +304,7 @@ pub fn unblock(id: TaskId) {
|
|||||||
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||||
|
|
||||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||||
if let Some(task) = scheduler.tasks[id].as_mut() {
|
if let Some(task) = scheduler.tasks[id.0].as_mut() {
|
||||||
if matches!(task.state, ThreadState::Blocked(_)) {
|
if matches!(task.state, ThreadState::Blocked(_)) {
|
||||||
task.state = ThreadState::Ready;
|
task.state = ThreadState::Ready;
|
||||||
assert!(scheduler.ready.push_back(id));
|
assert!(scheduler.ready.push_back(id));
|
||||||
@@ -303,10 +327,10 @@ pub fn yield_current() {
|
|||||||
|
|
||||||
let current_id = scheduler.current.expect("no current task");
|
let current_id = scheduler.current.expect("no current task");
|
||||||
|
|
||||||
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Ready;
|
scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Ready;
|
||||||
assert!(scheduler.ready.push_back(current_id));
|
assert!(scheduler.ready.push_back(current_id));
|
||||||
|
|
||||||
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running;
|
scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
|
||||||
scheduler.current = Some(next_id);
|
scheduler.current = Some(next_id);
|
||||||
|
|
||||||
scheduler.make_switch(current_id, next_id)
|
scheduler.make_switch(current_id, next_id)
|
||||||
@@ -331,10 +355,10 @@ pub fn exit_current(exit_code: usize) -> ! {
|
|||||||
crate::hcf();
|
crate::hcf();
|
||||||
};
|
};
|
||||||
|
|
||||||
let current = scheduler.tasks[current_id].as_mut().unwrap();
|
let current = scheduler.tasks[current_id.0].as_mut().unwrap();
|
||||||
current.state = ThreadState::Dead(ExitReason::Exited(exit_code));
|
current.state = ThreadState::Dead(ExitReason::Exited(exit_code));
|
||||||
|
|
||||||
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running;
|
scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
|
||||||
scheduler.current = Some(next_id);
|
scheduler.current = Some(next_id);
|
||||||
|
|
||||||
scheduler.make_switch(current_id, next_id)
|
scheduler.make_switch(current_id, next_id)
|
||||||
|
|||||||
+54
-19
@@ -2,22 +2,29 @@ use core::ops::BitOr;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::ThreadContext,
|
arch::ThreadContext,
|
||||||
memory::{FrameAddr, KernelStack, VirtualAddr},
|
memory::{AddressSpaceId, KernelStack, OwnedFrame, VirtualAddr},
|
||||||
|
task::scheduler::TaskId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Fault {
|
||||||
|
SegmentationFault,
|
||||||
|
IllegalInstruction,
|
||||||
|
Abort,
|
||||||
|
BadSystemCall,
|
||||||
|
}
|
||||||
|
|
||||||
// Thread Control Block
|
// Thread Control Block
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ExitReason {
|
pub enum ExitReason {
|
||||||
Exited(usize),
|
Exited(usize),
|
||||||
Killed,
|
Killed,
|
||||||
Fault,
|
Fault(Fault),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum BlockReason {
|
pub enum BlockReason {
|
||||||
Send { ep: usize },
|
Recv,
|
||||||
Recv { ep: usize },
|
|
||||||
Reply { client: usize },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@@ -33,7 +40,7 @@ pub const MAILBOX_CAPACITY: usize = 4;
|
|||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub sender: usize,
|
pub sender: TaskId,
|
||||||
pub length: usize,
|
pub length: usize,
|
||||||
pub data: [u8; MAX_MSG_SIZE],
|
pub data: [u8; MAX_MSG_SIZE],
|
||||||
}
|
}
|
||||||
@@ -79,9 +86,14 @@ impl Mailbox {
|
|||||||
const MAX_HANDLES: usize = 32;
|
const MAX_HANDLES: usize = 32;
|
||||||
|
|
||||||
pub enum KernelObject {
|
pub enum KernelObject {
|
||||||
AddressSpace(usize),
|
AddressSpace(AddressSpaceId),
|
||||||
Frame(FrameAddr),
|
Frame(OwnedFrame),
|
||||||
Thread(usize),
|
Mapping {
|
||||||
|
frame: OwnedFrame,
|
||||||
|
address_space: AddressSpaceId,
|
||||||
|
virtual_addr: VirtualAddr,
|
||||||
|
},
|
||||||
|
Thread(TaskId),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
@@ -118,15 +130,36 @@ impl HandleTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, handle: Handle) -> Option<usize> {
|
pub fn push(&mut self, handle: Handle) -> Result<usize, Handle> {
|
||||||
for (i, slot) in self.handles.iter_mut().enumerate() {
|
for (i, slot) in self.handles.iter_mut().enumerate() {
|
||||||
if slot.is_none() {
|
if slot.is_none() {
|
||||||
*slot = Some(handle);
|
*slot = Some(handle);
|
||||||
return Some(i);
|
return Ok(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
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> {
|
pub fn get(&self, id: usize) -> Option<&Handle> {
|
||||||
@@ -139,8 +172,8 @@ impl HandleTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tcb {
|
pub struct Tcb {
|
||||||
pub id: usize,
|
pub id: TaskId,
|
||||||
pub as_id: usize,
|
pub as_id: AddressSpaceId,
|
||||||
pub state: ThreadState,
|
pub state: ThreadState,
|
||||||
pub kernel_stack: KernelStack,
|
pub kernel_stack: KernelStack,
|
||||||
pub context: ThreadContext,
|
pub context: ThreadContext,
|
||||||
@@ -161,21 +194,23 @@ impl core::fmt::Debug for Tcb {
|
|||||||
|
|
||||||
impl Tcb {
|
impl Tcb {
|
||||||
pub fn new_user(
|
pub fn new_user(
|
||||||
id: usize,
|
as_id: AddressSpaceId,
|
||||||
as_id: usize,
|
|
||||||
kernel_stack: KernelStack,
|
kernel_stack: KernelStack,
|
||||||
entry: VirtualAddr,
|
entry: VirtualAddr,
|
||||||
user_stack: VirtualAddr,
|
user_stack: VirtualAddr,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let context = ThreadContext::new(entry, user_stack, kernel_stack.top());
|
let context = ThreadContext::new(entry, user_stack, kernel_stack.top());
|
||||||
let mut handles = HandleTable::new();
|
let mut handles = HandleTable::new();
|
||||||
handles.push(Handle {
|
match handles.push(Handle {
|
||||||
object: KernelObject::AddressSpace(as_id),
|
object: KernelObject::AddressSpace(as_id),
|
||||||
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
|
||||||
});
|
}) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => unreachable!("Cant push root address space handle"),
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id: TaskId::new(0),
|
||||||
as_id,
|
as_id,
|
||||||
state: ThreadState::Ready,
|
state: ThreadState::Ready,
|
||||||
kernel_stack,
|
kernel_stack,
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ pub extern "C" fn _start() -> ! {
|
|||||||
let msg = "Hello from client!";
|
let msg = "Hello from client!";
|
||||||
println!("[client] Sent: {}", msg);
|
println!("[client] Sent: {}", msg);
|
||||||
// TODO: we assume the echo server is task 1 (spawned by omega3)
|
// TODO: we assume the echo server is task 1 (spawned by omega3)
|
||||||
sys_send(1, msg.as_ptr() as usize, msg.len()).unwrap();
|
sys_send(1, msg.as_bytes()).unwrap();
|
||||||
|
|
||||||
let out = [0u8; 128];
|
let mut out = [0u8; 128];
|
||||||
let out_ptr = out.as_ptr() as usize;
|
let (actual_len, _) = sys_recv(&mut out).unwrap();
|
||||||
let max_len = out.len();
|
|
||||||
let (actual_len, _) = sys_recv(out_ptr, max_len).unwrap();
|
|
||||||
println!(
|
println!(
|
||||||
"[client] Received: {}",
|
"[client] Received: {}",
|
||||||
core::str::from_utf8(&out[..actual_len]).unwrap()
|
core::str::from_utf8(&out[..actual_len]).unwrap()
|
||||||
|
|||||||
@@ -10,15 +10,25 @@ pub enum Status {
|
|||||||
BadFileDescriptor = 3,
|
BadFileDescriptor = 3,
|
||||||
NoSuchTask = 4,
|
NoSuchTask = 4,
|
||||||
OutOfMemory = 5,
|
OutOfMemory = 5,
|
||||||
|
BadHandle = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opaque handle type
|
// Opaque handle type
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct Handle(usize);
|
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
|
// our own address space and thread handle are always given to us
|
||||||
pub const SELF_AS: Handle = Handle(0);
|
pub const SELF_AS: AddressSpaceHandle = AddressSpaceHandle(0);
|
||||||
pub const SELF_THREAD: Handle = Handle(1);
|
pub const SELF_THREAD: ThreadHandle = ThreadHandle(1);
|
||||||
|
|
||||||
impl From<usize> for Status {
|
impl From<usize> for Status {
|
||||||
fn from(value: usize) -> Self {
|
fn from(value: usize) -> Self {
|
||||||
@@ -28,6 +38,7 @@ impl From<usize> for Status {
|
|||||||
3 => Self::BadFileDescriptor,
|
3 => Self::BadFileDescriptor,
|
||||||
4 => Self::NoSuchTask,
|
4 => Self::NoSuchTask,
|
||||||
5 => Self::OutOfMemory,
|
5 => Self::OutOfMemory,
|
||||||
|
6 => Self::BadHandle,
|
||||||
_ => Self::InvalidArgument,
|
_ => Self::InvalidArgument,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,15 +136,15 @@ pub fn sys_exit(exit_code: usize) -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> {
|
pub fn sys_send(dest_task_id: usize, msg: &[u8]) -> Result<(), Status> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let status: usize;
|
let status: usize;
|
||||||
|
|
||||||
asm!(
|
asm!(
|
||||||
"syscall",
|
"syscall",
|
||||||
in("rdi") dest_task_id,
|
in("rdi") dest_task_id,
|
||||||
in("rsi") msg_ptr,
|
in("rsi") msg.as_ptr(),
|
||||||
in("rdx") len,
|
in("rdx") msg.len(),
|
||||||
inlateout("rax") SyscallNumber::Send as usize => status,
|
inlateout("rax") SyscallNumber::Send as usize => status,
|
||||||
lateout("rcx") _,
|
lateout("rcx") _,
|
||||||
lateout("r11") _,
|
lateout("r11") _,
|
||||||
@@ -147,7 +158,7 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status> {
|
pub fn sys_recv(buf: &mut [u8]) -> Result<(usize, usize), Status> {
|
||||||
let mut actual_len: usize = 0;
|
let mut actual_len: usize = 0;
|
||||||
let mut sender: usize = 0;
|
let mut sender: usize = 0;
|
||||||
|
|
||||||
@@ -156,8 +167,8 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status
|
|||||||
|
|
||||||
asm!(
|
asm!(
|
||||||
"syscall",
|
"syscall",
|
||||||
in("rdi") buf_ptr,
|
in("rdi") buf.as_mut_ptr(),
|
||||||
in("rsi") max_len,
|
in("rsi") buf.len(),
|
||||||
in("rdx") &raw mut actual_len as usize,
|
in("rdx") &raw mut actual_len as usize,
|
||||||
in("r10") &raw mut sender as usize,
|
in("r10") &raw mut sender as usize,
|
||||||
inlateout("rax") SyscallNumber::Recv as usize => status,
|
inlateout("rax") SyscallNumber::Recv as usize => status,
|
||||||
@@ -173,7 +184,7 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_frame_alloc() -> Result<Handle, Status> {
|
pub fn sys_frame_alloc() -> Result<FrameHandle, Status> {
|
||||||
let mut handle: usize = 0;
|
let mut handle: usize = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
let status: usize;
|
let status: usize;
|
||||||
@@ -189,12 +200,12 @@ pub fn sys_frame_alloc() -> Result<Handle, Status> {
|
|||||||
if status != 0 {
|
if status != 0 {
|
||||||
Err(status.into())
|
Err(status.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Handle(handle))
|
Ok(FrameHandle(handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_frame_dealloc(frame_handle: Handle) -> Result<(), Status> {
|
pub fn sys_frame_dealloc(frame_handle: FrameHandle) -> Result<(), Status> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let status: usize;
|
let status: usize;
|
||||||
|
|
||||||
@@ -214,7 +225,7 @@ pub fn sys_frame_dealloc(frame_handle: Handle) -> Result<(), Status> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_as_create() -> Result<Handle, Status> {
|
pub fn sys_as_create() -> Result<AddressSpaceHandle, Status> {
|
||||||
let mut handle: usize = 0;
|
let mut handle: usize = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
let status: usize;
|
let status: usize;
|
||||||
@@ -230,17 +241,19 @@ pub fn sys_as_create() -> Result<Handle, Status> {
|
|||||||
if status != 0 {
|
if status != 0 {
|
||||||
Err(status.into())
|
Err(status.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Handle(handle))
|
Ok(AddressSpaceHandle(handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_map(
|
pub fn sys_map(
|
||||||
as_handle: Handle,
|
as_handle: AddressSpaceHandle,
|
||||||
frame_handle: Handle,
|
frame_handle: FrameHandle,
|
||||||
virtual_addr: usize,
|
virtual_addr: usize,
|
||||||
permissions: usize,
|
permissions: usize,
|
||||||
) -> Result<(), Status> {
|
) -> Result<MappingHandle, Status> {
|
||||||
|
let mut handle: usize = 0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let status: usize;
|
let status: usize;
|
||||||
|
|
||||||
@@ -250,6 +263,7 @@ pub fn sys_map(
|
|||||||
in("rsi") frame_handle.0,
|
in("rsi") frame_handle.0,
|
||||||
in("rdx") virtual_addr,
|
in("rdx") virtual_addr,
|
||||||
in("r10") permissions,
|
in("r10") permissions,
|
||||||
|
in("r8") &raw mut handle as usize,
|
||||||
inlateout("rax") SyscallNumber::Map as usize => status,
|
inlateout("rax") SyscallNumber::Map as usize => status,
|
||||||
lateout("rcx") _,
|
lateout("rcx") _,
|
||||||
lateout("r11") _,
|
lateout("r11") _,
|
||||||
@@ -258,19 +272,18 @@ pub fn sys_map(
|
|||||||
if status != 0 {
|
if status != 0 {
|
||||||
Err(status.into())
|
Err(status.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(MappingHandle(handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> {
|
pub fn sys_unmap(mapping_handle: MappingHandle) -> Result<(), Status> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let status: usize;
|
let status: usize;
|
||||||
|
|
||||||
asm!(
|
asm!(
|
||||||
"syscall",
|
"syscall",
|
||||||
in("rdi") as_handle.0,
|
in("rdi") mapping_handle.0,
|
||||||
in("rsi") virtual_addr,
|
|
||||||
inlateout("rax") SyscallNumber::Unmap as usize => status,
|
inlateout("rax") SyscallNumber::Unmap as usize => status,
|
||||||
lateout("rcx") _,
|
lateout("rcx") _,
|
||||||
lateout("r11") _,
|
lateout("r11") _,
|
||||||
@@ -285,10 +298,10 @@ pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_task_create(
|
pub fn sys_task_create(
|
||||||
as_handle: Handle,
|
as_handle: AddressSpaceHandle,
|
||||||
entry: usize,
|
entry: usize,
|
||||||
user_stack: usize,
|
user_stack: usize,
|
||||||
) -> Result<Handle, Status> {
|
) -> Result<ThreadHandle, Status> {
|
||||||
let mut handle: usize = 0;
|
let mut handle: usize = 0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -308,7 +321,7 @@ pub fn sys_task_create(
|
|||||||
if status != 0 {
|
if status != 0 {
|
||||||
Err(status.into())
|
Err(status.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Handle(handle))
|
Ok(ThreadHandle(handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use dusk_sys::{println, sys_exit, sys_recv, sys_send};
|
|||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn _start() -> ! {
|
pub extern "C" fn _start() -> ! {
|
||||||
let out = [0u8; 128];
|
let mut out = [0u8; 128];
|
||||||
loop {
|
loop {
|
||||||
let (actual_len, sender) = sys_recv(out.as_ptr() as usize, out.len()).unwrap();
|
let (actual_len, sender) = sys_recv(&mut out).unwrap();
|
||||||
println!(
|
println!(
|
||||||
"[echo] Received: {}",
|
"[echo] Received: {}",
|
||||||
core::str::from_utf8(&out[..actual_len]).unwrap()
|
core::str::from_utf8(&out[..actual_len]).unwrap()
|
||||||
);
|
);
|
||||||
sys_send(sender, out.as_ptr() as usize, actual_len).unwrap();
|
sys_send(sender, &out[..actual_len]).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ mod cpio;
|
|||||||
mod elf;
|
mod elf;
|
||||||
|
|
||||||
use dusk_sys::{
|
use dusk_sys::{
|
||||||
Handle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map, sys_task_create,
|
AddressSpaceHandle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map,
|
||||||
sys_unmap, sys_yield,
|
sys_task_create, sys_unmap, sys_yield,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mapped into the root task's address space by the kernel.
|
// Mapped into the root task's address space by the kernel.
|
||||||
@@ -40,10 +40,15 @@ pub extern "C" fn _start() -> ! {
|
|||||||
map_stack(client_as, STACK_TOP, STACK_PAGES);
|
map_stack(client_as, STACK_TOP, STACK_PAGES);
|
||||||
let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap();
|
let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap();
|
||||||
|
|
||||||
|
let ptr = 0xDEAD_BEEF as *mut u32;
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_volatile(&mut *ptr, 0xDEAD_BEEF);
|
||||||
|
}
|
||||||
|
|
||||||
sys_exit(0);
|
sys_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
|
fn load_elf(elf: &elf::Elf, target_as: AddressSpaceHandle) -> usize {
|
||||||
for header in elf.program_headers().unwrap() {
|
for header in elf.program_headers().unwrap() {
|
||||||
let header = header.unwrap();
|
let header = header.unwrap();
|
||||||
if header.segment_type != elf::ProgramHeaderType::Load || header.memory_size == 0 {
|
if header.segment_type != elf::ProgramHeaderType::Load || header.memory_size == 0 {
|
||||||
@@ -66,7 +71,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
|
|||||||
for page in (page_start..segment_end).step_by(0x1000) {
|
for page in (page_start..segment_end).step_by(0x1000) {
|
||||||
let frame = sys_frame_alloc().unwrap();
|
let frame = sys_frame_alloc().unwrap();
|
||||||
|
|
||||||
sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap();
|
let scratch_handle = sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
core::ptr::write_bytes(SCRATCH_PAGE as *mut u8, 0, 0x1000);
|
core::ptr::write_bytes(SCRATCH_PAGE as *mut u8, 0, 0x1000);
|
||||||
|
|
||||||
@@ -84,7 +89,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sys_unmap(SELF_AS, SCRATCH_PAGE).unwrap();
|
sys_unmap(scratch_handle).unwrap();
|
||||||
|
|
||||||
sys_map(target_as, frame, page, perms).unwrap();
|
sys_map(target_as, frame, page, perms).unwrap();
|
||||||
}
|
}
|
||||||
@@ -93,7 +98,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
|
|||||||
elf.entry()
|
elf.entry()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_stack(target_as: Handle, stack_top: usize, pages: usize) {
|
fn map_stack(target_as: AddressSpaceHandle, stack_top: usize, pages: usize) {
|
||||||
for i in 1..=pages {
|
for i in 1..=pages {
|
||||||
let frame = sys_frame_alloc().unwrap();
|
let frame = sys_frame_alloc().unwrap();
|
||||||
let page_addr = stack_top - i * 0x1000;
|
let page_addr = stack_top - i * 0x1000;
|
||||||
|
|||||||
Reference in New Issue
Block a user