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}