Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements bitflag macro #1241

Merged
merged 1 commit into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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