refactor some kernel stuff, start terminal
This commit is contained in:
parent
8d1785fb06
commit
5821f51f02
39 changed files with 1154 additions and 326 deletions
|
|
@ -3,6 +3,8 @@
|
|||
-I
|
||||
dependencies/limine
|
||||
-I
|
||||
dependencies/flanterm/src
|
||||
-I
|
||||
include
|
||||
-D
|
||||
CALCITE_DEBUG
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
root://calcite/apps/hello/hello.elf
|
||||
root://calcite/apps/terminal/terminal.elf
|
||||
|
|
|
|||
|
|
@ -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 ..
|
||||
|
|
|
|||
24
include/calcite/dispatch.h
Normal file
24
include/calcite/dispatch.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
26
include/calcite/format.h
Normal file
26
include/calcite/format.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//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);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <kernel-public/process.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/input.h>
|
||||
#include <kernel-public/sync.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
|
||||
[[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);
|
||||
|
|
|
|||
22
include/calcite/terminal.h
Normal file
22
include/calcite/terminal.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void init_terminal();
|
||||
void terminal_write(const char *data, int bytes);
|
||||
void terminal_read(char *buffer, int bytes);
|
||||
23
include/kernel-public/sync.h
Normal file
23
include/kernel-public/sync.h
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint64_t mutex_handle_t;
|
||||
typedef uint64_t event_handle_t;
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
1
qemu.gdb
1
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
|
||||
|
|
|
|||
28
readme.txt
28
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 <https://github.com/limine-bootloader/limine>.
|
||||
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
|
||||
<https://codeberg.org/Limine/Limine>.
|
||||
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
|
||||
<https://codeberg.org/Mintsuki/Flanterm>.
|
||||
After running "sh get-dependencies.sh", you can find the license of Flanterm
|
||||
at "dependencies/flanterm/LICENSE".
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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])() = {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <kernel-public/process.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/sync.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <kernel-public/framebuffer.h>
|
||||
#include <kernel-public/process.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/sync.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 <stdint.h>
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <kernel-public/ipc.h>
|
||||
#include <calcite/syscalls.h>
|
||||
#include <silver/pam.h>
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,2 +1 @@
|
|||
calcite
|
||||
silver
|
||||
26
src/user-apps/shell/shell.c
Normal file
26
src/user-apps/shell/shell.c
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/syscalls.h>
|
||||
#include <calcite/terminal.h>
|
||||
|
||||
void main() {
|
||||
init_terminal();
|
||||
terminal_write("hello\n", 6);
|
||||
while (1)
|
||||
sleep_ms(1000000000);
|
||||
}
|
||||
2
src/user-apps/terminal/libraries.txt
Normal file
2
src/user-apps/terminal/libraries.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
calcite
|
||||
flanterm
|
||||
120
src/user-apps/terminal/terminal.c
Normal file
120
src/user-apps/terminal/terminal.c
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <kernel-public/framebuffer.h>
|
||||
#include <kernel-public/process.h>
|
||||
#include <flanterm_backends/fb.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
#include <calcite/dispatch.h>
|
||||
#include <calcite/syscalls.h>
|
||||
#include <calcite/format.h>
|
||||
#include <calcite/memory.h>
|
||||
#include <flanterm.h>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
87
src/user-libs/calcite/dispatch.c
Normal file
87
src/user-libs/calcite/dispatch.c
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <kernel-public/sync.h>
|
||||
#include <calcite/syscalls.h>
|
||||
#include <calcite/memory.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
42
src/user-libs/calcite/format.c
Normal file
42
src/user-libs/calcite/format.c
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/format.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <kernel-public/syscall-numbers.h>
|
||||
#include <kernel-public/process.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/sync.h>
|
||||
#include <kernel-public/ipc.h>
|
||||
#include <calcite/syscalls.h>
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
132
src/user-libs/calcite/terminal.c
Normal file
132
src/user-libs/calcite/terminal.c
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <kernel-public/ipc.h>
|
||||
#include <calcite/syscalls.h>
|
||||
#include <calcite/terminal.h>
|
||||
#include <calcite/format.h>
|
||||
#include <calcite/memory.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue