Skip to content

Commit b2d9887

Browse files
committed
Update HAL documentation for dual-mode support
The hardware abstraction layer now supports both cooperative and preemptive scheduling modes with distinct context management approaches. The documentation has been updated to reflect these architectural differences and their implications for task initialization and privilege management. The interrupt frame structure preserves complete trap context with 33 words for register state and control registers, plus 12 bytes of padding to maintain 16-byte alignment, totaling 144 bytes. This frame supports both interrupt handling and initial task setup for preemptive scheduling, where tasks launch through trap return rather than standard function calls. Task initialization varies between modes. Cooperative mode uses lightweight context structures containing only callee-saved registers for voluntary yielding. Preemptive mode builds complete interrupt frames with all registers initialized to zero, global and thread pointers configured, and processor state set for proper privilege transitions. The frame is positioned with a 256-byte initial stack reserve below the stack top to accommodate startup requirements. The dispatcher initialization process differs for each scheduling mode. Cooperative tasks transfer control through standard calling conventions with global interrupts enabled before execution. Preemptive tasks restore interrupt frames and execute trap return instructions, allowing hardware to transition to the configured privilege level and enable interrupts based on the saved processor state. The system call interface operates through the RISC-V trap mechanism for privilege boundary crossing. User mode tasks invoke kernel services using environment call instructions that trigger synchronous exceptions. The trap handler preserves all registers except the return value, maintaining standard calling convention semantics across the privilege boundary while the kernel validates parameters and mediates access to protected resources.
1 parent d2b0fd8 commit b2d9887

File tree

2 files changed

+93
-18
lines changed

2 files changed

+93
-18
lines changed

Documentation/hal-calling-convention.md

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,14 @@ void hal_context_restore(jmp_buf env, int32_t val); /* Restore context + process
109109
The ISR in `boot.c` performs a complete context save of all registers:
110110

111111
```
112-
Stack Frame Layout (128 bytes, offsets from sp):
112+
Stack Frame Layout (144 bytes, 33 words × 4 bytes, offsets from sp):
113113
0: ra, 4: gp, 8: tp, 12: t0, 16: t1, 20: t2
114-
24: s0, 28: s1, 32: a0, 36: a1, 40: a2, 44: a3
114+
24: s0, 28: s1, 32: a0, 36: a1, 40: a2, 44: a3
115115
48: a4, 52: a5, 56: a6, 60: a7, 64: s2, 68: s3
116116
72: s4, 76: s5, 80: s6, 84: s7, 88: s8, 92: s9
117117
96: s10, 100:s11, 104:t3, 108: t4, 112: t5, 116: t6
118-
120: mcause, 124: mepc
118+
120: mcause, 124: mepc, 128: mstatus
119+
132-143: padding (12 bytes for 16-byte alignment)
119120
```
120121

121122
Why full context save in ISR?
@@ -128,7 +129,7 @@ Why full context save in ISR?
128129

129130
Each task stack must reserve space for the ISR frame:
130131
```c
131-
#define ISR_STACK_FRAME_SIZE 128 /* 32 registers × 4 bytes */
132+
#define ISR_STACK_FRAME_SIZE 144 /* 33 words × 4 bytes, 16-byte aligned */
132133
```
133134
134135
This "red zone" is reserved at the top of every task stack to guarantee ISR safety.
@@ -147,10 +148,20 @@ int32_t result = mo_task_spawn(task_function, 2048);
147148

148149
### System Call Interface
149150

150-
Linmo uses standard function calls (not trap instructions) for system services:
151-
- Arguments passed in `a0-a7` registers
152-
- Return values in `a0`
153-
- No special calling convention required
151+
Linmo provides system calls through the RISC-V trap mechanism for privilege
152+
boundary crossing. User mode tasks invoke system calls using the environment
153+
call instruction, which triggers a synchronous exception handled by the kernel.
154+
155+
System call convention:
156+
- Arguments passed in `a0-a7` registers before trap
157+
- System call number in `a7` register
158+
- Trap handler preserves all registers except return value
159+
- Return value delivered in `a0` register after trap return
160+
- Standard RISC-V calling convention maintained across privilege boundary
161+
162+
The trap-based interface allows user mode tasks to safely access kernel
163+
services without requiring privileged instruction execution. The kernel
164+
validates all parameters and mediates access to protected resources.
154165

155166
### Task Entry Points
156167

@@ -174,9 +185,9 @@ Each task has its own stack with this layout:
174185
175186
```
176187
High Address
177-
+------------------+ <- stack_base + stack_size
178-
| ISR Red Zone | <- 128 bytes reserved for ISR
179-
| (128 bytes) |
188+
+------------------+ <- stack_base + stack_size
189+
| ISR Red Zone | <- 144 bytes reserved for ISR
190+
| (144 bytes) |
180191
+------------------+ <- Initial SP (16-byte aligned)
181192
| |
182193
| Task Stack | <- Grows downward
@@ -251,8 +262,8 @@ Minimal context (jmp_buf):
251262
- 17 × 32-bit loads/stores = 68 bytes
252263
- Essential for cooperative scheduling
253264
254-
Full context (ISR):
255-
- 32 × 32-bit loads/stores = 128 bytes
265+
Full context (ISR):
266+
- 33 × 32-bit loads/stores = 144 bytes (includes padding for alignment)
256267
- Required for preemptive interrupts
257268
258269
### Function Call Overhead

Documentation/hal-riscv-context-switch.md

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ State Preservation:
9696
- Nested interrupts are handled correctly by hardware's automatic state stacking
9797

9898
### Task Initialization
99-
New tasks are initialized with proper processor state:
99+
Task initialization differs between cooperative and preemptive modes due to
100+
their distinct context management approaches.
101+
102+
In cooperative mode, tasks use lightweight context structures for voluntary
103+
yielding. New tasks are initialized with execution context only:
100104

101105
```c
102106
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
@@ -109,7 +113,58 @@ void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
109113
}
110114
```
111115
112-
This ensures new tasks start with interrupts enabled in machine mode.
116+
This lightweight approach uses standard calling conventions where tasks
117+
return control through normal function returns.
118+
119+
Preemptive mode requires interrupt frame structures to support trap-based
120+
context switching and privilege mode transitions. Task initialization builds
121+
a complete interrupt service routine frame:
122+
123+
```c
124+
void *hal_build_initial_frame(void *stack_top,
125+
void (*task_entry)(void),
126+
int user_mode)
127+
{
128+
/* Place frame in stack with initial reserve below for proper startup */
129+
uint32_t *frame = (uint32_t *) ((uint8_t *) stack_top - 256 -
130+
ISR_STACK_FRAME_SIZE);
131+
132+
/* Initialize all general purpose registers to zero */
133+
for (int i = 0; i < 32; i++)
134+
frame[i] = 0;
135+
136+
/* Compute thread pointer: aligned to 64 bytes from _end */
137+
uint32_t tp_val = ((uint32_t) &_end + 63) & ~63U;
138+
139+
/* Set essential pointers */
140+
frame[FRAME_GP] = (uint32_t) &_gp; /* Global pointer */
141+
frame[FRAME_TP] = tp_val; /* Thread pointer */
142+
143+
/* Configure processor state for task entry:
144+
* - MPIE=1: Interrupts will enable when task starts
145+
* - MPP: Target privilege level (user or machine mode)
146+
* - MIE=0: Keep interrupts disabled during frame restoration
147+
*/
148+
uint32_t mstatus_val =
149+
MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
150+
frame[FRAME_MSTATUS] = mstatus_val;
151+
152+
/* Set entry point */
153+
frame[FRAME_EPC] = (uint32_t) task_entry;
154+
155+
return frame; /* Return frame base as initial stack pointer */
156+
}
157+
```
158+
159+
The interrupt frame layout reserves space for all register state, control
160+
registers, and alignment padding. When the scheduler first dispatches this
161+
task, the trap return mechanism restores the frame and transfers control to
162+
the entry point with the configured privilege level.
163+
164+
Key differences from cooperative mode include full register state allocation
165+
rather than minimal callee-saved registers, trap return semantics rather than
166+
function return, support for privilege level transitions through MPP
167+
configuration, and proper interrupt state initialization through MPIE bit.
113168

114169
## Implementation Details
115170

@@ -168,10 +223,19 @@ New Task Creation:
168223
4. Processor state initialized with interrupts enabled
169224
170225
First Task Launch:
171-
1. `hal_dispatch_init` transfers control from kernel to first task
226+
227+
**Cooperative Mode**:
228+
1. `hal_dispatch_init` receives lightweight context structure
172229
2. Global interrupts enabled just before task execution
173-
3. Timer interrupts activated for preemptive scheduling
174-
4. Task begins execution at its entry point
230+
3. Control transfers to first task through standard function call
231+
4. Task begins execution and voluntarily yields control
232+
233+
**Preemptive Mode**:
234+
1. `hal_dispatch_init` receives interrupt frame pointer
235+
2. Timer interrupt enabled for periodic preemption
236+
3. Dispatcher loads frame and executes trap return instruction
237+
4. Hardware restores registers and transitions to configured privilege level
238+
5. Task begins execution and can be preempted by timer
175239
176240
Context Switch Cycle:
177241
1. Timer interrupt triggers scheduler entry

0 commit comments

Comments
 (0)