From d8278a80fd4cc6232910ef91b9a073d77888a8bb Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Sun, 12 Jan 2025 15:32:24 +0700 Subject: [PATCH] Implements bitflag macro --- kernel/src/malloc/stage2.rs | 2 +- kernel/src/uma/keg.rs | 12 ++-- kernel/src/uma/mod.rs | 34 ++++------- kernel/src/uma/zone.rs | 2 +- macros/src/bitflag.rs | 109 ++++++++++++++++++++++++++++++++++++ macros/src/lib.rs | 16 ++++++ 6 files changed, 145 insertions(+), 30 deletions(-) create mode 100644 macros/src/bitflag.rs diff --git a/kernel/src/malloc/stage2.rs b/kernel/src/malloc/stage2.rs index 84261b4f2..751fe96e8 100644 --- a/kernel/src/malloc/stage2.rs +++ b/kernel/src/malloc/stage2.rs @@ -56,7 +56,7 @@ impl Stage2 { size.to_string(), size, Some(align - 1), - UmaFlags::new().with_malloc(true), + UmaFlags::Malloc, )); while last <= size.get() { diff --git a/kernel/src/uma/keg.rs b/kernel/src/uma/keg.rs index 991703490..32c23bbcb 100644 --- a/kernel/src/uma/keg.rs +++ b/kernel/src/uma/keg.rs @@ -16,24 +16,24 @@ impl UmaKeg { /// |---------|--------| /// |PS4 11.00|0x13CF40| pub(super) fn new(size: NonZero, _: 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::(); - let (mut min, off) = if flags.refcnt() { + let (mut min, off) = if flags.has(UmaFlags::RefCnt) { min.extend(Layout::new::()).unwrap() } else { min.extend(Layout::new::()).unwrap() diff --git a/kernel/src/uma/mod.rs b/kernel/src/uma/mod.rs index 57791014b..350641225 100644 --- a/kernel/src/uma/mod.rs +++ b/kernel/src/uma/mod.rs @@ -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; @@ -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, } diff --git a/kernel/src/uma/zone.rs b/kernel/src/uma/zone.rs index 8510d232c..584ef1c32 100644 --- a/kernel/src/uma/zone.rs +++ b/kernel/src/uma/zone.rs @@ -36,7 +36,7 @@ impl UmaZone { align: Option, 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 diff --git a/macros/src/bitflag.rs b/macros/src/bitflag.rs new file mode 100644 index 000000000..e1347e4c0 --- /dev/null +++ b/macros/src/bitflag.rs @@ -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 { + 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, +} + +impl Options { + pub fn parse(&mut self, m: ParseNestedMeta) -> syn::Result<()> { + if self.ty.is_none() { + self.ty = Some(m.path); + } + + Ok(()) + } +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 830e0f1fd..c9d31a56a 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -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 {