1use 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#[derive(Default, Debug)]
45pub struct Features {
46 pub kernel: KernelFeatures,
48
49 pub selinux: SELinuxFeature,
51
52 pub system_limits: SystemLimits,
54
55 pub ashmem: bool,
57
58 pub boot_notifier: bool,
60
61 pub boot_notifier_cpu_boost: Option<zx::MonotonicDuration>,
63
64 pub framebuffer: bool,
66
67 pub aspect_ratio: Option<AspectRatio>,
69
70 pub enable_visual_debugging: bool,
73
74 pub gralloc: bool,
76
77 pub kgsl: bool,
79
80 pub magma_supported_vendors: Option<Vec<u16>>,
82
83 pub gfxstream: bool,
85
86 pub container: bool,
88
89 pub test_data: bool,
91
92 pub custom_artifacts: bool,
94
95 pub android_serialno: bool,
97
98 pub perfetto: Option<FsString>,
100
101 pub rootfs_rw: bool,
103
104 pub network_manager: bool,
106
107 pub nanohub: bool,
109
110 pub fastrpc: bool,
112
113 pub enable_utc_time_adjustment: bool,
114
115 pub thermal: bool,
116
117 pub cooling: Option<Vec<String>>,
120
121 pub android_bootreason: bool,
123
124 pub hvdcp_opti: bool,
125
126 pub additional_mounts: Option<Vec<String>>,
127
128 pub wakeup_test: bool,
130
131 pub mmcblk_stub: bool,
134}
135
136#[derive(Default, Debug, PartialEq)]
137pub struct SELinuxFeature {
138 pub enabled: bool,
140
141 pub options: String,
143
144 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
297pub 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
484pub 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 *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 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 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
647pub 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}