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