Skip to main content

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}