obkrnl/vm/
phys.rs

1use super::{MemAffinity, VmPage};
2use alloc::collections::vec_deque::VecDeque;
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5
6/// Provides methods to allocate physical memory.
7pub struct PhysAllocator {
8    segs: Vec<PhysSeg>, // vm_phys_segs + vm_phys_nsegs
9    nfree: usize,       // vm_nfreelists
10    #[allow(clippy::type_complexity)] // TODO: Remove this.
11    lookup_lists: [Arc<[[[VecDeque<VmPage>; 13]; 3]; 2]>; 2], // vm_phys_lookup_lists
12}
13
14impl PhysAllocator {
15    /// See `vm_phys_init` on the Orbis for a reference.
16    ///
17    /// # Reference offsets
18    /// | Version | Offset |
19    /// |---------|--------|
20    /// |PS4 11.00|0x15F410|
21    pub fn new(phys_avail: &[u64; 61], ma: Option<&MemAffinity>) -> Self {
22        // Create segments.
23        let mut segs = Vec::new();
24        let mut nfree = 0;
25
26        for i in (0..).step_by(2) {
27            // Check if end entry.
28            let mut addr = phys_avail[i];
29            let end = phys_avail[i + 1];
30
31            if end == 0 {
32                break;
33            }
34
35            // TODO: Why Orbis need to create 16MB segment here?
36            if addr < 16777216 {
37                let unk = end < 0x1000001;
38
39                if !unk {
40                    Self::create_seg(&mut segs, ma, addr, 0x1000000);
41
42                    // The Orbis also update end address here but it seems like the value is always
43                    // the same as current value.
44                    addr = 0x1000000;
45                }
46
47                Self::create_seg(&mut segs, ma, addr, end);
48
49                nfree = 1;
50            } else {
51                Self::create_seg(&mut segs, ma, addr, end);
52            }
53        }
54
55        // Populate vm_phys_free_queues. Do not use Clone to construct the array here since it will
56        // refer to the same object.
57        let free_queues = [
58            Arc::<[[[VecDeque<VmPage>; 13]; 3]; 2]>::default(),
59            Arc::<[[[VecDeque<VmPage>; 13]; 3]; 2]>::default(),
60        ];
61
62        // Populate vm_phys_lookup_lists.
63        let lookup_lists = [free_queues[0].clone(), free_queues[1].clone()];
64
65        Self {
66            segs,
67            nfree,
68            lookup_lists,
69        }
70    }
71
72    /// See `vm_phys_paddr_to_vm_page` on the Orbis for a reference.
73    pub fn page_for(&mut self, pa: u64) -> Option<&mut VmPage> {
74        for s in &mut self.segs {
75            if s.start > pa || s.end <= pa {
76                continue;
77            }
78
79            todo!()
80        }
81
82        None
83    }
84
85    /// See `vm_phys_alloc_pages` on the Orbis for a reference.
86    ///
87    /// # Reference offsets
88    /// | Version | Offset |
89    /// |---------|--------|
90    /// |PS4 11.00|0x160520|
91    pub fn alloc_page(&self, vm: usize, pool: usize, order: usize) -> Option<VmPage> {
92        // TODO: There is an increasement on unknown variable here.
93        let mut i = 0;
94
95        loop {
96            let l = &self.lookup_lists[i];
97
98            if let Some(v) = self.alloc_freelist(&l[vm], pool, order) {
99                return Some(v);
100            }
101
102            i += 1;
103
104            if i >= (self.nfree + 1) {
105                break;
106            }
107        }
108
109        None
110    }
111
112    /// See `vm_phys_alloc_freelist_pages` on the Orbis for a reference.
113    ///
114    /// # Reference offsets
115    /// | Version | Offset |
116    /// |---------|--------|
117    /// |PS4 11.00|0x1605D0|
118    fn alloc_freelist(
119        &self,
120        list: &[[VecDeque<VmPage>; 13]; 3],
121        pool: usize,
122        order: usize,
123    ) -> Option<VmPage> {
124        if order >= 13 {
125            return None;
126        }
127
128        let mut i = 0;
129
130        loop {
131            match list[pool][order + i].front() {
132                Some(v) => v,
133                None => match (order + i) < 12 {
134                    true => {
135                        i += 1;
136                        continue;
137                    }
138                    false => break,
139                },
140            };
141
142            todo!()
143        }
144
145        let mut next = 11;
146
147        loop {
148            let mut found = None;
149
150            for f in list {
151                found = f[next + 1].front();
152
153                if found.is_some() {
154                    break;
155                }
156            }
157
158            match found {
159                Some(_) => todo!(),
160                None => {
161                    if next < order || next == 0 {
162                        break;
163                    }
164
165                    next -= 1;
166                }
167            }
168        }
169
170        None
171    }
172
173    /// See `vm_phys_create_seg` on the Orbis for a reference.
174    ///
175    /// # Reference offsets
176    /// | Version | Offset |
177    /// |---------|--------|
178    /// |PS4 11.00|0x15F8A0|
179    fn create_seg(segs: &mut Vec<PhysSeg>, ma: Option<&MemAffinity>, start: u64, end: u64) {
180        match ma {
181            Some(_) => todo!(),
182            None => segs.push(PhysSeg { start, end }),
183        }
184    }
185}
186
187/// Implementation of `vm_phys_seg` structure.
188struct PhysSeg {
189    start: u64,
190    end: u64,
191}