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