riscv/thread_id.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 2025.
4
5//! RISC-V support for getting the running thread ID.
6
7use kernel::platform::chip::ThreadIdProvider;
8
9/// Implement [`ThreadIdProvider`] for RISC-V.
10pub enum RiscvThreadIdProvider {}
11
12// # Safety
13//
14// By implementing [`ThreadIdProvider`] we are guaranteeing that we correctly
15// return the thread ID. On single-core platforms the thread ID only depends on
16// whether execution is in an interrupt service routine or not, which is what
17// this implementation checks for.
18unsafe impl ThreadIdProvider for RiscvThreadIdProvider {
19 /// Return the current thread ID, computed using the `mhartid` (hardware thread
20 /// ID), and a flag indicating whether the current hart is currently in a trap
21 /// handler context.
22 fn running_thread_id() -> usize {
23 // Mock implementation for non-rv32 target builds:
24 #[cfg(not(all(target_arch = "riscv32", target_os = "none")))]
25 {
26 unimplemented!()
27 }
28
29 // Proper rv32i-specific implementation:
30 #[cfg(all(target_arch = "riscv32", target_os = "none"))]
31 {
32 let hartid: usize;
33 let trap_handler_active_addr: *mut usize;
34
35 // Safety:
36 //
37 // This does not read any memory by itself, it merely loads a symbol
38 // address and reads the `mhartid` CSR:
39 unsafe {
40 core::arch::asm!(
41 // Determine the hart id and save in the appropriate output
42 // register:
43 "csrr {hartid_reg}, mhartid",
44 // Load the `_trap_handler_active` symbol address:
45 "la {trap_handler_active_addr_reg}, _trap_handler_active",
46 hartid_reg = out(reg) hartid,
47 trap_handler_active_addr_reg = out(reg) trap_handler_active_addr,
48 options(
49 // The assembly code has no side effects, must eventually
50 // return, and its outputs depend only on its direct inputs
51 // (i.e. the values themselves, not what they point to) or
52 // values read from memory (unless the nomem options is also
53 // set).
54 pure,
55 // The assembly code does not read from or write to any memory
56 // accessible outside of the assembly code.
57 nomem,
58 // The assembly code does not modify the flags register (for
59 // RISC-V: `fflags`, `vtype`, `vl`, or `vcsr`).
60 preserves_flags,
61 // The assembly code does not push data to the stack, or write
62 // to the stack red-zone (if supported by the target).
63 nostack,
64 ),
65 );
66 }
67
68 // Load the hart's trap_handler_active value.
69 //
70 // Safety:
71 //
72 // `hartid` * core::mem::size_of::<usize>() must fit into an `isize`. By
73 // allocating the `_trap_handler_active` array, the chip crate ensures
74 // that this array can fit all hart IDs for all harts on the target
75 // machine. The maximum size of any Rust allocation is `isize::MAX`
76 // bytes, and as such, by allocating this arrary in the first place, the
77 // chip crate maintains that indexing into it with `hartid` must stay in
78 // this object, and an index of `hartid` elements will not produce an
79 // offset larger than `isize::MAX` bytes.
80 let hart_trap_handler_active =
81 unsafe { core::ptr::read(trap_handler_active_addr.add(hartid)) };
82
83 // Determine the thread ID as a combination of the hart id, and whether
84 // it is currently in a trap handler context:
85 hartid.overflowing_shl(1).0 | ((hart_trap_handler_active != 0) as usize)
86 }
87 }
88}