1use crate::bpf::EbpfState;
6use crate::device::remote_block_device::RemoteBlockDeviceRegistry;
7use crate::device::{DeviceMode, DeviceRegistry};
8use crate::execution::CrashReporter;
9use crate::mm::{FutexTable, MappingSummary, MlockPinFlavor, SharedFutexKey};
10use crate::power::SuspendResumeManagerHandle;
11use crate::security::{self, AuditLogger};
12use crate::task::container_namespace::ContainerNamespace;
13use crate::task::limits::SystemLimits;
14use crate::task::memory_attribution::MemoryAttributionManager;
15use crate::task::net::NetstackDevices;
16use crate::task::tracing::PidToKoidMap;
17use crate::task::{
18 AbstractUnixSocketNamespace, AbstractVsockSocketNamespace, CurrentTask, DelayedReleaser,
19 HrTimerManager, HrTimerManagerHandle, IpTables, KernelCgroups, KernelStats, KernelThreads,
20 PidTable, SchedulerManager, StopState, Syslog, ThreadGroup, UtsNamespace, UtsNamespaceHandle,
21};
22use crate::vdso::vdso_loader::Vdso;
23use crate::vfs::fs_args::MountParams;
24use crate::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
25use crate::vfs::socket::{
26 GenericMessage, GenericNetlink, NetlinkAccessControl, NetlinkContextImpl,
27 NetlinkToClientSender, SocketAddress, SocketTokensStore,
28};
29use crate::vfs::{FileOps, FsNodeHandle, FsString, Mounts, NamespaceNode};
30use bstr::{BString, ByteSlice};
31use devicetree::types::Devicetree;
32use expando::Expando;
33use fidl::endpoints::{
34 ClientEnd, ControlHandle, DiscoverableProtocolMarker, ProtocolMarker, create_endpoints,
35};
36use fidl_fuchsia_component_runner::{ComponentControllerControlHandle, ComponentStopInfo};
37use fidl_fuchsia_feedback::CrashReporterProxy;
38use fidl_fuchsia_time_external::AdjustSynchronousProxy;
39use fuchsia_inspect::ArrayProperty;
40use futures::FutureExt;
41use netlink::interfaces::InterfacesHandler;
42use netlink::{NETLINK_LOG_TAG, Netlink};
43use once_cell::sync::OnceCell;
44use starnix_lifecycle::{AtomicU32Counter, AtomicU64Counter};
45use starnix_logging::{log_debug, log_error, log_info, log_warn};
46use starnix_sync::{
47 FileOpsCore, KernelSwapFiles, LockEqualOrBefore, Locked, Mutex, OrderedMutex, RwLock,
48};
49use starnix_types::ownership::TempRef;
50use starnix_uapi::device_type::DeviceType;
51use starnix_uapi::errors::{Errno, errno};
52use starnix_uapi::open_flags::OpenFlags;
53use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
54use std::borrow::Cow;
55use std::collections::{HashMap, HashSet};
56use std::num::NonZeroU64;
57use std::path::PathBuf;
58use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU16, Ordering};
59use std::sync::{Arc, OnceLock, Weak};
60use zx::{AsHandleRef, CpuFeatureFlags};
61use {
62 fidl_fuchsia_io as fio, fidl_fuchsia_memory_attribution as fattribution,
63 fuchsia_async as fasync,
64};
65
66#[derive(Debug, Default, Clone)]
67pub struct KernelFeatures {
68 pub bpf_v2: bool,
69
70 pub enable_suid: bool,
77
78 pub io_uring: bool,
82
83 pub error_on_failed_reboot: bool,
86
87 pub default_seclabel: Option<String>,
91
92 pub selinux_test_suite: bool,
97
98 pub default_ns_mount_options: Option<HashMap<String, String>>,
103
104 pub default_uid: u32,
108
109 pub mlock_always_onfault: bool,
111
112 pub mlock_pin_flavor: MlockPinFlavor,
114
115 pub crash_report_throttling: bool,
117
118 pub wifi: bool,
120
121 pub cached_zx_map_info_bytes: u32,
123}
124
125impl KernelFeatures {
126 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
130 if let Some(all_options) = &self.default_ns_mount_options {
131 if let Some(options) = all_options.get(ns_path) {
132 return MountParams::parse(options.as_bytes().into());
133 }
134 }
135 Ok(MountParams::default())
136 }
137}
138
139pub struct ArgNameAndValue<'a> {
141 pub name: &'a str,
142 pub value: Option<&'a str>,
143}
144
145pub struct Kernel {
155 pub weak_self: Weak<Kernel>,
157
158 pub kthreads: KernelThreads,
160
161 pub features: KernelFeatures,
163
164 pub pids: RwLock<PidTable>,
166
167 pub pid_to_koid_mapping: Arc<RwLock<Option<PidToKoidMap>>>,
169
170 pub expando: Expando,
175
176 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
182
183 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
185
186 pub cmdline: BString,
188
189 pub device_tree: Option<Devicetree>,
190
191 pub security_state: security::KernelState,
193
194 pub device_registry: DeviceRegistry,
196
197 pub container_namespace: ContainerNamespace,
201
202 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
204
205 iptables: OnceLock<IpTables>,
207
208 pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
210
211 pub root_uts_ns: UtsNamespaceHandle,
216
217 pub vdso: Vdso,
219
220 pub vdso_arch32: Option<Vdso>,
224
225 pub netstack_devices: Arc<NetstackDevices>,
228
229 pub swap_files: OrderedMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
233
234 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
236
237 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
239
240 pub inspect_node: fuchsia_inspect::Node,
242
243 pub actions_logged: AtomicU16,
250
251 pub suspend_resume_manager: SuspendResumeManagerHandle,
253
254 pub next_mount_id: AtomicU64Counter,
256 pub next_peer_group_id: AtomicU64Counter,
257 pub next_namespace_id: AtomicU64Counter,
258
259 pub next_file_object_id: AtomicU64Counter,
261
262 pub next_inotify_cookie: AtomicU32Counter,
264
265 pub ptrace_scope: AtomicU8,
267
268 pub build_version: OnceCell<String>,
270
271 pub stats: Arc<KernelStats>,
272
273 pub system_limits: SystemLimits,
275
276 pub delayed_releaser: DelayedReleaser,
280
281 pub scheduler: SchedulerManager,
283
284 pub syslog: Syslog,
286
287 pub mounts: Mounts,
289
290 pub hrtimer_manager: HrTimerManagerHandle,
292
293 pub memory_attribution_manager: MemoryAttributionManager,
295
296 pub crash_reporter: CrashReporter,
298
299 pub procfs_device_tree_setup: Vec<fn(&SimpleDirectoryMutator)>,
302
303 shutting_down: AtomicBool,
305
306 pub restrict_dmesg: AtomicBool,
309
310 pub disable_unprivileged_bpf: AtomicU8,
315
316 pub container_control_handle: Mutex<Option<ComponentControllerControlHandle>>,
318
319 pub ebpf_state: EbpfState,
321
322 pub cgroups: KernelCgroups,
324
325 pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
328
329 pub socket_tokens_store: SocketTokensStore,
331
332 pub hwcaps: HwCaps,
334}
335
336#[derive(Debug, Clone, Copy, Default)]
338pub struct HwCap {
339 pub hwcap: u32,
341 pub hwcap2: u32,
343}
344
345#[derive(Debug, Clone, Copy, Default)]
347pub struct HwCaps {
348 #[cfg(target_arch = "aarch64")]
350 pub arch32: HwCap,
351 pub arch64: HwCap,
353}
354
355struct InterfacesHandlerImpl(Weak<Kernel>);
362
363impl InterfacesHandlerImpl {
364 fn kernel(&self) -> Option<Arc<Kernel>> {
365 self.0.upgrade()
366 }
367}
368
369impl InterfacesHandler for InterfacesHandlerImpl {
370 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
371 if let Some(kernel) = self.kernel() {
372 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
373 }
374 }
375
376 fn handle_deleted_link(&mut self, name: &str) {
377 if let Some(kernel) = self.kernel() {
378 kernel.netstack_devices.remove_device(&kernel, name.into());
379 }
380 }
381
382 fn handle_idle_event(&mut self) {
383 let Some(kernel) = self.kernel() else {
384 log_error!("kernel went away while netlink is initializing");
385 return;
386 };
387 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
388 if initialized.swap(true, Ordering::SeqCst) {
389 log_error!("netlink initial devices should only be reported once");
390 return;
391 }
392 wq.notify_all()
393 }
394}
395
396impl Kernel {
397 pub fn new(
398 cmdline: BString,
399 features: KernelFeatures,
400 system_limits: SystemLimits,
401 container_namespace: ContainerNamespace,
402 scheduler: SchedulerManager,
403 crash_reporter_proxy: Option<CrashReporterProxy>,
404 inspect_node: fuchsia_inspect::Node,
405 security_state: security::KernelState,
406 procfs_device_tree_setup: Vec<fn(&SimpleDirectoryMutator)>,
407 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
408 device_tree: Option<Devicetree>,
409 ) -> Result<Arc<Kernel>, zx::Status> {
410 let unix_address_maker =
411 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
412 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
413 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
414 });
415
416 let crash_reporter = CrashReporter::new(
417 &inspect_node,
418 crash_reporter_proxy,
419 zx::Duration::from_minutes(8),
420 features.crash_report_throttling,
421 );
422 let hrtimer_manager = HrTimerManager::new(&inspect_node);
423
424 let cpu_feature_flags =
425 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
426 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
427 CpuFeatureFlags::empty()
428 });
429 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
430
431 let this = Arc::new_cyclic(|kernel| Kernel {
432 weak_self: kernel.clone(),
433 kthreads: KernelThreads::new(kernel.clone()),
434 features,
435 pids: Default::default(),
436 pid_to_koid_mapping: Arc::new(RwLock::new(None)),
437 expando: Default::default(),
438 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
439 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
440 vsock_address_maker,
441 ),
442 cmdline,
443 device_tree,
444 security_state,
445 device_registry: Default::default(),
446 container_namespace,
447 remote_block_device_registry: Default::default(),
448 iptables: OnceLock::new(),
449 shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
450 root_uts_ns: Arc::new(RwLock::new(UtsNamespace::default())),
451 vdso: Vdso::new(),
452 vdso_arch32: Vdso::new_arch32(),
453 netstack_devices: Arc::default(),
454 swap_files: Default::default(),
455 generic_netlink: OnceLock::new(),
456 network_netlink: OnceLock::new(),
457 inspect_node,
458 actions_logged: AtomicU16::new(0),
459 suspend_resume_manager: Default::default(),
460 next_mount_id: AtomicU64Counter::new(1),
461 next_peer_group_id: AtomicU64Counter::new(1),
462 next_namespace_id: AtomicU64Counter::new(1),
463 next_inotify_cookie: AtomicU32Counter::new(1),
464 next_file_object_id: Default::default(),
465 system_limits,
466 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
468 disable_unprivileged_bpf: AtomicU8::new(0), build_version: OnceCell::new(),
470 stats: Arc::new(KernelStats::default()),
471 delayed_releaser: Default::default(),
472 scheduler,
473 syslog: Default::default(),
474 mounts: Mounts::new(),
475 hrtimer_manager,
476 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
477 crash_reporter,
478 procfs_device_tree_setup,
479 shutting_down: AtomicBool::new(false),
480 container_control_handle: Mutex::new(None),
481 ebpf_state: Default::default(),
482 cgroups: Default::default(),
483 time_adjustment_proxy,
484 socket_tokens_store: Default::default(),
485 hwcaps,
486 });
487
488 this.device_registry.objects.init(&mut this.kthreads.unlocked_for_async(), &this);
492
493 let kernel = Arc::downgrade(&this);
496 this.inspect_node.record_lazy_child("thread_groups", move || {
497 if let Some(kernel) = kernel.upgrade() {
498 let inspector = kernel.get_thread_groups_inspect();
499 async move { Ok(inspector) }.boxed()
500 } else {
501 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
502 }
503 });
504
505 Ok(this)
506 }
507
508 pub fn shut_down(self: &Arc<Self>) {
511 self.kthreads.spawn_future({
514 let kernel = self.clone();
515 async move || {
516 kernel.run_shutdown().await;
517 }
518 });
519 }
520
521 async fn run_shutdown(&self) {
541 const INIT_PID: i32 = 1;
542 const SYSTEM_TASK_PID: i32 = 2;
543
544 if self
547 .shutting_down
548 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
549 .is_err()
550 {
551 log_debug!("Additional thread tried to initiate shutdown while already in-progress.");
552 return;
553 }
554
555 log_info!("Shutting down Starnix kernel.");
556
557 loop {
560 let tgs = {
561 self.pids
564 .read()
565 .get_thread_groups()
566 .filter(|tg| tg.leader != SYSTEM_TASK_PID && tg.leader != INIT_PID)
567 .collect::<Vec<_>>()
568 };
569 if tgs.is_empty() {
570 log_debug!("pid table is empty except init and system task");
571 break;
572 }
573
574 log_debug!(tgs:?; "shutting down thread groups");
575 let mut tasks = vec![];
576 for tg in tgs {
577 let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
578 tasks.push(task);
579 }
580 futures::future::join_all(tasks).await;
581 }
582
583 let maybe_init = {
585 self.pids.read().get_thread_group(1).map(|tg| Arc::downgrade(&tg))
588 };
589 if let Some(init) = maybe_init {
590 log_debug!("shutting down init");
591 ThreadGroup::shut_down(init).await;
592 } else {
593 log_debug!("init already terminated");
594 }
595
596 self.expando.remove::<memory_pinning::ShadowProcess>();
598
599 let kernel_job = fuchsia_runtime::job_default();
606 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
607 let own_koid = fuchsia_runtime::process_self().get_koid().unwrap();
608
609 log_debug!("waiting for this to be the only process in the job");
610 loop {
611 let mut remaining_processes = kernel_job
612 .processes()
613 .unwrap()
614 .into_iter()
615 .filter(|pid| pid != &own_koid)
617 .peekable();
618 if remaining_processes.peek().is_none() {
619 log_debug!("No stray Zircon processes.");
620 break;
621 }
622
623 let mut terminated_signals = vec![];
624 for pid in remaining_processes {
625 let handle = match kernel_job
626 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
627 {
628 Ok(h) => h,
629 Err(e) => {
630 log_debug!(pid:?, e:?; "failed to get child process from job");
631 continue;
632 }
633 };
634 terminated_signals
635 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
636 }
637 log_debug!("waiting on process terminated signals");
638 futures::future::join_all(terminated_signals).await;
639 }
640
641 self.mounts.clear();
643
644 log_debug!("all non-root processes killed, notifying CF container is stopped");
646 if let Some(control_handle) = self.container_control_handle.lock().take() {
647 log_debug!("Notifying CF that the container has stopped.");
648 control_handle
649 .send_on_stop(ComponentStopInfo {
650 termination_status: Some(zx::Status::OK.into_raw()),
651 exit_code: Some(0),
652 ..ComponentStopInfo::default()
653 })
654 .unwrap();
655 control_handle.shutdown_with_epitaph(zx::Status::OK);
656 } else {
657 log_warn!("Shutdown invoked without a container controller control handle.");
658 }
659
660 log_info!("All tasks killed, exiting Starnix kernel root process.");
662 zx::Process::exit(0);
670 }
671
672 pub fn is_shutting_down(&self) -> bool {
673 self.shutting_down.load(Ordering::Acquire)
674 }
675
676 pub fn open_device<L>(
678 &self,
679 locked: &mut Locked<L>,
680 current_task: &CurrentTask,
681 node: &NamespaceNode,
682 flags: OpenFlags,
683 dev: DeviceType,
684 mode: DeviceMode,
685 ) -> Result<Box<dyn FileOps>, Errno>
686 where
687 L: LockEqualOrBefore<FileOpsCore>,
688 {
689 self.device_registry.open_device(locked, current_task, node, flags, dev, mode)
690 }
691
692 pub fn audit_logger(&self) -> Arc<AuditLogger> {
696 self.expando.get_or_init(|| AuditLogger::new(self))
697 }
698
699 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
704 self.generic_netlink.get_or_init(|| {
705 let (generic_netlink, worker_params) = GenericNetlink::new();
706 let enable_nl80211 = self.features.wifi;
707 self.kthreads.spawn_future(async move || {
708 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211).await;
709 log_error!("Generic Netlink future unexpectedly exited");
710 });
711 generic_netlink
712 })
713 }
714
715 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
720 self.network_netlink.get_or_init(|| {
721 let (network_netlink, worker_params) =
722 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
723
724 let kernel = self.clone();
725 self.kthreads.spawn_future(async move || {
726 netlink::run_netlink_worker(
727 worker_params,
728 NetlinkAccessControl::new(kernel.kthreads.system_task()),
729 )
730 .await;
731 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
732 });
733 network_netlink
734 })
735 }
736
737 pub fn iptables(&self) -> &IpTables {
738 self.iptables.get_or_init(|| IpTables::new())
739 }
740
741 #[allow(unused)]
743 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
744 &self,
745 filename: &str,
746 ) -> Result<ClientEnd<P>, Errno> {
747 match self.container_namespace.get_namespace_channel("/svc") {
748 Ok(channel) => {
749 let (client_end, server_end) = create_endpoints::<P>();
750 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
751 .map_err(|status| from_status_like_fdio!(status))?;
752 Ok(client_end)
753 }
754 Err(err) => {
755 log_error!("Unable to get /svc namespace channel! {}", err);
756 Err(errno!(ENOENT))
757 }
758 }
759 }
760
761 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
763 &self,
764 ) -> Result<ClientEnd<P>, Errno> {
765 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
766 }
767
768 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
769 let inspector = fuchsia_inspect::Inspector::default();
770
771 let thread_groups = inspector.root();
772 let mut mm_summary = MappingSummary::default();
773 let mut mms_summarized = HashSet::new();
774
775 let all_thread_groups = {
777 let pid_table = self.pids.read();
778 pid_table.get_thread_groups().collect::<Vec<_>>()
779 };
780 for thread_group in all_thread_groups {
781 let (ppid, tasks) = {
783 let tg = thread_group.read();
784 (tg.get_ppid() as i64, tg.tasks().map(TempRef::into_static).collect::<Vec<_>>())
785 };
786
787 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
788 if let Ok(koid) = &thread_group.process.get_koid() {
789 tg_node.record_int("koid", koid.raw_koid() as i64);
790 }
791 tg_node.record_int("pid", thread_group.leader as i64);
792 tg_node.record_int("ppid", ppid);
793 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
794
795 let tasks_node = tg_node.create_child("tasks");
796 for task in tasks {
797 if let Ok(mm) = task.mm() {
798 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
799 mm.summarize(&mut mm_summary);
800 }
801 }
802 let set_properties = |node: &fuchsia_inspect::Node| {
803 node.record_string("command", task.command().to_string());
804
805 let scheduler_state = task.read().scheduler_state;
806 if !scheduler_state.is_default() {
807 node.record_child("sched", |node| {
808 node.record_string(
809 "role_name",
810 self.scheduler
811 .role_name(&task)
812 .map(|n| Cow::Borrowed(n))
813 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
814 );
815 node.record_string("state", format!("{scheduler_state:?}"));
816 });
817 }
818 };
819 if task.tid == thread_group.leader {
820 let mut argv = task.read_argv(256).unwrap_or_default();
821
822 argv.retain(|arg| !arg.is_empty());
825
826 let inspect_argv = tg_node.create_string_array("argv", argv.len());
827 for (i, arg) in argv.iter().enumerate() {
828 inspect_argv.set(i, arg.to_string());
829 }
830 tg_node.record(inspect_argv);
831
832 set_properties(&tg_node);
833 } else {
834 tasks_node.record_child(task.tid.to_string(), |task_node| {
835 set_properties(task_node);
836 });
837 };
838 }
839 tg_node.record(tasks_node);
840 thread_groups.record(tg_node);
841 }
842
843 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
844
845 inspector
846 }
847
848 pub fn new_memory_attribution_observer(
849 &self,
850 control_handle: fattribution::ProviderControlHandle,
851 ) -> attribution_server::Observer {
852 self.memory_attribution_manager.new_observer(control_handle)
853 }
854
855 pub fn open_ns_dir(
862 &self,
863 path: &str,
864 open_flags: fio::Flags,
865 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
866 let ns_path = match path {
867 "" | "/" | "." => PathBuf::from("/data"),
870 _ => PathBuf::from(path),
871 };
872
873 match self.container_namespace.find_closest_channel(&ns_path) {
874 Ok((root_channel, remaining_subdir)) => {
875 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
876 fdio::open_at(
877 &root_channel,
878 &remaining_subdir,
879 open_flags,
880 server_end.into_channel(),
881 )
882 .map_err(|e| {
883 log_error!("Failed to intialize the subdirs: {}", e);
884 errno!(EIO)
885 })?;
886
887 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
888 }
889 Err(err) => {
890 log_error!(
891 "Unable to find a channel for {}. Received error: {}",
892 ns_path.display(),
893 err
894 );
895 Err(errno!(ENOENT))
896 }
897 }
898 }
899
900 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
902 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
903 arg.split_once('=')
904 .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
905 .or(Some(ArgNameAndValue { name: arg, value: None }))
906 })
907 }
908}
909
910pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
911 let mut args = Vec::new();
912 let mut arg_start: Option<usize> = None;
913 let mut in_quotes = false;
914 let mut previous_char = ' ';
915
916 for (i, c) in cmdline.char_indices() {
917 if let Some(start) = arg_start {
918 match c {
919 ' ' if !in_quotes => {
920 args.push(&cmdline[start..i]);
921 arg_start = None;
922 }
923 '"' if previous_char != '\\' => {
924 in_quotes = !in_quotes;
925 }
926 _ => {}
927 }
928 } else if c != ' ' {
929 arg_start = Some(i);
930 if c == '"' {
931 in_quotes = true;
932 }
933 }
934 previous_char = c;
935 }
936 if let Some(start) = arg_start {
937 args.push(&cmdline[start..]);
938 }
939 args.into_iter()
940}
941
942impl std::fmt::Debug for Kernel {
943 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
944 f.debug_struct("Kernel").finish()
945 }
946}
947
948#[cfg(target_arch = "aarch64")]
950fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
951 use starnix_uapi::arch32;
952 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
953 | arch32::HWCAP_THUMB
954 | arch32::HWCAP_FAST_MULT
955 | arch32::HWCAP_EDSP
956 | arch32::HWCAP_TLS
957 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
959 | arch32::HWCAP_EVTSTRM;
960
961 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
962 let mut hwcap2 = 0;
963 for feature in cpu_feature_flags.iter() {
964 match feature {
965 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
966 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
967 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
968 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
969 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
970 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
971 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
972 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
973 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
974 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
975 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
976 }
977 _ => {}
978 }
979 }
980 HwCap { hwcap, hwcap2 }
981}
982
983#[cfg(target_arch = "aarch64")]
984fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
985 use starnix_uapi;
987 let mut hwcap = 0;
988 let mut hwcap2 = 0;
989
990 for feature in cpu_feature_flags.iter() {
991 match feature {
992 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
993 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
994 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
995 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
996 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
997 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
998 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
999 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1000 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1001 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1002 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1003 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1004 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1005 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1006 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1007 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1008 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1009 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1010 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1011 _ => {}
1012 }
1013 }
1014 HwCap { hwcap, hwcap2 }
1015}
1016
1017impl HwCaps {
1018 #[cfg(target_arch = "aarch64")]
1019 pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1020 Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1021 }
1022
1023 #[cfg(not(target_arch = "aarch64"))]
1024 pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1025 Self { arch64: HwCap::default() }
1026 }
1027}
1028
1029#[cfg(test)]
1030mod test {
1031 use super::parse_cmdline;
1032
1033 #[test]
1034 fn test_parse_cmdline() {
1035 let cmdline =
1036 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1037 let expected = vec![
1038 "first",
1039 "second=third",
1040 "\"fourth fifth\"",
1041 "sixth=\"seventh eighth\"",
1042 "\"ninth\\\" tenth\"",
1043 "eleventh",
1044 ];
1045 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1046 }
1047}