starnix_core/syscalls/
reboot.rs1use bstr::ByteSlice;
6use fuchsia_component::client::connect_to_protocol_sync;
7use linux_uapi::{
8 LINUX_REBOOT_CMD_CAD_OFF, LINUX_REBOOT_CMD_CAD_ON, LINUX_REBOOT_CMD_HALT,
9 LINUX_REBOOT_CMD_KEXEC, LINUX_REBOOT_CMD_POWER_OFF, LINUX_REBOOT_CMD_RESTART,
10 LINUX_REBOOT_CMD_RESTART2, LINUX_REBOOT_CMD_SW_SUSPEND,
11};
12use starnix_logging::{log_debug, log_info, log_warn, track_stub};
13use starnix_sync::{InterruptibleEvent, Locked, Unlocked};
14use starnix_uapi::auth::CAP_SYS_BOOT;
15use starnix_uapi::errors::Errno;
16use starnix_uapi::user_address::{UserAddress, UserCString};
17use starnix_uapi::{
18 LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_MAGIC2A, LINUX_REBOOT_MAGIC2B,
19 LINUX_REBOOT_MAGIC2C, errno, error,
20};
21use {fidl_fuchsia_hardware_power_statecontrol as fpower, fidl_fuchsia_recovery as frecovery};
22
23use crate::device::android::bootloader_message_store::{
24 AndroidBootloaderMessageStore, BootloaderMessage,
25};
26use crate::mm::MemoryAccessorExt;
27use crate::security;
28use crate::task::{CurrentTask, Kernel};
29use crate::vfs::FsString;
30
31#[track_caller]
32fn panic_or_error(kernel: &Kernel, errno: Errno) -> Result<(), Errno> {
33 if kernel.features.error_on_failed_reboot {
34 return Err(errno);
35 }
36 panic!("Fatal: {errno:?}");
37}
38
39pub fn sys_reboot(
40 _locked: &mut Locked<Unlocked>,
41 current_task: &CurrentTask,
42 magic: u32,
43 magic2: u32,
44 cmd: u32,
45 arg: UserAddress,
46) -> Result<(), Errno> {
47 if magic != LINUX_REBOOT_MAGIC1
48 || (magic2 != LINUX_REBOOT_MAGIC2
49 && magic2 != LINUX_REBOOT_MAGIC2A
50 && magic2 != LINUX_REBOOT_MAGIC2B
51 && magic2 != LINUX_REBOOT_MAGIC2C)
52 {
53 return error!(EINVAL);
54 }
55 security::check_task_capable(current_task, CAP_SYS_BOOT)?;
56
57 let arg_bytes = if matches!(cmd, LINUX_REBOOT_CMD_RESTART2) {
58 const MAX_REBOOT_ARG_LEN: usize = 256;
60 current_task
61 .read_c_string_to_vec(UserCString::new(current_task, arg), MAX_REBOOT_ARG_LEN)?
62 } else {
63 FsString::default()
64 };
65
66 if current_task.kernel().is_shutting_down() {
67 log_debug!("Ignoring reboot() and parking caller, already shutting down.");
68 let event = InterruptibleEvent::new();
69 return current_task.block_until(event.begin_wait(), zx::MonotonicInstant::INFINITE);
70 }
71
72 let proxy = connect_to_protocol_sync::<fpower::AdminMarker>().or_else(|_| error!(EINVAL))?;
73
74 match cmd {
75 LINUX_REBOOT_CMD_CAD_ON | LINUX_REBOOT_CMD_CAD_OFF => Ok(()),
77
78 LINUX_REBOOT_CMD_KEXEC => error!(EINVAL),
80
81 LINUX_REBOOT_CMD_SW_SUSPEND => error!(EINVAL),
83
84 LINUX_REBOOT_CMD_HALT | LINUX_REBOOT_CMD_POWER_OFF => {
85 log_info!("Powering off");
86 let options = fpower::ShutdownOptions {
87 action: Some(fpower::ShutdownAction::Poweroff),
88 ..Default::default()
89 };
90
91 match proxy.shutdown(&options, zx::MonotonicInstant::INFINITE) {
92 Ok(_) => {
93 zx::MonotonicInstant::INFINITE.sleep();
95 }
96 Err(e) => {
97 return panic_or_error(
98 current_task.kernel(),
99 errno!(EINVAL, format!("Failed to power off, status: {e}")),
100 );
101 }
102 }
103 Ok(())
104 }
105
106 LINUX_REBOOT_CMD_RESTART | LINUX_REBOOT_CMD_RESTART2 => {
107 let reboot_args: Vec<_> = arg_bytes.split_str(b",").collect();
108
109 let reboot_result = if reboot_args.contains(&&b"bootloader"[..]) {
110 log_info!("Rebooting to bootloader");
111 let options = fpower::ShutdownOptions {
112 action: Some(fpower::ShutdownAction::RebootToBootloader),
113 ..Default::default()
114 };
115 proxy.shutdown(&options, zx::MonotonicInstant::INFINITE)
116 } else if reboot_args.contains(&&b"recovery"[..]) {
117 if let Some(store) =
120 current_task.kernel().expando.peek::<AndroidBootloaderMessageStore>()
121 {
122 match store.read_bootloader_message() {
123 Ok(BootloaderMessage::BootRecovery(args)) => {
124 if args.iter().any(|arg| arg == "--wipe_data") {
125 let factory_reset_proxy =
126 connect_to_protocol_sync::<frecovery::FactoryResetMarker>()
127 .or_else(|_| error!(EINVAL))?;
128 log_info!("Initiating factory data reset...");
130 match factory_reset_proxy.reset(zx::MonotonicInstant::INFINITE) {
131 Ok(_) => {
132 zx::MonotonicInstant::INFINITE.sleep();
134 }
135 Err(e) => {
136 return panic_or_error(
137 current_task.kernel(),
138 errno!(
139 EINVAL,
140 format!("Failed to reboot for FDR, status: {e}")
141 ),
142 );
143 }
144 }
145 }
146 }
147 Ok(_) => log_info!("Boot message not recognized!"),
149 Err(e) => log_warn!("Failed to read boot message: {e}"),
150 }
151 }
152 log_info!("Rebooting to recovery...");
153 let options = fpower::ShutdownOptions {
154 action: Some(fpower::ShutdownAction::RebootToRecovery),
155 ..Default::default()
156 };
157 proxy.shutdown(&options, zx::MonotonicInstant::INFINITE)
158 } else {
159 let shutdown_reason =
163 if let Some(arg) = reboot_args.iter().find(|arg| arg.ends_with(b"-failed")) {
164 let process_name =
165 String::from_utf8_lossy(arg.strip_suffix(b"-failed").unwrap());
166 log_info!("Android critical process '{}' failed, rebooting", process_name);
170 fpower::ShutdownReason::AndroidCriticalProcessFailure
171 } else if reboot_args.contains(&&b"ota_update"[..])
172 || reboot_args.contains(&&b"System update during setup"[..])
173 {
174 fpower::ShutdownReason::SystemUpdate
175 } else if reboot_args.contains(&&b"shell"[..]) {
176 fpower::ShutdownReason::DeveloperRequest
177 } else if reboot_args.contains(&&b"RescueParty"[..])
178 || reboot_args.contains(&&b"rescueparty"[..])
179 {
180 fpower::ShutdownReason::AndroidRescueParty
181 } else if reboot_args == [b""] || reboot_args.contains(&&b"userrequested"[..])
183 {
184 fpower::ShutdownReason::UserRequest
185 } else {
186 log_warn!("Unknown reboot args: {arg_bytes:?}");
187 track_stub!(
188 TODO("https://fxbug.dev/322874610"),
189 "unknown reboot args, see logs for strings"
190 );
191 fpower::ShutdownReason::AndroidUnexpectedReason
192 };
193
194 log_info!("Rebooting... reason: {:?}", shutdown_reason);
195 proxy.shutdown(
196 &fpower::ShutdownOptions {
197 action: Some(fpower::ShutdownAction::Reboot),
198 reasons: Some(vec![shutdown_reason]),
199 ..Default::default()
200 },
201 zx::MonotonicInstant::INFINITE,
202 )
203 };
204
205 match reboot_result {
206 Ok(Ok(())) => {
207 zx::MonotonicInstant::INFINITE.sleep();
209 }
210 Ok(Err(e)) => {
211 return panic_or_error(
212 current_task.kernel(),
213 errno!(
214 EINVAL,
215 format!("Failed to reboot, status: {}", zx::Status::from_raw(e))
216 ),
217 );
218 }
219 Err(e) => {
220 return panic_or_error(
221 current_task.kernel(),
222 errno!(EINVAL, format!("Failed to reboot, FIDL error: {e}")),
223 );
224 }
225 }
226 Ok(())
227 }
228
229 _ => error!(EINVAL),
230 }
231}
232
233#[cfg(target_arch = "aarch64")]
234mod arch32 {
235 pub use super::sys_reboot as sys_arch32_reboot;
236}
237
238#[cfg(target_arch = "aarch64")]
239pub use arch32::*;