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