Skip to content

Commit

Permalink
Sets kernel GS (#957)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Sep 1, 2024
1 parent 8bd35ba commit b5448e6
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/obkrnl/src/context/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use super::Context;

pub unsafe fn activate(_: *mut Context) {
todo!();
}
46 changes: 46 additions & 0 deletions src/obkrnl/src/context/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::proc::Thread;
use alloc::sync::Arc;

#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
mod arch;

/// Implementation of `pcpu` structure.
///
/// Access to this structure must be done by **atomic reading or writing its field directly**. It is
/// not safe to have a temporary a pointer or reference to this struct or its field because the CPU
/// might get interupted, which mean it is possible for the next instruction to get executed on
/// a different CPU if the interupt cause the CPU to switch the task.
///
/// We don't support `pc_cpuid` field here because it value is 100% unpredictable due to the above
/// reason. Once we have loaded `pc_cpuid` the next instruction might get executed on a different
/// CPU, which render the loaded value incorrect. The only way to prevent this issue is to disable
/// interupt before reading `pc_cpuid`, which can make the CPU missed some events from the other
/// hardwares.
pub struct Context {
thread: *const Thread, // pc_curthread
}

impl Context {
/// See `pcpu_init` on the PS4 for a reference.
pub fn new(td: Arc<Thread>) -> Self {
Self {
thread: Arc::into_raw(td),
}
}

/// # Safety
/// The only place this method is safe to call is in the CPU entry point. Once this method
/// return this instance must outlive the CPU lifetime and it must never be accessed via this
/// variable again. The simple way to achieve this is keep the activated [`Context`] as a local
/// variable then move all code after it to a dedicated no-return function.
pub unsafe fn activate(&mut self) {
self::arch::activate(self);
}
}

impl Drop for Context {
fn drop(&mut self) {
unsafe { drop(Arc::from_raw(self.thread)) };
}
}
35 changes: 35 additions & 0 deletions src/obkrnl/src/context/x86_64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::Context;
use core::arch::asm;

/// Set kernel `GS` segment register to `cx`.
///
/// This also set user-mode `FS` and `GS` to null.
pub unsafe fn activate(cx: *mut Context) {
// Set GS for kernel mode.
let cx = cx as usize;

asm!(
"wrmsr",
in("ecx") 0xc0000101u32,
in("edx") cx >> 32,
in("eax") cx,
options(preserves_flags, nostack)
);

// Clear FS and GS for user mode.
asm!(
"wrmsr",
in("ecx") 0xc0000100u32,
in("edx") 0,
in("eax") 0,
options(preserves_flags, nostack)
);

asm!(
"wrmsr",
in("ecx") 0xc0000102u32,
in("edx") 0,
in("eax") 0,
options(preserves_flags, nostack)
);
}
30 changes: 27 additions & 3 deletions src/obkrnl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,36 @@
#![cfg_attr(not(test), no_main)]

use crate::config::set_boot_env;
use crate::context::Context;
use crate::malloc::KernelHeap;
use crate::proc::Thread;
use alloc::string::String;
use alloc::sync::Arc;
use core::arch::asm;
use core::mem::zeroed;
use core::panic::PanicInfo;
use obconf::BootEnv;

mod config;
mod console;
mod context;
mod imgfmt;
mod malloc;
mod panic;
mod proc;

extern crate alloc;

/// Entry point of the kernel.
///
/// This will be called by a bootloader or a hypervisor. The following are requirements before
/// transfer a control to this function:
/// This will be called by a bootloader or a hypervisor. The following are requirements to call this
/// function:
///
/// 1. The kernel does not remap itself so it must be mapped at a desired virtual address and all
/// relocations must be applied.
/// relocations must be applied. This imply that the kernel can only be run in a virtual address
/// space.
/// 2. Interrupt is disabled.
/// 3. Only main CPU can execute this function.
///
/// See PS4 kernel entry point for a reference.
#[allow(dead_code)]
Expand All @@ -34,6 +42,22 @@ extern "C" fn _start(env: &'static BootEnv) -> ! {

info!("Starting Obliteration Kernel.");

// Setup thread0 to represent this thread.
let thread0 = unsafe { Thread::new_bare() };

// Setup CPU context. We use a different mechanism here. The PS4 put all of pcpu at a global
// level but we put it on each CPU stack instead.
let thread0 = Arc::new(thread0);
let mut cx = Context::new(thread0);

// SAFETY: We are in the main CPU entry point and we move all the remaining code after this into
// a dedicated no-return function.
unsafe { cx.activate() };

main();
}

fn main() -> ! {
loop {
#[cfg(target_arch = "x86_64")]
unsafe {
Expand Down
3 changes: 3 additions & 0 deletions src/obkrnl/src/proc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub use self::thread::*;

mod thread;
12 changes: 12 additions & 0 deletions src/obkrnl/src/proc/thread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// Implementation of `thread` structure.
pub struct Thread {}

impl Thread {
/// # Safety
/// This function does not do anything except initialize the struct memory. It is the caller
/// responsibility to configure the thread after this so it have a proper states and trigger
/// necessary events.
pub unsafe fn new_bare() -> Self {
Self {}
}
}

0 comments on commit b5448e6

Please sign in to comment.