Skip to content

Commit a81d65e

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 a81d65e

File tree

2 files changed

+152
-34
lines changed

2 files changed

+152
-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: 129 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,18 @@ void hal_hardware_init(void)
238278
_stdout_install(__putchar);
239279
_stdin_install(__getchar);
240280
_stdpoll_install(__kbhit);
281+
/* [U-mode Support] Grant U-mode access to all memory.
282+
* Without this, U-mode tasks will trap immediately upon execution
283+
* (Instruction Access Fault) due to default PMP deny rules.
284+
*/
285+
uint32_t pmpaddr = -1UL; /* Cover entire address space */
286+
uint8_t pmpcfg = 0x0F; /* TOR, R, W, X enabled */
287+
288+
asm volatile(
289+
"csrw pmpaddr0, %0\n"
290+
"csrw pmpcfg0, %1\n"
291+
:
292+
: "r"(pmpaddr), "r"(pmpcfg));
241293
}
242294

243295
/* Halts the system in an unrecoverable state */
@@ -321,19 +373,46 @@ uint32_t do_trap(uint32_t cause, uint32_t epc, uint32_t isr_sp)
321373
} else { /* Synchronous Exception */
322374
uint32_t code = MCAUSE_GET_CODE(cause);
323375

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

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

338417
/* Invoke dispatcher for context switch - parameter 0 = from ecall,
339418
* don't increment ticks.
@@ -355,6 +434,19 @@ uint32_t do_trap(uint32_t cause, uint32_t epc, uint32_t isr_sp)
355434
uint32_t nibble = (epc >> i) & 0xF;
356435
_putchar(nibble < 10 ? '0' + nibble : 'A' + nibble - 10);
357436
}
437+
438+
/* Debug: print mstatus from frame */
439+
trap_puts(" mstatus=0x");
440+
uint32_t *f = (uint32_t *) isr_sp;
441+
uint32_t mstatus_at_trap = f[FRAME_MSTATUS];
442+
for (int i = 28; i >= 0; i -= 4) {
443+
uint32_t nibble = (mstatus_at_trap >> i) & 0xF;
444+
_putchar(nibble < 10 ? '0' + nibble : 'A' + nibble - 10);
445+
}
446+
trap_puts(" MPP=");
447+
uint32_t mpp = (mstatus_at_trap >> 11) & 0x3;
448+
_putchar('0' + mpp);
449+
358450
trap_puts("\r\n");
359451

360452
hal_panic();
@@ -409,7 +501,9 @@ extern uint32_t _gp, _end;
409501
* 0: ra, 4: gp, 8: tp, 12: t0, ... 116: t6
410502
* 120: mcause, 124: mepc
411503
*/
412-
void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void))
504+
void *hal_build_initial_frame(void *stack_top,
505+
void (*task_entry)(void),
506+
int user_mode)
413507
{
414508
#define INITIAL_STACK_RESERVE \
415509
256 /* Reserve space below stack_top for task startup */
@@ -432,11 +526,16 @@ void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void))
432526
/* Initialize critical registers for proper task startup:
433527
* - frame[1] = gp: Global pointer, required for accessing global variables
434528
* - frame[2] = tp: Thread pointer, required for thread-local storage
435-
* - frame[31] = mepc: Task entry point, where mret will jump to
529+
* - frame[32] = mepc: Task entry point, where mret will jump to
436530
*/
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 */
531+
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
532+
frame[2] = tp_val; /* tp - thread pointer */
533+
534+
uint32_t mstatus_val = MSTATUS_MIE | MSTATUS_MPIE |
535+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
536+
frame[FRAME_MSTATUS] = mstatus_val; /* mstatus - enable interrupts */
537+
538+
frame[FRAME_EPC] = (uint32_t) task_entry; /* mepc - entry point */
440539

441540
return (void *) frame;
442541
}
@@ -696,8 +795,9 @@ static void __attribute__((naked, used)) __dispatch_init(void)
696795
"lw gp, 12*4(a0)\n"
697796
"lw tp, 13*4(a0)\n"
698797
"lw sp, 14*4(a0)\n"
699-
"lw ra, 15*4(a0)\n"
700-
"ret\n"); /* Jump to the task's entry point */
798+
"lw t0, 15*4(a0)\n"
799+
"csrw mepc, t0\n" /* Load task entry point into mepc */
800+
"mret\n"); /* Jump to the task's entry point */
701801
}
702802

703803
/* Transfers control from the kernel's main thread to the first task */
@@ -721,12 +821,19 @@ __attribute__((noreturn)) void hal_dispatch_init(jmp_buf env)
721821
}
722822

723823
/* 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.
824+
* @ctx : Pointer to the 'jmp_buf' to initialize (must be valid).
825+
* @sp : Base address of the task's stack (must be valid).
826+
* @ss : Total size of the stack in bytes (must be >
827+
* ISR_STACK_FRAME_SIZE).
828+
* @ra : The task's entry point function, used as the initial return
829+
* address.
830+
* @user_mode : Non-zero to initialize for user mode, zero for machine mode.
728831
*/
729-
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
832+
void hal_context_init(jmp_buf *ctx,
833+
size_t sp,
834+
size_t ss,
835+
size_t ra,
836+
int user_mode)
730837
{
731838
if (unlikely(!ctx || !sp || ss < (ISR_STACK_FRAME_SIZE + 64) || !ra))
732839
hal_panic(); /* Invalid parameters - cannot safely initialize context */
@@ -759,12 +866,13 @@ void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra)
759866
/* Set the essential registers for a new task:
760867
* - SP is set to the prepared top of the task's stack.
761868
* - RA is set to the task's entry point.
762-
* - mstatus is set to enable interrupts and ensure machine mode.
869+
* - mstatus is set to enable interrupts and configure privilege mode.
763870
*
764871
* When this context is first restored, the ret instruction will effectively
765872
* jump to this entry point, starting the task.
766873
*/
767874
(*ctx)[CONTEXT_SP] = (uint32_t) stack_top;
768875
(*ctx)[CONTEXT_RA] = (uint32_t) ra;
769-
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPP_MACH;
876+
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE |
877+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
770878
}

0 commit comments

Comments
 (0)