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, Task,
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_io as fio;
40use fidl_fuchsia_memory_attribution as fattribution;
41use fidl_fuchsia_time_external::AdjustSynchronousProxy;
42use fuchsia_async as fasync;
43use fuchsia_inspect::ArrayProperty;
44use futures::FutureExt;
45use netlink::interfaces::InterfacesHandler;
46use netlink::{NETLINK_LOG_TAG, Netlink};
47use once_cell::sync::OnceCell;
48use starnix_lifecycle::AtomicCounter;
49use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
50use starnix_sync::{
51 ComponentControllerLock, FileOpsCore, KernelSwapFiles, LockDepMutex, LockDepRwLock,
52 LockEqualOrBefore, Locked, MountsLevel, OrderedMutex, PidToKoidMapLock, RwLock, RwSeqLock,
53 SyscallLogFiltersLock,
54};
55use starnix_uapi::device_id::DeviceId;
56use starnix_uapi::errors::{Errno, errno};
57use starnix_uapi::open_flags::OpenFlags;
58use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
59use std::borrow::Cow;
60use std::collections::{HashMap, HashSet};
61use std::num::NonZeroU64;
62use std::path::PathBuf;
63use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU16, Ordering};
64use std::sync::{Arc, OnceLock, Weak};
65use zx::CpuFeatureFlags;
66
67#[derive(Debug, Default, Clone)]
70pub struct KernelFeatures {
71 pub bpf_v2: bool,
72
73 pub enable_suid: bool,
80
81 pub io_uring: bool,
85
86 pub error_on_failed_reboot: bool,
89
90 pub default_seclabel: Option<String>,
94
95 pub selinux_test_suite: bool,
100
101 pub default_ns_mount_options: Option<HashMap<String, String>>,
106
107 pub default_uid: u32,
111
112 pub mlock_always_onfault: bool,
114
115 pub mlock_pin_flavor: MlockPinFlavor,
117
118 pub crash_report_throttling: bool,
120
121 pub wifi: bool,
123
124 pub cached_zx_map_info_bytes: u32,
126
127 pub dirent_cache_size: u32,
129
130 pub fake_ion: bool,
133}
134
135impl KernelFeatures {
136 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
140 if let Some(all_options) = &self.default_ns_mount_options {
141 if let Some(options) = all_options.get(ns_path) {
142 return MountParams::parse(options.as_bytes().into());
143 }
144 }
145 Ok(MountParams::default())
146 }
147}
148
149pub struct ArgNameAndValue<'a> {
151 pub name: &'a str,
152 pub value: Option<&'a str>,
153}
154
155pub struct MountsWriteToken(());
160
161impl MountsWriteToken {
162 fn new() -> Self {
163 Self(())
164 }
165}
166
167pub struct Kernel {
177 pub weak_self: Weak<Kernel>,
179
180 pub kthreads: KernelThreads,
182
183 pub features: KernelFeatures,
185
186 pub pids: RwLock<PidTable>,
188
189 pub init_task: OnceLock<Weak<Task>>,
191
192 pub pid_to_koid_mapping: Arc<LockDepRwLock<Option<PidToKoidMap>, PidToKoidMapLock>>,
194
195 pub expando: Expando,
200
201 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
207
208 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
210
211 pub cmdline: BString,
213
214 pub device_tree: Option<Devicetree>,
215
216 pub security_state: security::KernelState,
218
219 pub device_registry: DeviceRegistry,
221
222 pub container_namespace: ContainerNamespace,
226
227 pub mounts_lock: RwSeqLock<LockDepMutex<MountsWriteToken, MountsLevel>>,
236
237 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
239
240 iptables: OnceLock<IpTables>,
242
243 pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
245
246 pub root_uts_ns: UtsNamespaceHandle,
251
252 pub vdso: Vdso,
254
255 pub vdso_arch32: Option<Vdso>,
259
260 pub netstack_devices: Arc<NetstackDevices>,
263
264 pub swap_files: OrderedMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
268
269 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
271
272 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
274
275 pub inspect_node: fuchsia_inspect::Node,
277
278 pub actions_logged: AtomicU16,
285
286 pub suspend_resume_manager: SuspendResumeManagerHandle,
288
289 pub next_mount_id: AtomicCounter<u64>,
291 pub next_peer_group_id: AtomicCounter<u64>,
292 pub next_namespace_id: AtomicCounter<u64>,
293
294 pub next_file_object_id: AtomicCounter<u64>,
296
297 pub ptrace_scope: AtomicU8,
299
300 pub build_version: OnceCell<String>,
302
303 pub stats: Arc<KernelStats>,
304
305 pub system_limits: SystemLimits,
307
308 pub delayed_releaser: DelayedReleaser,
312
313 pub scheduler: SchedulerManager,
315
316 pub syslog: Syslog,
318
319 pub mounts: Mounts,
321
322 pub hrtimer_manager: HrTimerManagerHandle,
324
325 pub memory_attribution_manager: MemoryAttributionManager,
327
328 pub crash_reporter: CrashReporter,
330
331 shutting_down: AtomicBool,
333
334 pub restrict_dmesg: AtomicBool,
337
338 pub disable_unprivileged_bpf: AtomicU8,
343
344 pub container_control_handle:
346 LockDepMutex<Option<ComponentControllerControlHandle>, ComponentControllerLock>,
347
348 pub ebpf_state: EbpfState,
350
351 pub cgroups: KernelCgroups,
353
354 pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
357
358 pub socket_tokens_store: SocketTokensStore,
360
361 pub hwcaps: HwCaps,
363
364 pub syscall_log_filters: LockDepMutex<Vec<SyscallLogFilter>, SyscallLogFiltersLock>,
367}
368
369#[derive(Debug, Clone, Copy, Default)]
371pub struct HwCap {
372 pub hwcap: u32,
374 pub hwcap2: u32,
376}
377
378#[derive(Debug, Clone, Copy, Default)]
380pub struct HwCaps {
381 #[cfg(target_arch = "aarch64")]
383 pub arch32: HwCap,
384 pub arch64: HwCap,
386}
387
388struct InterfacesHandlerImpl(Weak<Kernel>);
395
396impl InterfacesHandlerImpl {
397 fn kernel(&self) -> Option<Arc<Kernel>> {
398 self.0.upgrade()
399 }
400}
401
402impl InterfacesHandler for InterfacesHandlerImpl {
403 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
404 if let Some(kernel) = self.kernel() {
405 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
406 }
407 }
408
409 fn handle_deleted_link(&mut self, name: &str) {
410 if let Some(kernel) = self.kernel() {
411 kernel.netstack_devices.remove_device(&kernel, name.into());
412 }
413 }
414
415 fn handle_idle_event(&mut self) {
416 let Some(kernel) = self.kernel() else {
417 log_error!("kernel went away while netlink is initializing");
418 return;
419 };
420 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
421 if initialized.swap(true, Ordering::SeqCst) {
422 log_error!("netlink initial devices should only be reported once");
423 return;
424 }
425 wq.notify_all()
426 }
427}
428
429impl Kernel {
430 pub fn new(
431 cmdline: BString,
432 features: KernelFeatures,
433 system_limits: SystemLimits,
434 container_namespace: ContainerNamespace,
435 scheduler: SchedulerManager,
436 crash_reporter_proxy: Option<CrashReporterProxy>,
437 inspect_node: fuchsia_inspect::Node,
438 security_state: security::KernelState,
439 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
440 device_tree: Option<Devicetree>,
441 ) -> Result<Arc<Kernel>, zx::Status> {
442 let unix_address_maker =
443 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
444 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
445 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
446 });
447
448 let crash_reporter = CrashReporter::new(
449 &inspect_node,
450 crash_reporter_proxy,
451 zx::Duration::from_minutes(8),
452 features.crash_report_throttling,
453 );
454 let hrtimer_manager = HrTimerManager::new(&inspect_node);
455
456 let cpu_feature_flags =
457 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
458 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
459 CpuFeatureFlags::empty()
460 });
461 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
462
463 let this = Arc::new_cyclic(|kernel| Kernel {
464 weak_self: kernel.clone(),
465 kthreads: KernelThreads::new(kernel.clone()),
466 features,
467 pids: Default::default(),
468 init_task: OnceLock::new(),
469 pid_to_koid_mapping: Default::default(),
470 expando: Default::default(),
471 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
472 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
473 vsock_address_maker,
474 ),
475 cmdline,
476 device_tree,
477 security_state,
478 device_registry: Default::default(),
479 container_namespace,
480 mounts_lock: RwSeqLock::new(MountsWriteToken::new().into()),
481 remote_block_device_registry: Default::default(),
482 iptables: OnceLock::new(),
483 shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
484 root_uts_ns: Arc::new(UtsNamespace::default().into()),
485 vdso: Vdso::new(),
486 vdso_arch32: Vdso::new_arch32(),
487 netstack_devices: Arc::default(),
488 swap_files: Default::default(),
489 generic_netlink: OnceLock::new(),
490 network_netlink: OnceLock::new(),
491 inspect_node,
492 actions_logged: AtomicU16::new(0),
493 suspend_resume_manager: Default::default(),
494 next_mount_id: AtomicCounter::<u64>::new(1),
495 next_peer_group_id: AtomicCounter::<u64>::new(1),
496 next_namespace_id: AtomicCounter::<u64>::new(1),
497 next_file_object_id: Default::default(),
498 system_limits,
499 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
501 disable_unprivileged_bpf: AtomicU8::new(0), build_version: OnceCell::new(),
503 stats: Arc::new(KernelStats::default()),
504 delayed_releaser: Default::default(),
505 scheduler,
506 syslog: Default::default(),
507 mounts: Mounts::new(),
508 hrtimer_manager,
509 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
510 crash_reporter,
511 shutting_down: AtomicBool::new(false),
512 container_control_handle: Default::default(),
513 ebpf_state: Default::default(),
514 cgroups: Default::default(),
515 time_adjustment_proxy,
516 socket_tokens_store: Default::default(),
517 hwcaps,
518 syscall_log_filters: Default::default(),
519 });
520
521 this.device_registry.objects.init(&mut this.kthreads.unlocked_for_async(), &this);
525
526 let kernel = Arc::downgrade(&this);
529 this.inspect_node.record_lazy_child("thread_groups", move || {
530 if let Some(kernel) = kernel.upgrade() {
531 let inspector = kernel.get_thread_groups_inspect();
532 async move { Ok(inspector) }.boxed()
533 } else {
534 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
535 }
536 });
537
538 let kernel = Arc::downgrade(&this);
539 this.inspect_node.record_lazy_child("cgroupv2", move || {
540 if let Some(kernel) = kernel.upgrade() {
541 async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
542 } else {
543 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
544 }
545 });
546
547 Ok(this)
548 }
549
550 pub fn get_init_task(&self) -> Result<Arc<Task>, Errno> {
552 self.init_task.get().and_then(|t| t.upgrade()).ok_or_else(|| errno!(EINVAL))
553 }
554
555 pub fn shut_down(self: &Arc<Self>) {
558 self.kthreads.spawn_future(
561 {
562 let kernel = self.clone();
563 move || async move {
564 kernel.run_shutdown().await;
565 }
566 },
567 "run_shutdown",
568 );
569 }
570
571 async fn run_shutdown(&self) {
591 const INIT_PID: i32 = 1;
592 const SYSTEM_TASK_PID: i32 = 2;
593
594 if self
597 .shutting_down
598 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
599 .is_err()
600 {
601 log_info!("Additional thread tried to initiate shutdown while already in-progress.");
602 return;
603 }
604
605 log_info!("Shutting down Starnix kernel.");
606
607 loop {
610 let tgs = {
611 self.pids
614 .read()
615 .get_thread_groups()
616 .into_iter()
617 .filter(|tg| tg.leader != SYSTEM_TASK_PID && tg.leader != INIT_PID)
618 .collect::<Vec<_>>()
619 };
620 if tgs.is_empty() {
621 log_info!("pid table is empty except init and system task");
622 break;
623 }
624
625 log_info!(tgs:?; "shutting down thread groups");
626 let mut tasks = vec![];
627 for tg in tgs {
628 let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
629 tasks.push(task);
630 }
631 futures::future::join_all(tasks).await;
632 }
633
634 let maybe_init = self.get_init_task().ok().map(|t| Arc::downgrade(&t.thread_group));
636 if let Some(init) = maybe_init {
637 log_info!("shutting down init");
638 ThreadGroup::shut_down(init).await;
639 } else {
640 log_info!("init already terminated");
641 }
642
643 log_info!("cleaning up pinned memory");
645 self.expando.remove::<crate::mm::InfoCacheShadowProcess>();
646 self.expando.remove::<crate::mm::MlockShadowProcess>();
647
648 let kernel_job = fuchsia_runtime::job_default();
655 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
656 let own_koid = fuchsia_runtime::process_self().koid().unwrap();
657
658 log_info!("waiting for this to be the only process in the job");
659 loop {
660 let mut remaining_processes = kernel_job
661 .processes()
662 .unwrap()
663 .into_iter()
664 .filter(|pid| pid != &own_koid)
666 .peekable();
667 if remaining_processes.peek().is_none() {
668 log_info!("No stray Zircon processes.");
669 break;
670 }
671
672 let mut terminated_signals = vec![];
673 for pid in remaining_processes {
674 let handle = match kernel_job
675 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
676 {
677 Ok(h) => h,
678 Err(e) => {
679 log_info!(pid:?, e:?; "failed to get child process from job");
680 continue;
681 }
682 };
683 log_info!(
684 pid:?,
685 name:? = handle.get_name();
686 "waiting on process terminated signal"
687 );
688 terminated_signals
689 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
690 }
691 log_info!("waiting on process terminated signals");
692 futures::future::join_all(terminated_signals).await;
693 }
694
695 log_info!("clearing mounts");
697 self.mounts.clear();
698
699 log_info!("all non-root processes killed, notifying CF container is stopped");
701 if let Some(control_handle) = self.container_control_handle.lock().take() {
702 log_info!("Notifying CF that the container has stopped.");
703 control_handle
704 .send_on_stop(ComponentStopInfo {
705 termination_status: Some(zx::Status::OK.into_raw()),
706 exit_code: Some(0),
707 ..ComponentStopInfo::default()
708 })
709 .unwrap();
710 control_handle.shutdown_with_epitaph(zx::Status::OK);
711 } else {
712 log_warn!("Shutdown invoked without a container controller control handle.");
713 }
714
715 log_info!("All tasks killed, exiting Starnix kernel root process.");
717 zx::Process::exit(0);
725 }
726
727 pub fn is_shutting_down(&self) -> bool {
728 self.shutting_down.load(Ordering::Acquire)
729 }
730
731 pub fn allow_unprivileged_bpf(&self) -> bool {
732 self.disable_unprivileged_bpf.load(Ordering::Relaxed) == 0
733 }
734
735 pub fn open_device<L>(
737 &self,
738 locked: &mut Locked<L>,
739 current_task: &CurrentTask,
740 node: &NamespaceNode,
741 flags: OpenFlags,
742 dev: DeviceId,
743 mode: DeviceMode,
744 ) -> Result<Box<dyn FileOps>, Errno>
745 where
746 L: LockEqualOrBefore<FileOpsCore>,
747 {
748 self.device_registry.open_device(locked, current_task, node, flags, dev, mode)
749 }
750
751 pub fn audit_logger(&self) -> Arc<AuditLogger> {
755 self.expando.get_or_init(|| AuditLogger::new(self))
756 }
757
758 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
763 self.generic_netlink.get_or_init(|| {
764 let (generic_netlink, worker_params) = GenericNetlink::new();
765 let enable_nl80211 = self.features.wifi;
766 self.kthreads.spawn_future(
767 move || async move {
768 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
769 .await;
770 log_error!("Generic Netlink future unexpectedly exited");
771 },
772 "generic_netlink_worker",
773 );
774 generic_netlink
775 })
776 }
777
778 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
783 self.network_netlink.get_or_init(|| {
784 let (network_netlink, worker_params) =
785 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
786
787 let kernel = self.clone();
788 self.kthreads.spawn_future(
789 move || async move {
790 netlink::run_netlink_worker(
791 worker_params,
792 NetlinkAccessControl::new(kernel.kthreads.system_task()),
793 )
794 .await;
795 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
796 },
797 "network_netlink_worker",
798 );
799 network_netlink
800 })
801 }
802
803 pub fn iptables(&self) -> &IpTables {
804 self.iptables.get_or_init(|| IpTables::new())
805 }
806
807 #[allow(unused)]
809 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
810 &self,
811 filename: &str,
812 ) -> Result<ClientEnd<P>, Errno> {
813 match self.container_namespace.get_namespace_channel("/svc") {
814 Ok(channel) => {
815 let (client_end, server_end) = create_endpoints::<P>();
816 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
817 .map_err(|status| from_status_like_fdio!(status))?;
818 Ok(client_end)
819 }
820 Err(err) => {
821 log_error!("Unable to get /svc namespace channel! {}", err);
822 Err(errno!(ENOENT))
823 }
824 }
825 }
826
827 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
829 &self,
830 ) -> Result<ClientEnd<P>, Errno> {
831 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
832 }
833
834 pub fn add_syscall_log_filter(&self, name: &str) {
835 let filter = SyscallLogFilter::new(name.to_string());
836 {
837 let mut filters = self.syscall_log_filters.lock();
838 if filters.contains(&filter) {
839 return;
840 }
841 filters.push(filter);
842 }
843 for headers in self.pids.read().get_thread_groups() {
844 headers.sync_syscall_log_level();
845 }
846 }
847
848 pub fn clear_syscall_log_filters(&self) {
849 {
850 let mut filters = self.syscall_log_filters.lock();
851 if filters.is_empty() {
852 return;
853 }
854 filters.clear();
855 }
856 for headers in self.pids.read().get_thread_groups() {
857 headers.sync_syscall_log_level();
858 }
859 }
860
861 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
862 let inspector = fuchsia_inspect::Inspector::default();
863
864 let thread_groups = inspector.root();
865 let mut mm_summary = MappingSummary::default();
866 let mut mms_summarized = HashSet::new();
867
868 let all_thread_groups = {
870 let pid_table = self.pids.read();
871 pid_table.get_thread_groups()
872 };
873 for thread_group in all_thread_groups {
874 let (ppid, tasks) = {
876 let tg = thread_group.read();
877 (tg.get_ppid() as i64, tg.tasks())
878 };
879
880 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
881 if let Ok(koid) = thread_group.process.koid() {
882 tg_node.record_int("koid", koid.raw_koid() as i64);
883 }
884 tg_node.record_int("pid", thread_group.leader as i64);
885 tg_node.record_int("ppid", ppid);
886 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
887
888 let tasks_node = tg_node.create_child("tasks");
889 for task in tasks {
890 if let Ok(mm) = task.mm() {
891 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
892 mm.summarize(&mut mm_summary);
893 }
894 }
895 let set_properties = |node: &fuchsia_inspect::Node| {
896 node.record_string("command", task.command().to_string());
897
898 let scheduler_state = task.read().scheduler_state;
899 if !scheduler_state.is_default() {
900 node.record_child("sched", |node| {
901 node.record_string(
902 "role_name",
903 self.scheduler
904 .role_name(&task)
905 .map(|n| Cow::Borrowed(n))
906 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
907 );
908 node.record_string("state", format!("{scheduler_state:?}"));
909 });
910 }
911 };
912 if task.tid == thread_group.leader {
913 let mut argv = task.read_argv(256).unwrap_or_default();
914
915 argv.retain(|arg| !arg.is_empty());
918
919 let inspect_argv = tg_node.create_string_array("argv", argv.len());
920 for (i, arg) in argv.iter().enumerate() {
921 inspect_argv.set(i, arg.to_string());
922 }
923 tg_node.record(inspect_argv);
924
925 set_properties(&tg_node);
926 } else {
927 tasks_node.record_child(task.tid.to_string(), |task_node| {
928 set_properties(task_node);
929 });
930 };
931 }
932 tg_node.record(tasks_node);
933 thread_groups.record(tg_node);
934 }
935
936 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
937
938 inspector
939 }
940
941 pub fn new_memory_attribution_observer(
942 &self,
943 control_handle: fattribution::ProviderControlHandle,
944 ) -> attribution_server::Observer {
945 self.memory_attribution_manager.new_observer(control_handle)
946 }
947
948 pub fn open_ns_dir(
960 &self,
961 path: &str,
962 open_flags: fio::Flags,
963 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
964 let ns_path = PathBuf::from(path);
965 match self.container_namespace.find_closest_channel(&ns_path) {
966 Ok((root_channel, remaining_subdir)) => {
967 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
968 fdio::open_at(
969 &root_channel,
970 &remaining_subdir,
971 open_flags,
972 server_end.into_channel(),
973 )
974 .map_err(|e| {
975 log_error!("Failed to intialize the subdirs: {}", e);
976 errno!(EIO)
977 })?;
978
979 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
980 }
981 Err(err) => {
982 log_error!(
983 "Unable to find a channel for {}. Received error: {}",
984 ns_path.display(),
985 err
986 );
987 Err(errno!(ENOENT))
988 }
989 }
990 }
991
992 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
994 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
995 arg.split_once('=')
996 .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
997 .or(Some(ArgNameAndValue { name: arg, value: None }))
998 })
999 }
1000
1001 pub fn fs_cache_config(&self) -> CacheConfig {
1003 CacheConfig { capacity: self.features.dirent_cache_size as usize }
1004 }
1005}
1006
1007pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
1008 let mut args = Vec::new();
1009 let mut arg_start: Option<usize> = None;
1010 let mut in_quotes = false;
1011 let mut previous_char = ' ';
1012
1013 for (i, c) in cmdline.char_indices() {
1014 if let Some(start) = arg_start {
1015 match c {
1016 ' ' if !in_quotes => {
1017 args.push(&cmdline[start..i]);
1018 arg_start = None;
1019 }
1020 '"' if previous_char != '\\' => {
1021 in_quotes = !in_quotes;
1022 }
1023 _ => {}
1024 }
1025 } else if c != ' ' {
1026 arg_start = Some(i);
1027 if c == '"' {
1028 in_quotes = true;
1029 }
1030 }
1031 previous_char = c;
1032 }
1033 if let Some(start) = arg_start {
1034 args.push(&cmdline[start..]);
1035 }
1036 args.into_iter()
1037}
1038
1039impl std::fmt::Debug for Kernel {
1040 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1041 f.debug_struct("Kernel").finish()
1042 }
1043}
1044
1045#[cfg(target_arch = "aarch64")]
1047fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1048 use starnix_uapi::arch32;
1049 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1050 | arch32::HWCAP_THUMB
1051 | arch32::HWCAP_FAST_MULT
1052 | arch32::HWCAP_EDSP
1053 | arch32::HWCAP_TLS
1054 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
1056 | arch32::HWCAP_EVTSTRM;
1057
1058 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1059 let mut hwcap2 = 0;
1060 for feature in cpu_feature_flags.iter() {
1061 match feature {
1062 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1063 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1064 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1065 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1066 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1067 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1068 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1069 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1070 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1071 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1072 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1073 }
1074 _ => {}
1075 }
1076 }
1077 HwCap { hwcap, hwcap2 }
1078}
1079
1080#[cfg(target_arch = "aarch64")]
1081fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1082 use starnix_uapi;
1084 let mut hwcap = 0;
1085 let mut hwcap2 = 0;
1086
1087 for feature in cpu_feature_flags.iter() {
1088 match feature {
1089 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1090 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1091 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1092 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1093 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1094 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1095 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1096 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1097 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1098 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1099 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1100 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1101 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1102 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1103 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1104 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1105 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1106 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1107 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1108 _ => {}
1109 }
1110 }
1111 HwCap { hwcap, hwcap2 }
1112}
1113
1114impl HwCaps {
1115 #[cfg(target_arch = "aarch64")]
1116 pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1117 Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1118 }
1119
1120 #[cfg(not(target_arch = "aarch64"))]
1121 pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1122 Self { arch64: HwCap::default() }
1123 }
1124}
1125
1126#[cfg(test)]
1127mod test {
1128 use super::parse_cmdline;
1129
1130 #[test]
1131 fn test_parse_cmdline() {
1132 let cmdline =
1133 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1134 let expected = vec![
1135 "first",
1136 "second=third",
1137 "\"fourth fifth\"",
1138 "sixth=\"seventh eighth\"",
1139 "\"ninth\\\" tenth\"",
1140 "eleventh",
1141 ];
1142 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1143 }
1144}