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};
10use crate::power::{SuspendResumeManagerHandle, create_watcher_for_wake_events};
11use crate::ptrace::StopState;
12use crate::security::{self, AuditLogger};
13use crate::signals::SignalInfo;
14use crate::task::container_namespace::ContainerNamespace;
15use crate::task::limits::SystemLimits;
16use crate::task::memory_attribution::MemoryAttributionManager;
17use crate::task::net::NetstackDevices;
18use crate::task::tracing::TracePerformanceEventManager;
19use crate::task::{
20 AbstractUnixSocketNamespace, AbstractVsockSocketNamespace, CurrentTask, DelayedReleaser,
21 ExitStatus, IpTables, KernelCgroups, KernelStats, KernelThreads, PidTable, SchedulerManager,
22 Syslog, Task, ThreadGroup, UtsNamespace, UtsNamespaceHandle,
23};
24use crate::time::{HrTimerManager, HrTimerManagerHandle};
25use crate::vdso::vdso_loader::Vdso;
26use crate::vfs::fs_args::MountParams;
27use crate::vfs::socket::{
28 GenericMessage, GenericNetlink, NetlinkAccessControl, NetlinkContextImpl,
29 NetlinkToClientSender, SocketAddress, SocketTokensStore,
30};
31use crate::vfs::{CacheConfig, FileOps, FsNodeHandle, FsString, Mounts, NamespaceNode};
32use bstr::{BString, ByteSlice};
33use devicetree::types::Devicetree;
34use expando::Expando;
35use fidl::endpoints::{ClientEnd, DiscoverableProtocolMarker, ProtocolMarker, create_endpoints};
36use fidl_fuchsia_component_runner::{ComponentControllerControlHandle, ComponentStopInfo};
37use fidl_fuchsia_feedback::CrashReporterProxy;
38use fidl_fuchsia_io as fio;
39use fidl_fuchsia_memory_attribution as fattribution;
40use fidl_fuchsia_net_power as fnet_power;
41use fidl_fuchsia_net_resources as fnet_resources;
42use fidl_fuchsia_time_external::AdjustSynchronousProxy;
43use fuchsia_async as fasync;
44use fuchsia_inspect::ArrayProperty;
45use fuchsia_rcu::RcuReadScope;
46use futures::FutureExt;
47use netlink::interfaces::InterfacesHandler;
48use netlink::{NETLINK_LOG_TAG, Netlink};
49use once_cell::sync::OnceCell;
50use scopeguard::ScopeGuard;
51use smallvec::SmallVec;
52use starnix_lifecycle::AtomicCounter;
53use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
54use starnix_sync::{
55 ComponentControllerLock, KernelSwapFiles, LockDepGuard, LockDepMutex, MountsLevel, RwSeqLock,
56 RwSeqLockGuard, SyscallLogFiltersLock,
57};
58use starnix_uapi::device_id::DeviceId;
59use starnix_uapi::errors::{Errno, errno};
60use starnix_uapi::open_flags::OpenFlags;
61use starnix_uapi::signals::SIGKILL;
62use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
63use std::borrow::Cow;
64use std::cell::RefCell;
65use std::collections::{HashMap, HashSet};
66use std::num::NonZeroU64;
67use std::ops::Deref;
68use std::path::PathBuf;
69use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU8, AtomicU16, Ordering};
70use std::sync::{Arc, OnceLock, Weak};
71use zx::{CpuFeatureFlags, CpuSveFeatureFlags};
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub struct OomWatermarksMb {
77 pub out_of_memory_mb: u32,
78 pub critical_mb: u32,
79 pub warning_mb: u32,
80}
81
82#[derive(Debug, Default, Clone)]
85pub struct KernelFeatures {
86 pub bpf_v2: bool,
87
88 pub devtmpfs: bool,
90
91 pub enable_suid: bool,
98
99 pub io_uring: bool,
103
104 pub error_on_failed_reboot: bool,
107
108 pub default_seclabel: Option<String>,
112
113 pub selinux_test_suite: bool,
118
119 pub default_ns_mount_options: Option<HashMap<String, String>>,
124
125 pub default_uid: u32,
129
130 pub mlock_always_onfault: bool,
132
133 pub mlock_pin_flavor: MlockPinFlavor,
135
136 pub crash_report_throttling: bool,
138
139 pub wifi: bool,
141
142 pub cached_zx_map_info_bytes: u32,
144
145 pub dirent_cache_size: u32,
147
148 pub fake_ion: bool,
151
152 pub remotevol_in_process: bool,
154
155 pub log_sysfs_lookup_misses: bool,
157
158 pub oom_watermarks_mb: Option<OomWatermarksMb>,
160
161 pub stop_last_on_shutdown: Vec<FsString>,
165}
166
167impl KernelFeatures {
168 pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
172 if let Some(all_options) = &self.default_ns_mount_options {
173 if let Some(options) = all_options.get(ns_path) {
174 return MountParams::parse(options.as_bytes().into());
175 }
176 }
177 Ok(MountParams::default())
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub struct ArgNameAndValue<'a> {
184 pub name: &'a str,
185 pub value: Option<&'a str>,
186}
187
188impl<'a> From<&'a str> for ArgNameAndValue<'a> {
189 fn from(arg: &'a str) -> Self {
190 match arg.split_once('=') {
191 Some((name, value)) => Self { name, value: Some(value) },
192 None => Self { name: arg, value: None },
193 }
194 }
195}
196
197type DeferredDropCallback = Box<dyn FnOnce() + Send + Sync>;
198
199pub struct MountsWriteToken {
204 deferred_drops: RefCell<SmallVec<[DeferredDropCallback; 8]>>,
205}
206
207impl MountsWriteToken {
208 fn new() -> Self {
209 Self { deferred_drops: RefCell::new(SmallVec::new()) }
210 }
211
212 fn defer_drop_internal<T: Send + Sync + 'static>(&self, value: T) {
213 self.deferred_drops.borrow_mut().push(Box::new(move || {
214 drop(value);
215 }));
216 }
217
218 pub fn defer_drop<T: Send + Sync + 'static>(
222 &self,
223 value: T,
224 ) -> ScopeGuard<T, impl FnOnce(T) + '_> {
225 scopeguard::guard(value, |value| {
226 self.defer_drop_internal(value);
227 })
228 }
229
230 pub fn retain<T: Send + Sync + 'static>(&self, value: &Arc<T>) -> Arc<T> {
232 self.defer_drop_internal(value.clone());
233 value.clone()
234 }
235
236 fn take_deferred_drops(&mut self) -> SmallVec<[DeferredDropCallback; 8]> {
237 std::mem::take(self.deferred_drops.get_mut())
238 }
239}
240
241pub struct MountsWriteGuard<'a> {
244 guard: Option<RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>>,
245}
246
247impl<'a> MountsWriteGuard<'a> {
248 fn new(guard: RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>) -> Self {
249 Self { guard: Some(guard) }
250 }
251}
252
253impl<'a> Deref for MountsWriteGuard<'a> {
254 type Target = MountsWriteToken;
255 fn deref(&self) -> &Self::Target {
256 self.guard.as_ref().unwrap()
257 }
258}
259
260impl<'a> Drop for MountsWriteGuard<'a> {
261 fn drop(&mut self) {
262 if let Some(mut guard) = self.guard.take() {
263 let drops = guard.take_deferred_drops();
264 drop(guard);
265 for callback in drops {
266 callback();
267 }
268 }
269 }
270}
271
272const DEFERRED_SHUTDOWN_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(3);
286
287pub struct Kernel {
297 pub weak_self: Weak<Kernel>,
299
300 pub kthreads: KernelThreads,
302
303 pub features: KernelFeatures,
305
306 pub pids: PidTable,
308
309 pub init_task: OnceLock<Weak<Task>>,
311
312 pub trace_event_manager: Arc<TracePerformanceEventManager>,
316
317 pub expando: Expando,
322
323 pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
329
330 pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
332
333 pub cmdline: BString,
335
336 pub device_tree: Option<Devicetree>,
337
338 pub security_state: security::KernelState,
340
341 pub device_registry: DeviceRegistry,
343
344 pub container_namespace: ContainerNamespace,
348
349 pub mounts_lock: RwSeqLock<LockDepMutex<MountsWriteToken, MountsLevel>>,
358
359 pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
361
362 iptables: OnceLock<IpTables>,
364
365 pub shared_futexes: FutexTable,
367
368 pub root_uts_ns: UtsNamespaceHandle,
373
374 pub vdso: Vdso,
376
377 pub vdso_arch32: Option<Vdso>,
381
382 pub netstack_devices: Arc<NetstackDevices>,
385
386 pub swap_files: LockDepMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
390
391 generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
393
394 network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
396
397 pub inspect_node: fuchsia_inspect::Node,
399
400 pub actions_logged: AtomicU16,
407
408 pub suspend_resume_manager: SuspendResumeManagerHandle,
410
411 pub next_mount_id: AtomicCounter<u64>,
413 pub next_peer_group_id: AtomicCounter<u64>,
414 pub next_namespace_id: AtomicCounter<u64>,
415
416 pub next_file_object_id: AtomicCounter<u64>,
418
419 pub ptrace_scope: AtomicU8,
421
422 pub build_version: OnceCell<String>,
424
425 pub stats: Arc<KernelStats>,
426
427 pub system_limits: SystemLimits,
429
430 pub delayed_releaser: DelayedReleaser,
434
435 pub scheduler: SchedulerManager,
437
438 pub syslog: Syslog,
440
441 pub mounts: Mounts,
443
444 pub hrtimer_manager: HrTimerManagerHandle,
446
447 pub memory_attribution_manager: MemoryAttributionManager,
449
450 pub crash_reporter: CrashReporter,
452
453 shutdown_start: OnceLock<zx::MonotonicInstant>,
455
456 pub restrict_dmesg: AtomicBool,
459
460 pub disable_unprivileged_bpf: AtomicU8,
465
466 pub perf_event_paranoid: AtomicI32,
472
473 pub container_control_handle:
475 LockDepMutex<Option<ComponentControllerControlHandle>, ComponentControllerLock>,
476
477 pub ebpf_state: EbpfState,
479
480 pub cgroups: KernelCgroups,
482
483 pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
486
487 pub netstack_wake_group: OnceLock<Option<fnet_resources::WakeGroupToken>>,
494
495 pub socket_tokens_store: SocketTokensStore,
497
498 pub hwcaps: HwCaps,
500
501 pub syscall_log_filters: LockDepMutex<Vec<SyscallLogFilter>, SyscallLogFiltersLock>,
504}
505
506#[derive(Debug, Clone, Copy, Default)]
508pub struct HwCap {
509 pub hwcap: u32,
511 pub hwcap2: u32,
513}
514
515#[derive(Debug, Clone, Copy, Default)]
517pub struct HwCaps {
518 #[cfg(target_arch = "aarch64")]
520 pub arch32: HwCap,
521 pub arch64: HwCap,
523}
524
525struct InterfacesHandlerImpl(Weak<Kernel>);
532
533impl InterfacesHandlerImpl {
534 fn kernel(&self) -> Option<Arc<Kernel>> {
535 self.0.upgrade()
536 }
537}
538
539impl InterfacesHandler for InterfacesHandlerImpl {
540 fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
541 if let Some(kernel) = self.kernel() {
542 kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
543 }
544 }
545
546 fn handle_deleted_link(&mut self, name: &str) {
547 if let Some(kernel) = self.kernel() {
548 kernel.netstack_devices.remove_device(kernel.kthreads.system_task(), name.into());
549 }
550 }
551
552 fn handle_idle_event(&mut self) {
553 let Some(kernel) = self.kernel() else {
554 log_error!("kernel went away while netlink is initializing");
555 return;
556 };
557 let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
558 if initialized.swap(true, Ordering::SeqCst) {
559 log_error!("netlink initial devices should only be reported once");
560 return;
561 }
562 wq.notify_all()
563 }
564}
565
566impl Kernel {
567 pub fn new(
568 cmdline: BString,
569 features: KernelFeatures,
570 system_limits: SystemLimits,
571 container_namespace: ContainerNamespace,
572 scheduler: SchedulerManager,
573 crash_reporter_proxy: Option<CrashReporterProxy>,
574 inspect_node: fuchsia_inspect::Node,
575 security_state: security::KernelState,
576 time_adjustment_proxy: Option<AdjustSynchronousProxy>,
577 device_tree: Option<Devicetree>,
578 ) -> Result<Arc<Kernel>, zx::Status> {
579 let unix_address_maker =
580 Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
581 let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
582 SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
583 });
584
585 let crash_reporter = CrashReporter::new(
586 &inspect_node,
587 crash_reporter_proxy,
588 zx::Duration::from_minutes(8),
589 features.crash_report_throttling,
590 );
591 let hrtimer_manager = HrTimerManager::new(&inspect_node);
592
593 let cpu_feature_flags =
594 zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
595 log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
596 CpuFeatureFlags::empty()
597 });
598 let cpu_sve_feature_flags = zx::system_get_feature_flags::<CpuSveFeatureFlags>()
599 .unwrap_or_else(|e| {
600 log_debug!("CPU SVE feature flags failed: {}, reporting 0", e);
601 CpuSveFeatureFlags::empty()
602 });
603 let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags, cpu_sve_feature_flags);
604
605 let this = Arc::new_cyclic(|kernel| Kernel {
606 weak_self: kernel.clone(),
607 kthreads: KernelThreads::new(kernel.clone()),
608 features,
609 pids: Default::default(),
610 init_task: OnceLock::new(),
611 trace_event_manager: Arc::new(TracePerformanceEventManager::new(kernel.clone())),
612 expando: Default::default(),
613 default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
614 default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
615 vsock_address_maker,
616 ),
617 cmdline,
618 device_tree,
619 security_state,
620 device_registry: Default::default(),
621 container_namespace,
622 mounts_lock: RwSeqLock::new(MountsWriteToken::new().into()),
623 remote_block_device_registry: Default::default(),
624 iptables: OnceLock::new(),
625 shared_futexes: FutexTable::default(),
626 root_uts_ns: Arc::new(UtsNamespace::default().into()),
627 vdso: Vdso::new(),
628 vdso_arch32: Vdso::new_arch32(),
629 netstack_devices: Arc::default(),
630 swap_files: Default::default(),
631 generic_netlink: OnceLock::new(),
632 network_netlink: OnceLock::new(),
633 inspect_node,
634 actions_logged: AtomicU16::new(0),
635 suspend_resume_manager: Default::default(),
636 next_mount_id: AtomicCounter::<u64>::new(1),
637 next_peer_group_id: AtomicCounter::<u64>::new(1),
638 next_namespace_id: AtomicCounter::<u64>::new(1),
639 next_file_object_id: Default::default(),
640 system_limits,
641 ptrace_scope: AtomicU8::new(0), restrict_dmesg: AtomicBool::new(false),
643 disable_unprivileged_bpf: AtomicU8::new(0), perf_event_paranoid: AtomicI32::new(2),
645 build_version: OnceCell::new(),
646 stats: Arc::new(KernelStats::default()),
647 delayed_releaser: Default::default(),
648 scheduler,
649 syslog: Default::default(),
650 mounts: Mounts::new(),
651 hrtimer_manager,
652 memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
653 crash_reporter,
654 shutdown_start: OnceLock::new(),
655 container_control_handle: Default::default(),
656 ebpf_state: Default::default(),
657 cgroups: Default::default(),
658 time_adjustment_proxy,
659 netstack_wake_group: OnceLock::new(),
660 socket_tokens_store: Default::default(),
661 hwcaps,
662 syscall_log_filters: Default::default(),
663 });
664
665 this.device_registry.objects.init(&this);
669
670 let kernel = Arc::downgrade(&this);
673 this.inspect_node.record_lazy_child("thread_groups", move || {
674 if let Some(kernel) = kernel.upgrade() {
675 let inspector = kernel.get_thread_groups_inspect();
676 async move { Ok(inspector) }.boxed()
677 } else {
678 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
679 }
680 });
681
682 let kernel = Arc::downgrade(&this);
683 this.inspect_node.record_lazy_child("cgroupv2", move || {
684 if let Some(kernel) = kernel.upgrade() {
685 async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
686 } else {
687 async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
688 }
689 });
690
691 Ok(this)
692 }
693
694 pub fn get_init_task(&self) -> Result<Arc<Task>, Errno> {
696 self.init_task.get().and_then(|t| t.upgrade()).ok_or_else(|| errno!(EINVAL))
697 }
698
699 pub fn shut_down(self: &Arc<Self>) {
702 self.kthreads.spawn_future(
705 {
706 let kernel = self.clone();
707 move || async move {
708 kernel.run_shutdown().await;
709 }
710 },
711 "run_shutdown",
712 );
713 }
714
715 async fn run_shutdown(&self) {
737 const INIT_PID: i32 = 1;
738 const SYSTEM_TASK_PID: i32 = 2;
739
740 if self.shutdown_start.set(zx::MonotonicInstant::get()).is_err() {
745 log_info!("Additional thread tried to initiate shutdown while already in-progress.");
746 return;
747 }
748 let deferred_shutdown_deadline =
749 self.deferred_shutdown_deadline().expect("shutdown has started");
750
751 log_info!("Shutting down Starnix kernel.");
752
753 let stop_last = &self.features.stop_last_on_shutdown;
760 let is_stop_last = |tg: &ThreadGroup| {
761 let exe = tg.read().first_task().and_then(|task| task.mm().ok());
762 let exe = exe.and_then(|mm| mm.executable_node());
763 exe.is_some_and(|exe| stop_last.contains(&exe.path_escaping_chroot()))
764 };
765 let mut keep_stop_last = !stop_last.is_empty();
766 loop {
767 let tgs = {
770 let pids = self.pids.lock();
771 pids.get_thread_groups(&RcuReadScope::new())
772 .filter(|tg| tg.leader.id != SYSTEM_TASK_PID && tg.leader.id != INIT_PID)
773 .collect::<Vec<_>>()
774 };
775 let tgs = tgs
776 .into_iter()
777 .filter(|tg| !(keep_stop_last && is_stop_last(tg)))
778 .collect::<Vec<_>>();
779 if tgs.is_empty() {
780 if keep_stop_last {
781 keep_stop_last = false;
782 continue;
783 }
784 log_info!("pid table is empty except init and system task");
785 break;
786 }
787
788 log_info!(tgs:?; "shutting down thread groups");
789 let mut tasks = vec![];
790 for tg in tgs {
791 let task = fasync::Task::local(ThreadGroup::shut_down(
792 Arc::downgrade(&tg),
793 deferred_shutdown_deadline,
794 ));
795 tasks.push(task);
796 }
797 futures::future::join_all(tasks).await;
798 }
799
800 let maybe_init = self.get_init_task().ok().map(|t| Arc::downgrade(&t.thread_group));
802 if let Some(init) = maybe_init {
803 log_info!("shutting down init");
804 ThreadGroup::shut_down(init, deferred_shutdown_deadline).await;
805 self.kill_processes_created_during_shutdown();
807 } else {
808 log_info!("init already terminated");
809 }
810
811 log_info!("cleaning up pinned memory");
813 self.expando.remove::<crate::mm::InfoCacheShadowProcess>();
814 self.expando.remove::<crate::mm::MlockShadowProcess>();
815
816 let kernel_job = fuchsia_runtime::job_default();
823 assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
824 let own_koid = fuchsia_runtime::process_self().koid().unwrap();
825
826 log_info!("waiting for this to be the only process in the job");
827 loop {
828 let mut remaining_processes = kernel_job
829 .processes()
830 .unwrap()
831 .into_iter()
832 .filter(|pid| pid != &own_koid)
834 .peekable();
835 if remaining_processes.peek().is_none() {
836 log_info!("No stray Zircon processes.");
837 break;
838 }
839
840 let mut terminated_signals = vec![];
841 for pid in remaining_processes {
842 let handle = match kernel_job
843 .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
844 {
845 Ok(h) => h,
846 Err(e) => {
847 log_info!(pid:?, e:?; "failed to get child process from job");
848 continue;
849 }
850 };
851 log_info!(
852 pid:?,
853 name:? = handle.get_name();
854 "waiting on process terminated signal"
855 );
856 terminated_signals
857 .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
858 }
859 log_info!("waiting on process terminated signals");
860 futures::future::join_all(terminated_signals).await;
861 }
862
863 log_info!("clearing mounts");
865 self.mounts.clear();
866
867 log_info!("all non-root processes killed, notifying CF container is stopped");
869 if let Some(control_handle) = self.container_control_handle.lock().take() {
870 log_info!("Notifying CF that the container has stopped.");
871 control_handle
872 .send_on_stop(ComponentStopInfo {
873 termination_status: Some(zx::sys::ZX_OK),
874 exit_code: Some(0),
875 ..ComponentStopInfo::default()
876 })
877 .unwrap();
878 control_handle.shutdown_with_epitaph(Ok(()));
879 } else {
880 log_warn!("Shutdown invoked without a container controller control handle.");
881 }
882
883 log_info!("All tasks killed, exiting Starnix kernel root process.");
885 zx::Process::exit(0);
893 }
894
895 pub fn is_shutting_down(&self) -> bool {
896 self.shutdown_start.get().is_some()
897 }
898
899 pub fn deferred_shutdown_deadline(&self) -> Option<zx::MonotonicInstant> {
902 self.shutdown_start.get().map(|start| *start + DEFERRED_SHUTDOWN_TIMEOUT)
903 }
904
905 pub fn kill_processes_created_during_shutdown(&self) {
914 let created = {
917 let pids = self.pids.lock();
918 pids.get_thread_groups(&RcuReadScope::new())
919 .filter(|tg| tg.created_during_shutdown.load(Ordering::Relaxed))
920 .collect::<Vec<_>>()
921 };
922 for tg in created {
923 tg.kill(ExitStatus::Kill(SignalInfo::kernel(SIGKILL)), None);
924 }
925 }
926
927 pub fn allow_unprivileged_bpf(&self) -> bool {
928 self.disable_unprivileged_bpf.load(Ordering::Relaxed) == 0
929 }
930
931 pub fn open_device(
933 &self,
934 current_task: &CurrentTask,
935 node: &NamespaceNode,
936 flags: OpenFlags,
937 dev: DeviceId,
938 mode: DeviceMode,
939 ) -> Result<Box<dyn FileOps>, Errno> {
940 self.device_registry.open_device(current_task, node, flags, dev, mode)
941 }
942
943 pub fn audit_logger(&self) -> Arc<AuditLogger> {
947 self.expando.get_or_init(|| AuditLogger::new(self))
948 }
949
950 pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
955 self.generic_netlink.get_or_init(|| {
956 let (generic_netlink, worker_params) = GenericNetlink::new();
957 let enable_nl80211 = self.features.wifi;
958 self.kthreads.spawn_future(
959 move || async move {
960 crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
961 .await;
962 log_error!("Generic Netlink future unexpectedly exited");
963 },
964 "generic_netlink_worker",
965 );
966 generic_netlink
967 })
968 }
969
970 pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
975 self.network_netlink.get_or_init(|| {
976 let (network_netlink, worker_params) =
977 Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
978
979 let kernel = self.clone();
980 self.kthreads.spawn_future(
981 move || async move {
982 netlink::run_netlink_worker(
983 worker_params,
984 NetlinkAccessControl::new(kernel.kthreads.system_task()),
985 )
986 .await;
987 log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
988 },
989 "network_netlink_worker",
990 );
991 network_netlink
992 })
993 }
994
995 pub(crate) fn netstack_wake_group(&self) -> Option<&fnet_resources::WakeGroupToken> {
1001 self.netstack_wake_group
1002 .get_or_init(|| {
1003 const GROUP_WAKEUP_SIGNAL: zx::Signals =
1006 zx::Signals::from_bits(fnet_power::GROUP_WAKEUP_SIGNAL).unwrap();
1007
1008 let provider = fuchsia_component::client::connect_to_protocol_sync::<
1009 fnet_power::WakeGroupProviderMarker,
1010 >()
1011 .expect("connect to WakeGroupProvider");
1012
1013 let (wake_watcher_waiter, wake_watcher_signaller) = zx::EventPair::create();
1014 create_watcher_for_wake_events(wake_watcher_signaller);
1015
1016 let token = match provider.create_wake_group(
1017 &fnet_power::WakeGroupOptions {
1018 debug_name: Some(String::from("starnix")),
1019 ..Default::default()
1020 },
1021 wake_watcher_waiter,
1022 zx::Instant::INFINITE,
1023 ) {
1024 Ok(fnet_power::CreateWakeGroupResponse { token, .. }) => token,
1025 Err(e) => {
1026 log_error!("failed to create wake group with the netstack: {e}");
1027 return None;
1028 }
1029 };
1030
1031 let token = token.expect("netstack provides a wake group token");
1032 let wake_source = token
1033 .token
1034 .duplicate_handle(zx::Rights::SAME_RIGHTS)
1035 .expect("duplicate handle to token");
1036 self.suspend_resume_manager
1037 .add_external_wake_source(
1038 wake_source.into(),
1039 GROUP_WAKEUP_SIGNAL,
1040 "netstack-wake-group".into(),
1041 )
1042 .expect("add wake group as wake source");
1043
1044 Some(token)
1045 })
1046 .as_ref()
1047 }
1048
1049 pub fn iptables(&self) -> &IpTables {
1050 self.iptables.get_or_init(|| IpTables::new())
1051 }
1052
1053 #[allow(unused)]
1055 pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
1056 &self,
1057 filename: &str,
1058 ) -> Result<ClientEnd<P>, Errno> {
1059 match self.container_namespace.get_namespace_channel("/svc") {
1060 Ok(channel) => {
1061 let (client_end, server_end) = create_endpoints::<P>();
1062 fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
1063 .map_err(|status| from_status_like_fdio!(status))?;
1064 Ok(client_end)
1065 }
1066 Err(err) => {
1067 log_error!("Unable to get /svc namespace channel! {}", err);
1068 Err(errno!(ENOENT))
1069 }
1070 }
1071 }
1072
1073 pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
1075 &self,
1076 ) -> Result<ClientEnd<P>, Errno> {
1077 self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
1078 }
1079
1080 pub fn add_syscall_log_filter(&self, name: &str) {
1081 let filter = SyscallLogFilter::new(name.to_string());
1082 {
1083 let mut filters = self.syscall_log_filters.lock();
1084 if filters.contains(&filter) {
1085 return;
1086 }
1087 filters.push(filter);
1088 }
1089 for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
1090 headers.sync_syscall_log_level();
1091 }
1092 }
1093
1094 pub fn clear_syscall_log_filters(&self) {
1095 {
1096 let mut filters = self.syscall_log_filters.lock();
1097 if filters.is_empty() {
1098 return;
1099 }
1100 filters.clear();
1101 }
1102 for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
1103 headers.sync_syscall_log_level();
1104 }
1105 }
1106
1107 fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
1108 let inspector = fuchsia_inspect::Inspector::default();
1109
1110 let thread_groups = inspector.root();
1111 let mut mm_summary = MappingSummary::default();
1112 let mut mms_summarized = HashSet::new();
1113
1114 for thread_group in self.pids.get_thread_groups(&RcuReadScope::new()) {
1115 let (ppid, tasks) = {
1117 let tg = thread_group.read();
1118 (tg.get_ppid() as i64, tg.tasks())
1119 };
1120
1121 let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
1122 if let Ok(koid) = thread_group.process.koid() {
1123 tg_node.record_int("koid", koid.raw_koid() as i64);
1124 }
1125 tg_node.record_int("pid", thread_group.leader.id as i64);
1126 tg_node.record_int("ppid", ppid);
1127 tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
1128
1129 let tasks_node = tg_node.create_child("tasks");
1130 for task in tasks {
1131 if let Ok(mm) = task.mm() {
1132 if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
1133 mm.summarize(&mut mm_summary);
1134 }
1135 }
1136 let set_properties = |node: &fuchsia_inspect::Node| {
1137 node.record_string("command", task.command().to_string());
1138
1139 let scheduler_state = task.scheduler_state.lock().clone();
1140 if !scheduler_state.is_default() {
1141 node.record_child("sched", |node| {
1142 node.record_string(
1143 "role_name",
1144 self.scheduler
1145 .role_name(&task, &scheduler_state)
1146 .map(|n| Cow::Borrowed(n))
1147 .unwrap_or_else(|e| Cow::Owned(e.to_string())),
1148 );
1149 node.record_string("state", format!("{scheduler_state:?}"));
1150 });
1151 }
1152 };
1153 if task.tid == thread_group.leader {
1154 let mut argv = task.read_argv(256).unwrap_or_default();
1155
1156 argv.retain(|arg| !arg.is_empty());
1159
1160 let inspect_argv = tg_node.create_string_array("argv", argv.len());
1161 for (i, arg) in argv.iter().enumerate() {
1162 inspect_argv.set(i, arg.to_string());
1163 }
1164 tg_node.record(inspect_argv);
1165
1166 set_properties(&tg_node);
1167 } else {
1168 tasks_node.record_child(task.tid.to_string(), |task_node| {
1169 set_properties(task_node);
1170 });
1171 };
1172 }
1173 tg_node.record(tasks_node);
1174 thread_groups.record(tg_node);
1175 }
1176
1177 thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
1178
1179 inspector
1180 }
1181
1182 pub fn new_memory_attribution_observer(
1183 &self,
1184 control_handle: fattribution::ProviderControlHandle,
1185 ) -> attribution_server::Observer {
1186 self.memory_attribution_manager.new_observer(control_handle)
1187 }
1188
1189 pub fn open_ns_dir(
1201 &self,
1202 path: &str,
1203 open_flags: fio::Flags,
1204 ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
1205 let ns_path = PathBuf::from(path);
1206 match self.container_namespace.find_closest_channel(&ns_path) {
1207 Ok((root_channel, remaining_subdir)) => {
1208 let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
1209 fdio::open_at(
1210 &root_channel,
1211 &remaining_subdir,
1212 open_flags,
1213 server_end.into_channel(),
1214 )
1215 .map_err(|e| {
1216 log_error!("Failed to intialize the subdirs: {}", e);
1217 errno!(EIO)
1218 })?;
1219
1220 Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
1221 }
1222 Err(err) => {
1223 log_error!(
1224 "Unable to find a channel for {}. Received error: {}",
1225 ns_path.display(),
1226 err
1227 );
1228 Err(errno!(ENOENT))
1229 }
1230 }
1231 }
1232
1233 pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
1235 parse_cmdline(self.cmdline.to_str().unwrap_or_default()).map(ArgNameAndValue::from)
1236 }
1237
1238 pub fn fs_cache_config(&self) -> CacheConfig {
1240 CacheConfig { capacity: self.features.dirent_cache_size as usize }
1241 }
1242
1243 pub fn mounts_lock(&self) -> MountsWriteGuard<'_> {
1244 MountsWriteGuard::new(self.mounts_lock.lock())
1245 }
1246}
1247
1248pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
1249 let mut args = Vec::new();
1250 let mut arg_start: Option<usize> = None;
1251 let mut in_quotes = false;
1252 let mut previous_char = ' ';
1253
1254 for (i, c) in cmdline.char_indices() {
1255 if let Some(start) = arg_start {
1256 match c {
1257 ' ' if !in_quotes => {
1258 args.push(&cmdline[start..i]);
1259 arg_start = None;
1260 }
1261 '"' if previous_char != '\\' => {
1262 in_quotes = !in_quotes;
1263 }
1264 _ => {}
1265 }
1266 } else if c != ' ' {
1267 arg_start = Some(i);
1268 if c == '"' {
1269 in_quotes = true;
1270 }
1271 }
1272 previous_char = c;
1273 }
1274 if let Some(start) = arg_start {
1275 args.push(&cmdline[start..]);
1276 }
1277 args.into_iter()
1278}
1279
1280impl std::fmt::Debug for Kernel {
1281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1282 f.debug_struct("Kernel").finish()
1283 }
1284}
1285
1286#[cfg(target_arch = "aarch64")]
1288fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1289 use starnix_uapi::arch32;
1290 const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1291 | arch32::HWCAP_THUMB
1292 | arch32::HWCAP_FAST_MULT
1293 | arch32::HWCAP_EDSP
1294 | arch32::HWCAP_TLS
1295 | arch32::HWCAP_IDIV | arch32::HWCAP_LPAE
1297 | arch32::HWCAP_EVTSTRM;
1298
1299 let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1300 let mut hwcap2 = 0;
1301 for feature in cpu_feature_flags.iter() {
1302 match feature {
1303 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1304 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1305 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1306 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1307 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1308 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1309 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1310 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1311 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1312 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1313 hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1314 }
1315 _ => {}
1316 }
1317 }
1318 if cpu_feature_flags
1323 .intersects(CpuFeatureFlags::ARM64_FEATURE_ISA_FHM | CpuFeatureFlags::ARM64_FEATURE_ISA_SVE)
1324 {
1325 hwcap |= arch32::HWCAP_FPHP | arch32::HWCAP_ASIMDHP;
1326 }
1327 HwCap { hwcap, hwcap2 }
1328}
1329
1330#[cfg(target_arch = "aarch64")]
1331fn arm64_hwcap(
1332 cpu_feature_flags: CpuFeatureFlags,
1333 cpu_sve_feature_flags: CpuSveFeatureFlags,
1334) -> HwCap {
1335 use starnix_uapi;
1337 let mut hwcap = 0;
1338 let mut hwcap2 = 0;
1339
1340 for feature in cpu_feature_flags.iter() {
1341 match feature {
1342 CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1343 CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1344 CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1345 CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1346 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1347 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1348 CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1349 CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1350 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1351 CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1352 CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1353 CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1354 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1355 CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1356 CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1357 CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1358 CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1359 CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1360 CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1361 CpuFeatureFlags::ARM64_FEATURE_ISA_SVE => hwcap |= starnix_uapi::HWCAP_SVE,
1362 _ => {}
1363 }
1364 }
1365 if cpu_feature_flags
1372 .intersects(CpuFeatureFlags::ARM64_FEATURE_ISA_FHM | CpuFeatureFlags::ARM64_FEATURE_ISA_SVE)
1373 {
1374 hwcap |= starnix_uapi::HWCAP_FPHP | starnix_uapi::HWCAP_ASIMDHP;
1375 }
1376 if cpu_feature_flags.contains(CpuFeatureFlags::ARM64_FEATURE_ISA_SVE) {
1377 for feature in cpu_sve_feature_flags.iter() {
1378 match feature {
1379 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEI8MM => {
1380 hwcap2 |= starnix_uapi::HWCAP2_SVEI8MM
1381 }
1382 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEF32MM => {
1383 hwcap2 |= starnix_uapi::HWCAP2_SVEF32MM
1384 }
1385 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEF64MM => {
1386 hwcap2 |= starnix_uapi::HWCAP2_SVEF64MM
1387 }
1388 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEBF16 => {
1389 hwcap2 |= starnix_uapi::HWCAP2_SVEBF16
1390 }
1391 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVE2 => hwcap2 |= starnix_uapi::HWCAP2_SVE2,
1392 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEAES => {
1393 hwcap2 |= starnix_uapi::HWCAP2_SVEAES
1394 }
1395 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEPMULL => {
1396 hwcap2 |= starnix_uapi::HWCAP2_SVEPMULL
1397 }
1398 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEBITPERM => {
1399 hwcap2 |= starnix_uapi::HWCAP2_SVEBITPERM
1400 }
1401 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVESHA3 => {
1402 hwcap2 |= starnix_uapi::HWCAP2_SVESHA3
1403 }
1404 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVESM4 => {
1405 hwcap2 |= starnix_uapi::HWCAP2_SVESM4
1406 }
1407 _ => {}
1408 }
1409 }
1410 }
1411 HwCap { hwcap, hwcap2 }
1412}
1413
1414impl HwCaps {
1415 #[cfg(target_arch = "aarch64")]
1416 pub fn from_cpu_feature_flags(
1417 cpu_feature_flags: CpuFeatureFlags,
1418 cpu_sve_feature_flags: CpuSveFeatureFlags,
1419 ) -> Self {
1420 Self {
1421 arch32: arm32_hwcap(cpu_feature_flags),
1422 arch64: arm64_hwcap(cpu_feature_flags, cpu_sve_feature_flags),
1423 }
1424 }
1425
1426 #[cfg(not(target_arch = "aarch64"))]
1427 pub fn from_cpu_feature_flags(
1428 _cpu_feature_flags: CpuFeatureFlags,
1429 _cpu_sve_feature_flags: CpuSveFeatureFlags,
1430 ) -> Self {
1431 Self { arch64: HwCap::default() }
1432 }
1433}
1434
1435#[cfg(test)]
1436mod test {
1437 use super::*;
1438
1439 #[test]
1440 fn test_parse_cmdline() {
1441 let cmdline =
1442 r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1443 let expected = vec![
1444 "first",
1445 "second=third",
1446 "\"fourth fifth\"",
1447 "sixth=\"seventh eighth\"",
1448 "\"ninth\\\" tenth\"",
1449 "eleventh",
1450 ];
1451 assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1452 }
1453
1454 #[cfg(target_arch = "aarch64")]
1455 #[test]
1456 fn test_arm64_hwcap_sve() {
1457 let empty =
1458 HwCaps::from_cpu_feature_flags(CpuFeatureFlags::empty(), CpuSveFeatureFlags::empty());
1459 assert_eq!(empty.arch64.hwcap, 0);
1460 assert_eq!(empty.arch64.hwcap2, 0);
1461
1462 let expected_sve_hwcap =
1463 starnix_uapi::HWCAP_SVE | starnix_uapi::HWCAP_FPHP | starnix_uapi::HWCAP_ASIMDHP;
1464 let sve_only = HwCaps::from_cpu_feature_flags(
1465 CpuFeatureFlags::ARM64_FEATURE_ISA_SVE,
1466 CpuSveFeatureFlags::empty(),
1467 );
1468 assert_eq!(sve_only.arch64.hwcap, expected_sve_hwcap);
1469 assert_eq!(sve_only.arch64.hwcap2, 0);
1470 assert_eq!(
1471 sve_only.arch32.hwcap,
1472 empty.arch32.hwcap
1473 | starnix_uapi::arch32::HWCAP_FPHP
1474 | starnix_uapi::arch32::HWCAP_ASIMDHP
1475 );
1476 assert_eq!(sve_only.arch32.hwcap2, 0);
1477
1478 let fhm_only = HwCaps::from_cpu_feature_flags(
1479 CpuFeatureFlags::ARM64_FEATURE_ISA_FHM,
1480 CpuSveFeatureFlags::empty(),
1481 );
1482 assert_eq!(
1483 fhm_only.arch64.hwcap,
1484 starnix_uapi::HWCAP_ASIMDFHM | starnix_uapi::HWCAP_FPHP | starnix_uapi::HWCAP_ASIMDHP
1485 );
1486 assert_eq!(fhm_only.arch64.hwcap2, 0);
1487 assert_eq!(
1488 fhm_only.arch32.hwcap,
1489 empty.arch32.hwcap
1490 | starnix_uapi::arch32::HWCAP_ASIMDFHM
1491 | starnix_uapi::arch32::HWCAP_FPHP
1492 | starnix_uapi::arch32::HWCAP_ASIMDHP
1493 );
1494 assert_eq!(fhm_only.arch32.hwcap2, 0);
1495
1496 let cases = [
1497 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEI8MM, starnix_uapi::HWCAP2_SVEI8MM),
1498 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEF32MM, starnix_uapi::HWCAP2_SVEF32MM),
1499 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEF64MM, starnix_uapi::HWCAP2_SVEF64MM),
1500 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEBF16, starnix_uapi::HWCAP2_SVEBF16),
1501 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVE2, starnix_uapi::HWCAP2_SVE2),
1502 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEAES, starnix_uapi::HWCAP2_SVEAES),
1503 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEPMULL, starnix_uapi::HWCAP2_SVEPMULL),
1504 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEBITPERM, starnix_uapi::HWCAP2_SVEBITPERM),
1505 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVESHA3, starnix_uapi::HWCAP2_SVESHA3),
1506 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVESM4, starnix_uapi::HWCAP2_SVESM4),
1507 ];
1508
1509 let mut all_sve_flags = CpuSveFeatureFlags::empty();
1510 let mut expected_hwcap2 = 0;
1511 for (flag, hwcap2_bit) in cases {
1512 let without_sve = HwCaps::from_cpu_feature_flags(CpuFeatureFlags::empty(), flag);
1513 assert_eq!(without_sve.arch64.hwcap, 0);
1514 assert_eq!(without_sve.arch64.hwcap2, 0);
1515
1516 let caps = HwCaps::from_cpu_feature_flags(CpuFeatureFlags::ARM64_FEATURE_ISA_SVE, flag);
1517 assert_eq!(caps.arch64.hwcap, expected_sve_hwcap);
1518 assert_eq!(caps.arch64.hwcap2, hwcap2_bit);
1519 all_sve_flags |= flag;
1520 expected_hwcap2 |= hwcap2_bit;
1521 }
1522
1523 let all_caps =
1524 HwCaps::from_cpu_feature_flags(CpuFeatureFlags::ARM64_FEATURE_ISA_SVE, all_sve_flags);
1525 assert_eq!(all_caps.arch64.hwcap, expected_sve_hwcap);
1526 assert_eq!(all_caps.arch64.hwcap2, expected_hwcap2);
1527 }
1528
1529 #[cfg(target_arch = "aarch64")]
1530 #[test]
1531 fn test_arm64_hwcap_sve_gating_and_linux_abi() {
1532 assert_eq!(starnix_uapi::HWCAP_FP, 1 << 0);
1537 assert_eq!(starnix_uapi::HWCAP_ASIMD, 1 << 1);
1538 assert_eq!(starnix_uapi::HWCAP_FPHP, 1 << 9);
1539 assert_eq!(starnix_uapi::HWCAP_ASIMDHP, 1 << 10);
1540 assert_eq!(starnix_uapi::HWCAP_SVE, 1 << 22);
1541 assert_eq!(starnix_uapi::HWCAP_ASIMDFHM, 1 << 23);
1542 assert_eq!(starnix_uapi::arch32::HWCAP_FPHP, 1 << 22);
1543 assert_eq!(starnix_uapi::arch32::HWCAP_ASIMDHP, 1 << 23);
1544 assert_eq!(starnix_uapi::arch32::HWCAP_ASIMDFHM, 1 << 25);
1545 let expected_sve_hwcap =
1546 starnix_uapi::HWCAP_SVE | starnix_uapi::HWCAP_FPHP | starnix_uapi::HWCAP_ASIMDHP;
1547 let sve_mappings = [
1548 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVE2, starnix_uapi::HWCAP2_SVE2, 1 << 1),
1549 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEAES, starnix_uapi::HWCAP2_SVEAES, 1 << 2),
1550 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEPMULL, starnix_uapi::HWCAP2_SVEPMULL, 1 << 3),
1551 (
1552 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEBITPERM,
1553 starnix_uapi::HWCAP2_SVEBITPERM,
1554 1 << 4,
1555 ),
1556 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVESHA3, starnix_uapi::HWCAP2_SVESHA3, 1 << 5),
1557 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVESM4, starnix_uapi::HWCAP2_SVESM4, 1 << 6),
1558 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEI8MM, starnix_uapi::HWCAP2_SVEI8MM, 1 << 9),
1559 (
1560 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEF32MM,
1561 starnix_uapi::HWCAP2_SVEF32MM,
1562 1 << 10,
1563 ),
1564 (
1565 CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEF64MM,
1566 starnix_uapi::HWCAP2_SVEF64MM,
1567 1 << 11,
1568 ),
1569 (CpuSveFeatureFlags::ARM64_FEATURE_ISA_SVEBF16, starnix_uapi::HWCAP2_SVEBF16, 1 << 12),
1570 ];
1571 let mut all_sve_flags = CpuSveFeatureFlags::empty();
1572 let mut all_sve_hwcap2 = 0u32;
1573 for (flag, hwcap2_bit, linux_bit) in sve_mappings {
1574 assert_eq!(hwcap2_bit, linux_bit, "{flag:?}");
1575 assert_eq!(all_sve_hwcap2 & hwcap2_bit, 0, "{flag:?} HWCAP2 bit is not unique");
1576 all_sve_flags |= flag;
1577 all_sve_hwcap2 |= hwcap2_bit;
1578 }
1579 let all_named_sve_flags = <CpuSveFeatureFlags as bitflags::Flags>::FLAGS
1581 .iter()
1582 .filter(|f| f.is_named())
1583 .fold(CpuSveFeatureFlags::empty(), |acc, f| acc | *f.value());
1584 assert_eq!(all_sve_flags, all_named_sve_flags);
1585
1586 let non_sve_cpu_flags = CpuFeatureFlags::ARM64_FEATURE_ISA_FP
1590 | CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD
1591 | CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM
1592 | CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR;
1593 let non_sve_hwcap = starnix_uapi::HWCAP_FP | starnix_uapi::HWCAP_ASIMD;
1594 let non_sve_hwcap2 = starnix_uapi::HWCAP2_I8MM | starnix_uapi::HWCAP2_RNG;
1595 let baseline =
1596 HwCaps::from_cpu_feature_flags(non_sve_cpu_flags, CpuSveFeatureFlags::empty());
1597 assert_eq!(baseline.arch64.hwcap, non_sve_hwcap);
1598 assert_eq!(baseline.arch64.hwcap2, non_sve_hwcap2);
1599 for sve_flags in [all_sve_flags, CpuSveFeatureFlags::from_bits_retain(!0)] {
1600 let gated = HwCaps::from_cpu_feature_flags(CpuFeatureFlags::empty(), sve_flags);
1601 assert_eq!(gated.arch64.hwcap, 0, "{sve_flags:?}");
1602 assert_eq!(gated.arch64.hwcap2, 0, "{sve_flags:?}");
1603
1604 let gated = HwCaps::from_cpu_feature_flags(non_sve_cpu_flags, sve_flags);
1605 assert_eq!(gated.arch64.hwcap, non_sve_hwcap, "{sve_flags:?}");
1606 assert_eq!(gated.arch64.hwcap & starnix_uapi::HWCAP_SVE, 0, "{sve_flags:?}");
1607 assert_eq!(gated.arch64.hwcap2, non_sve_hwcap2, "{sve_flags:?}");
1608 assert_eq!(gated.arch64.hwcap2 & all_sve_hwcap2, 0, "{sve_flags:?}");
1609 assert_eq!(gated.arch32.hwcap, baseline.arch32.hwcap);
1611 assert_eq!(gated.arch32.hwcap2, baseline.arch32.hwcap2);
1612
1613 let enabled = HwCaps::from_cpu_feature_flags(
1616 non_sve_cpu_flags | CpuFeatureFlags::ARM64_FEATURE_ISA_SVE,
1617 sve_flags,
1618 );
1619 assert_eq!(enabled.arch64.hwcap, non_sve_hwcap | expected_sve_hwcap);
1620 assert_eq!(enabled.arch64.hwcap2, non_sve_hwcap2 | all_sve_hwcap2);
1621 assert_eq!(
1622 enabled.arch32.hwcap,
1623 baseline.arch32.hwcap
1624 | starnix_uapi::arch32::HWCAP_FPHP
1625 | starnix_uapi::arch32::HWCAP_ASIMDHP
1626 );
1627 assert_eq!(enabled.arch32.hwcap2, baseline.arch32.hwcap2);
1628 }
1629
1630 for (flag, hwcap2_bit, _) in sve_mappings {
1633 let all_but_one = HwCaps::from_cpu_feature_flags(
1634 CpuFeatureFlags::ARM64_FEATURE_ISA_SVE,
1635 all_sve_flags.difference(flag),
1636 );
1637 assert_eq!(all_but_one.arch64.hwcap, expected_sve_hwcap, "{flag:?}");
1638 assert_eq!(all_but_one.arch64.hwcap2, all_sve_hwcap2 & !hwcap2_bit, "{flag:?}");
1639 }
1640 }
1641}