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::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    qa: bool,
24    qa_flags: &'static QaFlags,
25    env_vars: Box<[&'static str]>, // kenvp
26}
27
28impl Config {
29    pub fn new(src: &'static ::config::Config) -> Arc<Self> {
30        let env_vars = Self::load_env(src);
31
32        Arc::new(Self {
33            max_cpu: src.max_cpu,
34            qa: src.qa,
35            qa_flags: &src.qa_flags,
36            env_vars,
37        })
38    }
39
40    pub fn max_cpu(&self) -> NonZero<usize> {
41        self.max_cpu
42    }
43
44    /// See `getenv` on the Orbis for a reference.
45    ///
46    /// # Reference offsets
47    /// | Version | Offset |
48    /// |---------|--------|
49    /// |PS4 11.00|0x39D0A0|
50    pub fn env(&self, name: &str) -> Option<&'static str> {
51        for &v in &self.env_vars {
52            // Check prefix.
53            let v = match v.strip_prefix(name) {
54                Some(v) => v,
55                None => continue,
56            };
57
58            // Check if '=' follow the name.
59            let mut iter = v.chars();
60
61            if iter.next().is_some_and(|c| c == '=') {
62                return Some(iter.as_str());
63            }
64        }
65
66        None
67    }
68
69    /// See `sceSblRcMgrIsAllowDisablingAslr` on the Orbis for a reference.
70    ///
71    /// # Reference offsets
72    /// | Version | Offset |
73    /// |---------|--------|
74    /// |PS4 11.00|0x3CA8F0|
75    pub fn is_allow_disabling_aslr(&self) -> bool {
76        self.qa && self.qa_flags.internal_dev()
77    }
78
79    /// See `sceKernelCheckDipsw` on the Orbis for a reference.
80    ///
81    /// # Reference offsets
82    /// | Version | Offset |
83    /// |---------|--------|
84    /// |PS4 11.00|0x654D70|
85    pub fn dipsw(&self, _: Dipsw) -> bool {
86        todo!()
87    }
88
89    /// See `init_dynamic_kenv` on the Orbis for a reference.
90    ///
91    /// # Reference offsets
92    /// | Version | Offset |
93    /// |---------|--------|
94    /// |PS4 11.00|0x39DC90|
95    fn load_env(config: &'static ::config::Config) -> Box<[&'static str]> {
96        // Our implementation a bit different here. On Orbis they required the last entry to be an
97        // empty string but we don't.
98        let mut list = Vec::with_capacity(0x1000);
99        let mut rem = config.env_vars.as_slice();
100        let mut n = -1;
101
102        while !rem.is_empty() {
103            // We don't use https://crates.io/crates/memchr because it is likely to be useless since
104            // we don't have access to SIMD instructions in the kernel.
105            let v = match rem.iter().position(|&b| b == 0) {
106                Some(i) => {
107                    let v = &rem[..i];
108                    rem = &rem[(i + 1)..];
109                    v
110                }
111                None => core::mem::replace(&mut rem, b""),
112            };
113
114            // On Orbis they allow the first entry to be an empty string but I don't think it is
115            // intended behavior.
116            if v.is_empty() {
117                break;
118            }
119
120            n += 1;
121
122            // We required string to be UTF-8 while the Orbis does not.
123            let v = match core::str::from_utf8(v) {
124                Ok(v) => v,
125                Err(_) => {
126                    warn!("Ignoring non-UTF-8 kenv string #{n}.");
127                    continue;
128                }
129            };
130
131            if (v.len() + 1) >= 259 {
132                warn!("Too long kenv string, ignoring {v}.");
133                continue;
134            } else if list.len() > 511 {
135                warn!("Too many kenv strings, ignoring {v}.");
136                continue;
137            }
138
139            list.push(v);
140        }
141
142        list.into_boxed_slice()
143    }
144}
145
146#[elf_note(section = ".note.obkrnl.page-size", name = "obkrnl", ty = 0)]
147static NOTE_PAGE_SIZE: [u8; size_of::<usize>()] = PAGE_SIZE.get().to_ne_bytes();