1use 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#[derive(Default, Debug)]
44pub struct Features {
45 pub kernel: KernelFeatures,
47
48 pub selinux: SELinuxFeature,
50
51 pub system_limits: SystemLimits,
53
54 pub ashmem: bool,
56
57 pub boot_notifier: bool,
59
60 pub boot_notifier_cpu_boost: Option<zx::MonotonicDuration>,
62
63 pub framebuffer: bool,
65
66 pub aspect_ratio: Option<AspectRatio>,
68
69 pub enable_visual_debugging: bool,
72
73 pub gralloc: bool,
75
76 pub kgsl: bool,
78
79 pub magma_supported_vendors: Option<Vec<u16>>,
81
82 pub gfxstream: bool,
84
85 pub container: bool,
87
88 pub test_data: bool,
90
91 pub custom_artifacts: bool,
93
94 pub android_serialno: bool,
96
97 pub perfetto: Option<FsString>,
99
100 pub rootfs_rw: bool,
102
103 pub network_manager: bool,
105
106 pub nanohub: bool,
108
109 pub fastrpc: bool,
111
112 pub enable_utc_time_adjustment: bool,
113
114 pub thermal: bool,
115
116 pub cooling: Option<Vec<String>>,
119
120 pub android_bootreason: bool,
122
123 pub hvdcp_opti: bool,
124
125 pub additional_mounts: Option<Vec<String>>,
126
127 pub wakeup_test: bool,
129}
130
131#[derive(Default, Debug, PartialEq)]
132pub struct SELinuxFeature {
133 pub enabled: bool,
135
136 pub options: String,
138
139 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
288pub 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
473pub 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 *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 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 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
632pub 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}