Skip to content

Commit 91f47ca

Browse files
authored
Merge pull request #70 from sysprog21/refine-task-spawn
Refine task_spawn with defense-in-depth security
2 parents 7c63005 + f61d8ac commit 91f47ca

27 files changed

+224
-80
lines changed

Documentation/hal-calling-convention.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,30 @@ This "red zone" is reserved at the top of every task stack to guarantee ISR safe
141141
Standard RISC-V calling convention applies:
142142
143143
```c
144-
/* Example: mo_task_spawn(entry, stack_size, mode) */
145-
/* a0 = entry, a1 = stack_size, a2 = mode, return value in a0 */
146-
int32_t result = mo_task_spawn(task_function, 2048, TASK_MODE_M);
144+
/* Application API - the only interface apps should use: */
145+
int32_t result = mo_task_spawn(task_function, 2048);
147146
```
148147

148+
The `mo_task_spawn()` macro routes automatically based on build configuration:
149+
- `CONFIG_PRIVILEGED`: Direct kernel call → M-mode task
150+
- Otherwise: Syscall (`sys_task_spawn()`) → U-mode task
151+
152+
**Internal Architecture** (kernel developers only):
153+
154+
The implementation uses two internal functions declared in `include/private/task.h`:
155+
- `mo_task_spawn_kernel()`: Creates M-mode tasks (used by logger.c)
156+
- `mo_task_spawn_user()`: Creates U-mode tasks (used by main.c, syscall.c)
157+
158+
These are not exposed to applications. The public header only exposes the
159+
`mo_task_spawn()` macro, which provides the correct behavior transparently.
160+
161+
**Security Model**:
162+
- `mo_task_spawn_kernel()`: Protected by defense-in-depth:
163+
- Runtime check rejects calls from syscall context (returns -1)
164+
- Hardware protection: CRITICAL_ENTER traps if called from U-mode
165+
- `mo_task_spawn_user()`: No privilege restrictions (creates restricted tasks)
166+
- `mo_task_spawn()`: Safe for applications - routes to appropriate implementation
167+
149168
### System Call Interface
150169

151170
Linmo provides system calls through the RISC-V trap mechanism for privilege

app/cond.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ int32_t app_main(void)
110110
mo_mutex_init(&m);
111111
mo_cond_init(&cv);
112112

113-
mo_task_spawn(producer, DEFAULT_STACK_SIZE, TASK_MODE_M);
114-
mo_task_spawn(consumer, DEFAULT_STACK_SIZE, TASK_MODE_M);
115-
mo_task_spawn(mutex_tester, DEFAULT_STACK_SIZE, TASK_MODE_M);
116-
mo_task_spawn(idle_task, DEFAULT_STACK_SIZE, TASK_MODE_M);
113+
mo_task_spawn(producer, DEFAULT_STACK_SIZE);
114+
mo_task_spawn(consumer, DEFAULT_STACK_SIZE);
115+
mo_task_spawn(mutex_tester, DEFAULT_STACK_SIZE);
116+
mo_task_spawn(idle_task, DEFAULT_STACK_SIZE);
117117

118118
/* preemptive mode */
119119
return 1;

app/coop.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ void task0(void)
3434

3535
int32_t app_main(void)
3636
{
37-
mo_task_spawn(task0, DEFAULT_STACK_SIZE, TASK_MODE_M);
38-
mo_task_spawn(task1, DEFAULT_STACK_SIZE, TASK_MODE_M);
39-
mo_task_spawn(task2, DEFAULT_STACK_SIZE, TASK_MODE_M);
37+
mo_task_spawn(task0, DEFAULT_STACK_SIZE);
38+
mo_task_spawn(task1, DEFAULT_STACK_SIZE);
39+
mo_task_spawn(task2, DEFAULT_STACK_SIZE);
4040

4141
/* cooperative mode */
4242
return 0;

app/cpubench.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,6 @@ int32_t app_main(void)
6262
printf("Result: a=%d, b=%d, c=%d\n", a, b, c);
6363
printf("Elapsed time: %lu.%03lus\n", elapsed / 1000, elapsed % 1000);
6464

65-
mo_task_spawn(idle, DEFAULT_STACK_SIZE, TASK_MODE_M);
65+
mo_task_spawn(idle, DEFAULT_STACK_SIZE);
6666
return 1;
6767
}

app/echo.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ int32_t app_main(void)
5555
{
5656
pipe = mo_pipe_create(PIPE_CAP);
5757

58-
mo_task_spawn(task0, DEFAULT_STACK_SIZE, TASK_MODE_M);
59-
mo_task_spawn(task1, DEFAULT_STACK_SIZE, TASK_MODE_M);
58+
mo_task_spawn(task0, DEFAULT_STACK_SIZE);
59+
mo_task_spawn(task1, DEFAULT_STACK_SIZE);
6060

6161
/* preemptive mode */
6262
return 1;

app/hello.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ void task0(void)
3737

3838
int32_t app_main(void)
3939
{
40-
mo_task_spawn(task0, DEFAULT_STACK_SIZE, TASK_MODE_M);
41-
mo_task_spawn(task1, DEFAULT_STACK_SIZE, TASK_MODE_M);
42-
mo_task_spawn(task2, DEFAULT_STACK_SIZE, TASK_MODE_M);
40+
mo_task_spawn(task0, DEFAULT_STACK_SIZE);
41+
mo_task_spawn(task1, DEFAULT_STACK_SIZE);
42+
mo_task_spawn(task2, DEFAULT_STACK_SIZE);
4343

4444
mo_task_priority(2, TASK_PRIO_LOW);
4545

app/mqueues.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ int32_t app_main(void)
162162
/* Spawn all the tasks. The idle task (task 0) is typically spawned first
163163
* to ensure it's available when other tasks yield or block.
164164
*/
165-
mo_task_spawn(idle, DEFAULT_STACK_SIZE, TASK_MODE_M);
166-
mo_task_spawn(task1, DEFAULT_STACK_SIZE, TASK_MODE_M);
167-
mo_task_spawn(task2, DEFAULT_STACK_SIZE, TASK_MODE_M);
168-
mo_task_spawn(task3, DEFAULT_STACK_SIZE, TASK_MODE_M);
169-
mo_task_spawn(task4, DEFAULT_STACK_SIZE, TASK_MODE_M);
165+
mo_task_spawn(idle, DEFAULT_STACK_SIZE);
166+
mo_task_spawn(task1, DEFAULT_STACK_SIZE);
167+
mo_task_spawn(task2, DEFAULT_STACK_SIZE);
168+
mo_task_spawn(task3, DEFAULT_STACK_SIZE);
169+
mo_task_spawn(task4, DEFAULT_STACK_SIZE);
170170

171171
/* Create the message queues with a capacity of 8 items each. */
172172
mq1 = mo_mq_create(8); /* Queue for signaling task1. */

app/mutex.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,10 @@ int32_t app_main(void)
191191
printf("Binary semaphore created successfully\n");
192192

193193
/* Create tasks */
194-
int32_t task_a_id = mo_task_spawn(task_a, 1024, TASK_MODE_M);
195-
int32_t task_b_id = mo_task_spawn(task_b, 1024, TASK_MODE_M);
196-
int32_t monitor_id = mo_task_spawn(monitor_task, 1024, TASK_MODE_M);
197-
int32_t idle_id = mo_task_spawn(idle_task, 512, TASK_MODE_M);
194+
int32_t task_a_id = mo_task_spawn(task_a, 1024);
195+
int32_t task_b_id = mo_task_spawn(task_b, 1024);
196+
int32_t monitor_id = mo_task_spawn(monitor_task, 1024);
197+
int32_t idle_id = mo_task_spawn(idle_task, 512);
198198

199199
if (task_a_id < 0 || task_b_id < 0 || monitor_id < 0 || idle_id < 0) {
200200
printf("FATAL: Failed to create tasks\n");

app/pipes.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ void task0(void)
5757

5858
int32_t app_main(void)
5959
{
60-
mo_task_spawn(task0, DEFAULT_STACK_SIZE, TASK_MODE_M);
61-
mo_task_spawn(task1, DEFAULT_STACK_SIZE, TASK_MODE_M);
62-
mo_task_spawn(task2, DEFAULT_STACK_SIZE, TASK_MODE_M);
63-
mo_task_spawn(task3, DEFAULT_STACK_SIZE, TASK_MODE_M);
60+
mo_task_spawn(task0, DEFAULT_STACK_SIZE);
61+
mo_task_spawn(task1, DEFAULT_STACK_SIZE);
62+
mo_task_spawn(task2, DEFAULT_STACK_SIZE);
63+
mo_task_spawn(task3, DEFAULT_STACK_SIZE);
6464

6565
pipe1 = mo_pipe_create(
6666
128); /* pipe buffer, 128 bytes (allocated on the heap) */

app/pipes_small.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ void task0(void)
4242

4343
int32_t app_main(void)
4444
{
45-
mo_task_spawn(task0, DEFAULT_STACK_SIZE, TASK_MODE_M);
46-
mo_task_spawn(task1, DEFAULT_STACK_SIZE, TASK_MODE_M);
47-
mo_task_spawn(task2, DEFAULT_STACK_SIZE, TASK_MODE_M);
45+
mo_task_spawn(task0, DEFAULT_STACK_SIZE);
46+
mo_task_spawn(task1, DEFAULT_STACK_SIZE);
47+
mo_task_spawn(task2, DEFAULT_STACK_SIZE);
4848

4949
pipe1 = mo_pipe_create(
5050
64); /* pipe buffer, 64 bytes (allocated from the heap) */

0 commit comments

Comments
 (0)