obkrnl/config/
mod.rs

1pub use self::arch::*;
2pub use self::dipsw::*;
3
4use alloc::boxed::Box;
5use alloc::sync::Arc;
6use alloc::vec::Vec;
7use config::{ConsoleId, ProductId, QaFlags};
8use core::num::NonZero;
9use krt::warn;
10use macros::elf_note;
11
12#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
13#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
14mod arch;
15mod dipsw;
16
17pub const PAGE_SIZE: NonZero<usize> = NonZero::new(1 << PAGE_SHIFT).unwrap();
18pub const PAGE_MASK: NonZero<usize> = NonZero::new(PAGE_SIZE.get() - 1).unwrap();
19
20/// Runtime configurations for the kernel populated from [`config::Config`].
21pub struct Config {
22    max_cpu: NonZero<usize>,
23    unknown_dmem1: u8, // TODO: Figure out a correct name.
24    idps: &'static ConsoleId,
25    qa: bool,
26    qa_flags: &'static QaFlags,
27    env_vars: Box<[&'static str]>, // kenvp
28}
29
30impl Config {
31    pub fn new(src: &'static ::config::Config) -> Arc<Self> {
32        let env_vars = Self::load_env(src);
33
34        Arc::new(Self {
35            max_cpu: src.max_cpu,
36            unknown_dmem1: 0,
37            idps: &src.idps,
38            qa: src.qa,
39            qa_flags: &src.qa_flags,
40            env_vars,
41        })
42    }
43
44    pub fn max_cpu(&self) -> NonZero<usize> {
45        self.max_cpu
46    }
47
48    pub fn unknown_dmem1(&self) -> u8 {
49        self.unknown_dmem1
50    }
51
52    pub fn idps(&self) -> &'static ConsoleId {
53        self.idps
54    }
55
56    /// See `getenv` on the Orbis for a reference.
57    ///
58    /// # Reference offsets
59    /// | Version | Offset |
60    /// |---------|--------|
61    /// |PS4 11.00|0x39D0A0|
62    pub fn env(&self, name: &str) -> Option<&'static str> {
63        for &v in &self.env_vars {
64            // Check prefix.
65            let v = match v.strip_prefix(name) {
66                Some(v) => v,
67                None => continue,
68            };
69
70            // Check if '=' follow the name.
71            let mut iter = v.chars();
72
73            if iter.next().is_some_and(|c| c == '=') {
74                return Some(iter.as_str());
75            }
76        }
77
78        None
79    }
80
81    /// See `sceSblRcMgrIsAllowDisablingAslr` on the Orbis for a reference.
82    ///
83    /// # Reference offsets
84    /// | Version | Offset |
85    /// |---------|--------|
86    /// |PS4 11.00|0x3CA8F0|
87    pub fn is_allow_disabling_aslr(&self) -> bool {
88        self.qa && self.qa_flags.internal_dev()
89    }
90
91    /// See `sceSblAIMgrIsDevKit` on the Orbis for a reference.
92    ///
93    /// # Reference offsets
94    /// | Version | Offset |
95    /// |---------|--------|
96    /// |PS4 11.00|0x078F50|
97    pub fn is_devkit(&self) -> bool {
98        self.idps.product == ProductId::DEVKIT
99    }
100
101    /// See `sceSblAIMgrIsTestKit` on the Orbis for a reference.
102    ///
103    /// # Reference offsets
104    /// | Version | Offset |
105    /// |---------|--------|
106    /// |PS4 11.00|0x0790A0|
107    pub fn is_testkit(&self) -> bool {
108        self.idps.product == ProductId::TESTKIT
109    }
110
111    /// See `sceKernelCheckDipsw` on the Orbis for a reference.
112    ///
113    /// # Reference offsets
114    /// | Version | Offset |
115    /// |---------|--------|
116    /// |PS4 11.00|0x654D70|
117    pub fn dipsw(&self, _: Dipsw) -> bool {
118        if !self.is_testkit() {
119            if !self.is_devkit() {
120                return false;
121            }
122        } else {
123            todo!()
124        }
125
126        todo!()
127    }
128
129    /// See `init_dynamic_kenv` on the Orbis for a reference.
130    ///
131    /// # Reference offsets
132    /// | Version | Offset |
133    /// |---------|--------|
134    /// |PS4 11.00|0x39DC90|
135    fn load_env(config: &'static ::config::Config) -> Box<[&'static str]> {
136        // Our implementation a bit different here. On Orbis they required the last entry to be an
137        // empty string but we don't.
138        let mut list = Vec::with_capacity(0x1000);
139        let mut rem = config.env_vars.as_slice();
140        let mut n = -1;
141
142        while !rem.is_empty() {
143            // We don't use https://crates.io/crates/memchr because it is likely to be useless since
144            // we don't have access to SIMD instructions in the kernel.
145            let v = match rem.iter().position(|&b| b == 0) {
146                Some(i) => {
147                    let v = &rem[..i];
148                    rem = &rem[(i + 1)..];
149                    v
150                }
151                None => core::mem::replace(&mut rem, b""),
152            };
153
154            // On Orbis they allow the first entry to be an empty string but I don't think it is
155            // intended behavior.
156            if v.is_empty() {
157                break;
158            }
159
160            n += 1;
161
162            // We required string to be UTF-8 while the Orbis does not.
163            let v = match core::str::from_utf8(v) {
164                Ok(v) => v,
165                Err(_) => {
166                    warn!("Ignoring non-UTF-8 kenv string #{n}.");
167                    continue;
168                }
169            };
170
171            if (v.len() + 1) >= 259 {
172                warn!("Too long kenv string, ignoring {v}.");
173                continue;
174            } else if list.len() > 511 {
175                warn!("Too many kenv strings, ignoring {v}.");
176                continue;
177            }
178
179            list.push(v);
180        }
181
182        list.into_boxed_slice()
183    }
184}
185
186#[elf_note(section = ".note.obkrnl.page-size", name = "obkrnl", ty = 0)]
187static NOTE_PAGE_SIZE: [u8; size_of::<usize>()] = PAGE_SIZE.get().to_ne_bytes();