Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sets kernel GS #957

Merged
merged 2 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {}
}
}