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