obkrnl/uma/
mod.rs

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