Skip to content

Commit 7bfa04e

Browse files
committed
Add user mode task spawning interface
Kernel requires distinct privilege modes for kernel services and user applications. Return from trap instruction needs previous interrupt enable bit set to preserve interrupt state across privilege transitions. Parameterize context initialization to configure privilege mode during task creation. Set previous interrupt enable bit for correct interrupt behavior after mode transitions. Provide separate interface for spawning user mode tasks alongside existing kernel task interface.
1 parent 826615f commit 7bfa04e

File tree

4 files changed

+58
-24
lines changed

4 files changed

+58
-24
lines changed

arch/riscv/hal.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,14 @@ void hal_hardware_init(void)
282282
* Without this, U-mode tasks will trap immediately upon execution
283283
* (Instruction Access Fault) due to default PMP deny rules.
284284
*/
285-
uint32_t pmpaddr = -1UL; /* Cover entire address space */
286-
uint8_t pmpcfg = 0x0F; /* TOR, R, W, X enabled */
287-
285+
uint32_t pmpaddr = -1UL; /* Cover entire address space */
286+
uint8_t pmpcfg = 0x0F; /* TOR, R, W, X enabled */
287+
288288
asm volatile(
289289
"csrw pmpaddr0, %0\n"
290290
"csrw pmpcfg0, %1\n"
291-
: : "r"(pmpaddr), "r"(pmpcfg)
292-
);
291+
:
292+
: "r"(pmpaddr), "r"(pmpcfg));
293293
}
294294

295295
/* Halts the system in an unrecoverable state */
@@ -501,7 +501,9 @@ extern uint32_t _gp, _end;
501501
* 0: ra, 4: gp, 8: tp, 12: t0, ... 116: t6
502502
* 120: mcause, 124: mepc
503503
*/
504-
void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void), int user_mode)
504+
void *hal_build_initial_frame(void *stack_top,
505+
void (*task_entry)(void),
506+
int user_mode)
505507
{
506508
#define INITIAL_STACK_RESERVE \
507509
256 /* Reserve space below stack_top for task startup */
@@ -526,10 +528,11 @@ void *hal_build_initial_frame(void *stack_top, void (*task_entry)(void), int use
526528
* - frame[2] = tp: Thread pointer, required for thread-local storage
527529
* - frame[32] = mepc: Task entry point, where mret will jump to
528530
*/
529-
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
530-
frame[2] = tp_val; /* tp - thread pointer */
531+
frame[1] = (uint32_t) &_gp; /* gp - global pointer */
532+
frame[2] = tp_val; /* tp - thread pointer */
531533

532-
uint32_t mstatus_val = MSTATUS_MIE | MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
534+
uint32_t mstatus_val = MSTATUS_MIE | MSTATUS_MPIE |
535+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
533536
frame[FRAME_MSTATUS] = mstatus_val; /* mstatus - enable interrupts */
534537

535538
frame[FRAME_EPC] = (uint32_t) task_entry; /* mepc - entry point */
@@ -794,7 +797,7 @@ static void __attribute__((naked, used)) __dispatch_init(void)
794797
"lw sp, 14*4(a0)\n"
795798
"lw t0, 15*4(a0)\n"
796799
"csrw mepc, t0\n" /* Load task entry point into mepc */
797-
"mret\n"); /* Jump to the task's entry point */
800+
"mret\n"); /* Jump to the task's entry point */
798801
}
799802

800803
/* Transfers control from the kernel's main thread to the first task */
@@ -820,11 +823,17 @@ __attribute__((noreturn)) void hal_dispatch_init(jmp_buf env)
820823
/* Builds an initial 'jmp_buf' context for a brand-new task.
821824
* @ctx : Pointer to the 'jmp_buf' to initialize (must be valid).
822825
* @sp : Base address of the task's stack (must be valid).
823-
* @ss : Total size of the stack in bytes (must be > ISR_STACK_FRAME_SIZE).
824-
* @ra : The task's entry point function, used as the initial return address.
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.
825830
* @user_mode : Non-zero to initialize for user mode, zero for machine mode.
826831
*/
827-
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra, int user_mode)
832+
void hal_context_init(jmp_buf *ctx,
833+
size_t sp,
834+
size_t ss,
835+
size_t ra,
836+
int user_mode)
828837
{
829838
if (unlikely(!ctx || !sp || ss < (ISR_STACK_FRAME_SIZE + 64) || !ra))
830839
hal_panic(); /* Invalid parameters - cannot safely initialize context */
@@ -864,5 +873,6 @@ void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra, int user_mo
864873
*/
865874
(*ctx)[CONTEXT_SP] = (uint32_t) stack_top;
866875
(*ctx)[CONTEXT_RA] = (uint32_t) ra;
867-
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE | (user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
876+
(*ctx)[CONTEXT_MSTATUS] = MSTATUS_MIE | MSTATUS_MPIE |
877+
(user_mode ? MSTATUS_MPP_USER : MSTATUS_MPP_MACH);
868878
}

arch/riscv/hal.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,17 @@ void hal_timer_irq_disable(
105105
void hal_interrupt_tick(void); /* Enable interrupts on first task run */
106106
void *hal_build_initial_frame(
107107
void *stack_top,
108-
void (*task_entry)(void)); /* Build ISR frame for preemptive mode */
108+
void (*task_entry)(void),
109+
int user_mode); /* Build ISR frame for preemptive mode */
109110

110111
/* Initializes the context structure for a new task.
111-
* @ctx : Pointer to jmp_buf to initialize (must be non-NULL).
112-
* @sp : Base address of the task's stack (must be valid).
113-
* @ss : Total size of the stack in bytes (must be >= MIN_STACK_SIZE).
114-
* @ra : The task's entry point function (must be non-NULL).
112+
* @ctx : Pointer to jmp_buf to initialize (must be non-NULL).
113+
* @sp : Base address of the task's stack (must be valid).
114+
* @ss : Total size of the stack in bytes (must be >= MIN_STACK_SIZE).
115+
* @ra : The task's entry point function (must be non-NULL).
116+
* @user_mode : Non-zero to initialize for user mode, zero for machine mode.
115117
*/
116-
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra);
118+
void hal_context_init(jmp_buf *ctx, size_t sp, size_t ss, size_t ra, int user_mode);
117119

118120
/* Halts the CPU in an unrecoverable error state, shutting down if possible */
119121
void hal_panic(void);

include/sys/task.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,25 @@ void _yield(void);
187187

188188
/* Task Lifecycle Management */
189189

190-
/* Creates and starts a new task.
190+
/* Creates and starts a new task in machine mode.
191191
* @task_entry : Pointer to the task's entry function (void func(void))
192192
* @stack_size : The desired stack size in bytes (minimum is enforced)
193193
*
194194
* Returns the new task's ID on success. Panics on memory allocation failure.
195195
*/
196196
int32_t mo_task_spawn(void *task_entry, uint16_t stack_size);
197197

198+
/* Creates and starts a new task in user mode.
199+
* User mode tasks run with reduced privileges and must use syscalls to access
200+
* kernel services. This provides memory protection and privilege separation.
201+
*
202+
* @task_entry : Pointer to the task's entry function (void func(void))
203+
* @stack_size : The desired stack size in bytes (minimum is enforced)
204+
*
205+
* Returns the new task's ID on success. Panics on memory allocation failure.
206+
*/
207+
int32_t mo_task_spawn_user(void *task_entry, uint16_t stack_size);
208+
198209
/* Cancels and removes a task from the system. A task cannot cancel itself.
199210
* @id : The ID of the task to cancel
200211
*

kernel/task.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,8 @@ static bool init_task_stack(tcb_t *tcb, size_t stack_size)
712712

713713
/* Task Management API */
714714

715-
int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req)
715+
/* Internal task spawning implementation with privilege mode control */
716+
static int32_t task_spawn_impl(void *task_entry, uint16_t stack_size_req, int user_mode)
716717
{
717718
if (!task_entry)
718719
panic(ERR_TCB_ALLOC);
@@ -777,13 +778,13 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req)
777778

778779
/* Initialize execution context outside critical section. */
779780
hal_context_init(&tcb->context, (size_t) tcb->stack, new_stack_size,
780-
(size_t) task_entry);
781+
(size_t) task_entry, user_mode);
781782

782783
/* Initialize SP for preemptive mode.
783784
* Build initial ISR frame on stack with mepc pointing to task entry.
784785
*/
785786
void *stack_top = (void *) ((uint8_t *) tcb->stack + new_stack_size);
786-
tcb->sp = hal_build_initial_frame(stack_top, task_entry);
787+
tcb->sp = hal_build_initial_frame(stack_top, task_entry, user_mode);
787788

788789
printf("task %u: entry=%p stack=%p size=%u prio_level=%u time_slice=%u\n",
789790
tcb->id, task_entry, tcb->stack, (unsigned int) new_stack_size,
@@ -796,6 +797,16 @@ int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req)
796797
return tcb->id;
797798
}
798799

800+
int32_t mo_task_spawn(void *task_entry, uint16_t stack_size_req)
801+
{
802+
return task_spawn_impl(task_entry, stack_size_req, false);
803+
}
804+
805+
int32_t mo_task_spawn_user(void *task_entry, uint16_t stack_size_req)
806+
{
807+
return task_spawn_impl(task_entry, stack_size_req, true);
808+
}
809+
799810
int32_t mo_task_cancel(uint16_t id)
800811
{
801812
if (id == 0 || id == mo_task_id())

0 commit comments

Comments
 (0)