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::ptrace::StopState;
12use crate::security::{self, AuditLogger};
13use crate::task::container_namespace::ContainerNamespace;
14use crate::task::limits::SystemLimits;
15use crate::task::memory_attribution::MemoryAttributionManager;
16use crate::task::net::NetstackDevices;
17use crate::task::tracing::PidToKoidMap;
18use crate::task::{
19 AbstractUnixSocketNamespace, AbstractVsockSocketNamespace, CurrentTask, DelayedReleaser,
20 IpTables, KernelCgroups, KernelStats, KernelThreads, PidTable, SchedulerManager, Syslog,
21 ThreadGroup, UtsNamespace, UtsNamespaceHandle,
22};
23use crate::time::{HrTimerManager, HrTimerManagerHandle};
24use crate::vdso::vdso_loader::Vdso;
25use crate::vfs::fs_args::MountParams;
26use crate::vfs::socket::{
27 GenericMessage, GenericNetlink, NetlinkAccessControl, NetlinkContextImpl,
28 NetlinkToClientSender, SocketAddress, SocketTokensStore,
29};
30use crate::vfs::{CacheConfig, FileOps, FsNodeHandle, FsString, Mounts, NamespaceNode};
31use bstr::{BString, ByteSlice};
32use devicetree::types::Devicetree;
33use expando::Expando;
34use fidl::endpoints::{
35 ClientEnd, ControlHandle, DiscoverableProtocolMarker, ProtocolMarker, create_endpoints,
36};
37use fidl_fuchsia_component_runner::{ComponentControllerControlHandle, ComponentStopInfo};
38use fidl_fuchsia_feedback::CrashReporterProxy;
39use fidl_fuchsia_time_external::AdjustSynchronousProxy;
40use fuchsia_inspect::ArrayProperty;
41use futures::FutureExt;
42use netlink::interfaces::InterfacesHandler;
43use netlink::{NETLINK_LOG_TAG, Netlink};
44use once_cell::sync::OnceCell;
45use starnix_lifecycle::{AtomicU32Counter, AtomicU64Counter};
46use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
47use starnix_sync::{
48 FileOpsCore, KernelSwapFiles, LockEqualOrBefore, Locked, Mutex, OrderedMutex, RwLock,
49};
50use starnix_types::ownership::TempRef;
51use starnix_uapi::device_type::DeviceType;
52use starnix_uapi::errors::{Errno, errno};
53use starnix_uapi::open_flags::OpenFlags;
54use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
55use std::borrow::Cow;
56use std::collections::{HashMap, HashSet};
57use std::num::NonZeroU64;
58use std::path::PathBuf;
59use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU16, Ordering};
60use std::sync::{Arc, OnceLock, Weak};
61use zx::CpuFeatureFlags;
62use {
63 fidl_fuchsia_io as fio, fidl_fuchsia_memory_attribution as fattribution,
64 fuchsia_async as fasync,
65};
66
67#[derive(Debug, Default, Clone)]
68pub struct KernelFeatures {
69 pub bpf_v2: bool,
70
71 pub enable_suid: bool,
78
79 pub io_uring: bool,
83
84 pub error_on_failed_reboot: bool,
87
88 pub default_seclabel: Option<String>,
92
93 pub selinux_test_suite: bool,
98
99 pub default_ns_mount_options: Option<HashMap<String, String>>,
104
105 pub default_uid: u32,
109
110 pub mlock_always_onfault: bool,
112
113 pub mlock_pin_flavor: MlockPinFlavor,
115
116 pub crash_report_throttling: bool,
118
119 pub wifi: bool,
121
122 pub cached_zx_map_info_bytes: u32,
124
125 pub dirent_cache_size: u32,
127}
128
129impl KernelFeatures {
130 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
134 if let Some(all_options) = &self.default_ns_mount_options {
135 if let Some(options) = all_options.get(ns_path) {
136 return MountParams::parse(options.as_bytes().into());
137 }
138 }
139 Ok(MountParams::default())
140 }
141}
142
143pub struct ArgNameAndValue<'a> {
145 pub name: &'a str,
146 pub value: Option<&'a str>,
147}
148
149pub struct Kernel {
159 pub weak_self: Weak<Kernel>,
161
162 pub kthreads: KernelThreads,
164
165 pub features: KernelFeatures,
167
168 pub pids: RwLock<PidTable>,
170
171 pub pid_to_koid_mapping: Arc<RwLock<Option<PidToKoidMap>>>,
173
174 pub expando: Expando,
179
180 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
186
187 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
189
190 pub cmdline: BString,
192
193 pub device_tree: Option<Devicetree>,
194
195 pub security_state: security::KernelState,
197
198 pub device_registry: DeviceRegistry,
200
201 pub container_namespace: ContainerNamespace,
205
206 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
208
209 iptables: OnceLock<IpTables>,
211
212 pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
214
215 pub root_uts_ns: UtsNamespaceHandle,
220
221 pub vdso: Vdso,
223
224 pub vdso_arch32: Option<Vdso>,
228
229 pub netstack_devices: Arc<NetstackDevices>,
232
233 pub swap_files: OrderedMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
237
238 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
240
241 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
243
244 pub inspect_node: fuchsia_inspect::Node,
246
247 pub actions_logged: AtomicU16,
254
255 pub suspend_resume_manager: SuspendResumeManagerHandle,
257
258 pub next_mount_id: AtomicU64Counter,
260 pub next_peer_group_id: AtomicU64Counter,
261 pub next_namespace_id: AtomicU64Counter,
262
263 pub next_file_object_id: AtomicU64Counter,
265
266 pub next_inotify_cookie: AtomicU32Counter,
268
269 pub ptrace_scope: AtomicU8,
271
272 pub build_version: OnceCell<String>,
274
275 pub stats: Arc<KernelStats>,
276
277 pub system_limits: SystemLimits,
279
280 pub delayed_releaser: DelayedReleaser,
284
285 pub scheduler: SchedulerManager,
287
288 pub syslog: Syslog,
290
291 pub mounts: Mounts,
293
294 pub hrtimer_manager: HrTimerManagerHandle,
296
297 pub memory_attribution_manager: MemoryAttributionManager,
299
300 pub crash_reporter: CrashReporter,
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 pub syscall_log_filters: Mutex<Vec<SyscallLogFilter>>,
338}
339
340#[derive(Debug, Clone, Copy, Default)]
342pub struct HwCap {
343 pub hwcap: u32,
345 pub hwcap2: u32,
347}
348
349#[derive(Debug, Clone, Copy, Default)]
351pub struct HwCaps {
352 #[cfg(target_arch = "aarch64")]
354 pub arch32: HwCap,
355 pub arch64: HwCap,
357}
358
359struct InterfacesHandlerImpl(Weak<Kernel>);
366
367impl InterfacesHandlerImpl {
368 fn kernel(&self) -> Option<Arc<Kernel>> {
369 self.0.upgrade()
370 }
371}
372
373impl InterfacesHandler for InterfacesHandlerImpl {
374 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
375 if let Some(kernel) = self.kernel() {
376 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
377 }
378 }
379
380 fn handle_deleted_link(&mut self, name: &str) {
381 if let Some(kernel) = self.kernel() {
382 kernel.netstack_devices.remove_device(&kernel, name.into());
383 }
384 }
385
386 fn handle_idle_event(&mut self) {
387 let Some(kernel) = self.kernel() else {
388 log_error!("kernel went away while netlink is initializing");
389 return;
390 };
391 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
392 if initialized.swap(true, Ordering::SeqCst) {
393 log_error!("netlink initial devices should only be reported once");
394 return;
395 }
396 wq.notify_all()
397 }
398}
399
400impl Kernel {
401 pub fn new(
402 cmdline: BString,
403 features: KernelFeatures,
404 system_limits: SystemLimits,
405 container_namespace: ContainerNamespace,
406 scheduler: SchedulerManager,
407 crash_reporter_proxy: Option<CrashReporterProxy>,
408 inspect_node: fuchsia_inspect::Node,
409 security_state: security::KernelState,
410 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
411 device_tree: Option<Devicetree>,
412 ) -> Result<Arc<Kernel>, zx::Status> {
413 let unix_address_maker =
414 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
415 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
416 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
417 });
418
419 let crash_reporter = CrashReporter::new(
420 &inspect_node,
421 crash_reporter_proxy,
422 zx::Duration::from_minutes(8),
423 features.crash_report_throttling,
424 );
425 let hrtimer_manager = HrTimerManager::new(&inspect_node);
426
427 let cpu_feature_flags =
428 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
429 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
430 CpuFeatureFlags::empty()
431 });
432 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
433
434 let this = Arc::new_cyclic(|kernel| Kernel {
435 weak_self: kernel.clone(),
436 kthreads: KernelThreads::new(kernel.clone()),
437 features,
438 pids: Default::default(),
439 pid_to_koid_mapping: Arc::new(RwLock::new(None)),
440 expando: Default::default(),
441 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
442 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
443 vsock_address_maker,
444 ),
445 cmdline,
446 device_tree,
447 security_state,
448 device_registry: Default::default(),
449 container_namespace,
450 remote_block_device_registry: Default::default(),
451 iptables: OnceLock::new(),
452 shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
453 root_uts_ns: Arc::new(RwLock::new(UtsNamespace::default())),
454 vdso: Vdso::new(),
455 vdso_arch32: Vdso::new_arch32(),
456 netstack_devices: Arc::default(),
457 swap_files: Default::default(),
458 generic_netlink: OnceLock::new(),
459 network_netlink: OnceLock::new(),
460 inspect_node,
461 actions_logged: AtomicU16::new(0),
462 suspend_resume_manager: Default::default(),
463 next_mount_id: AtomicU64Counter::new(1),
464 next_peer_group_id: AtomicU64Counter::new(1),
465 next_namespace_id: AtomicU64Counter::new(1),
466 next_inotify_cookie: AtomicU32Counter::new(1),
467 next_file_object_id: Default::default(),
468 system_limits,
469 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
471 disable_unprivileged_bpf: AtomicU8::new(0), build_version: OnceCell::new(),
473 stats: Arc::new(KernelStats::default()),
474 delayed_releaser: Default::default(),
475 scheduler,
476 syslog: Default::default(),
477 mounts: Mounts::new(),
478 hrtimer_manager,
479 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
480 crash_reporter,
481 shutting_down: AtomicBool::new(false),
482 container_control_handle: Mutex::new(None),
483 ebpf_state: Default::default(),
484 cgroups: Default::default(),
485 time_adjustment_proxy,
486 socket_tokens_store: Default::default(),
487 hwcaps,
488 syscall_log_filters: Default::default(),
489 });
490
491 this.device_registry.objects.init(&mut this.kthreads.unlocked_for_async(), &this);
495
496 let kernel = Arc::downgrade(&this);
499 this.inspect_node.record_lazy_child("thread_groups", move || {
500 if let Some(kernel) = kernel.upgrade() {
501 let inspector = kernel.get_thread_groups_inspect();
502 async move { Ok(inspector) }.boxed()
503 } else {
504 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
505 }
506 });
507
508 let kernel = Arc::downgrade(&this);
509 this.inspect_node.record_lazy_child("cgroupv2", move || {
510 if let Some(kernel) = kernel.upgrade() {
511 async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
512 } else {
513 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
514 }
515 });
516
517 Ok(this)
518 }
519
520 pub fn shut_down(self: &Arc<Self>) {
523 self.kthreads.spawn_future(
526 {
527 let kernel = self.clone();
528 move || async move {
529 kernel.run_shutdown().await;
530 }
531 },
532 "run_shutdown",
533 );
534 }
535
536 async fn run_shutdown(&self) {
556 const INIT_PID: i32 = 1;
557 const SYSTEM_TASK_PID: i32 = 2;
558
559 if self
562 .shutting_down
563 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
564 .is_err()
565 {
566 log_debug!("Additional thread tried to initiate shutdown while already in-progress.");
567 return;
568 }
569
570 log_info!("Shutting down Starnix kernel.");
571
572 loop {
575 let tgs = {
576 self.pids
579 .read()
580 .get_thread_groups()
581 .filter(|tg| tg.leader != SYSTEM_TASK_PID && tg.leader != INIT_PID)
582 .collect::<Vec<_>>()
583 };
584 if tgs.is_empty() {
585 log_debug!("pid table is empty except init and system task");
586 break;
587 }
588
589 log_debug!(tgs:?; "shutting down thread groups");
590 let mut tasks = vec![];
591 for tg in tgs {
592 let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
593 tasks.push(task);
594 }
595 futures::future::join_all(tasks).await;
596 }
597
598 let maybe_init = {
600 self.pids.read().get_thread_group(1).map(|tg| Arc::downgrade(&tg))
603 };
604 if let Some(init) = maybe_init {
605 log_debug!("shutting down init");
606 ThreadGroup::shut_down(init).await;
607 } else {
608 log_debug!("init already terminated");
609 }
610
611 log_debug!("cleaning up pinned memory");
613 self.expando.remove::<memory_pinning::ShadowProcess>();
614
615 let kernel_job = fuchsia_runtime::job_default();
622 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
623 let own_koid = fuchsia_runtime::process_self().koid().unwrap();
624
625 log_debug!("waiting for this to be the only process in the job");
626 loop {
627 let mut remaining_processes = kernel_job
628 .processes()
629 .unwrap()
630 .into_iter()
631 .filter(|pid| pid != &own_koid)
633 .peekable();
634 if remaining_processes.peek().is_none() {
635 log_debug!("No stray Zircon processes.");
636 break;
637 }
638
639 let mut terminated_signals = vec![];
640 for pid in remaining_processes {
641 let handle = match kernel_job
642 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
643 {
644 Ok(h) => h,
645 Err(e) => {
646 log_debug!(pid:?, e:?; "failed to get child process from job");
647 continue;
648 }
649 };
650 log_debug!(
651 pid:?,
652 name:? = handle.get_name();
653 "waiting on process terminated signal"
654 );
655 terminated_signals
656 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
657 }
658 log_debug!("waiting on process terminated signals");
659 futures::future::join_all(terminated_signals).await;
660 }
661
662 log_debug!("clearing mounts");
664 self.mounts.clear();
665
666 log_debug!("all non-root processes killed, notifying CF container is stopped");
668 if let Some(control_handle) = self.container_control_handle.lock().take() {
669 log_debug!("Notifying CF that the container has stopped.");
670 control_handle
671 .send_on_stop(ComponentStopInfo {
672 termination_status: Some(zx::Status::OK.into_raw()),
673 exit_code: Some(0),
674 ..ComponentStopInfo::default()
675 })
676 .unwrap();
677 control_handle.shutdown_with_epitaph(zx::Status::OK);
678 } else {
679 log_warn!("Shutdown invoked without a container controller control handle.");
680 }
681
682 log_info!("All tasks killed, exiting Starnix kernel root process.");
684 zx::Process::exit(0);
692 }
693
694 pub fn is_shutting_down(&self) -> bool {
695 self.shutting_down.load(Ordering::Acquire)
696 }
697
698 pub fn open_device<L>(
700 &self,
701 locked: &mut Locked<L>,
702 current_task: &CurrentTask,
703 node: &NamespaceNode,
704 flags: OpenFlags,
705 dev: DeviceType,
706 mode: DeviceMode,
707 ) -> Result<Box<dyn FileOps>, Errno>
708 where
709 L: LockEqualOrBefore<FileOpsCore>,
710 {
711 self.device_registry.open_device(locked, current_task, node, flags, dev, mode)
712 }
713
714 pub fn audit_logger(&self) -> Arc<AuditLogger> {
718 self.expando.get_or_init(|| AuditLogger::new(self))
719 }
720
721 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
726 self.generic_netlink.get_or_init(|| {
727 let (generic_netlink, worker_params) = GenericNetlink::new();
728 let enable_nl80211 = self.features.wifi;
729 self.kthreads.spawn_future(
730 move || async move {
731 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
732 .await;
733 log_error!("Generic Netlink future unexpectedly exited");
734 },
735 "generic_netlink_worker",
736 );
737 generic_netlink
738 })
739 }
740
741 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
746 self.network_netlink.get_or_init(|| {
747 let (network_netlink, worker_params) =
748 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
749
750 let kernel = self.clone();
751 self.kthreads.spawn_future(
752 move || async move {
753 netlink::run_netlink_worker(
754 worker_params,
755 NetlinkAccessControl::new(kernel.kthreads.system_task()),
756 )
757 .await;
758 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
759 },
760 "network_netlink_worker",
761 );
762 network_netlink
763 })
764 }
765
766 pub fn iptables(&self) -> &IpTables {
767 self.iptables.get_or_init(|| IpTables::new())
768 }
769
770 #[allow(unused)]
772 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
773 &self,
774 filename: &str,
775 ) -> Result<ClientEnd<P>, Errno> {
776 match self.container_namespace.get_namespace_channel("/svc") {
777 Ok(channel) => {
778 let (client_end, server_end) = create_endpoints::<P>();
779 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
780 .map_err(|status| from_status_like_fdio!(status))?;
781 Ok(client_end)
782 }
783 Err(err) => {
784 log_error!("Unable to get /svc namespace channel! {}", err);
785 Err(errno!(ENOENT))
786 }
787 }
788 }
789
790 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
792 &self,
793 ) -> Result<ClientEnd<P>, Errno> {
794 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
795 }
796
797 pub fn add_syscall_log_filter(&self, name: &str) {
798 let filter = SyscallLogFilter::new(name.to_string());
799 {
800 let mut filters = self.syscall_log_filters.lock();
801 if filters.contains(&filter) {
802 return;
803 }
804 filters.push(filter);
805 }
806 for headers in self.pids.read().get_thread_groups() {
807 headers.sync_syscall_log_level();
808 }
809 }
810
811 pub fn clear_syscall_log_filters(&self) {
812 {
813 let mut filters = self.syscall_log_filters.lock();
814 if filters.is_empty() {
815 return;
816 }
817 filters.clear();
818 }
819 for headers in self.pids.read().get_thread_groups() {
820 headers.sync_syscall_log_level();
821 }
822 }
823
824 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
825 let inspector = fuchsia_inspect::Inspector::default();
826
827 let thread_groups = inspector.root();
828 let mut mm_summary = MappingSummary::default();
829 let mut mms_summarized = HashSet::new();
830
831 let all_thread_groups = {
833 let pid_table = self.pids.read();
834 pid_table.get_thread_groups().collect::<Vec<_>>()
835 };
836 for thread_group in all_thread_groups {
837 let (ppid, tasks) = {
839 let tg = thread_group.read();
840 (tg.get_ppid() as i64, tg.tasks().map(TempRef::into_static).collect::<Vec<_>>())
841 };
842
843 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
844 if let Ok(koid) = &thread_group.process.koid() {
845 tg_node.record_int("koid", koid.raw_koid() as i64);
846 }
847 tg_node.record_int("pid", thread_group.leader as i64);
848 tg_node.record_int("ppid", ppid);
849 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
850
851 let tasks_node = tg_node.create_child("tasks");
852 for task in tasks {
853 if let Ok(mm) = task.mm() {
854 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
855 mm.summarize(&mut mm_summary);
856 }
857 }
858 let set_properties = |node: &fuchsia_inspect::Node| {
859 node.record_string("command", task.command().to_string());
860
861 let scheduler_state = task.read().scheduler_state;
862 if !scheduler_state.is_default() {
863 node.record_child("sched", |node| {
864 node.record_string(
865 "role_name",
866 self.scheduler
867 .role_name(&task)
868 .map(|n| Cow::Borrowed(n))
869 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
870 );
871 node.record_string("state", format!("{scheduler_state:?}"));
872 });
873 }
874 };
875 if task.tid == thread_group.leader {
876 let mut argv = task.read_argv(256).unwrap_or_default();
877
878 argv.retain(|arg| !arg.is_empty());
881
882 let inspect_argv = tg_node.create_string_array("argv", argv.len());
883 for (i, arg) in argv.iter().enumerate() {
884 inspect_argv.set(i, arg.to_string());
885 }
886 tg_node.record(inspect_argv);
887
888 set_properties(&tg_node);
889 } else {
890 tasks_node.record_child(task.tid.to_string(), |task_node| {
891 set_properties(task_node);
892 });
893 };
894 }
895 tg_node.record(tasks_node);
896 thread_groups.record(tg_node);
897 }
898
899 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
900
901 inspector
902 }
903
904 pub fn new_memory_attribution_observer(
905 &self,
906 control_handle: fattribution::ProviderControlHandle,
907 ) -> attribution_server::Observer {
908 self.memory_attribution_manager.new_observer(control_handle)
909 }
910
911 pub fn open_ns_dir(
923 &self,
924 path: &str,
925 open_flags: fio::Flags,
926 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
927 let ns_path = PathBuf::from(path);
928 match self.container_namespace.find_closest_channel(&ns_path) {
929 Ok((root_channel, remaining_subdir)) => {
930 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
931 fdio::open_at(
932 &root_channel,
933 &remaining_subdir,
934 open_flags,
935 server_end.into_channel(),
936 )
937 .map_err(|e| {
938 log_error!("Failed to intialize the subdirs: {}", e);
939 errno!(EIO)
940 })?;
941
942 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
943 }
944 Err(err) => {
945 log_error!(
946 "Unable to find a channel for {}. Received error: {}",
947 ns_path.display(),
948 err
949 );
950 Err(errno!(ENOENT))
951 }
952 }
953 }
954
955 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
957 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
958 arg.split_once('=')
959 .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
960 .or(Some(ArgNameAndValue { name: arg, value: None }))
961 })
962 }
963
964 pub fn fs_cache_config(&self) -> CacheConfig {
966 CacheConfig { capacity: self.features.dirent_cache_size as usize }
967 }
968}
969
970pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
971 let mut args = Vec::new();
972 let mut arg_start: Option<usize> = None;
973 let mut in_quotes = false;
974 let mut previous_char = ' ';
975
976 for (i, c) in cmdline.char_indices() {
977 if let Some(start) = arg_start {
978 match c {
979 ' ' if !in_quotes => {
980 args.push(&cmdline[start..i]);
981 arg_start = None;
982 }
983 '"' if previous_char != '\\' => {
984 in_quotes = !in_quotes;
985 }
986 _ => {}
987 }
988 } else if c != ' ' {
989 arg_start = Some(i);
990 if c == '"' {
991 in_quotes = true;
992 }
993 }
994 previous_char = c;
995 }
996 if let Some(start) = arg_start {
997 args.push(&cmdline[start..]);
998 }
999 args.into_iter()
1000}
1001
1002impl std::fmt::Debug for Kernel {
1003 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1004 f.debug_struct("Kernel").finish()
1005 }
1006}
1007
1008#[cfg(target_arch = "aarch64")]
1010fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1011 use starnix_uapi::arch32;
1012 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1013 | arch32::HWCAP_THUMB
1014 | arch32::HWCAP_FAST_MULT
1015 | arch32::HWCAP_EDSP
1016 | arch32::HWCAP_TLS
1017 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
1019 | arch32::HWCAP_EVTSTRM;
1020
1021 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1022 let mut hwcap2 = 0;
1023 for feature in cpu_feature_flags.iter() {
1024 match feature {
1025 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1026 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1027 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1028 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1029 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1030 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1031 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1032 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1033 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1034 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1035 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1036 }
1037 _ => {}
1038 }
1039 }
1040 HwCap { hwcap, hwcap2 }
1041}
1042
1043#[cfg(target_arch = "aarch64")]
1044fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1045 use starnix_uapi;
1047 let mut hwcap = 0;
1048 let mut hwcap2 = 0;
1049
1050 for feature in cpu_feature_flags.iter() {
1051 match feature {
1052 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1053 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1054 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1055 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1056 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1057 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1058 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1059 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1060 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1061 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1062 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1063 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1064 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1065 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1066 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1067 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1068 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1069 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1070 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1071 _ => {}
1072 }
1073 }
1074 HwCap { hwcap, hwcap2 }
1075}
1076
1077impl HwCaps {
1078 #[cfg(target_arch = "aarch64")]
1079 pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1080 Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1081 }
1082
1083 #[cfg(not(target_arch = "aarch64"))]
1084 pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1085 Self { arch64: HwCap::default() }
1086 }
1087}
1088
1089#[cfg(test)]
1090mod test {
1091 use super::parse_cmdline;
1092
1093 #[test]
1094 fn test_parse_cmdline() {
1095 let cmdline =
1096 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1097 let expected = vec![
1098 "first",
1099 "second=third",
1100 "\"fourth fifth\"",
1101 "sixth=\"seventh eighth\"",
1102 "\"ninth\\\" tenth\"",
1103 "eleventh",
1104 ];
1105 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1106 }
1107}