Skip to main content

starnix_core/task/
kernel.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::bpf::EbpfState;
6use crate::device::remote_block_device::RemoteBlockDeviceRegistry;
7use crate::device::{DeviceMode, DeviceRegistry};
8use crate::execution::CrashReporter;
9use crate::mm::{FutexTable, MappingSummary, MlockPinFlavor, SharedFutexKey};
10use crate::power::{SuspendResumeManagerHandle, create_watcher_for_wake_events};
11use crate::ptrace::StopState;
12use crate::security::{self, AuditLogger};
13use crate::task::container_namespace::ContainerNamespace;
14use crate::task::limits::SystemLimits;
15use crate::task::memory_attribution::MemoryAttributionManager;
16use crate::task::net::NetstackDevices;
17use crate::task::tracing::TracePerformanceEventManager;
18use crate::task::{
19    AbstractUnixSocketNamespace, AbstractVsockSocketNamespace, CurrentTask, DelayedReleaser,
20    IpTables, KernelCgroups, KernelStats, KernelThreads, PidTable, SchedulerManager, Syslog, Task,
21    ThreadGroup, UtsNamespace, UtsNamespaceHandle,
22};
23use crate::time::{HrTimerManager, HrTimerManagerHandle};
24use crate::vdso::vdso_loader::Vdso;
25use crate::vfs::fs_args::MountParams;
26use crate::vfs::socket::{
27    GenericMessage, GenericNetlink, NetlinkAccessControl, NetlinkContextImpl,
28    NetlinkToClientSender, SocketAddress, SocketTokensStore,
29};
30use crate::vfs::{CacheConfig, FileOps, FsNodeHandle, FsString, Mounts, NamespaceNode};
31use bstr::{BString, ByteSlice};
32use devicetree::types::Devicetree;
33use expando::Expando;
34use fidl::endpoints::{ClientEnd, DiscoverableProtocolMarker, ProtocolMarker, create_endpoints};
35use fidl_fuchsia_component_runner::{ComponentControllerControlHandle, ComponentStopInfo};
36use fidl_fuchsia_feedback::CrashReporterProxy;
37use fidl_fuchsia_io as fio;
38use fidl_fuchsia_memory_attribution as fattribution;
39use fidl_fuchsia_net_power as fnet_power;
40use fidl_fuchsia_net_resources as fnet_resources;
41use fidl_fuchsia_time_external::AdjustSynchronousProxy;
42use fuchsia_async as fasync;
43use fuchsia_inspect::ArrayProperty;
44use fuchsia_rcu::RcuReadScope;
45use futures::FutureExt;
46use netlink::interfaces::InterfacesHandler;
47use netlink::{NETLINK_LOG_TAG, Netlink};
48use once_cell::sync::OnceCell;
49use scopeguard::ScopeGuard;
50use smallvec::SmallVec;
51use starnix_lifecycle::AtomicCounter;
52use starnix_logging::{SyscallLogFilter, log_debug, log_error, log_info, log_warn};
53use starnix_sync::{
54    ComponentControllerLock, KernelSwapFiles, LockDepGuard, LockDepMutex, MountsLevel, RwSeqLock,
55    RwSeqLockGuard, SyscallLogFiltersLock,
56};
57use starnix_uapi::device_id::DeviceId;
58use starnix_uapi::errors::{Errno, errno};
59use starnix_uapi::open_flags::OpenFlags;
60use starnix_uapi::{VMADDR_CID_HOST, from_status_like_fdio};
61use std::borrow::Cow;
62use std::cell::RefCell;
63use std::collections::{HashMap, HashSet};
64use std::num::NonZeroU64;
65use std::ops::Deref;
66use std::path::PathBuf;
67use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU8, AtomicU16, Ordering};
68use std::sync::{Arc, OnceLock, Weak};
69use zx::CpuFeatureFlags;
70
71/// Kernel features are specified in the component manifest of the starnix container
72/// or explicitly provided to the kernel constructor in tests.
73#[derive(Debug, Default, Clone)]
74pub struct KernelFeatures {
75    pub bpf_v2: bool,
76
77    /// Whether the kernel supports the S_ISUID and S_ISGID bits.
78    ///
79    /// For example, these bits are used by `sudo`.
80    ///
81    /// Enabling this feature is potentially a security risk because they allow privilege
82    /// escalation.
83    pub enable_suid: bool,
84
85    /// Whether io_uring is enabled.
86    ///
87    /// TODO(https://fxbug.dev/297431387): Enabled by default once the feature is completed.
88    pub io_uring: bool,
89
90    /// Whether the kernel should return an error to userspace, rather than panicking, if `reboot()`
91    /// is requested but cannot be enacted because the kernel lacks the relevant capabilities.
92    pub error_on_failed_reboot: bool,
93
94    /// The default seclabel that is applied to components that are run in this kernel.
95    ///
96    /// Components can override this by setting the `seclabel` field in their program block.
97    pub default_seclabel: Option<String>,
98
99    /// Whether the kernel is being used to run the SELinux Test Suite.
100    ///
101    /// TODO: https://fxbug.dev/388077431 - remove this once we no longer need workarounds for the
102    /// SELinux Test Suite.
103    pub selinux_test_suite: bool,
104
105    /// The default mount options to use when mounting directories from a component's namespace.
106    ///
107    /// The key is the path in the component's namespace, and the value is the mount options
108    /// string.
109    pub default_ns_mount_options: Option<HashMap<String, String>>,
110
111    /// The default uid that is applied to components that are run in this kernel.
112    ///
113    /// Components can override this by setting the `uid` field in their program block.
114    pub default_uid: u32,
115
116    /// mlock() never prefaults pages.
117    pub mlock_always_onfault: bool,
118
119    /// Implementation of mlock() to use for this kernel instance.
120    pub mlock_pin_flavor: MlockPinFlavor,
121
122    /// Whether excessive crash reports should be throttled.
123    pub crash_report_throttling: bool,
124
125    /// Whether or not to serve wifi support to Android.
126    pub wifi: bool,
127
128    /// The number of bytes to cache in pages for reading zx::MapInfo from VMARs.
129    pub cached_zx_map_info_bytes: u32,
130
131    /// The size of the Dirent LRU cache.
132    pub dirent_cache_size: u32,
133
134    /// Whether to expose a stub '/dev/ion' node, as a temporary workaround for compatibility.
135    // TODO(https://fxbug.dev/485370648) remove when unnecessary
136    pub fake_ion: bool,
137
138    /// Whether `remotevol` mounts the volume in-process rather than over FIDL.
139    pub remotevol_in_process: bool,
140
141    /// Whether to log unexpected lookup misses in `sysfs` via `track_stub_log!`.
142    pub log_sysfs_lookup_misses: bool,
143}
144
145impl KernelFeatures {
146    /// Returns the `MountParams` to use when mounting the specified path from a component's
147    /// namespace.  This mechanism is also used to specified options for mounts created via
148    /// container features, by specifying a pseudo-path e.g. "#container".
149    pub fn ns_mount_options(&self, ns_path: &str) -> Result<MountParams, Errno> {
150        if let Some(all_options) = &self.default_ns_mount_options {
151            if let Some(options) = all_options.get(ns_path) {
152                return MountParams::parse(options.as_bytes().into());
153            }
154        }
155        Ok(MountParams::default())
156    }
157}
158
159/// Kernel command line argument structure
160pub struct ArgNameAndValue<'a> {
161    pub name: &'a str,
162    pub value: Option<&'a str>,
163}
164
165type DeferredDropCallback = Box<dyn FnOnce() + Send + Sync>;
166
167/// A proof token representing the global lock over the namespace mount topology.
168///
169/// Functions that take `&MountsWriteToken` require the caller to hold the
170/// `Kernel::mounts_lock` to ensure safe modification of the global mount tree.
171pub struct MountsWriteToken {
172    deferred_drops: RefCell<SmallVec<[DeferredDropCallback; 8]>>,
173}
174
175impl MountsWriteToken {
176    fn new() -> Self {
177        Self { deferred_drops: RefCell::new(SmallVec::new()) }
178    }
179
180    fn defer_drop_internal<T: Send + Sync + 'static>(&self, value: T) {
181        self.deferred_drops.borrow_mut().push(Box::new(move || {
182            drop(value);
183        }));
184    }
185
186    /// We cannot block while holding a [MountsWriteGuard] as it is a spinlock. If a Drop requires
187    /// a blocking operation, use [Self::defer_drop] to defer the blocking operation until
188    /// after the [MountsWriteGuard] is released.
189    pub fn defer_drop<T: Send + Sync + 'static>(
190        &self,
191        value: T,
192    ) -> ScopeGuard<T, impl FnOnce(T) + '_> {
193        scopeguard::guard(value, |value| {
194            self.defer_drop_internal(value);
195        })
196    }
197
198    /// Helper method to [Self::defer_drop] specifically for [Arc<T>]s.
199    pub fn retain<T: Send + Sync + 'static>(&self, value: &Arc<T>) -> Arc<T> {
200        self.defer_drop_internal(value.clone());
201        value.clone()
202    }
203
204    fn take_deferred_drops(&mut self) -> SmallVec<[DeferredDropCallback; 8]> {
205        std::mem::take(self.deferred_drops.get_mut())
206    }
207}
208
209/// A guard special cased for mount operations to defer destroying mounts until after all
210/// modifications of the global mount tree are completed.
211pub struct MountsWriteGuard<'a> {
212    guard: Option<RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>>,
213}
214
215impl<'a> MountsWriteGuard<'a> {
216    fn new(guard: RwSeqLockGuard<'a, LockDepGuard<'a, MountsWriteToken>>) -> Self {
217        Self { guard: Some(guard) }
218    }
219}
220
221impl<'a> Deref for MountsWriteGuard<'a> {
222    type Target = MountsWriteToken;
223    fn deref(&self) -> &Self::Target {
224        self.guard.as_ref().unwrap()
225    }
226}
227
228impl<'a> Drop for MountsWriteGuard<'a> {
229    fn drop(&mut self) {
230        if let Some(mut guard) = self.guard.take() {
231            let drops = guard.take_deferred_drops();
232            drop(guard);
233            for callback in drops {
234                callback();
235            }
236        }
237    }
238}
239
240/// The shared, mutable state for the entire Starnix kernel.
241///
242/// The `Kernel` object holds all kernel threads, userspace tasks, and file system resources for a
243/// single instance of the Starnix kernel. In production, there is one instance of this object for
244/// the entire Starnix kernel. However, multiple instances of this object can be created in one
245/// process during unit testing.
246///
247/// The structure of this object will likely need to evolve as we implement more namespacing and
248/// isolation mechanisms, such as `namespaces(7)` and `pid_namespaces(7)`.
249pub struct Kernel {
250    /// Weak reference to self. Allows to not have to pass &Arc<Kernel> in apis.
251    pub weak_self: Weak<Kernel>,
252
253    /// The kernel threads running on behalf of this kernel.
254    pub kthreads: KernelThreads,
255
256    /// The features enabled for this kernel.
257    pub features: KernelFeatures,
258
259    /// The processes and threads running in this kernel, organized by pid_t.
260    pub pids: PidTable,
261
262    /// A weak reference to the init task (PID 1).
263    pub init_task: OnceLock<Weak<Task>>,
264
265    /// Shared manager of pid/tid to koid mappings for tracing and profiling clients.
266    /// A direct field (rather than an expando entry) so the task-creation fast path is a
267    /// single relaxed atomic load with no locks when nothing is recording.
268    pub trace_event_manager: Arc<TracePerformanceEventManager>,
269
270    /// Subsystem-specific properties that hang off the Kernel object.
271    ///
272    /// Instead of adding yet another property to the Kernel object, consider storing the property
273    /// in an expando if that property is only used by one part of the system, such as a module.
274    pub expando: Expando,
275
276    /// The default namespace for abstract AF_UNIX sockets in this kernel.
277    ///
278    /// Rather than use this default namespace, abstract socket addresses
279    /// should be looked up in the AbstractSocketNamespace on each Task
280    /// object because some Task objects might have a non-default namespace.
281    pub default_abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
282
283    /// The default namespace for abstract AF_VSOCK sockets in this kernel.
284    pub default_abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
285
286    /// The kernel command line. Shows up in /proc/cmdline.
287    pub cmdline: BString,
288
289    pub device_tree: Option<Devicetree>,
290
291    // Global state held by the Linux Security Modules subsystem.
292    pub security_state: security::KernelState,
293
294    /// The registry of device drivers.
295    pub device_registry: DeviceRegistry,
296
297    /// Mapping of top-level namespace entries to an associated proxy.
298    /// For example, "/svc" to the respective proxy. Only the namespace entries
299    /// which were known at component startup will be available by the kernel.
300    pub container_namespace: ContainerNamespace,
301
302    /// The global lock for the mount tree.
303    ///
304    /// This lock protects against concurrent modifications to the mount topology. It uses
305    /// an `RwSeqLock` to allow readers (like path walking traversing mount points) to get a
306    /// consistent, lock-free snapshot of the RCU-protected mount table using `read_seq`.
307    /// Writers must acquire the lock before mutating filesystems, moving mounts, or
308    /// propagating peer groups. The returned `MountsWriteToken` is used as a proof token
309    /// throughout the `namespace` module to statically enforce exclusive write access.
310    pub mounts_lock: RwSeqLock<LockDepMutex<MountsWriteToken, MountsLevel>>,
311
312    /// The registry of block devices backed by a remote fuchsia.io file.
313    pub remote_block_device_registry: Arc<RemoteBlockDeviceRegistry>,
314
315    /// The iptables used for filtering network packets.
316    iptables: OnceLock<IpTables>,
317
318    /// The futexes shared across processes.
319    pub shared_futexes: Arc<FutexTable<SharedFutexKey>>,
320
321    /// The default UTS namespace for all tasks.
322    ///
323    /// Because each task can have its own UTS namespace, you probably want to use
324    /// the UTS namespace handle of the task, which may/may not point to this one.
325    pub root_uts_ns: UtsNamespaceHandle,
326
327    /// A struct containing a VMO with a vDSO implementation, if implemented for a given architecture, and possibly an offset for a sigreturn function.
328    pub vdso: Vdso,
329
330    /// A struct containing a VMO with a arch32-vDSO implementation, if implemented for a given architecture.
331    // TODO(https://fxbug.dev/380431743) This could be made less clunky -- maybe a Vec<Vdso> above or
332    // something else
333    pub vdso_arch32: Option<Vdso>,
334
335    /// The table of devices installed on the netstack and their associated
336    /// state local to this `Kernel`.
337    pub netstack_devices: Arc<NetstackDevices>,
338
339    /// Files that are currently available for swapping.
340    /// Note: Starnix never actually swaps memory to these files. We just need to track them
341    /// to pass conformance tests.
342    pub swap_files: LockDepMutex<Vec<FsNodeHandle>, KernelSwapFiles>,
343
344    /// The implementation of generic Netlink protocol families.
345    generic_netlink: OnceLock<GenericNetlink<NetlinkToClientSender<GenericMessage>>>,
346
347    /// The implementation of networking-related Netlink protocol families.
348    network_netlink: OnceLock<Netlink<NetlinkContextImpl>>,
349
350    /// Inspect instrumentation for this kernel instance.
351    pub inspect_node: fuchsia_inspect::Node,
352
353    /// The kinds of seccomp action that gets logged, stored as a bit vector.
354    /// Each potential SeccompAction gets a bit in the vector, as specified by
355    /// SeccompAction::logged_bit_offset.  If the bit is set, that means the
356    /// action should be logged when it is taken, subject to the caveats
357    /// described in seccomp(2).  The value of the bit vector is exposed to users
358    /// in a text form in the file /proc/sys/kernel/seccomp/actions_logged.
359    pub actions_logged: AtomicU16,
360
361    /// The manager for suspend/resume.
362    pub suspend_resume_manager: SuspendResumeManagerHandle,
363
364    /// Unique IDs for new mounts and mount namespaces.
365    pub next_mount_id: AtomicCounter<u64>,
366    pub next_peer_group_id: AtomicCounter<u64>,
367    pub next_namespace_id: AtomicCounter<u64>,
368
369    /// Unique IDs for file objects.
370    pub next_file_object_id: AtomicCounter<u64>,
371
372    /// Controls which processes a process is allowed to ptrace.  See Documentation/security/Yama.txt
373    pub ptrace_scope: AtomicU8,
374
375    // The Fuchsia build version returned by `fuchsia.buildinfo.Provider`.
376    pub build_version: OnceCell<String>,
377
378    pub stats: Arc<KernelStats>,
379
380    /// Resource limits that are exposed, for example, via sysctl.
381    pub system_limits: SystemLimits,
382
383    // The service to handle delayed releases. This is required for elements that requires to
384    // execute some code when released and requires a known context (both in term of lock context,
385    // as well as `CurrentTask`).
386    pub delayed_releaser: DelayedReleaser,
387
388    /// Manages task priorities.
389    pub scheduler: SchedulerManager,
390
391    /// The syslog manager.
392    pub syslog: Syslog,
393
394    /// All mounts.
395    pub mounts: Mounts,
396
397    /// The manager for creating and managing high-resolution timers.
398    pub hrtimer_manager: HrTimerManagerHandle,
399
400    /// The manager for monitoring and reporting resources used by the kernel.
401    pub memory_attribution_manager: MemoryAttributionManager,
402
403    /// Handler for crashing Linux processes.
404    pub crash_reporter: CrashReporter,
405
406    /// Whether this kernel is shutting down. When shutting down, new processes may not be spawned.
407    shutting_down: AtomicBool,
408
409    /// True to disable syslog access to unprivileged callers.  This also controls whether read
410    /// access to /dev/kmsg requires privileged capabilities.
411    pub restrict_dmesg: AtomicBool,
412
413    /// Determines whether unprivileged BPF is permitted, or can be re-enabled.
414    ///   0 - Unprivileged BPF is permitted.
415    ///   1 - Unprivileged BPF is not permitted, and cannot be enabled.
416    ///   2 - Unprivileged BPF is not permitted, but can be enabled by a privileged task.
417    pub disable_unprivileged_bpf: AtomicU8,
418
419    /// Controls use of perf_event_open(2) by tasks without CAP_PERFMON (or CAP_SYS_ADMIN):
420    ///   -1 - No capability required.
421    ///    0 - Capability required for tracepoint events.
422    ///    1 - Capability additionally required for CPU-wide events.
423    ///    2 - Capability additionally required for kernel profiling (default).
424    pub perf_event_paranoid: AtomicI32,
425
426    /// Control handle to the running container's ComponentController.
427    pub container_control_handle:
428        LockDepMutex<Option<ComponentControllerControlHandle>, ComponentControllerLock>,
429
430    /// eBPF state: loaded programs, eBPF maps, etc.
431    pub ebpf_state: EbpfState,
432
433    /// Cgroups of the kernel.
434    pub cgroups: KernelCgroups,
435
436    /// Used to communicate requests to adjust system time from within a Starnix
437    /// container. Used from syscalls.
438    pub time_adjustment_proxy: Option<AdjustSynchronousProxy>,
439
440    /// A token for the wake group we have registered with the netstack to
441    /// receive wakeup notifications on incoming data while suspended.
442    ///
443    /// A value of `None` means we were unable to communicate with the netstack
444    /// to create the wake group. In that case, we assume something has gone
445    /// wrong with the netstack (failed to start, crash, etc) and won't retry.
446    pub netstack_wake_group: OnceLock<Option<fnet_resources::WakeGroupToken>>,
447
448    /// Used to store tokens for sockets, particularly per-uid sharing domain sockets.
449    pub socket_tokens_store: SocketTokensStore,
450
451    /// Hardware capabilities to push onto stack when loading an ELF binary.
452    pub hwcaps: HwCaps,
453
454    /// Filters for syscall logging. Processes with names matching these filters will have syscalls
455    /// logged at INFO level.
456    pub syscall_log_filters: LockDepMutex<Vec<SyscallLogFilter>, SyscallLogFiltersLock>,
457}
458
459/// Hardware capabilities.
460#[derive(Debug, Clone, Copy, Default)]
461pub struct HwCap {
462    /// The value for `AT_HWCAP`.
463    pub hwcap: u32,
464    /// The value for `AT_HWCAP2`.
465    pub hwcap2: u32,
466}
467
468/// Hardware capabilities for both 32-bit and 64-bit ELF binaries.
469#[derive(Debug, Clone, Copy, Default)]
470pub struct HwCaps {
471    /// For 32-bit binaries.
472    #[cfg(target_arch = "aarch64")]
473    pub arch32: HwCap,
474    /// For 64-bit binaries.
475    pub arch64: HwCap,
476}
477
478/// An implementation of [`InterfacesHandler`].
479///
480/// This holds a `Weak<Kernel>` because it is held within a [`Netlink`] which
481/// is itself held within an `Arc<Kernel>`. Holding an `Arc<T>` within an
482/// `Arc<T>` prevents the `Arc`'s ref count from ever reaching 0, causing a
483/// leak.
484struct InterfacesHandlerImpl(Weak<Kernel>);
485
486impl InterfacesHandlerImpl {
487    fn kernel(&self) -> Option<Arc<Kernel>> {
488        self.0.upgrade()
489    }
490}
491
492impl InterfacesHandler for InterfacesHandlerImpl {
493    fn handle_new_link(&mut self, name: &str, interface_id: NonZeroU64) {
494        if let Some(kernel) = self.kernel() {
495            kernel.netstack_devices.add_device(&kernel, name.into(), interface_id);
496        }
497    }
498
499    fn handle_deleted_link(&mut self, name: &str) {
500        if let Some(kernel) = self.kernel() {
501            kernel.netstack_devices.remove_device(&kernel, name.into());
502        }
503    }
504
505    fn handle_idle_event(&mut self) {
506        let Some(kernel) = self.kernel() else {
507            log_error!("kernel went away while netlink is initializing");
508            return;
509        };
510        let (initialized, wq) = &kernel.netstack_devices.initialized_and_wq;
511        if initialized.swap(true, Ordering::SeqCst) {
512            log_error!("netlink initial devices should only be reported once");
513            return;
514        }
515        wq.notify_all()
516    }
517}
518
519impl Kernel {
520    pub fn new(
521        cmdline: BString,
522        features: KernelFeatures,
523        system_limits: SystemLimits,
524        container_namespace: ContainerNamespace,
525        scheduler: SchedulerManager,
526        crash_reporter_proxy: Option<CrashReporterProxy>,
527        inspect_node: fuchsia_inspect::Node,
528        security_state: security::KernelState,
529        time_adjustment_proxy: Option<AdjustSynchronousProxy>,
530        device_tree: Option<Devicetree>,
531    ) -> Result<Arc<Kernel>, zx::Status> {
532        let unix_address_maker =
533            Box::new(|x: FsString| -> SocketAddress { SocketAddress::Unix(x) });
534        let vsock_address_maker = Box::new(|x: u32| -> SocketAddress {
535            SocketAddress::Vsock { port: x, cid: VMADDR_CID_HOST }
536        });
537
538        let crash_reporter = CrashReporter::new(
539            &inspect_node,
540            crash_reporter_proxy,
541            zx::Duration::from_minutes(8),
542            features.crash_report_throttling,
543        );
544        let hrtimer_manager = HrTimerManager::new(&inspect_node);
545
546        let cpu_feature_flags =
547            zx::system_get_feature_flags::<CpuFeatureFlags>().unwrap_or_else(|e| {
548                log_debug!("CPU feature flags are only supported on ARM64: {}, reporting 0", e);
549                CpuFeatureFlags::empty()
550            });
551        let hwcaps = HwCaps::from_cpu_feature_flags(cpu_feature_flags);
552
553        let this = Arc::new_cyclic(|kernel| Kernel {
554            weak_self: kernel.clone(),
555            kthreads: KernelThreads::new(kernel.clone()),
556            features,
557            pids: Default::default(),
558            init_task: OnceLock::new(),
559            trace_event_manager: Arc::new(TracePerformanceEventManager::new(kernel.clone())),
560            expando: Default::default(),
561            default_abstract_socket_namespace: AbstractUnixSocketNamespace::new(unix_address_maker),
562            default_abstract_vsock_namespace: AbstractVsockSocketNamespace::new(
563                vsock_address_maker,
564            ),
565            cmdline,
566            device_tree,
567            security_state,
568            device_registry: Default::default(),
569            container_namespace,
570            mounts_lock: RwSeqLock::new(MountsWriteToken::new().into()),
571            remote_block_device_registry: Default::default(),
572            iptables: OnceLock::new(),
573            shared_futexes: Arc::<FutexTable<SharedFutexKey>>::default(),
574            root_uts_ns: Arc::new(UtsNamespace::default().into()),
575            vdso: Vdso::new(),
576            vdso_arch32: Vdso::new_arch32(),
577            netstack_devices: Arc::default(),
578            swap_files: Default::default(),
579            generic_netlink: OnceLock::new(),
580            network_netlink: OnceLock::new(),
581            inspect_node,
582            actions_logged: AtomicU16::new(0),
583            suspend_resume_manager: Default::default(),
584            next_mount_id: AtomicCounter::<u64>::new(1),
585            next_peer_group_id: AtomicCounter::<u64>::new(1),
586            next_namespace_id: AtomicCounter::<u64>::new(1),
587            next_file_object_id: Default::default(),
588            system_limits,
589            ptrace_scope: AtomicU8::new(0), // Disable YAMA checks by default.
590            restrict_dmesg: AtomicBool::new(false),
591            disable_unprivileged_bpf: AtomicU8::new(0), // Enable unprivileged BPF by default.
592            perf_event_paranoid: AtomicI32::new(2),
593            build_version: OnceCell::new(),
594            stats: Arc::new(KernelStats::default()),
595            delayed_releaser: Default::default(),
596            scheduler,
597            syslog: Default::default(),
598            mounts: Mounts::new(),
599            hrtimer_manager,
600            memory_attribution_manager: MemoryAttributionManager::new(kernel.clone()),
601            crash_reporter,
602            shutting_down: AtomicBool::new(false),
603            container_control_handle: Default::default(),
604            ebpf_state: Default::default(),
605            cgroups: Default::default(),
606            time_adjustment_proxy,
607            netstack_wake_group: OnceLock::new(),
608            socket_tokens_store: Default::default(),
609            hwcaps,
610            syscall_log_filters: Default::default(),
611        });
612
613        // Initialize the device registry before registering any devices.
614        //
615        // We will create sysfs recursively within this function.
616        this.device_registry.objects.init(&this);
617
618        // Make a copy of this Arc for the inspect lazy node to use but don't create an Arc cycle
619        // because the inspect node that owns this reference is owned by the kernel.
620        let kernel = Arc::downgrade(&this);
621        this.inspect_node.record_lazy_child("thread_groups", move || {
622            if let Some(kernel) = kernel.upgrade() {
623                let inspector = kernel.get_thread_groups_inspect();
624                async move { Ok(inspector) }.boxed()
625            } else {
626                async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
627            }
628        });
629
630        let kernel = Arc::downgrade(&this);
631        this.inspect_node.record_lazy_child("cgroupv2", move || {
632            if let Some(kernel) = kernel.upgrade() {
633                async move { Ok(kernel.cgroups.cgroup2.get_cgroup_inspect()) }.boxed()
634            } else {
635                async move { Err(anyhow::format_err!("kernel was dropped")) }.boxed()
636            }
637        });
638
639        Ok(this)
640    }
641
642    /// Returns the init task for this kernel.
643    pub fn get_init_task(&self) -> Result<Arc<Task>, Errno> {
644        self.init_task.get().and_then(|t| t.upgrade()).ok_or_else(|| errno!(EINVAL))
645    }
646
647    /// Shuts down userspace and the kernel in an orderly fashion, eventually terminating the root
648    /// kernel process.
649    pub fn shut_down(self: &Arc<Self>) {
650        // Run shutdown code on a kthread in the main process so that it can be the last process
651        // alive.
652        self.kthreads.spawn_future(
653            {
654                let kernel = self.clone();
655                move || async move {
656                    kernel.run_shutdown().await;
657                }
658            },
659            "run_shutdown",
660        );
661    }
662
663    /// Starts shutting down the Starnix kernel and any running container. Only one thread can drive
664    /// shutdown at a time. This function will return immediately if shut down is already under way.
665    ///
666    /// Shutdown happens in several phases:
667    ///
668    /// 1. Disable launching new processes
669    /// 2. Shut down individual ThreadGroups until only the init and system tasks remain
670    /// 3. Repeat the above for the init task
671    /// 4. Clean up kernel-internal structures that can hold processes alive
672    /// 5. Ensure this process is the only one running in the kernel job.
673    /// 6. Unmounts the kernel's mounts' FileSystems.
674    /// 7. Tell CF the container component has stopped
675    /// 8. Exit this process
676    ///
677    /// If a ThreadGroup does not shut down on its own (including after SIGKILL), that phase of
678    /// shutdown will hang. To gracefully shut down any further we need the other kernel processes
679    /// to do controlled exits that properly release access to shared state. If our orderly shutdown
680    /// does hang, eventually CF will kill the container component which will lead to the job of
681    /// this process being killed and shutdown will still complete.
682    async fn run_shutdown(&self) {
683        const INIT_PID: i32 = 1;
684        const SYSTEM_TASK_PID: i32 = 2;
685
686        // Step 1: Prevent new processes from being created once they observe this update. We don't
687        // want the thread driving shutdown to be racing with other threads creating new processes.
688        if self
689            .shutting_down
690            .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
691            .is_err()
692        {
693            log_info!("Additional thread tried to initiate shutdown while already in-progress.");
694            return;
695        }
696
697        log_info!("Shutting down Starnix kernel.");
698
699        // Step 2: Shut down thread groups in a loop until init and the system task are all that
700        // remain.
701        loop {
702            let tgs = self
703                .pids
704                .get_thread_groups(&RcuReadScope::new())
705                .filter(|tg| tg.leader.id != SYSTEM_TASK_PID && tg.leader.id != INIT_PID)
706                .collect::<Vec<_>>();
707            if tgs.is_empty() {
708                log_info!("pid table is empty except init and system task");
709                break;
710            }
711
712            log_info!(tgs:?; "shutting down thread groups");
713            let mut tasks = vec![];
714            for tg in tgs {
715                let task = fasync::Task::local(ThreadGroup::shut_down(Arc::downgrade(&tg)));
716                tasks.push(task);
717            }
718            futures::future::join_all(tasks).await;
719        }
720
721        // Step 3: Terminate the init process.
722        let maybe_init = self.get_init_task().ok().map(|t| Arc::downgrade(&t.thread_group));
723        if let Some(init) = maybe_init {
724            log_info!("shutting down init");
725            ThreadGroup::shut_down(init).await;
726        } else {
727            log_info!("init already terminated");
728        }
729
730        // Step 4: Clean up any structures that can keep non-Linux processes live in our job.
731        log_info!("cleaning up pinned memory");
732        self.expando.remove::<crate::mm::InfoCacheShadowProcess>();
733        self.expando.remove::<crate::mm::MlockShadowProcess>();
734
735        // Step 5: Make sure this is the only process running in the job. We already should have
736        // cleared up all processes other than the system task at this point, but wait on any that
737        // might be around for good measure.
738        //
739        // Use unwrap liberally since we're shutting down anyway and errors will still tear down the
740        // kernel.
741        let kernel_job = fuchsia_runtime::job_default();
742        assert_eq!(kernel_job.children().unwrap(), &[], "starnix does not create any child jobs");
743        let own_koid = fuchsia_runtime::process_self().koid().unwrap();
744
745        log_info!("waiting for this to be the only process in the job");
746        loop {
747            let mut remaining_processes = kernel_job
748                .processes()
749                .unwrap()
750                .into_iter()
751                // Don't wait for ourselves to exit.
752                .filter(|pid| pid != &own_koid)
753                .peekable();
754            if remaining_processes.peek().is_none() {
755                log_info!("No stray Zircon processes.");
756                break;
757            }
758
759            let mut terminated_signals = vec![];
760            for pid in remaining_processes {
761                let handle = match kernel_job
762                    .get_child(&pid, zx::Rights::BASIC | zx::Rights::PROPERTY | zx::Rights::DESTROY)
763                {
764                    Ok(h) => h,
765                    Err(e) => {
766                        log_info!(pid:?, e:?; "failed to get child process from job");
767                        continue;
768                    }
769                };
770                log_info!(
771                    pid:?,
772                    name:? = handle.get_name();
773                    "waiting on process terminated signal"
774                );
775                terminated_signals
776                    .push(fuchsia_async::OnSignals::new(handle, zx::Signals::PROCESS_TERMINATED));
777            }
778            log_info!("waiting on process terminated signals");
779            futures::future::join_all(terminated_signals).await;
780        }
781
782        // Step 6: Forcibly unmounts the mounts' FileSystems.
783        log_info!("clearing mounts");
784        self.mounts.clear();
785
786        // Step 7: Tell CF the container stopped.
787        log_info!("all non-root processes killed, notifying CF container is stopped");
788        if let Some(control_handle) = self.container_control_handle.lock().take() {
789            log_info!("Notifying CF that the container has stopped.");
790            control_handle
791                .send_on_stop(ComponentStopInfo {
792                    termination_status: Some(zx::sys::ZX_OK),
793                    exit_code: Some(0),
794                    ..ComponentStopInfo::default()
795                })
796                .unwrap();
797            control_handle.shutdown_with_epitaph(Ok(()));
798        } else {
799            log_warn!("Shutdown invoked without a container controller control handle.");
800        }
801
802        // Step 8: exiting this process.
803        log_info!("All tasks killed, exiting Starnix kernel root process.");
804        // Normally a Rust program exits its process by calling `std::process::exit()` which goes
805        // through libc to exit the program. This runs drop impls on any thread-local variables
806        // which can cause issues during Starnix shutdown when we haven't yet integrated every
807        // subsystem with the shutdown flow. While those issues are indicative of underlying
808        // problems, we can't solve them without finishing the implementation of graceful shutdown.
809        // Instead, ask Zircon to exit our process directly, bypassing any libc atexit handlers.
810        // TODO(https://fxbug.dev/295073633) return from main instead of avoiding atexit handlers
811        zx::Process::exit(0);
812    }
813
814    pub fn is_shutting_down(&self) -> bool {
815        self.shutting_down.load(Ordering::Acquire)
816    }
817
818    pub fn allow_unprivileged_bpf(&self) -> bool {
819        self.disable_unprivileged_bpf.load(Ordering::Relaxed) == 0
820    }
821
822    /// Opens a device file (driver) identified by `dev`.
823    pub fn open_device(
824        &self,
825        current_task: &CurrentTask,
826        node: &NamespaceNode,
827        flags: OpenFlags,
828        dev: DeviceId,
829        mode: DeviceMode,
830    ) -> Result<Box<dyn FileOps>, Errno> {
831        self.device_registry.open_device(current_task, node, flags, dev, mode)
832    }
833
834    /// Return a reference to the Audit Framework
835    ///
836    /// This function follows the lazy initialization pattern.
837    pub fn audit_logger(&self) -> Arc<AuditLogger> {
838        self.expando.get_or_init(|| AuditLogger::new(self))
839    }
840
841    /// Return a reference to the GenericNetlink implementation.
842    ///
843    /// This function follows the lazy initialization pattern, where the first
844    /// call will instantiate the Generic Netlink server in a separate kthread.
845    pub fn generic_netlink(&self) -> &GenericNetlink<NetlinkToClientSender<GenericMessage>> {
846        self.generic_netlink.get_or_init(|| {
847            let (generic_netlink, worker_params) = GenericNetlink::new();
848            let enable_nl80211 = self.features.wifi;
849            self.kthreads.spawn_future(
850                move || async move {
851                    crate::vfs::socket::run_generic_netlink_worker(worker_params, enable_nl80211)
852                        .await;
853                    log_error!("Generic Netlink future unexpectedly exited");
854                },
855                "generic_netlink_worker",
856            );
857            generic_netlink
858        })
859    }
860
861    /// Return a reference to the [`netlink::Netlink`] implementation.
862    ///
863    /// This function follows the lazy initialization pattern, where the first
864    /// call will instantiate the Netlink implementation.
865    pub fn network_netlink(self: &Arc<Self>) -> &Netlink<NetlinkContextImpl> {
866        self.network_netlink.get_or_init(|| {
867            let (network_netlink, worker_params) =
868                Netlink::new(InterfacesHandlerImpl(self.weak_self.clone()));
869
870            let kernel = self.clone();
871            self.kthreads.spawn_future(
872                move || async move {
873                    netlink::run_netlink_worker(
874                        worker_params,
875                        NetlinkAccessControl::new(kernel.kthreads.system_task()),
876                    )
877                    .await;
878                    log_error!(tag = NETLINK_LOG_TAG; "Netlink async worker unexpectedly exited");
879                },
880                "network_netlink_worker",
881            );
882            network_netlink
883        })
884    }
885
886    /// Return a reference to the token representing our wake group registered
887    /// with the netstack.
888    ///
889    /// This function lazily initializes the wake group with the netstack and
890    /// the wake group as a wake source with the Starnix runner.
891    pub(crate) fn netstack_wake_group(&self) -> Option<&fnet_resources::WakeGroupToken> {
892        self.netstack_wake_group
893            .get_or_init(|| {
894                // The signal the netstack raises when it wants the container to
895                // wake up.
896                const GROUP_WAKEUP_SIGNAL: zx::Signals =
897                    zx::Signals::from_bits(fnet_power::GROUP_WAKEUP_SIGNAL).unwrap();
898
899                let provider = fuchsia_component::client::connect_to_protocol_sync::<
900                    fnet_power::WakeGroupProviderMarker,
901                >()
902                .expect("connect to WakeGroupProvider");
903
904                let (wake_watcher_waiter, wake_watcher_signaller) = zx::EventPair::create();
905                create_watcher_for_wake_events(wake_watcher_signaller);
906
907                let token = match provider.create_wake_group(
908                    &fnet_power::WakeGroupOptions {
909                        debug_name: Some(String::from("starnix")),
910                        ..Default::default()
911                    },
912                    wake_watcher_waiter,
913                    zx::Instant::INFINITE,
914                ) {
915                    Ok(fnet_power::CreateWakeGroupResponse { token, .. }) => token,
916                    Err(e) => {
917                        log_error!("failed to create wake group with the netstack: {e}");
918                        return None;
919                    }
920                };
921
922                let token = token.expect("netstack provides a wake group token");
923                let wake_source = token
924                    .token
925                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
926                    .expect("duplicate handle to token");
927                self.suspend_resume_manager
928                    .add_external_wake_source(
929                        wake_source.into(),
930                        GROUP_WAKEUP_SIGNAL,
931                        "netstack-wake-group".into(),
932                    )
933                    .expect("add wake group as wake source");
934
935                Some(token)
936            })
937            .as_ref()
938    }
939
940    pub fn iptables(&self) -> &IpTables {
941        self.iptables.get_or_init(|| IpTables::new())
942    }
943
944    /// Returns a Proxy to the service used by the container at `filename`.
945    #[allow(unused)]
946    pub fn connect_to_named_protocol_at_container_svc<P: ProtocolMarker>(
947        &self,
948        filename: &str,
949    ) -> Result<ClientEnd<P>, Errno> {
950        match self.container_namespace.get_namespace_channel("/svc") {
951            Ok(channel) => {
952                let (client_end, server_end) = create_endpoints::<P>();
953                fdio::service_connect_at(channel.as_ref(), filename, server_end.into_channel())
954                    .map_err(|status| from_status_like_fdio!(status))?;
955                Ok(client_end)
956            }
957            Err(err) => {
958                log_error!("Unable to get /svc namespace channel! {}", err);
959                Err(errno!(ENOENT))
960            }
961        }
962    }
963
964    /// Returns a Proxy to the service `P` used by the container.
965    pub fn connect_to_protocol_at_container_svc<P: DiscoverableProtocolMarker>(
966        &self,
967    ) -> Result<ClientEnd<P>, Errno> {
968        self.connect_to_named_protocol_at_container_svc::<P>(P::PROTOCOL_NAME)
969    }
970
971    pub fn add_syscall_log_filter(&self, name: &str) {
972        let filter = SyscallLogFilter::new(name.to_string());
973        {
974            let mut filters = self.syscall_log_filters.lock();
975            if filters.contains(&filter) {
976                return;
977            }
978            filters.push(filter);
979        }
980        for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
981            headers.sync_syscall_log_level();
982        }
983    }
984
985    pub fn clear_syscall_log_filters(&self) {
986        {
987            let mut filters = self.syscall_log_filters.lock();
988            if filters.is_empty() {
989                return;
990            }
991            filters.clear();
992        }
993        for headers in self.pids.get_thread_groups(&RcuReadScope::new()) {
994            headers.sync_syscall_log_level();
995        }
996    }
997
998    fn get_thread_groups_inspect(&self) -> fuchsia_inspect::Inspector {
999        let inspector = fuchsia_inspect::Inspector::default();
1000
1001        let thread_groups = inspector.root();
1002        let mut mm_summary = MappingSummary::default();
1003        let mut mms_summarized = HashSet::new();
1004
1005        for thread_group in self.pids.get_thread_groups(&RcuReadScope::new()) {
1006            // Avoid holding the state lock while summarizing.
1007            let (ppid, tasks) = {
1008                let tg = thread_group.read();
1009                (tg.get_ppid() as i64, tg.tasks())
1010            };
1011
1012            let tg_node = thread_groups.create_child(format!("{}", thread_group.leader));
1013            if let Ok(koid) = thread_group.process.koid() {
1014                tg_node.record_int("koid", koid.raw_koid() as i64);
1015            }
1016            tg_node.record_int("pid", thread_group.leader.id as i64);
1017            tg_node.record_int("ppid", ppid);
1018            tg_node.record_bool("stopped", thread_group.load_stopped() == StopState::GroupStopped);
1019
1020            let tasks_node = tg_node.create_child("tasks");
1021            for task in tasks {
1022                if let Ok(mm) = task.mm() {
1023                    if mms_summarized.insert(Arc::as_ptr(&mm) as usize) {
1024                        mm.summarize(&mut mm_summary);
1025                    }
1026                }
1027                let set_properties = |node: &fuchsia_inspect::Node| {
1028                    node.record_string("command", task.command().to_string());
1029
1030                    let scheduler_state = task.read().scheduler_state.clone();
1031                    if !scheduler_state.is_default() {
1032                        node.record_child("sched", |node| {
1033                            node.record_string(
1034                                "role_name",
1035                                self.scheduler
1036                                    .role_name(&task)
1037                                    .map(|n| Cow::Borrowed(n))
1038                                    .unwrap_or_else(|e| Cow::Owned(e.to_string())),
1039                            );
1040                            node.record_string("state", format!("{scheduler_state:?}"));
1041                        });
1042                    }
1043                };
1044                if task.tid == thread_group.leader {
1045                    let mut argv = task.read_argv(256).unwrap_or_default();
1046
1047                    // Any runtime that overwrites argv is likely to leave a lot of trailing
1048                    // nulls, no need to print those in inspect.
1049                    argv.retain(|arg| !arg.is_empty());
1050
1051                    let inspect_argv = tg_node.create_string_array("argv", argv.len());
1052                    for (i, arg) in argv.iter().enumerate() {
1053                        inspect_argv.set(i, arg.to_string());
1054                    }
1055                    tg_node.record(inspect_argv);
1056
1057                    set_properties(&tg_node);
1058                } else {
1059                    tasks_node.record_child(task.tid.to_string(), |task_node| {
1060                        set_properties(task_node);
1061                    });
1062                };
1063            }
1064            tg_node.record(tasks_node);
1065            thread_groups.record(tg_node);
1066        }
1067
1068        thread_groups.record_child("memory_managers", |node| mm_summary.record(node));
1069
1070        inspector
1071    }
1072
1073    pub fn new_memory_attribution_observer(
1074        &self,
1075        control_handle: fattribution::ProviderControlHandle,
1076    ) -> attribution_server::Observer {
1077        self.memory_attribution_manager.new_observer(control_handle)
1078    }
1079
1080    /// Opens and returns a directory proxy from the container's namespace, at
1081    /// the requested path, using the provided flags. This method will open the
1082    /// closest existing path from the namespace hierarchy, and then attempt
1083    /// initialize an open on the remaining subdirectory path, using the given open_flags.
1084    ///
1085    /// For example, given the parameter provided is `/path/to/foo/bar` and there
1086    /// are namespace entries already for `/path/to/foo` and `/path/to`. The entry
1087    /// for /path/to/foo will be opened, and then the /bar will attempt to be opened
1088    /// underneath that directory with the given open_flags. The returned value
1089    /// will be the proxy to the parent (/path/to/foo) and the string to the child
1090    /// path (/bar). The caller of this method can expect /bar to be initialized.
1091    pub fn open_ns_dir(
1092        &self,
1093        path: &str,
1094        open_flags: fio::Flags,
1095    ) -> Result<(fio::DirectorySynchronousProxy, String), Errno> {
1096        let ns_path = PathBuf::from(path);
1097        match self.container_namespace.find_closest_channel(&ns_path) {
1098            Ok((root_channel, remaining_subdir)) => {
1099                let (_, server_end) = create_endpoints::<fio::DirectoryMarker>();
1100                fdio::open_at(
1101                    &root_channel,
1102                    &remaining_subdir,
1103                    open_flags,
1104                    server_end.into_channel(),
1105                )
1106                .map_err(|e| {
1107                    log_error!("Failed to intialize the subdirs: {}", e);
1108                    errno!(EIO)
1109                })?;
1110
1111                Ok((fio::DirectorySynchronousProxy::new(root_channel), remaining_subdir))
1112            }
1113            Err(err) => {
1114                log_error!(
1115                    "Unable to find a channel for {}. Received error: {}",
1116                    ns_path.display(),
1117                    err
1118                );
1119                Err(errno!(ENOENT))
1120            }
1121        }
1122    }
1123
1124    /// Returns an iterator of the command line arguments.
1125    pub fn cmdline_args_iter(&self) -> impl Iterator<Item = ArgNameAndValue<'_>> {
1126        parse_cmdline(self.cmdline.to_str().unwrap_or_default()).filter_map(|arg| {
1127            arg.split_once('=')
1128                .map(|(name, value)| ArgNameAndValue { name: name, value: Some(value) })
1129                .or(Some(ArgNameAndValue { name: arg, value: None }))
1130        })
1131    }
1132
1133    /// Returns the container-configured CacheConfig.
1134    pub fn fs_cache_config(&self) -> CacheConfig {
1135        CacheConfig { capacity: self.features.dirent_cache_size as usize }
1136    }
1137
1138    pub fn mounts_lock(&self) -> MountsWriteGuard<'_> {
1139        MountsWriteGuard::new(self.mounts_lock.lock())
1140    }
1141}
1142
1143pub fn parse_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
1144    let mut args = Vec::new();
1145    let mut arg_start: Option<usize> = None;
1146    let mut in_quotes = false;
1147    let mut previous_char = ' ';
1148
1149    for (i, c) in cmdline.char_indices() {
1150        if let Some(start) = arg_start {
1151            match c {
1152                ' ' if !in_quotes => {
1153                    args.push(&cmdline[start..i]);
1154                    arg_start = None;
1155                }
1156                '"' if previous_char != '\\' => {
1157                    in_quotes = !in_quotes;
1158                }
1159                _ => {}
1160            }
1161        } else if c != ' ' {
1162            arg_start = Some(i);
1163            if c == '"' {
1164                in_quotes = true;
1165            }
1166        }
1167        previous_char = c;
1168    }
1169    if let Some(start) = arg_start {
1170        args.push(&cmdline[start..]);
1171    }
1172    args.into_iter()
1173}
1174
1175impl std::fmt::Debug for Kernel {
1176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1177        f.debug_struct("Kernel").finish()
1178    }
1179}
1180
1181// TODO(https://fxbug.dev/380427153): move arch dependent code to `kernel/core/arch/*`.
1182#[cfg(target_arch = "aarch64")]
1183fn arm32_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1184    use starnix_uapi::arch32;
1185    const COMPAT_ARM32_ELF_HWCAP: u32 = arch32::HWCAP_HALF
1186        | arch32::HWCAP_THUMB
1187        | arch32::HWCAP_FAST_MULT
1188        | arch32::HWCAP_EDSP
1189        | arch32::HWCAP_TLS
1190        | arch32::HWCAP_IDIV // == IDIVA | IDIVT.
1191        | arch32::HWCAP_LPAE
1192        | arch32::HWCAP_EVTSTRM;
1193
1194    let mut hwcap = COMPAT_ARM32_ELF_HWCAP;
1195    let mut hwcap2 = 0;
1196    for feature in cpu_feature_flags.iter() {
1197        match feature {
1198            CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= arch32::HWCAP_NEON,
1199            CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap2 |= arch32::HWCAP2_AES,
1200            CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap2 |= arch32::HWCAP2_PMULL,
1201            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap2 |= arch32::HWCAP2_SHA1,
1202            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap2 |= arch32::HWCAP2_SHA2,
1203            CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap2 |= arch32::HWCAP2_CRC32,
1204            CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap |= arch32::HWCAP_I8MM,
1205            CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= arch32::HWCAP_ASIMDFHM,
1206            CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= arch32::HWCAP_ASIMDDP,
1207            CpuFeatureFlags::ARM64_FEATURE_ISA_FP => {
1208                hwcap |= arch32::HWCAP_VFP | arch32::HWCAP_VFPv3 | arch32::HWCAP_VFPv4
1209            }
1210            _ => {}
1211        }
1212    }
1213    HwCap { hwcap, hwcap2 }
1214}
1215
1216#[cfg(target_arch = "aarch64")]
1217fn arm64_hwcap(cpu_feature_flags: CpuFeatureFlags) -> HwCap {
1218    // See https://docs.kernel.org/arch/arm64/elf_hwcaps.html for details.
1219    use starnix_uapi;
1220    let mut hwcap = 0;
1221    let mut hwcap2 = 0;
1222
1223    for feature in cpu_feature_flags.iter() {
1224        match feature {
1225            CpuFeatureFlags::ARM64_FEATURE_ISA_FP => hwcap |= starnix_uapi::HWCAP_FP,
1226            CpuFeatureFlags::ARM64_FEATURE_ISA_ASIMD => hwcap |= starnix_uapi::HWCAP_ASIMD,
1227            CpuFeatureFlags::ARM64_FEATURE_ISA_AES => hwcap |= starnix_uapi::HWCAP_AES,
1228            CpuFeatureFlags::ARM64_FEATURE_ISA_PMULL => hwcap |= starnix_uapi::HWCAP_PMULL,
1229            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA1 => hwcap |= starnix_uapi::HWCAP_SHA1,
1230            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA256 => hwcap |= starnix_uapi::HWCAP_SHA2,
1231            CpuFeatureFlags::ARM64_FEATURE_ISA_CRC32 => hwcap |= starnix_uapi::HWCAP_CRC32,
1232            CpuFeatureFlags::ARM64_FEATURE_ISA_I8MM => hwcap2 |= starnix_uapi::HWCAP2_I8MM,
1233            CpuFeatureFlags::ARM64_FEATURE_ISA_FHM => hwcap |= starnix_uapi::HWCAP_ASIMDFHM,
1234            CpuFeatureFlags::ARM64_FEATURE_ISA_DP => hwcap |= starnix_uapi::HWCAP_ASIMDDP,
1235            CpuFeatureFlags::ARM64_FEATURE_ISA_SM3 => hwcap |= starnix_uapi::HWCAP_SM3,
1236            CpuFeatureFlags::ARM64_FEATURE_ISA_SM4 => hwcap |= starnix_uapi::HWCAP_SM4,
1237            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA3 => hwcap |= starnix_uapi::HWCAP_SHA3,
1238            CpuFeatureFlags::ARM64_FEATURE_ISA_SHA512 => hwcap |= starnix_uapi::HWCAP_SHA512,
1239            CpuFeatureFlags::ARM64_FEATURE_ISA_ATOMICS => hwcap |= starnix_uapi::HWCAP_ATOMICS,
1240            CpuFeatureFlags::ARM64_FEATURE_ISA_RDM => hwcap |= starnix_uapi::HWCAP_ASIMDRDM,
1241            CpuFeatureFlags::ARM64_FEATURE_ISA_TS => hwcap |= starnix_uapi::HWCAP_FLAGM,
1242            CpuFeatureFlags::ARM64_FEATURE_ISA_DPB => hwcap |= starnix_uapi::HWCAP_DCPOP,
1243            CpuFeatureFlags::ARM64_FEATURE_ISA_RNDR => hwcap2 |= starnix_uapi::HWCAP2_RNG,
1244            _ => {}
1245        }
1246    }
1247    HwCap { hwcap, hwcap2 }
1248}
1249
1250impl HwCaps {
1251    #[cfg(target_arch = "aarch64")]
1252    pub fn from_cpu_feature_flags(cpu_feature_flags: CpuFeatureFlags) -> Self {
1253        Self { arch32: arm32_hwcap(cpu_feature_flags), arch64: arm64_hwcap(cpu_feature_flags) }
1254    }
1255
1256    #[cfg(not(target_arch = "aarch64"))]
1257    pub fn from_cpu_feature_flags(_cpu_feature_flags: CpuFeatureFlags) -> Self {
1258        Self { arch64: HwCap::default() }
1259    }
1260}
1261
1262#[cfg(test)]
1263mod test {
1264    use super::parse_cmdline;
1265
1266    #[test]
1267    fn test_parse_cmdline() {
1268        let cmdline =
1269            r#"first second=third "fourth fifth" sixth="seventh eighth" "ninth\" tenth" eleventh"#;
1270        let expected = vec![
1271            "first",
1272            "second=third",
1273            "\"fourth fifth\"",
1274            "sixth=\"seventh eighth\"",
1275            "\"ninth\\\" tenth\"",
1276            "eleventh",
1277        ];
1278        assert_eq!(parse_cmdline(cmdline).collect::<Vec<_>>(), expected);
1279    }
1280}