Skip to content

Commit e9499a5

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 6e5bb4a commit e9499a5

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
@@ -534,9 +534,15 @@ void *hal_build_initial_frame(void *stack_top,
534534
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
535535
frame[2] = tp_val; /* tp - thread pointer */
536536

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 */
537+
/* Initialize mstatus for new task:
538+
* - MPIE=1: mret will copy this to MIE, enabling interrupts after task
539+
* starts
540+
* - MPP: Set privilege level (U-mode or M-mode)
541+
* - MIE=0: Keep interrupts disabled during frame restoration
542+
*/
543+
uint32_t mstatus_val =
544+
MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
545+
frame[FRAME_MSTATUS] = mstatus_val;
540546

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

@@ -803,23 +809,102 @@ static void __attribute__((naked, used)) __dispatch_init(void)
803809
"mret\n"); /* Jump to the task's entry point */
804810
}
805811

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

812-
if (kcb->preemptive)
813-
hal_timer_enable();
880+
if (kcb->preemptive) {
881+
/* Preemptive mode: ctx is ISR frame pointer, restore from it.
882+
* Enable timer before jumping to task. Global interrupts will be
883+
* enabled by mret based on MPIE bit in restored mstatus.
884+
*/
885+
/* Save ctx before hal_timer_enable modifies registers */
886+
void *saved_ctx = ctx;
814887

815-
_ei(); /* Enable global interrupts just before launching the first task */
888+
hal_timer_enable();
816889

817-
asm volatile(
818-
"mv a0, %0\n" /* Move @env (the task's context) into 'a0' */
819-
"call __dispatch_init\n" /* Call the low-level restore routine */
820-
:
821-
: "r"(env)
822-
: "a0", "memory");
890+
/* Restore ISR frame pointer and call dispatch */
891+
asm volatile(
892+
"mv a0, %0\n" /* Load ISR frame pointer into a0 */
893+
"call __dispatch_init_isr\n" /* Restore from ISR frame */
894+
:
895+
: "r"(saved_ctx)
896+
: "a0", "memory");
897+
} else {
898+
/* Cooperative mode: ctx is jmp_buf, use standard dispatch */
899+
_ei(); /* Enable global interrupts */
900+
901+
asm volatile(
902+
"mv a0, %0\n" /* Move @env (the task's context) into 'a0' */
903+
"call __dispatch_init\n" /* Call the low-level restore routine */
904+
:
905+
: "r"(ctx)
906+
: "a0", "memory");
907+
}
823908
__builtin_unreachable();
824909
}
825910

@@ -876,6 +961,8 @@ void hal_context_init(jmp_buf *ctx,
876961
*/
877962
(*ctx)[CONTEXT_SP] = (uint32_t) stack_top;
878963
(*ctx)[CONTEXT_RA] = (uint32_t) ra;
879-
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE |
880-
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
964+
/* Note: CONTEXT_MSTATUS not used in cooperative mode (setjmp/longjmp),
965+
* but set it for consistency with ISR frame initialization */
966+
(*ctx)[CONTEXT_MSTATUS] =
967+
MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
881968
}

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)