Skip to content

Commit 02c82a3

Browse files
committed
Fix preemptive mode task initialization
The preemptive scheduler requires interrupt frame restoration during task startup to properly transition privilege modes. However, the dispatcher was initializing tasks using cooperative mode context structures, which lack the necessary state for privilege transitions. This mismatch caused privilege mode corruption and prevented tasks from executing correctly. The dispatcher initialization now selects the appropriate context type based on the active scheduler mode. For preemptive scheduling, the system restores the full interrupt frame and uses trap return instructions to transfer control with proper privilege level switching. The initial status register configuration has been adjusted to prevent interrupts from enabling prematurely during the restoration sequence, avoiding race conditions during task startup.
1 parent ed983be commit 02c82a3

File tree

3 files changed

+116
-19
lines changed

3 files changed

+116
-19
lines changed

arch/riscv/hal.c

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,15 @@ void *hal_build_initial_frame(void *stack_top,
523523
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
524524
frame[2] = tp_val; /* tp - thread pointer */
525525

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 */
526+
/* Initialize mstatus for new task:
527+
* - MPIE=1: mret will copy this to MIE, enabling interrupts after task
528+
* starts
529+
* - MPP: Set privilege level (U-mode or M-mode)
530+
* - MIE=0: Keep interrupts disabled during frame restoration
531+
*/
532+
uint32_t mstatus_val =
533+
MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
534+
frame[FRAME_MSTATUS] = mstatus_val;
529535

530536
frame[FRAME_EPC] = (uint32_t) task_entry; /* mepc - entry point */
531537

@@ -792,23 +798,102 @@ static void __attribute__((naked, used)) __dispatch_init(void)
792798
"mret\n"); /* Jump to the task's entry point */
793799
}
794800

795-
/* Transfers control from the kernel's main thread to the first task */
796-
__attribute__((noreturn)) void hal_dispatch_init(jmp_buf env)
801+
/* Low-level routine to restore context from ISR frame and jump to task.
802+
* This is used in preemptive mode where tasks are managed via ISR frames.
803+
*/
804+
static void __attribute__((naked, used)) __dispatch_init_isr(void)
797805
{
798-
if (unlikely(!env))
806+
asm volatile(
807+
/* a0 contains the ISR frame pointer (sp value) */
808+
"mv sp, a0\n"
809+
810+
/* Restore mstatus from frame[32] */
811+
"lw t0, 32*4(sp)\n"
812+
"csrw mstatus, t0\n"
813+
814+
/* Restore mepc from frame[31] */
815+
"lw t1, 31*4(sp)\n"
816+
"csrw mepc, t1\n"
817+
818+
/* Restore all general-purpose registers */
819+
"lw ra, 0*4(sp)\n"
820+
"lw gp, 1*4(sp)\n"
821+
"lw tp, 2*4(sp)\n"
822+
"lw t0, 3*4(sp)\n"
823+
"lw t1, 4*4(sp)\n"
824+
"lw t2, 5*4(sp)\n"
825+
"lw s0, 6*4(sp)\n"
826+
"lw s1, 7*4(sp)\n"
827+
"lw a0, 8*4(sp)\n"
828+
"lw a1, 9*4(sp)\n"
829+
"lw a2, 10*4(sp)\n"
830+
"lw a3, 11*4(sp)\n"
831+
"lw a4, 12*4(sp)\n"
832+
"lw a5, 13*4(sp)\n"
833+
"lw a6, 14*4(sp)\n"
834+
"lw a7, 15*4(sp)\n"
835+
"lw s2, 16*4(sp)\n"
836+
"lw s3, 17*4(sp)\n"
837+
"lw s4, 18*4(sp)\n"
838+
"lw s5, 19*4(sp)\n"
839+
"lw s6, 20*4(sp)\n"
840+
"lw s7, 21*4(sp)\n"
841+
"lw s8, 22*4(sp)\n"
842+
"lw s9, 23*4(sp)\n"
843+
"lw s10, 24*4(sp)\n"
844+
"lw s11, 25*4(sp)\n"
845+
"lw t3, 26*4(sp)\n"
846+
"lw t4, 27*4(sp)\n"
847+
"lw t5, 28*4(sp)\n"
848+
"lw t6, 29*4(sp)\n"
849+
850+
/* Deallocate stack frame */
851+
"addi sp, sp, %0\n"
852+
853+
/* Return from trap - jump to task entry point */
854+
"mret\n"
855+
:
856+
: "i"(ISR_STACK_FRAME_SIZE)
857+
: "memory");
858+
}
859+
860+
/* Transfers control from the kernel's main thread to the first task.
861+
* In preemptive mode, ctx should be the ISR frame pointer (void *sp).
862+
* In cooperative mode, ctx should be the jmp_buf context.
863+
*/
864+
__attribute__((noreturn)) void hal_dispatch_init(void *ctx)
865+
{
866+
if (unlikely(!ctx))
799867
hal_panic(); /* Cannot proceed without valid context */
800868

801-
if (kcb->preemptive)
802-
hal_timer_enable();
869+
if (kcb->preemptive) {
870+
/* Preemptive mode: ctx is ISR frame pointer, restore from it.
871+
* Enable timer before jumping to task. Global interrupts will be
872+
* enabled by mret based on MPIE bit in restored mstatus.
873+
*/
874+
/* Save ctx before hal_timer_enable modifies registers */
875+
void *saved_ctx = ctx;
803876

804-
_ei(); /* Enable global interrupts just before launching the first task */
877+
hal_timer_enable();
805878

806-
asm volatile(
807-
"mv a0, %0\n" /* Move @env (the task's context) into 'a0' */
808-
"call __dispatch_init\n" /* Call the low-level restore routine */
809-
:
810-
: "r"(env)
811-
: "a0", "memory");
879+
/* Restore ISR frame pointer and call dispatch */
880+
asm volatile(
881+
"mv a0, %0\n" /* Load ISR frame pointer into a0 */
882+
"call __dispatch_init_isr\n" /* Restore from ISR frame */
883+
:
884+
: "r"(saved_ctx)
885+
: "a0", "memory");
886+
} else {
887+
/* Cooperative mode: ctx is jmp_buf, use standard dispatch */
888+
_ei(); /* Enable global interrupts */
889+
890+
asm volatile(
891+
"mv a0, %0\n" /* Move @env (the task's context) into 'a0' */
892+
"call __dispatch_init\n" /* Call the low-level restore routine */
893+
:
894+
: "r"(ctx)
895+
: "a0", "memory");
896+
}
812897
__builtin_unreachable();
813898
}
814899

@@ -865,6 +950,8 @@ void hal_context_init(jmp_buf *ctx,
865950
*/
866951
(*ctx)[CONTEXT_SP] = (uint32_t) stack_top;
867952
(*ctx)[CONTEXT_RA] = (uint32_t) ra;
868-
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE |
869-
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
953+
/* Note: CONTEXT_MSTATUS not used in cooperative mode (setjmp/longjmp),
954+
* but set it for consistency with ISR frame initialization */
955+
(*ctx)[CONTEXT_MSTATUS] =
956+
MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
870957
}

arch/riscv/hal.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,13 @@ void longjmp(jmp_buf env, int32_t val);
7474
/* HAL context switching routines for complete context management */
7575
int32_t hal_context_save(jmp_buf env);
7676
void hal_context_restore(jmp_buf env, int32_t val);
77-
void hal_dispatch_init(jmp_buf env);
77+
78+
/* Transfers control from kernel main thread to the first task.
79+
* In preemptive mode, ctx should be the ISR frame pointer (void *sp).
80+
* In cooperative mode, ctx should be the jmp_buf context.
81+
* @ctx : ISR frame pointer (preemptive) or jmp_buf (cooperative).
82+
*/
83+
void hal_dispatch_init(void *ctx);
7884

7985
/* Stack switching for preemptive context switch.
8086
* Saves current SP to *old_sp and loads new SP from new_sp.

kernel/main.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ int32_t main(void)
7272
*/
7373
scheduler_started = true;
7474

75-
hal_dispatch_init(first_task->context);
75+
/* In preemptive mode, tasks are managed via ISR frames (sp).
76+
* In cooperative mode, tasks are managed via jmp_buf (context).
77+
*/
78+
void *ctx = kcb->preemptive ? first_task->sp : first_task->context;
79+
hal_dispatch_init(ctx);
7680

7781
/* This line should be unreachable. */
7882
panic(ERR_UNKNOWN);

0 commit comments

Comments
 (0)