RISC-V Implementation
Relevant source files
This document details the RISC-V architecture-specific implementation of signal handling in the axsignal
crate. It covers the signal trampoline mechanism, context saving/restoring operations, and the data structures specific to RISC-V processors. For information about other architectures, see the corresponding implementation pages: x86_64 Implementation, ARM64 Implementation, and LoongArch64 Implementation.
RISC-V Signal Handling Architecture
The RISC-V signal handling implementation provides the architecture-specific components needed to save CPU state before executing a signal handler and to restore that state afterward. It consists of two main components:
- A signal trampoline implementation in assembly language
- Data structures for storing CPU context
The implementation supports both 32-bit (riscv32) and 64-bit (riscv64) RISC-V architectures through a unified module.
flowchart TD subgraph subGraph1["Integration Points"] TSM["ThreadSignalManager"] TF["TrapFrame"] end subgraph subGraph0["Signal Handling System"] ARCH["arch/mod.rs"] RISCV["arch/riscv.rs"] TRAMP["signal_trampoline"] MCTX["MContext"] UCTX["UContext"] end ARCH --> RISCV MCTX --> TF MCTX --> UCTX RISCV --> MCTX RISCV --> TRAMP RISCV --> UCTX TF --> MCTX TSM --> TRAMP TSM --> UCTX
Sources: src/arch/mod.rs(L1 - L26) src/arch/riscv.rs(L1 - L64)
Signal Trampoline Implementation
The signal trampoline is a small piece of assembly code that serves as the bridge between signal handler execution and returning to normal execution. In RISC-V, it's implemented as a simple syscall wrapper that invokes syscall number 139 (sigreturn).
flowchart TD subgraph subGraph0["Signal Trampoline Flow"] SH["Signal Handler"] ST["signal_trampoline"] SC["Syscall 139 (sigreturn)"] KR["Kernel Return Processing"] RT["Return to Normal Execution"] end KR --> RT SC --> KR SH --> ST ST --> SC
The trampoline is defined in assembly and aligned to a 4096-byte page boundary:
flowchart TD ASM["Assembly Code"] TRAM["signal_trampoline"] ECALL["syscall 139 (sigreturn)"] ASM --> TRAM TRAM --> ECALL
Sources: src/arch/riscv.rs(L5 - L16) src/arch/mod.rs(L19 - L25)
Context Data Structures
The RISC-V implementation defines two key structures for context management:
MContext Structure
MContext
stores the essential machine context that needs to be saved and restored during signal handling.
classDiagram class MContext { +usize pc -GeneralRegisters regs -usize[66] fpstate +new(TrapFrame) MContext +restore(TrapFrame) void } class TrapFrame { +usize sepc +GeneralRegisters regs } MContext --> TrapFrame : converts from/to
The structure contains:
pc
: Program counter (stored assepc
in the trap frame)regs
: General-purpose registers from theGeneralRegisters
structurefpstate
: Floating-point state (66 words of storage)
UContext Structure
UContext
is a higher-level structure that encapsulates MContext
along with additional signal-related information.
The structure contains:
flags
: Context flags (not currently used, set to 0)link
: Link to another context (not currently used, set to 0)stack
: Signal stack information (typeSignalStack
)sigmask
: Signal mask (typeSignalSet
)__unused
: Padding to ensure proper structure alignmentmcontext
: The machine context described above
Sources: src/arch/riscv.rs(L18 - L63)
Context Operations
The RISC-V implementation provides two primary operations on context:
- Context Creation: Converting from a trap frame to an
MContext
/UContext
- Context Restoration: Restoring a trap frame from an
MContext
Context Creation
When a signal is delivered, the current CPU state (represented by a TrapFrame
) is saved into an MContext
and then into a UContext
.
sequenceDiagram participant ThreadSignalManager as ThreadSignalManager participant TrapFrame as TrapFrame participant MContext as MContext participant UContext as UContext ThreadSignalManager ->> UContext: new(tf, sigmask) UContext ->> MContext: new(tf) MContext ->> TrapFrame: read sepc to pc MContext ->> TrapFrame: copy regs MContext ->> MContext: initialize fpstate to zeros UContext ->> UContext: initialize other fields
Context Restoration
After signal handler execution, the saved context is restored to continue normal execution.
sequenceDiagram participant ThreadSignalManager as ThreadSignalManager participant TrapFrame as TrapFrame participant MContext as MContext ThreadSignalManager ->> MContext: restore(tf) MContext ->> TrapFrame: write pc to sepc MContext ->> TrapFrame: copy regs
Sources: src/arch/riscv.rs(L27 - L38) src/arch/riscv.rs(L53 - L62)
Integration with Signal Handling System
The RISC-V implementation integrates with the rest of the signal handling system through the architecture abstraction layer defined in arch/mod.rs
. This layer selects the appropriate architecture-specific implementation at compile time based on the target architecture.
flowchart TD subgraph subGraph1["Signal Processing"] TSM["ThreadSignalManager"] TADDR["signal_trampoline_address()"] TRAMP["signal_trampoline"] SH["Signal Handler"] ARCH["arch/mod.rs"] X86["x86_64.rs"] RISCV["riscv.rs"] ARM["aarch64.rs"] end subgraph subGraph0["Architecture Selection"] TSM["ThreadSignalManager"] TADDR["signal_trampoline_address()"] SH["Signal Handler"] ARCH["arch/mod.rs"] X86["x86_64.rs"] RISCV["riscv.rs"] ARM["aarch64.rs"] LOONG["loongarch64.rs"] end ARCH --> ARM ARCH --> LOONG ARCH --> RISCV ARCH --> X86 SH --> TRAMP TADDR --> TRAMP TSM --> SH TSM --> TADDR
Key integration points:
- The
signal_trampoline_address()
function provides the address of the architecture-specific trampoline implementation ThreadSignalManager
uses the context structures to save and restore CPU state
Sources: src/arch/mod.rs(L1 - L26)
Technical Details
Signal Trampoline Memory Layout
The signal trampoline is carefully aligned to a 4096-byte page boundary and padded to fill an entire page. This is important for security and memory protection:
.section .text
.balign 4096
.global signal_trampoline
signal_trampoline:
li a7, 139 # Load syscall number 139 (sigreturn) into a7
ecall # Execute syscall
.fill 4096 - (. - signal_trampoline), 1, 0 # Fill remainder of page with zeros
The trampoline simply loads the sigreturn syscall number (139) into register a7 and executes the syscall instruction.
RISC-V Register Handling
The MContext
structure saves the program counter (PC) separately from the general registers. During restoration:
- The program counter is restored to the
sepc
(Supervisor Exception Program Counter) field of the trap frame - The general registers are copied directly between the trap frame and
MContext
The floating-point state (fpstate
) is currently initialized to zeros but provides space for future implementations to save floating-point registers.
Sources: src/arch/riscv.rs(L5 - L16) src/arch/riscv.rs(L27 - L38)
Summary
The RISC-V implementation in the axsignal
crate provides the architecture-specific components needed for signal handling on RISC-V processors. It defines the data structures for saving and restoring CPU context (MContext
and UContext
) and implements the signal trampoline needed to return from signal handlers. The implementation supports both 32-bit and 64-bit RISC-V architectures through a single module.
The architecture-specific implementation is selected at compile time based on the target architecture, ensuring that the appropriate code is used without runtime overhead.
Sources: src/arch/mod.rs(L1 - L26) src/arch/riscv.rs(L1 - L64)