feat: tasks
This commit is contained in:
+4
-1
@@ -5,4 +5,7 @@ mod x86_64;
|
||||
pub use x86_64::*;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub(crate) use x86_64::{PageTableCreateError, PageTableMapError, PageTableUnmapError};
|
||||
pub(crate) use x86_64::{
|
||||
PageTableCreateError, PageTableMapError, PageTableUnmapError, ThreadContext, set_kernel_stack,
|
||||
switch_context,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::{
|
||||
memory::{
|
||||
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
|
||||
},
|
||||
println,
|
||||
};
|
||||
|
||||
const APIC_ID: u32 = 0x20;
|
||||
|
||||
+109
-1
@@ -1,8 +1,104 @@
|
||||
use core::arch::asm;
|
||||
use core::arch::{asm, naked_asm};
|
||||
|
||||
use crate::memory::VirtualAddr;
|
||||
|
||||
#[repr(C, align(64))]
|
||||
pub struct CpuLocal {
|
||||
pub kernel_stack_top: usize,
|
||||
pub user_rsp_scratch: usize,
|
||||
pub cpu_id: u32,
|
||||
}
|
||||
|
||||
pub static mut BOOT_CPU: CpuLocal = CpuLocal {
|
||||
kernel_stack_top: 0,
|
||||
user_rsp_scratch: 0,
|
||||
cpu_id: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadContext {
|
||||
rsp: usize,
|
||||
}
|
||||
|
||||
impl ThreadContext {
|
||||
pub fn new(
|
||||
user_entry: VirtualAddr,
|
||||
user_stack: VirtualAddr,
|
||||
kernel_stack_top: VirtualAddr,
|
||||
) -> Self {
|
||||
// Stack layout (grows downwards from kernel_stack_top):
|
||||
// [top - 8] = user_thread_entry (popped by `ret`)
|
||||
// [top - 16] = rbp (0)
|
||||
// [top - 24] = rbx (user_stack)
|
||||
// [top - 32] = r12 (user_entry)
|
||||
// [top - 40] = r13 (0)
|
||||
// [top - 48] = r14 (0)
|
||||
// [top - 56] = r15 (0) <- initial rsp
|
||||
|
||||
let stack_ptr = (kernel_stack_top.as_usize() - 56) as *mut usize;
|
||||
|
||||
unsafe {
|
||||
stack_ptr.add(0).write(0); // r15
|
||||
stack_ptr.add(1).write(0); // r14
|
||||
stack_ptr.add(2).write(0); // r13
|
||||
stack_ptr.add(3).write(user_entry.as_usize()); // r12 (user_entry)
|
||||
stack_ptr.add(4).write(user_stack.as_usize()); // rbx (user_stack)
|
||||
stack_ptr.add(5).write(0); // rbp (0)
|
||||
stack_ptr
|
||||
.add(6)
|
||||
.write(user_thread_entry as *const () as usize); // return address
|
||||
}
|
||||
|
||||
Self {
|
||||
rsp: kernel_stack_top.as_usize() - 56,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self { rsp: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
unsafe extern "C" fn user_thread_entry() -> ! {
|
||||
naked_asm!(
|
||||
// r12 = user_entry, rbx = user_stack
|
||||
"mov rdi, r12",
|
||||
"mov rsi, rbx",
|
||||
"call {enter_user}",
|
||||
enter_user = sym crate::arch::enter_user,
|
||||
);
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
pub unsafe extern "C" fn switch_context(prev: *mut ThreadContext, next: *const ThreadContext) {
|
||||
naked_asm!(
|
||||
"push rbp",
|
||||
"push rbx",
|
||||
"push r12",
|
||||
"push r13",
|
||||
"push r14",
|
||||
"push r15",
|
||||
"",
|
||||
// save current rsp into prev.rsp
|
||||
"mov [rdi], rsp",
|
||||
// load next rsp into rsp
|
||||
"mov rsp, [rsi]",
|
||||
"",
|
||||
"pop r15",
|
||||
"pop r14",
|
||||
"pop r13",
|
||||
"pop r12",
|
||||
"pop rbx",
|
||||
"pop rbp",
|
||||
"ret",
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CpuFeaturesError {
|
||||
CpuidFeaturesNotSupported,
|
||||
SyscallNotSupported,
|
||||
InvalidPhysicalAddressWidth,
|
||||
InvalidVirtualAddressWidth,
|
||||
}
|
||||
@@ -40,6 +136,18 @@ pub fn detect_features_and_enable() -> Result<CpuFeatures, CpuFeaturesError> {
|
||||
|
||||
features.nx_supported = cpuid_result.edx & (1 << 20) != 0;
|
||||
|
||||
// TODO: on AMD K6 *only*, this bit is bit 10, should we consider that edge case?
|
||||
let syscall_supported = cpuid_result.edx & (1 << 11) != 0;
|
||||
if !syscall_supported {
|
||||
return Err(CpuFeaturesError::SyscallNotSupported);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let efer = read_msr(IA32_EFER);
|
||||
let value = efer | 1;
|
||||
write_msr(IA32_EFER, value);
|
||||
};
|
||||
|
||||
let cpuid_result = core::arch::x86_64::__cpuid(0x80000008);
|
||||
|
||||
features.physical_address_bits = (cpuid_result.eax & 0xFF) as u8;
|
||||
|
||||
@@ -3,8 +3,8 @@ use core::arch::asm;
|
||||
use crate::memory::VirtualAddr;
|
||||
|
||||
#[repr(C, align(8))]
|
||||
struct Gdt {
|
||||
entries: [u64; 7],
|
||||
pub(super) struct Gdt {
|
||||
pub entries: [u64; 7],
|
||||
}
|
||||
|
||||
impl Gdt {
|
||||
@@ -49,12 +49,12 @@ const DOUBLE_FAULT_STACK_SIZE: usize = 16 * 1024;
|
||||
|
||||
pub(super) const KERNEL_CODE_SELECTOR: u16 = 1 * 8;
|
||||
pub(super) const KERNEL_DATA_SELECTOR: u16 = 2 * 8;
|
||||
pub(super) const USER_CODE_SELECTOR: u16 = (3 * 8) | 3;
|
||||
pub(super) const USER_DATA_SELECTOR: u16 = (4 * 8) | 3;
|
||||
pub(super) const USER_DATA_SELECTOR: u16 = (3 * 8) | 3;
|
||||
pub(super) const USER_CODE_SELECTOR: u16 = (4 * 8) | 3;
|
||||
pub(super) const TSS_SELECTOR: u16 = 5 * 8;
|
||||
|
||||
#[repr(align(16))]
|
||||
#[allow(dead_code)] // field 0 is read, rust just cant tell
|
||||
#[allow(unused)] // field 0 is read, rust just cant tell
|
||||
struct ExceptionStack([u8; DOUBLE_FAULT_STACK_SIZE]);
|
||||
|
||||
static mut GDT: Gdt = Gdt::new();
|
||||
@@ -78,8 +78,10 @@ pub fn init() {
|
||||
0,
|
||||
kernel_code_descriptor(),
|
||||
kernel_data_descriptor(),
|
||||
user_code_descriptor(),
|
||||
// In Long Mode, userland CS will be loaded from STAR 63:48 + 16
|
||||
// and userland SS from STAR 63:48 + 8 on SYSRET.
|
||||
user_data_descriptor(),
|
||||
user_code_descriptor(),
|
||||
tss_low,
|
||||
tss_high,
|
||||
],
|
||||
|
||||
@@ -6,6 +6,33 @@ mod idt;
|
||||
|
||||
pub use idt::idt_init as init;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn disable_interrupts_and_save() -> u64 {
|
||||
let flags: u64;
|
||||
|
||||
unsafe {
|
||||
asm!("
|
||||
pushfq",
|
||||
"pop {flags}",
|
||||
"cli",
|
||||
flags = out(reg) flags,
|
||||
);
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn restore_interrupts(flags: u64) {
|
||||
unsafe {
|
||||
asm!(
|
||||
"push {flags}",
|
||||
"popfq",
|
||||
flags = in(reg) flags,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn disable_interrupts() {
|
||||
unsafe {
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{
|
||||
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
|
||||
},
|
||||
platform::acpi::{InterruptPolarity, TriggerMode},
|
||||
println,
|
||||
};
|
||||
|
||||
const IOWIN: usize = 0x10;
|
||||
|
||||
+41
-14
@@ -1,16 +1,18 @@
|
||||
pub mod apic;
|
||||
pub(super) mod apic;
|
||||
mod cpu;
|
||||
mod gdt;
|
||||
mod interrupts;
|
||||
pub mod io_apic;
|
||||
pub(super) mod io_apic;
|
||||
mod paging;
|
||||
mod pit;
|
||||
pub mod port;
|
||||
mod syscall;
|
||||
pub mod timer;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
pub use interrupts::disable_interrupts;
|
||||
pub use cpu::{ThreadContext, switch_context};
|
||||
pub use interrupts::{disable_interrupts, disable_interrupts_and_save, restore_interrupts};
|
||||
pub(crate) use paging::{
|
||||
MapError as PageTableMapError, PageTableCreateError, UnmapError as PageTableUnmapError,
|
||||
};
|
||||
@@ -22,11 +24,7 @@ pub struct ArchState {
|
||||
|
||||
use crate::{
|
||||
KernelHandoff,
|
||||
arch::{
|
||||
apic::LocalApic,
|
||||
io_apic::{IOAPIC_VIRTUAL_ADDRESS, IoApic},
|
||||
x86_64::interrupts::apic_vectors::PIT_CALIBRATION_VECTOR,
|
||||
},
|
||||
arch::x86_64::cpu::BOOT_CPU,
|
||||
memory::{AddressSpace, FrameAllocator, VirtualAddr},
|
||||
platform::acpi::Madt,
|
||||
println,
|
||||
@@ -45,6 +43,14 @@ pub fn init() -> ArchState {
|
||||
ArchState { paging }
|
||||
}
|
||||
|
||||
pub fn set_kernel_stack(stack_top: VirtualAddr) {
|
||||
gdt::set_kernel_stack(stack_top);
|
||||
|
||||
unsafe {
|
||||
BOOT_CPU.kernel_stack_top = stack_top.as_usize();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub enum InterruptInitError {
|
||||
@@ -60,8 +66,8 @@ pub enum InterruptInitError {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptController {
|
||||
local_apic: LocalApic,
|
||||
io_apic: IoApic,
|
||||
local_apic: apic::LocalApic,
|
||||
io_apic: io_apic::IoApic,
|
||||
local_timer_frequency: u64,
|
||||
}
|
||||
|
||||
@@ -89,7 +95,7 @@ pub fn init_interrupt_controller(
|
||||
io_apic_info.id,
|
||||
io_apic_info.apic_address,
|
||||
io_apic_info.global_system_interrupt_base,
|
||||
IOAPIC_VIRTUAL_ADDRESS,
|
||||
io_apic::IOAPIC_VIRTUAL_ADDRESS,
|
||||
allocator,
|
||||
address_space,
|
||||
)
|
||||
@@ -107,7 +113,7 @@ pub fn init_interrupt_controller(
|
||||
.configure_masked(
|
||||
pit_route.gsi,
|
||||
io_apic::RedirectionConfig {
|
||||
vector: PIT_CALIBRATION_VECTOR,
|
||||
vector: interrupts::apic_vectors::PIT_CALIBRATION_VECTOR,
|
||||
destination,
|
||||
polarity: pit_route.polarity,
|
||||
trigger: pit_route.trigger,
|
||||
@@ -165,6 +171,9 @@ pub unsafe fn enter_kernel(stack_top: VirtualAddr, handoff: *mut KernelHandoff)
|
||||
unsafe {
|
||||
gdt::set_kernel_stack(stack_top);
|
||||
|
||||
BOOT_CPU.kernel_stack_top = stack_top.as_usize();
|
||||
syscall::init(&raw const BOOT_CPU);
|
||||
|
||||
asm!(
|
||||
"mov rsp, {stack_top}",
|
||||
"xor rbp, rbp",
|
||||
@@ -193,13 +202,31 @@ pub unsafe fn enter_user(
|
||||
"mov ds, {user_data_selector:x}",
|
||||
"mov es, {user_data_selector:x}",
|
||||
"mov fs, {user_data_selector:x}",
|
||||
"mov gs, {user_data_selector:x}", // ss is handled by iretq
|
||||
"mov gs, {user_data_selector:x}",
|
||||
|
||||
"push {user_data_selector}",
|
||||
"push {user_stack_pointer}",
|
||||
"pushfq",
|
||||
"push 0x202", // RFLAGS (IF=1, bit 1 reserved=1)
|
||||
"push {user_code_selector}",
|
||||
"push {user_instruction_pointer}",
|
||||
|
||||
// clear GPRs
|
||||
"xor rax, rax",
|
||||
"xor rbx, rbx",
|
||||
"xor rcx, rcx",
|
||||
"xor rdx, rdx",
|
||||
"xor rsi, rsi",
|
||||
"xor rdi, rdi",
|
||||
"xor rbp, rbp",
|
||||
"xor r8, r8",
|
||||
"xor r9, r9",
|
||||
"xor r10, r10",
|
||||
"xor r11, r11",
|
||||
"xor r12, r12",
|
||||
"xor r13, r13",
|
||||
"xor r14, r14",
|
||||
"xor r15, r15",
|
||||
|
||||
"iretq",
|
||||
user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize,
|
||||
user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize,
|
||||
|
||||
@@ -264,12 +264,21 @@ pub(crate) enum PageTableCreateError {
|
||||
OutOfFrames,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PageTable {
|
||||
pub direct_map: DirectMap,
|
||||
config: PagingConfig,
|
||||
frame: OwnedFrame,
|
||||
}
|
||||
|
||||
impl PartialEq for PageTable {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.frame.frame_address() == other.frame.frame_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PageTable {}
|
||||
|
||||
impl PageTable {
|
||||
pub fn new(
|
||||
direct_map: DirectMap,
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
use core::arch::{asm, naked_asm};
|
||||
|
||||
use crate::arch::x86_64::{
|
||||
cpu::{CpuLocal, write_msr},
|
||||
gdt::{KERNEL_CODE_SELECTOR, KERNEL_DATA_SELECTOR},
|
||||
};
|
||||
|
||||
const IA32_STAR: u32 = 0xC000_0081;
|
||||
const IA32_LSTAR: u32 = 0xC000_0082;
|
||||
const IA32_CSTAR: u32 = 0xC000_0083;
|
||||
const IA32_FMASK: u32 = 0xC000_0084;
|
||||
const IA32_GS_BASE: u32 = 0xC000_0101;
|
||||
const IA32_KERNEL_GS_BASE: u32 = 0xC000_0102;
|
||||
|
||||
const RFLAGS_MASK: u64 = 0x257FD5; // Clear IF, TF, DF, IOPL, NT, AC
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct SyscallFrame {
|
||||
pub r15: u64,
|
||||
pub r14: u64,
|
||||
pub r13: u64,
|
||||
pub r12: u64,
|
||||
pub rbp: u64,
|
||||
pub rbx: u64,
|
||||
pub r9: u64, // arg5
|
||||
pub r8: u64, // arg4
|
||||
pub r10: u64, // arg3
|
||||
pub rdx: u64, // arg2
|
||||
pub rsi: u64, // arg1
|
||||
pub rdi: u64, // arg0
|
||||
pub rax: u64, // syscall number on entry / return value on exit
|
||||
pub user_rip: u64, // rcx
|
||||
pub user_rflags: u64, // r11
|
||||
pub user_rsp: u64,
|
||||
}
|
||||
|
||||
pub fn init(cpu_local: *const CpuLocal) {
|
||||
unsafe {
|
||||
let star = ((KERNEL_DATA_SELECTOR as u64) << 48) | ((KERNEL_CODE_SELECTOR as u64) << 32);
|
||||
write_msr(IA32_STAR, star);
|
||||
write_msr(IA32_LSTAR, syscall_entry as *const () as u64);
|
||||
write_msr(IA32_CSTAR, 0);
|
||||
write_msr(IA32_FMASK, RFLAGS_MASK);
|
||||
write_msr(IA32_GS_BASE, 0);
|
||||
write_msr(IA32_KERNEL_GS_BASE, cpu_local as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
unsafe extern "C" fn syscall_entry() {
|
||||
naked_asm!(
|
||||
"swapgs",
|
||||
"mov gs:[8], rsp", // user_rsp_scratch
|
||||
"mov rsp, gs:[0]", // kernel_stack_top
|
||||
"",
|
||||
// build the syscall frame
|
||||
"push qword ptr gs:[8]", // user_rsp
|
||||
"push r11", // user_rflags
|
||||
"push rcx", // user_rip
|
||||
"push rax",
|
||||
"push rdi",
|
||||
"push rsi",
|
||||
"push rdx",
|
||||
"push r10",
|
||||
"push r8",
|
||||
"push r9",
|
||||
"push rbx",
|
||||
"push rbp",
|
||||
"push r12",
|
||||
"push r13",
|
||||
"push r14",
|
||||
"push r15",
|
||||
"",
|
||||
// Syscall calling convention:
|
||||
// Syscall number in rax, args in rdi, rsi, rdx, r10, r8, r9
|
||||
"mov rdi, rsp",
|
||||
"call {dispatch}",
|
||||
"",
|
||||
// restore the syscall frame
|
||||
"pop r15",
|
||||
"pop r14",
|
||||
"pop r13",
|
||||
"pop r12",
|
||||
"pop rbp",
|
||||
"pop rbx",
|
||||
"pop r9",
|
||||
"pop r8",
|
||||
"pop r10",
|
||||
"pop rdx",
|
||||
"pop rsi",
|
||||
"pop rdi",
|
||||
"pop rax", // return value
|
||||
"pop rcx", // user_rip for sysret
|
||||
"pop r11", // user_rflags for sysret
|
||||
"pop qword ptr gs:[8]", // user_rsp
|
||||
"",
|
||||
"mov rsp, gs:[8]", // switch to user stack
|
||||
"swapgs",
|
||||
"sysretq",
|
||||
dispatch = sym syscall_dispatch,
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" fn syscall_dispatch(frame: &mut SyscallFrame) {
|
||||
let ret = crate::task::syscall::handle(
|
||||
frame.rax, frame.rdi, frame.rsi, frame.rdx, frame.r10, frame.r8, frame.r9,
|
||||
);
|
||||
|
||||
frame.rax = ret;
|
||||
}
|
||||
+53
-10
@@ -8,12 +8,14 @@ mod boot;
|
||||
mod debug;
|
||||
mod memory;
|
||||
mod platform;
|
||||
mod task;
|
||||
|
||||
use crate::{
|
||||
debug::serial,
|
||||
memory::{
|
||||
AddressSpace, KernelStack, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr,
|
||||
AddressSpace, KernelStackPool, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr,
|
||||
},
|
||||
task::tcb::Tcb,
|
||||
};
|
||||
|
||||
pub struct KernelHandoff {
|
||||
@@ -21,6 +23,7 @@ pub struct KernelHandoff {
|
||||
address_space: AddressSpace,
|
||||
direct_map: memory::DirectMap,
|
||||
boot_info: boot::BootInfo,
|
||||
kernel_stack_pool: KernelStackPool,
|
||||
handoff_frame: memory::OwnedFrame,
|
||||
}
|
||||
|
||||
@@ -49,7 +52,10 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
println!("Entering kernel main...");
|
||||
|
||||
let bootstrap_stack = KernelStack::allocate(&mut address_space, &mut allocator)
|
||||
let mut kernel_stack_pool = KernelStackPool::new();
|
||||
|
||||
let kernel_stack = kernel_stack_pool
|
||||
.allocate(&mut address_space, &mut allocator)
|
||||
.expect("failed to allocate bootstrap stack");
|
||||
|
||||
let handoff_frame = allocator
|
||||
@@ -59,12 +65,13 @@ pub extern "C" fn _start() -> ! {
|
||||
.to_virtual(handoff_frame.frame_address().start_address())
|
||||
.expect("failed to map kernel handoff");
|
||||
|
||||
let bootstrap_stack_top = bootstrap_stack.top();
|
||||
let bootstrap_stack_top = kernel_stack.top();
|
||||
let handoff = KernelHandoff {
|
||||
allocator,
|
||||
address_space,
|
||||
direct_map,
|
||||
boot_info,
|
||||
kernel_stack_pool,
|
||||
handoff_frame,
|
||||
};
|
||||
|
||||
@@ -82,13 +89,21 @@ pub extern "C" fn _start() -> ! {
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
let (mut allocator, mut address_space, direct_map, boot_info, handoff_frame) = unsafe {
|
||||
let (
|
||||
mut allocator,
|
||||
mut address_space,
|
||||
direct_map,
|
||||
boot_info,
|
||||
mut kernel_stack_pool,
|
||||
handoff_frame,
|
||||
) = unsafe {
|
||||
let handoff = handoff.read();
|
||||
(
|
||||
handoff.allocator,
|
||||
handoff.address_space,
|
||||
handoff.direct_map,
|
||||
handoff.boot_info,
|
||||
handoff.kernel_stack_pool,
|
||||
handoff.handoff_frame,
|
||||
)
|
||||
};
|
||||
@@ -106,19 +121,31 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
|
||||
let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
|
||||
|
||||
println!("Parsing MADT...");
|
||||
|
||||
let madt = acpi
|
||||
.madt()
|
||||
.expect("failed to parse ACPI")
|
||||
.expect("MADT not found");
|
||||
|
||||
println!("Initializing interrupt controller...");
|
||||
|
||||
let interrupt_controller =
|
||||
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
|
||||
.expect("failed to initialize interrupt controller");
|
||||
|
||||
println!("Creating user address space...");
|
||||
|
||||
let task_kernel_stack = kernel_stack_pool
|
||||
.allocate(&mut address_space, &mut allocator)
|
||||
.expect("failed to allocate user task stack");
|
||||
|
||||
let mut user_addr_space = address_space
|
||||
.new_user(&mut allocator)
|
||||
.expect("failed to create user address space");
|
||||
|
||||
println!("Allocating user stack...");
|
||||
|
||||
let user_stack = UserStack::allocate(&mut user_addr_space, &mut allocator)
|
||||
.expect("failed to allocate user stack");
|
||||
|
||||
@@ -138,21 +165,37 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
.expect("failed to map user code page");
|
||||
|
||||
unsafe {
|
||||
user_addr_space.activate();
|
||||
|
||||
let user_code: [u8; 5] = [
|
||||
let user_code: &[u8] = &[
|
||||
0xCC, // INT3
|
||||
0xCD, 0x80, // INT 0x80
|
||||
0x0F, 0x05, // SYSCALL
|
||||
0xEB, 0xFE, // JMP -2 (loop forever if exit returns)
|
||||
];
|
||||
|
||||
let user_code_virtual = direct_map
|
||||
.translate(user_code_page.start_address())
|
||||
.unwrap();
|
||||
|
||||
println!("Copying user code to {:#X}", user_code_virtual.as_usize());
|
||||
|
||||
core::ptr::copy_nonoverlapping(
|
||||
user_code.as_ptr(),
|
||||
user_instruction_pointer.as_mut_ptr::<u8>(),
|
||||
user_code_virtual.as_mut_ptr::<u8>(),
|
||||
user_code.len(),
|
||||
);
|
||||
|
||||
arch::enter_user(user_instruction_pointer, user_stack.top());
|
||||
println!("Activating user address space...");
|
||||
|
||||
let user_tcb = Tcb::new_user(
|
||||
0,
|
||||
user_addr_space,
|
||||
task_kernel_stack,
|
||||
user_instruction_pointer,
|
||||
user_stack.top(),
|
||||
);
|
||||
|
||||
println!("Running first user task {user_tcb:?}...");
|
||||
|
||||
task::tcb::run_first_user(&user_tcb);
|
||||
}
|
||||
|
||||
hcf();
|
||||
|
||||
@@ -88,6 +88,7 @@ enum AddressSpaceKind {
|
||||
User,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct AddressSpace {
|
||||
root: PageTable,
|
||||
kind: AddressSpaceKind,
|
||||
|
||||
+3
-2
@@ -1,11 +1,12 @@
|
||||
mod address_space;
|
||||
mod frame;
|
||||
pub mod stack;
|
||||
mod stack;
|
||||
|
||||
#[allow(unused)]
|
||||
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
|
||||
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame};
|
||||
pub use stack::{KernelStack, StackCreateError, UserStack};
|
||||
#[allow(unused)]
|
||||
pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack};
|
||||
|
||||
pub struct KernelSegment {
|
||||
pub physical_base: PhysicalAddr,
|
||||
|
||||
+55
-7
@@ -3,10 +3,16 @@ use crate::memory::{
|
||||
VirtualAddr,
|
||||
};
|
||||
|
||||
const STACK_PAGES: usize = 16;
|
||||
const STACK_SIZE: usize = STACK_PAGES * FRAME_SIZE;
|
||||
const GUARD_PAGES: usize = 1; // 4KiB
|
||||
const KERNEL_STACK_PAGES: usize = 8; // 32KiB
|
||||
const USER_STACK_PAGES: usize = 16; // 64KiB
|
||||
const KERNEL_STACK_SIZE: usize = KERNEL_STACK_PAGES * FRAME_SIZE;
|
||||
const USER_STACK_SIZE: usize = USER_STACK_PAGES * FRAME_SIZE;
|
||||
|
||||
const KERNEL_SLOT_SIZE: usize = (KERNEL_STACK_PAGES + GUARD_PAGES) * FRAME_SIZE;
|
||||
const MAX_KERNEL_STACKS: usize = 64;
|
||||
const KERNEL_STACK_BASE: usize = 0xFFFF_FFFE_0000_0000;
|
||||
|
||||
const KERNEL_STACK_TOP: VirtualAddr = VirtualAddr::new(0xFFFF_FFFE_0000_0000);
|
||||
const USER_STACK_TOP: VirtualAddr = VirtualAddr::new(0x0000_7FFF_FFFF_F000);
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -14,13 +20,16 @@ pub enum StackCreateError {
|
||||
AddressOverflow,
|
||||
UnalignedStackTop,
|
||||
OutOfFrames,
|
||||
OutOfStacks,
|
||||
GuardPageMapped,
|
||||
Map(MapError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StackMapping {
|
||||
guard_page: VirtualAddr,
|
||||
mapped_start: VirtualAddr,
|
||||
stack_size: usize,
|
||||
top: VirtualAddr,
|
||||
}
|
||||
|
||||
@@ -28,6 +37,7 @@ impl StackMapping {
|
||||
fn allocate(
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
stack_size: usize,
|
||||
top: VirtualAddr,
|
||||
permissions: PagePermissions,
|
||||
) -> Result<Self, StackCreateError> {
|
||||
@@ -37,7 +47,7 @@ impl StackMapping {
|
||||
|
||||
let mapped_start = VirtualAddr::new(
|
||||
top.as_usize()
|
||||
.checked_sub(STACK_SIZE)
|
||||
.checked_sub(stack_size)
|
||||
.ok_or(StackCreateError::AddressOverflow)?,
|
||||
);
|
||||
let guard_page = VirtualAddr::new(
|
||||
@@ -53,7 +63,7 @@ impl StackMapping {
|
||||
|
||||
let mut mapped_pages = 0;
|
||||
|
||||
while mapped_pages < STACK_PAGES {
|
||||
while mapped_pages < stack_size / FRAME_SIZE {
|
||||
let virtual_address = VirtualAddr::new(
|
||||
mapped_start
|
||||
.as_usize()
|
||||
@@ -89,6 +99,7 @@ impl StackMapping {
|
||||
Ok(Self {
|
||||
guard_page,
|
||||
mapped_start,
|
||||
stack_size,
|
||||
top,
|
||||
})
|
||||
}
|
||||
@@ -115,7 +126,7 @@ impl StackMapping {
|
||||
/// The caller must ensure this stack is not active on any CPU and cannot be accessed by any
|
||||
/// kernel operation while it is being destroyed.
|
||||
unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
|
||||
for page in (0..STACK_PAGES).rev() {
|
||||
for page in (0..self.stack_size / FRAME_SIZE).rev() {
|
||||
let virtual_address =
|
||||
VirtualAddr::new(self.mapped_start.as_usize() + page * FRAME_SIZE);
|
||||
let frame = unsafe {
|
||||
@@ -130,6 +141,7 @@ impl StackMapping {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KernelStack {
|
||||
mapping: StackMapping,
|
||||
}
|
||||
@@ -137,12 +149,14 @@ pub struct KernelStack {
|
||||
impl KernelStack {
|
||||
pub fn allocate(
|
||||
address_space: &mut AddressSpace,
|
||||
top: usize,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<Self, StackCreateError> {
|
||||
let mapping = StackMapping::allocate(
|
||||
address_space,
|
||||
allocator,
|
||||
KERNEL_STACK_TOP,
|
||||
KERNEL_STACK_SIZE,
|
||||
VirtualAddr::new(top),
|
||||
PagePermissions::new(true, false, false),
|
||||
)?;
|
||||
|
||||
@@ -175,6 +189,7 @@ impl UserStack {
|
||||
let mapping = StackMapping::allocate(
|
||||
address_space,
|
||||
allocator,
|
||||
USER_STACK_SIZE,
|
||||
top,
|
||||
PagePermissions::new(true, false, true),
|
||||
)?;
|
||||
@@ -194,3 +209,36 @@ impl UserStack {
|
||||
unsafe { self.mapping.destroy(address_space, allocator) };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KernelStackPool {
|
||||
free_slots: u64, // bitmap
|
||||
}
|
||||
|
||||
impl KernelStackPool {
|
||||
pub fn new() -> Self {
|
||||
Self { free_slots: 0 }
|
||||
}
|
||||
|
||||
pub fn allocate(
|
||||
&mut self,
|
||||
address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
) -> Result<KernelStack, StackCreateError> {
|
||||
let mut slot = 0;
|
||||
while slot < MAX_KERNEL_STACKS {
|
||||
if self.free_slots & (1 << slot) == 0 {
|
||||
self.free_slots |= 1 << slot;
|
||||
let top = KERNEL_STACK_BASE + ((slot + 1) * KERNEL_SLOT_SIZE);
|
||||
return KernelStack::allocate(address_space, top, allocator);
|
||||
}
|
||||
slot += 1;
|
||||
}
|
||||
|
||||
Err(StackCreateError::OutOfStacks)
|
||||
}
|
||||
|
||||
pub fn free(&mut self, stack: &KernelStack) {
|
||||
let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1;
|
||||
self.free_slots &= !(1 << slot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod syscall;
|
||||
pub mod tcb;
|
||||
@@ -0,0 +1,18 @@
|
||||
use crate::{hcf, println};
|
||||
|
||||
pub fn handle(
|
||||
syscall_num: u64,
|
||||
arg0: u64,
|
||||
arg1: u64,
|
||||
arg2: u64,
|
||||
arg3: u64,
|
||||
arg4: u64,
|
||||
arg5: u64,
|
||||
) -> u64 {
|
||||
println!(
|
||||
"Syscall nr={:#X} args=({:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X})",
|
||||
syscall_num, arg0, arg1, arg2, arg3, arg4, arg5
|
||||
);
|
||||
hcf();
|
||||
0
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use crate::{
|
||||
arch::ThreadContext,
|
||||
memory::{AddressSpace, KernelStack, VirtualAddr},
|
||||
println,
|
||||
};
|
||||
|
||||
// Thread Control Block
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ThreadState {
|
||||
Ready,
|
||||
Running,
|
||||
Blocked,
|
||||
Dead,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tcb {
|
||||
pub id: usize,
|
||||
pub state: ThreadState,
|
||||
pub kernel_stack: KernelStack,
|
||||
pub context: ThreadContext,
|
||||
pub address_space: AddressSpace,
|
||||
}
|
||||
|
||||
impl Tcb {
|
||||
pub fn new_user(
|
||||
id: usize,
|
||||
address_space: AddressSpace,
|
||||
kernel_stack: KernelStack,
|
||||
user_entry: VirtualAddr,
|
||||
user_stack: VirtualAddr,
|
||||
) -> Self {
|
||||
let context = ThreadContext::new(user_entry, user_stack, kernel_stack.top());
|
||||
|
||||
Self {
|
||||
id,
|
||||
state: ThreadState::Ready,
|
||||
kernel_stack,
|
||||
context,
|
||||
address_space,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn run_first_user(user_tcb: &Tcb) {
|
||||
let previous_interrupts = crate::arch::disable_interrupts_and_save();
|
||||
|
||||
let mut boot_thread_ctx = ThreadContext::empty();
|
||||
unsafe {
|
||||
user_tcb.address_space.activate();
|
||||
crate::arch::set_kernel_stack(user_tcb.kernel_stack.top());
|
||||
crate::arch::switch_context(&mut boot_thread_ctx, &user_tcb.context);
|
||||
}
|
||||
|
||||
crate::arch::restore_interrupts(previous_interrupts);
|
||||
}
|
||||
|
||||
pub unsafe fn switch(prev: &mut Tcb, next: &Tcb) {
|
||||
let previous_interrupts = crate::arch::disable_interrupts_and_save();
|
||||
|
||||
if prev.address_space != next.address_space {
|
||||
unsafe { next.address_space.activate() };
|
||||
}
|
||||
|
||||
crate::arch::set_kernel_stack(next.kernel_stack.top());
|
||||
|
||||
unsafe {
|
||||
crate::arch::switch_context(&mut prev.context, &next.context);
|
||||
}
|
||||
|
||||
crate::arch::restore_interrupts(previous_interrupts);
|
||||
}
|
||||
Reference in New Issue
Block a user