From 4f9bf8afef4ca42016fb734b25e68f8783cc2167 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Mon, 29 Dec 2025 16:55:53 -0500 Subject: [PATCH] ipc and thread spawning --- disk/calcite/init.rc | 1 + include/calcite/syscalls.h | 18 +++ include/kernel-public/ipc.h | 38 +++++ include/kernel-public/syscall-numbers.h | 8 +- src/kernel/entry.c | 12 +- src/kernel/ipc-dgram.c | 147 +++++++++++++++++++ src/kernel/ipc-dgram.h | 58 ++++++++ src/kernel/process.asm | 15 +- src/kernel/process.c | 186 +++++++++++++++++++++++- src/kernel/process.h | 36 +++++ src/kernel/scheduler.asm | 36 ++++- src/kernel/scheduler.c | 7 +- src/kernel/scheduler.h | 6 + src/kernel/syscalls.h | 3 +- src/user-apps/hello/hello.c | 30 +++- src/user-apps/init/init.c | 83 +++++++++++ src/user-apps/init/libraries.txt | 1 + src/user-libs/calcite/syscalls.c | 28 ++++ 18 files changed, 681 insertions(+), 32 deletions(-) create mode 100644 disk/calcite/init.rc create mode 100644 include/kernel-public/ipc.h create mode 100644 src/kernel/ipc-dgram.c create mode 100644 src/kernel/ipc-dgram.h create mode 100644 src/user-apps/init/init.c create mode 100644 src/user-apps/init/libraries.txt diff --git a/disk/calcite/init.rc b/disk/calcite/init.rc new file mode 100644 index 0000000..f00bbe3 --- /dev/null +++ b/disk/calcite/init.rc @@ -0,0 +1 @@ +root://calcite/apps/hello/hello.elf diff --git a/include/calcite/syscalls.h b/include/calcite/syscalls.h index 65f7a38..703d531 100644 --- a/include/calcite/syscalls.h +++ b/include/calcite/syscalls.h @@ -20,6 +20,7 @@ #include #include #include +#include [[noreturn]] void end_thread(); @@ -38,3 +39,20 @@ void wait_for_mouse_packet(struct mouse_packet *packet_out); void *map_pages(uint64_t count); void sleep_ms(uint64_t ms_to_sleep); + +//1 on success, 0 on failure. +int start_elf(const char *path); + +enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out); +enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out); + +//on entry, bytes is maximum accepted packet length. +//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length. +//actual packet length will always be positive. +enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes); + +//bytes must be positive. +enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes); + +//f should not return +void create_thread(void (*f)(uint64_t x), uint64_t x); diff --git a/include/kernel-public/ipc.h b/include/kernel-public/ipc.h new file mode 100644 index 0000000..56f9f2c --- /dev/null +++ b/include/kernel-public/ipc.h @@ -0,0 +1,38 @@ +/* Calcite, include/kernel-public/ipc.h + * 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 . + */ + +#pragma once + +#include + +enum ipc_dgram_result { + + IPR_SUCCESS, + + //on write, packet is bigger than kernel data structure. + //on read, packet is bigger than max requested size. + IPR_TOO_BIG, + + IPR_BAD_HANDLE, + + //on create receiver, someone is already receiving at that address. + IPR_IN_USE + +}; + +typedef uint64_t ipc_dgram_receiver_handle_t; +typedef uint64_t ipc_dgram_sender_handle_t; diff --git a/include/kernel-public/syscall-numbers.h b/include/kernel-public/syscall-numbers.h index 24440bc..7e667c3 100644 --- a/include/kernel-public/syscall-numbers.h +++ b/include/kernel-public/syscall-numbers.h @@ -26,5 +26,11 @@ enum { SYSCALL_READ_FILE, SYSCALL_WAIT_FOR_MOUSE_PACKET, SYSCALL_MAP_PAGES, - SYSCALL_SLEEP_MS + SYSCALL_SLEEP_MS, + SYSCALL_START_ELF, + SYSCALL_IPC_CREATE_DGRAM_RECEIVER, + SYSCALL_IPC_CREATE_DGRAM_SENDER, + SYSCALL_IPC_RECEIVE_DGRAM, + SYSCALL_IPC_SEND_DGRAM, + SYSCALL_CREATE_THREAD }; diff --git a/src/kernel/entry.c b/src/kernel/entry.c index 272d4b2..5a4e233 100644 --- a/src/kernel/entry.c +++ b/src/kernel/entry.c @@ -265,6 +265,12 @@ static const char *cmdline_look_up(const char *key) { register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet); register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages); register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms); + register_syscall(SYSCALL_START_ELF, (void *)&syscall_start_elf); + register_syscall(SYSCALL_IPC_CREATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_dgram_receiver); + register_syscall(SYSCALL_IPC_CREATE_DGRAM_SENDER, (void *)&syscall_ipc_create_dgram_sender); + register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram); + register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram); + register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread); //probe for drives @@ -288,12 +294,12 @@ static const char *cmdline_look_up(const char *key) { set_fs("root", root_fs); - //load hello and start it + //load init and start it init_scheduler(); - if (!start_elf("root://calcite/apps/hello/hello.elf")) - panic("could not start hello.elf") + if (!start_elf("root://calcite/apps/init/init.elf")) + panic("could not start init.elf") resume_next_continuation(); diff --git a/src/kernel/ipc-dgram.c b/src/kernel/ipc-dgram.c new file mode 100644 index 0000000..3472b26 --- /dev/null +++ b/src/kernel/ipc-dgram.c @@ -0,0 +1,147 @@ +/* Calcite, src/kernel/ipc-dgram.c + * 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 . + */ + +#include "ipc-dgram.h" +#include "scheduler.h" +#include "utility.h" +#include "panic.h" +#include "heap.h" + +#include + +struct ipc_dgram_address_book { + const char *address; + struct ipc_dgram_box box; + struct ipc_dgram_address_book *prev; +}; + +static struct ipc_dgram_address_book *last = 0; + +#define INITIAL_SEND_BACKLOG 4 + +struct ipc_dgram_box *get_ipc_dgram_box(const char *address) { + + for (struct ipc_dgram_address_book *book = last; book != 0; book = book->prev) + if (strequ(book->address, address)) + return &book->box; + + struct ipc_dgram_address_book *new = heap_alloc(sizeof(struct ipc_dgram_address_book)); + + int address_length = 0; + while (address[address_length] != 0) + ++address_length; + + char *new_address = heap_alloc(address_length + 1); + memcpy(new_address, address, address_length); + new_address[address_length] = 0; + new->address = new_address; + + new->box.is_receiver_held = 0; + new->box.is_someone_waiting_to_receive = 0; + create_queue(&new->box.waiting_to_send, INITIAL_SEND_BACKLOG); + new->box.buffer_read_pointer = 0; + new->box.buffer_read_available = 0; + + new->prev = last; + last = new; + return &new->box; + +} + +int get_ipc_dgram_size(struct ipc_dgram_box *box) { + + if (box->is_someone_waiting_to_receive) + return -1; + + while (box->buffer_read_available == 0) { + box->is_someone_waiting_to_receive = 1; + yield(&box->waiting_to_receive); + } + + assert(box->buffer_read_available >= 4) + return *(int *)&box->buffer[box->buffer_read_pointer]; + +} + +void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer) { + + assert(box->is_someone_waiting_to_receive == 0) + assert(box->buffer_read_available >= 4) + + int size = *(int *)&box->buffer[box->buffer_read_pointer]; + + box->buffer_read_available -= 4; + box->buffer_read_pointer = (box->buffer_read_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH; + + int size_rounded_up = ((size - 1) / 4 + 1) * 4; + + assert(box->buffer_read_available >= size_rounded_up) + + int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_pointer; + if (size < first_block) + first_block = size; + + memcpy(buffer, &box->buffer[box->buffer_read_pointer], first_block); + if (first_block != size) + memcpy(buffer + first_block, box->buffer, size - first_block); + + box->buffer_read_available -= size_rounded_up; + box->buffer_read_pointer = (box->buffer_read_pointer + size_rounded_up) % IPC_DGRAM_BOX_BUFFER_LENGTH; + + struct continuation_info ci; + while (take_from_queue(&box->waiting_to_send, &ci)) + add_to_queue(&ready_continuations, &ci); + +} + +enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes) { + + assert(bytes > 0) + + int bytes_round_up = ((bytes - 1) / 4 + 1) * 4; + + if (4 + bytes_round_up > IPC_DGRAM_BOX_BUFFER_LENGTH) + return IPR_TOO_BIG; + + while (IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_available < 4 + bytes_round_up) + yield_to_queue(&box->waiting_to_send); + + int write_pointer = (box->buffer_read_pointer + box->buffer_read_available) % IPC_DGRAM_BOX_BUFFER_LENGTH; + assert(write_pointer + 4 <= IPC_DGRAM_BOX_BUFFER_LENGTH) + + *(int *)&box->buffer[write_pointer] = bytes; + + write_pointer = (write_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH; + + int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - write_pointer; + if (bytes < first_block) + first_block = bytes; + + memcpy(&box->buffer[write_pointer], data, first_block); + if (first_block != bytes) + memcpy(box->buffer, data + first_block, bytes - first_block); + + box->buffer_read_available += 4 + bytes_round_up; + + if (box->is_someone_waiting_to_receive) { + box->is_someone_waiting_to_receive = 0; + add_to_queue(&ready_continuations, &box->waiting_to_receive); + } + + return IPR_SUCCESS; + +} diff --git a/src/kernel/ipc-dgram.h b/src/kernel/ipc-dgram.h new file mode 100644 index 0000000..d48c735 --- /dev/null +++ b/src/kernel/ipc-dgram.h @@ -0,0 +1,58 @@ +/* Calcite, src/kernel/ipc-dgram.h + * 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 . + */ + +#pragma once + +#include "scheduler.h" + +#include + +//must be a multiple of 4 +#define IPC_DGRAM_BOX_BUFFER_LENGTH 1024 + +struct ipc_dgram_box { + + int is_receiver_held; + + int is_someone_waiting_to_receive; + struct continuation_info waiting_to_receive; + + struct continuation_queue waiting_to_send; + + int buffer_read_pointer; + int buffer_read_available; + + //packets are of the form: + // int size; + // uint8_t packet[size]; + // uint8_t padding[size % 4 == 0 ? 0 : 4 - size % 4]; + uint8_t buffer[IPC_DGRAM_BOX_BUFFER_LENGTH]; + +}; + +struct ipc_dgram_box *get_ipc_dgram_box(const char *address); + +//waits until a packet is available, then returns the size of that packet. +//returns -1 if someone is already waiting. +int get_ipc_dgram_size(struct ipc_dgram_box *box); + +//should only be called just after get_ipc_dgram_size +//(i.e. with no intervening continuation resumptions) +void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer); + +//only returns IPR_SUCCESS or IPR_TOO_BIG +enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes); diff --git a/src/kernel/process.asm b/src/kernel/process.asm index 59c6e8b..4732fd1 100644 --- a/src/kernel/process.asm +++ b/src/kernel/process.asm @@ -18,28 +18,19 @@ bits 64 default rel -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 + mov rdi, r13 xor rax, rax xor rbx, rbx xor rdx, rdx - xor rdi, rdi xor rsi, rsi xor rbp, rbp xor r8, r8 @@ -51,7 +42,3 @@ thread_start: 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 6eeee44..2a1dfb8 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -16,6 +16,7 @@ */ #include "framebuffer.h" +#include "ipc-dgram.h" #include "scheduler.h" #include "syscalls.h" #include "process.h" @@ -26,6 +27,7 @@ #include "fs.h" #include +#include struct process_file_info *get_file_info(struct process *process, file_handle_t handle) { if (handle > (uint64_t)process->files_buffer_size || process->files[handle].fs == 0) @@ -63,6 +65,7 @@ void create_process(struct process *process_out) { process_out->n_threads = 0; process_out->files = 0; + process_out->ipc_dgram_handles = 0; } @@ -334,11 +337,11 @@ 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) +// rdi = value of r13 when we jump here // all other registers zeroed extern uint8_t thread_start; @@ -365,10 +368,11 @@ int start_elf(const char *uri) { 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; + ci.r13 = 0; + ci.running_thread = thread; add_to_queue(&ready_continuations, &ci); @@ -376,6 +380,36 @@ int start_elf(const char *uri) { } +void syscall_create_thread(void (*f)(uint64_t), uint64_t x) { + + assert(running_thread != 0) + + struct thread *thread = heap_alloc(sizeof(struct thread)); + create_thread(running_thread->process, thread); + + struct continuation_info ci; + ci.rip = (uint64_t)&thread_start; + ci.rbp = thread->process->p4_physical_base; + ci.rsp = (uint64_t)thread->stack_top; + ci.r12 = (uint64_t)f; + ci.r13 = x; + ci.running_thread = thread; + + add_to_queue(&ready_continuations, &ci); + +} + +int syscall_start_elf(const char *uri) { + + assert(running_thread != 0) + + if (!is_mapped_readable_string(running_thread->process, uri)) + syscall_illegal_args(); + + return start_elf(uri); + +} + void destroy_process(struct process *process) { if (process->files) { @@ -385,6 +419,15 @@ void destroy_process(struct process *process) { heap_dealloc(process->files, process->files_buffer_size * sizeof(struct process_file_info)); } + if (process->ipc_dgram_handles) { + for (int i = 0; i < process->ipc_dgram_handles_buffer_size; ++i) + if (process->ipc_dgram_handles[i].box != 0 && process->ipc_dgram_handles[i].is_receiver == 1) + process->ipc_dgram_handles[i].box->is_receiver_held = 0; + heap_dealloc( + process->ipc_dgram_handles, + process->ipc_dgram_handles_buffer_size * sizeof(struct process_ipc_dgram_handle_info)); + } + for (int p3i = 0; p3i < 512; ++p3i) if (process->p3_virtual_base[p3i]) { for (int p2i = 0; p2i < 512; ++p2i) @@ -441,7 +484,9 @@ void create_thread(struct process *process, struct thread *thread_out) { uint64_t pma = take_free_physical_page(); map_page_for_process( - process, pma, stack_bottom_vma + i * 4096, 1, 0, 1); + process, pma, + stack_bottom_vma + i * 4096, + 1, 0, 1); void *kvma = find_free_kernel_region(4096); map_in_kernel_page_table(pma, kvma, 1, 0); @@ -458,7 +503,7 @@ void create_thread(struct process *process, struct thread *thread_out) { } -[[noreturn]] static void syscall_illegal_args() { +[[noreturn]] void syscall_illegal_args() { panic("bad syscall") } @@ -626,3 +671,136 @@ void *syscall_map_pages(uint64_t count) { return vma; } + +#define INITIAL_IPC_DGRAM_HANDLE_COUNT 16 + +static uint64_t get_free_ipc_dgram_handle(struct process *process) { + + if (process->ipc_dgram_handles == 0) { + process->ipc_dgram_handles = + heap_alloc(INITIAL_IPC_DGRAM_HANDLE_COUNT * sizeof(struct process_ipc_dgram_handle_info)); + process->ipc_dgram_handles_buffer_size = INITIAL_IPC_DGRAM_HANDLE_COUNT; + for (int i = 0; i < INITIAL_IPC_DGRAM_HANDLE_COUNT; ++i) + process->ipc_dgram_handles[i].box = 0; + return 0; + } + + for (int i = 0; i < process->ipc_dgram_handles_buffer_size; ++i) + if (process->ipc_dgram_handles[i].box == 0) + return i; + + int old_size = process->ipc_dgram_handles_buffer_size; + int new_size = old_size * 2; + struct process_ipc_dgram_handle_info *new_buffer = + heap_alloc(new_size * sizeof(struct process_ipc_dgram_handle_info)); + + memcpy( + new_buffer, process->ipc_dgram_handles, + old_size * sizeof(struct process_ipc_dgram_handle_info)); + + heap_dealloc( + process->ipc_dgram_handles, + old_size * sizeof(struct process_ipc_dgram_handle_info)); + + process->ipc_dgram_handles = new_buffer; + process->ipc_dgram_handles_buffer_size = new_size; + + for (int i = old_size; i < new_size; ++i) + new_buffer[i].box = 0; + + return old_size; + +} + +enum ipc_dgram_result syscall_ipc_create_dgram_receiver( + const char *address, ipc_dgram_receiver_handle_t *handle_out) { + + assert(running_thread != 0) + + if (!is_mapped_readable_string(running_thread->process, address) || + !is_mapped_writable( + running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t))) + syscall_illegal_args(); + + struct ipc_dgram_box *box = get_ipc_dgram_box(address); + if (box->is_receiver_held) + return IPR_IN_USE; + + box->is_receiver_held = 1; + + *handle_out = get_free_ipc_dgram_handle(running_thread->process); + struct process_ipc_dgram_handle_info *info = &running_thread->process->ipc_dgram_handles[*handle_out]; + info->box = box; + info->is_receiver = 1; + return IPR_SUCCESS; + +} + +enum ipc_dgram_result syscall_ipc_create_dgram_sender( + const char *address, ipc_dgram_sender_handle_t *handle_out) { + + assert(running_thread != 0) + + if (!is_mapped_readable_string(running_thread->process, address) || + !is_mapped_writable( + running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t))) + syscall_illegal_args(); + + struct ipc_dgram_box *box = get_ipc_dgram_box(address); + + *handle_out = get_free_ipc_dgram_handle(running_thread->process); + struct process_ipc_dgram_handle_info *info = &running_thread->process->ipc_dgram_handles[*handle_out]; + info->box = box; + info->is_receiver = 0; + return IPR_SUCCESS; + +} + +enum ipc_dgram_result syscall_ipc_receive_dgram( + ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) { + + assert(running_thread != 0) + + if (!is_mapped_writable(running_thread->process, bytes, sizeof(uint64_t)) || + !is_mapped_writable(running_thread->process, buffer, *bytes)) + syscall_illegal_args(); + + if (running_thread->process->ipc_dgram_handles == 0 || + handle >= (uint64_t)running_thread->process->ipc_dgram_handles_buffer_size || + running_thread->process->ipc_dgram_handles[handle].box == 0 || + running_thread->process->ipc_dgram_handles[handle].is_receiver != 1) + return IPR_BAD_HANDLE; + + struct ipc_dgram_box *box = running_thread->process->ipc_dgram_handles[handle].box; + int actual_bytes = get_ipc_dgram_size(box); + assert(actual_bytes > 0); + + if (actual_bytes > *bytes) { + *bytes = actual_bytes; + return IPR_TOO_BIG; + } + + receive_ipc_dgram(box, buffer); + *bytes = actual_bytes; + return IPR_SUCCESS; + +} + +enum ipc_dgram_result syscall_ipc_send_dgram( + ipc_dgram_sender_handle_t handle, const void *data, int bytes) { + + assert(running_thread != 0) + + if (bytes <= 0 || !is_mapped_readable(running_thread->process, data, bytes)) + syscall_illegal_args(); + + if (running_thread->process->ipc_dgram_handles == 0 || + handle >= (uint64_t)running_thread->process->ipc_dgram_handles_buffer_size || + running_thread->process->ipc_dgram_handles[handle].box == 0 || + running_thread->process->ipc_dgram_handles[handle].is_receiver != 0) + return IPR_BAD_HANDLE; + + struct ipc_dgram_box *box = running_thread->process->ipc_dgram_handles[handle].box; + return send_ipc_dgram(box, data, bytes); + +} diff --git a/src/kernel/process.h b/src/kernel/process.h index 24659c0..75a8151 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -21,6 +21,9 @@ #include #include +#include + +struct ipc_dgram_box; struct process_file_info { const struct fs_info *fs; @@ -28,6 +31,13 @@ struct process_file_info { struct fs_stat stat; }; +struct process_ipc_dgram_handle_info { + //0 if this handle number is not used. + struct ipc_dgram_box *box; + //1 if this is a receiver handle, 0 if this is a sender handle. + int is_receiver; +}; + struct process { uint64_t p4_physical_base; @@ -42,6 +52,9 @@ struct process { struct process_file_info *files; int files_buffer_size; + struct process_ipc_dgram_handle_info *ipc_dgram_handles; + int ipc_dgram_handles_buffer_size; + //0 for missing levels. just bottom p3 of address space. uint64_t *p2_virtual_bases[512]; uint64_t **p1_virtual_bases[512]; @@ -97,9 +110,13 @@ int load_elf( //starts user mode at the elf's entry point. int start_elf(const char *uri); +int syscall_start_elf(const char *uri); + void destroy_process(struct process *process); void destroy_thread(struct thread *thread); +[[noreturn]] void syscall_illegal_args(); + //returs 1 if [start, start + length) is writable by process, otherwise 0. int is_mapped_writable(struct process *process, const void *start, uint64_t length); @@ -119,3 +136,22 @@ enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *byte enum fs_access_result syscall_read_file(struct read_file_parameter *parameter); void *syscall_map_pages(uint64_t count); + +enum ipc_dgram_result syscall_ipc_create_dgram_receiver( + const char *address, ipc_dgram_receiver_handle_t *handle_out); + +enum ipc_dgram_result syscall_ipc_create_dgram_sender( + const char *address, ipc_dgram_sender_handle_t *handle_out); + +//on entry, bytes is maximum accepted packet length. +//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length. +//actual packet length will always be positive. +enum ipc_dgram_result syscall_ipc_receive_dgram( + ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes); + +//bytes must be positive. +enum ipc_dgram_result syscall_ipc_send_dgram( + ipc_dgram_sender_handle_t handle, const void *data, int bytes); + +//f should not return. +void syscall_create_thread(void (*f)(uint64_t x), uint64_t x); diff --git a/src/kernel/scheduler.asm b/src/kernel/scheduler.asm index 76b16f6..0937cce 100644 --- a/src/kernel/scheduler.asm +++ b/src/kernel/scheduler.asm @@ -19,6 +19,8 @@ bits 64 default rel extern resume_next_continuation +extern running_thread +extern add_to_queue ;referenced in scheduler.c global resume_continuation @@ -32,15 +34,14 @@ resume_continuation: mov r13, qword [rdi + 40] mov r14, qword [rdi + 48] mov r15, qword [rdi + 56] + mov rdi, qword [rdi + 64] + mov qword [running_thread], rdi jmp rax -ret: - ret - global yield yield: - mov qword [rdi], ret + mov qword [rdi], .ret mov qword [rdi + 8], rbx mov qword [rdi + 16], rbp mov qword [rdi + 24], rsp @@ -48,6 +49,8 @@ yield: mov qword [rdi + 40], r13 mov qword [rdi + 48], r14 mov qword [rdi + 56], r15 + mov qword rsi, qword [running_thread] + mov qword [rdi + 64], rsi jmp resume_next_continuation @@ -56,7 +59,7 @@ yield: global yield_sti yield_sti: - mov qword [rdi], ret + mov qword [rdi], .ret mov qword [rdi + 8], rbx mov qword [rdi + 16], rbp mov qword [rdi + 24], rsp @@ -64,6 +67,8 @@ yield_sti: mov qword [rdi + 40], r13 mov qword [rdi + 48], r14 mov qword [rdi + 56], r15 + mov qword rsi, qword [running_thread] + mov qword [rdi + 64], rsi sti @@ -71,3 +76,24 @@ yield_sti: .ret: ret + +global yield_to_queue +yield_to_queue: + mov qword [rsp - 72], .ret + mov qword [rsp - 64], rbx + mov qword [rsp - 56], rbp + mov qword [rsp - 48], rsp + mov qword [rsp - 40], r12 + mov qword [rsp - 32], r13 + mov qword [rsp - 24], r14 + mov qword [rsp - 16], r15 + mov rdx, qword [running_thread] + mov qword [rsp - 8], rdx + + sub rsp, 72 + mov rsi, rsp + call add_to_queue + jmp resume_continuation + +.ret: + ret diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index 3285f4d..6fa825c 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -16,7 +16,9 @@ */ #include "scheduler.h" +#include "process.h" #include "utility.h" +#include "panic.h" #include "heap.h" struct continuation_queue ready_continuations; @@ -30,11 +32,14 @@ void init_scheduler() { [[noreturn]] void resume_next_continuation() { - struct continuation_info ci; + running_thread = 0; + struct continuation_info ci; while (!take_from_queue(&ready_continuations, &ci)) __asm__ ("hlt"); + assert(running_thread == 0) + assert(ci.running_thread != 0) resume_continuation(&ci); } diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index febefd2..7d1a866 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -17,6 +17,8 @@ #pragma once +#include "process.h" + #include struct continuation_info { @@ -28,6 +30,7 @@ struct continuation_info { uint64_t r13; uint64_t r14; uint64_t r15; + struct thread *running_thread; }; void init_scheduler(); @@ -57,3 +60,6 @@ void yield(struct continuation_info *save_current_continuation_to); //save as yield but enables interrupts between saving this continuation and resuming next ready one. void yield_sti(struct continuation_info *save_current_continuation_to); + +//same as yield, but instead of saving continuation to a pointer, saves it to the end of the queue. +void yield_to_queue(struct continuation_queue *queue); diff --git a/src/kernel/syscalls.h b/src/kernel/syscalls.h index b05df9a..5c6f84a 100644 --- a/src/kernel/syscalls.h +++ b/src/kernel/syscalls.h @@ -27,7 +27,8 @@ void register_syscall( uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3)); //the top of the syscall stack of the most recently started syscall. -//kind of a hack, used in the end_thread syscall to deallocate that stack. +//kind of a hack, used in the end_thread syscall to deallocate that stack +//and in the fork_thread syscall to copy the stack. extern void *most_recent_syscall_stack; //also kind of a hack. switches to temporary stack and runs f with that stack. diff --git a/src/user-apps/hello/hello.c b/src/user-apps/hello/hello.c index 069b78a..63bd26c 100644 --- a/src/user-apps/hello/hello.c +++ b/src/user-apps/hello/hello.c @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "kernel-public/ipc.h" #include #include @@ -59,14 +60,37 @@ static void copy_frame() { } } -void main() { +[[noreturn]] static void panic() { + for (int y = 0; y < fbb->height; ++y) + for (int x = 0; x < fbb->width; ++x) { + struct pixel *pixel = &fbb->pixels[y * fbb->width + x]; + pixel->r = (x + y) * 256 / (fbb->height + fbb->width); + pixel->g = 0; + pixel->b = 0; + } + copy_frame(); + end_thread(); +} - if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image)) - return; +void main() { map_framebuffer(&fb_info); create_image(fb_info.fb_width, fb_info.fb_height, &fbb); + ipc_dgram_receiver_handle_t receiver; + if (ipc_create_dgram_receiver("calcite-test", &receiver) != IPR_SUCCESS) + panic(); + + char buffer[5]; + int bytes = 5; + if (ipc_receive_dgram(receiver, buffer, &bytes) != IPR_SUCCESS || + bytes != 5 || + buffer[0] != 'h' || buffer[1] != 'e' || buffer[2] != 'l' || buffer[3] != 'l' || buffer[4] != 'o') + panic(); + + if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image)) + panic(); + int cursor_x = fb_info.fb_width / 2; int cursor_y = fb_info.fb_height / 2; diff --git a/src/user-apps/init/init.c b/src/user-apps/init/init.c new file mode 100644 index 0000000..7ac9f1b --- /dev/null +++ b/src/user-apps/init/init.c @@ -0,0 +1,83 @@ +/* Calcite, src/user-apps/init/init.c + * 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 . + */ + +#include +#include +#include +#include + +const char *hello = "hello"; + +[[noreturn]] void test(const char *message) { + + ipc_dgram_sender_handle_t handle; + if (ipc_create_dgram_sender("calcite-test", &handle) == IPR_SUCCESS) + ipc_send_dgram(handle, message, 5); + + end_thread(); + +} + +#define MAX_LINE_LENGTH 1023 + +void main() { + + create_thread((void *)&test, (uint64_t)hello); + + struct file_stream *rc; + if (open_file_stream("root://calcite/init.rc", &rc) != FAR_SUCCESS) + return; + + int at_file_end = 0; + + while (!at_file_end) { + + char line[MAX_LINE_LENGTH + 1]; + int line_length = 0; + + while (1) { + + char ch = read_file_stream_byte(rc); + + if (ch == -1) { + at_file_end = 1; + break; + } + if (ch == '\n') + break; + if (line_length == MAX_LINE_LENGTH) { + while (1) { + ch = read_file_stream_byte(rc); + if (ch == -1 || ch == '\n') + break; + } + break; + } + + line[line_length++] = ch; + + } + + if (line_length == 0 || line[0] == '#') + continue; + + line[line_length] = 0; + start_elf(line); + + } + +} diff --git a/src/user-apps/init/libraries.txt b/src/user-apps/init/libraries.txt new file mode 100644 index 0000000..8503661 --- /dev/null +++ b/src/user-apps/init/libraries.txt @@ -0,0 +1 @@ +calcite diff --git a/src/user-libs/calcite/syscalls.c b/src/user-libs/calcite/syscalls.c index 80a5b7f..82ee61c 100644 --- a/src/user-libs/calcite/syscalls.c +++ b/src/user-libs/calcite/syscalls.c @@ -17,6 +17,7 @@ #include #include +#include #include //defined in syscalls.asm @@ -64,3 +65,30 @@ void *map_pages(uint64_t count) { void sleep_ms(uint64_t ms_to_sleep) { do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS); } + +int start_elf(const char *uri) { + return do_syscall((uint64_t)uri, 0, 0, SYSCALL_START_ELF); +} + +enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out) { + return do_syscall( + (uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_RECEIVER); +} + +enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out) { + return do_syscall( + (uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_SENDER); +} + +enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) { + return do_syscall( + handle, (uint64_t)buffer, (uint64_t)bytes, SYSCALL_IPC_RECEIVE_DGRAM); +} + +enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes) { + return do_syscall(handle, (uint64_t)data, bytes, SYSCALL_IPC_SEND_DGRAM); +} + +void create_thread(void (*f)(uint64_t x), uint64_t x) { + do_syscall((uint64_t)f, x, 0, SYSCALL_CREATE_THREAD); +}