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