xref: /DragonOS/kernel/src/init/cmdline.rs (revision 4dd4856f933be0b4624c7f7ffa9e3d0c8c218873)
1 use core::{
2     str,
3     sync::atomic::{fence, Ordering},
4 };
5 
6 use alloc::{ffi::CString, vec::Vec};
7 
8 use crate::libs::spinlock::SpinLock;
9 
10 use super::boot_params;
11 
12 #[::linkme::distributed_slice]
13 pub static KCMDLINE_PARAM_EARLY_KV: [KernelCmdlineParameter] = [..];
14 
15 #[::linkme::distributed_slice]
16 pub static KCMDLINE_PARAM_KV: [KernelCmdlineParameter] = [..];
17 
18 #[::linkme::distributed_slice]
19 pub static KCMDLINE_PARAM_ARG: [KernelCmdlineParameter] = [..];
20 
21 static KERNEL_CMDLINE_PARAM_MANAGER: KernelCmdlineManager = KernelCmdlineManager::new();
22 
23 #[inline(always)]
24 pub fn kenrel_cmdline_param_manager() -> &'static KernelCmdlineManager {
25     &KERNEL_CMDLINE_PARAM_MANAGER
26 }
27 
28 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
29 pub enum KCmdlineParamType {
30     /// bool类型参数
31     Arg,
32     /// key-value类型参数
33     KV,
34     /// 内存管理初始化之前的KV参数
35     EarlyKV,
36 }
37 
38 pub struct KernelCmdlineParamBuilder {
39     name: &'static str,
40     ty: KCmdlineParamType,
41     default_str: &'static str,
42     default_bool: bool,
43     inv: bool,
44 }
45 
46 impl KernelCmdlineParamBuilder {
47     pub const fn new(name: &'static str, ty: KCmdlineParamType) -> Self {
48         Self {
49             name,
50             ty,
51             default_str: "",
52             default_bool: false,
53             inv: false,
54         }
55     }
56 
57     pub const fn default_str(mut self, default_str: &'static str) -> Self {
58         self.default_str = default_str;
59         self
60     }
61 
62     pub const fn default_bool(mut self, default_bool: bool) -> Self {
63         self.default_bool = default_bool;
64         self
65     }
66 
67     pub const fn inv(mut self, inv: bool) -> Self {
68         self.inv = inv;
69         self
70     }
71 
72     pub const fn build_early_kv(self) -> Option<KernelCmdlineEarlyKV> {
73         if matches!(self.ty, KCmdlineParamType::EarlyKV) {
74             Some(KernelCmdlineEarlyKV {
75                 name: self.name,
76                 value: [0; KernelCmdlineEarlyKV::VALUE_MAX_LEN],
77                 index: 0,
78                 initialized: false,
79                 default: self.default_str,
80             })
81         } else {
82             None
83         }
84     }
85 
86     pub const fn build(self) -> Option<KernelCmdlineParameter> {
87         match self.ty {
88             KCmdlineParamType::Arg => Some(KernelCmdlineParameter::Arg(KernelCmdlineArg {
89                 name: self.name,
90                 value: self.default_bool,
91                 initialized: false,
92                 inv: self.inv,
93                 default: self.default_bool,
94             })),
95             KCmdlineParamType::KV => Some(KernelCmdlineParameter::KV(KernelCmdlineKV {
96                 name: self.name,
97                 value: None,
98                 initialized: false,
99                 default: self.default_str,
100             })),
101             _ => None,
102         }
103     }
104 }
105 
106 #[allow(dead_code)]
107 pub enum KernelCmdlineParameter {
108     Arg(KernelCmdlineArg),
109     KV(KernelCmdlineKV),
110     EarlyKV(&'static KernelCmdlineEarlyKV),
111 }
112 
113 impl KernelCmdlineParameter {
114     pub fn name(&self) -> &str {
115         match self {
116             KernelCmdlineParameter::Arg(v) => v.name,
117             KernelCmdlineParameter::KV(v) => v.name,
118             KernelCmdlineParameter::EarlyKV(v) => v.name,
119         }
120     }
121 
122     /// 获取bool类型参数的值
123     pub fn value_bool(&self) -> Option<bool> {
124         match self {
125             KernelCmdlineParameter::Arg(v) => Some(v.value()),
126             _ => None,
127         }
128     }
129 
130     /// 获取key-value类型参数的值
131     pub fn value_str(&self) -> Option<&str> {
132         match self {
133             KernelCmdlineParameter::Arg(_) => None,
134             KernelCmdlineParameter::KV(v) => v
135                 .value
136                 .as_ref()
137                 .and_then(|v| str::from_utf8(v.as_bytes()).ok()),
138             KernelCmdlineParameter::EarlyKV(v) => v.value_str(),
139         }
140     }
141 
142     pub fn is_arg(&self) -> bool {
143         matches!(self, KernelCmdlineParameter::Arg(_))
144     }
145 
146     pub fn is_kv(&self) -> bool {
147         matches!(self, KernelCmdlineParameter::KV(_))
148     }
149 
150     pub fn is_early_kv(&self) -> bool {
151         matches!(self, KernelCmdlineParameter::EarlyKV(_))
152     }
153 
154     /// 强行获取可变引用
155     ///
156     /// # Safety
157     ///
158     /// 只能在内核初始化阶段pid0使用!
159     #[allow(clippy::mut_from_ref)]
160     unsafe fn force_mut(&self) -> &mut Self {
161         let p = self as *const Self as *mut Self;
162         p.as_mut().unwrap()
163     }
164 }
165 
166 #[derive(Debug)]
167 pub struct KernelCmdlineArg {
168     name: &'static str,
169     value: bool,
170     initialized: bool,
171     /// 是否反转
172     inv: bool,
173     default: bool,
174 }
175 
176 impl KernelCmdlineArg {
177     pub fn value(&self) -> bool {
178         volatile_read!(self.value)
179     }
180 }
181 
182 pub struct KernelCmdlineKV {
183     name: &'static str,
184     value: Option<CString>,
185     initialized: bool,
186     default: &'static str,
187 }
188 
189 /// 在内存管理初始化之前的KV参数
190 pub struct KernelCmdlineEarlyKV {
191     name: &'static str,
192     value: [u8; Self::VALUE_MAX_LEN],
193     index: usize,
194     initialized: bool,
195     default: &'static str,
196 }
197 
198 impl KernelCmdlineEarlyKV {
199     pub const VALUE_MAX_LEN: usize = 256;
200 
201     pub fn value(&self) -> &[u8] {
202         &self.value[..self.index]
203     }
204 
205     pub fn value_str(&self) -> Option<&str> {
206         core::str::from_utf8(&self.value[..self.index]).ok()
207     }
208 
209     /// 强行获取可变引用
210     ///
211     /// # Safety
212     ///
213     /// 只能在内核初始化阶段pid0使用!
214     #[allow(clippy::mut_from_ref)]
215     unsafe fn force_mut(&self) -> &mut Self {
216         let p = self as *const Self as *mut Self;
217         p.as_mut().unwrap()
218     }
219 }
220 
221 pub struct KernelCmdlineManager {
222     inner: SpinLock<InnerKernelCmdlineManager>,
223 }
224 
225 pub(super) struct InnerKernelCmdlineManager {
226     /// init进程的路径
227     init_path: Option<CString>,
228     init_args: Vec<CString>,
229     init_envs: Vec<CString>,
230 }
231 
232 impl KernelCmdlineManager {
233     const fn new() -> Self {
234         Self {
235             inner: SpinLock::new(InnerKernelCmdlineManager {
236                 init_path: None,
237                 init_args: Vec::new(),
238                 init_envs: Vec::new(),
239             }),
240         }
241     }
242 
243     pub(super) fn init_proc_path(&self) -> Option<CString> {
244         self.inner.lock().init_path.clone()
245     }
246 
247     pub(super) fn init_proc_args(&self) -> Vec<CString> {
248         self.inner.lock().init_args.clone()
249     }
250 
251     pub(super) fn init_proc_envs(&self) -> Vec<CString> {
252         self.inner.lock().init_envs.clone()
253     }
254 
255     /// 在内存管理初始化之前设置部分参数
256     pub fn early_init(&self) {
257         let boot_params = boot_params().read();
258 
259         for argument in self.split_args(boot_params.boot_cmdline_str()) {
260             let (node, option, value) = match self.split_arg(argument) {
261                 Some(v) => v,
262                 None => continue,
263             };
264             // 查找参数
265             if let Some(param) = self.find_param(node, option, KCmdlineParamType::EarlyKV) {
266                 let param = unsafe { param.force_mut() };
267                 match param {
268                     KernelCmdlineParameter::EarlyKV(p) => {
269                         let p = unsafe { p.force_mut() };
270                         if let Some(value) = value {
271                             let value = value.as_bytes();
272                             let len = value.len().min(KernelCmdlineEarlyKV::VALUE_MAX_LEN);
273                             p.value[..len].copy_from_slice(&value[..len]);
274                             p.index = len;
275                         }
276                         p.initialized = true;
277                     }
278                     _ => unreachable!(),
279                 }
280                 fence(Ordering::SeqCst);
281             }
282         }
283 
284         // 初始化默认值
285         KCMDLINE_PARAM_EARLY_KV.iter().for_each(|x| {
286             let x = unsafe { x.force_mut() };
287             if let KernelCmdlineParameter::EarlyKV(v) = x {
288                 if !v.initialized {
289                     let v = unsafe { v.force_mut() };
290                     let len = v.default.len().min(KernelCmdlineEarlyKV::VALUE_MAX_LEN);
291                     v.value[..len].copy_from_slice(v.default.as_bytes());
292                     v.index = len;
293                     v.initialized = true;
294                 }
295             }
296         });
297     }
298 
299     /// 在内存管理初始化之后设置命令行参数
300     pub fn init(&self) {
301         let mut inner = self.inner.lock();
302         let boot_params = boot_params().read();
303         // `--`以后的参数都是init进程的参数
304         let mut kernel_cmdline_end = false;
305         for argument in self.split_args(boot_params.boot_cmdline_str()) {
306             if kernel_cmdline_end {
307                 if inner.init_path.is_none() {
308                     panic!("cmdline: init proc path is not set while init proc args are set");
309                 }
310                 if !argument.is_empty() {
311                     inner.init_args.push(CString::new(argument).unwrap());
312                 }
313                 continue;
314             }
315 
316             if argument == "--" {
317                 kernel_cmdline_end = true;
318                 continue;
319             }
320 
321             let (node, option, value) = match self.split_arg(argument) {
322                 Some(v) => v,
323                 None => continue,
324             };
325             if option == "init" && value.is_some() {
326                 if inner.init_path.is_some() {
327                     panic!("cmdline: init proc path is set twice");
328                 }
329                 inner.init_path = Some(CString::new(value.unwrap()).unwrap());
330                 continue;
331             }
332             // log::debug!(
333             //     "cmdline: node: {:?}, option: {:?}, value: {:?}",
334             //     node,
335             //     option,
336             //     value
337             // );
338             if let Some(param) = self.find_param(node, option, KCmdlineParamType::KV) {
339                 let param = unsafe { param.force_mut() };
340                 match param {
341                     KernelCmdlineParameter::KV(p) => {
342                         if p.value.is_some() {
343                             log::warn!("cmdline: parameter {} is set twice", p.name);
344                             continue;
345                         }
346                         p.value = Some(CString::new(value.unwrap()).unwrap());
347                         p.initialized = true;
348                     }
349                     _ => unreachable!(),
350                 }
351                 fence(Ordering::SeqCst);
352             } else if let Some(param) = self.find_param(node, option, KCmdlineParamType::Arg) {
353                 let param = unsafe { param.force_mut() };
354                 match param {
355                     KernelCmdlineParameter::Arg(p) => {
356                         if p.initialized {
357                             log::warn!("cmdline: parameter {} is set twice", p.name);
358                             continue;
359                         }
360                         p.value = !p.inv;
361                         p.initialized = true;
362                     }
363                     _ => unreachable!(),
364                 }
365                 fence(Ordering::SeqCst);
366             } else if node.is_none() {
367                 if let Some(val) = value {
368                     inner
369                         .init_envs
370                         .push(CString::new(format!("{}={}", option, val)).unwrap());
371                 } else if !option.is_empty() {
372                     inner.init_args.push(CString::new(option).unwrap());
373                 }
374             }
375         }
376         fence(Ordering::SeqCst);
377         // 初始化默认值
378         self.default_initialize();
379         fence(Ordering::SeqCst);
380     }
381 
382     fn default_initialize(&self) {
383         KCMDLINE_PARAM_ARG.iter().for_each(|x| {
384             let x = unsafe { x.force_mut() };
385             if let KernelCmdlineParameter::Arg(v) = x {
386                 if !v.initialized {
387                     v.value = v.default;
388                     v.initialized = true;
389                 }
390             }
391             fence(Ordering::SeqCst);
392         });
393 
394         KCMDLINE_PARAM_KV.iter().for_each(|x| {
395             let x = unsafe { x.force_mut() };
396             if let KernelCmdlineParameter::KV(v) = x {
397                 if !v.initialized {
398                     v.value = Some(CString::new(v.default).unwrap());
399                     v.initialized = true;
400                 }
401             }
402             fence(Ordering::SeqCst);
403         });
404     }
405 
406     fn find_param(
407         &self,
408         node: Option<&str>,
409         option: &str,
410         param_typ: KCmdlineParamType,
411     ) -> Option<&KernelCmdlineParameter> {
412         let list = match param_typ {
413             KCmdlineParamType::Arg => &KCMDLINE_PARAM_ARG,
414             KCmdlineParamType::KV => &KCMDLINE_PARAM_KV,
415             KCmdlineParamType::EarlyKV => &KCMDLINE_PARAM_EARLY_KV,
416         };
417 
418         list.iter().find(|x| {
419             let name = x.name();
420             if let Some(node) = node {
421                 // 加1是因为有一个点号
422                 name.len() == (node.len() + option.len() + 1)
423                     && name.starts_with(node)
424                     && name[node.len() + 1..].starts_with(option)
425             } else {
426                 name == option
427             }
428         })
429     }
430 
431     fn split_arg<'a>(&self, arg: &'a str) -> Option<(Option<&'a str>, &'a str, Option<&'a str>)> {
432         let mut iter = arg.splitn(2, '=');
433         let key = iter.next().unwrap();
434         let value = iter.next();
435         let value = value.map(|v| v.trim());
436         if value.is_some() && iter.next().is_some() {
437             log::warn!("cmdline: invalid argument: {}", arg);
438             return None;
439         }
440 
441         let mut iter = key.splitn(2, '.');
442         let v1 = iter.next().map(|v| v.trim());
443         let v2 = iter.next().map(|v| v.trim());
444         let v3 = iter.next().map(|v| v.trim());
445         let v = [v1, v2, v3];
446 
447         let mut key_split_len = 0;
448         v.iter().for_each(|x| {
449             if x.is_some() {
450                 key_split_len += 1
451             }
452         });
453 
454         let (node, option) = match key_split_len {
455             1 => (None, v[0].unwrap()),
456             2 => (Some(v[0].unwrap()), v[1].unwrap()),
457             _ => {
458                 log::warn!("cmdline: invalid argument: {}", arg);
459                 return None;
460             }
461         };
462 
463         Some((node, option, value))
464     }
465 
466     fn split_args<'a>(&self, cmdline: &'a str) -> impl Iterator<Item = &'a str> {
467         // 是否在引号内
468         let mut in_quote = false;
469         cmdline.split(move |c: char| {
470             if c == '"' {
471                 in_quote = !in_quote;
472             }
473             !in_quote && c.is_whitespace()
474         })
475     }
476 }
477