Switches fork1 flags from bitfield to bitflag (#1290)

This commit is contained in:
Putta Khunchalee 2025-02-24 01:32:14 +09:00 committed by GitHub
parent ea6b1513a5
commit 39ceb714f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 249 additions and 84 deletions

5
Cargo.lock generated
View file

@ -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",

View file

@ -6,6 +6,7 @@ members = [
"config",
"gui",
"kernel",
"lib/bitflag",
"lib/krt",
"macros",
]

View file

@ -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" }

View file

@ -91,11 +91,16 @@ fn init_vm(procs: &Arc<ProcMgr>) -> Arc<Uma> {
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();

View file

@ -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<dyn ProcAbi>, flags: Fork) -> Result<Arc<Proc>, 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.

View file

@ -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::<Slab<()>>();
let (mut hdr, off) = if flags.has(UmaFlags::RefCnt) {
let (mut hdr, off) = if flags.has_any(UmaFlags::RefCnt) {
hdr.extend(Layout::new::<RcFree>()).unwrap()
} else {
hdr.extend(Layout::new::<Free>()).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

View file

@ -100,7 +100,7 @@ impl Uma {
name: impl Into<String>,
size: NonZero<usize>,
align: Option<usize>,
flags: UmaFlags,
flags: impl Into<UmaFlags>,
) -> 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.

View file

@ -53,10 +53,11 @@ impl UmaZone {
keg: Option<UmaKeg>,
size: NonZero<usize>,
align: Option<usize>,
flags: UmaFlags,
flags: impl Into<UmaFlags>,
) -> 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;
}
}

6
lib/bitflag/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "bitflag"
version = "0.1.0"
edition = "2024"
[dependencies]

17
lib/bitflag/src/lib.rs Normal file
View file

@ -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<Self::Raw> {
type Raw: Raw;
}
/// Underlying type of [`Type`].
pub trait Raw: BitOr<Output = Self> + Not<Output = Self> + Copy {}
impl Raw for u32 {}

49
lib/bitflag/src/mask.rs Normal file
View file

@ -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<T: Type, V> {
mask: T::Raw,
phantom: PhantomData<V>,
}
impl<T: Type, V> Mask<T, V> {
/// # 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<T: Type, V> Clone for Mask<T, V> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Type, V> Copy for Mask<T, V> {}
impl<T: Type> BitOr for Mask<T, bool> {
type Output = T;
fn bitor(self, rhs: Self) -> Self::Output {
T::from(self.mask | rhs.mask)
}
}
impl<T: Type, V> Not for Mask<T, V> {
type Output = T;
fn not(self) -> Self::Output {
T::from(!self.mask)
}
}

View file

@ -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<TokenStream> {
let ty = opts
@ -18,10 +20,22 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
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<TokenStream> {
}
};
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<Self, #ty> = unsafe { ::bitflag::Mask::new(#mask) };
});
}
@ -52,8 +103,8 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
///
/// 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<Self>) -> 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<TokenStream> {
#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<Self, bool>> for #impl_ident {
fn from(value: ::bitflag::Mask<Self, bool>) -> Self {
Self(value.mask())
}
}
impl ::core::ops::BitOr<::bitflag::Mask<Self, bool>> 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, bool>) -> Self::Output {
Self(self.0 | rhs.mask())
}
}
@ -102,6 +163,12 @@ pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
}
}
impl ::core::ops::BitOrAssign<::bitflag::Mask<Self, bool>> for #impl_ident {
fn bitor_assign(&mut self, rhs: ::bitflag::Mask<Self, bool>) {
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<TokenStream> {
}
}
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<TokenStream> {
})
}
fn parse_discriminant(ty: &Path, dis: LitInt) -> syn::Result<(TokenStream, NonZero<u32>, LitInt)> {
let v = if ty.is_ident("u32") {
let v = dis.base10_parse::<u32>()?;
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<Path>,