diff --git a/compile_flags.txt b/compile_flags.txt index eb64b69..bfe9d86 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -3,6 +3,8 @@ -I dependencies/limine -I +dependencies/flanterm/src +-I include -D CALCITE_DEBUG diff --git a/disk/calcite/apps/hello/pointer.pam b/disk/calcite/apps/hello/pointer.pam deleted file mode 100644 index 18bd8ce..0000000 Binary files a/disk/calcite/apps/hello/pointer.pam and /dev/null differ diff --git a/disk/calcite/init.rc b/disk/calcite/init.rc index f00bbe3..cfef8bd 100644 --- a/disk/calcite/init.rc +++ b/disk/calcite/init.rc @@ -1 +1 @@ -root://calcite/apps/hello/hello.elf +root://calcite/apps/terminal/terminal.elf diff --git a/get-dependencies.sh b/get-dependencies.sh index 5b025bc..da748d5 100644 --- a/get-dependencies.sh +++ b/get-dependencies.sh @@ -1,6 +1,7 @@ #!/bin/sh LIMINE_VERSION="9.3.4" +FLANTERM_VERSION="2.1.0" if [ -e dependencies ]; then echo dependencies directory already exists. @@ -12,7 +13,10 @@ set -e mkdir dependencies cd dependencies -curl -L https://github.com/limine-bootloader/limine/archive/refs/tags/v${LIMINE_VERSION}-binary.tar.gz | tar xz -mv limine-${LIMINE_VERSION}-binary limine +curl -L https://codeberg.org/Limine/Limine/archive/v${LIMINE_VERSION}-binary.tar.gz | tar xz make -C limine cd .. + +cd dependencies +curl -L https://codeberg.org/Mintsuki/Flanterm/archive/v${FLANTERM_VERSION}.tar.gz | tar xz +cd .. diff --git a/include/calcite/dispatch.h b/include/calcite/dispatch.h new file mode 100644 index 0000000..a159a0d --- /dev/null +++ b/include/calcite/dispatch.h @@ -0,0 +1,24 @@ +/* Calcite, include/calcite/dispatch.h + * Copyright 2026 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 + +struct dispatch_queue; + +struct dispatch_queue *create_dispatch_queue(); +void dispatch(struct dispatch_queue *queue, void (*callback)(void *x), void *x); +[[noreturn]] void dispatch_loop(struct dispatch_queue *queue); diff --git a/include/calcite/format.h b/include/calcite/format.h new file mode 100644 index 0000000..fb11257 --- /dev/null +++ b/include/calcite/format.h @@ -0,0 +1,26 @@ +/* Calcite, include/calcite/format.h + * Copyright 2026 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 + +//does not write null terminator. buffer must be at least digit_count bytes long. +void to_hex(uint64_t value, int digit_count, char *buffer); + +//buffer must be at least digit_count bytes long. +uint64_t from_hex(int digit_count, char *buffer); diff --git a/include/calcite/memory.h b/include/calcite/memory.h index 70993bd..7a74414 100644 --- a/include/calcite/memory.h +++ b/include/calcite/memory.h @@ -1,5 +1,5 @@ /* Calcite, include/calcite/memory.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -26,3 +26,4 @@ void *heap_alloc(uint64_t bytes); void heap_dealloc(void *start, uint64_t bytes); void memcpy(void *to, const void *from, uint64_t bytes); +void memset(void *start, uint8_t value, uint64_t length); diff --git a/include/calcite/syscalls.h b/include/calcite/syscalls.h index 2f0abc0..3fcdadd 100644 --- a/include/calcite/syscalls.h +++ b/include/calcite/syscalls.h @@ -1,5 +1,5 @@ /* Calcite, include/calcite/syscalls.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include [[noreturn]] void end_thread(); @@ -55,6 +56,11 @@ enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void //bytes must be positive. enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes); +//returned id is global across processes. only one receiver and one sender can be created per pipe. +uint64_t ipc_create_private_dgram_pipe(); +enum ipc_dgram_result ipc_create_private_dgram_receiver(uint64_t id, ipc_dgram_receiver_handle_t *handle_out); +enum ipc_dgram_result ipc_create_private_dgram_sender(uint64_t id, ipc_dgram_sender_handle_t *handle_out); + //f should not return void create_thread(void (*f)(uint64_t x), uint64_t x); @@ -65,3 +71,14 @@ void create_thread(void (*f)(uint64_t x), uint64_t x); //and if that is at most what value_space was set to at entry, also //copies the value including null terminator to value_out. void get_envvar(const char *key, char *value_out, int *value_space); + +mutex_handle_t create_mutex(); +void acquire_mutex(mutex_handle_t handle); +void release_mutex(mutex_handle_t handle); + +event_handle_t create_event(); +void signal_event(event_handle_t handle); +void wait_event(event_handle_t handle); + +uint64_t get_ms_since_boot(); +void sleep_until_ms_since_boot(uint64_t ms); diff --git a/include/calcite/terminal.h b/include/calcite/terminal.h new file mode 100644 index 0000000..d6e4986 --- /dev/null +++ b/include/calcite/terminal.h @@ -0,0 +1,22 @@ +/* Calcite, include/calcite/terminal.h + * Copyright 2026 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 + +void init_terminal(); +void terminal_write(const char *data, int bytes); +void terminal_read(char *buffer, int bytes); diff --git a/include/kernel-public/sync.h b/include/kernel-public/sync.h new file mode 100644 index 0000000..893b6ca --- /dev/null +++ b/include/kernel-public/sync.h @@ -0,0 +1,23 @@ +/* Calcite, include/kernel-public/sync.h + * Copyright 2026 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 + +typedef uint64_t mutex_handle_t; +typedef uint64_t event_handle_t; diff --git a/include/kernel-public/syscall-numbers.h b/include/kernel-public/syscall-numbers.h index 1c1cc3b..351d804 100644 --- a/include/kernel-public/syscall-numbers.h +++ b/include/kernel-public/syscall-numbers.h @@ -1,5 +1,5 @@ /* Calcite, include/kernel-public/syscall-numbers.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -33,5 +33,16 @@ enum { SYSCALL_IPC_RECEIVE_DGRAM, SYSCALL_IPC_SEND_DGRAM, SYSCALL_CREATE_THREAD, - SYSCALL_GET_ENVVAR + SYSCALL_GET_ENVVAR, + SYSCALL_CREATE_MUTEX, + SYSCALL_ACQUIRE_MUTEX, + SYSCALL_RELEASE_MUTEX, + SYSCALL_CREATE_EVENT, + SYSCALL_SIGNAL_EVENT, + SYSCALL_WAIT_EVENT, + SYSCALL_IPC_CREATE_PRIVATE_DGRAM_PIPE, + SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER, + SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER, + SYSCALL_GET_MS_SINCE_BOOT, + SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT }; diff --git a/make-build.sh b/make-build.sh index 3630d18..126013b 100644 --- a/make-build.sh +++ b/make-build.sh @@ -18,7 +18,7 @@ fi KERNEL_CC_FLAGS="-mno-sse -I dependencies/limine ${COMMON_CC_FLAGS}" #in the future user code will be allowed to use sse -USER_CC_FLAGS="-mno-sse ${COMMON_CC_FLAGS}" +USER_CC_FLAGS="-mno-sse -I dependencies/flanterm/src ${COMMON_CC_FLAGS}" if [ -e build.ninja ]; then echo build.ninja already exists. @@ -78,6 +78,15 @@ build_all() { build_all src/kernel kernel_cc kernel_ld kernel.elf +objects="" +for file in flanterm.c flanterm_backends/fb.c; do + src="dependencies/flanterm/src/$file" + build="build/user-libs/flanterm/$file.o" + echo "build $build: user_cc $src" >> build.ninja + objects="$objects $build" +done +echo "build build/user-libs/flanterm/libflanterm.o: user_lib_ld $objects" >> build.ninja + for dir in src/user-libs/*; do lib_name=$(echo $dir | sed -e 's/^src\/user-libs\///') build_all $dir user_cc user_lib_ld lib$lib_name.o diff --git a/qemu.gdb b/qemu.gdb index 6de1ab4..8ec043a 100644 --- a/qemu.gdb +++ b/qemu.gdb @@ -1,6 +1,5 @@ target remote | qemu-system-x86_64 -gdb stdio -cdrom build/disk.iso -boot d symbol-file build/kernel/kernel.elf -add-symbol-file build/user-apps/hello/hello.elf set disassembly-flavor intel set print asm-demangle on layout src diff --git a/readme.txt b/readme.txt index 5df6c0f..5c3f22f 100644 --- a/readme.txt +++ b/readme.txt @@ -48,17 +48,25 @@ information. If you already built Calcite in "release" mode, run: sh make-build.sh debug ninja -If you did not already built Calcite, then you do not need the first command. +If you did not already build Calcite, then you do not need the first command. === License -Calcite is Copyright 2025 Benji Dial, and licensed under the GNU GPL v3. See -"license.txt" for the text of the license. +Calcite is Copyright 2025-2026 Benji Dial, and licensed under the GNU GPL v3. +See "license.txt" for the text of the license. -The disk image includes the Limine bootloader. Limine is Copyright 2019-2025 -Mintsuki and contributors, and is released under the BSD 2-Clause license. -You can find Limine online at . -After running "sh get-dependencies.sh", you can find the license of Limine at -"dependencies/limine/LICENSE". Limine also has its own dependencies. In the -source repository of Limine, you can find a list of these with their licenses -at "3RDPARTY.md". +The disk image includes the Limine bootloader, version 9.3.4. Limine is +Copyright 2019-2025 Mintsuki and contributors, and is released under the +BSD 2-Clause license. You can find Limine online at + . +After running "sh get-dependencies.sh", you can find the license of Limine +at "dependencies/limine/LICENSE". Limine also has its own dependencies. +In the source repository of Limine, you can find a list of these with +their licenses at "3RDPARTY.md". + +The terminal, included in the disk image, is linked with the Flanterm library, +version 2.1.0. Flanterm is Copyright 2022-2025 minstuki and contributors, and +is released under the BSD 2-Clause license. You can find Flanterm online at + . +After running "sh get-dependencies.sh", you can find the license of Flanterm +at "dependencies/flanterm/LICENSE". diff --git a/src/kernel/debug.h b/src/kernel/debug.h index 82f80d4..3c531d1 100644 --- a/src/kernel/debug.h +++ b/src/kernel/debug.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/debug.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -19,12 +19,14 @@ void log_core(const char *file, const char *function, const char *format, ...); -#ifdef CALCITE_DEBUG -#define debug_log(...) { \ +#define log(...) { \ log_core(__FILE__, __func__, __VA_ARGS__); \ } + +#ifdef CALCITE_DEBUG +#define debug_log(...) log(__VA_ARGS__) #elif CALCITE_RELEASE -#define debug_log(format, ...) {} +#define debug_log(...) {} #else #error neither CALCITE_DEBUG nor CALCITE_RELEASE defined #endif diff --git a/src/kernel/entry.c b/src/kernel/entry.c index b725c2d..7303b55 100644 --- a/src/kernel/entry.c +++ b/src/kernel/entry.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/entry.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -17,6 +17,7 @@ #include "framebuffer.h" #include "interrupts.h" +#include "ipc-dgram.h" #include "scheduler.h" #include "syscalls.h" #include "process.h" @@ -279,6 +280,17 @@ static const char *cmdline_look_up(const char *key) { register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram); register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread); register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar); + register_syscall(SYSCALL_CREATE_MUTEX, (void *)&syscall_create_mutex); + register_syscall(SYSCALL_ACQUIRE_MUTEX, (void *)&syscall_acquire_mutex); + register_syscall(SYSCALL_RELEASE_MUTEX, (void *)&syscall_release_mutex); + register_syscall(SYSCALL_CREATE_EVENT, (void *)&syscall_create_event); + register_syscall(SYSCALL_SIGNAL_EVENT, (void *)&syscall_signal_event); + register_syscall(SYSCALL_WAIT_EVENT, (void *)&syscall_wait_event); + register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_PIPE, (void *)&syscall_ipc_create_private_dgram_pipe); + register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_private_dgram_receiver); + register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER, (void *)&syscall_ipc_create_private_dgram_sender); + register_syscall(SYSCALL_GET_MS_SINCE_BOOT, (void *)&syscall_get_ms_since_boot); + register_syscall(SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT, (void *)&syscall_sleep_until_ms_since_boot); //probe pci devices diff --git a/src/kernel/interrupts.asm b/src/kernel/interrupts.asm index 3dcd723..1fd0f10 100644 --- a/src/kernel/interrupts.asm +++ b/src/kernel/interrupts.asm @@ -1,5 +1,5 @@ ; Calcite, src/kernel/interrupts.asm - ; Copyright 2025 Benji Dial + ; Copyright 2025-2026 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 @@ -71,6 +71,10 @@ isr_exception_common: push r13 push r14 push r15 + mov rax, cr2 + push rax + mov rax, cr3 + push rax mov rdi, rsp jmp isr_exception_c diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index c10326f..f1d64a4 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/interrupts.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -24,7 +24,10 @@ #include "interrupts.h" #include "debug.h" +//interrupts.asm depends on layout struct [[gnu::packed]] exception_parameter { + uint64_t cr3; + uint64_t cr2; uint64_t r15; uint64_t r14; uint64_t r13; @@ -50,10 +53,60 @@ struct [[gnu::packed]] exception_parameter { uint64_t ss; }; +//names are from amd64 manual mentioned above +const char *exception_names[32] = { + "divide-by-zero-error", "debug", "non-maskable-interrupt", "breakpoint", "overflow", + "bound-range", "invalid-opcode", "device-not-available", "double-fault", 0, + "invalid-tss", "segment-not-present", "stack", "general-protection", "page-fault", + 0, "x87 floating-point exception-pending", "alignment-check", "machine-check", + "simd floating-point", 0, 0, 0, 0, 0, 0, 0, 0, + "hypervisor injection exception", "vmm communication exception", "security exception", 0 +}; + +[[noreturn]] static void unhandled_exception(struct exception_parameter *parameter) { + + assert(parameter->exception_number < 32 && exception_names[parameter->exception_number] != 0) + + log("unhandled exception") + log(" exception name = %s", exception_names[parameter->exception_number]) + log(" error code = 0x%h", parameter->error_code, 16) + log(" cr2 = 0x%h", parameter->cr2, 16) + log(" cr3 = 0x%h", parameter->cr3, 16) + log(" cs = 0x%h", parameter->cs, 16) + log(" rip = 0x%h", parameter->rip, 16) + log(" ss = 0x%h", parameter->ss, 16) + log(" rsp = 0x%h", parameter->rsp, 16) + log(" rflags = 0x%h", parameter->rflags, 16) + log(" rax = 0x%h", parameter->rax, 16) + log(" rbx = 0x%h", parameter->rbx, 16) + log(" rcx = 0x%h", parameter->rcx, 16) + log(" rdx = 0x%h", parameter->rdx, 16) + log(" rdi = 0x%h", parameter->rdi, 16) + log(" rsi = 0x%h", parameter->rsi, 16) + log(" rbp = 0x%h", parameter->rbp, 16) + log(" r8 = 0x%h", parameter->r8, 16) + log(" r9 = 0x%h", parameter->r9, 16) + log(" r10 = 0x%h", parameter->r10, 16) + log(" r11 = 0x%h", parameter->r11, 16) + log(" r12 = 0x%h", parameter->r12, 16) + log(" r13 = 0x%h", parameter->r13, 16) + log(" r14 = 0x%h", parameter->r14, 16) + log(" r15 = 0x%h", parameter->r15, 16) + log("halting") + + __asm__ ("cli"); + while (1) + __asm__ ("hlt"); + +} + //referenced in interrupts.asm void isr_exception_c(struct exception_parameter *parameter) { - (void)parameter; - panic("TODO"); + + //eventually some exceptions will be handled in here + + unhandled_exception(parameter); + } void (*irq_handlers[16])() = { diff --git a/src/kernel/ipc-dgram.c b/src/kernel/ipc-dgram.c index 083a09c..74c64fe 100644 --- a/src/kernel/ipc-dgram.c +++ b/src/kernel/ipc-dgram.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/ipc-dgram.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -16,6 +16,7 @@ */ #include "ipc-dgram.h" +#include "process.h" #include "scheduler.h" #include "utility.h" #include "debug.h" @@ -51,6 +52,7 @@ struct ipc_dgram_box *get_ipc_dgram_box(const char *address) { new->address = new_address; new->box.is_receiver_held = 0; + new->box.is_private = 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; @@ -145,3 +147,123 @@ enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data return IPR_SUCCESS; } + +static uint64_t next_private_id = 0; + +struct ipc_dgram_private_book { + uint64_t id; + struct ipc_dgram_box *box; + struct ipc_dgram_private_book *prev; +}; + +static struct ipc_dgram_private_book *last_without_receiver = 0; +static struct ipc_dgram_private_book *last_without_sender = 0; + +//0 for none with that id +static struct ipc_dgram_box *take_from_private_book(struct ipc_dgram_private_book **book, uint64_t id) { + + while (*book != 0) { + + if ((*book)->id == id) { + struct ipc_dgram_box *box = (*book)->box; + struct ipc_dgram_private_book *prev = (*book)->prev; + heap_dealloc(*book, sizeof(struct ipc_dgram_private_book)); + *book = prev; + return box; + } + + book = &(*book)->prev; + + } + + return 0; + +} + +uint64_t syscall_ipc_create_private_dgram_pipe() { + + uint64_t id = next_private_id++; + + struct ipc_dgram_box *box = heap_alloc(sizeof(struct ipc_dgram_box)); + box->is_receiver_held = 0; + box->is_private = 1; + box->is_someone_waiting_to_receive = 0; + create_queue(&box->waiting_to_send, INITIAL_SEND_BACKLOG); + box->buffer_read_pointer = 0; + box->buffer_read_available = 0; + + struct ipc_dgram_private_book *without_receiver = heap_alloc(sizeof(struct ipc_dgram_private_book)); + without_receiver->id = id; + without_receiver->box = box; + without_receiver->prev = last_without_receiver; + last_without_receiver = without_receiver; + + struct ipc_dgram_private_book *without_sender = heap_alloc(sizeof(struct ipc_dgram_private_book)); + without_sender->id = id; + without_sender->box = box; + without_sender->prev = last_without_sender; + last_without_sender = without_sender; + + return id; + +} + +enum ipc_dgram_result syscall_ipc_create_private_dgram_receiver( + uint64_t id, ipc_dgram_receiver_handle_t *handle_out) { + + assert(running_thread != 0) + + if (!is_mapped_writable( + running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t))) + syscall_illegal_args(); + + struct ipc_dgram_box *box = take_from_private_book(&last_without_receiver, id); + if (box == 0) + return IPR_BAD_HANDLE; + + assert(box->is_receiver_held == 0) + assert(box->private_sides < 2) + + box->is_receiver_held = 1; + ++box->private_sides; + + struct process_ipc_dgram_handle_info *handle_info = + add_handle(&running_thread->process->ipc_dgram_handles, handle_out); + + handle_info->box = box; + handle_info->is_receiver = 1; + return IPR_SUCCESS; + +} + +enum ipc_dgram_result syscall_ipc_create_private_dgram_sender( + uint64_t id, ipc_dgram_sender_handle_t *handle_out) { + + assert(running_thread != 0) + + if (!is_mapped_writable( + running_thread->process, handle_out, sizeof(ipc_dgram_sender_handle_t))) + syscall_illegal_args(); + + struct ipc_dgram_box *box = take_from_private_book(&last_without_sender, id); + if (box == 0) + return IPR_BAD_HANDLE; + + assert(box->private_sides < 2) + + ++box->private_sides; + + struct process_ipc_dgram_handle_info *handle_info = + add_handle(&running_thread->process->ipc_dgram_handles, handle_out); + + handle_info->box = box; + handle_info->is_receiver = 0; + return IPR_SUCCESS; + +} + +void destroy_ipc_dgram_box(struct ipc_dgram_box *box) { + assert(box->is_someone_waiting_to_receive == 0) + assert(box->waiting_to_send.count == 0) + destroy_queue(&box->waiting_to_send); +} diff --git a/src/kernel/ipc-dgram.h b/src/kernel/ipc-dgram.h index d48c735..fd399f8 100644 --- a/src/kernel/ipc-dgram.h +++ b/src/kernel/ipc-dgram.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/ipc-dgram.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -27,6 +27,8 @@ struct ipc_dgram_box { int is_receiver_held; + int is_private; + int private_sides; int is_someone_waiting_to_receive; struct continuation_info waiting_to_receive; @@ -56,3 +58,11 @@ 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); + +uint64_t syscall_ipc_create_private_dgram_pipe(); +enum ipc_dgram_result syscall_ipc_create_private_dgram_receiver( + uint64_t id, ipc_dgram_receiver_handle_t *handle_out); +enum ipc_dgram_result syscall_ipc_create_private_dgram_sender( + uint64_t id, ipc_dgram_sender_handle_t *handle_out); + +void destroy_ipc_dgram_box(struct ipc_dgram_box *box); diff --git a/src/kernel/process.c b/src/kernel/process.c index 73a696c..b3096d6 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/process.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -27,14 +27,9 @@ #include #include +#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) - return 0; - return &process->files[handle]; -} - void create_process(struct process *process_out) { process_out->p4_physical_base = take_free_physical_page(); @@ -64,10 +59,14 @@ void create_process(struct process *process_out) { } process_out->n_threads = 0; - process_out->files = 0; - process_out->ipc_dgram_handles = 0; process_out->env_pairs = 0; + create_handle_list(&process_out->file_handles, sizeof(struct process_file_info)); + create_handle_list( + &process_out->ipc_dgram_handles, sizeof(struct process_ipc_dgram_handle_info)); + create_handle_list(&process_out->mutex_handles, sizeof(struct process_mutex_info)); + create_handle_list(&process_out->event_handles, sizeof(struct process_event_info)); + } void map_page_for_process( @@ -455,25 +454,44 @@ int syscall_start_elf(const char *path, const struct process_start_info *info) { } +static void destroy_process_file_info(struct process_file_info *info) { + (*info->fs->free_node)(info->fs, info->node); +} + +static void destroy_process_ipc_dgram_handle_info(struct process_ipc_dgram_handle_info *info) { + if (info->is_receiver == 1) + info->box->is_receiver_held = 0; + if (info->box->is_private) { + if (info->box->private_sides == 1) { + destroy_ipc_dgram_box(info->box); + heap_dealloc(info->box, sizeof(struct ipc_dgram_box)); + } + else + --info->box->private_sides; + } +} + +static void destroy_process_mutex_info(struct process_mutex_info *info) { + destroy_queue(&info->waiting_to_acquire); +} + +static void destroy_process_event_info(struct process_event_info *info) { + destroy_queue(&info->waiting_for_signal); +} + void destroy_process(struct process *process) { assert(process->n_threads == 0) - if (process->files) { - for (int i = 0; i < process->files_buffer_size; ++i) - if (process->files[i].fs != 0) - (*process->files[i].fs->free_node)(process->files[i].fs, process->files[i].node); - heap_dealloc(process->files, process->files_buffer_size * sizeof(struct process_file_info)); - } + for_each_handle(&process->file_handles, (void *)&destroy_process_file_info); + for_each_handle(&process->ipc_dgram_handles, (void *)&destroy_process_ipc_dgram_handle_info); + for_each_handle(&process->mutex_handles, (void *)&destroy_process_mutex_info); + for_each_handle(&process->event_handles, (void *)&destroy_process_event_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)); - } + destroy_handle_list(&process->file_handles); + destroy_handle_list(&process->ipc_dgram_handles); + destroy_handle_list(&process->mutex_handles); + destroy_handle_list(&process->event_handles); if (process->env_pairs) { for (int i = 0; i < process->env_pairs_buffer_size; ++i) @@ -645,40 +663,10 @@ enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_ return result; } - if (running_thread->process->files == 0) { - running_thread->process->files = - heap_alloc(INITIAL_FILE_HANDLE_COUNT * sizeof(struct process_file_info)); - running_thread->process->files_buffer_size = INITIAL_FILE_HANDLE_COUNT; - for (int i = 0; i < INITIAL_FILE_HANDLE_COUNT; ++i) - running_thread->process->files[i].fs = 0; - } - - for (int i = 0; i < running_thread->process->files_buffer_size; ++i) - if (running_thread->process->files[i].fs == 0) { - running_thread->process->files[i].fs = fs; - running_thread->process->files[i].node = node; - memcpy(&running_thread->process->files[i].stat, &stat, sizeof(struct fs_stat)); - *handle_out = i; - return FAR_SUCCESS; - } - - struct process_file_info *old_buffer = running_thread->process->files; - int old_size = running_thread->process->files_buffer_size; - - struct process_file_info *new_buffer = heap_alloc(2 * old_size * sizeof(struct process_file_info)); - memcpy(new_buffer, old_buffer, old_size * sizeof(struct process_file_info)); - heap_dealloc(old_buffer, old_size * sizeof(struct process_file_info)); - - new_buffer[old_size].fs = fs; - new_buffer[old_size].node = node; - memcpy(&new_buffer[old_size].stat, &stat, sizeof(struct fs_stat)); - for (int i = old_size + 1; i < old_size * 2; ++i) - new_buffer[i].fs = 0; - - running_thread->process->files = new_buffer; - running_thread->process->files_buffer_size *= 2; - - *handle_out = old_size; + struct process_file_info *info = add_handle(&running_thread->process->file_handles, handle_out); + info->fs = fs; + info->node = node; + memcpy(&info->stat, &stat, sizeof(struct fs_stat)); return FAR_SUCCESS; } @@ -687,7 +675,7 @@ void syscall_close_file(file_handle_t handle) { assert(running_thread != 0) - struct process_file_info *file = get_file_info(running_thread->process, handle); + struct process_file_info *file = look_up_handle(&running_thread->process->file_handles, handle); if (file != 0) { (*file->fs->free_node)(file->fs, file->node); file->fs = 0; @@ -699,7 +687,7 @@ enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *byte assert(running_thread != 0) - struct process_file_info *file = get_file_info(running_thread->process, handle); + struct process_file_info *file = look_up_handle(&running_thread->process->file_handles, handle); if (file == 0) return FAR_BAD_HANDLE; @@ -718,7 +706,8 @@ enum fs_access_result syscall_read_file(struct read_file_parameter *parameter) { running_thread->process, parameter->buffer, parameter->bytes)) syscall_illegal_args(); - struct process_file_info *file = get_file_info(running_thread->process, parameter->handle); + struct process_file_info *file = + look_up_handle(&running_thread->process->file_handles, parameter->handle); if (file == 0) return FAR_BAD_HANDLE; @@ -747,46 +736,6 @@ void *syscall_map_pages(uint64_t count) { } -#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) { @@ -803,8 +752,8 @@ enum ipc_dgram_result syscall_ipc_create_dgram_receiver( 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]; + struct process_ipc_dgram_handle_info *info = + add_handle(&running_thread->process->ipc_dgram_handles, handle_out); info->box = box; info->is_receiver = 1; return IPR_SUCCESS; @@ -823,8 +772,8 @@ enum ipc_dgram_result syscall_ipc_create_dgram_sender( 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]; + struct process_ipc_dgram_handle_info *info = + add_handle(&running_thread->process->ipc_dgram_handles, handle_out); info->box = box; info->is_receiver = 0; return IPR_SUCCESS; @@ -836,18 +785,17 @@ enum ipc_dgram_result syscall_ipc_receive_dgram( assert(running_thread != 0) - if (!is_mapped_writable(running_thread->process, bytes, sizeof(uint64_t)) || + if (!is_mapped_writable(running_thread->process, bytes, sizeof(int)) || !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) + struct process_ipc_dgram_handle_info *info = + look_up_handle(&running_thread->process->ipc_dgram_handles, handle); + + if (info == 0 || info->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); + int actual_bytes = get_ipc_dgram_size(info->box); assert(actual_bytes > 0); if (actual_bytes > *bytes) { @@ -855,7 +803,7 @@ enum ipc_dgram_result syscall_ipc_receive_dgram( return IPR_TOO_BIG; } - receive_ipc_dgram(box, buffer); + receive_ipc_dgram(info->box, buffer); *bytes = actual_bytes; return IPR_SUCCESS; @@ -869,14 +817,13 @@ enum ipc_dgram_result syscall_ipc_send_dgram( 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) + struct process_ipc_dgram_handle_info *info = + look_up_handle(&running_thread->process->ipc_dgram_handles, handle); + + if (info == 0 || info->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); + return send_ipc_dgram(info->box, data, bytes); } @@ -989,3 +936,105 @@ void syscall_get_envvar(const char *key, char *value_out, int *value_space) { memcpy(value_out, pair->value, pair->value_length + 1); } + +mutex_handle_t syscall_create_mutex() { + + assert(running_thread != 0) + + mutex_handle_t handle; + struct process_mutex_info *info = + add_handle(&running_thread->process->mutex_handles, &handle); + + info->is_acquired = 0; + create_queue(&info->waiting_to_acquire, 4); + + return handle; + +} + +void syscall_acquire_mutex(mutex_handle_t handle) { + + assert(running_thread != 0) + + struct process_mutex_info *info = look_up_handle(&running_thread->process->mutex_handles, handle); + if (info == 0) + syscall_illegal_args(); + + while (info->is_acquired == 1) + yield_to_queue(&info->waiting_to_acquire); + + info->is_acquired = 1; + +} + +void syscall_release_mutex(mutex_handle_t handle) { + + assert(running_thread != 0) + + struct process_mutex_info *info = look_up_handle(&running_thread->process->mutex_handles, handle); + if (info == 0 || info->is_acquired == 0) + syscall_illegal_args(); + + info->is_acquired = 0; + + struct continuation_info ci; + if (take_from_queue(&info->waiting_to_acquire, &ci)) + add_to_queue(&ready_continuations, &ci); + +} + +event_handle_t syscall_create_event() { + + assert(running_thread != 0) + + event_handle_t handle; + struct process_event_info *info = + add_handle(&running_thread->process->event_handles, &handle); + + info->is_signaled = 0; + create_queue(&info->waiting_for_signal, 4); + + return handle; + +} + +void syscall_signal_event(event_handle_t handle) { + + assert(running_thread != 0) + + struct process_event_info *info = look_up_handle(&running_thread->process->event_handles, handle); + if (info == 0) + syscall_illegal_args(); + + if (info->is_signaled == 1) + return; + + int resumed_any = 0; + + struct continuation_info ci; + while (take_from_queue(&info->waiting_for_signal, &ci)) { + add_to_queue(&ready_continuations, &ci); + resumed_any = 1; + } + + if (resumed_any == 0) + info->is_signaled = 1; + +} + +void syscall_wait_event(event_handle_t handle) { + + assert(running_thread != 0) + + struct process_event_info *info = look_up_handle(&running_thread->process->event_handles, handle); + if (info == 0) + syscall_illegal_args(); + + if (info->is_signaled == 1) { + info->is_signaled = 0; + return; + } + + yield_to_queue(&info->waiting_for_signal); + +} diff --git a/src/kernel/process.h b/src/kernel/process.h index 727390e..8915003 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/process.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -17,11 +17,14 @@ #pragma once +#include "scheduler.h" +#include "utility.h" #include "fs.h" #include #include #include +#include #include struct ipc_dgram_box; @@ -33,12 +36,21 @@ struct process_file_info { }; 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_mutex_info { + int is_acquired; + struct continuation_queue waiting_to_acquire; +}; + +struct process_event_info { + int is_signaled; + struct continuation_queue waiting_for_signal; +}; + //lengths don't include null terminators, buffers do. //key and value should not be empty. struct process_env_pair { @@ -49,6 +61,7 @@ struct process_env_pair { char *value; }; +//layout used in scheduler.asm struct process { uint64_t p4_physical_base; @@ -58,13 +71,10 @@ struct process { int n_threads; - //handles are indices into this. - //0 for fs means unused handle. - struct process_file_info *files; - int files_buffer_size; - - struct process_ipc_dgram_handle_info *ipc_dgram_handles; - int ipc_dgram_handles_buffer_size; + struct handle_list file_handles; + struct handle_list ipc_dgram_handles; + struct handle_list mutex_handles; + struct handle_list event_handles; struct process_env_pair *env_pairs; int env_pairs_buffer_size; @@ -79,11 +89,7 @@ struct process { }; -//returns 0 if that handle is not used. -//return value might be invalidated by future allocation of file handles for this process. -struct process_file_info *get_file_info(struct process *process, file_handle_t handle); - -//layout used in syscalls.asm +//layout used in syscalls.asm and scheduler.asm struct thread { struct process *process; @@ -184,3 +190,11 @@ struct process_env_pair *get_envvar(struct process *process, const char *key); //see comment in include/calcite/syscalls.h void syscall_get_envvar(const char *key, char *value_out, int *value_space); + +mutex_handle_t syscall_create_mutex(); +void syscall_acquire_mutex(mutex_handle_t handle); +void syscall_release_mutex(mutex_handle_t handle); + +event_handle_t syscall_create_event(); +void syscall_signal_event(event_handle_t handle); +void syscall_wait_event(event_handle_t handle); diff --git a/src/kernel/scheduler.asm b/src/kernel/scheduler.asm index 0937cce..c6ae2b1 100644 --- a/src/kernel/scheduler.asm +++ b/src/kernel/scheduler.asm @@ -1,5 +1,5 @@ ; Calcite, src/kernel/scheduler.asm - ; Copyright 2025 Benji Dial + ; Copyright 2025-2026 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 @@ -36,6 +36,9 @@ resume_continuation: mov r15, qword [rdi + 56] mov rdi, qword [rdi + 64] mov qword [running_thread], rdi + mov rdi, qword [rdi] + mov rdi, qword [rdi] + mov cr3, rdi jmp rax @@ -93,7 +96,7 @@ yield_to_queue: sub rsp, 72 mov rsi, rsp call add_to_queue - jmp resume_continuation + jmp resume_next_continuation .ret: ret diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index ccaa8a6..68aaaa6 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/scheduler.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -51,6 +51,11 @@ void create_queue(struct continuation_queue *queue, int buffer_size) { queue->count = 0; } +void destroy_queue(struct continuation_queue *queue) { + assert(queue->count == 0) + heap_dealloc(queue->cis, queue->buffer_size * sizeof(struct continuation_info)); +} + void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci) { if (queue->count == queue->buffer_size) { diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index 7d1a866..2475f98 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/scheduler.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -17,10 +17,11 @@ #pragma once -#include "process.h" - #include +struct thread; + +//scheduler.asm depends on layout struct continuation_info { uint64_t rip; uint64_t rbx; @@ -47,6 +48,7 @@ struct continuation_queue { extern struct continuation_queue ready_continuations; void create_queue(struct continuation_queue *queue, int buffer_size); +void destroy_queue(struct continuation_queue *queue); //ci is copied void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci); diff --git a/src/kernel/timer.c b/src/kernel/timer.c index 405da78..31a792f 100644 --- a/src/kernel/timer.c +++ b/src/kernel/timer.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/timer.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -91,3 +91,11 @@ static void sleep_until(uint64_t until) { void syscall_sleep_ms(uint64_t ms_to_sleep) { sleep_until(pit_irqs_since_boot + ms_to_sleep); } + +uint64_t syscall_get_ms_since_boot() { + return pit_irqs_since_boot; +} + +void syscall_sleep_until_ms_since_boot(uint64_t ms) { + sleep_until(ms); +} diff --git a/src/kernel/timer.h b/src/kernel/timer.h index 9e53a09..c4466e2 100644 --- a/src/kernel/timer.h +++ b/src/kernel/timer.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/timer.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -24,3 +24,5 @@ void init_timer(); void on_pit_irq(); void syscall_sleep_ms(uint64_t ms_to_sleep); +uint64_t syscall_get_ms_since_boot(); +void syscall_sleep_until_ms_since_boot(uint64_t ms); diff --git a/src/kernel/utility.c b/src/kernel/utility.c index 2058505..ed5245e 100644 --- a/src/kernel/utility.c +++ b/src/kernel/utility.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/utility.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -45,3 +45,73 @@ void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry) { *buffer = new_buffer; *length *= 2; } + +void create_handle_list(struct handle_list *list, uint64_t entry_size) { + list->entry_size = entry_size; + list->buffer = 0; + list->buffer_used_bitmap = 0; + list->buffer_size = 0; +} + +//must be multiple of 8 +#define HANDLE_LIST_INITIAL_SIZE 16 + +void *add_handle(struct handle_list *list, uint64_t *handle_out) { + + if (list->buffer == 0) { + list->buffer = heap_alloc(HANDLE_LIST_INITIAL_SIZE * list->entry_size); + list->buffer_used_bitmap = heap_alloc(HANDLE_LIST_INITIAL_SIZE / 8); + list->buffer_size = HANDLE_LIST_INITIAL_SIZE; + list->buffer_used_bitmap[0] = 1; + memzero(list->buffer_used_bitmap + 1, HANDLE_LIST_INITIAL_SIZE / 8 - 1); + *handle_out = 0; + return list->buffer; + } + + for (uint64_t i = 0; i < list->buffer_size; ++i) + if ((list->buffer_used_bitmap[i / 8] & (1 << (i % 8))) == 0) { + list->buffer_used_bitmap[i / 8] |= (1 << (i % 8)); + *handle_out = i; + return list->buffer + i * list->entry_size; + } + + void *new_buffer = heap_alloc(2 * list->buffer_size * list->entry_size); + uint8_t *new_used_bitmap = heap_alloc(list->buffer_size / 4); + + memcpy(new_buffer, list->buffer, list->buffer_size * list->entry_size); + memcpy(new_used_bitmap, list->buffer_used_bitmap, list->buffer_size / 8); + + heap_dealloc(list->buffer, list->buffer_size * list->entry_size); + heap_dealloc(list->buffer_used_bitmap, list->buffer_size / 8); + + new_used_bitmap[list->buffer_size / 8] = 1; + memzero(new_used_bitmap + list->buffer_size / 8 + 1, list->buffer_size / 8 - 1); + + list->buffer = new_buffer; + list->buffer_used_bitmap = new_used_bitmap; + list->buffer_size *= 2; + + *handle_out = list->buffer_size / 2; + return list->buffer + (list->buffer_size / 2) * list->entry_size; + +} + +void *look_up_handle(struct handle_list *list, uint64_t handle) { + if (list->buffer == 0 || handle >= list->buffer_size || + (list->buffer_used_bitmap[handle / 8] & (1 << (handle % 8))) == 0) + return 0; + return list->buffer + handle * list->entry_size; +} + +void for_each_handle(struct handle_list *list, void (*callback)(void *object)) { + for (uint64_t i = 0; i < list->buffer_size; ++i) + if ((list->buffer_used_bitmap[i / 8] & (1 << (i % 8))) != 0) + (*callback)(list->buffer + i * list->entry_size); +} + +void destroy_handle_list(struct handle_list *list) { + if (list->buffer != 0) { + heap_dealloc(list->buffer, list->buffer_size * list->entry_size); + heap_dealloc(list->buffer_used_bitmap, list->buffer_size / 8); + } +} diff --git a/src/kernel/utility.h b/src/kernel/utility.h index 6bde244..e9f35b8 100644 --- a/src/kernel/utility.h +++ b/src/kernel/utility.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/utility.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -39,3 +39,19 @@ void outb(uint16_t port, uint8_t byte); uint8_t inb(uint16_t port); void outsw(uint16_t port, const void *buffer, uint64_t words); void insw(uint16_t port, void *buffer, uint64_t words); + +struct handle_list { + uint64_t entry_size; + void *buffer; + uint8_t *buffer_used_bitmap; + //in entries, always a multiple of 8 + uint64_t buffer_size; +}; + +void create_handle_list(struct handle_list *list, uint64_t entry_size); +//add to list by calling this and writing into returned object +void *add_handle(struct handle_list *list, uint64_t *handle_out); +//zero if not in list +void *look_up_handle(struct handle_list *list, uint64_t handle); +void for_each_handle(struct handle_list *list, void (*callback)(void *object)); +void destroy_handle_list(struct handle_list *list); diff --git a/src/user-apps/hello/hello.c b/src/user-apps/hello/hello.c deleted file mode 100644 index 9922874..0000000 --- a/src/user-apps/hello/hello.c +++ /dev/null @@ -1,151 +0,0 @@ -/* Calcite, src/user-apps/hello/hello.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 - -static struct framebuffer_info fb_info; -static struct image *fbb; - -static struct image *cursor_image; - -static void draw_bg(int startx, int starty, int width, int height) { - if (startx + width > fbb->width) - width = fbb->width - startx; - if (starty + height > fbb->height) - height = fbb->height - starty; - for (int y = starty; y < starty + height; ++y) - for (int x = startx; x < startx + width; ++x) { - struct pixel *pixel = &fbb->pixels[y * fbb->width + x]; - pixel->r = 0; - pixel->g = y * 256 / fb_info.fb_height; - pixel->b = x * 256 / fb_info.fb_width; - } -} - -static void draw_cursor(int x, int y) { - for (int yy = 0; yy < cursor_image->height && y + yy < fbb->height; ++yy) - for (int xx = 0; xx < cursor_image->width && x + xx < fbb->width; ++xx) { - struct pixel *fb_pixel = &fbb->pixels[(y + yy) * fbb->width + (x + xx)]; - struct pixel *ci_pixel = &cursor_image->pixels[yy * cursor_image->width + xx]; - fb_pixel->r = (fb_pixel->r * (255 - ci_pixel->a) + ci_pixel->r * ci_pixel->a) / 255; - fb_pixel->g = (fb_pixel->g * (255 - ci_pixel->a) + ci_pixel->g * ci_pixel->a) / 255; - fb_pixel->b = (fb_pixel->b * (255 - ci_pixel->a) + ci_pixel->b * ci_pixel->a) / 255; - } -} - -static void copy_frame() { - for (int y = 0; y < fbb->height; ++y) - for (int x = 0; x < fbb->width; ++x) { - uint8_t *fb_pixel = &fb_info.fb_base[y * fb_info.fb_pitch + x * 4]; - struct pixel *fbb_pixel = &fbb->pixels[y * fbb->width + x]; - fb_pixel[0] = fbb_pixel->b; - fb_pixel[1] = fbb_pixel->g; - fb_pixel[2] = fbb_pixel->r; - } -} - -[[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(); -} - -static int strequ(const char *str1, const char *str2) { - while (1) { - if (*str1 == 0 && *str2 == 0) - return 1; - if (*str1 != *str2) - return 0; - ++str1; - ++str2; - } -} - -static int memequ(const char *str1, const char *str2, int length) { - for (int i = 0; i < length; ++i) - if (str1[i] != str2[i]) - return 0; - return 1; -} - -void main() { - - map_framebuffer(&fb_info); - create_image(fb_info.fb_width, fb_info.fb_height, &fbb); - - char buffer[100]; - - int bytes = 100; - get_envvar("hello", buffer, &bytes); - if (bytes != 6 || !strequ(buffer, "world")) - panic(); - - ipc_dgram_receiver_handle_t receiver; - if (ipc_create_dgram_receiver("calcite-test", &receiver) != IPR_SUCCESS) - panic(); - - bytes = 100; - if (ipc_receive_dgram(receiver, buffer, &bytes) != IPR_SUCCESS || - bytes != 5 || !memequ(buffer, "hello", 5)) - 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; - - draw_bg(0, 0, fbb->width, fbb->height); - draw_cursor(cursor_x, cursor_y); - copy_frame(); - - while (1) { - - sleep_ms(10); - - struct mouse_packet packet; - wait_for_mouse_packet(&packet); - - draw_bg(cursor_x, cursor_y, cursor_image->width, cursor_image->height); - - cursor_x += packet.x_change; - cursor_y -= packet.y_change; - - if (cursor_x < 0) - cursor_x = 0; - if (cursor_x >= fbb->width) - cursor_x = fbb->width - 1; - - if (cursor_y < 0) - cursor_y = 0; - if (cursor_y >= fbb->height) - cursor_y = fbb->height - 1; - - draw_cursor(cursor_x, cursor_y); - copy_frame(); - - } - -} diff --git a/src/user-apps/hello/libraries.txt b/src/user-apps/shell/libraries.txt similarity index 53% rename from src/user-apps/hello/libraries.txt rename to src/user-apps/shell/libraries.txt index 8412fe5..8503661 100644 --- a/src/user-apps/hello/libraries.txt +++ b/src/user-apps/shell/libraries.txt @@ -1,2 +1 @@ calcite -silver diff --git a/src/user-apps/shell/shell.c b/src/user-apps/shell/shell.c new file mode 100644 index 0000000..fd3f3db --- /dev/null +++ b/src/user-apps/shell/shell.c @@ -0,0 +1,26 @@ +/* Calcite, src/user-apps/shell/shell.c + * Copyright 2026 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 + +void main() { + init_terminal(); + terminal_write("hello\n", 6); + while (1) + sleep_ms(1000000000); +} diff --git a/src/user-apps/terminal/libraries.txt b/src/user-apps/terminal/libraries.txt new file mode 100644 index 0000000..12c28c2 --- /dev/null +++ b/src/user-apps/terminal/libraries.txt @@ -0,0 +1,2 @@ +calcite +flanterm diff --git a/src/user-apps/terminal/terminal.c b/src/user-apps/terminal/terminal.c new file mode 100644 index 0000000..84f168a --- /dev/null +++ b/src/user-apps/terminal/terminal.c @@ -0,0 +1,120 @@ +/* Calcite, src/user-apps/terminal/terminal.c + * Copyright 2026 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 +#include +#include +#include +#include +#include + +static struct flanterm_context *flanterm_context; + +static ipc_dgram_receiver_handle_t input_handle; +static ipc_dgram_receiver_handle_t output_handle; + +static struct dispatch_queue *queue; + +struct got_output_param { + int dgram_bytes; + void *dgram; +}; + +static void got_output(void *x) { + struct got_output_param *param = x; + flanterm_write(flanterm_context, param->dgram, param->dgram_bytes); + heap_dealloc(param->dgram, param->dgram_bytes); + heap_dealloc(param, sizeof(struct got_output_param)); +} + +[[noreturn]] static void output_thread(uint64_t) { + + while (1) { + + int dgram_bytes = 0; + ipc_receive_dgram(output_handle, 0, &dgram_bytes); + + void *dgram = heap_alloc(dgram_bytes); + ipc_receive_dgram(output_handle, dgram, &dgram_bytes); + + struct got_output_param *param = heap_alloc(sizeof(struct got_output_param)); + param->dgram_bytes = dgram_bytes; + param->dgram = dgram; + + dispatch(queue, &got_output, param); + + } + +} + +[[noreturn]] static void input_thread(uint64_t) { + + //TODO + while (1) + sleep_ms(1000000000); + +} + +void main() { + + uint64_t output_pipe = ipc_create_private_dgram_pipe(); + uint64_t input_pipe = ipc_create_private_dgram_pipe(); + + ipc_create_private_dgram_receiver(output_pipe, &output_handle); + ipc_create_private_dgram_sender(input_pipe, &input_handle); + + struct framebuffer_info fb; + map_framebuffer(&fb); + + flanterm_context = + flanterm_fb_init( + &heap_alloc, &heap_dealloc, (void *)fb.fb_base, fb.fb_width, + fb.fb_height, fb.fb_pitch, 8, 16, 8, + 8, 8, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0); + + queue = create_dispatch_queue(); + + create_thread(&output_thread, 0); + create_thread(&input_thread, 0); + + char input_pipe_hex[17]; + to_hex(input_pipe, 16, input_pipe_hex); + input_pipe_hex[16] = 0; + + char output_pipe_hex[17]; + to_hex(output_pipe, 16, output_pipe_hex); + output_pipe_hex[16] = 0; + + const char *envvars[4] = { + "TERMINAL_INPUT_PIPE_ID", input_pipe_hex, + "TERMINAL_OUTPUT_PIPE_ID", output_pipe_hex }; + + struct process_start_info psi; + psi.forwared_envvar_count = 0; + psi.set_envvar_count = 2; + psi.set_envvars = envvars; + + start_elf("root://calcite/apps/shell/shell.elf", &psi); + + dispatch_loop(queue); + +} diff --git a/src/user-libs/calcite/dispatch.c b/src/user-libs/calcite/dispatch.c new file mode 100644 index 0000000..516f1a1 --- /dev/null +++ b/src/user-libs/calcite/dispatch.c @@ -0,0 +1,87 @@ +/* Calcite, src/user-libs/calcite/dispatch.c + * Copyright 2026 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 + +struct dispatch_callback { + void (*callback)(void *x); + void *x; + struct dispatch_callback *next; +}; + +struct dispatch_queue { + struct dispatch_callback *start; + struct dispatch_callback *end; + mutex_handle_t mutex; + event_handle_t event; +}; + +struct dispatch_queue *create_dispatch_queue() { + struct dispatch_queue *queue = heap_alloc(sizeof(struct dispatch_queue)); + queue->start = 0; + queue->end = 0; + queue->mutex = create_mutex(); + queue->event = create_event(); + return queue; +} + +void dispatch(struct dispatch_queue *queue, void (*callback)(void *x), void *x) { + + struct dispatch_callback *new = heap_alloc(sizeof(struct dispatch_callback)); + new->callback = callback; + new->x = x; + new->next = 0; + + acquire_mutex(queue->mutex); + + if (queue->start == 0) + queue->start = new; + else + queue->end->next = new; + + queue->end = new; + + release_mutex(queue->mutex); + signal_event(queue->event); + +} + +[[noreturn]] void dispatch_loop(struct dispatch_queue *queue) { + + while (1) { + + wait_event(queue->event); + acquire_mutex(queue->mutex); + + struct dispatch_callback *start = queue->start; + queue->start = 0; + queue->end = 0; + + release_mutex(queue->mutex); + + while (start != 0) { + (*start->callback)(start->x); + struct dispatch_callback *next = start->next; + heap_dealloc(start, sizeof(struct dispatch_callback)); + start = next; + } + + } + +} diff --git a/src/user-libs/calcite/format.c b/src/user-libs/calcite/format.c new file mode 100644 index 0000000..650e9e9 --- /dev/null +++ b/src/user-libs/calcite/format.c @@ -0,0 +1,42 @@ +/* Calcite, src/user-libs/calcite/format.c + * Copyright 2026 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 + +void to_hex(uint64_t value, int digit_count, char *buffer) { + for (int i = 0; i < digit_count; ++i) { + char *digit = &buffer[digit_count - 1 - i]; + uint8_t place_value = (value >> (i * 4)) & 0xf; + if (place_value < 10) + *digit = '0' + place_value; + else + *digit = 'a' - 10 + place_value; + } +} + +uint64_t from_hex(int digit_count, char *buffer) { + uint64_t value = 0; + for (int i = 0; i < digit_count; ++i) { + uint64_t place_value = 0; + if (buffer[i] >= '0' && buffer[i] <= '9') + place_value = buffer[i] - '0'; + else if (buffer[i] >= 'a' && buffer[i] <= 'f') + place_value = buffer[i] - 'a' + 10; + value |= place_value << ((digit_count - 1 - i) * 4); + } + return value; +} diff --git a/src/user-libs/calcite/memory.c b/src/user-libs/calcite/memory.c index 06f5c8f..766bbf5 100644 --- a/src/user-libs/calcite/memory.c +++ b/src/user-libs/calcite/memory.c @@ -1,5 +1,5 @@ /* Calcite, src/user-libs/calcite/memory.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -189,3 +189,9 @@ void memcpy(void *to, const void *from, uint64_t bytes) { for (uint64_t i = 0; i < bytes; ++i) to8[i] = from8[i]; } + +void memset(void *start, uint8_t value, uint64_t length) { + uint8_t *start8 = start; + for (uint64_t i = 0; i < length; ++i) + start8[i] = value; +} diff --git a/src/user-libs/calcite/syscalls.c b/src/user-libs/calcite/syscalls.c index 73c3c79..215218a 100644 --- a/src/user-libs/calcite/syscalls.c +++ b/src/user-libs/calcite/syscalls.c @@ -1,5 +1,5 @@ /* Calcite, src/user-libs/calcite/syscalls.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 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 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -98,3 +99,49 @@ void get_envvar(const char *key, char *value_out, int *value_space) { do_syscall( (uint64_t)key, (uint64_t)value_out, (uint64_t)value_space, SYSCALL_GET_ENVVAR); } + +mutex_handle_t create_mutex() { + return do_syscall(0, 0, 0, SYSCALL_CREATE_MUTEX); +} + +void acquire_mutex(mutex_handle_t handle) { + do_syscall(handle, 0, 0, SYSCALL_ACQUIRE_MUTEX); +} + +void release_mutex(mutex_handle_t handle) { + do_syscall(handle, 0, 0, SYSCALL_RELEASE_MUTEX); +} + +event_handle_t create_event() { + return do_syscall(0, 0, 0, SYSCALL_CREATE_EVENT); +} + +void signal_event(event_handle_t handle) { + do_syscall(handle, 0, 0, SYSCALL_SIGNAL_EVENT); +} + +void wait_event(event_handle_t handle) { + do_syscall(handle, 0, 0, SYSCALL_WAIT_EVENT); +} + +uint64_t ipc_create_private_dgram_pipe() { + return do_syscall(0, 0, 0, SYSCALL_IPC_CREATE_PRIVATE_DGRAM_PIPE); +} + +enum ipc_dgram_result ipc_create_private_dgram_receiver(uint64_t id, ipc_dgram_receiver_handle_t *handle_out) { + return do_syscall( + id, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER); +} + +enum ipc_dgram_result ipc_create_private_dgram_sender(uint64_t id, ipc_dgram_sender_handle_t *handle_out) { + return do_syscall( + id, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER); +} + +uint64_t get_ms_since_boot() { + return do_syscall(0, 0, 0, SYSCALL_GET_MS_SINCE_BOOT); +} + +void sleep_until_ms_since_boot(uint64_t ms) { + do_syscall(ms, 0, 0, SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT); +} diff --git a/src/user-libs/calcite/terminal.c b/src/user-libs/calcite/terminal.c new file mode 100644 index 0000000..6f23e52 --- /dev/null +++ b/src/user-libs/calcite/terminal.c @@ -0,0 +1,132 @@ +/* Calcite, src/user-libs/calcite/terminal.c + * Copyright 2026 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 +#include + +static ipc_dgram_receiver_handle_t input_handle; +static ipc_dgram_sender_handle_t output_handle; + +static int set_up_terminal_input = 0; +static int set_up_terminal_output = 0; + +static char *output_buffer; +static int output_buffer_size; +static int output_buffer_used; + +static void output_buffer_size_at_least(int bytes) { + + if (output_buffer_size >= bytes) + return; + + int new_size = output_buffer_size * 2; + while (new_size < bytes) + new_size *= 2; + + char *new_buffer = heap_alloc(new_size); + memcpy(new_buffer, output_buffer, output_buffer_used); + heap_dealloc(output_buffer, output_buffer_size); + output_buffer = new_buffer; + output_buffer_size = new_size; + +} + +#define MAX_PACKET_LENGTH 500 + +static void send_output(const char *buffer, int bytes) { + + if (set_up_terminal_output == 0) + return; + + while (bytes >= MAX_PACKET_LENGTH) { + ipc_send_dgram(output_handle, buffer, MAX_PACKET_LENGTH); + buffer += MAX_PACKET_LENGTH; + bytes -= MAX_PACKET_LENGTH; + } + + if (bytes > 0) + ipc_send_dgram(output_handle, buffer, bytes); + +} + +void init_terminal() { + + output_buffer_size = 512; + output_buffer_used = 0; + output_buffer = heap_alloc(output_buffer_size); + + char hex[17]; + int bytes = 17; + get_envvar("TERMINAL_INPUT_PIPE_ID", hex, &bytes); + + if (bytes == 17 && + ipc_create_private_dgram_receiver( + from_hex(16, hex), + &input_handle) == IPR_SUCCESS) + set_up_terminal_input = 1; + + bytes = 17; + get_envvar("TERMINAL_OUTPUT_PIPE_ID", hex, &bytes); + + if (bytes == 17 && + ipc_create_private_dgram_sender( + from_hex(16, hex), + &output_handle) == IPR_SUCCESS) + set_up_terminal_output = 1; + +} + +void terminal_write(const char *data, int bytes) { + + if (bytes == 0) + return; + + int last_newline = bytes - 1; + while (last_newline >= 0 && data[last_newline] != '\n') + --last_newline; + + if (last_newline == -1) { + output_buffer_size_at_least(output_buffer_used + bytes); + memcpy(&output_buffer[output_buffer_used], data, bytes); + output_buffer_used += bytes; + } + + else { + if (output_buffer_used != 0) { + send_output(output_buffer, output_buffer_used); + output_buffer_used = 0; + } + send_output(data, last_newline + 1); + if (last_newline != bytes - 1) { + output_buffer_size_at_least(bytes - last_newline - 1); + memcpy(output_buffer, &data[last_newline + 1], bytes - last_newline - 1); + output_buffer_used = bytes - last_newline - 1; + } + } + +} + +void terminal_read(char *buffer, int bytes) { + //TODO + (void)buffer; + (void)bytes; + while (1) + sleep_ms(1000000000); +}