diff --git a/src/kernel/entry.c b/src/kernel/entry.c
index d921382..1c551e7 100644
--- a/src/kernel/entry.c
+++ b/src/kernel/entry.c
@@ -140,7 +140,8 @@ static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
uint64_t fb_length = ((fb->height * fb->pitch - 1) / 4096 + 1) * 4096;
fb_physical_base = (uint64_t)fb->address - hhdm_request.response->offset;
fb_base = find_free_kernel_region(fb_length);
- map_kernel_region(fb_physical_base, fb_base, fb_length, 1, 0);
+ map_kernel_region(
+ fb_physical_base, fb_base, fb_length, 1, 0);
//store rest of framebuffer information
@@ -282,25 +283,12 @@ static const char *cmdline_look_up(const char *key) {
"calcite/apps/hello/hello.elf") != FAR_SUCCESS)
panic("could not look up hello.elf")
- struct process *hello = heap_alloc(sizeof(struct process));
- uint64_t hello_entry;
- create_process(hello);
- if (load_elf(hello, &hello_entry, &root_fs, hello_node) != 1)
- panic("could not load hello.elf")
+ if (!start_elf(&root_fs, hello_node))
+ panic("could not start hello.elf")
if ((*root_fs.free_node)(&root_fs, hello_node) != FAR_SUCCESS)
panic("could not free hello.elf node")
- struct thread *hello_thread = heap_alloc(sizeof(struct thread));
- create_thread(hello, hello_thread);
-
- create_user_task(
- hello->p4_physical_base,
- hello_entry,
- (uint64_t)hello_thread->stack_top);
-
- running_thread = hello_thread;
-
resume_next_continuation();
}
diff --git a/src/kernel/process.asm b/src/kernel/process.asm
new file mode 100644
index 0000000..48cdc41
--- /dev/null
+++ b/src/kernel/process.asm
@@ -0,0 +1,56 @@
+ ; Calcite, src/kernel/process.asm
+ ; Copyright 2025 Benji Dial
+ ;
+ ; This program is free software: you can redistribute it and/or modify
+ ; it under the terms of the GNU General Public License as published by
+ ; the Free Software Foundation, either version 3 of the License, or
+ ; (at your option) any later version.
+ ;
+ ; This program is distributed in the hope that it will be useful, but
+ ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ ; for more details.
+ ;
+ ; You should have received a copy of the GNU General Public License along
+ ; with this program. If not, see .
+
+
+bits 64
+
+extern running_thread
+
+section .text
+
+;referenced in process.c
+global thread_start
+thread_start:
+
+ mov rax, qword [running_thread]
+ test rax, rax
+ jnz .assert_fail
+
+ mov qword [running_thread], rbx
+
+ mov cr3, rbp
+ mov rcx, r12
+ mov r11, 0x200
+
+ xor rax, rax
+ xor rbx, rbx
+ xor rdx, rdx
+ xor rdi, rdi
+ xor rsi, rsi
+ xor rbp, rbp
+ xor r8, r8
+ xor r9, r9
+ xor r10, r10
+ xor r12, r12
+ xor r13, r13
+ xor r14, r14
+ xor r15, r15
+
+ o64 sysret
+
+.assert_fail:
+ ;could be nice to handle this more gracefully in the future
+ ud2
diff --git a/src/kernel/process.c b/src/kernel/process.c
index 25d8178..4f17557 100644
--- a/src/kernel/process.c
+++ b/src/kernel/process.c
@@ -284,6 +284,42 @@ int load_elf(
}
+//defined in process.asm. enters user mode with:
+// running_thread = value of rbx when we jump here
+// cr3 = value of rbp when we jump here
+// rsp = value of rsp when we jump here
+// rip = rcx = value of r12 when we jump here
+// rflags = r11 = 0x200 (IF)
+// all other registers zeroed
+extern uint8_t thread_start;
+
+int start_elf(const struct fs_info *fs_info, void *fs_node) {
+
+ struct process *process = heap_alloc(sizeof(struct process));
+ create_process(process);
+
+ uint64_t entry;
+
+ if (!load_elf(process, &entry, fs_info, fs_node)) {
+ destroy_process(process);
+ return 0;
+ }
+
+ struct thread *thread = heap_alloc(sizeof(struct thread));
+ create_thread(process, thread);
+
+ struct continuation_info ci;
+ ci.rip = (uint64_t)&thread_start;
+ ci.rbx = (uint64_t)thread;
+ ci.rbp = thread->process->p4_physical_base;
+ ci.rsp = (uint64_t)thread->stack_top;
+ ci.r12 = entry;
+
+ add_ready_continuation(&ci);
+ return 1;
+
+}
+
void destroy_process(struct process *process) {
for (int p3i = 0; p3i < 512; ++p3i)
if (process->p3_virtual_base[p3i]) {
@@ -307,6 +343,26 @@ void destroy_process(struct process *process) {
heap_dealloc(process, sizeof(struct process));
}
+void destroy_thread(struct thread *thread) {
+
+ assert(thread->process->n_threads >= 1)
+
+ if (thread->process->n_threads == 1)
+ destroy_process(thread->process);
+
+ else {
+
+ --thread->process->n_threads;
+
+ for (void *p = thread->stack_bottom; p < thread->stack_top; p += 4096)
+ unmap_page_for_process(thread->process, p);
+
+ }
+
+ heap_dealloc(thread, sizeof(struct thread));
+
+}
+
#define INITIAL_STACK_SIZE (16 << 20)
void create_thread(struct process *process, struct thread *thread_out) {
@@ -319,7 +375,8 @@ void create_thread(struct process *process, struct thread *thread_out) {
for (int i = 0; i < INITIAL_STACK_SIZE / 4096; ++i) {
uint64_t pma = take_free_physical_page();
- map_page_for_process(process, pma, stack_bottom_vma + i * 4096, 1, 0);
+ map_page_for_process(
+ process, pma, stack_bottom_vma + i * 4096, 1, 0);
void *kvma = find_free_kernel_region(4096);
map_in_kernel_page_table(pma, kvma, 1, 0);
@@ -339,28 +396,10 @@ void create_thread(struct process *process, struct thread *thread_out) {
struct thread *running_thread = 0;
[[noreturn]] void syscall_end_thread() {
-
assert(running_thread != 0)
- assert(running_thread->process->n_threads >= 1)
-
- if (running_thread->process->n_threads == 1)
- destroy_process(running_thread->process);
-
- else {
-
- --running_thread->process->n_threads;
-
- for (void *p = running_thread->stack_bottom;
- p < running_thread->stack_top; p += 4096)
- unmap_page_for_process(running_thread->process, p);
-
- }
-
- heap_dealloc(running_thread, sizeof(struct thread));
-
+ destroy_thread(running_thread);
running_thread = 0;
resume_next_continuation();
-
}
void syscall_map_framebuffer(struct framebuffer_info *info_out) {
diff --git a/src/kernel/process.h b/src/kernel/process.h
index 2e93612..4f4fb02 100644
--- a/src/kernel/process.h
+++ b/src/kernel/process.h
@@ -70,7 +70,14 @@ int load_elf(
struct process *process, uint64_t *entry_out,
const struct fs_info *fs_info, void *fs_node);
+//returns 0 on failure, 1 on success.
+//creates a process and a thread in that process, loads the elf into the process,
+//and schedules a ready task that sets the running thread to the new thread and
+//starts user mode at the elf's entry point.
+int start_elf(const struct fs_info *fs_info, void *fs_node);
+
void destroy_process(struct process *process);
+void destroy_thread(struct thread *thread);
//returs 1 if [start, start + length) is writable by process, otherwise 0.
int is_mapped_writable(struct process *process, void *start, uint64_t length);
diff --git a/src/kernel/scheduler.asm b/src/kernel/scheduler.asm
index ec2d14d..5fa150d 100644
--- a/src/kernel/scheduler.asm
+++ b/src/kernel/scheduler.asm
@@ -1,4 +1,4 @@
- ; Calcite, src/kernel/scheduler.c
+ ; Calcite, src/kernel/scheduler.asm
; Copyright 2025 Benji Dial
;
; This program is free software: you can redistribute it and/or modify
@@ -17,30 +17,6 @@
bits 64
-;referenced in scheduler.c
-global user_task_start
-user_task_start:
-
- mov cr3, rbx
- mov rcx, rbp
- mov r11, 0x200
-
- xor rax, rax
- xor rbx, rbx
- xor rdx, rdx
- xor rdi, rdi
- xor rsi, rsi
- xor rbp, rbp
- xor r8, r8
- xor r9, r9
- xor r10, r10
- xor r12, r12
- xor r13, r13
- xor r14, r14
- xor r15, r15
-
- o64 sysret
-
;referenced in scheduler.c
global resume_continuation
resume_continuation:
diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c
index f3d4595..dd2ae77 100644
--- a/src/kernel/scheduler.c
+++ b/src/kernel/scheduler.c
@@ -19,18 +19,7 @@
#include "utility.h"
#include "heap.h"
-struct continuation_info {
- uint64_t rip;
- uint64_t rbx;
- uint64_t rbp;
- uint64_t rsp;
- uint64_t r12;
- uint64_t r13;
- uint64_t r14;
- uint64_t r15;
-};
-
-static struct continuation_info **ready_continuations = 0;
+static struct continuation_info *ready_continuations = 0;
static int rc_buffer_length = 0;
static int rc_read_ptr = 0;
static int rc_count = 0;
@@ -38,34 +27,30 @@ static int rc_count = 0;
#define INITIAL_RC_BUFFER_LENGTH 128
void init_scheduler() {
- ready_continuations = heap_alloc(INITIAL_RC_BUFFER_LENGTH * sizeof(void *));
+ ready_continuations = heap_alloc(INITIAL_RC_BUFFER_LENGTH * sizeof(struct continuation_info));
rc_buffer_length = INITIAL_RC_BUFFER_LENGTH;
- for (int i = 0; i < INITIAL_RC_BUFFER_LENGTH; ++i)
- ready_continuations[i] = 0;
}
-static void queue_continuation(struct continuation_info *info) {
+void add_ready_continuation(struct continuation_info *info) {
if (rc_count == rc_buffer_length) {
- struct continuation_info **new_rc_buffer =
- heap_alloc(2 * rc_buffer_length * sizeof(void *));
+ struct continuation_info *new_rc_buffer =
+ heap_alloc(2 * rc_buffer_length * sizeof(struct continuation_info));
memcpy(
new_rc_buffer,
ready_continuations + rc_read_ptr,
- (rc_buffer_length - rc_read_ptr) * sizeof(void *));
+ (rc_buffer_length - rc_read_ptr) * sizeof(struct continuation_info));
memcpy(
new_rc_buffer + rc_buffer_length - rc_read_ptr,
ready_continuations,
- rc_read_ptr * sizeof(void *));
+ rc_read_ptr * sizeof(struct continuation_info));
- heap_dealloc(ready_continuations, rc_buffer_length * sizeof(void *));
+ heap_dealloc(ready_continuations, rc_buffer_length * sizeof(struct continuation_info));
- new_rc_buffer[rc_buffer_length] = info;
- for (int i = rc_buffer_length + 1; i < 2 * rc_buffer_length; ++i)
- new_rc_buffer[i] = 0;
+ memcpy(&new_rc_buffer[rc_buffer_length], info, sizeof(struct continuation_info));
ready_continuations = new_rc_buffer;
rc_buffer_length *= 2;
@@ -75,30 +60,14 @@ static void queue_continuation(struct continuation_info *info) {
}
else {
- ready_continuations[(rc_read_ptr + rc_count) % rc_buffer_length] = info;
+ memcpy(
+ &ready_continuations[(rc_read_ptr + rc_count) % rc_buffer_length],
+ info, sizeof(struct continuation_info));
++rc_count;
}
}
-//defined in scheduler.asm
-void user_task_start();
-
-void create_user_task(
- uint64_t cr3, uint64_t rip, uint64_t rsp) {
-
- struct continuation_info *info =
- heap_alloc(sizeof(struct continuation_info));
-
- info->rip = (uint64_t)&user_task_start;
- info->rsp = (uint64_t)rsp;
- info->rbx = cr3;
- info->rbp = rip;
-
- queue_continuation(info);
-
-}
-
//defined in scheduler.asm
[[noreturn]] void resume_continuation(struct continuation_info *info);
@@ -107,12 +76,12 @@ void create_user_task(
while (rc_count == 0)
__asm__ ("hlt");
- struct continuation_info *info = ready_continuations[rc_read_ptr];
+ struct continuation_info info;
+ memcpy(&info, &ready_continuations[rc_read_ptr], sizeof(struct continuation_info));
- ready_continuations[rc_read_ptr] = 0;
rc_read_ptr = (rc_read_ptr + 1) % rc_buffer_length;
--rc_count;
- resume_continuation(info);
+ resume_continuation(&info);
}
diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h
index cf503f3..ccdf439 100644
--- a/src/kernel/scheduler.h
+++ b/src/kernel/scheduler.h
@@ -19,9 +19,20 @@
#include
+struct continuation_info {
+ uint64_t rip;
+ uint64_t rbx;
+ uint64_t rbp;
+ uint64_t rsp;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+};
+
void init_scheduler();
-void create_user_task(
- uint64_t cr3, uint64_t rip, uint64_t rsp);
-
[[noreturn]] void resume_next_continuation();
+
+//ci is copied
+void add_ready_continuation(struct continuation_info *ci);