Skip to content

Commit

Permalink
Implements bitflag macro (#1241)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Jan 12, 2025
1 parent b828540 commit 5ba8a39
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 30 deletions.
2 changes: 1 addition & 1 deletion kernel/src/malloc/stage2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Stage2 {
size.to_string(),
size,
Some(align - 1),
UmaFlags::new().with_malloc(true),
UmaFlags::Malloc,
));

while last <= size.get() {
Expand Down
12 changes: 6 additions & 6 deletions kernel/src/uma/keg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ impl UmaKeg {
/// |---------|--------|
/// |PS4 11.00|0x13CF40|
pub(super) fn new(size: NonZero<usize>, _: usize, mut flags: UmaFlags) -> Self {
if flags.vm() {
if flags.has(UmaFlags::Vm) {
todo!()
}

if flags.zinit() {
if flags.has(UmaFlags::ZInit) {
todo!()
}

if flags.malloc() || flags.refcnt() {
flags.set_vtoslab(true);
if flags.has(UmaFlags::Malloc | UmaFlags::RefCnt) {
flags |= UmaFlags::VToSlab;
}

if flags.cache_spread() {
if flags.has(UmaFlags::CacheSpread) {
todo!()
} else {
// Check if item size exceed slab size.
let min = Layout::new::<SlabHdr>();
let (mut min, off) = if flags.refcnt() {
let (mut min, off) = if flags.has(UmaFlags::RefCnt) {
min.extend(Layout::new::<RcFree>()).unwrap()
} else {
min.extend(Layout::new::<Free>()).unwrap()
Expand Down
34 changes: 12 additions & 22 deletions kernel/src/uma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ pub use self::zone::*;

use alloc::string::String;
use alloc::sync::Arc;
use bitfield_struct::bitfield;
use core::num::NonZero;
use macros::bitflag;

mod bucket;
mod keg;
Expand Down Expand Up @@ -45,32 +45,22 @@ impl Uma {
}

/// Flags for [`Uma::create_zone()`].
#[bitfield(u32)]
pub struct UmaFlags {
__: bool,
pub zinit: bool,
#[bits(2)]
__: u8,
#[bitflag(u32)]
pub enum UmaFlags {
/// `UMA_ZONE_ZINIT`.
ZInit = 0x2,
/// `UMA_ZONE_MALLOC`.
pub malloc: bool,
#[bits(2)]
__: u8,
Malloc = 0x10,
/// `UMA_ZONE_VM`.
pub vm: bool,
__: bool,
Vm = 0x80,
/// `UMA_ZONE_SECONDARY`.
pub secondary: bool,
Secondary = 0x200,
/// `UMA_ZONE_REFCNT`.
pub refcnt: bool,
__: bool,
RefCnt = 0x400,
/// `UMA_ZONE_CACHESPREAD`.
pub cache_spread: bool,
CacheSpread = 0x1000,
/// `UMA_ZONE_VTOSLAB`.
pub vtoslab: bool,
#[bits(15)]
__: u32,
VToSlab = 0x2000,
/// `UMA_ZFLAG_INTERNAL`.
pub internal: bool,
__: bool,
__: bool,
Internal = 0x20000000,
}
2 changes: 1 addition & 1 deletion kernel/src/uma/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl UmaZone {
align: Option<usize>,
flags: UmaFlags,
) -> Self {
if flags.secondary() {
if flags.has(UmaFlags::Secondary) {
todo!()
} else {
// We use a different approach here to make it idiomatic to Rust. On Orbis it will
Expand Down
109 changes: 109 additions & 0 deletions macros/src/bitflag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::meta::ParseNestedMeta;
use syn::{Error, Fields, ItemEnum, Path};

pub fn transform(opts: Options, item: ItemEnum) -> syn::Result<TokenStream> {
let ty = opts
.ty
.as_ref()
.ok_or_else(|| Error::new(Span::call_site(), "missing underlying type name"))?;
let ident = item.ident;

if item.generics.lt_token.is_some() {
return Err(Error::new_spanned(ident, "generic enum is not supported"));
}

// Parse body.
let mut body = TokenStream::new();

for v in item.variants {
let attrs = v.attrs;
let ident = v.ident;
let discriminant = match v.discriminant {
Some(v) => v.1,
None => {
return Err(Error::new_spanned(
ident,
"auto-discriminant is not supported",
));
}
};

if !matches!(v.fields, Fields::Unit) {
return Err(Error::new_spanned(ident, "only unit variant is supported"));
}

body.extend(quote! {
#(#attrs)*
#[allow(non_upper_case_globals)]
pub const #ident: Self = Self(#discriminant);
});
}

// Generate methods.
body.extend(quote! {
/// Returns `true` if this set contains **any** flags in the `rhs` set.
///
/// 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
}

/// Returns `true` if this set contains **all** flags in the `rhs` set.
///
/// This performs the `&` operation on the underlying value and check if the results is
/// equal to `rhs`.
pub const fn has_all(self, rhs: Self) -> bool {
(self.0 & rhs.0) == rhs.0
}
});

// Compose.
let attrs = item.attrs;
let vis = item.vis;
let mut impl_ident = ident.clone();

impl_ident.set_span(Span::call_site());

Ok(quote! {
#(#attrs)*
#[repr(transparent)]
#[derive(Clone, Copy)]
#vis struct #ident(#ty);

impl #impl_ident {
#body
}

impl ::core::ops::BitOr for #impl_ident {
type Output = Self;

fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}

impl ::core::ops::BitOrAssign for #impl_ident {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
})
}

#[derive(Default)]
pub struct Options {
ty: Option<Path>,
}

impl Options {
pub fn parse(&mut self, m: ParseNestedMeta) -> syn::Result<()> {
if self.ty.is_none() {
self.ty = Some(m.path);
}

Ok(())
}
}
16 changes: 16 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
use proc_macro::TokenStream;
use syn::{parse_macro_input, Error, ItemEnum, ItemStatic, LitStr};

mod bitflag;
mod elf;
mod enum_conversions;
mod errno;
mod vpath;

/// The reason we use `bitflag` as a name instead of `bitflags` is to make it matched with
/// `bitfield-struct` crate.
#[proc_macro_attribute]
pub fn bitflag(args: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as ItemEnum);
let mut opts = self::bitflag::Options::default();
let parser = syn::meta::parser(|m| opts.parse(m));

parse_macro_input!(args with parser);

self::bitflag::transform(opts, item)
.unwrap_or_else(Error::into_compile_error)
.into()
}

/// Note will not produced for test target.
#[proc_macro_attribute]
pub fn elf_note(args: TokenStream, item: TokenStream) -> TokenStream {
Expand Down

0 comments on commit 5ba8a39

Please sign in to comment.