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, 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
139impl KernelFeatures {
140 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
144 if let Some(all_options) = &self.default_ns_mount_options {
145 if let Some(options) = all_options.get(ns_path) {
146 return MountParams::parse(options.as_bytes().into());
147 }
148 }
149 Ok(MountParams::default())
150 }
151}
152
153pub struct ArgNameAndValue<'a> {
155 pub name: &'a str,
156 pub value: Option<&'a str>,
157}
158
159type DeferredDropCallback = Box<dyn FnOnce() + Send + Sync>;
160
161pub struct MountsWriteToken {
166 deferred_drops: RefCell<SmallVec<[DeferredDropCallback; 8]>>,
167}
168
169impl MountsWriteToken {
170 fn new() -> Self {
171 Self { deferred_drops: RefCell::new(SmallVec::new()) }
172 }
173
174 fn defer_drop_internal<T: Send + Sync + 'static>(&self, value: T) {
175 self.deferred_drops.borrow_mut().push(Box::new(move || {
176 drop(value);
177 }));
178 }
179
180 pub fn defer_drop<T: Send + Sync + 'static>(
184 &self,
185 value: T,
186 ) -> ScopeGuard<T, impl FnOnce(T) + '_> {
187 scopeguard::guard(value, |value| {
188 self.defer_drop_internal(value);
189 })
190 }
191
192 pub fn retain<T: Send + Sync + 'static>(&self, value: &Arc<T>) -> Arc<T> {
194 self.defer_drop_internal(value.clone());
195 value.clone()
196 }
197
198 fn take_deferred_drops(&mut self) -> SmallVec<[DeferredDropCallback; 8]> {
199 std::mem::take(self.deferred_drops.get_mut())
200 }
201}
202
203pub struct MountsWriteGuard<'a> {
206 guard: Option<RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>>,
207}
208
209impl<'a> MountsWriteGuard<'a> {
210 fn new(guard: RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>) -> Self {
211 Self { guard: Some(guard) }
212 }
213}
214
215impl<'a> Deref for MountsWriteGuard<'a> {
216 type Target = MountsWriteToken;
217 fn deref(&self) -> &Self::Target {
218 self.guard.as_ref().unwrap()
219 }
220}
221
222impl<'a> Drop for MountsWriteGuard<'a> {
223 fn drop(&mut self) {
224 if let Some(mut guard) = self.guard.take() {
225 let drops = guard.take_deferred_drops();
226 drop(guard);
227 for callback in drops {
228 callback();
229 }
230 }
231 }
232}
233
234pub struct Kernel {
244 pub weak_self: Weak<Kernel>,
246
247 pub kthreads: KernelThreads,
249
250 pub features: KernelFeatures,
252
253 pub pids: PidTable,
255
256 pub init_task: OnceLock<Weak<Task>>,
258
259 pub trace_event_manager: Arc<TracePerformanceEventManager>,
263
264 pub expando: Expando,
269
270 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
276
277 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
279
280 pub cmdline: BString,
282
283 pub device_tree: Option<Devicetree>,
284
285 pub security_state: security::KernelState,
287
288 pub device_registry: DeviceRegistry,
290
291 pub container_namespace: ContainerNamespace,
295
296 pub mounts_lock: RwSeqLock<LockDepMutex<MountsWriteToken, MountsLevel>>,
305
306 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
308
309 iptables: OnceLock<IpTables>,
311
312 pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
314
315 pub root_uts_ns: UtsNamespaceHandle,
320
321 pub vdso: Vdso,
323
324 pub vdso_arch32: Option<Vdso>,
328
329 pub netstack_devices: Arc<NetstackDevices>,
332
333 pub swap_files: LockDepMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
337
338 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
340
341 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
343
344 pub inspect_node: fuchsia_inspect::Node,
346
347 pub actions_logged: AtomicU16,
354
355 pub suspend_resume_manager: SuspendResumeManagerHandle,
357
358 pub next_mount_id: AtomicCounter<u64>,
360 pub next_peer_group_id: AtomicCounter<u64>,
361 pub next_namespace_id: AtomicCounter<u64>,
362
363 pub next_file_object_id: AtomicCounter<u64>,
365
366 pub ptrace_scope: AtomicU8,
368
369 pub build_version: OnceCell<String>,
371
372 pub stats: Arc<KernelStats>,
373
374 pub system_limits: SystemLimits,
376
377 pub delayed_releaser: DelayedReleaser,
381
382 pub scheduler: SchedulerManager,
384
385 pub syslog: Syslog,
387
388 pub mounts: Mounts,
390
391 pub hrtimer_manager: HrTimerManagerHandle,
393
394 pub memory_attribution_manager: MemoryAttributionManager,
396
397 pub crash_reporter: CrashReporter,
399
400 shutting_down: AtomicBool,
402
403 pub restrict_dmesg: AtomicBool,
406
407 pub disable_unprivileged_bpf: AtomicU8,
412
413 pub container_control_handle:
415 LockDepMutex<Option<ComponentControllerControlHandle>, ComponentControllerLock>,
416
417 pub ebpf_state: EbpfState,
419
420 pub cgroups: KernelCgroups,
422
423 pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
426
427 pub netstack_wake_group: OnceLock<Option<fnet_resources::WakeGroupToken>>,
434
435 pub socket_tokens_store: SocketTokensStore,
437
438 pub hwcaps: HwCaps,
440
441 pub syscall_log_filters: LockDepMutex<Vec<SyscallLogFilter>, SyscallLogFiltersLock>,
444}
445
446#[derive(Debug, Clone, Copy, Default)]
448pub struct HwCap {
449 pub hwcap: u32,
451 pub hwcap2: u32,
453}
454
455#[derive(Debug, Clone, Copy, Default)]
457pub struct HwCaps {
458 #[cfg(target_arch = "aarch64")]
460 pub arch32: HwCap,
461 pub arch64: HwCap,
463}
464
465struct InterfacesHandlerImpl(Weak<Kernel>);
472
473impl InterfacesHandlerImpl {
474 fn kernel(&self) -> Option<Arc<Kernel>> {
475 self.0.upgrade()
476 }
477}
478
479impl InterfacesHandler for InterfacesHandlerImpl {
480 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
481 if let Some(kernel) = self.kernel() {
482 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
483 }
484 }
485
486 fn handle_deleted_link(&mut self, name: &str) {
487 if let Some(kernel) = self.kernel() {
488 kernel.netstack_devices.remove_device(&kernel, name.into());
489 }
490 }
491
492 fn handle_idle_event(&mut self) {
493 let Some(kernel) = self.kernel() else {
494 log_error!("kernel went away while netlink is initializing");
495 return;
496 };
497 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
498 if initialized.swap(true, Ordering::SeqCst) {
499 log_error!("netlink initial devices should only be reported once");
500 return;
501 }
502 wq.notify_all()
503 }
504}
505
506impl Kernel {
507 pub fn new(
508 cmdline: BString,
509 features: KernelFeatures,
510 system_limits: SystemLimits,
511 container_namespace: ContainerNamespace,
512 scheduler: SchedulerManager,
513 crash_reporter_proxy: Option<CrashReporterProxy>,
514 inspect_node: fuchsia_inspect::Node,
515 security_state: security::KernelState,
516 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
517 device_tree: Option<Devicetree>,
518 ) -> Result<Arc<Kernel>, zx::Status> {
519 let unix_address_maker =
520 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
521 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
522 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
523 });
524
525 let crash_reporter = CrashReporter::new(
526 &inspect_node,
527 crash_reporter_proxy,
528 zx::Duration::from_minutes(8),
529 features.crash_report_throttling,
530 );
531 let hrtimer_manager = HrTimerManager::new(&inspect_node);
532
533 let cpu_feature_flags =
534 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
535 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
536 CpuFeatureFlags::empty()
537 });
538 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
539
540 let this = Arc::new_cyclic(|kernel| Kernel {
541 weak_self: kernel.clone(),
542 kthreads: KernelThreads::new(kernel.clone()),
543 features,
544 pids: Default::default(),
545 init_task: OnceLock::new(),
546 trace_event_manager: Arc::new(TracePerformanceEventManager::new(kernel.clone())),
547 expando: Default::default(),
548 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
549 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
550 vsock_address_maker,
551 ),
552 cmdline,
553 device_tree,
554 security_state,
555 device_registry: Default::default(),
556 container_namespace,
557 mounts_lock: RwSeqLock::new(MountsWriteToken::new().into()),
558 remote_block_device_registry: Default::default(),
559 iptables: OnceLock::new(),
560 shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
561 root_uts_ns: Arc::new(UtsNamespace::default().into()),
562 vdso: Vdso::new(),
563 vdso_arch32: Vdso::new_arch32(),
564 netstack_devices: Arc::default(),
565 swap_files: Default::default(),
566 generic_netlink: OnceLock::new(),
567 network_netlink: OnceLock::new(),
568 inspect_node,
569 actions_logged: AtomicU16::new(0),
570 suspend_resume_manager: Default::default(),
571 next_mount_id: AtomicCounter::<u64>::new(1),
572 next_peer_group_id: AtomicCounter::<u64>::new(1),
573 next_namespace_id: AtomicCounter::<u64>::new(1),
574 next_file_object_id: Default::default(),
575 system_limits,
576 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
578 disable_unprivileged_bpf: AtomicU8::new(0), build_version: OnceCell::new(),
580 stats: Arc::new(KernelStats::default()),
581 delayed_releaser: Default::default(),
582 scheduler,
583 syslog: Default::default(),
584 mounts: Mounts::new(),
585 hrtimer_manager,
586 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
587 crash_reporter,
588 shutting_down: AtomicBool::new(false),
589 container_control_handle: Default::default(),
590 ebpf_state: Default::default(),
591 cgroups: Default::default(),
592 time_adjustment_proxy,
593 netstack_wake_group: OnceLock::new(),
594 socket_tokens_store: Default::default(),
595 hwcaps,
596 syscall_log_filters: Default::default(),
597 });
598
599 this.device_registry.objects.init(&this);
603
604 let kernel = Arc::downgrade(&this);
607 this.inspect_node.record_lazy_child("thread_groups", move || {
608 if let Some(kernel) = kernel.upgrade() {
609 let inspector = kernel.get_thread_groups_inspect();
610 async move { Ok(inspector) }.boxed()
611 } else {
612 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
613 }
614 });
615
616 let kernel = Arc::downgrade(&this);
617 this.inspect_node.record_lazy_child("cgroupv2", move || {
618 if let Some(kernel) = kernel.upgrade() {
619 async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
620 } else {
621 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
622 }
623 });
624
625 Ok(this)
626 }
627
628 pub fn get_init_task(&self) -> Result<Arc<Task>, Errno> {
630 self.init_task.get().and_then(|t| t.upgrade()).ok_or_else(|| errno!(EINVAL))
631 }
632
633 pub fn shut_down(self: &Arc<Self>) {
636 self.kthreads.spawn_future(
639 {
640 let kernel = self.clone();
641 move || async move {
642 kernel.run_shutdown().await;
643 }
644 },
645 "run_shutdown",
646 );
647 }
648
649 async fn run_shutdown(&self) {
669 const INIT_PID: i32 = 1;
670 const SYSTEM_TASK_PID: i32 = 2;
671
672 if self
675 .shutting_down
676 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
677 .is_err()
678 {
679 log_info!("Additional thread tried to initiate shutdown while already in-progress.");
680 return;
681 }
682
683 log_info!("Shutting down Starnix kernel.");
684
685 loop {
688 let tgs = self
689 .pids
690 .get_thread_groups(&RcuReadScope::new())
691 .filter(|tg| tg.leader.id != SYSTEM_TASK_PID && tg.leader.id != INIT_PID)
692 .collect::<Vec<_>>();
693 if tgs.is_empty() {
694 log_info!("pid table is empty except init and system task");
695 break;
696 }
697
698 log_info!(tgs:?; "shutting down thread groups");
699 let mut tasks = vec![];
700 for tg in tgs {
701 let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
702 tasks.push(task);
703 }
704 futures::future::join_all(tasks).await;
705 }
706
707 let maybe_init = self.get_init_task().ok().map(|t| Arc::downgrade(&t.thread_group));
709 if let Some(init) = maybe_init {
710 log_info!("shutting down init");
711 ThreadGroup::shut_down(init).await;
712 } else {
713 log_info!("init already terminated");
714 }
715
716 log_info!("cleaning up pinned memory");
718 self.expando.remove::<crate::mm::InfoCacheShadowProcess>();
719 self.expando.remove::<crate::mm::MlockShadowProcess>();
720
721 let kernel_job = fuchsia_runtime::job_default();
728 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
729 let own_koid = fuchsia_runtime::process_self().koid().unwrap();
730
731 log_info!("waiting for this to be the only process in the job");
732 loop {
733 let mut remaining_processes = kernel_job
734 .processes()
735 .unwrap()
736 .into_iter()
737 .filter(|pid| pid != &own_koid)
739 .peekable();
740 if remaining_processes.peek().is_none() {
741 log_info!("No stray Zircon processes.");
742 break;
743 }
744
745 let mut terminated_signals = vec![];
746 for pid in remaining_processes {
747 let handle = match kernel_job
748 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
749 {
750 Ok(h) => h,
751 Err(e) => {
752 log_info!(pid:?, e:?; "failed to get child process from job");
753 continue;
754 }
755 };
756 log_info!(
757 pid:?,
758 name:? = handle.get_name();
759 "waiting on process terminated signal"
760 );
761 terminated_signals
762 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
763 }
764 log_info!("waiting on process terminated signals");
765 futures::future::join_all(terminated_signals).await;
766 }
767
768 log_info!("clearing mounts");
770 self.mounts.clear();
771
772 log_info!("all non-root processes killed, notifying CF container is stopped");
774 if let Some(control_handle) = self.container_control_handle.lock().take() {
775 log_info!("Notifying CF that the container has stopped.");
776 control_handle
777 .send_on_stop(ComponentStopInfo {
778 termination_status: Some(zx::sys::ZX_OK),
779 exit_code: Some(0),
780 ..ComponentStopInfo::default()
781 })
782 .unwrap();
783 control_handle.shutdown_with_epitaph(Ok(()));
784 } else {
785 log_warn!("Shutdown invoked without a container controller control handle.");
786 }
787
788 log_info!("All tasks killed, exiting Starnix kernel root process.");
790 zx::Process::exit(0);
798 }
799
800 pub fn is_shutting_down(&self) -> bool {
801 self.shutting_down.load(Ordering::Acquire)
802 }
803
804 pub fn allow_unprivileged_bpf(&self) -> bool {
805 self.disable_unprivileged_bpf.load(Ordering::Relaxed) == 0
806 }
807
808 pub fn open_device(
810 &self,
811 current_task: &CurrentTask,
812 node: &NamespaceNode,
813 flags: OpenFlags,
814 dev: DeviceId,
815 mode: DeviceMode,
816 ) -> Result<Box<dyn FileOps>, Errno> {
817 self.device_registry.open_device(current_task, node, flags, dev, mode)
818 }
819
820 pub fn audit_logger(&self) -> Arc<AuditLogger> {
824 self.expando.get_or_init(|| AuditLogger::new(self))
825 }
826
827 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
832 self.generic_netlink.get_or_init(|| {
833 let (generic_netlink, worker_params) = GenericNetlink::new();
834 let enable_nl80211 = self.features.wifi;
835 self.kthreads.spawn_future(
836 move || async move {
837 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
838 .await;
839 log_error!("Generic Netlink future unexpectedly exited");
840 },
841 "generic_netlink_worker",
842 );
843 generic_netlink
844 })
845 }
846
847 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
852 self.network_netlink.get_or_init(|| {
853 let (network_netlink, worker_params) =
854 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
855
856 let kernel = self.clone();
857 self.kthreads.spawn_future(
858 move || async move {
859 netlink::run_netlink_worker(
860 worker_params,
861 NetlinkAccessControl::new(kernel.kthreads.system_task()),
862 )
863 .await;
864 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
865 },
866 "network_netlink_worker",
867 );
868 network_netlink
869 })
870 }
871
872 pub(crate) fn netstack_wake_group(&self) -> Option<&fnet_resources::WakeGroupToken> {
878 self.netstack_wake_group
879 .get_or_init(|| {
880 const GROUP_WAKEUP_SIGNAL: zx::Signals =
883 zx::Signals::from_bits(fnet_power::GROUP_WAKEUP_SIGNAL).unwrap();
884
885 let provider = fuchsia_component::client::connect_to_protocol_sync::<
886 fnet_power::WakeGroupProviderMarker,
887 >()
888 .expect("connect to WakeGroupProvider");
889
890 let (wake_watcher_waiter, wake_watcher_signaller) = zx::EventPair::create();
891 create_watcher_for_wake_events(wake_watcher_signaller);
892
893 let token = match provider.create_wake_group(
894 &fnet_power::WakeGroupOptions {
895 debug_name: Some(String::from("starnix")),
896 ..Default::default()
897 },
898 wake_watcher_waiter,
899 zx::Instant::INFINITE,
900 ) {
901 Ok(fnet_power::CreateWakeGroupResponse { token, .. }) => token,
902 Err(e) => {
903 log_error!("failed to create wake group with the netstack: {e}");
904 return None;
905 }
906 };
907
908 let token = token.expect("netstack provides a wake group token");
909 let wake_source = token
910 .token
911 .duplicate_handle(zx::Rights::SAME_RIGHTS)
912 .expect("duplicate handle to token");
913 self.suspend_resume_manager
914 .add_external_wake_source(
915 wake_source.into(),
916 GROUP_WAKEUP_SIGNAL,
917 "netstack-wake-group".into(),
918 )
919 .expect("add wake group as wake source");
920
921 Some(token)
922 })
923 .as_ref()
924 }
925
926 pub fn iptables(&self) -> &IpTables {
927 self.iptables.get_or_init(|| IpTables::new())
928 }
929
930 #[allow(unused)]
932 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
933 &self,
934 filename: &str,
935 ) -> Result<ClientEnd<P>, Errno> {
936 match self.container_namespace.get_namespace_channel("/svc") {
937 Ok(channel) => {
938 let (client_end, server_end) = create_endpoints::<P>();
939 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
940 .map_err(|status| from_status_like_fdio!(status))?;
941 Ok(client_end)
942 }
943 Err(err) => {
944 log_error!("Unable to get /svc namespace channel! {}", err);
945 Err(errno!(ENOENT))
946 }
947 }
948 }
949
950 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
952 &self,
953 ) -> Result<ClientEnd<P>, Errno> {
954 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
955 }
956
957 pub fn add_syscall_log_filter(&self, name: &str) {
958 let filter = SyscallLogFilter::new(name.to_string());
959 {
960 let mut filters = self.syscall_log_filters.lock();
961 if filters.contains(&filter) {
962 return;
963 }
964 filters.push(filter);
965 }
966 for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
967 headers.sync_syscall_log_level();
968 }
969 }
970
971 pub fn clear_syscall_log_filters(&self) {
972 {
973 let mut filters = self.syscall_log_filters.lock();
974 if filters.is_empty() {
975 return;
976 }
977 filters.clear();
978 }
979 for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
980 headers.sync_syscall_log_level();
981 }
982 }
983
984 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
985 let inspector = fuchsia_inspect::Inspector::default();
986
987 let thread_groups = inspector.root();
988 let mut mm_summary = MappingSummary::default();
989 let mut mms_summarized = HashSet::new();
990
991 for thread_group in self.pids.get_thread_groups(&RcuReadScope::new()) {
992 let (ppid, tasks) = {
994 let tg = thread_group.read();
995 (tg.get_ppid() as i64, tg.tasks())
996 };
997
998 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
999 if let Ok(koid) = thread_group.process.koid() {
1000 tg_node.record_int("koid", koid.raw_koid() as i64);
1001 }
1002 tg_node.record_int("pid", thread_group.leader.id as i64);
1003 tg_node.record_int("ppid", ppid);
1004 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
1005
1006 let tasks_node = tg_node.create_child("tasks");
1007 for task in tasks {
1008 if let Ok(mm) = task.mm() {
1009 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
1010 mm.summarize(&mut mm_summary);
1011 }
1012 }
1013 let set_properties = |node: &fuchsia_inspect::Node| {
1014 node.record_string("command", task.command().to_string());
1015
1016 let scheduler_state = task.read().scheduler_state;
1017 if !scheduler_state.is_default() {
1018 node.record_child("sched", |node| {
1019 node.record_string(
1020 "role_name",
1021 self.scheduler
1022 .role_name(&task)
1023 .map(|n| Cow::Borrowed(n))
1024 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
1025 );
1026 node.record_string("state", format!("{scheduler_state:?}"));
1027 });
1028 }
1029 };
1030 if task.tid == thread_group.leader {
1031 let mut argv = task.read_argv(256).unwrap_or_default();
1032
1033 argv.retain(|arg| !arg.is_empty());
1036
1037 let inspect_argv = tg_node.create_string_array("argv", argv.len());
1038 for (i, arg) in argv.iter().enumerate() {
1039 inspect_argv.set(i, arg.to_string());
1040 }
1041 tg_node.record(inspect_argv);
1042
1043 set_properties(&tg_node);
1044 } else {
1045 tasks_node.record_child(task.tid.to_string(), |task_node| {
1046 set_properties(task_node);
1047 });
1048 };
1049 }
1050 tg_node.record(tasks_node);
1051 thread_groups.record(tg_node);
1052 }
1053
1054 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
1055
1056 inspector
1057 }
1058
1059 pub fn new_memory_attribution_observer(
1060 &self,
1061 control_handle: fattribution::ProviderControlHandle,
1062 ) -> attribution_server::Observer {
1063 self.memory_attribution_manager.new_observer(control_handle)
1064 }
1065
1066 pub fn open_ns_dir(
1078 &self,
1079 path: &str,
1080 open_flags: fio::Flags,
1081 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
1082 let ns_path = PathBuf::from(path);
1083 match self.container_namespace.find_closest_channel(&ns_path) {
1084 Ok((root_channel, remaining_subdir)) => {
1085 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
1086 fdio::open_at(
1087 &root_channel,
1088 &remaining_subdir,
1089 open_flags,
1090 server_end.into_channel(),
1091 )
1092 .map_err(|e| {
1093 log_error!("Failed to intialize the subdirs: {}", e);
1094 errno!(EIO)
1095 })?;
1096
1097 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
1098 }
1099 Err(err) => {
1100 log_error!(
1101 "Unable to find a channel for {}. Received error: {}",
1102 ns_path.display(),
1103 err
1104 );
1105 Err(errno!(ENOENT))
1106 }
1107 }
1108 }
1109
1110 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
1112 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
1113 arg.split_once('=')
1114 .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
1115 .or(Some(ArgNameAndValue { name: arg, value: None }))
1116 })
1117 }
1118
1119 pub fn fs_cache_config(&self) -> CacheConfig {
1121 CacheConfig { capacity: self.features.dirent_cache_size as usize }
1122 }
1123
1124 pub fn mounts_lock(&self) -> MountsWriteGuard<'_> {
1125 MountsWriteGuard::new(self.mounts_lock.lock())
1126 }
1127}
1128
1129pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
1130 let mut args = Vec::new();
1131 let mut arg_start: Option<usize> = None;
1132 let mut in_quotes = false;
1133 let mut previous_char = ' ';
1134
1135 for (i, c) in cmdline.char_indices() {
1136 if let Some(start) = arg_start {
1137 match c {
1138 ' ' if !in_quotes => {
1139 args.push(&cmdline[start..i]);
1140 arg_start = None;
1141 }
1142 '"' if previous_char != '\\' => {
1143 in_quotes = !in_quotes;
1144 }
1145 _ => {}
1146 }
1147 } else if c != ' ' {
1148 arg_start = Some(i);
1149 if c == '"' {
1150 in_quotes = true;
1151 }
1152 }
1153 previous_char = c;
1154 }
1155 if let Some(start) = arg_start {
1156 args.push(&cmdline[start..]);
1157 }
1158 args.into_iter()
1159}
1160
1161impl std::fmt::Debug for Kernel {
1162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1163 f.debug_struct("Kernel").finish()
1164 }
1165}
1166
1167#[cfg(target_arch = "aarch64")]
1169fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1170 use starnix_uapi::arch32;
1171 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1172 | arch32::HWCAP_THUMB
1173 | arch32::HWCAP_FAST_MULT
1174 | arch32::HWCAP_EDSP
1175 | arch32::HWCAP_TLS
1176 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
1178 | arch32::HWCAP_EVTSTRM;
1179
1180 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1181 let mut hwcap2 = 0;
1182 for feature in cpu_feature_flags.iter() {
1183 match feature {
1184 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1185 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1186 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1187 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1188 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1189 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1190 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1191 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1192 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1193 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1194 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1195 }
1196 _ => {}
1197 }
1198 }
1199 HwCap { hwcap, hwcap2 }
1200}
1201
1202#[cfg(target_arch = "aarch64")]
1203fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1204 use starnix_uapi;
1206 let mut hwcap = 0;
1207 let mut hwcap2 = 0;
1208
1209 for feature in cpu_feature_flags.iter() {
1210 match feature {
1211 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1212 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1213 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1214 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1215 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1216 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1217 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1218 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1219 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1220 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1221 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1222 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1223 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1224 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1225 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1226 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1227 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1228 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1229 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1230 _ => {}
1231 }
1232 }
1233 HwCap { hwcap, hwcap2 }
1234}
1235
1236impl HwCaps {
1237 #[cfg(target_arch = "aarch64")]
1238 pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1239 Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1240 }
1241
1242 #[cfg(not(target_arch = "aarch64"))]
1243 pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1244 Self { arch64: HwCap::default() }
1245 }
1246}
1247
1248#[cfg(test)]
1249mod test {
1250 use super::parse_cmdline;
1251
1252 #[test]
1253 fn test_parse_cmdline() {
1254 let cmdline =
1255 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1256 let expected = vec![
1257 "first",
1258 "second=third",
1259 "\"fourth fifth\"",
1260 "sixth=\"seventh eighth\"",
1261 "\"ninth\\\" tenth\"",
1262 "eleventh",
1263 ];
1264 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1265 }
1266}