obkrnl/uma/
mod.rs

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