1#![no_std]
8#![allow(unsafe_op_in_unsafe_fn)]
9#![allow(unused_crate_dependencies)]
10#![cfg(console_enabled)]
11
12use core::ffi::{c_char, c_int, c_void};
13use core::sync::atomic::{AtomicBool, Ordering};
14use zx_status::Status;
15
16macro_rules! zx_status {
18 ($status:expr) => {
19 $status.into_raw()
20 };
21}
22
23pub const CMD_AVAIL_NORMAL: u8 = 1 << 0;
24pub const CMD_AVAIL_PANIC: u8 = 1 << 1;
25pub const CMD_AVAIL_ALWAYS: u8 = CMD_AVAIL_NORMAL | CMD_AVAIL_PANIC;
26
27pub const CMD_FLAG_PANIC: u32 = 1 << 0;
29
30const BOOT_TEST_SUCCESS_STRING: &core::ffi::CStr = unsafe {
31 core::ffi::CStr::from_bytes_with_nul_unchecked(
32 concat!(env!("BOOT_TEST_SUCCESS_STRING"), "\0").as_bytes(),
33 )
34};
35
36pub static ECHO: AtomicBool = AtomicBool::new(true);
37pub static EXIT_CONSOLE: AtomicBool = AtomicBool::new(false);
38
39#[repr(C)]
40#[derive(Clone, Copy)]
41pub struct CmdArgs {
42 pub arg_str: *const c_char,
43 pub arg_uint: core::ffi::c_ulong,
44 pub arg_ptr: *mut c_void,
45 pub arg_int: core::ffi::c_long,
46 pub arg_bool: bool,
47}
48
49pub type CmdCallback = unsafe extern "C" fn(argc: c_int, argv: *const CmdArgs, flags: u32) -> c_int;
50
51#[repr(C)]
52pub struct Cmd {
53 pub cmd_str: *const c_char,
54 pub help_str: *const c_char,
55 pub cmd_callback: CmdCallback,
56 pub availability_mask: u8,
57}
58
59unsafe impl Sync for Cmd {}
62
63zr::static_assert!(core::mem::size_of::<CmdArgs>() == 40);
65zr::static_assert!(core::mem::align_of::<CmdArgs>() == 8);
66zr::static_assert!(core::mem::size_of::<Cmd>() == 32);
67zr::static_assert!(core::mem::align_of::<Cmd>() == 8);
68
69#[cfg(console_enabled)]
70#[macro_export]
71macro_rules! commands_section {
72 () => {
73 ".data.rel.ro.commands"
74 };
75}
76
77#[macro_export]
78macro_rules! static_command {
79 ($var_name:ident, $cmd:expr, $help:expr, $func:expr, $mask:expr) => {
80 #[cfg(console_enabled)]
81 #[used]
82 #[unsafe(link_section = $crate::commands_section!())]
83 pub static $var_name: $crate::Cmd = $crate::Cmd {
84 cmd_str: $cmd,
85 help_str: $help,
86 cmd_callback: $func,
87 availability_mask: $mask,
88 };
89 };
90}
91
92unsafe extern "C" {
94 fn printf(format: *const c_char, ...) -> c_int;
96 fn cpp_console_get_lastresult() -> c_int;
97}
98
99static_command!(CMD_ECHO, c"echo".as_ptr(), core::ptr::null(), cmd_echo, CMD_AVAIL_ALWAYS);
101
102static_command!(
103 CMD_EXIT,
104 c"exit".as_ptr(),
105 c"exit the command processor".as_ptr(),
106 cmd_exit,
107 CMD_AVAIL_NORMAL
108);
109
110static_command!(
111 CMD_TEST,
112 c"test".as_ptr(),
113 c"test the command processor".as_ptr(),
114 cmd_test,
115 CMD_AVAIL_ALWAYS
116);
117
118static_command!(
119 CMD_GRACEFUL_SHUTDOWN,
120 c"graceful-shutdown".as_ptr(),
121 c"shut the system down gracefully".as_ptr(),
122 cmd_graceful_shutdown,
123 CMD_AVAIL_ALWAYS
124);
125
126static_command!(
127 CMD_BOOT_TEST_SUCCESS,
128 c"boot-test-success".as_ptr(),
129 c"report boot-test success".as_ptr(),
130 cmd_boot_test_success,
131 CMD_AVAIL_ALWAYS
132);
133
134static_command!(
135 CMD_AND,
136 c"and".as_ptr(),
137 c"execute command if last command succeeded".as_ptr(),
138 cmd_and,
139 CMD_AVAIL_ALWAYS
140);
141
142static_command!(
143 CMD_REPEAT,
144 c"repeat".as_ptr(),
145 c"execute command in a loop for N loops or until error".as_ptr(),
146 cmd_repeat,
147 CMD_AVAIL_ALWAYS
148);
149
150static_command!(CMD_HELP, c"help".as_ptr(), c"this list".as_ptr(), cmd_help, CMD_AVAIL_ALWAYS);
151
152unsafe extern "C" fn cmd_echo(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
154 if argc > 1 {
155 let args = core::slice::from_raw_parts(argv, argc as usize);
156 ECHO.store(args[1].arg_bool, Ordering::Relaxed);
157 }
158 zx_status!(Status::OK)
159}
160
161unsafe extern "C" fn cmd_exit(_argc: c_int, _argv: *const CmdArgs, _flags: u32) -> c_int {
162 rust_console_set_exit(true);
163 zx_status!(Status::OK)
164}
165
166unsafe extern "C" fn cmd_test(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
167 let args = core::slice::from_raw_parts(argv, argc as usize);
168 printf(c"argc %d, argv %p\n".as_ptr(), argc, argv);
169 for (i, arg) in args.iter().enumerate() {
170 printf(
171 c"\t%d: str '%s', int %ld, uint %#lx, ptr %p, bool %d\n".as_ptr(),
172 i as c_int,
173 arg.arg_str,
174 arg.arg_int,
175 arg.arg_uint,
176 arg.arg_ptr,
177 arg.arg_bool as c_int,
178 );
179 }
180 zx_status!(Status::OK)
181}
182
183unsafe extern "C" fn cmd_graceful_shutdown(
184 _argc: c_int,
185 _argv: *const CmdArgs,
186 _flags: u32,
187) -> c_int {
188 printf(c"%c** Performing graceful shutdown from kernel shell... ***\n".as_ptr(), b'*' as c_int);
191 const ZX_SEC_10: i64 = 10 * 1_000_000_000;
192 let dlog_deadline = platform_rs::current_mono_time() + ZX_SEC_10;
193 if let Err(status) = debuglog_rs::dlog_shutdown(dlog_deadline) {
194 printf(c"debuglog shutdown failed: %d\n".as_ptr(), status.into_raw());
195 }
197 platform_rs::power::platform_halt(
199 platform_rs::power::PlatformHaltAction::Shutdown,
200 platform_rs::power::ZirconCrashReason::NoCrash,
201 );
202}
203
204unsafe extern "C" fn cmd_boot_test_success(
205 _argc: c_int,
206 _argv: *const CmdArgs,
207 _flags: u32,
208) -> c_int {
209 let last = cpp_console_get_lastresult();
210 printf(c"*** Last script command result: %d ***\n".as_ptr(), last);
211 if last == 0 {
212 printf(c"%s%c".as_ptr(), BOOT_TEST_SUCCESS_STRING.as_ptr(), b'\n' as c_int);
213 }
214 last
215}
216
217unsafe fn get_commands() -> &'static [Cmd] {
218 unsafe extern "C" {
219 #[link_name = "__start_commands"]
220 static __start_commands: Cmd;
221 #[link_name = "__stop_commands"]
222 static __stop_commands: Cmd;
223 }
224 let start = &__start_commands as *const Cmd;
225 let stop = &__stop_commands as *const Cmd;
226 let count = (stop as usize - start as usize) / core::mem::size_of::<Cmd>();
227 unsafe { core::slice::from_raw_parts(start, count) }
228}
229
230unsafe fn match_command(name: *const c_char, availability_mask: u8) -> Option<&'static Cmd> {
231 let commands = unsafe { get_commands() };
232 let name_cstr = unsafe { core::ffi::CStr::from_ptr(name) };
233 for cmd in commands {
234 if (availability_mask & cmd.availability_mask) != 0 {
235 let cmd_str_cstr = unsafe { core::ffi::CStr::from_ptr(cmd.cmd_str) };
236 if cmd_str_cstr == name_cstr {
237 return Some(cmd);
238 }
239 }
240 }
241 None
242}
243
244unsafe extern "C" fn cmd_and(argc: c_int, argv: *const CmdArgs, flags: u32) -> c_int {
245 if argc < 2 {
246 printf(c"Usage: and COMMAND...%c".as_ptr(), b'\n' as c_int);
247 return zx_status!(Status::INVALID_ARGS);
248 }
249
250 let last = cpp_console_get_lastresult();
251 if last != 0 {
252 return last;
253 }
254
255 let args = core::slice::from_raw_parts(argv, argc as usize);
256 let cmd = match match_command(args[1].arg_str, CMD_AVAIL_NORMAL) {
257 Some(cmd) => cmd,
258 None => {
259 printf(c"command \"%s\" not found%c".as_ptr(), args[1].arg_str, b'\n' as c_int);
260 return zx_status!(Status::NOT_FOUND);
261 }
262 };
263
264 (cmd.cmd_callback)(argc - 1, argv.add(1), flags)
265}
266
267unsafe extern "C" fn cmd_repeat(argc: c_int, argv: *const CmdArgs, flags: u32) -> c_int {
268 const MIN_ARGS: c_int = 3;
269 if argc < MIN_ARGS {
270 printf(c"Usage: repeat <iterations | -1> COMMAND...%c".as_ptr(), b'\n' as c_int);
271 return zx_status!(Status::INVALID_ARGS);
272 }
273
274 let args = core::slice::from_raw_parts(argv, argc as usize);
275 let cmd = match match_command(args[2].arg_str, CMD_AVAIL_NORMAL) {
276 Some(cmd) => cmd,
277 None => {
278 printf(c"command \"%s\" not found%c".as_ptr(), args[2].arg_str, b'\n' as c_int);
279 return zx_status!(Status::NOT_FOUND);
280 }
281 };
282
283 let iterations = if args[1].arg_int >= 0 { args[1].arg_uint as usize } else { usize::MAX };
285 for i in 0..iterations {
286 if iterations == usize::MAX {
287 printf(c"repeat (%zu): %s".as_ptr(), i + 1, args[2].arg_str);
288 } else {
289 printf(c"repeat (%zu/%zu): %s".as_ptr(), i + 1, iterations, args[2].arg_str);
290 }
291 for arg in MIN_ARGS..argc {
292 printf(c" %s".as_ptr(), args[arg as usize].arg_str);
293 }
294 printf(c"%c%s".as_ptr(), b'\n' as c_int, c"".as_ptr());
297
298 let err = (cmd.cmd_callback)(argc - 2, argv.add(2), flags);
299 if err != zx_status!(Status::OK) {
300 printf(c"stopping repeat due to nonzero status %d%c".as_ptr(), err, b'\n' as c_int);
301 return err;
302 }
303 }
304
305 zx_status!(Status::OK)
306}
307
308unsafe extern "C" fn cmd_help(_argc: c_int, _argv: *const CmdArgs, flags: u32) -> c_int {
309 let commands = unsafe { get_commands() };
310 let count = commands.len();
311
312 let availability_mask =
314 if (flags & CMD_FLAG_PANIC) != 0 { CMD_AVAIL_PANIC } else { CMD_AVAIL_NORMAL };
315
316 unsafe {
317 printf(c"command list:%c".as_ptr(), b'\n' as c_int);
318 }
319
320 if (flags & CMD_FLAG_PANIC) != 0 {
323 for cmd in commands {
324 if (availability_mask & cmd.availability_mask) != 0 {
325 if !cmd.help_str.is_null() {
326 unsafe {
327 printf(
328 c"\t%-16s: %s%c".as_ptr(),
329 cmd.cmd_str,
330 cmd.help_str,
331 b'\n' as c_int,
332 );
333 }
334 }
335 }
336 }
337 } else {
338 let mut ptrs_slice = match kalloc::Box::<[*const Cmd]>::try_new_zeroed_slice(count) {
339 Ok(s) => s,
340 Err(_) => return zx_status!(Status::NO_MEMORY),
341 };
342 for (i, cmd) in commands.iter().enumerate() {
343 ptrs_slice[i] = cmd;
344 }
345
346 ptrs_slice.sort_unstable_by(|&a, &b| {
347 let a_str = unsafe { core::ffi::CStr::from_ptr((*a).cmd_str) };
348 let b_str = unsafe { core::ffi::CStr::from_ptr((*b).cmd_str) };
349 a_str.cmp(b_str)
350 });
351
352 for &cmd_ptr in ptrs_slice.iter() {
353 let cmd = unsafe { &*cmd_ptr };
354 if (availability_mask & cmd.availability_mask) != 0 {
355 if !cmd.help_str.is_null() {
356 unsafe {
357 printf(
358 c"\t%-16s: %s%c".as_ptr(),
359 cmd.cmd_str,
360 cmd.help_str,
361 b'\n' as c_int,
362 );
363 }
364 }
365 }
366 }
367 }
368
369 zx_status!(Status::OK)
370}
371
372#[unsafe(no_mangle)]
374pub extern "C" fn rust_console_get_echo() -> bool {
375 ECHO.load(Ordering::Relaxed)
376}
377
378#[unsafe(no_mangle)]
379pub extern "C" fn rust_console_get_exit() -> bool {
380 EXIT_CONSOLE.load(Ordering::Relaxed)
381}
382
383#[unsafe(no_mangle)]
384pub extern "C" fn rust_console_set_exit(val: bool) {
385 EXIT_CONSOLE.store(val, Ordering::Relaxed);
386}
387
388#[unsafe(no_mangle)]
392pub unsafe extern "C" fn rust_console_match_command(
393 name: *const c_char,
394 availability_mask: u8,
395) -> *const Cmd {
396 match match_command(name, availability_mask) {
397 Some(cmd) => cmd,
398 None => core::ptr::null(),
399 }
400}