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::TracePerformanceEventManager;
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 fuchsia_rcu::RcuReadScope;
45use futures::FutureExt;
46use netlink::interfaces::InterfacesHandler;
47use netlink::{NETLINK_LOG_TAG, Netlink};
48use once_cell::sync::OnceCell;
49use scopeguard::ScopeGuard;
50use smallvec::SmallVec;
51use starnix_lifecycle::AtomicCounter;
52use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
53use starnix_sync::{
54 ComponentControllerLock, KernelSwapFiles, LockDepGuard, LockDepMutex, MountsLevel, RwSeqLock,
55 RwSeqLockGuard, SyscallLogFiltersLock,
56};
57use starnix_uapi::device_id::DeviceId;
58use starnix_uapi::errors::{Errno, errno};
59use starnix_uapi::open_flags::OpenFlags;
60use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
61use std::borrow::Cow;
62use std::cell::RefCell;
63use std::collections::{HashMap, HashSet};
64use std::num::NonZeroU64;
65use std::ops::Deref;
66use std::path::PathBuf;
67use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU8, AtomicU16, Ordering};
68use std::sync::{Arc, OnceLock, Weak};
69use zx::CpuFeatureFlags;
70
71#[derive(Debug, Default, Clone)]
74pub struct KernelFeatures {
75 pub bpf_v2: bool,
76
77 pub enable_suid: bool,
84
85 pub io_uring: bool,
89
90 pub error_on_failed_reboot: bool,
93
94 pub default_seclabel: Option<String>,
98
99 pub selinux_test_suite: bool,
104
105 pub default_ns_mount_options: Option<HashMap<String, String>>,
110
111 pub default_uid: u32,
115
116 pub mlock_always_onfault: bool,
118
119 pub mlock_pin_flavor: MlockPinFlavor,
121
122 pub crash_report_throttling: bool,
124
125 pub wifi: bool,
127
128 pub cached_zx_map_info_bytes: u32,
130
131 pub dirent_cache_size: u32,
133
134 pub fake_ion: bool,
137
138 pub remotevol_in_process: bool,
140
141 pub log_sysfs_lookup_misses: bool,
143}
144
145impl KernelFeatures {
146 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
150 if let Some(all_options) = &self.default_ns_mount_options {
151 if let Some(options) = all_options.get(ns_path) {
152 return MountParams::parse(options.as_bytes().into());
153 }
154 }
155 Ok(MountParams::default())
156 }
157}
158
159pub struct ArgNameAndValue<'a> {
161 pub name: &'a str,
162 pub value: Option<&'a str>,
163}
164
165type DeferredDropCallback = Box<dyn FnOnce() + Send + Sync>;
166
167pub struct MountsWriteToken {
172 deferred_drops: RefCell<SmallVec<[DeferredDropCallback; 8]>>,
173}
174
175impl MountsWriteToken {
176 fn new() -> Self {
177 Self { deferred_drops: RefCell::new(SmallVec::new()) }
178 }
179
180 fn defer_drop_internal<T: Send + Sync + 'static>(&self, value: T) {
181 self.deferred_drops.borrow_mut().push(Box::new(move || {
182 drop(value);
183 }));
184 }
185
186 pub fn defer_drop<T: Send + Sync + 'static>(
190 &self,
191 value: T,
192 ) -> ScopeGuard<T, impl FnOnce(T) + '_> {
193 scopeguard::guard(value, |value| {
194 self.defer_drop_internal(value);
195 })
196 }
197
198 pub fn retain<T: Send + Sync + 'static>(&self, value: &Arc<T>) -> Arc<T> {
200 self.defer_drop_internal(value.clone());
201 value.clone()
202 }
203
204 fn take_deferred_drops(&mut self) -> SmallVec<[DeferredDropCallback; 8]> {
205 std::mem::take(self.deferred_drops.get_mut())
206 }
207}
208
209pub struct MountsWriteGuard<'a> {
212 guard: Option<RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>>,
213}
214
215impl<'a> MountsWriteGuard<'a> {
216 fn new(guard: RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>) -> Self {
217 Self { guard: Some(guard) }
218 }
219}
220
221impl<'a> Deref for MountsWriteGuard<'a> {
222 type Target = MountsWriteToken;
223 fn deref(&self) -> &Self::Target {
224 self.guard.as_ref().unwrap()
225 }
226}
227
228impl<'a> Drop for MountsWriteGuard<'a> {
229 fn drop(&mut self) {
230 if let Some(mut guard) = self.guard.take() {
231 let drops = guard.take_deferred_drops();
232 drop(guard);
233 for callback in drops {
234 callback();
235 }
236 }
237 }
238}
239
240pub struct Kernel {
250 pub weak_self: Weak<Kernel>,
252
253 pub kthreads: KernelThreads,
255
256 pub features: KernelFeatures,
258
259 pub pids: PidTable,
261
262 pub init_task: OnceLock<Weak<Task>>,
264
265 pub trace_event_manager: Arc<TracePerformanceEventManager>,
269
270 pub expando: Expando,
275
276 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
282
283 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
285
286 pub cmdline: BString,
288
289 pub device_tree: Option<Devicetree>,
290
291 pub security_state: security::KernelState,
293
294 pub device_registry: DeviceRegistry,
296
297 pub container_namespace: ContainerNamespace,
301
302 pub mounts_lock: RwSeqLock<LockDepMutex<MountsWriteToken, MountsLevel>>,
311
312 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
314
315 iptables: OnceLock<IpTables>,
317
318 pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
320
321 pub root_uts_ns: UtsNamespaceHandle,
326
327 pub vdso: Vdso,
329
330 pub vdso_arch32: Option<Vdso>,
334
335 pub netstack_devices: Arc<NetstackDevices>,
338
339 pub swap_files: LockDepMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
343
344 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
346
347 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
349
350 pub inspect_node: fuchsia_inspect::Node,
352
353 pub actions_logged: AtomicU16,
360
361 pub suspend_resume_manager: SuspendResumeManagerHandle,
363
364 pub next_mount_id: AtomicCounter<u64>,
366 pub next_peer_group_id: AtomicCounter<u64>,
367 pub next_namespace_id: AtomicCounter<u64>,
368
369 pub next_file_object_id: AtomicCounter<u64>,
371
372 pub ptrace_scope: AtomicU8,
374
375 pub build_version: OnceCell<String>,
377
378 pub stats: Arc<KernelStats>,
379
380 pub system_limits: SystemLimits,
382
383 pub delayed_releaser: DelayedReleaser,
387
388 pub scheduler: SchedulerManager,
390
391 pub syslog: Syslog,
393
394 pub mounts: Mounts,
396
397 pub hrtimer_manager: HrTimerManagerHandle,
399
400 pub memory_attribution_manager: MemoryAttributionManager,
402
403 pub crash_reporter: CrashReporter,
405
406 shutting_down: AtomicBool,
408
409 pub restrict_dmesg: AtomicBool,
412
413 pub disable_unprivileged_bpf: AtomicU8,
418
419 pub perf_event_paranoid: AtomicI32,
425
426 pub container_control_handle:
428 LockDepMutex<Option<ComponentControllerControlHandle>, ComponentControllerLock>,
429
430 pub ebpf_state: EbpfState,
432
433 pub cgroups: KernelCgroups,
435
436 pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
439
440 pub netstack_wake_group: OnceLock<Option<fnet_resources::WakeGroupToken>>,
447
448 pub socket_tokens_store: SocketTokensStore,
450
451 pub hwcaps: HwCaps,
453
454 pub syscall_log_filters: LockDepMutex<Vec<SyscallLogFilter>, SyscallLogFiltersLock>,
457}
458
459#[derive(Debug, Clone, Copy, Default)]
461pub struct HwCap {
462 pub hwcap: u32,
464 pub hwcap2: u32,
466}
467
468#[derive(Debug, Clone, Copy, Default)]
470pub struct HwCaps {
471 #[cfg(target_arch = "aarch64")]
473 pub arch32: HwCap,
474 pub arch64: HwCap,
476}
477
478struct InterfacesHandlerImpl(Weak<Kernel>);
485
486impl InterfacesHandlerImpl {
487 fn kernel(&self) -> Option<Arc<Kernel>> {
488 self.0.upgrade()
489 }
490}
491
492impl InterfacesHandler for InterfacesHandlerImpl {
493 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
494 if let Some(kernel) = self.kernel() {
495 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
496 }
497 }
498
499 fn handle_deleted_link(&mut self, name: &str) {
500 if let Some(kernel) = self.kernel() {
501 kernel.netstack_devices.remove_device(&kernel, name.into());
502 }
503 }
504
505 fn handle_idle_event(&mut self) {
506 let Some(kernel) = self.kernel() else {
507 log_error!("kernel went away while netlink is initializing");
508 return;
509 };
510 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
511 if initialized.swap(true, Ordering::SeqCst) {
512 log_error!("netlink initial devices should only be reported once");
513 return;
514 }
515 wq.notify_all()
516 }
517}
518
519impl Kernel {
520 pub fn new(
521 cmdline: BString,
522 features: KernelFeatures,
523 system_limits: SystemLimits,
524 container_namespace: ContainerNamespace,
525 scheduler: SchedulerManager,
526 crash_reporter_proxy: Option<CrashReporterProxy>,
527 inspect_node: fuchsia_inspect::Node,
528 security_state: security::KernelState,
529 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
530 device_tree: Option<Devicetree>,
531 ) -> Result<Arc<Kernel>, zx::Status> {
532 let unix_address_maker =
533 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
534 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
535 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
536 });
537
538 let crash_reporter = CrashReporter::new(
539 &inspect_node,
540 crash_reporter_proxy,
541 zx::Duration::from_minutes(8),
542 features.crash_report_throttling,
543 );
544 let hrtimer_manager = HrTimerManager::new(&inspect_node);
545
546 let cpu_feature_flags =
547 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
548 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
549 CpuFeatureFlags::empty()
550 });
551 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
552
553 let this = Arc::new_cyclic(|kernel| Kernel {
554 weak_self: kernel.clone(),
555 kthreads: KernelThreads::new(kernel.clone()),
556 features,
557 pids: Default::default(),
558 init_task: OnceLock::new(),
559 trace_event_manager: Arc::new(TracePerformanceEventManager::new(kernel.clone())),
560 expando: Default::default(),
561 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
562 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
563 vsock_address_maker,
564 ),
565 cmdline,
566 device_tree,
567 security_state,
568 device_registry: Default::default(),
569 container_namespace,
570 mounts_lock: RwSeqLock::new(MountsWriteToken::new().into()),
571 remote_block_device_registry: Default::default(),
572 iptables: OnceLock::new(),
573 shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
574 root_uts_ns: Arc::new(UtsNamespace::default().into()),
575 vdso: Vdso::new(),
576 vdso_arch32: Vdso::new_arch32(),
577 netstack_devices: Arc::default(),
578 swap_files: Default::default(),
579 generic_netlink: OnceLock::new(),
580 network_netlink: OnceLock::new(),
581 inspect_node,
582 actions_logged: AtomicU16::new(0),
583 suspend_resume_manager: Default::default(),
584 next_mount_id: AtomicCounter::<u64>::new(1),
585 next_peer_group_id: AtomicCounter::<u64>::new(1),
586 next_namespace_id: AtomicCounter::<u64>::new(1),
587 next_file_object_id: Default::default(),
588 system_limits,
589 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
591 disable_unprivileged_bpf: AtomicU8::new(0), perf_event_paranoid: AtomicI32::new(2),
593 build_version: OnceCell::new(),
594 stats: Arc::new(KernelStats::default()),
595 delayed_releaser: Default::default(),
596 scheduler,
597 syslog: Default::default(),
598 mounts: Mounts::new(),
599 hrtimer_manager,
600 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
601 crash_reporter,
602 shutting_down: AtomicBool::new(false),
603 container_control_handle: Default::default(),
604 ebpf_state: Default::default(),
605 cgroups: Default::default(),
606 time_adjustment_proxy,
607 netstack_wake_group: OnceLock::new(),
608 socket_tokens_store: Default::default(),
609 hwcaps,
610 syscall_log_filters: Default::default(),
611 });
612
613 this.device_registry.objects.init(&this);
617
618 let kernel = Arc::downgrade(&this);
621 this.inspect_node.record_lazy_child("thread_groups", move || {
622 if let Some(kernel) = kernel.upgrade() {
623 let inspector = kernel.get_thread_groups_inspect();
624 async move { Ok(inspector) }.boxed()
625 } else {
626 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
627 }
628 });
629
630 let kernel = Arc::downgrade(&this);
631 this.inspect_node.record_lazy_child("cgroupv2", move || {
632 if let Some(kernel) = kernel.upgrade() {
633 async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
634 } else {
635 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
636 }
637 });
638
639 Ok(this)
640 }
641
642 pub fn get_init_task(&self) -> Result<Arc<Task>, Errno> {
644 self.init_task.get().and_then(|t| t.upgrade()).ok_or_else(|| errno!(EINVAL))
645 }
646
647 pub fn shut_down(self: &Arc<Self>) {
650 self.kthreads.spawn_future(
653 {
654 let kernel = self.clone();
655 move || async move {
656 kernel.run_shutdown().await;
657 }
658 },
659 "run_shutdown",
660 );
661 }
662
663 async fn run_shutdown(&self) {
683 const INIT_PID: i32 = 1;
684 const SYSTEM_TASK_PID: i32 = 2;
685
686 if self
689 .shutting_down
690 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
691 .is_err()
692 {
693 log_info!("Additional thread tried to initiate shutdown while already in-progress.");
694 return;
695 }
696
697 log_info!("Shutting down Starnix kernel.");
698
699 loop {
702 let tgs = self
703 .pids
704 .get_thread_groups(&RcuReadScope::new())
705 .filter(|tg| tg.leader.id != SYSTEM_TASK_PID && tg.leader.id != INIT_PID)
706 .collect::<Vec<_>>();
707 if tgs.is_empty() {
708 log_info!("pid table is empty except init and system task");
709 break;
710 }
711
712 log_info!(tgs:?; "shutting down thread groups");
713 let mut tasks = vec![];
714 for tg in tgs {
715 let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
716 tasks.push(task);
717 }
718 futures::future::join_all(tasks).await;
719 }
720
721 let maybe_init = self.get_init_task().ok().map(|t| Arc::downgrade(&t.thread_group));
723 if let Some(init) = maybe_init {
724 log_info!("shutting down init");
725 ThreadGroup::shut_down(init).await;
726 } else {
727 log_info!("init already terminated");
728 }
729
730 log_info!("cleaning up pinned memory");
732 self.expando.remove::<crate::mm::InfoCacheShadowProcess>();
733 self.expando.remove::<crate::mm::MlockShadowProcess>();
734
735 let kernel_job = fuchsia_runtime::job_default();
742 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
743 let own_koid = fuchsia_runtime::process_self().koid().unwrap();
744
745 log_info!("waiting for this to be the only process in the job");
746 loop {
747 let mut remaining_processes = kernel_job
748 .processes()
749 .unwrap()
750 .into_iter()
751 .filter(|pid| pid != &own_koid)
753 .peekable();
754 if remaining_processes.peek().is_none() {
755 log_info!("No stray Zircon processes.");
756 break;
757 }
758
759 let mut terminated_signals = vec![];
760 for pid in remaining_processes {
761 let handle = match kernel_job
762 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
763 {
764 Ok(h) => h,
765 Err(e) => {
766 log_info!(pid:?, e:?; "failed to get child process from job");
767 continue;
768 }
769 };
770 log_info!(
771 pid:?,
772 name:? = handle.get_name();
773 "waiting on process terminated signal"
774 );
775 terminated_signals
776 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
777 }
778 log_info!("waiting on process terminated signals");
779 futures::future::join_all(terminated_signals).await;
780 }
781
782 log_info!("clearing mounts");
784 self.mounts.clear();
785
786 log_info!("all non-root processes killed, notifying CF container is stopped");
788 if let Some(control_handle) = self.container_control_handle.lock().take() {
789 log_info!("Notifying CF that the container has stopped.");
790 control_handle
791 .send_on_stop(ComponentStopInfo {
792 termination_status: Some(zx::sys::ZX_OK),
793 exit_code: Some(0),
794 ..ComponentStopInfo::default()
795 })
796 .unwrap();
797 control_handle.shutdown_with_epitaph(Ok(()));
798 } else {
799 log_warn!("Shutdown invoked without a container controller control handle.");
800 }
801
802 log_info!("All tasks killed, exiting Starnix kernel root process.");
804 zx::Process::exit(0);
812 }
813
814 pub fn is_shutting_down(&self) -> bool {
815 self.shutting_down.load(Ordering::Acquire)
816 }
817
818 pub fn allow_unprivileged_bpf(&self) -> bool {
819 self.disable_unprivileged_bpf.load(Ordering::Relaxed) == 0
820 }
821
822 pub fn open_device(
824 &self,
825 current_task: &CurrentTask,
826 node: &NamespaceNode,
827 flags: OpenFlags,
828 dev: DeviceId,
829 mode: DeviceMode,
830 ) -> Result<Box<dyn FileOps>, Errno> {
831 self.device_registry.open_device(current_task, node, flags, dev, mode)
832 }
833
834 pub fn audit_logger(&self) -> Arc<AuditLogger> {
838 self.expando.get_or_init(|| AuditLogger::new(self))
839 }
840
841 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
846 self.generic_netlink.get_or_init(|| {
847 let (generic_netlink, worker_params) = GenericNetlink::new();
848 let enable_nl80211 = self.features.wifi;
849 self.kthreads.spawn_future(
850 move || async move {
851 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
852 .await;
853 log_error!("Generic Netlink future unexpectedly exited");
854 },
855 "generic_netlink_worker",
856 );
857 generic_netlink
858 })
859 }
860
861 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
866 self.network_netlink.get_or_init(|| {
867 let (network_netlink, worker_params) =
868 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
869
870 let kernel = self.clone();
871 self.kthreads.spawn_future(
872 move || async move {
873 netlink::run_netlink_worker(
874 worker_params,
875 NetlinkAccessControl::new(kernel.kthreads.system_task()),
876 )
877 .await;
878 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
879 },
880 "network_netlink_worker",
881 );
882 network_netlink
883 })
884 }
885
886 pub(crate) fn netstack_wake_group(&self) -> Option<&fnet_resources::WakeGroupToken> {
892 self.netstack_wake_group
893 .get_or_init(|| {
894 const GROUP_WAKEUP_SIGNAL: zx::Signals =
897 zx::Signals::from_bits(fnet_power::GROUP_WAKEUP_SIGNAL).unwrap();
898
899 let provider = fuchsia_component::client::connect_to_protocol_sync::<
900 fnet_power::WakeGroupProviderMarker,
901 >()
902 .expect("connect to WakeGroupProvider");
903
904 let (wake_watcher_waiter, wake_watcher_signaller) = zx::EventPair::create();
905 create_watcher_for_wake_events(wake_watcher_signaller);
906
907 let token = match provider.create_wake_group(
908 &fnet_power::WakeGroupOptions {
909 debug_name: Some(String::from("starnix")),
910 ..Default::default()
911 },
912 wake_watcher_waiter,
913 zx::Instant::INFINITE,
914 ) {
915 Ok(fnet_power::CreateWakeGroupResponse { token, .. }) => token,
916 Err(e) => {
917 log_error!("failed to create wake group with the netstack: {e}");
918 return None;
919 }
920 };
921
922 let token = token.expect("netstack provides a wake group token");
923 let wake_source = token
924 .token
925 .duplicate_handle(zx::Rights::SAME_RIGHTS)
926 .expect("duplicate handle to token");
927 self.suspend_resume_manager
928 .add_external_wake_source(
929 wake_source.into(),
930 GROUP_WAKEUP_SIGNAL,
931 "netstack-wake-group".into(),
932 )
933 .expect("add wake group as wake source");
934
935 Some(token)
936 })
937 .as_ref()
938 }
939
940 pub fn iptables(&self) -> &IpTables {
941 self.iptables.get_or_init(|| IpTables::new())
942 }
943
944 #[allow(unused)]
946 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
947 &self,
948 filename: &str,
949 ) -> Result<ClientEnd<P>, Errno> {
950 match self.container_namespace.get_namespace_channel("/svc") {
951 Ok(channel) => {
952 let (client_end, server_end) = create_endpoints::<P>();
953 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
954 .map_err(|status| from_status_like_fdio!(status))?;
955 Ok(client_end)
956 }
957 Err(err) => {
958 log_error!("Unable to get /svc namespace channel! {}", err);
959 Err(errno!(ENOENT))
960 }
961 }
962 }
963
964 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
966 &self,
967 ) -> Result<ClientEnd<P>, Errno> {
968 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
969 }
970
971 pub fn add_syscall_log_filter(&self, name: &str) {
972 let filter = SyscallLogFilter::new(name.to_string());
973 {
974 let mut filters = self.syscall_log_filters.lock();
975 if filters.contains(&filter) {
976 return;
977 }
978 filters.push(filter);
979 }
980 for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
981 headers.sync_syscall_log_level();
982 }
983 }
984
985 pub fn clear_syscall_log_filters(&self) {
986 {
987 let mut filters = self.syscall_log_filters.lock();
988 if filters.is_empty() {
989 return;
990 }
991 filters.clear();
992 }
993 for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
994 headers.sync_syscall_log_level();
995 }
996 }
997
998 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
999 let inspector = fuchsia_inspect::Inspector::default();
1000
1001 let thread_groups = inspector.root();
1002 let mut mm_summary = MappingSummary::default();
1003 let mut mms_summarized = HashSet::new();
1004
1005 for thread_group in self.pids.get_thread_groups(&RcuReadScope::new()) {
1006 let (ppid, tasks) = {
1008 let tg = thread_group.read();
1009 (tg.get_ppid() as i64, tg.tasks())
1010 };
1011
1012 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
1013 if let Ok(koid) = thread_group.process.koid() {
1014 tg_node.record_int("koid", koid.raw_koid() as i64);
1015 }
1016 tg_node.record_int("pid", thread_group.leader.id as i64);
1017 tg_node.record_int("ppid", ppid);
1018 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
1019
1020 let tasks_node = tg_node.create_child("tasks");
1021 for task in tasks {
1022 if let Ok(mm) = task.mm() {
1023 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
1024 mm.summarize(&mut mm_summary);
1025 }
1026 }
1027 let set_properties = |node: &fuchsia_inspect::Node| {
1028 node.record_string("command", task.command().to_string());
1029
1030 let scheduler_state = task.read().scheduler_state.clone();
1031 if !scheduler_state.is_default() {
1032 node.record_child("sched", |node| {
1033 node.record_string(
1034 "role_name",
1035 self.scheduler
1036 .role_name(&task)
1037 .map(|n| Cow::Borrowed(n))
1038 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
1039 );
1040 node.record_string("state", format!("{scheduler_state:?}"));
1041 });
1042 }
1043 };
1044 if task.tid == thread_group.leader {
1045 let mut argv = task.read_argv(256).unwrap_or_default();
1046
1047 argv.retain(|arg| !arg.is_empty());
1050
1051 let inspect_argv = tg_node.create_string_array("argv", argv.len());
1052 for (i, arg) in argv.iter().enumerate() {
1053 inspect_argv.set(i, arg.to_string());
1054 }
1055 tg_node.record(inspect_argv);
1056
1057 set_properties(&tg_node);
1058 } else {
1059 tasks_node.record_child(task.tid.to_string(), |task_node| {
1060 set_properties(task_node);
1061 });
1062 };
1063 }
1064 tg_node.record(tasks_node);
1065 thread_groups.record(tg_node);
1066 }
1067
1068 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
1069
1070 inspector
1071 }
1072
1073 pub fn new_memory_attribution_observer(
1074 &self,
1075 control_handle: fattribution::ProviderControlHandle,
1076 ) -> attribution_server::Observer {
1077 self.memory_attribution_manager.new_observer(control_handle)
1078 }
1079
1080 pub fn open_ns_dir(
1092 &self,
1093 path: &str,
1094 open_flags: fio::Flags,
1095 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
1096 let ns_path = PathBuf::from(path);
1097 match self.container_namespace.find_closest_channel(&ns_path) {
1098 Ok((root_channel, remaining_subdir)) => {
1099 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
1100 fdio::open_at(
1101 &root_channel,
1102 &remaining_subdir,
1103 open_flags,
1104 server_end.into_channel(),
1105 )
1106 .map_err(|e| {
1107 log_error!("Failed to intialize the subdirs: {}", e);
1108 errno!(EIO)
1109 })?;
1110
1111 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
1112 }
1113 Err(err) => {
1114 log_error!(
1115 "Unable to find a channel for {}. Received error: {}",
1116 ns_path.display(),
1117 err
1118 );
1119 Err(errno!(ENOENT))
1120 }
1121 }
1122 }
1123
1124 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
1126 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
1127 arg.split_once('=')
1128 .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
1129 .or(Some(ArgNameAndValue { name: arg, value: None }))
1130 })
1131 }
1132
1133 pub fn fs_cache_config(&self) -> CacheConfig {
1135 CacheConfig { capacity: self.features.dirent_cache_size as usize }
1136 }
1137
1138 pub fn mounts_lock(&self) -> MountsWriteGuard<'_> {
1139 MountsWriteGuard::new(self.mounts_lock.lock())
1140 }
1141}
1142
1143pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
1144 let mut args = Vec::new();
1145 let mut arg_start: Option<usize> = None;
1146 let mut in_quotes = false;
1147 let mut previous_char = ' ';
1148
1149 for (i, c) in cmdline.char_indices() {
1150 if let Some(start) = arg_start {
1151 match c {
1152 ' ' if !in_quotes => {
1153 args.push(&cmdline[start..i]);
1154 arg_start = None;
1155 }
1156 '"' if previous_char != '\\' => {
1157 in_quotes = !in_quotes;
1158 }
1159 _ => {}
1160 }
1161 } else if c != ' ' {
1162 arg_start = Some(i);
1163 if c == '"' {
1164 in_quotes = true;
1165 }
1166 }
1167 previous_char = c;
1168 }
1169 if let Some(start) = arg_start {
1170 args.push(&cmdline[start..]);
1171 }
1172 args.into_iter()
1173}
1174
1175impl std::fmt::Debug for Kernel {
1176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1177 f.debug_struct("Kernel").finish()
1178 }
1179}
1180
1181#[cfg(target_arch = "aarch64")]
1183fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1184 use starnix_uapi::arch32;
1185 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1186 | arch32::HWCAP_THUMB
1187 | arch32::HWCAP_FAST_MULT
1188 | arch32::HWCAP_EDSP
1189 | arch32::HWCAP_TLS
1190 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
1192 | arch32::HWCAP_EVTSTRM;
1193
1194 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1195 let mut hwcap2 = 0;
1196 for feature in cpu_feature_flags.iter() {
1197 match feature {
1198 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1199 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1200 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1201 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1202 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1203 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1204 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1205 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1206 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1207 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1208 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1209 }
1210 _ => {}
1211 }
1212 }
1213 HwCap { hwcap, hwcap2 }
1214}
1215
1216#[cfg(target_arch = "aarch64")]
1217fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1218 use starnix_uapi;
1220 let mut hwcap = 0;
1221 let mut hwcap2 = 0;
1222
1223 for feature in cpu_feature_flags.iter() {
1224 match feature {
1225 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1226 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1227 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1228 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1229 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1230 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1231 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1232 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1233 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1234 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1235 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1236 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1237 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1238 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1239 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1240 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1241 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1242 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1243 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1244 _ => {}
1245 }
1246 }
1247 HwCap { hwcap, hwcap2 }
1248}
1249
1250impl HwCaps {
1251 #[cfg(target_arch = "aarch64")]
1252 pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1253 Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1254 }
1255
1256 #[cfg(not(target_arch = "aarch64"))]
1257 pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1258 Self { arch64: HwCap::default() }
1259 }
1260}
1261
1262#[cfg(test)]
1263mod test {
1264 use super::parse_cmdline;
1265
1266 #[test]
1267 fn test_parse_cmdline() {
1268 let cmdline =
1269 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1270 let expected = vec![
1271 "first",
1272 "second=third",
1273 "\"fourth fifth\"",
1274 "sixth=\"seventh eighth\"",
1275 "\"ninth\\\" tenth\"",
1276 "eleventh",
1277 ];
1278 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1279 }
1280}