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                UmaFlags::Bucket | UmaFlags::Internal,
76            ));
77
78            while ki <= size {
79                bucket_keys.push(si);
80                ki += 1 << Self::BUCKET_SHIFT;
81            }
82        }
83
84        Arc::new(Self {
85            vm,
86            bucket_enable,
87            bucket_keys: Arc::new(bucket_keys),
88            bucket_zones: Arc::new(bucket_zones),
89        })
90    }
91
92    /// See `uma_zcreate` on the Orbis for a reference.
93    ///
94    /// # Reference offsets
95    /// | Version | Offset |
96    /// |---------|--------|
97    /// |PS4 11.00|0x13DC80|
98    pub fn create_zone(
99        &self,
100        name: impl Into<String>,
101        size: NonZero<usize>,
102        align: Option<usize>,
103        flags: impl Into<UmaFlags>,
104    ) -> UmaZone {
105        // The Orbis will allocate a new zone from masterzone_z. We choose to remove this since it
106        // does not idomatic to Rust, which mean our uma_zone itself can live on the stack.
107        UmaZone::new(
108            self.vm.clone(),
109            self.bucket_enable.clone(),
110            self.bucket_keys.clone(),
111            self.bucket_zones.clone(),
112            name,
113            None,
114            size,
115            align,
116            flags,
117        )
118    }
119}
120
121/// Flags for [`Uma::create_zone()`].
122#[bitflag(u32)]
123pub enum UmaFlags {
124    /// `UMA_ZONE_ZINIT`.
125    ZInit = 0x2,
126    /// `UMA_ZONE_OFFPAGE`.
127    Offpage = 0x8,
128    /// `UMA_ZONE_MALLOC`.
129    Malloc = 0x10,
130    /// `UMA_ZONE_MTXCLASS`.
131    MtxClass = 0x40,
132    /// `UMA_ZONE_VM`.
133    Vm = 0x80,
134    /// `UMA_ZONE_HASH`.
135    Hash = 0x100,
136    /// `UMA_ZONE_SECONDARY`.
137    Secondary = 0x200,
138    /// `UMA_ZONE_REFCNT`.
139    RefCnt = 0x400,
140    /// `UMA_ZONE_MAXBUCKET`.
141    MaxBucket = 0x800,
142    /// `UMA_ZONE_CACHESPREAD`.
143    CacheSpread = 0x1000,
144    /// `UMA_ZONE_VTOSLAB`.
145    VToSlab = 0x2000,
146    /// `UMA_ZFLAG_BUCKET`.
147    Bucket = 0x2000000,
148    /// `UMA_ZFLAG_INTERNAL`.
149    Internal = 0x20000000,
150    /// `UMA_ZFLAG_CACHEONLY`.
151    CacheOnly = 0x80000000,
152}
153
154/// Implementation of `malloc` flags.
155#[bitflag(u32)]
156pub enum Alloc {
157    /// `M_NOWAIT`.
158    NoWait = 0x1,
159    /// `M_WAITOK`.
160    Wait = 0x2,
161    /// `M_ZERO`.
162    Zero = 0x100,
163    /// `M_NOVM`.
164    NoVm = 0x200,
165}