mirror of
https://github.com/obhq/obliteration.git
synced 2024-10-07 00:13:24 +00:00
Initializes uma_zalloc_arg implementation (#982)
This commit is contained in:
parent
3fabe8d75f
commit
aa362f7b42
@ -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 }
|
||||
|
@ -12,6 +12,10 @@ 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)*) => {
|
||||
@ -19,6 +23,9 @@ macro_rules! info {
|
||||
};
|
||||
}
|
||||
|
||||
/// # 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(
|
||||
@ -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(
|
||||
@ -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),
|
||||
@ -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:#}",
|
||||
|
@ -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);
|
||||
@ -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(())
|
||||
|
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
@ -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) -> ! {
|
||||
|
@ -66,6 +66,8 @@ impl Stage2 {
|
||||
Self { zones, stats }
|
||||
}
|
||||
|
||||
/// Returns null on failure.
|
||||
///
|
||||
/// See `malloc` on the PS4 for a reference.
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -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")]
|
||||
|
@ -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
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
10
src/obkrnl/src/uma/bucket.rs
Normal file
10
src/obkrnl/src/uma/bucket.rs
Normal file
@ -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
src/obkrnl/src/uma/cache.rs
Normal file
13
src/obkrnl/src/uma/cache.rs
Normal file
@ -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()
|
||||
}
|
||||
}
|
@ -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 {
|
||||
@ -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!()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user