1#![recursion_limit = "512"]
6
7use anyhow::Context;
8use fidl_fuchsia_power_cpu_manager::BoostMarker;
9use fidl_fuchsia_sys2 as fsys;
10use fuchsia_inspect::Property;
11use starnix_core::device::DeviceOps;
12use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
13use starnix_core::task::{CurrentTask, Kernel};
14use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
15use starnix_core::vfs::{
16 CloseFreeSafe, FileObject, FileOps, NamespaceNode, fileops_impl_nonseekable,
17 fileops_impl_noop_sync,
18};
19use starnix_logging::{log_error, log_info, log_warn};
20use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, Unlocked};
21use starnix_uapi::device_type::DeviceType;
22use starnix_uapi::error;
23use starnix_uapi::errors::Errno;
24use starnix_uapi::open_flags::OpenFlags;
25use std::sync::Arc;
26use std::sync::mpsc::{Receiver, Sender, channel};
27use zerocopy::IntoBytes;
28
29pub fn booted_device_init(
31 locked: &mut Locked<Unlocked>,
32 system_task: &CurrentTask,
33 cpu_boost_duration: Option<zx::MonotonicDuration>,
34) {
35 let kernel = system_task.kernel();
36
37 let (booted_sender, booted_receiver) = channel::<bool>();
38 let node = fuchsia_inspect::component::inspector().root().create_child("boot");
39 let device = BootedDevice::new(system_task.kernel(), booted_sender, node, cpu_boost_duration)
40 .expect("must be able to initialize booted device");
41 device.clone().register(locked, &kernel.kthreads.system_task());
42 device.start_relay(&kernel, booted_receiver);
43
44 let registry = &kernel.device_registry;
45 registry
46 .register_misc_device(locked, system_task, "booted".into(), device)
47 .expect("can register boot module");
48}
49
50#[derive(Clone)]
51struct BootedDevice {
52 inner: Arc<Inner>,
53}
54
55const INSPECT_KEY: &str = "boot_timestamp";
56
57impl BootedDevice {
58 pub fn new(
59 kernel: &Kernel,
60 booted_sender: Sender<bool>,
61 inspect_node: fuchsia_inspect::Node,
62 cpu_boost_duration: Option<zx::MonotonicDuration>,
63 ) -> Result<Self, anyhow::Error> {
64 let boot_timestamp = inspect_node.create_uint(INSPECT_KEY, 0);
65
66 if let Some(duration) = cpu_boost_duration {
67 match kernel.connect_to_protocol_at_container_svc::<BoostMarker>() {
68 Ok(client_end) => {
69 log_info!("Enabling boot-time CPU boost");
70 let booster = client_end.into_proxy();
71 kernel.kthreads.spawn_future(
72 move || async move {
73 let token = match booster.boost().await {
74 Ok(Ok(token)) => token,
75 e => {
76 log_warn!(e:?; "Failed to enable boot-time CPU boost");
77 return;
78 }
79 };
80 fuchsia_async::Timer::new(zx::MonotonicInstant::after(duration)).await;
81 log_info!("Disabling boot-time CPU boost");
82 drop(token);
83 },
84 "boot_cpu_boost",
85 );
86 }
87 Err(e) => {
88 log_warn!(e:?; "Failed to connect to cpu boost protocol");
89 }
90 }
91 }
92
93 Ok(Self {
94 inner: Arc::new(Inner {
95 file: File::new(booted_sender),
96 _inspect_node: inspect_node,
97 boot_timestamp,
98 }),
99 })
100 }
101
102 pub fn register<L>(self, locked: &mut Locked<L>, system_task: &CurrentTask)
103 where
104 L: LockEqualOrBefore<FileOpsCore>,
105 {
106 let kernel = system_task.kernel();
107 let registry = &kernel.device_registry;
108 registry
109 .register_dyn_device(
110 locked,
111 system_task,
112 "booted".into(),
113 registry.objects.starnix_class(),
114 self,
115 )
116 .expect("can register booted device");
117 }
118
119 pub fn start_relay(&self, kernel: &Kernel, booted_receiver: Receiver<bool>) {
120 let this = self.inner.clone();
121 let closure = move |_lock_context: &mut Locked<Unlocked>, _current_task: &CurrentTask| {
122 let mut prev_booted = false;
123 while let Ok(booted) = booted_receiver.recv() {
124 if booted && !prev_booted {
125 match this.notify_boot_completed() {
126 Ok(()) => log_info!("Notified system boot completed"),
127 Err(e) => log_error!(e:?; "Failed to notify system boot completed"),
128 }
129 }
130 prev_booted = booted;
131 }
132 log_error!("booted relay was terminated unexpectedly.");
133 };
134 let req = SpawnRequestBuilder::new()
135 .with_debug_name("boot-notifier-relay")
136 .with_sync_closure(closure)
137 .build();
138
139 kernel.kthreads.spawner().spawn_from_request(req);
140 }
141}
142
143struct Inner {
144 file: Arc<File>,
145 _inspect_node: fuchsia_inspect::Node,
146 boot_timestamp: fuchsia_inspect::UintProperty,
147}
148
149impl Inner {
150 fn notify_boot_completed(&self) -> Result<(), anyhow::Error> {
151 log_info!("Boot has been marked completed");
152 let client =
153 fuchsia_component::client::connect_to_protocol_sync::<fsys::BootControllerMarker>()
154 .context("connecting to BootController")?;
155 client.notify(zx::MonotonicInstant::INFINITE).context("calling BootController/Notify")?;
156 let ts = zx::BootInstant::get().into_nanos() as u64;
157 let _ = self.boot_timestamp.set(ts);
158
159 Ok(())
160 }
161}
162
163struct File {
164 booted: Mutex<bool>,
165 sender: Sender<bool>,
166}
167
168impl File {
169 fn new(sender: Sender<bool>) -> Arc<Self> {
170 Arc::new(Self { booted: Mutex::new(false), sender })
171 }
172}
173
174impl CloseFreeSafe for File {}
176impl FileOps for File {
177 fileops_impl_nonseekable!();
178 fileops_impl_noop_sync!();
179
180 fn read(
181 &self,
182 _locked: &mut Locked<FileOpsCore>,
183 _file: &FileObject,
184 _current_task: &CurrentTask,
185 offset: usize,
186 data: &mut dyn OutputBuffer,
187 ) -> Result<usize, Errno> {
188 debug_assert!(offset == 0);
189 let booted = self.booted.lock().to_owned();
190 data.write_all(booted.as_bytes())
191 }
192
193 fn write(
194 &self,
195 _locked: &mut Locked<FileOpsCore>,
196 _file: &FileObject,
197 _current_task: &CurrentTask,
198 _offset: usize,
199 data: &mut dyn InputBuffer,
200 ) -> Result<usize, Errno> {
201 let content = data.read_all()?;
202 let booted = match &*content {
203 b"0" | b"0\n" => false,
204 b"1" | b"1\n" => true,
205 _ => {
206 log_error!("Invalid booted value - must be 0 or 1");
207 return error!(EINVAL);
208 }
209 };
210 *self.booted.lock() = booted;
211 if let Err(e) = self.sender.send(booted) {
212 log_error!("unable to send recent booted state to device relay: {:?}", e);
213 }
214 Ok(content.len())
215 }
216}
217
218impl DeviceOps for BootedDevice {
219 fn open(
220 &self,
221 _locked: &mut Locked<FileOpsCore>,
222 _current_task: &CurrentTask,
223 _device_type: DeviceType,
224 _node: &NamespaceNode,
225 _flags: OpenFlags,
226 ) -> Result<Box<dyn FileOps>, Errno> {
227 let file = self.inner.file.clone();
228 Ok(Box::new(file))
229 }
230}