Skip to content

Commit

Permalink
Simplify scheduling by introducing an init process
Browse files Browse the repository at this point in the history
Scheduling is easier if there is an init process which does nothing.
This way we don't have to perform hacks in order to idle in S privilege
mode while touching special registers in weird ways.

For now this process is kind of costly since 'wfi' is not accepted in
this context, but this can be further tuned down in the future by
setting TW=1 on 'mstatus', or by handling the exception and allowing it
if it comes from the proper process.

All of that being said, there is still work to be done as things fail
when optimitzations are on.

Signed-off-by: Miquel Sabaté Solà <[email protected]>
  • Loading branch information
mssola committed Nov 24, 2024
1 parent 7f6b234 commit 33a4ee9
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 27 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ SRC = $(filter-out kernel/fbos.ld.S, $(wildcard kernel/*.S kernel/*.c lib/*.c
OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SRC)))
LINKER = kernel/fbos.ld
KRNL = fbos
USR = usr/bin/fizz usr/bin/buzz usr/bin/fizzbuzz
USR = usr/bin/init usr/bin/fizz usr/bin/buzz usr/bin/fizzbuzz
INIT = usr/initramfs.cpio
TESTS = test/test_dt test/test_initrd

Expand Down
7 changes: 2 additions & 5 deletions include/fbos/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ struct task_struct {
// Tasks available on this kernel.
extern struct task_struct tasks[4];

// Identifier for the next task to be run.
extern int next_task;

// Bring the machine to idle mode.
__noreturn __kernel void idle(void);
// Switch execution to the given task id.
void switch_to(int task_id);

#endif // __FBOS_SCHED_H_
4 changes: 3 additions & 1 deletion kernel/initrd.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ __kernel uint64_t strtoul16(const char *str, size_t count)

__kernel int get_task_id_from_name(const char *const name)
{
if (strcmp(name, "usr/bin/fizz") == 0) {
if (strcmp(name, "usr/bin/init") == 0) {
return TASK_INIT;
} else if (strcmp(name, "usr/bin/fizz") == 0) {
return TASK_FIZZ;
} else if (strcmp(name, "usr/bin/buzz") == 0) {
return TASK_BUZZ;
Expand Down
14 changes: 2 additions & 12 deletions kernel/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ struct task_struct tasks[4] = {
[TASK_FIZZBUZZ] = { .stack = init_stack[3], .addr = nullptr, .entry_offset = 0, },
};

int next_task;

// TODO: this feels really brittle
// TODO
__kernel void switch_to(int task_id)
{
const char *ddr = (const char *)tasks[task_id].addr + tasks[task_id].entry_offset;

asm volatile("csrc sstatus, %[mask]" : : [mask] "r"(1 << 8));
asm volatile("mv ra, %0" : : "r"(ddr));
asm volatile("csrw sepc, ra");
asm volatile("sret");
}

/*
Expand All @@ -47,15 +44,8 @@ __noreturn __kernel void start_kernel(void *dtb)
seconds_elapsed = 0;
setup_interrupts();

idle();
}

__noreturn __kernel void idle(void)
{
// Loop indefinitely while preserving power.
for (;;) {
if (next_task != TASK_UNKNOWN && next_task != TASK_INIT) {
switch_to(next_task);
}
asm volatile("wfi");
}
}
18 changes: 10 additions & 8 deletions kernel/trap.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,8 @@ __kernel __always_inline void exception_handler(uint64_t cause)
die("Bad syscall\n");
}

// TODO: acknowledge 'sip' bit?
next_task = TASK_UNKNOWN;
write(message, n);

// TODO: is it safe to jump to idle here? Any preparation to be done?
idle();
}

/*
Expand All @@ -79,6 +75,9 @@ __kernel __always_inline void exception_handler(uint64_t cause)
* all registers. It's probably a bit over the top since it also does that for
* registers we never care on this kernel (e.g. floating point registers), but
* it's convenient.
*
* TODO: once this properly works, switch it to assembly to avoid creepy
* statements like the sad 'goto end' one.
*/
__aligned(4) __s_interrupt __kernel void interrupt_handler(void)
{
Expand All @@ -87,6 +86,8 @@ __aligned(4) __s_interrupt __kernel void interrupt_handler(void)

if (IS_EXCEPTION(cause)) {
exception_handler(cause);
switch_to(TASK_INIT);
goto end;
}

if ((cause & TIMER_SCAUSE_MASK) == TIMER_SCAUSE_MASK) {
Expand All @@ -101,13 +102,12 @@ __aligned(4) __s_interrupt __kernel void interrupt_handler(void)
// BEHOLD! The fizz buzz logic! :D
seconds_elapsed += 1;
if ((seconds_elapsed % 15) == 0) {
next_task = TASK_FIZZBUZZ;
switch_to(TASK_FIZZBUZZ);
} else if ((seconds_elapsed % 5) == 0) {
next_task = TASK_BUZZ;
switch_to(TASK_BUZZ);
} else if ((seconds_elapsed % 3) == 0) {
next_task = TASK_FIZZ;
switch_to(TASK_FIZZ);
} else {
next_task = TASK_UNKNOWN;
}

// Re-enable timer interrupts.
Expand All @@ -122,6 +122,8 @@ __aligned(4) __s_interrupt __kernel void interrupt_handler(void)
} else {
printk("WARN: unknown interrupt just came in...\n");
}

end:;
}

__kernel void setup_interrupts(void)
Expand Down

0 comments on commit 33a4ee9

Please sign in to comment.