Skip to main content

starnix_kernel_runner/
features.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::ContainerStartInfo;
6use anyhow::{Context, Error, anyhow};
7use starnix_container_structured_config::Config as ContainerStructuredConfig;
8use starnix_core::device::block::add_mmc_block_device;
9use starnix_core::mm::MlockPinFlavor;
10use starnix_core::task::{CurrentTask, Kernel, KernelFeatures, SystemLimits};
11use starnix_core::vfs::FsString;
12use starnix_features::Feature;
13use starnix_logging::log_error;
14use starnix_modules_ashmem::ashmem_device_init;
15use starnix_modules_boot::booted_device_init;
16use starnix_modules_fastrpc::fastrpc_device_init;
17use starnix_modules_framebuffer::{AspectRatio, Framebuffer};
18use starnix_modules_gpu::gpu_device_init;
19use starnix_modules_gralloc::gralloc_device_init;
20use starnix_modules_hvdcp_opti::hvdcp_opti_init;
21use starnix_modules_input::uinput::register_uinput_device;
22use starnix_modules_input::{
23    DEFAULT_KEYBOARD_DEVICE_ID, DEFAULT_MOUSE_DEVICE_ID, DEFAULT_TOUCH_DEVICE_ID, EventProxyMode,
24    InputDevice, new_input_relay,
25};
26use starnix_modules_kgsl::kgsl_device_init;
27use starnix_modules_magma::magma_device_init;
28use starnix_modules_nanohub::nanohub_device_init;
29use starnix_modules_nmfs::nmfs_init;
30use starnix_modules_perfetto_consumer::start_perfetto_consumer_thread;
31use starnix_modules_thermal::{cooling_device_init, thermal_device_init};
32use starnix_modules_touch_power_policy::TouchPowerPolicyDevice;
33use starnix_modules_wakeup_test::register_wakeup_test_device;
34use starnix_sync::{Locked, Unlocked};
35use starnix_uapi::error;
36use starnix_uapi::errors::Errno;
37use std::sync::mpsc::channel;
38use {
39    fidl_fuchsia_ui_composition as fuicomposition, fidl_fuchsia_ui_input3 as fuiinput,
40    fidl_fuchsia_ui_policy as fuipolicy, fidl_fuchsia_ui_views as fuiviews,
41};
42
43/// A collection of parsed features, and their arguments.
44#[derive(Default, Debug)]
45pub struct Features {
46    /// Features that must be available to the kernel after initialization.
47    pub kernel: KernelFeatures,
48
49    /// SELinux configuration.
50    pub selinux: SELinuxFeature,
51
52    /// Limits value passed to the kernel during the initialization.
53    pub system_limits: SystemLimits,
54
55    /// Whether to enable ashmem.
56    pub ashmem: bool,
57
58    /// Whether to enable a boot notifier device.
59    pub boot_notifier: bool,
60
61    /// Whether to boost CPU for a fixed duration.
62    pub boot_notifier_cpu_boost: Option<zx::MonotonicDuration>,
63
64    /// Whether to enable a framebuffer device.
65    pub framebuffer: bool,
66
67    /// Display aspect ratio.
68    pub aspect_ratio: Option<AspectRatio>,
69
70    /// This controls whether or not the default framebuffer background is black or colorful, to
71    /// aid debugging.
72    pub enable_visual_debugging: bool,
73
74    /// Whether to enable gralloc.
75    pub gralloc: bool,
76
77    /// Whether to enable the Kernel Graphics Support Layer (kgsl) device used by Adreno GPUs.
78    pub kgsl: bool,
79
80    /// Supported magma vendor IDs.
81    pub magma_supported_vendors: Option<Vec<u16>>,
82
83    /// Whether to enable gfxstream.
84    pub gfxstream: bool,
85
86    /// Include the /container directory in the root file system.
87    pub container: bool,
88
89    /// Include the /test_data directory in the root file system.
90    pub test_data: bool,
91
92    /// Include the /custom_artifacts directory in the root file system.
93    pub custom_artifacts: bool,
94
95    /// Whether to provide android with a serial number.
96    pub android_serialno: bool,
97
98    /// Optional perfetto configuration.
99    pub perfetto: Option<FsString>,
100
101    /// Whether to allow the root filesystem to be read/write.
102    pub rootfs_rw: bool,
103
104    /// Whether to enable network manager and its filesystem.
105    pub network_manager: bool,
106
107    /// Whether to enable the nanohub module.
108    pub nanohub: bool,
109
110    /// Whether to enable the fastrpc module.
111    pub fastrpc: bool,
112
113    pub enable_utc_time_adjustment: bool,
114
115    pub thermal: bool,
116
117    /// Optional cooling features to enable. See [`cooling_device_init`] for a list of possible
118    /// values and their parameters.
119    pub cooling: Option<Vec<String>>,
120
121    /// Whether to add android bootreason to kernel cmdline.
122    pub android_bootreason: bool,
123
124    pub hvdcp_opti: bool,
125
126    pub additional_mounts: Option<Vec<String>>,
127
128    /// Support kernel level suspend/resume tests.
129    pub wakeup_test: bool,
130
131    /// Add a stub block device entry for mmcblk0. This is required for certain containers that
132    /// query certain functionality based on block device naming.
133    pub mmcblk_stub: bool,
134}
135
136#[derive(Default, Debug, PartialEq)]
137pub struct SELinuxFeature {
138    /// True if SELinux should be enabled in the container.
139    pub enabled: bool,
140
141    /// Optional set of options to pass to the SELinux module.
142    pub options: String,
143
144    /// Optional set of access-check exceptions to pass to the SELinux module.
145    pub exceptions: Vec<String>,
146}
147
148impl Features {
149    pub fn record_inspect(&self, parent_node: &fuchsia_inspect::Node) {
150        parent_node.record_child("features", |inspect_node| match self {
151            Features {
152                kernel:
153                    KernelFeatures {
154                        bpf_v2,
155                        enable_suid,
156                        io_uring,
157                        error_on_failed_reboot,
158                        default_uid,
159                        default_seclabel,
160                        selinux_test_suite,
161                        default_ns_mount_options,
162                        mlock_always_onfault,
163                        mlock_pin_flavor,
164                        crash_report_throttling,
165                        wifi,
166                        cached_zx_map_info_bytes,
167                        dirent_cache_size,
168                        fake_ion,
169                    },
170                system_limits,
171                selinux,
172                ashmem,
173                boot_notifier,
174                boot_notifier_cpu_boost,
175                framebuffer,
176                aspect_ratio,
177                enable_visual_debugging,
178                gralloc,
179                kgsl,
180                magma_supported_vendors,
181                gfxstream,
182                container,
183                test_data,
184                custom_artifacts,
185                android_serialno,
186                perfetto,
187                rootfs_rw,
188                network_manager,
189                nanohub,
190                fastrpc,
191                enable_utc_time_adjustment,
192                thermal,
193                cooling,
194                android_bootreason,
195                hvdcp_opti,
196                additional_mounts,
197                wakeup_test,
198                mmcblk_stub,
199            } => {
200                inspect_node.record_bool("selinux", selinux.enabled);
201                inspect_node.record_bool("ashmem", *ashmem);
202                inspect_node.record_bool("boot_notifier", *boot_notifier);
203                inspect_node.record_string(
204                    "boot_notifier_cpu_boost",
205                    boot_notifier_cpu_boost
206                        .map(|d| format!("{}s", d.into_seconds()))
207                        .unwrap_or_else(|| "none".to_string()),
208                );
209                inspect_node.record_bool("framebuffer", *framebuffer);
210                inspect_node.record_bool("gralloc", *gralloc);
211                inspect_node.record_bool("kgsl", *kgsl);
212                inspect_node.record_string(
213                    "magma_supported_vendors",
214                    match magma_supported_vendors {
215                        Some(vendors) => vendors
216                            .iter()
217                            .map(|vendor| format!("0x{:x}", vendor))
218                            .collect::<Vec<String>>()
219                            .join(","),
220                        None => "".to_string(),
221                    },
222                );
223                inspect_node.record_bool("gfxstream", *gfxstream);
224                inspect_node.record_bool("container", *container);
225                inspect_node.record_bool("test_data", *test_data);
226                inspect_node.record_bool("custom_artifacts", *custom_artifacts);
227                inspect_node.record_bool("android_serialno", *android_serialno);
228                inspect_node.record_string(
229                    "aspect_ratio",
230                    aspect_ratio
231                        .as_ref()
232                        .map(|aspect_ratio| {
233                            format!("width: {} height: {}", aspect_ratio.width, aspect_ratio.height)
234                        })
235                        .unwrap_or_default(),
236                );
237                inspect_node.record_string(
238                    "perfetto",
239                    perfetto.as_ref().map(|p| p.to_string()).unwrap_or_default(),
240                );
241                inspect_node.record_bool("rootfs_rw", *rootfs_rw);
242                inspect_node.record_bool("network_manager", *network_manager);
243                inspect_node.record_bool("nanohub", *nanohub);
244                inspect_node.record_bool("fastrpc", *fastrpc);
245                inspect_node.record_bool("thermal", *thermal);
246                inspect_node.record_string(
247                    "cooling",
248                    match cooling {
249                        Some(devices) => devices.join(","),
250                        None => "".to_string(),
251                    },
252                );
253                inspect_node.record_bool("android_bootreason", *android_bootreason);
254                inspect_node.record_bool("hvdcp_opti", *hvdcp_opti);
255                inspect_node.record_string("ping_group_range", {
256                    let range = system_limits.socket.icmp_ping_gids.lock();
257                    std::format!("{},{}", range.start, range.end - 1)
258                });
259
260                inspect_node.record_child("kernel", |kernel_node| {
261                    kernel_node.record_bool("bpf_v2", *bpf_v2);
262                    kernel_node.record_bool("enable_suid", *enable_suid);
263                    kernel_node.record_bool("io_uring", *io_uring);
264                    kernel_node.record_bool("error_on_failed_reboot", *error_on_failed_reboot);
265                    kernel_node.record_bool("enable_visual_debugging", *enable_visual_debugging);
266                    kernel_node.record_int("default_uid", (*default_uid).into());
267                    kernel_node.record_string(
268                        "default_seclabel",
269                        default_seclabel.as_deref().unwrap_or_default(),
270                    );
271                    kernel_node.record_bool("selinux_test_suite", *selinux_test_suite);
272                    kernel_node.record_bool("crash_report_throttling", *crash_report_throttling);
273                    kernel_node
274                        .record_uint("cached_zx_map_info_bytes", *cached_zx_map_info_bytes as u64);
275                    inspect_node.record_string(
276                        "default_ns_mount_options",
277                        format!("{:?}", default_ns_mount_options),
278                    );
279                    inspect_node
280                        .record_bool("enable_utc_time_adjustment", *enable_utc_time_adjustment);
281                    inspect_node.record_bool("mlock_always_onfault", *mlock_always_onfault);
282                    inspect_node
283                        .record_string("mlock_pin_flavor", format!("{:?}", mlock_pin_flavor));
284                    inspect_node.record_bool("wifi", *wifi);
285                    inspect_node
286                        .record_string("additional_mounts", format!("{:?}", additional_mounts));
287                    inspect_node.record_uint("dirent_cache_size", *dirent_cache_size as u64);
288                    inspect_node.record_bool("wakeup_test", *wakeup_test);
289                    inspect_node.record_bool("mmcblk_stub", *mmcblk_stub);
290                    inspect_node.record_bool("fake_ion", *fake_ion);
291                });
292            }
293        });
294    }
295}
296
297/// Parses all the featurse in `entries`.
298///
299/// Returns an error if parsing fails, or if an unsupported feature is present in `features`.
300pub fn parse_features(
301    start_info: &ContainerStartInfo,
302    kernel_extra_features: &[String],
303) -> Result<Features, Error> {
304    let ContainerStructuredConfig {
305        crash_report_throttling,
306        enable_utc_time_adjustment,
307        extra_features,
308        cached_zx_map_info_bytes,
309        mlock_always_onfault,
310        mlock_pin_flavor,
311        selinux_exceptions,
312        ui_visual_debugging_level,
313        additional_mounts,
314        dirent_cache_size,
315    } = &start_info.config;
316
317    let mut features = Features::default();
318    for entry in start_info
319        .program
320        .features
321        .iter()
322        .chain(kernel_extra_features.iter())
323        .chain(extra_features.iter())
324    {
325        let (feature, raw_args) = Feature::try_parse_feature_and_args(entry)?;
326        match (feature, raw_args) {
327            (Feature::AndroidSerialno, _) => features.android_serialno = true,
328            (Feature::AndroidBootreason, _) => features.android_bootreason = true,
329            (Feature::AspectRatio, Some(args)) => {
330                let e = anyhow!("Invalid aspect_ratio: {:?}", args);
331                let components: Vec<_> = args.split(':').collect();
332                if components.len() != 2 {
333                    return Err(e);
334                }
335                let width: u32 =
336                    components[0].parse().map_err(|_| anyhow!("Invalid aspect ratio width"))?;
337                let height: u32 =
338                    components[1].parse().map_err(|_| anyhow!("Invalid aspect ratio height"))?;
339                features.aspect_ratio = Some(AspectRatio { width, height });
340            }
341            (Feature::AspectRatio, None) => {
342                return Err(anyhow!(
343                    "Aspect ratio feature must contain the aspect ratio in the format: aspect_ratio:w:h"
344                ));
345            }
346            (Feature::Container, _) => features.container = true,
347            (Feature::CustomArtifacts, _) => features.custom_artifacts = true,
348            (Feature::Ashmem, _) => features.ashmem = true,
349            (Feature::BootNotifier, _) => features.boot_notifier = true,
350            (Feature::BootNotifierCpuBoost, Some(arg)) => {
351                let duration = zx::MonotonicDuration::from_seconds(arg.parse::<i64>()?);
352                features.boot_notifier_cpu_boost = Some(duration);
353            }
354            (Feature::BootNotifierCpuBoost, None) => {
355                return Err(anyhow!(
356                    "boot_notifier_cpu_boost feature must have an argument (e.g. \"boot_notifier_cpu_boost:60\")"
357                ));
358            }
359            (Feature::Framebuffer, _) => features.framebuffer = true,
360            (Feature::Gralloc, _) => features.gralloc = true,
361            (Feature::Kgsl, _) => features.kgsl = true,
362            (Feature::Magma, _) => {
363                if features.magma_supported_vendors.is_none() {
364                    const VENDOR_ARM: u16 = 0x13B5;
365                    const VENDOR_INTEL: u16 = 0x8086;
366                    features.magma_supported_vendors = Some(vec![VENDOR_ARM, VENDOR_INTEL])
367                }
368            }
369            (Feature::MagmaSupportedVendors, Some(arg)) => {
370                features.magma_supported_vendors = Some(
371                    arg.split(',')
372                        .map(|s| {
373                            let err = anyhow!(
374                                "Feature format must be: magma_supported_vendors:0x1234[,0xabcd]"
375                            );
376                            let trimmed = s.trim_start_matches("0x");
377                            u16::from_str_radix(trimmed, 16).map_err(|_| err)
378                        })
379                        .collect::<Result<Vec<u16>, Error>>()?,
380                );
381            }
382            (Feature::MagmaSupportedVendors, None) => {
383                return Err(anyhow!(
384                    "Feature format must be: magma_supported_vendors:0x1234[,0xabcd]"
385                ));
386            }
387            (Feature::Nanohub, _) => features.nanohub = true,
388            (Feature::Fastrpc, _) => features.fastrpc = true,
389            (Feature::NetworkManager, _) => features.network_manager = true,
390            (Feature::Gfxstream, _) => features.gfxstream = true,
391            (Feature::Bpf, Some(version)) => features.kernel.bpf_v2 = version == "v2",
392            (Feature::Bpf, None) => {
393                return Err(anyhow!("bpf feature must have an argument (e.g. \"bpf:v2\")"));
394            }
395            (Feature::EnableSuid, _) => features.kernel.enable_suid = true,
396            (Feature::IoUring, _) => features.kernel.io_uring = true,
397            (Feature::ErrorOnFailedReboot, _) => features.kernel.error_on_failed_reboot = true,
398            (Feature::Perfetto, Some(socket_path)) => {
399                features.perfetto = Some(socket_path.into());
400            }
401            (Feature::Perfetto, None) => {
402                return Err(anyhow!("Perfetto feature must contain a socket path"));
403            }
404            (Feature::PingGroupRange, Some(arg)) => {
405                let mut args = arg.split(',');
406                let (min, max) = (|| {
407                    let min = args.next()?.trim_ascii().parse::<u32>().ok()?;
408                    let max = args.next()?.trim_ascii().parse::<u32>().ok()?.checked_add(1)?;
409                    if args.next().is_some() {
410                        return None;
411                    }
412                    Some((min, max))
413                })()
414                .ok_or_else(|| anyhow!("Feature format must be: ping_group_range:0,100"))?;
415                *features.system_limits.socket.icmp_ping_gids.lock() = min..max;
416            }
417            (Feature::PingGroupRange, None) => {
418                return Err(anyhow!("Feature format must be: ping_group_range:0,100"));
419            }
420            (Feature::RootfsRw, _) => features.rootfs_rw = true,
421            (Feature::Selinux, arg) => {
422                features.selinux = SELinuxFeature {
423                    enabled: true,
424                    options: arg.unwrap_or_default(),
425                    exceptions: selinux_exceptions.clone(),
426                };
427            }
428            (Feature::SelinuxTestSuite, _) => features.kernel.selinux_test_suite = true,
429            (Feature::TestData, _) => features.test_data = true,
430            (Feature::Thermal, _) => features.thermal = true,
431            (Feature::Cooling, Some(arg)) => {
432                features.cooling = Some(arg.split(',').map(String::from).collect::<Vec<String>>())
433            }
434            (Feature::Cooling, None) => {
435                return Err(anyhow!("cooling feature must have an argument"));
436            }
437            (Feature::HvdcpOpti, _) => features.hvdcp_opti = true,
438            (Feature::Wifi, _) => features.kernel.wifi = true,
439            (Feature::AdditionalMounts, _) => {
440                features.additional_mounts = Some(additional_mounts.clone())
441            }
442            (Feature::WakeupTest, _) => features.wakeup_test = true,
443            (Feature::MmcblkStub, _) => features.mmcblk_stub = true,
444            (Feature::FakeIon, _) => features.kernel.fake_ion = true,
445        };
446    }
447
448    if features.boot_notifier_cpu_boost.is_some() && !features.boot_notifier {
449        return Err(anyhow!("boot_notifier_cpu_boost feature requires boot_notifier"));
450    }
451
452    if *ui_visual_debugging_level > 0 {
453        features.enable_visual_debugging = true;
454    }
455    features.enable_utc_time_adjustment = *enable_utc_time_adjustment;
456
457    features.kernel.default_uid = start_info.program.default_uid.0;
458    features.kernel.default_seclabel = start_info.program.default_seclabel.clone();
459    features.kernel.default_ns_mount_options =
460        if let Some(mount_options) = &start_info.program.default_ns_mount_options {
461            let options = mount_options
462                .iter()
463                .map(|item| {
464                    let mut splitter = item.splitn(2, ":");
465                    let key = splitter.next().expect("Failed to parse mount options");
466                    let value = splitter.next().expect("Failed to parse mount options");
467                    (key.to_string(), value.to_string())
468                })
469                .collect();
470            Some(options)
471        } else {
472            None
473        };
474
475    features.kernel.mlock_always_onfault = *mlock_always_onfault;
476    features.kernel.mlock_pin_flavor = MlockPinFlavor::parse(mlock_pin_flavor.as_str())?;
477    features.kernel.crash_report_throttling = *crash_report_throttling;
478    features.kernel.cached_zx_map_info_bytes = *cached_zx_map_info_bytes;
479    features.kernel.dirent_cache_size = *dirent_cache_size;
480
481    Ok(features)
482}
483
484/// Runs all the features that are enabled in `system_task.kernel()`.
485
486pub fn run_container_features(
487    locked: &mut Locked<Unlocked>,
488    system_task: &CurrentTask,
489    features: &Features,
490) -> Result<(), Error> {
491    let kernel = system_task.kernel();
492
493    if features.framebuffer {
494        let framebuffer = Framebuffer::device_init(
495            locked,
496            system_task,
497            features.aspect_ratio,
498            features.enable_visual_debugging,
499        )
500        .context("initializing framebuffer")?;
501
502        let (touch_source_client, touch_source_server) = fidl::endpoints::create_endpoints();
503        let (mouse_source_client, mouse_source_server) = fidl::endpoints::create_endpoints();
504        let view_bound_protocols = fuicomposition::ViewBoundProtocols {
505            touch_source: Some(touch_source_server),
506            mouse_source: Some(mouse_source_server),
507            ..Default::default()
508        };
509        let view_identity = fuiviews::ViewIdentityOnCreation::from(
510            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair"),
511        );
512        let view_ref = fuchsia_scenic::duplicate_view_ref(&view_identity.view_ref)
513            .expect("Failed to dup view ref.");
514        let keyboard =
515            fuchsia_component::client::connect_to_protocol_sync::<fuiinput::KeyboardMarker>()
516                .expect("Failed to connect to keyboard");
517        let registry_proxy = fuchsia_component::client::connect_to_protocol_sync::<
518            fuipolicy::DeviceListenerRegistryMarker,
519        >()
520        .expect("Failed to connect to device listener registry");
521
522        // These need to be set before `Framebuffer::start_server` is called.
523        // `Framebuffer::start_server` is only called when the `framebuffer` component feature is
524        // enabled. The container is the runner for said components, and `run_container_features`
525        // is performed before the Container is fully initialized. Therefore, it's safe to set
526        // these values at this point.
527        //
528        // In the future, we would like to avoid initializing a framebuffer unconditionally on the
529        // Kernel, at which point this logic will need to change.
530        *framebuffer.view_identity.lock() = Some(view_identity);
531        *framebuffer.view_bound_protocols.lock() = Some(view_bound_protocols);
532
533        let framebuffer_info = framebuffer.info.read();
534
535        let display_width = framebuffer_info.xres as i32;
536        let display_height = framebuffer_info.yres as i32;
537
538        let touch_device =
539            InputDevice::new_touch(display_width, display_height, &kernel.inspect_node);
540        let keyboard_device = InputDevice::new_keyboard(&kernel.inspect_node);
541        let mouse_device = InputDevice::new_mouse(&kernel.inspect_node);
542
543        touch_device.clone().register(
544            locked,
545            &kernel.kthreads.system_task(),
546            DEFAULT_TOUCH_DEVICE_ID,
547        )?;
548        keyboard_device.clone().register(
549            locked,
550            &kernel.kthreads.system_task(),
551            DEFAULT_KEYBOARD_DEVICE_ID,
552        )?;
553        mouse_device.clone().register(
554            locked,
555            &kernel.kthreads.system_task(),
556            DEFAULT_MOUSE_DEVICE_ID,
557        )?;
558
559        let (input_events_relay, input_events_relay_handle) = new_input_relay();
560        input_events_relay.start_relays(
561            &kernel,
562            EventProxyMode::WakeContainer,
563            touch_source_client,
564            keyboard,
565            mouse_source_client,
566            view_ref,
567            registry_proxy,
568            touch_device.open_files.clone(),
569            keyboard_device.open_files.clone(),
570            mouse_device.open_files.clone(),
571            Some(touch_device.inspect_status),
572            Some(keyboard_device.inspect_status),
573            Some(mouse_device.inspect_status),
574        );
575
576        register_uinput_device(locked, &kernel.kthreads.system_task(), input_events_relay_handle)?;
577
578        // Channel we use to inform the relay of changes to `touch_standby`
579        let (touch_standby_sender, touch_standby_receiver) = channel::<bool>();
580        let touch_policy_device = TouchPowerPolicyDevice::new(touch_standby_sender);
581        touch_policy_device.clone().register(locked, &kernel.kthreads.system_task());
582        touch_policy_device.start_relay(&kernel, touch_standby_receiver);
583
584        framebuffer.start_server(kernel, None);
585    }
586    if features.gralloc {
587        // The virtgralloc0 device allows vulkan_selector to indicate to gralloc
588        // whether swiftshader or magma will be used. This is separate from the
589        // magma feature because the policy choice whether to use magma or
590        // swiftshader is in vulkan_selector, and it can potentially choose
591        // switfshader for testing purposes even when magma0 is present. Also,
592        // it's nice to indicate swiftshader the same way regardless of whether
593        // the magma feature is enabled or disabled. If a call to gralloc AIDL
594        // IAllocator allocate2 occurs with this feature disabled, the call will
595        // fail.
596        gralloc_device_init(locked, system_task);
597    }
598    if features.kgsl {
599        kgsl_device_init(locked, system_task);
600    }
601    if let Some(supported_vendors) = &features.magma_supported_vendors {
602        magma_device_init(locked, system_task, supported_vendors.clone());
603    }
604    if features.gfxstream {
605        gpu_device_init(locked, system_task);
606    }
607    if let Some(socket_path) = features.perfetto.clone() {
608        start_perfetto_consumer_thread(kernel, socket_path)
609            .context("Failed to start perfetto consumer thread")?;
610    }
611    if features.ashmem {
612        ashmem_device_init(locked, system_task);
613    }
614    if features.boot_notifier {
615        booted_device_init(locked, system_task, features.boot_notifier_cpu_boost);
616    }
617    if features.network_manager {
618        if let Err(e) = nmfs_init(system_task) {
619            log_error!("Network manager initialization failed: ({e:?})");
620        }
621    }
622    if features.nanohub {
623        nanohub_device_init(locked, system_task);
624    }
625    if features.thermal {
626        thermal_device_init(locked, kernel)?;
627    }
628    if let Some(devices) = &features.cooling {
629        cooling_device_init(locked, kernel, devices.clone())?;
630    }
631    if features.hvdcp_opti {
632        hvdcp_opti_init(locked, system_task)?;
633    }
634    if features.fastrpc {
635        fastrpc_device_init(locked, system_task);
636    }
637    if features.wakeup_test {
638        register_wakeup_test_device(locked, system_task)?;
639    }
640    if features.mmcblk_stub {
641        let _device = add_mmc_block_device(locked, system_task)
642            .context("Failed to add stub mmcblk0 device")?;
643    }
644    Ok(())
645}
646
647/// Runs features requested by individual components inside the container.
648pub fn run_component_features(
649    kernel: &Kernel,
650    entries: &Vec<String>,
651    mut incoming_dir: Option<fidl_fuchsia_io::DirectoryProxy>,
652) -> Result<(), Errno> {
653    for entry in entries {
654        match entry.as_str() {
655            "framebuffer" => {
656                Framebuffer::get(kernel)?.start_server(kernel, incoming_dir.take());
657            }
658            feature => {
659                return error!(ENOSYS, format!("Unsupported feature: {}", feature));
660            }
661        }
662    }
663    Ok(())
664}