Skip to content

Commit 3124d07

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 259670a commit 3124d07

File tree

2 files changed

+155
-34
lines changed

2 files changed

+155
-34
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: 132 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,51 @@
3636

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

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

243298
/* Halts the system in an unrecoverable state */
@@ -321,19 +376,46 @@ uint32_t do_trap(uint32_t cause, uint32_t epc, uint32_t isr_sp)
321376
} else { /* Synchronous Exception */
322377
uint32_t code = MCAUSE_GET_CODE(cause);
323378

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

330413
/* 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!
414+
* The ISR epilogue will restore mepc from the frame. If we don't
415+
* update the frame, mret will jump back to the ecall instruction!
334416
*/
335-
uint32_t *isr_frame = (uint32_t *) isr_sp;
336-
isr_frame[31] = new_epc;
417+
uint32_t *f = (uint32_t *) isr_sp;
418+
f[FRAME_EPC] = new_epc;
337419

338420
/* Invoke dispatcher for context switch - parameter 0 = from ecall,
339421
* don't increment ticks.
@@ -355,6 +437,19 @@ uint32_t do_trap(uint32_t cause, uint32_t epc, uint32_t isr_sp)
355437
uint32_t nibble = (epc >> i) & 0xF;
356438
_putchar(nibble < 10 ? '0' + nibble : 'A' + nibble - 10);
357439
}
440+
441+
/* Debug: print mstatus from frame */
442+
trap_puts(" mstatus=0x");
443+
uint32_t *f = (uint32_t *) isr_sp;
444+
uint32_t mstatus_at_trap = f[FRAME_MSTATUS];
445+
for (int i = 28; i >= 0; i -= 4) {
446+
uint32_t nibble = (mstatus_at_trap >> i) & 0xF;
447+
_putchar(nibble < 10 ? '0' + nibble : 'A' + nibble - 10);
448+
}
449+
trap_puts(" MPP=");
450+
uint32_t mpp = (mstatus_at_trap >> 11) & 0x3;
451+
_putchar('0' + mpp);
452+
358453
trap_puts("\r\n");
359454

360455
hal_panic();
@@ -409,7 +504,9 @@ extern uint32_t _gp, _end;
409504
* 0: ra, 4: gp, 8: tp, 12: t0, ... 116: t6
410505
* 120: mcause, 124: mepc
411506
*/
412-
void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void))
507+
void *hal_build_initial_frame(void *stack_top,
508+
void (*task_entry)(void),
509+
int user_mode)
413510
{
414511
#define INITIAL_STACK_RESERVE \
415512
256 /* Reserve space below stack_top for task startup */
@@ -432,11 +529,16 @@ void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void))
432529
/* Initialize critical registers for proper task startup:
433530
* - frame[1] = gp: Global pointer, required for accessing global variables
434531
* - frame[2] = tp: Thread pointer, required for thread-local storage
435-
* - frame[31] = mepc: Task entry point, where mret will jump to
532+
* - frame[32] = mepc: Task entry point, where mret will jump to
436533
*/
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 */
534+
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
535+
frame[2] = tp_val; /* tp - thread pointer */
536+
537+
uint32_t mstatus_val = MSTATUS_MIE | MSTATUS_MPIE |
538+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
539+
frame[FRAME_MSTATUS] = mstatus_val; /* mstatus - enable interrupts */
540+
541+
frame[FRAME_EPC] = (uint32_t) task_entry; /* mepc - entry point */
440542

441543
return (void *) frame;
442544
}
@@ -696,8 +798,9 @@ static void __attribute__((naked, used)) __dispatch_init(void)
696798
"lw gp, 12*4(a0)\n"
697799
"lw tp, 13*4(a0)\n"
698800
"lw sp, 14*4(a0)\n"
699-
"lw ra, 15*4(a0)\n"
700-
"ret\n"); /* Jump to the task's entry point */
801+
"lw t0, 15*4(a0)\n"
802+
"csrw mepc, t0\n" /* Load task entry point into mepc */
803+
"mret\n"); /* Jump to the task's entry point */
701804
}
702805

703806
/* Transfers control from the kernel's main thread to the first task */
@@ -721,12 +824,19 @@ __attribute__((noreturn)) void hal_dispatch_init(jmp_buf env)
721824
}
722825

723826
/* 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.
827+
* @ctx : Pointer to the 'jmp_buf' to initialize (must be valid).
828+
* @sp : Base address of the task's stack (must be valid).
829+
* @ss : Total size of the stack in bytes (must be >
830+
* ISR_STACK_FRAME_SIZE).
831+
* @ra : The task's entry point function, used as the initial return
832+
* address.
833+
* @user_mode : Non-zero to initialize for user mode, zero for machine mode.
728834
*/
729-
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
835+
void hal_context_init(jmp_buf *ctx,
836+
size_t sp,
837+
size_t ss,
838+
size_t ra,
839+
int user_mode)
730840
{
731841
if (unlikely(!ctx || !sp || ss < (ISR_STACK_FRAME_SIZE + 64) || !ra))
732842
hal_panic(); /* Invalid parameters - cannot safely initialize context */
@@ -759,12 +869,13 @@ void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
759869
/* Set the essential registers for a new task:
760870
* - SP is set to the prepared top of the task's stack.
761871
* - RA is set to the task's entry point.
762-
* - mstatus is set to enable interrupts and ensure machine mode.
872+
* - mstatus is set to enable interrupts and configure privilege mode.
763873
*
764874
* When this context is first restored, the ret instruction will effectively
765875
* jump to this entry point, starting the task.
766876
*/
767877
(*ctx)[CONTEXT_SP] = (uint32_t) stack_top;
768878
(*ctx)[CONTEXT_RA] = (uint32_t) ra;
769-
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPP_MACH;
879+
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE |
880+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
770881
}

0 commit comments

Comments
 (0)