feat: tasks

This commit is contained in:
Zoe
2026-08-31 04:46:13 -05:00
parent b1f9dc0f8a
commit 4c32bcfb35
16 changed files with 513 additions and 43 deletions
+4 -1
View File
@@ -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,
};
-1
View File
@@ -13,7 +13,6 @@ use crate::{
memory::{
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
},
println,
};
const APIC_ID: u32 = 0x20;
+109 -1
View File
@@ -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;
+8 -6
View File
@@ -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,
],
+27
View File
@@ -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 {
-1
View File
@@ -3,7 +3,6 @@ use crate::{
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
},
platform::acpi::{InterruptPolarity, TriggerMode},
println,
};
const IOWIN: usize = 0x10;
+41 -14
View File
@@ -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,
+9
View File
@@ -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,
+111
View File
@@ -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;
}