Skip to content

Commit

Permalink
kernel: Use 'swapgs' to access rsp0 entry in tss
Browse files Browse the repository at this point in the history
  • Loading branch information
fruhland committed Jun 11, 2024
1 parent a3dfab3 commit 19f9812
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 46 deletions.
20 changes: 8 additions & 12 deletions os/kernel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ use x86_64::structures::idt::InterruptDescriptorTable;
use x86_64::structures::paging::frame::PhysFrameRange;
use x86_64::structures::paging::PhysFrame;
use x86_64::structures::tss::TaskStateSegment;
use x86_64::{PhysAddr, VirtAddr};
use x86_64::PhysAddr;
use crate::device::pci::PciBus;
use crate::memory::PAGE_SIZE;
use crate::process::process::ProcessManager;
use crate::syscall::syscall_dispatcher::CoreLocalStorage;

extern crate alloc;

Expand Down Expand Up @@ -103,6 +104,7 @@ impl EfiSystemTable {
static GDT: Mutex<GlobalDescriptorTable> = Mutex::new(GlobalDescriptorTable::new());
static TSS: Mutex<TaskStateSegment> = Mutex::new(TaskStateSegment::new());
static IDT: Mutex<InterruptDescriptorTable> = Mutex::new(InterruptDescriptorTable::new());
static CORE_LOCAL_STORAGE: Mutex<CoreLocalStorage> = Mutex::new(CoreLocalStorage::new());
static EFI_SYSTEM_TABLE: Once<EfiSystemTable> = Once::new();
static ACPI_TABLES: Once<Mutex<AcpiTables<AcpiHandler>>> = Once::new();
static INIT_RAMDISK: Once<TarArchiveRef> = Once::new();
Expand Down Expand Up @@ -223,6 +225,10 @@ pub fn idt() -> &'static Mutex<InterruptDescriptorTable> {
&IDT
}

pub fn core_local_storage() -> &'static Mutex<CoreLocalStorage> {
&CORE_LOCAL_STORAGE
}

pub fn acpi_tables() -> &'static Mutex<AcpiTables<AcpiHandler>> {
ACPI_TABLES.get().expect("Trying to access ACPI tables before initialization!")
}
Expand Down Expand Up @@ -286,14 +292,4 @@ pub fn ps2_devices() -> &'static PS2 {

pub fn pci_bus() -> &'static PciBus {
PCI.get().expect("Trying to access PCI bus before initialization!")
}

#[no_mangle]
pub extern "C" fn tss_set_rsp0(rsp0: u64) {
tss().lock().privilege_stack_table[0] = VirtAddr::new(rsp0);
}

#[no_mangle]
pub extern "C" fn tss_get_rsp0() -> u64 {
tss().lock().privilege_stack_table[0].as_u64()
}
}
16 changes: 6 additions & 10 deletions os/kernel/src/process/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{memory, process_manager, scheduler, tss};
use crate::memory::alloc::StackAllocator;
use crate::memory::r#virtual::{VirtualMemoryArea, VmaType};
use crate::process::process::Process;
use crate::syscall::syscall_dispatcher::CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX;

pub const MAIN_USER_STACK_START: usize = 0x400000000000;
pub const MAX_USER_STACK_SIZE: usize = 0x40000000;
Expand Down Expand Up @@ -344,17 +345,11 @@ unsafe extern "C" fn thread_switch(current_rsp0: *mut u64, next_rsp0: u64, next_
// Save stack pointer in 'current_rsp0' (first parameter)
"mov [rdi], rsp",

// Store rsi and rcx in r12 and r13, as they might be overwritten by the following function call
"mov r12, rsi",
"mov r13, rcx",

// Set rsp0 of kernel stack in tss (third parameter 'next_rsp0_end')
"mov rdi, rdx",
"call tss_set_rsp0",

// Restore rsi and rcx
"mov rcx, r13",
"mov rsi, r12",
"swapgs", // Setup core local storage access via gs base
"mov rax,gs:[{CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX}]", // Load pointer to rsp0 entry of tss into rax
"mov [rax],rdx", // Set rsp0 entry in tss to 'next_rsp0_end' (third parameter)
"swapgs", // Restore gs base

// Switch address space (fourth parameter 'next_cr3')
"mov cr3, rcx",
Expand All @@ -380,6 +375,7 @@ unsafe extern "C" fn thread_switch(current_rsp0: *mut u64, next_rsp0: u64, next_

"call unlock_scheduler",
"ret", // Return to next thread
CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX = const CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX,
options(noreturn)
)
}
68 changes: 44 additions & 24 deletions os/kernel/src/syscall/syscall_dispatcher.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
use core::arch::asm;
use core::mem::size_of;
use core::ops::Deref;
use core::ptr;
use x86_64::registers::control::{Efer, EferFlags};
use x86_64::registers::model_specific::{LStar, Star};
use x86_64::registers::model_specific::{KernelGsBase, LStar, Star};
use x86_64::structures::gdt::SegmentSelector;
use x86_64::{PrivilegeLevel, VirtAddr};
use syscall::NUM_SYSCALLS;
use crate::{core_local_storage, tss};
use crate::syscall::{sys_write, sys_thread_exit, sys_thread_sleep, sys_thread_switch, sys_process_id, sys_thread_id, sys_read, sys_map_user_heap, sys_thread_join, sys_process_execute_binary, sys_get_system_time, sys_get_date, sys_set_date, sys_thread_create, sys_process_exit};

pub const CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX: u64 = 0x00;
pub const CORE_LOCAL_STORAGE_USER_RSP_INDEX: u64 = 0x08;


#[repr(C, packed)]
pub struct CoreLocalStorage {
tss_rsp0_ptr: VirtAddr,
user_rsp: VirtAddr,
}

impl CoreLocalStorage {
pub const fn new() -> Self {
Self { tss_rsp0_ptr: VirtAddr::zero(), user_rsp: VirtAddr::zero() }
}
}

pub fn init() {
// Enable system call extensions
Expand All @@ -26,6 +45,11 @@ pub fn init() {

// Set rip for syscall
LStar::write(VirtAddr::new(syscall_handler as u64));

// Initialize core local storage (accessible via 'swapgs')
let mut core_local_storage = core_local_storage().lock();
core_local_storage.tss_rsp0_ptr = VirtAddr::new(ptr::from_ref(tss().lock().deref()) as u64 + size_of::<u32>() as u64);
KernelGsBase::write(VirtAddr::new(ptr::from_ref(core_local_storage.deref()) as u64));
}

#[no_mangle]
Expand Down Expand Up @@ -75,7 +99,15 @@ unsafe extern "C" fn syscall_handler() {
// Disable interrupts until we have switched to kernel stack
"cli",

// Save registers (except rax, which is used for system call ID and return value)
// Switch to kernel stack
"swapgs", // Setup core local storage access via gs base
"mov gs:[{CORE_LOCAL_STORAGE_USER_RSP_INDEX}], rsp", // Temporarily store user rip in core local storage
"mov rsp, gs:[{CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX}]", // Load pointer to rsp0 entry of tss from core local storage
"mov rsp, [rsp]", // Dereference rsp0 pointer to switch to kernel stack
"push gs:[{CORE_LOCAL_STORAGE_USER_RSP_INDEX}]", // Store user rip on kernel stack (core local storage might be overwritten, when a thread switch occurs during system call execution)
"swapgs", // Restore gs base

// Store registers (except rax, which is used for system call ID and return value)
"push rbx",
"push rcx", // Contains rip for returning to ring 3
"push rdx",
Expand All @@ -90,34 +122,16 @@ unsafe extern "C" fn syscall_handler() {
"push r14",
"push r15",

// Switch to kernel stack and enable interrupts
"mov r15, rax", // Save system call ID in r15
"mov r14, rdi", // Save first parameter in r14
"mov r13, rsi", // Save second parameter in r13
"mov r12, rdx", // Save third parameter in r12
"call tss_get_rsp0", // Get kernel rsp (returned in rax)
"mov rbx, rax", // Save kernel rsp in rbx
"mov rcx, rsp", // Save user rsp in rcx
"mov rdx, r12", // Restore third parameter
"mov rsi, r13", // Restore second parameter
"mov rdi, r14", // Restore first parameter
"mov rax, r15", // Restore system call ID
"mov rsp, rbx", // Switch to kernel stack
"push rcx", // Save user rsp on stack
// Enable interrupts (we are now on the kernel stack and can handle them properly)
"sti",

// Check if system call ID is in bounds
"cmp rax, {}",
"cmp rax, {NUM_SYSCALLS}",
"jge syscall_abort", // Panics and does not return

// Call system call handler, corresponding to ID (in rax)
"call syscall_disp",

// Switch to user stack (user rsp is last value on stack)
// Disable interrupts, since we are still in Ring 0 and no interrupt handler should be called with the user stack
"cli",
"pop rsp",

// Restore registers
"pop r15",
"pop r14",
Expand All @@ -133,10 +147,16 @@ unsafe extern "C" fn syscall_handler() {
"pop rcx", // Contains rip for returning to ring 3
"pop rbx",

// Switch back to user stack
"cli", // Disable interrupts, since we are still in Ring 0 and no interrupt handler should be called with the user stack
"pop rsp", // Restore rsp from kernel stack,

// Return to Ring 3
// Interrupts will be enabled automatically, because eflags gets restored from r11
// Interrupts will be enabled automatically, because eflags is restored from r11
"sysretq",
const NUM_SYSCALLS,
NUM_SYSCALLS = const NUM_SYSCALLS,
CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX = const CORE_LOCAL_STORAGE_TSS_RSP0_PTR_INDEX,
CORE_LOCAL_STORAGE_USER_RSP_INDEX = const CORE_LOCAL_STORAGE_USER_RSP_INDEX,
options(noreturn)
);
}
Expand Down

0 comments on commit 19f9812

Please sign in to comment.