cortexm/semihosting.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2026.
4
5//! Semihosting support for ARM Cortex M Architectures.
6
7/// Issue an ARM semihosting call.
8///
9/// `operation` is the semihosting operation number (e.g. `0x18` for
10/// `SYS_EXIT`) and `parameter` is its operation-specific argument.
11///
12/// Not exposed outside this module: it's a general, unrestricted semihosting
13/// interface. External callers should use specific, narrow operations (e.g.
14/// [`terminate`]) that encode specific commands.
15///
16/// # Safety
17///
18/// Only meaningful when running under a semihosting host (e.g. QEMU started
19/// with `-semihosting`, or an attached debug probe). With no host, the `bkpt`
20/// raises a debug exception instead, escalating to a `HardFault` when nothing
21/// is configured to take it.
22///
23/// The exact safety requirements depend on `operation`. This method should
24/// not be called directly with raw parameters. Instead, this module wraps
25/// calls with fixed `operation`s that specify their safety requirements.
26// Reference documentation for Cortex-M series arches semihosting:
27// https://support.arm.com/documentation/dui0471/e/semihosting/the-semihosting-interface
28#[cfg(all(target_arch = "arm", target_os = "none"))]
29#[inline(always)]
30#[doc(hidden)]
31unsafe fn semihost_command(operation: u32, parameter: u32) -> u32 {
32 use core::arch::asm;
33 let result;
34
35 // SAFETY: r0/r1 are set to `operation`/`parameter`, per the ABI ARM
36 // semihosting defines (ARM's "Semihosting for AArch32 and AArch64"
37 // specification); the caller is responsible for those being valid for
38 // the chosen `operation`, per this function's own `# Safety` doc above.
39 // - OUTPUTS: r0 is overwritten with the semihosting call's result.
40 // - Options set:
41 // - nostack: This does not use the stack.
42 // - Options not set:
43 // - nomem: not guaranteed in general -- some operations (e.g.
44 // `SYS_WRITEC`) dereference `parameter` as a pointer.
45 // - pure, readonly: not applicable, as above.
46 // - preserves_flags: not documented by the semihosting spec.
47 // - noreturn: we do fall through -- even for `SYS_EXIT`, since a
48 // semihosting host can choose to resume the target.
49 // - att_syntax: not on arm.
50 // - raw: not required.
51 unsafe {
52 asm!(
53 "bkpt #0xAB",
54 inout("r0") operation => result,
55 in("r1") parameter,
56 options(nostack),
57 );
58 }
59 result
60}
61
62#[cfg(not(all(target_arch = "arm", target_os = "none")))]
63unsafe fn semihost_command(_operation: u32, _parameter: u32) -> u32 {
64 unimplemented!()
65}
66
67/// Reason codes for SysExit under semihosting.
68///
69/// See ARM documentation for details:
70/// <https://support.arm.com/documentation/dui0471/e/semihosting/angel-swireason-reportexception--0x18-?lang=en>
71#[repr(u32)]
72#[allow(non_camel_case_types)] // Match ARM Specs
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum SysexitReason {
75 // Hardware events
76 ADP_Stopped_BranchThroughZero = 0x20000,
77 ADP_Stopped_UndefinedInstr = 0x20001,
78 ADP_Stopped_SoftwareInterrupt = 0x20002,
79 ADP_Stopped_PrefetchAbort = 0x20003,
80 ADP_Stopped_DataAbort = 0x20004,
81 ADP_Stopped_AddressException = 0x20005,
82 ADP_Stopped_IRQ = 0x20006,
83 ADP_Stopped_FIQ = 0x20007,
84 // Software events
85 ADP_Stopped_BreakPoint = 0x20020,
86 ADP_Stopped_WatchPoint = 0x20021,
87 ADP_Stopped_StepComplete = 0x20022,
88 ADP_Stopped_RunTimeErrorUnknown = 0x20023,
89 ADP_Stopped_InternalError = 0x20024,
90 ADP_Stopped_UserInterruption = 0x20025,
91 ADP_Stopped_ApplicationExit = 0x20026,
92 ADP_Stopped_StackOverflow = 0x20027,
93 ADP_Stopped_DivisionByZero = 0x20028,
94 ADP_Stopped_OSSpecific = 0x20029,
95}
96
97/// Ask a semihosting host to terminate execution.
98///
99/// Issues ARM semihosting's `SYS_EXIT` (`0x18`) [aka,
100/// `angel_SWIreason_ReportException (0x18)` on older cortex-m arches,
101/// but they have the same semantics].
102///
103/// This **may not actually halt execution**. A debugger *can* tell semihosting
104/// to resume the target. In that case, this will stick at the terminal `loop`.
105///
106/// # Safety
107///
108/// This nominally halts execution, thus the caller should have the authority to
109/// halt execution. This *should* only be called on under semihosting (e.g. on
110/// a QEMU board); on other targets the `BKPT` will escalate to a `HardFault`.
111// TODO: This, and other semihosting commands, should likely take a capability
112// rather than being `unsafe`.
113#[inline(always)]
114pub unsafe fn terminate(reason: SysexitReason) -> ! {
115 const SYS_EXIT: u32 = 0x18;
116 // SAFETY: SYS_EXIT is a valid `operation` for semihosting. With SYS_EXIT,
117 // `parameter` is interpreted as a plain integer, which are constrained to
118 // valid values for SYS_EXIT by the enum type parameter.
119 unsafe {
120 semihost_command(SYS_EXIT, reason as u32);
121 }
122
123 // Ensure `-> !` if semihosting does something funny.
124 loop {
125 crate::support::wfi(&kernel::create_capability!(
126 kernel::capabilities::CpuStateControlCapability
127 ));
128 }
129}