obkrnl/dmem/
mod.rs

1use crate::MemoryInfo;
2use crate::config::{Dipsw, PAGE_SHIFT};
3use crate::context::config;
4use alloc::sync::Arc;
5use core::num::NonZero;
6
7/// Implementation of Direct Memory system.
8pub struct Dmem {
9    mode: usize,
10    config: &'static DmemConfig,
11}
12
13impl Dmem {
14    /// See `initialize_dmem` on the Orbis for a reference.
15    ///
16    /// # Reference offsets
17    /// | Version | Offset |
18    /// |---------|--------|
19    /// |PS4 11.00|0x3F5C20|
20    pub fn new(mi: &mut MemoryInfo) -> Arc<Self> {
21        // TODO: Figure out what the purpose of this 16MB block of memory.
22        let haddr = Self::reserve_phys(mi, 0x1000000.try_into().unwrap(), 1);
23
24        if haddr == 0 {
25            panic!("no available memory for high-address memory");
26        }
27
28        // TODO: Invoke bootparam_get_ddr3_capacity.
29        let mode = Self::load_mode(mi);
30
31        if (0x80C6C0C4u64 & (1 << mode)) != 0 {
32            panic!("game DMEM size is not configured");
33        }
34
35        // Allocate game DMEM.
36        let dc = DMEM_CONFIGS[mode].as_ref().unwrap();
37        let game = Self::reserve_phys(mi, dc.game_size, 0x8000000);
38
39        if game == 0 {
40            panic!("not enough memory for game DMEM");
41        }
42
43        // TODO: There is an unknown call here.
44        let game_end = game + dc.game_size.get();
45
46        if (0x7F393733u64 & (1 << mode)) != 0 {
47            // Get alignment for mini-app DMEM.
48            let align = if config().unknown_dmem1() == 0 {
49                0x8000000i64
50            } else {
51                0x200000i64
52            };
53
54            // Allocate mini-app DMEM.
55            let size = dc.mini_size;
56            let mini = match dc.mini_shared {
57                true => (-align) as u64 & (game_end - (dc.fmem_max.get() + size)),
58                false => todo!(),
59            };
60
61            if mini == 0 {
62                panic!("not enough memory for mini-app DMEM");
63            }
64
65            // TODO: Invoke pmap_change_attr.
66        }
67
68        if (0x7F393F3Bu64 & (1 << mode)) != 0 {
69            let size = dc.vsh_size;
70            let vsh = Self::reserve_phys(mi, size, 0x200000);
71
72            if vsh == 0 {
73                panic!("not enough memory for VSH DMEM");
74            }
75
76            // TODO: There are some write to unknow variable here.
77            // TODO: Invoke pmap_change_attr.
78        }
79
80        if (0x47000703u64 & (1 << mode)) != 0 {
81            todo!()
82        }
83
84        // TODO: There is a write to unknown variable here.
85        if (0x47010703u64 & (1 << mode)) != 0 {
86            todo!()
87        }
88
89        // Allocate vision DMEM.
90        let vision = Self::reserve_phys(mi, 0x1000000.try_into().unwrap(), 0x200000);
91
92        if vision == 0 {
93            panic!("not enough memory for vision DMEM");
94        }
95
96        // TODO: Invoke pmap_change_attr.
97        // TODO: There are some write to unknown variables here.
98        let mut unk = dc.unk1;
99
100        if ((0x80C6C0C4u64 + 0x20212022u64) & (1 << mode)) == 0 {
101            loop {
102                let (size, align) = if unk < 0x200000 {
103                    // TODO: Verify if 0x4000 is actually a (virtual) page size.
104                    (unk, 0x4000)
105                } else {
106                    (0x200000, 0x200000)
107                };
108
109                // TODO: Invoke pmap_change_attr.
110                let addr = Self::reserve_phys(mi, size.try_into().unwrap(), align);
111
112                if addr == 0 {
113                    // TODO: Figure out the name of this memory.
114                    panic!("failed to reserve memory");
115                }
116
117                // Check if completed.
118                unk -= size;
119
120                if unk == 0 {
121                    break;
122                }
123            }
124        }
125
126        Self::adjust_pmap(mi, game_end - dc.fmem_max.get(), game_end);
127        Self::adjust_pmap(
128            mi,
129            haddr,
130            haddr + 0xC00000 + if mi.unk != 0 { 0x400000 } else { 0 },
131        );
132
133        mi.end_page = mi.physmap[mi.physmap_last + 1] >> PAGE_SHIFT;
134
135        Arc::new(Self { mode, config: dc })
136    }
137
138    pub fn mode(&self) -> usize {
139        self.mode
140    }
141
142    pub fn config(&self) -> &'static DmemConfig {
143        self.config
144    }
145
146    /// # Reference offsets
147    /// | Version | Offset |
148    /// |---------|--------|
149    /// |PS4 11.00|0x3F64C0|
150    fn reserve_phys(mi: &mut MemoryInfo, size: NonZero<u64>, align: i64) -> u64 {
151        let mut i = mi.physmap_last;
152
153        loop {
154            let start = mi.physmap[i];
155            let end = mi.physmap[i + 1];
156            let addr = (end - size.get()) & ((-align) as u64);
157
158            if addr >= start {
159                let aligned_end = addr + size.get();
160
161                // Check if this take the whole block.
162                if (addr == start) && (aligned_end == end) {
163                    mi.physmap.copy_within((i + 2).., i);
164                    mi.physmap_last -= 2;
165                    return addr;
166                }
167
168                // Check if this create a hole in the block.
169                if (addr != start) && (aligned_end != end) {
170                    mi.physmap.copy_within(i..(mi.physmap_last + 2), i + 2);
171                    mi.physmap[i + 1] = addr;
172                    mi.physmap[i + 2] = aligned_end;
173                    mi.physmap_last += 2;
174                    return addr;
175                }
176
177                // Check if this take the end of the block.
178                if addr != start {
179                    assert_eq!(aligned_end, end);
180                    mi.physmap[i + 1] = addr;
181                    return addr;
182                }
183
184                // Take the start of the block.
185                mi.physmap[i] = aligned_end;
186
187                return addr;
188            }
189
190            // Move to lower map.
191            if i < 2 {
192                break 0;
193            }
194
195            i -= 2;
196        }
197    }
198
199    /// # Reference offsets
200    /// | Version | Offset |
201    /// |---------|--------|
202    /// |PS4 11.00|0x3F65B0|
203    fn adjust_pmap(mi: &mut MemoryInfo, start: u64, end: u64) {
204        // Orbis also check if physmap_last + 2 is less than one. We don't do this because it is
205        // impossible for the value to be zero.
206        let mut idx = 0;
207
208        if start > mi.physmap[0] {
209            // Find the first map with address greater or equal start address.
210            let mut found = true;
211
212            for i in (2..).step_by(2) {
213                idx = i;
214
215                if idx > mi.physmap_last {
216                    found = false;
217                    break;
218                }
219
220                if mi.physmap[idx] >= start {
221                    break;
222                }
223            }
224
225            // TODO: What is this?
226            let mut empty = false;
227
228            if found {
229                empty = mi.physmap[idx] == end;
230
231                if (mi.physmap[idx - 1] == start) && (mi.physmap[idx] == end) {
232                    todo!()
233                }
234            }
235
236            if mi.physmap[idx - 1] == start {
237                todo!()
238            }
239
240            if !empty {
241                if !found {
242                    mi.physmap[idx] = start;
243                    mi.physmap[idx | 1] = end;
244                    mi.physmap_last += 2;
245                    return;
246                }
247
248                todo!()
249            }
250        } else {
251            todo!()
252        }
253
254        mi.physmap[idx] = start;
255    }
256
257    /// # Reference offsets
258    /// | Version | Offset |
259    /// |---------|--------|
260    /// |PS4 11.00|0x3F5B10|
261    fn load_mode(mi: &MemoryInfo) -> usize {
262        // Ths PS4 cache the calculation of this value here but we move it to Dmem struct instead.
263        let c = config();
264        let v = if c.unknown_dmem1() == 0 {
265            if c.dipsw(Dipsw::Unk97) {
266                // TODO: Figure out the name of this constant.
267                3
268            } else if c.dipsw(Dipsw::Unk0) && !c.dipsw(Dipsw::Unk24) {
269                // TODO: Figure out the name of 3 constant.
270                if c.dipsw(Dipsw::Unk16) && c.dipsw(Dipsw::Unk17) && (mi.unk & 3) == 3 {
271                    // TODO: Figure out the name of this constant.
272                    6
273                } else {
274                    todo!()
275                }
276            } else {
277                // TODO: Figure out the name of this constant.
278                4
279            }
280        } else {
281            // TODO: Figure out the name of this constant.
282            5
283        };
284
285        v + usize::try_from(mi.unk).unwrap() * 8
286    }
287}
288
289/// Configurations set for each DMEM mode.
290pub struct DmemConfig {
291    pub name: &'static str,
292    pub game_size: NonZero<u64>,
293    pub fmem_max: NonZero<u64>,
294    pub mini_size: u64,
295    pub mini_shared: bool,
296    pub vsh_size: NonZero<u64>,
297    pub unk1: u64,
298}
299
300// TODO: It is likely to be more than 21 entries on PS4 11.00.
301static DMEM_CONFIGS: [Option<DmemConfig>; 21] = [
302    Some(DmemConfig {
303        name: "BC8 normal",
304        game_size: NonZero::new(0x148000000).unwrap(),
305        fmem_max: NonZero::new(0x40000000).unwrap(),
306        mini_size: 0x30000000,
307        mini_shared: true,
308        vsh_size: NonZero::new(0x17C00000).unwrap(),
309        unk1: 0x13600000,
310    }),
311    Some(DmemConfig {
312        name: "BC8 large",
313        game_size: NonZero::new(0x170000000).unwrap(),
314        fmem_max: NonZero::new(0x40000000).unwrap(),
315        mini_size: 0x30000000,
316        mini_shared: true,
317        vsh_size: NonZero::new(0x16C00000).unwrap(),
318        unk1: 0,
319    }),
320    None,
321    Some(DmemConfig {
322        name: "BC8 kratos",
323        game_size: NonZero::new(0x148000000).unwrap(),
324        fmem_max: NonZero::new(0x40000000).unwrap(),
325        mini_size: 0,
326        mini_shared: false,
327        vsh_size: NonZero::new(0x1CA00000).unwrap(),
328        unk1: 0xA00000,
329    }),
330    Some(DmemConfig {
331        name: "BC8 release",
332        game_size: NonZero::new(0x148000000).unwrap(),
333        fmem_max: NonZero::new(0x40000000).unwrap(),
334        mini_size: 0x30000000,
335        mini_shared: false,
336        vsh_size: NonZero::new(0x1A800000).unwrap(),
337        unk1: 0xA00000,
338    }),
339    Some(DmemConfig {
340        name: "BC8 CS",
341        game_size: NonZero::new(0x124000000).unwrap(),
342        fmem_max: NonZero::new(0x4000000).unwrap(),
343        mini_size: 0x58800000,
344        mini_shared: false,
345        vsh_size: NonZero::new(0x28200000).unwrap(),
346        unk1: 0,
347    }),
348    None,
349    None,
350    Some(DmemConfig {
351        name: "BC16 normal",
352        game_size: NonZero::new(0x148000000).unwrap(),
353        fmem_max: NonZero::new(0x40000000).unwrap(),
354        mini_size: 0x30000000,
355        mini_shared: false,
356        vsh_size: NonZero::new(0x1C200000).unwrap(),
357        unk1: 0x17FB00000,
358    }),
359    Some(DmemConfig {
360        name: "BC16 large",
361        game_size: NonZero::new(0x28C000000).unwrap(),
362        fmem_max: NonZero::new(0x5C000000).unwrap(),
363        mini_size: 0x30000000,
364        mini_shared: false,
365        vsh_size: NonZero::new(0x1B200000).unwrap(),
366        unk1: 0x3CB00000,
367    }),
368    Some(DmemConfig {
369        name: "BC16 mini-app large",
370        game_size: NonZero::new(0x148000000).unwrap(),
371        fmem_max: NonZero::new(0x40000000).unwrap(),
372        mini_size: 0x48000000,
373        mini_shared: false,
374        vsh_size: NonZero::new(0x19600000).unwrap(),
375        unk1: 0x13A700000,
376    }),
377    Some(DmemConfig {
378        name: "BC16 kratos",
379        game_size: NonZero::new(0x148000000).unwrap(),
380        fmem_max: NonZero::new(0x40000000).unwrap(),
381        mini_size: 0,
382        mini_shared: false,
383        vsh_size: NonZero::new(0x1CA00000).unwrap(),
384        unk1: 0x1F3C00000,
385    }),
386    Some(DmemConfig {
387        name: "BC16 release",
388        game_size: NonZero::new(0x148000000).unwrap(),
389        fmem_max: NonZero::new(0x40000000).unwrap(),
390        mini_size: 0x30000000,
391        mini_shared: false,
392        vsh_size: NonZero::new(0x1C200000).unwrap(),
393        unk1: 0x1FF600000,
394    }),
395    Some(DmemConfig {
396        name: "BC16 CS",
397        game_size: NonZero::new(0x324000000).unwrap(),
398        fmem_max: NonZero::new(0x4000000).unwrap(),
399        mini_size: 0x58000000,
400        mini_shared: false,
401        vsh_size: NonZero::new(0x28000000).unwrap(),
402        unk1: 0,
403    }),
404    None,
405    None,
406    Some(DmemConfig {
407        name: "GL8 normal",
408        game_size: NonZero::new(0x170000000).unwrap(),
409        fmem_max: NonZero::new(0x40000000).unwrap(),
410        mini_size: 0x70000000,
411        mini_shared: true,
412        vsh_size: NonZero::new(0x27C00000).unwrap(),
413        unk1: 0,
414    }),
415    None,
416    None,
417    Some(DmemConfig {
418        name: "GL8 kratos",
419        game_size: NonZero::new(0x170000000).unwrap(),
420        fmem_max: NonZero::new(0x40000000).unwrap(),
421        mini_size: 0x70000000,
422        mini_shared: true,
423        vsh_size: NonZero::new(0x27C00000).unwrap(),
424        unk1: 0xA00000,
425    }),
426    Some(DmemConfig {
427        name: "GL8 release",
428        game_size: NonZero::new(0x170000000).unwrap(),
429        fmem_max: NonZero::new(0x40000000).unwrap(),
430        mini_size: 0x70000000,
431        mini_shared: true,
432        vsh_size: NonZero::new(0x28000000).unwrap(),
433        unk1: 0xA00000,
434    }),
435];