Skip to main content

obkrnl/uma/
mod.rs

1pub use self::slab::StdFree;
2pub use self::zone::*;
3
4pub(self) use self::bucket::*;
5pub(self) use self::keg::*;
6pub(self) use self::slab::{FreeItem, Slab, SlabHdr};
7
8use crate::config::PAGE_SIZE;
9use crate::vm::Vm;
10use alloc::format;
11use alloc::string::String;
12use alloc::sync::Arc;
13use alloc::vec::Vec;
14use core::alloc::Layout;
15use core::num::NonZero;
16use core::sync::atomic::AtomicBool;
17use macros::bitflag;
18
19#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
20#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
21mod arch;
22mod bucket;
23mod keg;
24mod slab;
25mod zone;
26
27/// Implementation of UMA system.
28pub struct Uma {
29    vm: Arc<Vm>,
30    bucket_enable: Arc<AtomicBool>,
31    bucket_keys: Arc<Vec<usize>>,             // bucket_size
32    bucket_zones: Arc<Vec<UmaZone<StdFree>>>, // bucket_zones
33}
34
35impl Uma {
36    /// `UMA_SMALLEST_UNIT`.
37    const SMALLEST_UNIT: NonZero<usize> = NonZero::new(PAGE_SIZE.get() / 256).unwrap();
38
39    /// `UMA_MAX_WASTE`.
40    const MAX_WASTE: NonZero<usize> = NonZero::new(PAGE_SIZE.get() / 10).unwrap();
41    const BUCKET_MAX: usize = 128;
42    const BUCKET_SHIFT: usize = 4;
43
44    /// `bucket_zones`.
45    const BUCKET_SIZES: [usize; 4] = [16, 32, 64, 128];
46
47    /// See `uma_startup` on the Orbis for a reference.
48    ///
49    /// # Reference offsets
50    /// | Version | Offset |
51    /// |---------|--------|
52    /// |PS4 11.00|0x13CA70|
53    pub fn new(vm: Arc<Vm>) -> Arc<Self> {
54        let bucket_enable = Arc::new(AtomicBool::new(true)); // TODO: Use a proper value.
55        let mut bucket_keys = Vec::new();
56        let mut bucket_zones = Vec::with_capacity(Self::BUCKET_SIZES.len());
57        let mut ki = 0;
58
59        // Create bucket zones.
60        for (si, size) in Self::BUCKET_SIZES.into_iter().enumerate() {
61            let items = Layout::array::<*mut u8>(size).unwrap();
62            let layout = Layout::new::<BucketHdr>()
63                .extend(items)
64                .unwrap()
65                .0
66                .pad_to_align();
67
68            bucket_zones.push(UmaZone::new(
69                vm.clone(),
70                bucket_enable.clone(),
71                Arc::default(),
72                Arc::default(),
73                format!("{size} Bucket"),
74                None,
75                layout.size().try_into().unwrap(),
76                Some(layout.align() - 1),
77                None,
78                UmaFlags::Bucket | UmaFlags::Internal,
79            ));
80
81            while ki <= size {
82                bucket_keys.push(si);
83                ki += 1 << Self::BUCKET_SHIFT;
84            }
85        }
86
87        Arc::new(Self {
88            vm,
89            bucket_enable,
90            bucket_keys: Arc::new(bucket_keys),
91            bucket_zones: Arc::new(bucket_zones),
92        })
93    }
94
95    /// See `uma_zcreate` on the Orbis for a reference.
96    ///
97    /// # Reference offsets
98    /// | Version | Offset |
99    /// |---------|--------|
100    /// |PS4 11.00|0x13DC80|
101    pub fn create_zone<T: FreeItem>(
102        &self,
103        name: impl Into<String>,
104        size: NonZero<usize>,
105        align: Option<usize>,
106        init: Option<fn()>,
107        flags: impl Into<UmaFlags>,
108    ) -> UmaZone<T> {
109        // The Orbis will allocate a new zone from masterzone_z. We choose to remove this since it
110        // does not idomatic to Rust, which mean our uma_zone itself can live on the stack.
111        UmaZone::new(
112            self.vm.clone(),
113            self.bucket_enable.clone(),
114            self.bucket_keys.clone(),
115            self.bucket_zones.clone(),
116            name,
117            None,
118            size,
119            align,
120            init,
121            flags,
122        )
123    }
124}
125
126/// Flags for [`Uma::create_zone()`].
127#[bitflag(u32)]
128pub enum UmaFlags {
129    /// `UMA_ZONE_ZINIT`.
130    ZInit = 0x2,
131    /// `UMA_ZONE_OFFPAGE`.
132    Offpage = 0x8,
133    /// `UMA_ZONE_MALLOC`.
134    Malloc = 0x10,
135    /// `UMA_ZONE_MTXCLASS`.
136    MtxClass = 0x40,
137    /// `UMA_ZONE_VM`.
138    Vm = 0x80,
139    /// `UMA_ZONE_HASH`.
140    Hash = 0x100,
141    /// `UMA_ZONE_SECONDARY`.
142    Secondary = 0x200,
143    /// `UMA_ZONE_MAXBUCKET`.
144    MaxBucket = 0x800,
145    /// `UMA_ZONE_CACHESPREAD`.
146    CacheSpread = 0x1000,
147    /// `UMA_ZONE_VTOSLAB`.
148    VToSlab = 0x2000,
149    /// `UMA_ZFLAG_BUCKET`.
150    Bucket = 0x2000000,
151    /// `UMA_ZFLAG_INTERNAL`.
152    Internal = 0x20000000,
153    /// `UMA_ZFLAG_CACHEONLY`.
154    CacheOnly = 0x80000000,
155}
156
157/// Implementation of `malloc` flags.
158#[bitflag(u32)]
159pub enum Alloc {
160    /// `M_NOWAIT`.
161    NoWait = 0x1,
162    /// `M_WAITOK`.
163    Wait = 0x2,
164    /// `M_ZERO`.
165    Zero = 0x100,
166    /// `M_NOVM`.
167    NoVm = 0x200,
168}