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, create_watcher_for_wake_events};
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::{ClientEnd, DiscoverableProtocolMarker, ProtocolMarker, create_endpoints};
35use fidl_fuchsia_component_runner::{ComponentControllerControlHandle, ComponentStopInfo};
36use fidl_fuchsia_feedback::CrashReporterProxy;
37use fidl_fuchsia_io as fio;
38use fidl_fuchsia_memory_attribution as fattribution;
39use fidl_fuchsia_net_power as fnet_power;
40use fidl_fuchsia_net_resources as fnet_resources;
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 smallvec::SmallVec;
49use starnix_lifecycle::AtomicCounter;
50use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
51use starnix_sync::{
52 ComponentControllerLock, KernelSwapFiles, LockDepGuard, LockDepMutex, LockDepRwLock,
53 MountsLevel, PidToKoidMapLock, RwLock, RwSeqLock, RwSeqLockGuard, 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::cell::RefCell;
61use std::collections::{HashMap, HashSet};
62use std::num::NonZeroU64;
63use std::ops::Deref;
64use std::path::PathBuf;
65use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU16, Ordering};
66use std::sync::{Arc, OnceLock, Weak};
67use zx::CpuFeatureFlags;
68
69#[derive(Debug, Default, Clone)]
72pub struct KernelFeatures {
73 pub bpf_v2: bool,
74
75 pub enable_suid: bool,
82
83 pub io_uring: bool,
87
88 pub error_on_failed_reboot: bool,
91
92 pub default_seclabel: Option<String>,
96
97 pub selinux_test_suite: bool,
102
103 pub default_ns_mount_options: Option<HashMap<String, String>>,
108
109 pub default_uid: u32,
113
114 pub mlock_always_onfault: bool,
116
117 pub mlock_pin_flavor: MlockPinFlavor,
119
120 pub crash_report_throttling: bool,
122
123 pub wifi: bool,
125
126 pub cached_zx_map_info_bytes: u32,
128
129 pub dirent_cache_size: u32,
131
132 pub fake_ion: bool,
135}
136
137impl KernelFeatures {
138 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
142 if let Some(all_options) = &self.default_ns_mount_options {
143 if let Some(options) = all_options.get(ns_path) {
144 return MountParams::parse(options.as_bytes().into());
145 }
146 }
147 Ok(MountParams::default())
148 }
149}
150
151pub struct ArgNameAndValue<'a> {
153 pub name: &'a str,
154 pub value: Option<&'a str>,
155}
156
157type DeferredDropCallback = Box<dyn FnOnce() + Send + Sync>;
158
159pub struct MountsWriteToken {
164 deferred_drops: RefCell<SmallVec<[DeferredDropCallback; 8]>>,
165}
166
167impl MountsWriteToken {
168 fn new() -> Self {
169 Self { deferred_drops: RefCell::new(SmallVec::new()) }
170 }
171
172 pub fn defer_drop<T: Send + Sync + 'static>(&self, value: T) {
176 self.deferred_drops.borrow_mut().push(Box::new(move || {
177 drop(value);
178 }));
179 }
180
181 pub fn retain<T: Send + Sync + 'static>(&self, value: &Arc<T>) -> Arc<T> {
183 self.defer_drop(value.clone());
184 value.clone()
185 }
186
187 fn take_deferred_drops(&mut self) -> SmallVec<[DeferredDropCallback; 8]> {
188 std::mem::take(self.deferred_drops.get_mut())
189 }
190}
191
192pub struct MountsWriteGuard<'a> {
195 guard: Option<RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>>,
196}
197
198impl<'a> MountsWriteGuard<'a> {
199 fn new(guard: RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>) -> Self {
200 Self { guard: Some(guard) }
201 }
202}
203
204impl<'a> Deref for MountsWriteGuard<'a> {
205 type Target = MountsWriteToken;
206 fn deref(&self) -> &Self::Target {
207 self.guard.as_ref().unwrap()
208 }
209}
210
211impl<'a> Drop for MountsWriteGuard<'a> {
212 fn drop(&mut self) {
213 if let Some(mut guard) = self.guard.take() {
214 let drops = guard.take_deferred_drops();
215 drop(guard);
216 for callback in drops {
217 callback();
218 }
219 }
220 }
221}
222
223pub struct Kernel {
233 pub weak_self: Weak<Kernel>,
235
236 pub kthreads: KernelThreads,
238
239 pub features: KernelFeatures,
241
242 pub pids: RwLock<PidTable>,
244
245 pub init_task: OnceLock<Weak<Task>>,
247
248 pub pid_to_koid_mapping: Arc<LockDepRwLock<Option<PidToKoidMap>, PidToKoidMapLock>>,
250
251 pub expando: Expando,
256
257 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
263
264 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
266
267 pub cmdline: BString,
269
270 pub device_tree: Option<Devicetree>,
271
272 pub security_state: security::KernelState,
274
275 pub device_registry: DeviceRegistry,
277
278 pub container_namespace: ContainerNamespace,
282
283 pub mounts_lock: RwSeqLock<LockDepMutex<MountsWriteToken, MountsLevel>>,
292
293 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
295
296 iptables: OnceLock<IpTables>,
298
299 pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
301
302 pub root_uts_ns: UtsNamespaceHandle,
307
308 pub vdso: Vdso,
310
311 pub vdso_arch32: Option<Vdso>,
315
316 pub netstack_devices: Arc<NetstackDevices>,
319
320 pub swap_files: LockDepMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
324
325 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
327
328 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
330
331 pub inspect_node: fuchsia_inspect::Node,
333
334 pub actions_logged: AtomicU16,
341
342 pub suspend_resume_manager: SuspendResumeManagerHandle,
344
345 pub next_mount_id: AtomicCounter<u64>,
347 pub next_peer_group_id: AtomicCounter<u64>,
348 pub next_namespace_id: AtomicCounter<u64>,
349
350 pub next_file_object_id: AtomicCounter<u64>,
352
353 pub ptrace_scope: AtomicU8,
355
356 pub build_version: OnceCell<String>,
358
359 pub stats: Arc<KernelStats>,
360
361 pub system_limits: SystemLimits,
363
364 pub delayed_releaser: DelayedReleaser,
368
369 pub scheduler: SchedulerManager,
371
372 pub syslog: Syslog,
374
375 pub mounts: Mounts,
377
378 pub hrtimer_manager: HrTimerManagerHandle,
380
381 pub memory_attribution_manager: MemoryAttributionManager,
383
384 pub crash_reporter: CrashReporter,
386
387 shutting_down: AtomicBool,
389
390 pub restrict_dmesg: AtomicBool,
393
394 pub disable_unprivileged_bpf: AtomicU8,
399
400 pub container_control_handle:
402 LockDepMutex<Option<ComponentControllerControlHandle>, ComponentControllerLock>,
403
404 pub ebpf_state: EbpfState,
406
407 pub cgroups: KernelCgroups,
409
410 pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
413
414 pub netstack_wake_group: OnceLock<Option<fnet_resources::WakeGroupToken>>,
421
422 pub socket_tokens_store: SocketTokensStore,
424
425 pub hwcaps: HwCaps,
427
428 pub syscall_log_filters: LockDepMutex<Vec<SyscallLogFilter>, SyscallLogFiltersLock>,
431}
432
433#[derive(Debug, Clone, Copy, Default)]
435pub struct HwCap {
436 pub hwcap: u32,
438 pub hwcap2: u32,
440}
441
442#[derive(Debug, Clone, Copy, Default)]
444pub struct HwCaps {
445 #[cfg(target_arch = "aarch64")]
447 pub arch32: HwCap,
448 pub arch64: HwCap,
450}
451
452struct InterfacesHandlerImpl(Weak<Kernel>);
459
460impl InterfacesHandlerImpl {
461 fn kernel(&self) -> Option<Arc<Kernel>> {
462 self.0.upgrade()
463 }
464}
465
466impl InterfacesHandler for InterfacesHandlerImpl {
467 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
468 if let Some(kernel) = self.kernel() {
469 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
470 }
471 }
472
473 fn handle_deleted_link(&mut self, name: &str) {
474 if let Some(kernel) = self.kernel() {
475 kernel.netstack_devices.remove_device(&kernel, name.into());
476 }
477 }
478
479 fn handle_idle_event(&mut self) {
480 let Some(kernel) = self.kernel() else {
481 log_error!("kernel went away while netlink is initializing");
482 return;
483 };
484 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
485 if initialized.swap(true, Ordering::SeqCst) {
486 log_error!("netlink initial devices should only be reported once");
487 return;
488 }
489 wq.notify_all()
490 }
491}
492
493impl Kernel {
494 pub fn new(
495 cmdline: BString,
496 features: KernelFeatures,
497 system_limits: SystemLimits,
498 container_namespace: ContainerNamespace,
499 scheduler: SchedulerManager,
500 crash_reporter_proxy: Option<CrashReporterProxy>,
501 inspect_node: fuchsia_inspect::Node,
502 security_state: security::KernelState,
503 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
504 device_tree: Option<Devicetree>,
505 ) -> Result<Arc<Kernel>, zx::Status> {
506 let unix_address_maker =
507 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
508 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
509 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
510 });
511
512 let crash_reporter = CrashReporter::new(
513 &inspect_node,
514 crash_reporter_proxy,
515 zx::Duration::from_minutes(8),
516 features.crash_report_throttling,
517 );
518 let hrtimer_manager = HrTimerManager::new(&inspect_node);
519
520 let cpu_feature_flags =
521 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
522 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
523 CpuFeatureFlags::empty()
524 });
525 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
526
527 let this = Arc::new_cyclic(|kernel| Kernel {
528 weak_self: kernel.clone(),
529 kthreads: KernelThreads::new(kernel.clone()),
530 features,
531 pids: Default::default(),
532 init_task: OnceLock::new(),
533 pid_to_koid_mapping: Default::default(),
534 expando: Default::default(),
535 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
536 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
537 vsock_address_maker,
538 ),
539 cmdline,
540 device_tree,
541 security_state,
542 device_registry: Default::default(),
543 container_namespace,
544 mounts_lock: RwSeqLock::new(MountsWriteToken::new().into()),
545 remote_block_device_registry: Default::default(),
546 iptables: OnceLock::new(),
547 shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
548 root_uts_ns: Arc::new(UtsNamespace::default().into()),
549 vdso: Vdso::new(),
550 vdso_arch32: Vdso::new_arch32(),
551 netstack_devices: Arc::default(),
552 swap_files: Default::default(),
553 generic_netlink: OnceLock::new(),
554 network_netlink: OnceLock::new(),
555 inspect_node,
556 actions_logged: AtomicU16::new(0),
557 suspend_resume_manager: Default::default(),
558 next_mount_id: AtomicCounter::<u64>::new(1),
559 next_peer_group_id: AtomicCounter::<u64>::new(1),
560 next_namespace_id: AtomicCounter::<u64>::new(1),
561 next_file_object_id: Default::default(),
562 system_limits,
563 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
565 disable_unprivileged_bpf: AtomicU8::new(0), build_version: OnceCell::new(),
567 stats: Arc::new(KernelStats::default()),
568 delayed_releaser: Default::default(),
569 scheduler,
570 syslog: Default::default(),
571 mounts: Mounts::new(),
572 hrtimer_manager,
573 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
574 crash_reporter,
575 shutting_down: AtomicBool::new(false),
576 container_control_handle: Default::default(),
577 ebpf_state: Default::default(),
578 cgroups: Default::default(),
579 time_adjustment_proxy,
580 netstack_wake_group: OnceLock::new(),
581 socket_tokens_store: Default::default(),
582 hwcaps,
583 syscall_log_filters: Default::default(),
584 });
585
586 this.device_registry.objects.init(&this);
590
591 let kernel = Arc::downgrade(&this);
594 this.inspect_node.record_lazy_child("thread_groups", move || {
595 if let Some(kernel) = kernel.upgrade() {
596 let inspector = kernel.get_thread_groups_inspect();
597 async move { Ok(inspector) }.boxed()
598 } else {
599 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
600 }
601 });
602
603 let kernel = Arc::downgrade(&this);
604 this.inspect_node.record_lazy_child("cgroupv2", move || {
605 if let Some(kernel) = kernel.upgrade() {
606 async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
607 } else {
608 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
609 }
610 });
611
612 Ok(this)
613 }
614
615 pub fn get_init_task(&self) -> Result<Arc<Task>, Errno> {
617 self.init_task.get().and_then(|t| t.upgrade()).ok_or_else(|| errno!(EINVAL))
618 }
619
620 pub fn shut_down(self: &Arc<Self>) {
623 self.kthreads.spawn_future(
626 {
627 let kernel = self.clone();
628 move || async move {
629 kernel.run_shutdown().await;
630 }
631 },
632 "run_shutdown",
633 );
634 }
635
636 async fn run_shutdown(&self) {
656 const INIT_PID: i32 = 1;
657 const SYSTEM_TASK_PID: i32 = 2;
658
659 if self
662 .shutting_down
663 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
664 .is_err()
665 {
666 log_info!("Additional thread tried to initiate shutdown while already in-progress.");
667 return;
668 }
669
670 log_info!("Shutting down Starnix kernel.");
671
672 loop {
675 let tgs = {
676 self.pids
679 .read()
680 .get_thread_groups()
681 .into_iter()
682 .filter(|tg| tg.leader != SYSTEM_TASK_PID && tg.leader != INIT_PID)
683 .collect::<Vec<_>>()
684 };
685 if tgs.is_empty() {
686 log_info!("pid table is empty except init and system task");
687 break;
688 }
689
690 log_info!(tgs:?; "shutting down thread groups");
691 let mut tasks = vec![];
692 for tg in tgs {
693 let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
694 tasks.push(task);
695 }
696 futures::future::join_all(tasks).await;
697 }
698
699 let maybe_init = self.get_init_task().ok().map(|t| Arc::downgrade(&t.thread_group));
701 if let Some(init) = maybe_init {
702 log_info!("shutting down init");
703 ThreadGroup::shut_down(init).await;
704 } else {
705 log_info!("init already terminated");
706 }
707
708 log_info!("cleaning up pinned memory");
710 self.expando.remove::<crate::mm::InfoCacheShadowProcess>();
711 self.expando.remove::<crate::mm::MlockShadowProcess>();
712
713 let kernel_job = fuchsia_runtime::job_default();
720 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
721 let own_koid = fuchsia_runtime::process_self().koid().unwrap();
722
723 log_info!("waiting for this to be the only process in the job");
724 loop {
725 let mut remaining_processes = kernel_job
726 .processes()
727 .unwrap()
728 .into_iter()
729 .filter(|pid| pid != &own_koid)
731 .peekable();
732 if remaining_processes.peek().is_none() {
733 log_info!("No stray Zircon processes.");
734 break;
735 }
736
737 let mut terminated_signals = vec![];
738 for pid in remaining_processes {
739 let handle = match kernel_job
740 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
741 {
742 Ok(h) => h,
743 Err(e) => {
744 log_info!(pid:?, e:?; "failed to get child process from job");
745 continue;
746 }
747 };
748 log_info!(
749 pid:?,
750 name:? = handle.get_name();
751 "waiting on process terminated signal"
752 );
753 terminated_signals
754 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
755 }
756 log_info!("waiting on process terminated signals");
757 futures::future::join_all(terminated_signals).await;
758 }
759
760 log_info!("clearing mounts");
762 self.mounts.clear();
763
764 log_info!("all non-root processes killed, notifying CF container is stopped");
766 if let Some(control_handle) = self.container_control_handle.lock().take() {
767 log_info!("Notifying CF that the container has stopped.");
768 control_handle
769 .send_on_stop(ComponentStopInfo {
770 termination_status: Some(zx::Status::OK.into_raw()),
771 exit_code: Some(0),
772 ..ComponentStopInfo::default()
773 })
774 .unwrap();
775 control_handle.shutdown_with_epitaph(Ok(()));
776 } else {
777 log_warn!("Shutdown invoked without a container controller control handle.");
778 }
779
780 log_info!("All tasks killed, exiting Starnix kernel root process.");
782 zx::Process::exit(0);
790 }
791
792 pub fn is_shutting_down(&self) -> bool {
793 self.shutting_down.load(Ordering::Acquire)
794 }
795
796 pub fn allow_unprivileged_bpf(&self) -> bool {
797 self.disable_unprivileged_bpf.load(Ordering::Relaxed) == 0
798 }
799
800 pub fn open_device(
802 &self,
803 current_task: &CurrentTask,
804 node: &NamespaceNode,
805 flags: OpenFlags,
806 dev: DeviceId,
807 mode: DeviceMode,
808 ) -> Result<Box<dyn FileOps>, Errno> {
809 self.device_registry.open_device(current_task, node, flags, dev, mode)
810 }
811
812 pub fn audit_logger(&self) -> Arc<AuditLogger> {
816 self.expando.get_or_init(|| AuditLogger::new(self))
817 }
818
819 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
824 self.generic_netlink.get_or_init(|| {
825 let (generic_netlink, worker_params) = GenericNetlink::new();
826 let enable_nl80211 = self.features.wifi;
827 self.kthreads.spawn_future(
828 move || async move {
829 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
830 .await;
831 log_error!("Generic Netlink future unexpectedly exited");
832 },
833 "generic_netlink_worker",
834 );
835 generic_netlink
836 })
837 }
838
839 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
844 self.network_netlink.get_or_init(|| {
845 let (network_netlink, worker_params) =
846 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
847
848 let kernel = self.clone();
849 self.kthreads.spawn_future(
850 move || async move {
851 netlink::run_netlink_worker(
852 worker_params,
853 NetlinkAccessControl::new(kernel.kthreads.system_task()),
854 )
855 .await;
856 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
857 },
858 "network_netlink_worker",
859 );
860 network_netlink
861 })
862 }
863
864 pub(crate) fn netstack_wake_group(&self) -> Option<&fnet_resources::WakeGroupToken> {
870 self.netstack_wake_group
871 .get_or_init(|| {
872 const GROUP_WAKEUP_SIGNAL: zx::Signals =
875 zx::Signals::from_bits(fnet_power::GROUP_WAKEUP_SIGNAL).unwrap();
876
877 let provider = fuchsia_component::client::connect_to_protocol_sync::<
878 fnet_power::WakeGroupProviderMarker,
879 >()
880 .expect("connect to WakeGroupProvider");
881
882 let (wake_watcher_waiter, wake_watcher_signaller) = zx::EventPair::create();
883 create_watcher_for_wake_events(wake_watcher_signaller);
884
885 let token = match provider.create_wake_group(
886 &fnet_power::WakeGroupOptions {
887 debug_name: Some(String::from("starnix")),
888 ..Default::default()
889 },
890 wake_watcher_waiter,
891 zx::Instant::INFINITE,
892 ) {
893 Ok(fnet_power::CreateWakeGroupResponse { token, .. }) => token,
894 Err(e) => {
895 log_error!("failed to create wake group with the netstack: {e}");
896 return None;
897 }
898 };
899
900 let token = token.expect("netstack provides a wake group token");
901 let wake_source = token
902 .token
903 .duplicate_handle(zx::Rights::SAME_RIGHTS)
904 .expect("duplicate handle to token");
905 self.suspend_resume_manager
906 .add_external_wake_source(
907 wake_source.into(),
908 GROUP_WAKEUP_SIGNAL,
909 "netstack-wake-group".into(),
910 )
911 .expect("add wake group as wake source");
912
913 Some(token)
914 })
915 .as_ref()
916 }
917
918 pub fn iptables(&self) -> &IpTables {
919 self.iptables.get_or_init(|| IpTables::new())
920 }
921
922 #[allow(unused)]
924 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
925 &self,
926 filename: &str,
927 ) -> Result<ClientEnd<P>, Errno> {
928 match self.container_namespace.get_namespace_channel("/svc") {
929 Ok(channel) => {
930 let (client_end, server_end) = create_endpoints::<P>();
931 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
932 .map_err(|status| from_status_like_fdio!(status))?;
933 Ok(client_end)
934 }
935 Err(err) => {
936 log_error!("Unable to get /svc namespace channel! {}", err);
937 Err(errno!(ENOENT))
938 }
939 }
940 }
941
942 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
944 &self,
945 ) -> Result<ClientEnd<P>, Errno> {
946 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
947 }
948
949 pub fn add_syscall_log_filter(&self, name: &str) {
950 let filter = SyscallLogFilter::new(name.to_string());
951 {
952 let mut filters = self.syscall_log_filters.lock();
953 if filters.contains(&filter) {
954 return;
955 }
956 filters.push(filter);
957 }
958 for headers in self.pids.read().get_thread_groups() {
959 headers.sync_syscall_log_level();
960 }
961 }
962
963 pub fn clear_syscall_log_filters(&self) {
964 {
965 let mut filters = self.syscall_log_filters.lock();
966 if filters.is_empty() {
967 return;
968 }
969 filters.clear();
970 }
971 for headers in self.pids.read().get_thread_groups() {
972 headers.sync_syscall_log_level();
973 }
974 }
975
976 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
977 let inspector = fuchsia_inspect::Inspector::default();
978
979 let thread_groups = inspector.root();
980 let mut mm_summary = MappingSummary::default();
981 let mut mms_summarized = HashSet::new();
982
983 let all_thread_groups = {
985 let pid_table = self.pids.read();
986 pid_table.get_thread_groups()
987 };
988 for thread_group in all_thread_groups {
989 let (ppid, tasks) = {
991 let tg = thread_group.read();
992 (tg.get_ppid() as i64, tg.tasks())
993 };
994
995 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
996 if let Ok(koid) = thread_group.process.koid() {
997 tg_node.record_int("koid", koid.raw_koid() as i64);
998 }
999 tg_node.record_int("pid", thread_group.leader as i64);
1000 tg_node.record_int("ppid", ppid);
1001 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
1002
1003 let tasks_node = tg_node.create_child("tasks");
1004 for task in tasks {
1005 if let Ok(mm) = task.mm() {
1006 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
1007 mm.summarize(&mut mm_summary);
1008 }
1009 }
1010 let set_properties = |node: &fuchsia_inspect::Node| {
1011 node.record_string("command", task.command().to_string());
1012
1013 let scheduler_state = task.read().scheduler_state;
1014 if !scheduler_state.is_default() {
1015 node.record_child("sched", |node| {
1016 node.record_string(
1017 "role_name",
1018 self.scheduler
1019 .role_name(&task)
1020 .map(|n| Cow::Borrowed(n))
1021 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
1022 );
1023 node.record_string("state", format!("{scheduler_state:?}"));
1024 });
1025 }
1026 };
1027 if task.tid == thread_group.leader {
1028 let mut argv = task.read_argv(256).unwrap_or_default();
1029
1030 argv.retain(|arg| !arg.is_empty());
1033
1034 let inspect_argv = tg_node.create_string_array("argv", argv.len());
1035 for (i, arg) in argv.iter().enumerate() {
1036 inspect_argv.set(i, arg.to_string());
1037 }
1038 tg_node.record(inspect_argv);
1039
1040 set_properties(&tg_node);
1041 } else {
1042 tasks_node.record_child(task.tid.to_string(), |task_node| {
1043 set_properties(task_node);
1044 });
1045 };
1046 }
1047 tg_node.record(tasks_node);
1048 thread_groups.record(tg_node);
1049 }
1050
1051 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
1052
1053 inspector
1054 }
1055
1056 pub fn new_memory_attribution_observer(
1057 &self,
1058 control_handle: fattribution::ProviderControlHandle,
1059 ) -> attribution_server::Observer {
1060 self.memory_attribution_manager.new_observer(control_handle)
1061 }
1062
1063 pub fn open_ns_dir(
1075 &self,
1076 path: &str,
1077 open_flags: fio::Flags,
1078 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
1079 let ns_path = PathBuf::from(path);
1080 match self.container_namespace.find_closest_channel(&ns_path) {
1081 Ok((root_channel, remaining_subdir)) => {
1082 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
1083 fdio::open_at(
1084 &root_channel,
1085 &remaining_subdir,
1086 open_flags,
1087 server_end.into_channel(),
1088 )
1089 .map_err(|e| {
1090 log_error!("Failed to intialize the subdirs: {}", e);
1091 errno!(EIO)
1092 })?;
1093
1094 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
1095 }
1096 Err(err) => {
1097 log_error!(
1098 "Unable to find a channel for {}. Received error: {}",
1099 ns_path.display(),
1100 err
1101 );
1102 Err(errno!(ENOENT))
1103 }
1104 }
1105 }
1106
1107 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
1109 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
1110 arg.split_once('=')
1111 .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
1112 .or(Some(ArgNameAndValue { name: arg, value: None }))
1113 })
1114 }
1115
1116 pub fn fs_cache_config(&self) -> CacheConfig {
1118 CacheConfig { capacity: self.features.dirent_cache_size as usize }
1119 }
1120
1121 pub fn mounts_lock(&self) -> MountsWriteGuard<'_> {
1122 MountsWriteGuard::new(self.mounts_lock.lock())
1123 }
1124}
1125
1126pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
1127 let mut args = Vec::new();
1128 let mut arg_start: Option<usize> = None;
1129 let mut in_quotes = false;
1130 let mut previous_char = ' ';
1131
1132 for (i, c) in cmdline.char_indices() {
1133 if let Some(start) = arg_start {
1134 match c {
1135 ' ' if !in_quotes => {
1136 args.push(&cmdline[start..i]);
1137 arg_start = None;
1138 }
1139 '"' if previous_char != '\\' => {
1140 in_quotes = !in_quotes;
1141 }
1142 _ => {}
1143 }
1144 } else if c != ' ' {
1145 arg_start = Some(i);
1146 if c == '"' {
1147 in_quotes = true;
1148 }
1149 }
1150 previous_char = c;
1151 }
1152 if let Some(start) = arg_start {
1153 args.push(&cmdline[start..]);
1154 }
1155 args.into_iter()
1156}
1157
1158impl std::fmt::Debug for Kernel {
1159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1160 f.debug_struct("Kernel").finish()
1161 }
1162}
1163
1164#[cfg(target_arch = "aarch64")]
1166fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1167 use starnix_uapi::arch32;
1168 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1169 | arch32::HWCAP_THUMB
1170 | arch32::HWCAP_FAST_MULT
1171 | arch32::HWCAP_EDSP
1172 | arch32::HWCAP_TLS
1173 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
1175 | arch32::HWCAP_EVTSTRM;
1176
1177 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1178 let mut hwcap2 = 0;
1179 for feature in cpu_feature_flags.iter() {
1180 match feature {
1181 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1182 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1183 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1184 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1185 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1186 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1187 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1188 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1189 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1190 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1191 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1192 }
1193 _ => {}
1194 }
1195 }
1196 HwCap { hwcap, hwcap2 }
1197}
1198
1199#[cfg(target_arch = "aarch64")]
1200fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1201 use starnix_uapi;
1203 let mut hwcap = 0;
1204 let mut hwcap2 = 0;
1205
1206 for feature in cpu_feature_flags.iter() {
1207 match feature {
1208 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1209 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1210 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1211 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1212 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1213 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1214 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1215 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1216 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1217 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1218 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1219 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1220 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1221 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1222 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1223 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1224 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1225 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1226 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1227 _ => {}
1228 }
1229 }
1230 HwCap { hwcap, hwcap2 }
1231}
1232
1233impl HwCaps {
1234 #[cfg(target_arch = "aarch64")]
1235 pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1236 Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1237 }
1238
1239 #[cfg(not(target_arch = "aarch64"))]
1240 pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1241 Self { arch64: HwCap::default() }
1242 }
1243}
1244
1245#[cfg(test)]
1246mod test {
1247 use super::parse_cmdline;
1248
1249 #[test]
1250 fn test_parse_cmdline() {
1251 let cmdline =
1252 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1253 let expected = vec![
1254 "first",
1255 "second=third",
1256 "\"fourth fifth\"",
1257 "sixth=\"seventh eighth\"",
1258 "\"ninth\\\" tenth\"",
1259 "eleventh",
1260 ];
1261 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1262 }
1263}