diff --git a/Cargo.lock b/Cargo.lock index 30134a62..c94d60ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,6 +432,10 @@ dependencies = [ "syn", ] +[[package]] +name = "bitflag" +version = "0.1.0" + [[package]] name = "bitflags" version = "1.3.2" @@ -2925,6 +2929,7 @@ name = "obkrnl" version = "0.1.0" dependencies = [ "bitfield-struct", + "bitflag", "config", "hashbrown", "krt", diff --git a/Cargo.toml b/Cargo.toml index 0a29845e..9f8fd179 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "config", "gui", "kernel", + "lib/bitflag", "lib/krt", "macros", ] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 2312eb84..b21e59d6 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] bitfield-struct = "0.9.2" +bitflag = { path = "../lib/bitflag" } config = { path = "../config" } hashbrown = "0.14.5" krt = { path = "../lib/krt" } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 4b760aa8..36d99088 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -91,11 +91,16 @@ fn init_vm(procs: &Arc) -> Arc { Uma::new(vm) } -/// See `create_init` function on the PS4 for a reference. +/// See `create_init` function on the Orbis for a reference. +/// +/// # Reference offsets +/// | Version | Offset | +/// |---------|--------| +/// |PS4 11.00|0x2BEF30| fn create_init() { let pmgr = current_procmgr().unwrap(); let abi = Arc::new(Ps4Abi); - let flags = Fork::new().with_copy_fd(true).with_create_process(true); + let flags = Fork::CopyFd | Fork::CreateProcess; pmgr.fork(abi, flags).unwrap(); diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs index b4206e73..c3276497 100644 --- a/kernel/src/proc/mod.rs +++ b/kernel/src/proc/mod.rs @@ -8,10 +8,10 @@ use crate::lock::{MappedMutex, Mutex, MutexGuard}; use crate::signal::Signal; use crate::subsystem::Subsystem; use alloc::sync::{Arc, Weak}; -use bitfield_struct::bitfield; use core::error::Error; use core::fmt::{Display, Formatter}; use hashbrown::HashMap; +use macros::bitflag; mod abi; mod cell; @@ -48,19 +48,24 @@ impl ProcMgr { MutexGuard::map(self.procs.lock(), |procs| procs.values()) } - /// We imply `RFSTOPPED` to make [`ProcMgr`] not depend on a scheduler. + /// We imply `RFSTOPPED` to make [`ProcMgr`] not depend on the scheduler. /// - /// See `fork1` on the PS4 for a reference. + /// See `fork1` on the Orbis for a reference. + /// + /// # Reference offsets + /// | Version | Offset | + /// |---------|--------| + /// |PS4 11.00|0x14B830| pub fn fork(&self, abi: Arc, flags: Fork) -> Result, ForkError> { // TODO: Refactor this for readability. - if (flags.into_bits() & 0x60008f8b) != 0 - || flags.copy_fd() && flags.clear_fd() - || flags.parent_signal().into_bits() != 0 && !flags.custom_signal() + if (u32::from(flags) & 0x60008f8b) != 0 + || flags.has_all(Fork::CopyFd | Fork::ClearFd) + || flags.has_any(Fork::ParentSignal.mask()) && !flags.has_any(Fork::CustomSignal) { return Err(ForkError::InvalidFlags); } - if !flags.create_process() { + if !flags.has_any(Fork::CreateProcess) { todo!() } @@ -89,52 +94,31 @@ pub struct ProcEvents { } /// Flags to control behavior of [`ProcMgr::fork()`]. -#[bitfield(u32)] -pub struct Fork { - __: bool, - __: bool, +#[bitflag(u32)] +pub enum Fork { /// Duplicate file descriptor table to the child instead of sharing it with the parent. Cannot /// used together with [`Self::clear_fd()`]. /// /// This has the same value as `RFFDG`. - pub copy_fd: bool, - __: bool, + CopyFd = 0x4, /// Create a child process. /// /// This has the same value as `RFPROC`. - pub create_process: bool, - __: bool, - __: bool, - __: bool, - __: bool, - __: bool, - __: bool, - __: bool, + CreateProcess = 0x10, /// Create an empty file descriptor table for the child. Cannot used together with /// [`Self::copy_fd()`]. /// /// This has the same value as `RFCFDG`. - pub clear_fd: bool, - __: bool, - __: bool, - __: bool, - __: bool, - __: bool, - __: bool, + ClearFd = 0x1000, /// Enable [`Self::parent_signal()`]. /// /// This has the same value as `RFTSIGZMB`. - pub custom_signal: bool, + CustomSignal = 0x80000, /// Use this signal instead of `SIGCHLD` to notify the parent. Requires /// [`Self::custom_signal()`] to be enabled. /// /// This has the same value produced by `RFTSIGNUM` macro. - #[bits(8)] - pub parent_signal: Signal, - __: bool, - __: bool, - __: bool, - __: bool, + ParentSignal(Signal) = 0xFF00000, } /// Represents an error when [`ProcMgr::fork()`] fails. diff --git a/kernel/src/uma/keg.rs b/kernel/src/uma/keg.rs index 12ac0506..50837208 100644 --- a/kernel/src/uma/keg.rs +++ b/kernel/src/uma/keg.rs @@ -37,21 +37,21 @@ impl UmaKeg { align: usize, mut flags: UmaFlags, ) -> Self { - if flags.has(UmaFlags::Vm) { + if flags.has_any(UmaFlags::Vm) { todo!() } - if flags.has(UmaFlags::ZInit) { + if flags.has_any(UmaFlags::ZInit) { todo!() } - if flags.has(UmaFlags::Malloc | UmaFlags::RefCnt) { + if flags.has_any(UmaFlags::Malloc | UmaFlags::RefCnt) { flags |= UmaFlags::VToSlab; } // Get header layout. let hdr = Layout::new::>(); - let (mut hdr, off) = if flags.has(UmaFlags::RefCnt) { + let (mut hdr, off) = if flags.has_any(UmaFlags::RefCnt) { hdr.extend(Layout::new::()).unwrap() } else { hdr.extend(Layout::new::()).unwrap() @@ -64,7 +64,7 @@ impl UmaKeg { let available = PAGE_SIZE.get() - hdr.size(); // Get uk_ppera and uk_ipers. - let (ppera, ipers) = if flags.has(UmaFlags::CacheSpread) { + let (ppera, ipers) = if flags.has_any(UmaFlags::CacheSpread) { // Round size. let rsize = if (size.get() & align) == 0 { size.get() @@ -93,10 +93,10 @@ impl UmaKeg { // TODO: Not sure why we need space at least for 2 free item? if (size.get() + free_item) > available { // TODO: Set uk_ppera and uk_rsize. - if !flags.has(UmaFlags::Internal) { + if !flags.has_any(UmaFlags::Internal) { flags |= UmaFlags::Offpage; - if !flags.has(UmaFlags::VToSlab) { + if !flags.has_any(UmaFlags::VToSlab) { flags |= UmaFlags::Hash; } } @@ -123,7 +123,7 @@ impl UmaKeg { let ipers = available / (rsize + free_item); // TODO: Verify if this valid for PAGE_SIZE < 0x4000. - if !flags.has(UmaFlags::Internal | UmaFlags::CacheOnly) + if !flags.has_any(UmaFlags::Internal | UmaFlags::CacheOnly) && (available % (rsize + free_item)) >= Uma::MAX_WASTE.get() && (PAGE_SIZE.get() / rsize) > ipers { @@ -134,8 +134,8 @@ impl UmaKeg { } }; - if flags.has(UmaFlags::Offpage) { - if flags.has(UmaFlags::RefCnt) { + if flags.has_any(UmaFlags::Offpage) { + if flags.has_any(UmaFlags::RefCnt) { // TODO: Set uk_slabzone to slabrefzone. } else { // TODO: Set uk_slabzone to slabzone. @@ -150,11 +150,11 @@ impl UmaKeg { Self::page_alloc }; - if flags.has(UmaFlags::MtxClass) { + if flags.has_any(UmaFlags::MtxClass) { todo!() } - if !flags.has(UmaFlags::Offpage) { + if !flags.has_any(UmaFlags::Offpage) { let space = ppera * PAGE_SIZE.get(); let pgoff = (space - hdr.size()) - ipers * free_item; @@ -164,7 +164,7 @@ impl UmaKeg { } } - if flags.has(UmaFlags::Hash) { + if flags.has_any(UmaFlags::Hash) { todo!() } @@ -207,7 +207,7 @@ impl UmaKeg { /// |PS4 11.00|0x141E20| pub fn fetch_slab(&mut self, _: &UmaZone, flags: Alloc) -> Option<()> { while self.free == 0 { - if flags.has(Alloc::NoVm) { + if flags.has_any(Alloc::NoVm) { return None; } @@ -233,10 +233,10 @@ impl UmaKeg { /// |---------|--------| /// |PS4 11.00|0x13FBA0| fn alloc_slab(&self, flags: Alloc) { - if self.flags.has(UmaFlags::Offpage) { + if self.flags.has_any(UmaFlags::Offpage) { todo!() } else { - let flags = if self.flags.has(UmaFlags::Malloc) { + let flags = if self.flags.has_any(UmaFlags::Malloc) { flags & !Alloc::Zero } else { flags | Alloc::Zero diff --git a/kernel/src/uma/mod.rs b/kernel/src/uma/mod.rs index ac7d60d1..2c6c0aa1 100644 --- a/kernel/src/uma/mod.rs +++ b/kernel/src/uma/mod.rs @@ -100,7 +100,7 @@ impl Uma { name: impl Into, size: NonZero, align: Option, - flags: UmaFlags, + flags: impl Into, ) -> UmaZone { // The Orbis will allocate a new zone from masterzone_z. We choose to remove this since it // does not idomatic to Rust, which mean our uma_zone itself can live on the stack. diff --git a/kernel/src/uma/zone.rs b/kernel/src/uma/zone.rs index 038ab646..e1b73491 100644 --- a/kernel/src/uma/zone.rs +++ b/kernel/src/uma/zone.rs @@ -53,10 +53,11 @@ impl UmaZone { keg: Option, size: NonZero, align: Option, - flags: UmaFlags, + flags: impl Into, ) -> Self { let name = name.into(); - let (keg, mut flags) = if flags.has(UmaFlags::Secondary) { + let flags = flags.into(); + let (keg, mut flags) = if flags.has_any(UmaFlags::Secondary) { todo!() } else { // We use a different approach here to make it idiomatic to Rust. On Orbis it will @@ -74,8 +75,8 @@ impl UmaZone { let mut ty = ZoneType::Other; let mut count = 0; - if !keg.flags().has(UmaFlags::Internal) { - count = if !keg.flags().has(UmaFlags::MaxBucket) { + if !keg.flags().has_any(UmaFlags::Internal) { + count = if !keg.flags().has_any(UmaFlags::MaxBucket) { min(keg.item_per_slab(), Uma::BUCKET_MAX) } else { Uma::BUCKET_MAX @@ -148,7 +149,7 @@ impl UmaZone { /// |---------|--------| /// |PS4 11.00|0x13E750| pub fn alloc(&self, flags: Alloc) -> *mut u8 { - if flags.has(Alloc::Wait) { + if flags.has_any(Alloc::Wait) { // TODO: The Orbis also modify td_pflags on a certain condition. let td = current_thread(); @@ -210,7 +211,7 @@ impl UmaZone { | ZoneType::Mbuf | ZoneType::MbufCluster ) { - if flags.has(Alloc::Wait) { + if flags.has_any(Alloc::Wait) { todo!() } @@ -272,7 +273,7 @@ impl UmaZone { // Get allocation flags. let mut flags = flags & !Alloc::Zero; - if self.flags.has(UmaFlags::CacheOnly) { + if self.flags.has_any(UmaFlags::CacheOnly) { flags |= Alloc::NoVm; } @@ -317,13 +318,13 @@ impl UmaZone { let mut kegs = self.kegs.write(); let keg = keg.unwrap_or(kegs.front_mut().unwrap()); - if !keg.flags().has(UmaFlags::Bucket) || keg.recurse() == 0 { + if !keg.flags().has_any(UmaFlags::Bucket) || keg.recurse() == 0 { loop { if let Some(v) = keg.fetch_slab(self, flags) { return Some(v); } - if flags.has(Alloc::NoWait | Alloc::NoVm) { + if flags.has_any(Alloc::NoWait | Alloc::NoVm) { break; } } diff --git a/lib/bitflag/Cargo.toml b/lib/bitflag/Cargo.toml new file mode 100644 index 00000000..a933e18a --- /dev/null +++ b/lib/bitflag/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bitflag" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/lib/bitflag/src/lib.rs b/lib/bitflag/src/lib.rs new file mode 100644 index 00000000..2b083b76 --- /dev/null +++ b/lib/bitflag/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] + +pub use self::mask::*; + +use core::ops::{BitOr, Not}; + +mod mask; + +/// Type of bit flag. +pub trait Type: From { + type Raw: Raw; +} + +/// Underlying type of [`Type`]. +pub trait Raw: BitOr + Not + Copy {} + +impl Raw for u32 {} diff --git a/lib/bitflag/src/mask.rs b/lib/bitflag/src/mask.rs new file mode 100644 index 00000000..9b1d7434 --- /dev/null +++ b/lib/bitflag/src/mask.rs @@ -0,0 +1,49 @@ +use crate::Type; +use core::marker::PhantomData; +use core::ops::{BitOr, Not}; + +/// Mask of a value in the set. +pub struct Mask { + mask: T::Raw, + phantom: PhantomData, +} + +impl Mask { + /// # Safety + /// `mask` must be valid for `V` (e.g. has exactly one bit set if `V` is `bool`) and must not + /// have zero bit in the middle. + pub const unsafe fn new(mask: T::Raw) -> Self { + Self { + mask, + phantom: PhantomData, + } + } + + pub const fn mask(self) -> T::Raw { + self.mask + } +} + +impl Clone for Mask { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Mask {} + +impl BitOr for Mask { + type Output = T; + + fn bitor(self, rhs: Self) -> Self::Output { + T::from(self.mask | rhs.mask) + } +} + +impl Not for Mask { + type Output = T; + + fn not(self) -> Self::Output { + T::from(!self.mask) + } +} diff --git a/macros/src/bitflag.rs b/macros/src/bitflag.rs index 435a9b13..f02d88e9 100644 --- a/macros/src/bitflag.rs +++ b/macros/src/bitflag.rs @@ -1,7 +1,9 @@ use proc_macro2::{Span, TokenStream}; -use quote::quote; +use quote::{ToTokens, quote}; +use std::num::NonZero; use syn::meta::ParseNestedMeta; -use syn::{Error, Fields, ItemEnum, Path}; +use syn::punctuated::Pair; +use syn::{Error, Expr, ExprLit, Fields, ItemEnum, Lit, LitInt, Path, Type, parse_quote}; pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { let ty = opts @@ -18,10 +20,22 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { let mut body = TokenStream::new(); for v in item.variants { - let attrs = v.attrs; + // Parse discriminant. let ident = v.ident; - let discriminant = match v.discriminant { - Some(v) => v.1, + let (mask, bits, dis) = match v.discriminant { + Some(( + _, + Expr::Lit(ExprLit { + attrs: _, + lit: Lit::Int(v), + }), + )) => parse_discriminant(ty, v)?, + Some((_, v)) => { + return Err(Error::new_spanned( + v, + "discriminant other than integer literal is not supported", + )); + } None => { return Err(Error::new_spanned( ident, @@ -30,14 +44,51 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { } }; - if !matches!(v.fields, Fields::Unit) { - return Err(Error::new_spanned(ident, "only unit variant is supported")); + // Generate flag. + let attrs = v.attrs; + let ty = match v.fields { + Fields::Named(_) => { + return Err(Error::new_spanned( + ident, + "variant with named fields is not supported", + )); + } + Fields::Unnamed(mut v) => { + // Get field. + let f = match v.unnamed.pop() { + Some(Pair::End(v)) => v, + Some(_) => { + return Err(Error::new_spanned( + ident, + "variant with multiple fields is not supported", + )); + } + None => { + return Err(Error::new_spanned( + ident, + "field-less variant is not supported", + )); + } + }; + + f.ty + } + Fields::Unit => parse_quote!(bool), + }; + + if let Type::Path(t) = &ty { + if t.qself.is_none() && t.path.is_ident("bool") && bits.get() != 1 { + return Err(Error::new_spanned( + dis, + "multiple bits for a boolean mask is not supported", + )); + } } body.extend(quote! { #(#attrs)* #[allow(non_upper_case_globals)] - pub const #ident: Self = Self(#discriminant); + pub const #ident: ::bitflag::Mask = unsafe { ::bitflag::Mask::new(#mask) }; }); } @@ -52,8 +103,8 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { /// /// This performs the `&` operation on the underlying value and check if the results is /// non-zero. - pub const fn has(self, rhs: Self) -> bool { - (self.0 & rhs.0) != 0 + pub fn has_any(self, rhs: impl Into) -> bool { + (self.0 & rhs.into().0) != 0 } /// Returns `true` if this set contains **all** flags in the `rhs` set. @@ -82,17 +133,27 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { #body } + impl ::bitflag::Type for #impl_ident { + type Raw = #ty; + } + impl From<#ty> for #impl_ident { fn from(value: #ty) -> Self { Self(value) } } - impl ::core::ops::BitOr for #impl_ident { + impl From<::bitflag::Mask> for #impl_ident { + fn from(value: ::bitflag::Mask) -> Self { + Self(value.mask()) + } + } + + impl ::core::ops::BitOr<::bitflag::Mask> for #impl_ident { type Output = Self; - fn bitor(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) + fn bitor(self, rhs: ::bitflag::Mask) -> Self::Output { + Self(self.0 | rhs.mask()) } } @@ -102,6 +163,12 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { } } + impl ::core::ops::BitOrAssign<::bitflag::Mask> for #impl_ident { + fn bitor_assign(&mut self, rhs: ::bitflag::Mask) { + self.0 |= rhs.mask(); + } + } + impl ::core::ops::BitAnd for #impl_ident { type Output = Self; @@ -110,14 +177,6 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { } } - impl ::core::ops::Not for #impl_ident { - type Output = Self; - - fn not(self) -> Self::Output { - Self(!self.0) - } - } - impl From<#impl_ident> for #ty { fn from(value: #impl_ident) -> Self { value.0 @@ -126,6 +185,43 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result { }) } +fn parse_discriminant(ty: &Path, dis: LitInt) -> syn::Result<(TokenStream, NonZero, LitInt)> { + let v = if ty.is_ident("u32") { + let v = dis.base10_parse::()?; + let i = v.trailing_zeros(); + let mut r = v >> i; + + // Disallow zero value. + if r == 0 { + return Err(Error::new_spanned( + dis, + "zero discriminant is not supported", + )); + } + + // Disallow zero bit in the middle. + let mut n = 0; + + while r != 0 { + if r & 1 == 0 { + return Err(Error::new_spanned( + dis, + "discriminant with non-contiguous bits is not supported", + )); + } + + n += 1; + r >>= 1; + } + + (v.into_token_stream(), n.try_into().unwrap(), dis) + } else { + return Err(Error::new_spanned(ty, "unsupported underlying type")); + }; + + Ok(v) +} + #[derive(Default)] pub struct Options { ty: Option,