Skip to content

Commit 0944bd0

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 has been expanded to include processor state registers and alignment padding, increasing from the previous size to accommodate complete trap context preservation. This larger 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 documentation now distinguishes between the lightweight context structures used for voluntary yielding in cooperative mode and the full interrupt frames required for preemptive scheduling with privilege transitions. The dispatcher initialization process is documented separately for each mode, explaining how cooperative tasks use standard calling conventions while preemptive tasks rely on trap return mechanisms. The system call interface documentation has been updated to describe the trap-based privilege boundary crossing mechanism. This replaces the previous description of standard function call semantics, accurately reflecting how user mode tasks invoke kernel services through environment call instructions that trigger synchronous exceptions.
1 parent 111c49e commit 0944bd0

File tree

2 files changed

+87
-18
lines changed

2 files changed

+87
-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-140: padding (alignment to 16 bytes)
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: 63 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,52 @@ 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, void *task_entry,
125+
uint32_t tp_val, bool user_mode)
126+
{
127+
/* Allocate ISR frame at top of stack */
128+
uint32_t *frame = (uint32_t *) stack_top - ISR_FRAME_SIZE;
129+
130+
/* Initialize all general purpose registers to zero */
131+
memset(frame, 0, ISR_FRAME_SIZE * sizeof(uint32_t));
132+
133+
/* Set essential pointers */
134+
frame[1] = (uint32_t) &_gp; /* Global pointer */
135+
frame[2] = tp_val; /* Thread pointer */
136+
137+
/* Configure processor state for task entry:
138+
* - MPIE=1: Interrupts will enable when task starts
139+
* - MPP: Target privilege level (user or machine mode)
140+
* - MIE=0: Keep interrupts disabled during frame restoration
141+
*/
142+
uint32_t mstatus_val =
143+
MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
144+
frame[FRAME_MSTATUS] = mstatus_val;
145+
146+
/* Set entry point */
147+
frame[FRAME_EPC] = (uint32_t) task_entry;
148+
149+
return frame; /* Return frame base as initial stack pointer */
150+
}
151+
```
152+
153+
The interrupt frame layout reserves space for all register state, control
154+
registers, and alignment padding. When the scheduler first dispatches this
155+
task, the trap return mechanism restores the frame and transfers control to
156+
the entry point with the configured privilege level.
157+
158+
Key differences from cooperative mode include full register state allocation
159+
rather than minimal callee-saved registers, trap return semantics rather than
160+
function return, support for privilege level transitions through MPP
161+
configuration, and proper interrupt state initialization through MPIE bit.
113162

114163
## Implementation Details
115164

@@ -168,10 +217,19 @@ New Task Creation:
168217
4. Processor state initialized with interrupts enabled
169218
170219
First Task Launch:
171-
1. `hal_dispatch_init` transfers control from kernel to first task
220+
221+
**Cooperative Mode**:
222+
1. `hal_dispatch_init` receives lightweight context structure
172223
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
224+
3. Control transfers to first task through standard function call
225+
4. Task begins execution and voluntarily yields control
226+
227+
**Preemptive Mode**:
228+
1. `hal_dispatch_init` receives interrupt frame pointer
229+
2. Timer interrupt enabled for periodic preemption
230+
3. Dispatcher loads frame and executes trap return instruction
231+
4. Hardware restores registers and transitions to configured privilege level
232+
5. Task begins execution and can be preempted by timer
175233
176234
Context Switch Cycle:
177235
1. Timer interrupt triggers scheduler entry

0 commit comments

Comments
 (0)