Skip to content

Commit 9867462

Browse files
committed
Handle user mode ecall for syscall dispatch
User mode tasks require privilege escalation to invoke kernel services. Without proper trap frame preservation, context switches corrupt privilege state, preventing tasks from resuming at correct levels. Add trap handler for user mode environment calls to dispatch syscalls. Extend trap frame to preserve privilege mode across context switches. Correct frame layout to match actual register storage order in trap entry sequence.
1 parent 5be9481 commit 9867462

File tree

2 files changed

+147
-37
lines changed

2 files changed

+147
-37
lines changed

arch/riscv/boot.c

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ __attribute__((naked, section(".text.prologue"))) void _entry(void)
9494
}
9595

9696
/* Size of the full trap context frame saved on the stack by the ISR.
97-
* 30 GPRs (x1, x3-x31) + mcause + mepc = 32 registers * 4 bytes = 128 bytes.
98-
* This provides a 16-byte aligned full context save.
97+
* 30 GPRs (x1, x3-x31) + mcause + mepc + mstatus = 33 words * 4 bytes = 132
98+
* bytes. Round up to 144 bytes for 16-byte alignment.
9999
*/
100-
#define ISR_CONTEXT_SIZE 128
100+
#define ISR_CONTEXT_SIZE 144
101101

102102
/* Low-level Interrupt Service Routine (ISR) trampoline.
103103
*
@@ -154,11 +154,17 @@ __attribute__((naked, aligned(4))) void _isr(void)
154154
"sw t6, 29*4(sp)\n"
155155

156156
/* Save trap-related CSRs and prepare arguments for do_trap */
157-
"csrr a0, mcause\n" /* Arg 1: cause */
158-
"csrr a1, mepc\n" /* Arg 2: epc */
159-
"mv a2, sp\n" /* Arg 3: isr_sp (current stack frame) */
160-
"sw a0, 30*4(sp)\n"
161-
"sw a1, 31*4(sp)\n"
157+
"csrr t0, mcause\n"
158+
"csrr t1, mepc\n"
159+
"csrr t2, mstatus\n" /* For context switching in privilege change */
160+
161+
"sw t0, 30*4(sp)\n"
162+
"sw t1, 31*4(sp)\n"
163+
"sw t2, 32*4(sp)\n"
164+
165+
"mv a0, t0\n" /* a0 = cause */
166+
"mv a1, t1\n" /* a1 = epc */
167+
"mv a2, sp\n" /* a2 = isr_sp */
162168

163169
/* Call the high-level C trap handler.
164170
* Returns: a0 = SP to use for restoring context (may be different
@@ -169,9 +175,13 @@ __attribute__((naked, aligned(4))) void _isr(void)
169175
/* Use returned SP for context restore (enables context switching) */
170176
"mv sp, a0\n"
171177

172-
/* Restore context. mepc might have been modified by the handler */
173-
"lw a1, 31*4(sp)\n"
174-
"csrw mepc, a1\n"
178+
/* Restore mstatus from frame[32] */
179+
"lw t0, 32*4(sp)\n"
180+
"csrw mstatus, t0\n"
181+
182+
/* Restore mepc from frame[31] (might have been modified by handler) */
183+
"lw t1, 31*4(sp)\n"
184+
"csrw mepc, t1\n"
175185
"lw ra, 0*4(sp)\n"
176186
"lw gp, 1*4(sp)\n"
177187
"lw tp, 2*4(sp)\n"
@@ -208,7 +218,7 @@ __attribute__((naked, aligned(4))) void _isr(void)
208218

209219
/* Return from trap */
210220
"mret\n"
211-
: /* no outputs */
212-
: "i"(ISR_CONTEXT_SIZE)
221+
: /* no outputs */
222+
: "i"(ISR_CONTEXT_SIZE) /* +16 for mcause, mepc, mstatus */
213223
: "memory");
214224
}

arch/riscv/hal.c

Lines changed: 124 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,53 @@
3535
#define CONTEXT_MSTATUS 16 /* Machine Status CSR */
3636

3737
/* Defines the size of the full trap frame saved by the ISR in 'boot.c'.
38-
* The _isr routine saves 32 registers (30 GPRs + mcause + mepc), resulting
39-
* in a 128-byte frame. This space MUST be reserved at the top of every task's
40-
* stack (as a "red zone") to guarantee that an interrupt, even at peak stack
41-
* usage, will not corrupt memory outside the task's stack bounds.
38+
* The _isr routine saves 33 words (30 GPRs + mcause + mepc + mstatus),
39+
* resulting in a 144-byte frame with alignment padding. This space MUST be
40+
* reserved at the top of every task's stack (as a "red zone") to guarantee
41+
* that an interrupt, even at peak stack usage, will not corrupt memory
42+
* outside the task's stack bounds.
4243
*/
43-
#define ISR_STACK_FRAME_SIZE 128
44+
#define ISR_STACK_FRAME_SIZE 144
45+
46+
/* ISR frame register indices (as 32-bit word offsets from isr_sp).
47+
* This layout matches the stack frame created by _isr in boot.c.
48+
* Indices are in word offsets (divide byte offset by 4).
49+
*/
50+
enum {
51+
FRAME_RA = 0, /* x1 - Return Address */
52+
FRAME_GP = 1, /* x3 - Global Pointer */
53+
FRAME_TP = 2, /* x4 - Thread Pointer */
54+
FRAME_T0 = 3, /* x5 - Temporary register 0 */
55+
FRAME_T1 = 4, /* x6 - Temporary register 1 */
56+
FRAME_T2 = 5, /* x7 - Temporary register 2 */
57+
FRAME_S0 = 6, /* x8 - Saved register 0 / Frame Pointer */
58+
FRAME_S1 = 7, /* x9 - Saved register 1 */
59+
FRAME_A0 = 8, /* x10 - Argument/Return 0 */
60+
FRAME_A1 = 9, /* x11 - Argument/Return 1 */
61+
FRAME_A2 = 10, /* x12 - Argument 2 */
62+
FRAME_A3 = 11, /* x13 - Argument 3 */
63+
FRAME_A4 = 12, /* x14 - Argument 4 */
64+
FRAME_A5 = 13, /* x15 - Argument 5 */
65+
FRAME_A6 = 14, /* x16 - Argument 6 */
66+
FRAME_A7 = 15, /* x17 - Argument 7 / Syscall Number */
67+
FRAME_S2 = 16, /* x18 - Saved register 2 */
68+
FRAME_S3 = 17, /* x19 - Saved register 3 */
69+
FRAME_S4 = 18, /* x20 - Saved register 4 */
70+
FRAME_S5 = 19, /* x21 - Saved register 5 */
71+
FRAME_S6 = 20, /* x22 - Saved register 6 */
72+
FRAME_S7 = 21, /* x23 - Saved register 7 */
73+
FRAME_S8 = 22, /* x24 - Saved register 8 */
74+
FRAME_S9 = 23, /* x25 - Saved register 9 */
75+
FRAME_S10 = 24, /* x26 - Saved register 10 */
76+
FRAME_S11 = 25, /* x27 - Saved register 11 */
77+
FRAME_T3 = 26, /* x28 - Temporary register 3 */
78+
FRAME_T4 = 27, /* x29 - Temporary register 4 */
79+
FRAME_T5 = 28, /* x30 - Temporary register 5 */
80+
FRAME_T6 = 29, /* x31 - Temporary register 6 */
81+
FRAME_MCAUSE = 30, /* Machine Cause CSR */
82+
FRAME_EPC = 31, /* Machine Exception PC (mepc) */
83+
FRAME_MSTATUS = 32 /* Machine Status CSR */
84+
};
4485

4586
/* Global variable to hold the new stack pointer for pending context switch.
4687
* When a context switch is needed, hal_switch_stack() saves the current SP
@@ -238,6 +279,21 @@ void hal_hardware_init(void)
238279
_stdout_install(__putchar);
239280
_stdin_install(__getchar);
240281
_stdpoll_install(__kbhit);
282+
283+
/* Grant U-mode access to all memory for validation purposes.
284+
* By default, RISC-V PMP denies all access to U-mode, which would cause
285+
* instruction access faults immediately upon task switch. This minimal
286+
* setup allows U-mode tasks to execute and serves as a placeholder until
287+
* the full PMP driver is integrated.
288+
*/
289+
uint32_t pmpaddr = -1UL; /* Cover entire address space */
290+
uint8_t pmpcfg = 0x0F; /* TOR, R, W, X enabled */
291+
292+
asm volatile(
293+
"csrw pmpaddr0, %0\n"
294+
"csrw pmpcfg0, %1\n"
295+
:
296+
: "r"(pmpaddr), "r"(pmpcfg));
241297
}
242298

243299
/* Halts the system in an unrecoverable state */
@@ -321,19 +377,46 @@ uint32_t do_trap(uint32_t cause, uint32_t epc, uint32_t isr_sp)
321377
} else { /* Synchronous Exception */
322378
uint32_t code = MCAUSE_GET_CODE(cause);
323379

380+
/* Handle ecall from U-mode - system calls */
381+
if (code == MCAUSE_ECALL_UMODE) {
382+
/* Advance mepc past the ecall instruction (4 bytes) */
383+
uint32_t new_epc = epc + 4;
384+
write_csr(mepc, new_epc);
385+
386+
/* Extract syscall arguments from ISR frame */
387+
uint32_t *f = (uint32_t *) isr_sp;
388+
389+
int syscall_num = f[FRAME_A7];
390+
void *arg1 = (void *) f[FRAME_A0];
391+
void *arg2 = (void *) f[FRAME_A1];
392+
void *arg3 = (void *) f[FRAME_A2];
393+
394+
/* Dispatch to syscall implementation via direct table lookup.
395+
* Must use do_syscall here instead of syscall() to avoid recursive
396+
* traps, as the user-space syscall() may be overridden with ecall.
397+
*/
398+
extern int do_syscall(int num, void *arg1, void *arg2, void *arg3);
399+
int retval = do_syscall(syscall_num, arg1, arg2, arg3);
400+
401+
/* Store return value and updated PC */
402+
f[FRAME_A0] = (uint32_t) retval;
403+
f[FRAME_EPC] = new_epc;
404+
405+
return isr_sp;
406+
}
407+
324408
/* Handle ecall from M-mode - used for yielding in preemptive mode */
325409
if (code == MCAUSE_ECALL_MMODE) {
326410
/* Advance mepc past the ecall instruction (4 bytes) */
327411
uint32_t new_epc = epc + 4;
328412
write_csr(mepc, new_epc);
329413

330414
/* Also update mepc in the ISR frame on the stack!
331-
* The ISR epilogue will restore mepc from the frame (offset 31*4 =
332-
* 124 bytes). If we don't update the frame, mret will jump back to
333-
* the ecall instruction!
415+
* The ISR epilogue will restore mepc from the frame. If we don't
416+
* update the frame, mret will jump back to the ecall instruction!
334417
*/
335-
uint32_t *isr_frame = (uint32_t *) isr_sp;
336-
isr_frame[31] = new_epc;
418+
uint32_t *f = (uint32_t *) isr_sp;
419+
f[FRAME_EPC] = new_epc;
337420

338421
/* Invoke dispatcher for context switch - parameter 0 = from ecall,
339422
* don't increment ticks.
@@ -355,6 +438,7 @@ uint32_t do_trap(uint32_t cause, uint32_t epc, uint32_t isr_sp)
355438
uint32_t nibble = (epc >> i) & 0xF;
356439
_putchar(nibble < 10 ? '0' + nibble : 'A' + nibble - 10);
357440
}
441+
358442
trap_puts("\r\n");
359443

360444
hal_panic();
@@ -409,7 +493,9 @@ extern uint32_t _gp, _end;
409493
* 0: ra, 4: gp, 8: tp, 12: t0, ... 116: t6
410494
* 120: mcause, 124: mepc
411495
*/
412-
void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void))
496+
void *hal_build_initial_frame(void *stack_top,
497+
void (*task_entry)(void),
498+
int user_mode)
413499
{
414500
#define INITIAL_STACK_RESERVE \
415501
256 /* Reserve space below stack_top for task startup */
@@ -432,11 +518,16 @@ void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void))
432518
/* Initialize critical registers for proper task startup:
433519
* - frame[1] = gp: Global pointer, required for accessing global variables
434520
* - frame[2] = tp: Thread pointer, required for thread-local storage
435-
* - frame[31] = mepc: Task entry point, where mret will jump to
521+
* - frame[32] = mepc: Task entry point, where mret will jump to
436522
*/
437-
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
438-
frame[2] = tp_val; /* tp - thread pointer */
439-
frame[31] = (uint32_t) task_entry; /* mepc - entry point */
523+
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
524+
frame[2] = tp_val; /* tp - thread pointer */
525+
526+
uint32_t mstatus_val = MSTATUS_MIE | MSTATUS_MPIE |
527+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
528+
frame[FRAME_MSTATUS] = mstatus_val; /* mstatus - enable interrupts */
529+
530+
frame[FRAME_EPC] = (uint32_t) task_entry; /* mepc - entry point */
440531

441532
return (void *) frame;
442533
}
@@ -696,8 +787,9 @@ static void __attribute__((naked, used)) __dispatch_init(void)
696787
"lw gp, 12*4(a0)\n"
697788
"lw tp, 13*4(a0)\n"
698789
"lw sp, 14*4(a0)\n"
699-
"lw ra, 15*4(a0)\n"
700-
"ret\n"); /* Jump to the task's entry point */
790+
"lw t0, 15*4(a0)\n"
791+
"csrw mepc, t0\n" /* Load task entry point into mepc */
792+
"mret\n"); /* Jump to the task's entry point */
701793
}
702794

703795
/* Transfers control from the kernel's main thread to the first task */
@@ -721,12 +813,19 @@ __attribute__((noreturn)) void hal_dispatch_init(jmp_buf env)
721813
}
722814

723815
/* Builds an initial 'jmp_buf' context for a brand-new task.
724-
* @ctx : Pointer to the 'jmp_buf' to initialize (must be valid).
725-
* @sp : Base address of the task's stack (must be valid).
726-
* @ss : Total size of the stack in bytes (must be > ISR_STACK_FRAME_SIZE).
727-
* @ra : The task's entry point function, used as the initial return address.
816+
* @ctx : Pointer to the 'jmp_buf' to initialize (must be valid).
817+
* @sp : Base address of the task's stack (must be valid).
818+
* @ss : Total size of the stack in bytes (must be >
819+
* ISR_STACK_FRAME_SIZE).
820+
* @ra : The task's entry point function, used as the initial return
821+
* address.
822+
* @user_mode : Non-zero to initialize for user mode, zero for machine mode.
728823
*/
729-
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
824+
void hal_context_init(jmp_buf *ctx,
825+
size_t sp,
826+
size_t ss,
827+
size_t ra,
828+
int user_mode)
730829
{
731830
if (unlikely(!ctx || !sp || ss < (ISR_STACK_FRAME_SIZE + 64) || !ra))
732831
hal_panic(); /* Invalid parameters - cannot safely initialize context */
@@ -759,12 +858,13 @@ void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
759858
/* Set the essential registers for a new task:
760859
* - SP is set to the prepared top of the task's stack.
761860
* - RA is set to the task's entry point.
762-
* - mstatus is set to enable interrupts and ensure machine mode.
861+
* - mstatus is set to enable interrupts and configure privilege mode.
763862
*
764863
* When this context is first restored, the ret instruction will effectively
765864
* jump to this entry point, starting the task.
766865
*/
767866
(*ctx)[CONTEXT_SP] = (uint32_t) stack_top;
768867
(*ctx)[CONTEXT_RA] = (uint32_t) ra;
769-
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPP_MACH;
868+
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE |
869+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
770870
}

0 commit comments

Comments
 (0)