starnix_modules_boot/
lib.rs1#![recursion_limit = "512"]
6
7use anyhow::{Context, Error, anyhow};
8use argh::FromArgs;
9use fidl_fuchsia_power_cpu_manager::BoostMarker;
10use fidl_fuchsia_sys2 as fsys;
11use fuchsia_inspect::Property;
12use starnix_core::device::DeviceOps;
13use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
14use starnix_core::task::{CurrentTask, Kernel, LockupDetectorReceiver, ThreadLockupDetector};
15use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
16use starnix_core::vfs::{
17 CloseFreeSafe, FileObject, FileOps, NamespaceNode, fileops_impl_nonseekable,
18 fileops_impl_noop_sync,
19};
20use starnix_logging::{log_error, log_info, log_warn};
21use starnix_sync::{BootedLock, LockDepMutex};
22use starnix_uapi::device_id::DeviceId;
23use starnix_uapi::error;
24use starnix_uapi::errors::Errno;
25use starnix_uapi::open_flags::OpenFlags;
26use std::sync::Arc;
27use std::sync::mpsc::Sender;
28use zerocopy::IntoBytes;
29
30#[derive(FromArgs, Debug, Default)]
32struct BootNotifierArgs {
33 #[argh(option)]
35 cpu_boost_secs: Option<u32>,
36}
37
38fn parse_args(args: &[&str]) -> Result<Option<zx::MonotonicDuration>, Error> {
39 let parsed = BootNotifierArgs::from_args(&["boot_notifier"], args)
40 .map_err(|e| anyhow!("{}", e.output))?;
41 Ok(parsed.cpu_boost_secs.map(|secs| zx::MonotonicDuration::from_seconds(secs.into())))
42}
43
44pub fn validate_args(args: &[&str]) -> Result<(), Error> {
46 parse_args(args).map(|_| ())
47}
48
49pub fn module_init(kernel: &Kernel, args: &[&str]) -> Result<(), Error> {
51 let cpu_boost_duration = parse_args(args)?;
52 let (booted_sender, booted_receiver) = ThreadLockupDetector::tracked_channel::<bool>();
53 let node = fuchsia_inspect::component::inspector().root().create_child("boot");
54 let device = BootedDevice::new(kernel, booted_sender, node, cpu_boost_duration)?;
55 device.clone().register(kernel);
56 device.start_relay(kernel, booted_receiver);
57 Ok(())
58}
59
60#[derive(Clone)]
61struct BootedDevice {
62 inner: Arc<Inner>,
63}
64
65const INSPECT_KEY: &str = "boot_timestamp";
66
67impl BootedDevice {
68 pub fn new(
69 kernel: &Kernel,
70 booted_sender: Sender<bool>,
71 inspect_node: fuchsia_inspect::Node,
72 cpu_boost_duration: Option<zx::MonotonicDuration>,
73 ) -> Result<Self, anyhow::Error> {
74 let boot_timestamp = inspect_node.create_uint(INSPECT_KEY, 0);
75
76 if let Some(duration) = cpu_boost_duration {
77 match kernel.connect_to_protocol_at_container_svc::<BoostMarker>() {
78 Ok(client_end) => {
79 log_info!("Enabling boot-time CPU boost");
80 let booster = client_end.into_proxy();
81 kernel.kthreads.spawn_future(
82 move || async move {
83 let token = match booster.boost().await {
84 Ok(Ok(token)) => token,
85 e => {
86 log_warn!(e:?; "Failed to enable boot-time CPU boost");
87 return;
88 }
89 };
90 fuchsia_async::Timer::new(zx::MonotonicInstant::after(duration)).await;
91 log_info!("Disabling boot-time CPU boost");
92 drop(token);
93 },
94 "boot_cpu_boost",
95 );
96 }
97 Err(e) => {
98 log_warn!(e:?; "Failed to connect to cpu boost protocol");
99 }
100 }
101 }
102
103 Ok(Self {
104 inner: Arc::new(Inner {
105 file: File::new(booted_sender),
106 _inspect_node: inspect_node,
107 boot_timestamp,
108 }),
109 })
110 }
111
112 pub fn register(self, kernel: &Kernel) {
113 let registry = &kernel.device_registry;
114 registry
115 .register_dyn_device(kernel, "booted".into(), registry.objects.starnix_class(), self)
116 .expect("can register booted device");
117 }
118
119 pub fn start_relay(&self, kernel: &Kernel, booted_receiver: LockupDetectorReceiver<bool>) {
120 let this = self.inner.clone();
121 let closure = move |_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: LockDepMutex<bool, BootedLock>,
165 sender: Sender<bool>,
166}
167
168impl File {
169 fn new(sender: Sender<bool>) -> Arc<Self> {
170 Arc::new(Self { booted: false.into(), 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 _file: &FileObject,
183 _current_task: &CurrentTask,
184 offset: usize,
185 data: &mut dyn OutputBuffer,
186 ) -> Result<usize, Errno> {
187 debug_assert!(offset == 0);
188 let booted = self.booted.lock().to_owned();
189 data.write_all(booted.as_bytes())
190 }
191
192 fn write(
193 &self,
194 _file: &FileObject,
195 _current_task: &CurrentTask,
196 _offset: usize,
197 data: &mut dyn InputBuffer,
198 ) -> Result<usize, Errno> {
199 let content = data.read_all()?;
200 let booted = match &*content {
201 b"0" | b"0\n" => false,
202 b"1" | b"1\n" => true,
203 _ => {
204 log_error!("Invalid booted value - must be 0 or 1");
205 return error!(EINVAL);
206 }
207 };
208 *self.booted.lock() = booted;
209 if let Err(e) = self.sender.send(booted) {
210 log_error!("unable to send recent booted state to device relay: {:?}", e);
211 }
212 Ok(content.len())
213 }
214}
215
216impl DeviceOps for BootedDevice {
217 fn open(
218 &self,
219 _current_task: &CurrentTask,
220 _devt: DeviceId,
221 _node: &NamespaceNode,
222 _flags: OpenFlags,
223 ) -> Result<Box<dyn FileOps>, Errno> {
224 let file = self.inner.file.clone();
225 Ok(Box::new(file))
226 }
227}