Skip to main content

starnix_modules/
lib.rs

1// Copyright 2023 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
5// Increase recursion limit because LTO causes overflow.
6#![recursion_limit = "256"]
7
8use anyhow::{Context, Error, anyhow, bail};
9use bstr::BString;
10use fidl_fuchsia_ui_composition as fuicomposition;
11use fidl_fuchsia_ui_input as fuiinput;
12use fidl_fuchsia_ui_input3 as fuiinput3;
13use fidl_fuchsia_ui_policy as fuipolicy;
14use fidl_fuchsia_ui_views as fuiviews;
15use starnix_consent_sync::init as consent_sync_init;
16use starnix_core::bpf::fs::BpfFs;
17use starnix_core::device::kobject::DeviceMetadata;
18use starnix_core::device::mem::{DevRandom, mem_device_init};
19use starnix_core::device::{DeviceMode, simple_device_ops};
20use starnix_core::fs::devtmpfs::dev_tmp_fs;
21use starnix_core::fs::fuchsia::{RemoteBundle, new_remote_fs, new_remote_vol};
22use starnix_core::fs::sysfs::sys_fs;
23use starnix_core::fs::tmpfs::tmp_fs;
24use starnix_core::task::{Kernel, ThreadLockupDetector};
25use starnix_core::vfs::fs_registry::FsRegistry;
26use starnix_core::vfs::pipe::register_pipe_fs;
27use starnix_logging::log_error;
28use starnix_modules_android_usb::usb_device_init;
29use starnix_modules_ashmem::ashmem_device_init;
30use starnix_modules_battery_manager::battery_manager_device_init;
31use starnix_modules_binderfs::BinderFs;
32use starnix_modules_cgroupfs::{CgroupV1Fs, cgroup2_fs};
33use starnix_modules_debugfs::debug_fs;
34use starnix_modules_device_mapper::{create_device_mapper, device_mapper_init};
35pub use starnix_modules_devpts::create_main_and_replica;
36use starnix_modules_devpts::{dev_pts_fs, tty_device_init};
37use starnix_modules_ext4::ExtFilesystem;
38use starnix_modules_fastrpc::fastrpc_device_init;
39pub use starnix_modules_framebuffer::Framebuffer;
40use starnix_modules_functionfs::FunctionFs;
41use starnix_modules_fuse::{new_fuse_fs, new_fusectl_fs, open_fuse_device};
42use starnix_modules_goog_chip_info::goog_chip_info_device_init;
43use starnix_modules_gpu::gpu_device_init;
44use starnix_modules_gralloc::gralloc_device_init;
45use starnix_modules_hvdcp_opti::hvdcp_opti_init;
46use starnix_modules_inotify::inotify::inotify_init;
47use starnix_modules_input::uinput::register_uinput_device;
48use starnix_modules_input::{EventProxyMode, StartRelaysArgs, new_input_relay};
49use starnix_modules_kgsl::kgsl_device_init;
50pub use starnix_modules_layeredfs::{LayeredFsBuilder, LayeredFsMounts};
51use starnix_modules_loop::{create_loop_control_device, loop_device_init};
52use starnix_modules_mmcblk_stub::mmcblk_stub_device_init;
53use starnix_modules_nanohub::nanohub_device_init;
54use starnix_modules_nmfs::nmfs_init;
55use starnix_modules_odpm::odpm_device_init;
56pub use starnix_modules_overlayfs::OverlayStack;
57use starnix_modules_overlayfs::new_overlay_fs;
58use starnix_modules_power_residency::init_power_residency_device;
59use starnix_modules_procfs::proc_fs;
60use starnix_modules_pstore::pstore_fs;
61use starnix_modules_rtc::rtc_device_init;
62use starnix_modules_selinuxfs::selinux_fs;
63use starnix_modules_touch_power_policy::TouchPowerPolicyDevice;
64use starnix_modules_tracefs::trace_fs;
65use starnix_modules_tun::DevTun;
66use starnix_modules_usbfs::usbfs_init;
67use starnix_modules_wakeup_test::register_wakeup_test_device;
68use starnix_modules_zram::zram_device_init;
69use starnix_uapi::device_id::DeviceId;
70use starnix_uapi::errors::Errno;
71use starnix_uapi::fs_type::FileSystemTypeFlags;
72use std::collections::BTreeMap;
73use std::sync::Arc;
74
75/// Modules that are enabled by default for all containers unless explicitly disabled via
76/// `"-<module>"` in `program.modules`.
77const DEFAULT_MODULES: &[&str] = &[
78    "battery_manager",
79    "device_mapper",
80    "goog_chip_info",
81    "hw_random",
82    "loop",
83    "power_residency",
84    "rtc",
85    "tun",
86    "zram",
87];
88
89/// Validates that `name` is a known container module and that `args` (if `Some`) are valid for
90/// that module.
91pub fn validate_module(name: &str, args: Option<&[&str]>) -> Result<(), Error> {
92    let validator: Option<fn(&[&str]) -> Result<(), Error>> = match name {
93        "boot_notifier" => Some(starnix_modules_boot::validate_args),
94        "framebuffer" => Some(starnix_modules_framebuffer::validate_args),
95        "magma" => Some(starnix_modules_magma::validate_args),
96        "perfetto" => Some(starnix_modules_perfetto_consumer::validate_args),
97        "thermal" => Some(starnix_modules_thermal::validate_args),
98        "android_usb"
99        | "ashmem"
100        | "battery_manager"
101        | "data_collection_consent_sync"
102        | "device_mapper"
103        | "fastrpc"
104        | "gfxstream"
105        | "goog_chip_info"
106        | "google_odpm"
107        | "gralloc"
108        | "hvdcp_opti"
109        | "hw_random"
110        | "kgsl"
111        | "loop"
112        | "mmcblk_stub"
113        | "nanohub"
114        | "network_manager"
115        | "power_residency"
116        | "rtc"
117        | "tun"
118        | "usbfs"
119        | "wakeup_test"
120        | "zram" => None,
121        _ => bail!("unsupported module: {name}"),
122    };
123    if let Some(args) = args {
124        match validator {
125            Some(validate) => validate(args)?,
126            None if !args.is_empty() => {
127                bail!("module '{name}' does not accept arguments: {args:?}");
128            }
129            None => {}
130        }
131    }
132    Ok(())
133}
134
135impl Modules {
136    fn init_module(&self, name: &str, kernel: &Arc<Kernel>, args: &[&str]) -> Result<(), Error> {
137        validate_module(name, Some(args))?;
138        let registry = &kernel.device_registry;
139        match name {
140            "android_usb" => {
141                usb_device_init(kernel).context("Failed to add android usb device nodes")?;
142            }
143            "ashmem" => ashmem_device_init(kernel),
144            "battery_manager" => battery_manager_device_init(kernel),
145            "boot_notifier" => starnix_modules_boot::module_init(kernel, args)?,
146            "data_collection_consent_sync" => consent_sync_init(kernel),
147            "device_mapper" => {
148                registry.add_device(
149                    kernel,
150                    "device-mapper".into(),
151                    DeviceMetadata::new(
152                        "mapper/control".into(),
153                        DeviceId::DEVICE_MAPPER,
154                        DeviceMode::Char,
155                    ),
156                    registry.objects.misc_class(),
157                    create_device_mapper,
158                );
159                device_mapper_init(kernel)?;
160            }
161            "fastrpc" => fastrpc_device_init(kernel),
162            "framebuffer" => init_framebuffer_module(
163                kernel,
164                args,
165                self.enable_visual_debugging,
166                &self.initial_view_id_annotation,
167            )?,
168            "gfxstream" => gpu_device_init(kernel),
169            "goog_chip_info" => goog_chip_info_device_init(kernel),
170            // Note: `google_odpm` and `hvdcp_opti` both statically register `iio:device0`;
171            // `Modules::validate` rejects enabling both simultaneously.
172            "google_odpm" => odpm_device_init(kernel),
173            // The virtgralloc0 device allows vulkan_selector to indicate to gralloc whether
174            // swiftshader or magma will be used. This is separate from the magma module because
175            // the policy choice whether to use magma or swiftshader is in vulkan_selector, and it
176            // can potentially choose swiftshader for testing purposes even when magma0 is present.
177            // Also, it's nice to indicate swiftshader the same way regardless of whether the magma
178            // module is enabled or disabled.
179            "gralloc" => gralloc_device_init(kernel),
180            "hvdcp_opti" => hvdcp_opti_init(kernel),
181            "hw_random" => {
182                registry.add_device(
183                    kernel,
184                    "hw_random".into(),
185                    DeviceMetadata::new("hwrng".into(), DeviceId::HW_RANDOM, DeviceMode::Char),
186                    registry.objects.misc_class(),
187                    simple_device_ops::<DevRandom>,
188                );
189            }
190            "kgsl" => kgsl_device_init(kernel),
191            "loop" => {
192                registry.add_device(
193                    kernel,
194                    "loop-control".into(),
195                    DeviceMetadata::new(
196                        "loop-control".into(),
197                        DeviceId::LOOP_CONTROL,
198                        DeviceMode::Char,
199                    ),
200                    registry.objects.misc_class(),
201                    create_loop_control_device,
202                );
203                loop_device_init(kernel)?;
204            }
205            "magma" => starnix_modules_magma::module_init(kernel, args)?,
206            "mmcblk_stub" => {
207                mmcblk_stub_device_init(kernel).context("Failed to add stub mmcblk0 device")?;
208            }
209            "nanohub" => nanohub_device_init(kernel),
210            "network_manager" => {
211                if let Err(e) = nmfs_init(kernel) {
212                    log_error!("Network manager initialization failed: ({e:?})");
213                }
214            }
215            "perfetto" => starnix_modules_perfetto_consumer::module_init(kernel, args)?,
216            "power_residency" => init_power_residency_device(kernel),
217            "rtc" => rtc_device_init(kernel)?,
218            "thermal" => starnix_modules_thermal::module_init(kernel, args)?,
219            "tun" => {
220                registry.add_device(
221                    kernel,
222                    "tun".into(),
223                    DeviceMetadata::new("tun".into(), DeviceId::TUN, DeviceMode::Char),
224                    registry.objects.misc_class(),
225                    simple_device_ops::<DevTun>,
226                );
227            }
228            "usbfs" => {
229                usbfs_init(kernel).context("Failed to initialize usbfs")?;
230            }
231            "wakeup_test" => register_wakeup_test_device(kernel)?,
232            "zram" => zram_device_init(kernel)?,
233            _ => bail!("unsupported module: {name}"),
234        }
235        Ok(())
236    }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq)]
240enum ModuleState {
241    DefaultEnabled,
242    Enabled(Vec<String>),
243    Disabled,
244}
245
246/// Configuration and initialization registry for container kernel modules.
247#[derive(Debug, Clone, PartialEq, Eq)]
248pub struct Modules {
249    entries: BTreeMap<String, ModuleState>,
250    enable_visual_debugging: bool,
251    initial_view_id_annotation: String,
252}
253
254impl Default for Modules {
255    fn default() -> Self {
256        let entries = DEFAULT_MODULES
257            .iter()
258            .map(|&module| (module.to_string(), ModuleState::DefaultEnabled))
259            .collect();
260        Self { entries, enable_visual_debugging: false, initial_view_id_annotation: String::new() }
261    }
262}
263
264impl Modules {
265    /// Sets container-level visual debugging and view annotation options used when the
266    /// `framebuffer` module is initialized.
267    pub fn set_framebuffer_config(
268        &mut self,
269        enable_visual_debugging: bool,
270        initial_view_id_annotation: String,
271    ) {
272        self.enable_visual_debugging = enable_visual_debugging;
273        self.initial_view_id_annotation = initial_view_id_annotation;
274    }
275
276    /// Parses and registers a single module specification string (`"<module> [key=value...]"` or
277    /// `"-<module>"`).
278    pub fn add_spec(&mut self, spec: &str) -> Result<(), Error> {
279        let mut tokens = spec.split_whitespace();
280        let name = tokens.next().ok_or_else(|| anyhow!("empty module specification"))?;
281        if let Some(disabled_module) = name.strip_prefix('-') {
282            if disabled_module.is_empty() || disabled_module.starts_with('-') {
283                bail!("invalid module specification: {spec}");
284            }
285            if tokens.next().is_some() {
286                bail!("cannot specify arguments when disabling module '{disabled_module}'");
287            }
288            self.disable(disabled_module);
289            return Ok(());
290        }
291        let args = tokens.map(String::from).collect();
292        self.add_with_args(name, args);
293        Ok(())
294    }
295
296    /// Disables `module`, preventing it from being initialized even if enabled by default or in an
297    /// included manifest shard.
298    fn disable(&mut self, module: &str) {
299        self.entries.insert(module.to_string(), ModuleState::Disabled);
300    }
301
302    /// Enables `module` with no arguments, preserving any arguments or explicit disablement already
303    /// configured for `module`.
304    pub fn enable(&mut self, module: &str) {
305        self.add_with_args(module, Vec::new());
306    }
307
308    /// Enables `module` with `args` unless `module` has been explicitly disabled.
309    ///
310    /// If `args` is empty and `module` is already enabled, any existing arguments configured for
311    /// `module` are preserved so that bare `<module>` entries (for example from an included
312    /// manifest shard) do not overwrite explicit module arguments.
313    pub fn add_with_args(&mut self, module: &str, args: Vec<String>) {
314        match self.entries.get_mut(module) {
315            Some(ModuleState::Disabled) => {}
316            Some(ModuleState::Enabled(existing_args)) => {
317                if !args.is_empty() {
318                    *existing_args = args;
319                }
320            }
321            Some(ModuleState::DefaultEnabled) | None => {
322                self.entries.insert(module.to_string(), ModuleState::Enabled(args));
323            }
324        }
325    }
326
327    /// Returns whether `module` is enabled.
328    pub fn is_enabled(&self, module: &str) -> bool {
329        self.args(module).is_some()
330    }
331
332    /// Returns the arguments configured for `module`, if enabled.
333    pub fn args(&self, module: &str) -> Option<&[String]> {
334        match self.entries.get(module)? {
335            ModuleState::DefaultEnabled => {
336                // `hvdcp_opti` registers its own `power_supply/battery` device, superseding the
337                // default `battery_manager` module unless `battery_manager` was explicitly enabled.
338                if module == "battery_manager"
339                    && matches!(self.entries.get("hvdcp_opti"), Some(ModuleState::Enabled(_)))
340                {
341                    None
342                } else {
343                    Some(&[])
344                }
345            }
346            ModuleState::Enabled(args) => Some(args.as_slice()),
347            ModuleState::Disabled => None,
348        }
349    }
350
351    /// Validates all configured module names, module arguments, and cross-module constraints.
352    pub fn validate(&self) -> Result<(), Error> {
353        for module in self.entries.keys() {
354            let arg_refs: Option<Vec<&str>> =
355                self.args(module).map(|a| a.iter().map(String::as_str).collect());
356            validate_module(module, arg_refs.as_deref())?;
357        }
358        if self.is_enabled("battery_manager") && self.is_enabled("hvdcp_opti") {
359            bail!("modules 'battery_manager' and 'hvdcp_opti' cannot be enabled simultaneously");
360        }
361        if self.is_enabled("google_odpm") && self.is_enabled("hvdcp_opti") {
362            bail!("modules 'google_odpm' and 'hvdcp_opti' cannot be enabled simultaneously");
363        }
364        Ok(())
365    }
366
367    /// Returns any extra kernel command-line parameters contributed by enabled modules.
368    pub fn extra_cmdline_params(&self) -> Result<Option<BString>, Error> {
369        if let Some(args) = self.args("magma") {
370            let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect();
371            return Ok(Some(starnix_modules_magma::cmdline_params(&arg_refs)?));
372        }
373        Ok(None)
374    }
375
376    /// Records the enabled modules and their arguments under a `"modules"` child Inspect node.
377    pub fn record_inspect(&self, parent_node: &fuchsia_inspect::Node) {
378        parent_node.record_child("modules", |inspect_node| {
379            for module in self.entries.keys() {
380                if let Some(args) = self.args(module) {
381                    inspect_node.record_string(module, args.join(" "));
382                }
383            }
384        });
385    }
386
387    /// Initializes all enabled modules in `kernel`.
388    pub fn init_modules(&self, kernel: &Arc<Kernel>) -> Result<(), Error> {
389        for module in self.entries.keys() {
390            if let Some(args) = self.args(module) {
391                let arg_refs: Vec<&str> = args.iter().map(String::as_str).collect();
392                self.init_module(module, kernel, &arg_refs)?;
393            }
394        }
395        Ok(())
396    }
397}
398
399fn init_framebuffer_module(
400    kernel: &Kernel,
401    args: &[&str],
402    enable_visual_debugging: bool,
403    initial_view_id_annotation: &str,
404) -> Result<(), Error> {
405    let framebuffer = Framebuffer::device_init(
406        kernel,
407        args,
408        enable_visual_debugging,
409        initial_view_id_annotation.to_string(),
410    )
411    .context("initializing framebuffer")?;
412
413    let (touch_source_client, touch_source_server) = fidl::endpoints::create_endpoints();
414    let (mouse_source_client, mouse_source_server) = fidl::endpoints::create_endpoints();
415    let view_bound_protocols = fuicomposition::ViewBoundProtocols {
416        touch_source_v2: Some(touch_source_server),
417        mouse_source_v2: Some(mouse_source_server),
418        ..Default::default()
419    };
420    let view_identity = fuiviews::ViewIdentityOnCreation::from(
421        fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair"),
422    );
423    let view_ref = fuchsia_scenic::duplicate_view_ref(&view_identity.view_ref)
424        .expect("Failed to dup view ref.");
425    let keyboard =
426        fuchsia_component::client::connect_to_protocol_sync::<fuiinput3::KeyboardMarker>()
427            .expect("Failed to connect to keyboard");
428    let registry_proxy = fuchsia_component::client::connect_to_protocol_sync::<
429        fuipolicy::DeviceListenerRegistryMarker,
430    >()
431    .context("Failed to connect to policy device listener registry")?;
432    let device_listener_registry = fuchsia_component::client::connect_to_protocol_sync::<
433        fuiinput::DeviceListenerRegistryMarker,
434    >()
435    .context("Failed to connect to device listener registry")?;
436
437    // `view_identity` and `view_bound_protocols` must be populated before
438    // `Framebuffer::start_server` is called.
439    *framebuffer.view_identity.lock() = Some(view_identity);
440    *framebuffer.view_bound_protocols.lock() = Some(view_bound_protocols);
441
442    let (display_width, display_height) = {
443        let framebuffer_info = framebuffer.info.read();
444        (framebuffer_info.xres as i32, framebuffer_info.yres as i32)
445    };
446
447    let (input_events_relay, input_events_relay_handle) = new_input_relay();
448    input_events_relay.start_relays(
449        kernel,
450        StartRelaysArgs {
451            event_proxy_mode: EventProxyMode::WakeContainer,
452            touch_source_client_end: touch_source_client,
453            keyboard_proxy: keyboard,
454            mouse_source_client_end: mouse_source_client,
455            view_ref,
456            registry_proxy,
457            device_listener_registry: Some(device_listener_registry),
458            display_width,
459            display_height,
460        },
461    );
462
463    register_uinput_device(kernel, input_events_relay_handle)?;
464
465    // Channel we use to inform the relay of changes to `touch_standby`
466    let (touch_standby_sender, touch_standby_receiver) =
467        ThreadLockupDetector::tracked_channel::<bool>();
468    let touch_policy_device = TouchPowerPolicyDevice::new(touch_standby_sender);
469    touch_policy_device.clone().register(kernel);
470    touch_policy_device.start_relay(kernel, touch_standby_receiver);
471
472    framebuffer.start_server(kernel, /* incoming_dir = */ None);
473    Ok(())
474}
475
476/// Initializes common devices in `Kernel`.
477///
478/// When `KernelFeatures::devtmpfs` is enabled, adding device nodes to `devtmpfs` dispatches work to
479/// `kernel.kthreads`, so this function must be called after `kernel.kthreads` has been initialized.
480pub fn init_common_devices(kernel: &Kernel) -> Result<(), Errno> {
481    let registry = &kernel.device_registry;
482    registry.add_device(
483        kernel,
484        "fuse".into(),
485        DeviceMetadata::new("fuse".into(), DeviceId::FUSE, DeviceMode::Char),
486        registry.objects.misc_class(),
487        open_fuse_device,
488    );
489    mem_device_init(kernel)?;
490    tty_device_init(kernel)?;
491    Ok(())
492}
493
494pub fn register_common_file_systems(kernel: &Kernel) {
495    let registry = kernel.expando.get::<FsRegistry>();
496    registry.register(b"binder".into(), FileSystemTypeFlags::empty(), BinderFs::new_fs);
497    registry.register(b"bpf".into(), FileSystemTypeFlags::empty(), BpfFs::new_fs);
498    registry.register(b"cgroup".into(), FileSystemTypeFlags::empty(), CgroupV1Fs::new_fs);
499    registry.register(b"cgroup2".into(), FileSystemTypeFlags::empty(), cgroup2_fs);
500    // Cpusets use the generic cgroup (v1) subsystem.
501    // From https://docs.kernel.org/admin-guide/cgroup-v1/cpusets.html
502    registry.register(b"cpuset".into(), FileSystemTypeFlags::empty(), CgroupV1Fs::new_fs_cpuset);
503    registry.register(b"debugfs".into(), FileSystemTypeFlags::empty(), debug_fs);
504    registry.register(b"devpts".into(), FileSystemTypeFlags::empty(), dev_pts_fs);
505    if kernel.features.devtmpfs {
506        registry.register(b"devtmpfs".into(), FileSystemTypeFlags::empty(), dev_tmp_fs);
507    }
508    registry.register(b"erofs".into(), FileSystemTypeFlags::empty(), starnix_modules_erofs::new_fs);
509    registry.register(b"ext4".into(), FileSystemTypeFlags::REQUIRES_DEV, ExtFilesystem::new_fs);
510    registry.register(b"functionfs".into(), FileSystemTypeFlags::empty(), FunctionFs::new_fs);
511    registry.register(b"fuse".into(), FileSystemTypeFlags::empty(), new_fuse_fs);
512    registry.register(b"fusectl".into(), FileSystemTypeFlags::empty(), new_fusectl_fs);
513    registry.register(b"overlay".into(), FileSystemTypeFlags::empty(), new_overlay_fs);
514    register_pipe_fs(registry.as_ref());
515    registry.register(b"proc".into(), FileSystemTypeFlags::empty(), proc_fs);
516    registry.register(b"pstore".into(), FileSystemTypeFlags::empty(), pstore_fs);
517    registry.register(b"remotefs".into(), FileSystemTypeFlags::empty(), new_remote_fs);
518    registry.register(b"remotevol".into(), FileSystemTypeFlags::empty(), new_remote_vol);
519    registry.register(b"remote_bundle".into(), FileSystemTypeFlags::empty(), RemoteBundle::new_fs);
520    registry.register(b"selinuxfs".into(), FileSystemTypeFlags::empty(), selinux_fs);
521    registry.register(b"sysfs".into(), FileSystemTypeFlags::empty(), sys_fs);
522    registry.register(b"tmpfs".into(), FileSystemTypeFlags::empty(), tmp_fs);
523    registry.register(b"tracefs".into(), FileSystemTypeFlags::empty(), trace_fs);
524}
525
526pub fn register_common_syscalls(kernel: &Kernel) {
527    inotify_init(kernel);
528}