Skip to content

Commit

Permalink
Initializes uma_zalloc_arg implementation (#982)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Sep 14, 2024
1 parent 3fabe8d commit aa362f7
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/obkrnl/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ pub use self::arch::*;
#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
mod arch;

/// # Interupt safety
/// This function is interupt safe.
pub fn boot_env() -> &'static BootEnv {
// SAFETY: This is safe because the setup() requirements.
unsafe { &*BOOT_ENV }
}

/// # Interupt safety
/// This function is interupt safe.
pub fn config() -> &'static Config {
// SAFETY: This is safe because the setup() requirements.
unsafe { &*CONFIG }
Expand Down
14 changes: 14 additions & 0 deletions src/obkrnl/src/console/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,20 @@ mod vm;
/// performance critical path.
///
/// The LF character will be automatically appended.
///
/// # Interupt safety
/// This macro is interupt safe as long as [`Display`] implementation on all arguments are interupt
/// safe (e.g. no heap allocation).
#[macro_export]
macro_rules! info {
($($args:tt)*) => {
$crate::console::info(file!(), line!(), format_args!($($args)*))
};
}

/// # Interupt safety
/// This function is interupt safe as long as [`Display`] implementation on `msg` are interupt safe
/// (e.g. no heap allocation).
#[inline(never)]
pub fn info(file: &str, line: u32, msg: impl Display) {
print(
Expand All @@ -33,6 +40,9 @@ pub fn info(file: &str, line: u32, msg: impl Display) {
);
}

/// # Interupt safety
/// This function is interupt safe as long as [`Display`] implementation on `msg` are interupt safe
/// (e.g. no heap allocation).
#[inline(never)]
pub fn error(file: &str, line: u32, msg: impl Display) {
print(
Expand All @@ -47,6 +57,9 @@ pub fn error(file: &str, line: u32, msg: impl Display) {
)
}

/// # Interupt safety
/// This function is interupt safe as long as [`Display`] implementation on `msg` are interupt safe
/// (e.g. no heap allocation).
fn print(vty: MsgType, msg: impl Display) {
match boot_env() {
BootEnv::Vm(env) => self::vm::print(env, vty, msg),
Expand All @@ -64,6 +77,7 @@ struct Log<'a, M: Display> {

impl<'a, M: Display> Display for Log<'a, M> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
// This implementation must be interupt safe.
writeln!(
f,
"{}++++++++++++++++++ {} {}:{}{0:#}",
Expand Down
4 changes: 4 additions & 0 deletions src/obkrnl/src/console/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use core::ptr::{addr_of_mut, write_volatile};
use obconf::Vm;
use obvirt::console::{Memory, MsgType};

/// # Interupt safety
/// This function is interupt safe as long as [`Display`] implementation on `msg` are interupt safe
/// (e.g. no heap allocation).
pub fn print(env: &Vm, ty: MsgType, msg: impl Display) {
let c = env.console as *mut Memory;
let mut w = Writer(c);
Expand All @@ -16,6 +19,7 @@ struct Writer(*mut Memory);

impl Write for Writer {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
// This implementation must be interupt safe.
unsafe { write_volatile(addr_of_mut!((*self.0).msg_len), s.len()) };
unsafe { write_volatile(addr_of_mut!((*self.0).msg_addr), s.as_ptr() as usize) };
Ok(())
Expand Down
22 changes: 15 additions & 7 deletions src/obkrnl/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ impl Context {
}
}

/// # Interupt safety
/// This function is interupt safe.
#[inline(never)]
pub fn thread() -> Arc<Thread> {
// It does not matter if we are on a different CPU after we load the Context::thread because
// it is going to be the same one since it represent the current thread.
Expand All @@ -38,15 +41,19 @@ impl Context {
unsafe { Arc::from_raw(td) }
}

/// Pin the calling thread to one CPU.
///
/// This thread will never switch to a different CPU until the returned [`PinnedContext`] is
/// dropped (but it is allowed to sleep).
///
/// See `critical_enter` and `critical_exit` on the PS4 for a reference.
#[inline(never)]
pub fn pin() -> PinnedContext {
// TODO: Verify if memory ordering here is correct. We need a call to self::arch::current()
// to execute after the thread is in a critical section. The CPU must not reorder this. Our
// current implementation follow how Drop on Arc is implemented.
// Relax ordering should be enough here since this increment will be checked by the same CPU
// when an interupt happens.
let td = unsafe { self::arch::thread() };

unsafe { (*td).critical_sections().fetch_add(1, Ordering::Release) };
core::sync::atomic::fence(Ordering::Acquire);
unsafe { (*td).critical_sections().fetch_add(1, Ordering::Relaxed) };

// Once the thread is in a critical section it will never be switch a CPU so it is safe to
// keep a pointer to a context here.
Expand Down Expand Up @@ -83,10 +90,11 @@ impl PinnedContext {

impl Drop for PinnedContext {
fn drop(&mut self) {
// TODO: Verify if memory ordering here is correct.
// Relax ordering should be enough here since this decrement will be checked by the same CPU
// when an interupt happens.
let td = unsafe { (*self.0).thread.load(Ordering::Relaxed) };

unsafe { (*td).critical_sections().fetch_sub(1, Ordering::Release) };
unsafe { (*td).critical_sections().fetch_sub(1, Ordering::Relaxed) };

// TODO: Implement td_owepreempt.
}
Expand Down
5 changes: 3 additions & 2 deletions src/obkrnl/src/context/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ pub unsafe fn thread() -> *const Thread {
}

pub unsafe fn current() -> *const Context {
// Load current GS.
// Load current GS. Although the "rdmsr" does not read or write to any memory but it need to
// synchronize with a critical section.
let mut edx: u32;
let mut eax: u32;

Expand All @@ -61,7 +62,7 @@ pub unsafe fn current() -> *const Context {
in("ecx") 0xc0000101u32,
out("edx") edx,
out("eax") eax,
options(pure, nomem, preserves_flags, nostack)
options(preserves_flags, nostack)
);

// Combine EDX and EAX.
Expand Down
2 changes: 2 additions & 0 deletions src/obkrnl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ fn main() -> ! {
}
}

/// # Interupt safety
/// This function is interupt safe.
#[allow(dead_code)]
#[cfg_attr(target_os = "none", panic_handler)]
fn panic(i: &PanicInfo) -> ! {
Expand Down
2 changes: 2 additions & 0 deletions src/obkrnl/src/malloc/stage2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ impl Stage2 {
Self { zones, stats }
}

/// Returns null on failure.
///
/// See `malloc` on the PS4 for a reference.
///
/// # Safety
Expand Down
3 changes: 3 additions & 0 deletions src/obkrnl/src/panic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use core::arch::asm;

/// Perform panic after printing the panic message.
///
/// # Interupt safety
/// This function is interupt safe.
pub fn panic() -> ! {
loop {
#[cfg(target_arch = "aarch64")]
Expand Down
10 changes: 9 additions & 1 deletion src/obkrnl/src/proc/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use core::sync::atomic::AtomicU32;
///
/// All thread **must** run to completion once execution has been started otherwise resource will be
/// leak if the thread is dropped while its execution currently in the kernel space.
///
/// We subtitute `TDP_NOSLEEPING` with `td_intr_nesting_level` since the only cases the thread
/// should not allow to sleep is when it being handle an interupt.
pub struct Thread {
critical_sections: AtomicU32, // td_critnest
active_interrupts: usize, // td_intr_nesting_level
Expand All @@ -23,7 +26,12 @@ impl Thread {
}
}

pub fn critical_sections(&self) -> &AtomicU32 {
/// See [`crate::context::Context::pin()`] for a safe wrapper.
///
/// # Safety
/// This is a counter. Each increment must paired with a decrement. Failure to do so will cause
/// the whole system to be in an undefined behavior.
pub unsafe fn critical_sections(&self) -> &AtomicU32 {
&self.critical_sections
}

Expand Down
10 changes: 10 additions & 0 deletions src/obkrnl/src/uma/bucket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// Implementation of `uma_bucket` structure.
pub struct UmaBucket {
len: usize, // ub_cnt
}

impl UmaBucket {
pub fn len(&self) -> usize {
self.len
}
}
13 changes: 13 additions & 0 deletions src/obkrnl/src/uma/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::bucket::UmaBucket;

/// Implementation of `uma_cache` structure.
#[derive(Default)]
pub struct UmaCache {
alloc: Option<UmaBucket>, // uc_allocbucket
}

impl UmaCache {
pub fn alloc(&self) -> Option<&UmaBucket> {
self.alloc.as_ref()
}
}
45 changes: 42 additions & 3 deletions src/obkrnl/src/uma/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
use self::cache::UmaCache;
use crate::config::config;
use crate::context::Context;
use alloc::borrow::Cow;
use alloc::vec::Vec;

mod bucket;
mod cache;

/// Implementation of `uma_zone` structure.
pub struct UmaZone {
size: usize, // uz_size
size: usize, // uz_size
caches: Vec<UmaCache>, // uz_cpu
}

impl UmaZone {
/// See `uma_zcreate` on the PS4 for a reference.
pub fn new(_: Cow<'static, str>, size: usize, _: usize) -> Self {
// TODO: Check if size is allowed to be zero. If not, change it to NonZero<usize>.
Self { size }
// Ths PS4 allocate a new uma_zone from masterzone_z but we don't have that. This method
// basically an implementation of zone_ctor.
let len = config().max_cpu.get();
let mut caches = Vec::with_capacity(len);

for _ in 0..len {
caches.push(UmaCache::default());
}

Self {
size, // TODO: Check if size is allowed to be zero. If not, change it to NonZero<usize>.
caches,
}
}

pub fn size(&self) -> usize {
Expand All @@ -18,6 +37,26 @@ impl UmaZone {

/// See `uma_zalloc_arg` on the PS4 for a reference.
pub fn alloc(&self) -> *mut u8 {
// Our implementation imply M_WAITOK and M_ZERO.
let td = Context::thread();

if td.active_interrupts() != 0 {
panic!("heap allocation in an interrupt handler is not supported");
}

// Try to allocate from per-CPU cache.
let cx = Context::pin();
let cache = &self.caches[cx.cpu()];
let bucket = cache.alloc();

while let Some(bucket) = bucket {
if bucket.len() != 0 {
todo!()
}

todo!()
}

todo!()
}
}

0 comments on commit aa362f7

Please sign in to comment.