process/scheduler: make interface a little cleaner, don't leak continuation info

This commit is contained in:
Benji Dial 2025-12-27 22:21:46 -05:00
parent bb10b27152
commit 32524106e8
7 changed files with 156 additions and 110 deletions

View file

@ -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();
}

56
src/kernel/process.asm Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
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

View file

@ -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) {

View file

@ -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);

View file

@ -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:

View file

@ -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);
}

View file

@ -19,9 +19,20 @@
#include <stdint.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;
};
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);