diff --git a/clean-build.sh b/clean-build.sh
new file mode 100755
index 0000000..e548695
--- /dev/null
+++ b/clean-build.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+rm -rf build build.ninja
diff --git a/clean-dependencies.sh b/clean-dependencies.sh
new file mode 100755
index 0000000..f16a2aa
--- /dev/null
+++ b/clean-dependencies.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+rm -rf dependencies
diff --git a/compile_flags.txt b/compile_flags.txt
index bfe9d86..5fb5551 100644
--- a/compile_flags.txt
+++ b/compile_flags.txt
@@ -1,10 +1,3 @@
--std=c23
--ffreestanding
--I
-dependencies/limine
--I
-dependencies/flanterm/src
+-std=c++17
-I
include
--D
-CALCITE_DEBUG
diff --git a/disk/calcite/init.rc b/disk/calcite/init.rc
deleted file mode 100644
index cfef8bd..0000000
--- a/disk/calcite/init.rc
+++ /dev/null
@@ -1 +0,0 @@
-root://calcite/apps/terminal/terminal.elf
diff --git a/disk/calcite/qwerty.bin b/disk/calcite/qwerty.bin
deleted file mode 100644
index a5141fe..0000000
Binary files a/disk/calcite/qwerty.bin and /dev/null differ
diff --git a/disk/limine/limine.conf b/disk/limine/limine.conf
index 9dd1bae..7e148c3 100644
--- a/disk/limine/limine.conf
+++ b/disk/limine/limine.conf
@@ -1,7 +1,6 @@
timeout: 0
-quiet: yes
+quiet: yes
/Calcite
protocol: limine
-path: boot():/calcite/kernel.elf
-cmdline: root-drive=pata0,root-fs=iso9660
+path: boot():/calcite/core.elf
diff --git a/get-dependencies.sh b/get-dependencies.sh
old mode 100644
new mode 100755
index da748d5..8376843
--- a/get-dependencies.sh
+++ b/get-dependencies.sh
@@ -1,22 +1,25 @@
#!/bin/sh
-LIMINE_VERSION="9.3.4"
-FLANTERM_VERSION="2.1.0"
+LIMINE_VERSION="12.5.2"
+LIMINE_PROTOCOL_COMMIT="630686a3dd3ce40f9e510a7dd9fea6b4c60d952e"
if [ -e dependencies ]; then
- echo dependencies directory already exists.
+ echo The dependencies directory already exists.
+ echo Use clean-dependencies.sh to remove it.
exit
fi
set -e
mkdir dependencies
-
cd dependencies
-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
+curl -L https://github.com/Limine-Bootloader/Limine/releases/download/v12.5.2/limine-binary.tar.gz | tar xz
+make -C limine-binary
+
+curl -L https://github.com/Limine-Bootloader/limine-protocol/archive/${LIMINE_PROTOCOL_COMMIT}.zip -o limine-protocol.zip
+unzip limine-protocol.zip
+rm limine-protocol.zip
+mv limine-protocol-${LIMINE_PROTOCOL_COMMIT} limine-protocol
+
cd ..
diff --git a/include/calcite/assert.hxx b/include/calcite/assert.hxx
new file mode 100644
index 0000000..a32ad28
--- /dev/null
+++ b/include/calcite/assert.hxx
@@ -0,0 +1,30 @@
+/* include/calcite/assert.hxx
+ * Runtime assertions and panics.
+ *
+ * Copyright 2026 Benji Dial
+ * This file is part of the Calcite Operating System.
+ *
+ * Calcite 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, version 3 only.
+ *
+ * Calcite 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
+ * Calcite. If not, see .
+ */
+
+#pragma once
+
+#define assert(cond) { \
+ if (cond) {} \
+ else ::calcite::assert_fail(); \
+}
+
+namespace calcite {
+
+ [[noreturn]] void assert_fail();
+
+}
diff --git a/include/calcite/dispatch.h b/include/calcite/dispatch.h
deleted file mode 100644
index a159a0d..0000000
--- a/include/calcite/dispatch.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* 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/file-streams.h b/include/calcite/file-streams.h
deleted file mode 100644
index 597c070..0000000
--- a/include/calcite/file-streams.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Calcite, include/calcite/file-streams.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-struct file_stream;
-
-enum fs_access_result open_file_stream(const char *path, struct file_stream **file_stream_out);
-void close_file_stream(struct file_stream *file_stream);
-
-enum fs_access_result read_file_stream(struct file_stream *file_stream, void *buffer, uint64_t bytes);
-
-//returns next byte casted to int. on error or eof, returns -1.
-int read_file_stream_byte(struct file_stream *file_stream);
diff --git a/include/calcite/format.h b/include/calcite/format.h
deleted file mode 100644
index fb11257..0000000
--- a/include/calcite/format.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* 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
deleted file mode 100644
index 7a74414..0000000
--- a/include/calcite/memory.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Calcite, include/calcite/memory.h
- * 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
- * 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
-
-void *heap_alloc(uint64_t bytes);
-
-//start should be something returned from heap_alloc,
-//unless this is called (carefully) from inside memory.c
-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/result.hxx b/include/calcite/result.hxx
new file mode 100644
index 0000000..7c89d5b
--- /dev/null
+++ b/include/calcite/result.hxx
@@ -0,0 +1,86 @@
+/* include/calcite/result.hxx
+ * Result type returned from functions across Calcite.
+ *
+ * Copyright 2026 Benji Dial
+ * This file is part of the Calcite Operating System.
+ *
+ * Calcite 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, version 3 only.
+ *
+ * Calcite 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
+ * Calcite. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace calcite {
+
+ enum class result_code {
+ good,
+ out_of_memory,
+ not_found
+ };
+
+ class empty {};
+
+ template
+ class [[nodiscard]] result {
+ private:
+
+ result_code code;
+ uint8_t buffer[sizeof(type)];
+
+ public:
+
+ result(result_code code) : code(code) {
+ assert(code != result_code::good);
+ }
+
+ template
+ result(param_types &&...params) : code(result_code::good) {
+ new ((type *)buffer) type(std::forward(params)...);
+ }
+
+ ~result() {
+ if (code != result_code::good)
+ ((type *)buffer)->~type();
+ }
+
+ result(result &&other) = delete;
+ result(const result &other) = delete;
+
+ result &operator =(result &&other) = delete;
+ result &operator =(const result &other) = delete;
+
+ type &unwrap() {
+ assert(code == result_code::good);
+ return *(type *)buffer;
+ }
+
+ const type &unwrap() const {
+ assert(code == result_code::good);
+ return *(const type *)buffer;
+ }
+
+ bool is_error() const {
+ return code != result_code::good;
+ }
+
+ result_code get_error() const {
+ assert(code != result_code::good);
+ return code;
+ }
+
+ };
+
+}
diff --git a/include/calcite/syscalls.h b/include/calcite/syscalls.h
deleted file mode 100644
index fd881b8..0000000
--- a/include/calcite/syscalls.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Calcite, include/calcite/syscalls.h
- * 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
- * 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
-#include
-#include
-#include
-#include
-#include
-
-[[noreturn]] void end_thread();
-
-void map_framebuffer(struct framebuffer_info *info_out);
-
-enum fs_access_result open_file(const char *path, file_handle_t *handle_out);
-void close_file(file_handle_t handle);
-
-enum fs_access_result get_file_size(file_handle_t handle, uint64_t *bytes_out);
-
-enum fs_access_result read_file(struct read_file_parameter *parameter);
-enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes);
-
-void wait_for_mouse_packet(struct mouse_packet *packet_out);
-int wait_for_keyboard_packet();
-
-void *map_pages(uint64_t count);
-
-void sleep_ms(uint64_t ms_to_sleep);
-
-//1 on success, 0 on failure.
-int start_elf(const char *path, const struct process_start_info *info);
-
-enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out);
-enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out);
-
-//on entry, bytes is maximum accepted packet length.
-//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length.
-//actual packet length will always be positive.
-enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes);
-
-//bytes must be positive.
-enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes);
-
-//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);
-
-//value_space must be positive at entry.
-//looks up the environment variable with this key. if there is no such
-//variable, sets value_space to 0. otherwise, sets value_space to the
-//length of the value including null terminator (which will be positive),
-//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);
-
-//vma should be page-aligned. writable and executable should be 1 or 0.
-void change_page_permissions(void *vma, int writable, int executable);
diff --git a/include/calcite/terminal.h b/include/calcite/terminal.h
deleted file mode 100644
index f910161..0000000
--- a/include/calcite/terminal.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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
-
-#include
-
-void init_terminal();
-
-void terminal_write(const void *data, int bytes);
-void terminal_flush();
-
-enum terminal_key_flags {
- TKF_LEFT_SHIFT = 0x20000,
- TKF_RIGHT_SHIFT = 0x40000,
- TKF_LEFT_CTRL = 0x80000,
- TKF_RIGHT_CTRL = 0x100000,
- TKF_LEFT_ALT = 0x200000,
- TKF_RIGHT_ALT = 0x400000,
- TKF_CAPS_LOCK = 0x800000,
- TKF_NUM_LOCK = 0x1000000
-};
-
-//0x000000ff is printable character, if any, otherwise 0.
-// this includes space, newline, and tab, but not delete or backspace.
-// control, alt, and make are ignored, but shift, caps lock, and num lock are considered.
-//0x0001ff00 is result of syscall_wait_for_keyboard_packet, shifted left.
-//0x01fe0000 is flags as above.
-int terminal_readkey();
-
-//has null terminator, no newline. returned pointer is invalidated by next call.
-const char *terminal_readline();
diff --git a/include/kernel-public/files.h b/include/kernel-public/files.h
deleted file mode 100644
index 32205af..0000000
--- a/include/kernel-public/files.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Calcite, include/kernel-public/files.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-enum fs_access_result {
- FAR_SUCCESS,
- FAR_HARDWARE_ERROR,
- FAR_FORMAT_ERROR,
- FAR_UNKNOWN_FS_TYPE,
- FAR_NOT_FOUND,
- FAR_OUT_OF_BOUNDS,
- FAR_BAD_HANDLE
-};
-
-typedef uint64_t file_handle_t;
-
-struct read_file_parameter {
- file_handle_t handle;
- void *buffer;
- //in bytes
- uint64_t start;
- uint64_t bytes;
-};
diff --git a/include/kernel-public/framebuffer.h b/include/kernel-public/framebuffer.h
deleted file mode 100644
index 6ee1267..0000000
--- a/include/kernel-public/framebuffer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Calcite, include/kernel-public/framebuffer.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-struct framebuffer_info {
- uint8_t *fb_base;
- int fb_width;
- int fb_height;
- int fb_pitch;
-};
diff --git a/include/kernel-public/input.h b/include/kernel-public/input.h
deleted file mode 100644
index 8736a31..0000000
--- a/include/kernel-public/input.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/* Calcite, include/kernel-public/input.h
- * 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
- * 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 mouse_packet {
- int x_change;
- int y_change;
-};
-
-//named after qwerty value of key
-enum key_value {
- KEY_F9 = 0x01,
- KEY_F5 = 0x03,
- KEY_F3 = 0x04,
- KEY_F1 = 0x05,
- KEY_F2 = 0x06,
- KEY_F12 = 0x07,
- KEY_F10 = 0x09,
- KEY_F8 = 0x0a,
- KEY_F6 = 0x0b,
- KEY_F4 = 0x0c,
- KEY_TAB = 0x0d,
- KEY_GRAVE = 0x0e,
- KEY_LEFT_ALT = 0x11,
- KEY_LEFT_SHIFT = 0x12,
- KEY_LEFT_CTRL = 0x14,
- KEY_Q = 0x15,
- KEY_1 = 0x16,
- KEY_Z = 0x1a,
- KEY_S = 0x1b,
- KEY_A = 0x1c,
- KEY_W = 0x1d,
- KEY_2 = 0x1e,
- KEY_C = 0x21,
- KEY_X = 0x22,
- KEY_D = 0x23,
- KEY_E = 0x24,
- KEY_4 = 0x25,
- KEY_3 = 0x26,
- KEY_SPACE = 0x29,
- KEY_V = 0x2a,
- KEY_F = 0x2b,
- KEY_T = 0x2c,
- KEY_R = 0x2d,
- KEY_5 = 0x2e,
- KEY_N = 0x31,
- KEY_B = 0x32,
- KEY_H = 0x33,
- KEY_G = 0x34,
- KEY_Y = 0x35,
- KEY_6 = 0x36,
- KEY_M = 0x3a,
- KEY_J = 0x3b,
- KEY_U = 0x3c,
- KEY_7 = 0x3d,
- KEY_8 = 0x3e,
- KEY_COMMA = 0x41,
- KEY_K = 0x42,
- KEY_I = 0x43,
- KEY_O = 0x44,
- KEY_0 = 0x45,
- KEY_9 = 0x46,
- KEY_PERIOD = 0x49,
- KEY_SLASH = 0x4a,
- KEY_L = 0x4b,
- KEY_SEMICOLON = 0x4c,
- KEY_P = 0x4d,
- KEY_MINUS = 0x4e,
- KEY_APOSTRAPHE = 0x52,
- KEY_OPEN_BRACKET = 0x54,
- KEY_EQUALS = 0x55,
- KEY_CAPS_LOCK = 0x58,
- KEY_RIGHT_SHIFT = 0x59,
- KEY_ENTER = 0x5a,
- KEY_CLOSE_BRACKET = 0x5b,
- KEY_BACKSLASH = 0x5d,
- KEY_BACKSPACE = 0x66,
- KEY_NUM_1 = 0x69,
- KEY_NUM_4 = 0x6b,
- KEY_NUM_7 = 0x6c,
- KEY_NUM_0 = 0x70,
- KEY_NUM_PERIOD = 0x71,
- KEY_NUM_2 = 0x72,
- KEY_NUM_5 = 0x73,
- KEY_NUM_6 = 0x74,
- KEY_NUM_8 = 0x75,
- KEY_ESCAPE = 0x76,
- KEY_NUM_LOCK = 0x77,
- KEY_F11 = 0x78,
- KEY_NUM_PLUS = 0x79,
- KEY_NUM_3 = 0x7a,
- KEY_NUM_MINUS = 0x7b,
- KEY_NUM_TIMES = 0x7c,
- KEY_NUM_9 = 0x7d,
- KEY_SCROLL_LOCK = 0x7e,
- KEY_F7 = 0x83,
- KEY_WEB_SEARCH = 0x90,
- KEY_RIGHT_ALT = 0x91,
- KEY_RIGHT_CTRL = 0x94,
- KEY_MEDIA_PREVIOUS = 0x95,
- KEY_WEB_FAVORITES = 0x98,
- KEY_LEFT_GUI = 0x9f,
- KEY_WEB_REFRESH = 0xa0,
- KEY_MEDIA_VOLUME_DOWN = 0xa1,
- KEY_MEDIA_MUTE = 0xa3,
- KEY_RIGHT_GUI = 0xa7,
- KEY_WEB_STOP = 0xa8,
- KEY_CALCULATOR = 0xab,
- KEY_MENU = 0xaf,
- KEY_WEB_FORWARD = 0xb0,
- KEY_MEDIA_VOLUME_UP = 0xb2,
- KEY_MEDIA_PAUSE = 0xb4,
- KEY_ACPI_POWER = 0xb7,
- KEY_WEB_BACK = 0xb8,
- KEY_WEB_HOME = 0xba,
- KEY_MEDIA_STOP = 0xbb,
- KEY_ACPI_SLEEP = 0xbf,
- KEY_COMPUTER = 0xc0,
- KEY_EMAIL = 0xc8,
- KEY_NUM_DIVIDE = 0xca,
- KEY_MEDIA_NEXT = 0xcd,
- KEY_MEDIA_SELECT = 0xd0,
- KEY_NUM_ENTER = 0xda,
- KEY_ACPI_WAKE = 0xde,
- KEY_END = 0xe9,
- KEY_LEFT = 0xeb,
- KEY_HOME = 0xec,
- KEY_INSERT = 0xf0,
- KEY_DELETE = 0xf1,
- KEY_DOWN = 0xf2,
- KEY_RIGHT = 0xf4,
- KEY_UP = 0xf5,
- KEY_PAGE_DOWN = 0xfa,
- KEY_PAGE_UP = 0xfd,
- KEY_PRINT_SCREEN = 0xfe,
- KEY_PAUSE_BREAK = 0xff,
- KEY_FLAG_MAKE = 0x100
-};
diff --git a/include/kernel-public/ipc.h b/include/kernel-public/ipc.h
deleted file mode 100644
index 56f9f2c..0000000
--- a/include/kernel-public/ipc.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Calcite, include/kernel-public/ipc.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-enum ipc_dgram_result {
-
- IPR_SUCCESS,
-
- //on write, packet is bigger than kernel data structure.
- //on read, packet is bigger than max requested size.
- IPR_TOO_BIG,
-
- IPR_BAD_HANDLE,
-
- //on create receiver, someone is already receiving at that address.
- IPR_IN_USE
-
-};
-
-typedef uint64_t ipc_dgram_receiver_handle_t;
-typedef uint64_t ipc_dgram_sender_handle_t;
diff --git a/include/kernel-public/process.h b/include/kernel-public/process.h
deleted file mode 100644
index 320acea..0000000
--- a/include/kernel-public/process.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Calcite, include/kernel-public/process.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-struct process_start_info {
-
- //keys of envvars to copy to new process.
- const char **forwared_envvars;
- int forwared_envvar_count;
-
- //keys and values to set in new process.
- //entry i has key set_envvars[i * 2] and value set_envvars[i * 2 + 1].
- //set_envvar_count is number of envvars, not twice that.
- const char **set_envvars;
- int set_envvar_count;
-
-};
diff --git a/include/kernel-public/sync.h b/include/kernel-public/sync.h
deleted file mode 100644
index 893b6ca..0000000
--- a/include/kernel-public/sync.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* 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
deleted file mode 100644
index 6e80c4e..0000000
--- a/include/kernel-public/syscall-numbers.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Calcite, include/kernel-public/syscall-numbers.h
- * 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
- * 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
-
-enum {
- SYSCALL_END_THREAD,
- SYSCALL_MAP_FRAMEBUFFER,
- SYSCALL_OPEN_FILE,
- SYSCALL_CLOSE_FILE,
- SYSCALL_GET_FILE_SIZE,
- SYSCALL_READ_FILE,
- SYSCALL_WAIT_FOR_MOUSE_PACKET,
- SYSCALL_MAP_PAGES,
- SYSCALL_SLEEP_MS,
- SYSCALL_START_ELF,
- SYSCALL_IPC_CREATE_DGRAM_RECEIVER,
- SYSCALL_IPC_CREATE_DGRAM_SENDER,
- SYSCALL_IPC_RECEIVE_DGRAM,
- SYSCALL_IPC_SEND_DGRAM,
- SYSCALL_CREATE_THREAD,
- 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,
- SYSCALL_CHANGE_PAGE_PERMISSIONS,
- SYSCALL_WAIT_FOR_KEYBOARD_PACKET
-};
diff --git a/include/silver/image.h b/include/silver/image.h
deleted file mode 100644
index 73061d9..0000000
--- a/include/silver/image.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Calcite, include/silver/image.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-struct pixel {
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-};
-
-struct image {
- int width;
- int height;
- struct pixel *pixels;
-};
-
-void create_image(int width, int height, struct image **image_out);
-void destroy_image(struct image *image);
diff --git a/include/silver/pam.h b/include/silver/pam.h
deleted file mode 100644
index 8566c2d..0000000
--- a/include/silver/pam.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Calcite, include/silver/pam.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-//returns 0 on failure, 1 on success.
-int load_pam(const char *path, struct image **image_out);
diff --git a/make-build.sh b/make-build.sh
old mode 100644
new mode 100755
index 5747e4d..03690bb
--- a/make-build.sh
+++ b/make-build.sh
@@ -1,145 +1,148 @@
#!/bin/sh
-COMMON_CC_EXTRA_FLAGS="-Wall -Wextra"
-COMMON_LD_EXTRA_FLAGS=""
-
-COMMON_CC_FLAGS="-std=c23 -ffreestanding -I include ${COMMON_CC_EXTRA_FLAGS}"
-COMMON_LD_FLAGS="${COMMON_LD_EXTRA_FLAGS}"
-
-if [ "$1" = debug ]; then
- COMMON_CC_FLAGS="-O0 -ggdb -D CALCITE_DEBUG ${COMMON_CC_FLAGS}"
-elif [ "$1" = release ]; then
- COMMON_CC_FLAGS="-O3 -D CALCITE_RELEASE ${COMMON_CC_FLAGS}"
- COMMON_LD_FLAGS="-s ${COMMON_LD_FLAGS}"
-else
- echo pass either "debug" or "release" as an argument.
+if [ -e build.ninja ]; then
+ echo The build.ninja file already exists.
+ echo Remove it with clean-build.sh.
exit
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 -I dependencies/flanterm/src ${COMMON_CC_FLAGS}"
-
-FLANTERM_CC_FLAGS="-D FLANTERM_FB_DISABLE_BUMP_ALLOC ${USER_CC_FLAGS}"
-
-if [ -e build.ninja ]; then
- echo build.ninja already exists.
+if [ "$#" -eq 0 ]; then
+ echo This script must be run with at least one flag. The recognized flags are:
+ echo
+ echo " -- Do nothing, but silence this message."
+ echo
+ echo " --cxx-flags FLAGS Add FLAGS to the flags sent to the C++ compiler. The flags"
+ echo " should be quoted, so that they are all read by this script"
+ echo " as one argument."
+ echo
+ echo " --debug Enable debugging symbols."
+ echo
+ echo " --ld-flags FLAGS Add FLAGS to the flags sent to ld. The flags should be"
+ echo " quoted, so that they are all read by this script as one"
+ echo " argument."
+ echo
+ echo " --no-optimize Disable compiler optimizations. This might help with using"
+ echo " the debugger. A bug could only appear when optimizations"
+ echo " are enabled, so it might be necessary to run the debugger"
+ echo " without this flag."
+ echo
+ echo These options can be combined.
exit
fi
set -e
-echo "rule nasm" >> build.ninja
-echo " command = nasm -f elf64 \$in -o \$out" >> build.ninja
+CXX_FLAGS="-std=c++17 -ffreestanding -fno-exceptions -fno-rtti -mno-sse -I include -Wall -Wextra"
+LD_FLAGS="-L build/libraries"
-echo "rule kernel_cc" >> build.ninja
-echo " depfile = \$out.d" >> build.ninja
-echo " command = cc -c -MD -MF \$out.d ${KERNEL_CC_FLAGS} \$in -o \$out" >> build.ninja
+OPTIMIZE_CXX_FLAGS="-O2"
+DEBUG_CXX_FLAGS=""
+NO_DEBUG_LD_FLAGS="-s"
+EXTRA_CXX_FLAGS=""
+EXTRA_LD_FLAGS=""
-echo "rule user_cc" >> build.ninja
-echo " depfile = \$out.d" >> build.ninja
-echo " command = cc -c -MD -MF \$out.d ${USER_CC_FLAGS} \$in -o \$out" >> build.ninja
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --cxx-flags)
+ shift
+ EXTRA_CXX_FLAGS="$EXTRA_CXX_FLAGS $1"
+ ;;
+ --debug)
+ DEBUG_CXX_FLAGS="-ggdb"
+ NO_DEBUG_LD_FLAGS=""
+ ;;
+ --ld-flags)
+ shift
+ EXTRA_LD_FLAGS="$EXTRA_LD_FLAGS $1"
+ ;;
+ --no-optimize)
+ OPTIMIZE_CXX_FLAGS=""
+ ;;
+ --)
+ ;;
+ *)
+ echo Unknown argument.
+ exit
+ esac
+ shift
+done
-echo "rule flanterm_cc" >> build.ninja
-echo " depfile = \$out.d" >> build.ninja
-echo " command = cc -c -MD -MF \$out.d ${FLANTERM_CC_FLAGS} \$in -o \$out" >> build.ninja
-
-echo "rule kernel_ld" >> build.ninja
-echo " command = ld ${COMMON_LD_FLAGS} -T src/kernel/link.ld \$in -o \$out" >> build.ninja
-
-echo "rule user_lib_ld" >> build.ninja
-echo " command = ld -r \$in -o \$out" >> build.ninja
-
-#eventually maybe a libc will be linked in
-echo "rule user_app_ld" >> build.ninja
-echo " command = ld ${COMMON_LD_FLAGS} -T src/user-apps/link.ld \$in -o \$out" >> build.ninja
-
-#builds everything in a directory
-# $1 - source directory
-# $2 - cc rule
-# $3 - ld rule
-# $4 - linked object name
-# $5 - extra objects to link in
-build_all() {
-
- sources="$1/*.asm $1/*.c"
- objects="$5"
-
- for src in $sources; do
- if echo $src | grep -q -v '*'; then
- build=$(echo $src | sed -e 's/^src/build/').o
- if echo $src | grep -q '.c$'; then
- echo "build $build: $2 $src" >> build.ninja
- else
- echo "build $build: nasm $src" >> build.ninja
- fi
- objects="$objects $build"
- fi
- done
-
- build_dir=$(echo $1 | sed -e 's/^src/build/')
- echo "build $build_dir/$4: $3 $objects" >> build.ninja
+CXX_FLAGS="$CXX_FLAGS $OPTIMIZE_CXX_FLAGS $DEBUG_CXX_FLAGS $EXTRA_CXX_FLAGS"
+LD_FLAGS="$LD_FLAGS $NO_DEBUG_LD_FLAGS $EXTRA_LD_FLAGS"
+rules() {
+ echo "rule asm"
+ echo " command = nasm -f elf64 \$in -o \$out"
+ echo "rule cxx"
+ echo " depfile = \$out.d"
+ echo " command = c++ -c -MD -MF \$out.d $CXX_FLAGS \$in -o \$out"
+ echo "rule core_ld"
+ echo " depfile = \$out.d"
+ echo " command = ld $LD_FLAGS -T src/core/link.ld \$in -o \$out"
+ echo "rule service_ld"
+ echo " depfile = \$out.d"
+ echo " command = ld $LD_FLAGS -T src/service/link.ld \$in -o \$out"
+ echo "rule disk"
+ echo " command =" \
+ "rm -rf build/disk &&" \
+ "cp -r disk build/disk &&" \
+ "mkdir -p build/disk/EFI/BOOT &&" \
+ "mkdir -p build/disk/limine &&" \
+ "mkdir -p build/disk/calcite/service &&" \
+ "cp dependencies/limine-binary/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \
+ "cp dependencies/limine-binary/limine-uefi-cd.bin build/disk/limine/ &&" \
+ "cp dependencies/limine-binary/limine-bios-cd.bin build/disk/limine/ &&" \
+ "cp dependencies/limine-binary/limine-bios.sys build/disk/limine/ &&" \
+ "cp build/core.elf build/disk/calcite/ &&" \
+ "cp build/service/*.elf build/disk/calcite/service/ &&" \
+ "xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4" \
+ "-boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin" \
+ "-efi-boot-part --efi-boot-image --protective-msdos-label build/disk -o build/disk.iso &&" \
+ "dependencies/limine-binary/limine bios-install build/disk.iso"
}
-build_all src/kernel kernel_cc kernel_ld kernel.elf
+build_core() {
+ echo "build build/core/core.cxx.o: cxx src/core/core.cxx"
+ echo "build build/core/core.asm.o: asm src/core/core.asm"
+ echo "build build/core.elf: core_ld build/core/core.cxx.o build/core/core.asm.o"
+}
-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: flanterm_cc $src" >> build.ninja
- objects="$objects $build"
-done
-echo "build build/user-libs/flanterm/libflanterm.o: user_lib_ld $objects" >> build.ninja
+build_service() {
+ SERVICE="$1"
+ OBJECTS=""
+ for SOURCE in $(find "src/service/$SERVICE" -regex '.*\.cxx'); do
+ OBJECT="$(echo $SOURCE | sed 's/src/build/').o"
+ echo "build $OBJECT: cxx $SOURCE"
+ OBJECTS="$OBJECTS $OBJECT"
+ done
+ for SOURCE in $(find "src/service/$SERVICE" -regex '.*\.asm'); do
+ OBJECT="$(echo $SOURCE | sed 's/src/build/').o"
+ echo "build $OBJECT: asm $SOURCE"
+ OBJECTS="$OBJECTS $OBJECT"
+ done
+ echo "build build/service/$SERVICE.elf: service_ld$OBJECTS"
+}
-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
-done
+build_disk() {
+ echo "build build/disk.iso: disk | $@"
+}
-cp_apps=""
-app_deps=""
+default() {
+ echo "default build/disk.iso"
+}
-for dir in src/user-apps/*; do
- if [ -d $dir ]; then
+rules >> build.ninja
- if [ -e $dir/libraries.txt ]; then
- lib_paths=$(cat $dir/libraries.txt | sed -e 's/^.*$/build\/user-libs\/\0\/lib\0.o/' | tr '\n' ' ')
- else
- lib_paths=""
- fi
-
- app_name=$(echo $dir | sed -e 's/^src\/user-apps\///')
- build_all $dir user_cc user_app_ld $app_name.elf "$lib_paths"
-
- build_elf=$(echo $dir | sed -e 's/^src/build/')/$app_name.elf
- disk_dir=build/disk/calcite/apps/$app_name
- disk_elf=$disk_dir/$app_name.elf
-
- cp_apps=$(echo "$cp_apps" \
- "mkdir -p $disk_dir &&" \
- "cp $build_elf $disk_elf &&")
- app_deps="$app_deps $build_elf"
+DISK_DEPS="build/core.elf"
+build_core >> build.ninja
+for DIRECTORY in src/service/*; do
+ if [ -d "$DIRECTORY" ]; then
+ SERVICE="$(basename $DIRECTORY)"
+ DISK_DEPS="$DISK_DEPS build/service/$SERVICE.elf"
+ build_service $SERVICE >> build.ninja
fi
done
-echo "rule disk" >> build.ninja
-echo " command =" \
- "rm -rf build/disk &&" \
- "cp -r disk build/disk &&" \
- "cp dependencies/limine/limine-uefi-cd.bin build/disk/limine/ &&" \
- "cp dependencies/limine/limine-bios-cd.bin build/disk/limine/ &&" \
- "cp dependencies/limine/limine-bios.sys build/disk/limine/ &&"\
- "mkdir -p build/disk/EFI/BOOT &&" \
- "cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \
- "mkdir -p build/disk/calcite &&" \
- "cp build/kernel/kernel.elf build/disk/calcite/ &&" \
- $cp_apps \
- "xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image --protective-msdos-label -quiet build/disk -o build/disk.iso &&" \
- "dependencies/limine/limine bios-install --quiet build/disk.iso" >> build.ninja
-
-echo "build build/disk.iso: disk | build/kernel/kernel.elf $app_deps" >> build.ninja
-
-echo "default build/disk.iso" >> build.ninja
+build_disk $DISK_DEPS >> build.ninja
+default >> build.ninja
diff --git a/qemu.gdb b/qemu.gdb
index 8ec043a..f422028 100644
--- a/qemu.gdb
+++ b/qemu.gdb
@@ -1,5 +1,5 @@
target remote | qemu-system-x86_64 -gdb stdio -cdrom build/disk.iso -boot d
-symbol-file build/kernel/kernel.elf
+symbol-file build/core.elf
set disassembly-flavor intel
set print asm-demangle on
layout src
diff --git a/readme.txt b/readme.txt
index 5c3f22f..21b7516 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,72 +1,95 @@
- ____ _ _ _
- / ___|__ _| | ___(_) |_ ___
-| | / _` | |/ __| | __/ _ \
-| |__| (_| | | (__| | || __/
- \____\__,_|_|\___|_|\__\___|
+ ___ _ _ _
+ / __|__ _| |__(_) |_ ___
+ | (__/ _` | / _| | _/ -_)
+ \___\__,_|_\__|_|\__\___|
Calcite is a hobby operating system. I am maintaining a weblog with occasional
status updates at .
Calcite is very much alpha software. Do not expect it to do anything useful.
-=== Building
+ === Building ===
Calcite requires some software to be installed before it can be built:
- * curl
- * GCC (Any C compiler supporting both C99 and C23 should work.)
- * GNU Binutils (Specifically, "ld" is used to link the kernel.)
- * GNU xorriso
- * NASM
- * Ninja build system
+* a C compiler supporting C99 (e.g. GCC)
+* a C++ compiler supporting C++17 (e.g. GCC)
+* curl
+* GNU ld
+* GNU tar
+* GNU xorriso
+* NASM
+* Ninja build system
On Debian, it is sufficient to run this command:
- apt install binutils curl gcc nasm ninja-build xorriso
+ apt install binutils curl g++ gcc nasm ninja-build tar xorriso
-To build Calcite, run these commands:
- sh get-dependencies.sh
- sh make-build.sh release
- ninja
+The Calcite disk includes some software that is not in this repository.
+To download that software into the repository tree, use the get-dependencies.sh
+script. To remove those files, use the clean-dependencies.sh script.
-This will build a disk image at "build/disk.iso" that can be booted with
-either BIOS or UEFI.
+Next, use the make-build.sh script to make the ninja.build file. Running this
+script with no arguments will display information about the arguments that this
+script accepts.
-=== Debugging
+Finally, just run ninja to build the disk. A disk image will be placed at
+build/disk.iso. This disk must be booted with UEFI. All build files, including
+ninja.build, but not including the files downloaded by get-dependencies.sh,
+can be removed with the clean-build.sh script.
-Once Calcite has been built, some more software is required to debug it:
- * GNU's GDB
- * QEMU, including the x86_64 system
+ === Debugging ===
+
+More software must be installed to debug Calcite:
+* GNU's GDB
+* QEMU, including the x86_64 system
On Debian, it is sufficient to run this command:
apt install gdb qemu-system-x86
-Then, just run "gdb -x qemu.gdb". Calcite will be booted in QEMU, and GDB will
-be attached to it. Use "c" or "continue" in GDB to start execution.
+Run "gdb -x qemu.gdb" to boot Calcite in QEMU and attach GDB to it.
+The debugging experience will be nicer if debugging symbols were
+enabled during the make-build.sh step.
-While debugging, it is useful to disable optimizations and enable debugging
-information. If you already built Calcite in "release" mode, run:
- rm -r build build.ninja
- sh make-build.sh debug
- ninja
+ === License ===
-If you did not already build Calcite, then you do not need the first command.
+Calcite is Copyright 2026 Benji Dial.
-=== License
+Calcite 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 (version 3 only).
-Calcite is Copyright 2025-2026 Benji Dial, and licensed under the GNU GPL v3.
-See "license.txt" for the text of the license.
+Calcite 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.
-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".
+You should have received a copy of the GNU General Public License along with
+Calcite (in license.txt). If not, see .
-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".
+The Calcite disk image includes the Limine bootloader, version 12.5.0. Limine is
+Copyright 2019-2026 Mintsuki and contributors, and is used under the terms of
+the BSD 2-Clause License, reproduced below:
+
+ Copyright (C) 2019-2026 Mintsuki and contributors.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must n the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Limine has some transitive dependencies. Information about these can be found at
+.
diff --git a/src/core/core.asm b/src/core/core.asm
new file mode 100644
index 0000000..bab145e
--- /dev/null
+++ b/src/core/core.asm
@@ -0,0 +1,28 @@
+ ; src/core/core.asm
+ ; The core of the Calcite Operating System.
+ ;
+ ; Copyright 2026 Benji Dial
+ ; This file is part of the Calcite Operating System.
+ ;
+ ; Calcite 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, version 3 only.
+ ;
+ ; Calcite 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
+ ; Calcite. If not, see .
+
+bits 64
+
+section .text
+
+global asm_set_cr3
+asm_set_cr3:
+ mov cr3, rdi
+ mov rsp, rsi
+ push qword 0
+ xor rbp, rbp
+ jmp rdx
diff --git a/src/core/core.cxx b/src/core/core.cxx
new file mode 100644
index 0000000..16692ac
--- /dev/null
+++ b/src/core/core.cxx
@@ -0,0 +1,456 @@
+/* src/core/core.cxx
+ * The core of the Calcite Operating System.
+ *
+ * Copyright 2026 Benji Dial
+ * This file is part of the Calcite Operating System.
+ *
+ * Calcite 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, version 3 only.
+ *
+ * Calcite 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
+ * Calcite. If not, see .
+ */
+
+#include
+#include
+#include "limine.hxx"
+
+using namespace calcite;
+
+//the following abbreviations are used in here:
+// p1 = page table
+// p2 = page directory
+// p3 = page directory pointer table
+// p4 = page map level 4
+// pma = physical memory address
+// vma = virtual memory address
+
+//the core has one p3 that is mapped in all services at:
+// 0xffff.ff80.0000.0000 - 0xffff.ffff.ffff.ffff
+
+//the core's p1's are mapped into:
+// 0xffff.ffff.c000.0000 - 0xffff.ffff.ffff.ffff
+
+//each service has its own p3 that is mapped in only that service at:
+// 0xffff.ff00.0000.0000 - 0xffff.ff7f.ffff.ffff
+
+//in a p1, a stack guard is 0x1000
+
+static uint64_t max_physical;
+static uint64_t *physical_free_bitmap;
+static uint64_t physical_free_count_bytes;
+
+static uint64_t next_try_physical_free;
+
+alignas(4096) static uint64_t core_p4[512];
+alignas(4096) static uint64_t core_p3[512];
+alignas(4096) static uint64_t core_p2s[512 * 512];
+alignas(4096) static uint64_t core_top_p1s[512 * 512];
+
+constexpr uint64_t temp_stack_length = 65536;
+alignas(4096) static uint64_t temp_stack_and_guards[temp_stack_length + 8192];
+
+static uint64_t hhdm;
+
+extern uint8_t __image_rx_start;
+extern uint8_t __image_rx_end;
+extern uint8_t __image_ro_start;
+extern uint8_t __image_ro_end;
+extern uint8_t __image_rw_start;
+extern uint8_t __image_rw_end;
+
+//remove 4 once limine updates
+uint64_t limine_base_revision[4] = LIMINE_BASE_REVISION(6);
+
+limine_hhdm_request hhdm_request = {
+ .id = LIMINE_HHDM_REQUEST_ID,
+ .revision = 0,
+ .response = 0
+};
+
+limine_executable_address_request executable_address_request = {
+ .id = LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID,
+ .revision = 0,
+ .response = 0
+};
+
+limine_memmap_request memmap_request = {
+ .id = LIMINE_MEMMAP_REQUEST_ID,
+ .revision = 0,
+ .response = 0
+};
+
+[[noreturn]] static void die() {
+ while (true)
+ __asm__ ("hlt");
+}
+
+[[noreturn]] void calcite::assert_fail() {
+ die();
+}
+
+static void mark_physical_free(uint64_t start, uint64_t end) {
+
+ assert(start % 4096 == 0);
+ assert(end % 4096 == 0);
+ assert(start <= end);
+ assert(end <= max_physical);
+
+ physical_free_count_bytes += end - start;
+
+ start /= 4096;
+ end /= 4096;
+
+ if (start / 64 == end / 64) {
+ uint64_t mask = (1ULL << (end % 64)) - (1ULL << (start % 64));
+ assert((physical_free_bitmap[start / 64] & mask) == 0);
+ physical_free_bitmap[start / 64] |= mask;
+ return;
+ }
+
+ if (start % 64 != 0) {
+ uint64_t mask = -(1ULL << (start % 64));
+ assert((physical_free_bitmap[start / 64] & mask) == 0);
+ physical_free_bitmap[start / 64] |= mask;
+ start += 64 - start % 64;
+ }
+
+ if (end % 64 != 0) {
+ uint64_t mask = (1ULL << (end % 64)) - 1;
+ assert((physical_free_bitmap[end / 64] & mask) == 0);
+ physical_free_bitmap[end / 64] |= mask;
+ end -= end % 64;
+ }
+
+ for (uint64_t i = start / 64; i < end / 64; ++i) {
+ assert(physical_free_bitmap[i] == 0);
+ physical_free_bitmap[i] = -1ULL;
+ }
+
+}
+
+static result take_free_physical_page() {
+
+ uint64_t i = next_try_physical_free;
+
+ while (true) {
+
+ if (physical_free_bitmap[i] != 0) {
+ next_try_physical_free = i;
+ for (int j = 0;; ++j)
+ if (physical_free_bitmap[i] & (1ULL << j)) {
+ physical_free_bitmap[i] &= ~(1ULL << j);
+ physical_free_count_bytes -= 4096;
+ return (i * 64 + j) * 4096;
+ }
+ }
+
+ i = (i + 1) % (max_physical / 4096 / 64);
+
+ if (i == next_try_physical_free)
+ return result_code::out_of_memory;
+
+ }
+
+}
+
+//maps p1 if needed. returns old p1e value.
+template
+static result set_core_p1e(void *vma, uint64_t p1e) {
+
+ uint64_t vma64 = (uint64_t)vma;
+
+ assert(vma64 % 4096 == 0);
+ assert(vma64 >= 0xffffff8000000000);
+ assert(vma64 < 0xffffffffc0000000);
+
+ int p1i = (vma64 / 4096) % 512;
+ int p2i = (vma64 / 4096 / 512) % 512;
+ int p3i = (vma64 / 4096 / 512 / 512) % 512;
+
+ bool p1_is_new = false;
+
+ if (core_top_p1s[p3i * 512 + p2i] == 0) {
+ auto result = take_free_physical_page();
+ if (result.is_error())
+ return result.get_error();
+ uint64_t p1_pma = result.unwrap();
+ core_top_p1s[p3i * 512 + p2i] = p1_pma | 0x8000000000000003;
+ core_p2s[p3i * 512 + p2i] = p1_pma | 0x3;
+ p1_is_new = true;
+ }
+
+ uint64_t *p1_vma;
+ if constexpr (hhdm_p1s) {
+ uint64_t p1_pma = core_top_p1s[p3i * 512 + p2i] & 0x7ffffffffffff000;
+ p1_vma = (uint64_t *)(hhdm + p1_pma);
+ }
+ else
+ p1_vma = (uint64_t *)(0xffffffffc0000000 + (p3i * 512 + p2i) * 4096);
+
+ if (p1_is_new)
+ for (int i = 0; i < 512; ++i)
+ p1_vma[i] = 0;
+
+ uint64_t old_p1e = p1_vma[p1i];
+ p1_vma[p1i] = p1e;
+ return old_p1e;
+
+}
+
+template
+static result create_core_stack_guard(void *vma) {
+
+ auto result = set_core_p1e(vma, 0x1000);
+ if (result.is_error())
+ return result.get_error();
+
+ assert(result.unwrap() == 0);
+ return {};
+
+}
+
+template
+static result map_core_page(
+ void *vma, uint64_t pma, bool executable, bool writable) {
+
+ auto result = set_core_p1e(vma,
+ pma | 0x1 | (executable ? 0 : 0x8000000000000000) | (writable ? 0x2 : 0));
+ if (result.is_error())
+ return result.get_error();
+
+ assert(result.unwrap() == 0);
+ return {};
+
+}
+
+//might return error with some mapped
+template
+static result map_core_page_range(
+ void *vma, uint64_t pma, uint64_t page_count,
+ bool executable, bool writable) {
+
+ for (uint64_t i = 0; i < page_count; ++i) {
+ auto result = map_core_page(
+ (uint8_t *)vma + 4096 * i, pma + 4096 * i, executable, writable);
+ if (result.is_error())
+ return result.get_error();
+ }
+
+ return {};
+
+}
+
+template
+static result find_free_core_virtual_pages(uint64_t page_count) {
+
+ uint64_t start_i = 0;
+ uint64_t on_i = 0;
+ uint64_t count = 0;
+
+ while (true) {
+
+ if (count >= page_count)
+ return (void *)(0xffffff8000000000 + start_i * 4096);
+
+ if (on_i >= 512 * 512 * 511)
+ return result_code::out_of_memory;
+
+ int p2si = on_i / 512;
+ int p1i = on_i % 512;
+
+ if (p1i == 0 && core_p2s[p2si] == 0) {
+ count += 512;
+ on_i += 512;
+ continue;
+ }
+
+ uint64_t *p1;
+ if constexpr (hhdm_p1s) {
+ uint64_t p1_pma = core_p2s[p2si] & 0x7ffffffffffff000;
+ p1 = (uint64_t *)(hhdm + p1_pma);
+ }
+ else
+ p1 = (uint64_t *)(0xffffffffc0000000 + p2si * 4096);
+
+ if (p1[p1i] == 0) {
+ ++count;
+ ++on_i;
+ continue;
+ }
+
+ count = 0;
+ ++on_i;
+ start_i = on_i;
+
+ }
+
+}
+
+template
+static result unmap_and_free_core_page(void *vma) {
+
+ auto result = set_core_p1e(vma, 0);
+ if (result.is_error())
+ return result.get_error();
+ uint64_t old_p1e = result.unwrap();
+
+ assert(old_p1e & 0x1);
+
+ uint64_t pma = old_p1e & 0x7ffffffffffff000;
+ mark_physical_free(pma, pma + 4096);
+
+ return {};
+
+}
+
+extern "C" [[noreturn]] void asm_set_cr3(
+ uint64_t cr3, void *stack_top, void (*jump_to)());
+
+[[noreturn]] static void with_cr3();
+
+extern "C" [[noreturn]] void __limine_entry() {
+
+ if (!LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision))
+ die();
+
+ auto hhdm_response = hhdm_request.response;
+ if (hhdm_response == 0)
+ die();
+
+ hhdm = hhdm_response->offset;
+
+ auto executable_address_response = executable_address_request.response;
+ if (executable_address_response == 0)
+ die();
+
+ auto exe_offset =
+ executable_address_response->virtual_base -
+ executable_address_response->physical_base;
+
+ auto memmap_response = memmap_request.response;
+ if (memmap_response == 0)
+ die();
+
+ for (uint64_t i = 0; i < memmap_response->entry_count; ++i) {
+ auto entry = memmap_response->entries[i];
+ if (entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE ||
+ entry->type == LIMINE_MEMMAP_USABLE) {
+ if (entry->base % 4096 != 0 || entry->length % 4096 != 0)
+ die();
+ if (entry->base + entry->length > max_physical)
+ max_physical = entry->base + entry->length;
+ }
+ }
+
+ uint64_t physical_map_pages_needed = (max_physical - 1) / 32768 + 1;
+ uint64_t physical_map_physical;
+ bool found_physical_map_physical = false;
+
+ for (uint64_t i = 0; i < memmap_response->entry_count; ++i) {
+ auto entry = memmap_response->entries[i];
+ if (entry->type == LIMINE_MEMMAP_USABLE &&
+ entry->length >= physical_map_pages_needed * 4096) {
+ physical_map_physical = entry->base;
+ entry->base += physical_map_pages_needed * 4096;
+ entry->length -= physical_map_pages_needed * 4096;
+ found_physical_map_physical = true;
+ break;
+ }
+ }
+
+ if (!found_physical_map_physical)
+ die();
+
+ physical_free_bitmap = (uint64_t *)(hhdm + physical_map_physical);
+ for (uint64_t i = 0; i < physical_map_pages_needed * 4096 / 8; ++i)
+ physical_free_bitmap[i] = 0;
+
+ for (uint64_t i = 0; i < memmap_response->entry_count; ++i) {
+ auto entry = memmap_response->entries[i];
+ if (entry->type == LIMINE_MEMMAP_USABLE)
+ mark_physical_free(entry->base, entry->base + entry->length);
+ }
+
+ for (int i = 0; i < 511; ++i)
+ core_p4[i] = 0;
+ core_p4[511] = ((uint64_t)core_p3 - exe_offset) | 0x3;
+
+ for (int i = 0; i < 512; ++i)
+ core_p3[i] = ((uint64_t)&core_p2s[i * 512] - exe_offset) | 0x3;
+
+ for (int i = 0; i < 512 * 511; ++i)
+ core_p2s[i] = 0;
+ for (int i = 0; i < 512; ++i)
+ core_p2s[512 * 511 + i] =
+ ((uint64_t)&core_top_p1s[i * 512] - exe_offset) | 0x3;
+
+ for (int i = 0; i < 512 * 512; ++i)
+ core_top_p1s[i] = 0;
+
+ auto result = map_core_page_range(
+ &__image_rx_start, (uint64_t)&__image_rx_start - exe_offset,
+ (&__image_rx_end - &__image_rx_start) / 4096, true, false);
+ if (result.is_error())
+ die();
+
+ auto result_2 = map_core_page_range(
+ &__image_ro_start, (uint64_t)&__image_ro_start - exe_offset,
+ (&__image_ro_end - &__image_ro_start) / 4096, false, false);
+ if (result_2.is_error())
+ die();
+
+ auto result_3 = map_core_page_range(
+ &__image_rw_start, (uint64_t)&__image_rw_start - exe_offset,
+ (&__image_rw_end - &__image_rw_start) / 4096, false, true);
+ if (result_3.is_error())
+ die();
+
+ auto new_physical_free_bitmap_result =
+ find_free_core_virtual_pages(physical_map_physical);
+ if (new_physical_free_bitmap_result.is_error())
+ die();
+ void *new_physical_free_bitmap = new_physical_free_bitmap_result.unwrap();
+
+ auto result_4 = map_core_page_range(
+ new_physical_free_bitmap, physical_map_physical,
+ physical_map_pages_needed, false, true);
+ if (result_4.is_error())
+ die();
+
+ auto result_5 = unmap_and_free_core_page(temp_stack_and_guards);
+ if (result_5.is_error())
+ die();
+ auto result_6 = create_core_stack_guard(temp_stack_and_guards);
+ if (result_6.is_error())
+ die();
+
+ auto result_7 = unmap_and_free_core_page(
+ &temp_stack_and_guards[4096 + temp_stack_length]);
+ if (result_7.is_error())
+ die();
+ auto result_8 = create_core_stack_guard(
+ &temp_stack_and_guards[4096 + temp_stack_length]);
+ if (result_8.is_error())
+ die();
+
+ physical_free_bitmap = (uint64_t *)new_physical_free_bitmap;
+
+ asm_set_cr3(
+ (uint64_t)core_p4 - exe_offset,
+ &temp_stack_and_guards[4096 + temp_stack_length],
+ &with_cr3);
+
+}
+
+[[noreturn]] static void with_cr3() {
+
+ while (true)
+ ;
+
+}
diff --git a/src/core/limine.hxx b/src/core/limine.hxx
new file mode 120000
index 0000000..5f027f0
--- /dev/null
+++ b/src/core/limine.hxx
@@ -0,0 +1 @@
+../../dependencies/limine-protocol/include/limine.h
\ No newline at end of file
diff --git a/src/kernel/link.ld b/src/core/link.ld
similarity index 65%
rename from src/kernel/link.ld
rename to src/core/link.ld
index f3f4a2f..de3298c 100644
--- a/src/kernel/link.ld
+++ b/src/core/link.ld
@@ -1,7 +1,7 @@
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
-ENTRY(kernel_entry)
+ENTRY(__limine_entry)
PHDRS {
rx PT_LOAD FLAGS(5);
@@ -11,36 +11,36 @@ PHDRS {
SECTIONS {
- . = 0xffffffffc0000000;
- __kernel_rx_start = .;
+ . = 0xffffffff80000000;
+ __image_rx_start = .;
.text : {
*(.text .text.*)
} : rx
-
. = ALIGN(4096);
- __kernel_rx_end = .;
- __kernel_ro_start = .;
+ __image_rx_end = .;
+ __image_ro_start = .;
.rodata : {
*(.rodata .rodata.*)
} : ro
-
. = ALIGN(4096);
- __kernel_ro_end = .;
- __kernel_rw_start = .;
+ __image_ro_end = .;
+ __image_rw_start = .;
.data : {
*(.data .data.*)
} : rw
-
. = ALIGN(4096);
.bss : {
*(.bss .bss.*)
*(COMMON)
} : rw
-
. = ALIGN(4096);
- __kernel_rw_end = .;
+ __image_rw_end = .;
+
+ /DISCARD/ : {
+ *(.eh_frame)
+ }
}
diff --git a/src/kernel/debug.c b/src/kernel/debug.c
deleted file mode 100644
index 2c794ab..0000000
--- a/src/kernel/debug.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Calcite, src/kernel/debug.c
- * 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
- * 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 "serial.h"
-#include "debug.h"
-
-#include
-
-void log_core(const char *file, const char *function, const char *format, ...) {
-
- int file_length = 0;
- while (file[file_length] != 0)
- ++file_length;
-
- int function_length = 0;
- while (function[function_length] != 0)
- ++function_length;
-
- write_serial_string_n(file, file_length);
- write_serial_string_n(" ", 1);
- write_serial_string_n(function, function_length);
-
- write_serial_string_n(" -", 2);
- for (int i = file_length + function_length + 3; i < 49; ++i)
- write_serial_string_n("-", 1);
- while (format[0] == ' ') {
- write_serial_string_n("-", 1);
- ++format;
- }
- write_serial_string_n(" ", 1);
-
- va_list args;
- va_start(args, format);
-
- while (1) {
-
- int next_percent = 0;
- while (format[next_percent] != '%') {
- if (format[next_percent] == 0) {
- write_serial_string_n(format, next_percent);
- va_end(args);
- write_serial_string_n("\r\n", 2);
- return;
- }
- ++next_percent;
- }
-
- write_serial_string_n(format, next_percent);
-
- switch (format[next_percent + 1]) {
- case 's': {
- const char *arg = va_arg(args, const char *);
- write_serial_string(arg);
- break;
- }
- case 'd': {
- int arg = va_arg(args, int);
- write_serial_integer(arg);
- break;
- }
- case 'h': {
- uint64_t arg1 = va_arg(args, uint64_t);
- int arg2 = va_arg(args, int);
- write_serial_hex(arg1, arg2);
- break;
- }
- case 'B': {
- uint64_t arg = va_arg(args, uint64_t);
- if (arg < 10000) {
- write_serial_integer(arg);
- write_serial_string_n(" B", 2);
- }
- else if ((arg + 512) / 1024 < 10000) {
- write_serial_integer((arg + 512) / 1024);
- write_serial_string_n(" kiB", 4);
- }
- else if ((arg + 512 * 1024) / (1024 * 1024) < 10000) {
- write_serial_integer((arg + 512 * 1024) / (1024 * 1024));
- write_serial_string_n(" MiB", 4);
- }
- else if ((arg + 512 * 1024 * 1024) / (1024 * 1024 * 1024) < 10000) {
- write_serial_integer((arg + 512 * 1024 * 1024) / (1024 * 1024 * 1024));
- write_serial_string_n(" GiB", 4);
- }
- else {
- write_serial_integer((arg + 512 * 1024 * 1024 * 1024ULL) / (1024 * 1024 * 1024 * 1024ULL));
- write_serial_string_n(" TiB", 4);
- }
- break;
- }
- default:
- panic("bad format string")
- }
-
- format = &format[next_percent + 2];
-
- }
-
-}
diff --git a/src/kernel/debug.h b/src/kernel/debug.h
deleted file mode 100644
index 3c531d1..0000000
--- a/src/kernel/debug.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Calcite, src/kernel/debug.h
- * 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
- * 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 log_core(const char *file, const char *function, const char *format, ...);
-
-#define log(...) { \
- log_core(__FILE__, __func__, __VA_ARGS__); \
-}
-
-#ifdef CALCITE_DEBUG
-#define debug_log(...) log(__VA_ARGS__)
-#elif CALCITE_RELEASE
-#define debug_log(...) {}
-#else
-#error neither CALCITE_DEBUG nor CALCITE_RELEASE defined
-#endif
-
-#define panic(message) { \
- log_core(__FILE__, __func__, "kernel panic: %s", message); \
- while (1) \
- __asm__ ("hlt"); \
-}
-
-#define assert(condition) \
- { \
- if (!(condition)) \
- panic("assertion failed: " #condition) \
- }
diff --git a/src/kernel/drives.c b/src/kernel/drives.c
deleted file mode 100644
index 422cf8b..0000000
--- a/src/kernel/drives.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Calcite, src/kernel/drives.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 "utility.h"
-#include "drives.h"
-#include "heap.h"
-
-struct drive_info *drive_list;
-int drive_list_buffer_length = 0;
-int drive_list_count = 0;
-
-#define DRIVE_LIST_INITIAL_LENGTH 16
-
-struct drive_info *add_drive() {
-
- if (drive_list_buffer_length == 0) {
- drive_list = heap_alloc(DRIVE_LIST_INITIAL_LENGTH * sizeof(struct drive_info));
- drive_list_buffer_length = DRIVE_LIST_INITIAL_LENGTH;
- }
-
- else if (drive_list_count == drive_list_buffer_length) {
- struct drive_info *new_drive_list =
- heap_alloc(2 * drive_list_buffer_length * sizeof(struct drive_info));
- memcpy(new_drive_list, drive_list, drive_list_count * sizeof(struct drive_info));
- heap_dealloc(drive_list, drive_list_count * sizeof(struct drive_info));
- drive_list = new_drive_list;
- drive_list_buffer_length *= 2;
- }
-
- struct drive_info *to_return = &drive_list[drive_list_count];
- ++drive_list_count;
- return to_return;
-
-}
-
-const struct drive_info *look_up_drive(const char *name) {
- for (int i = 0; i < drive_list_count; ++i)
- if (strequ(drive_list[i].name, name))
- return &drive_list[i];
- return 0;
-}
diff --git a/src/kernel/drives.h b/src/kernel/drives.h
deleted file mode 100644
index 6baef36..0000000
--- a/src/kernel/drives.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Calcite, src/kernel/drives.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-enum drive_access_result {
- DAR_SUCCESS,
- DAR_HARDWARE_ERROR
-};
-
-struct drive_info {
-
- const char *name;
- uint64_t block_size;
- uint64_t block_count;
- const void *driver_info;
-
- //start and count are both in blocks. bounds-checking is caller responsibility.
- enum drive_access_result (*read_blocks)(
- const struct drive_info *info, void *buffer, uint64_t start, uint64_t count);
-
-};
-
-//add a drive by calling this and writing to the returned pointer.
-//the returned pointer might be invalidated by future calls.
-struct drive_info *add_drive();
-
-//returned pointer might be invalidated by future calls to add_drive.
-//returns 0 if there is no drive with that name.
-const struct drive_info *look_up_drive(const char *name);
diff --git a/src/kernel/entry.c b/src/kernel/entry.c
deleted file mode 100644
index 44e6486..0000000
--- a/src/kernel/entry.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/* Calcite, src/kernel/entry.c
- * 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
- * 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 "framebuffer.h"
-#include "interrupts.h"
-#include "ipc-dgram.h"
-#include "scheduler.h"
-#include "syscalls.h"
-#include "process.h"
-#include "utility.h"
-#include "drives.h"
-#include "paging.h"
-#include "serial.h"
-#include "debug.h"
-#include "input.h"
-#include "timer.h"
-#include "heap.h"
-#include "pci.h"
-#include "ps2.h"
-#include "fs.h"
-
-#include
-#include
-
-LIMINE_BASE_REVISION(3)
-
-static volatile struct limine_framebuffer_request fb_request = {
- .id = LIMINE_FRAMEBUFFER_REQUEST,
- .revision = 0,
- .response = 0
-};
-
-static volatile struct limine_hhdm_request hhdm_request = {
- .id = LIMINE_HHDM_REQUEST,
- .revision = 0,
- .response = 0
-};
-
-static volatile struct limine_kernel_address_request ka_request = {
- .id = LIMINE_KERNEL_ADDRESS_REQUEST,
- .revision = 0,
- .response = 0
-};
-
-static volatile struct limine_memmap_request memmap_request = {
- .id = LIMINE_MEMMAP_REQUEST,
- .revision = 0,
- .response = 0
-};
-
-static volatile struct limine_executable_cmdline_request cmdline_request = {
- .id = LIMINE_EXECUTABLE_CMDLINE_REQUEST,
- .revision = 0,
- .response = 0
-};
-
-[[noreturn]] static void die() {
- while (1)
- __asm__ ("hlt");
-}
-
-//these are defined in the linker script. they are page-aligned.
-extern uint8_t __kernel_rx_start;
-extern uint8_t __kernel_rx_end;
-extern uint8_t __kernel_ro_start;
-extern uint8_t __kernel_ro_end;
-extern uint8_t __kernel_rw_start;
-extern uint8_t __kernel_rw_end;
-
-static void map_kernel_region(
- uint64_t physical_start, void *virtual_start,
- uint64_t length, int writable, int executable) {
- for (uint64_t i = 0; i < length; i += 4096)
- map_in_kernel_page_table(
- physical_start + i, (uint8_t *)virtual_start + i, writable, executable);
-}
-
-#define MAX_CMDLINE_LENGTH 1000
-static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
-
-[[noreturn]] static void with_kernel_page_tables();
-
-[[noreturn]] void kernel_entry() {
-
- //die if the bootloader hasn't given us something that we need.
-
- if (fb_request.response == 0 || hhdm_request.response == 0 ||
- ka_request.response == 0 || memmap_request.response == 0 ||
- fb_request.response->framebuffer_count == 0)
- die();
-
- struct limine_framebuffer *fb = fb_request.response->framebuffers[0];
- if (fb->memory_model != LIMINE_FRAMEBUFFER_RGB || fb->bpp != 32 ||
- fb->red_mask_shift != 16 || fb->red_mask_size != 8 ||
- fb->green_mask_shift != 8 || fb->green_mask_size != 8 ||
- fb->blue_mask_shift != 0 || fb->blue_mask_size != 8)
- die();
-
- //set up page tables. we will mark the regions with bootloader structures as
- //usable, so we need to be careful not to allocate any physical pages until
- //after we are done using bootloader structures. we map the kernel into our
- //page tables, and we remap the framebuffer to somewhere in kernel memory.
- //we do not map the bootloader structures, so we also need to not switch
- //to the new tables until we are done using them.
-
- uint64_t kernel_physical_base = ka_request.response->physical_base;
- uint8_t *kernel_virtual_base = (uint8_t *)ka_request.response->virtual_base;
- init_paging(kernel_physical_base, kernel_virtual_base);
-
- struct limine_memmap_response *mm_response = memmap_request.response;
- for (uint64_t i = 0; i < mm_response->entry_count; ++i) {
- struct limine_memmap_entry *entry = mm_response->entries[i];
- if (entry->type == LIMINE_MEMMAP_USABLE ||
- entry->type == LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE)
- //limine guarantees these two types are page-aligned already.
- mark_physical_memory_free(entry->base, entry->length);
- }
-
- map_kernel_region(
- &__kernel_rx_start - kernel_virtual_base + kernel_physical_base,
- &__kernel_rx_start, &__kernel_rx_end - &__kernel_rx_start, 0, 1);
- map_kernel_region(
- &__kernel_ro_start - kernel_virtual_base + kernel_physical_base,
- &__kernel_ro_start, &__kernel_ro_end - &__kernel_ro_start, 0, 0);
- map_kernel_region(
- &__kernel_rw_start - kernel_virtual_base + kernel_physical_base,
- &__kernel_rw_start, &__kernel_rw_end - &__kernel_rw_start, 1, 0);
-
- //we round up to a multiple of a page.
- uint64_t fb_length = ((fb->height * fb->pitch - 1) / 4096 + 1) * 4096;
- fb_physical_base = (uint64_t)fb->address - hhdm_request.response->offset;
- fb_base = find_free_kernel_region(fb_length);
- map_kernel_region(
- fb_physical_base, fb_base, fb_length, 1, 0);
-
- //store rest of framebuffer information
-
- fb_width = fb->width;
- fb_height = fb->height;
- fb_pitch = fb->pitch;
-
- //make a copy of the kernel cmdline
-
- const char *original_cmdline = cmdline_request.response->cmdline;
- int cmdline_length = 0;
- while (original_cmdline[cmdline_length] != 0)
- ++cmdline_length;
-
- if (cmdline_length > MAX_CMDLINE_LENGTH)
- die();
-
- memcpy(cmdline_copy, original_cmdline, cmdline_length + 1);
-
- //switch to kernel page tables!
-
- switch_to_kernel_page_tables(&with_kernel_page_tables);
-
-}
-
-struct cmdline_pair {
- const char *key;
- const char *value;
-};
-
-static const struct cmdline_pair *cmdline_pairs;
-static int cmdline_pair_count;
-
-//returns the corresponding value if the key exists, otherwise returns 0.
-static const char *cmdline_look_up(const char *key) {
- for (int i = 0; i < cmdline_pair_count; ++i)
- if (strequ(cmdline_pairs[i].key, key))
- return cmdline_pairs[i].value;
- return 0;
-}
-
-[[noreturn]] static void with_kernel_page_tables() {
-
- init_serial();
-
- //store cmdline as key-value pairs
-
- if (cmdline_copy[0] == 0)
- cmdline_pair_count = 0;
-
- else {
-
- int comma_count = 0;
- for (int i = 0; cmdline_copy[i] != 0; ++i)
- if (cmdline_copy[i] == ',')
- ++comma_count;
-
- cmdline_pair_count = comma_count + 1;
- struct cmdline_pair *pairs = heap_alloc(cmdline_pair_count * sizeof(struct cmdline_pair));
-
- int key_start = 0;
- for (int i = 0; i < cmdline_pair_count; ++i) {
-
- int key_end = key_start;
- while (1) {
- if (cmdline_copy[key_end] == '=')
- break;
- if (cmdline_copy[key_end] == ',' || cmdline_copy[key_end] == 0)
- die();
- ++key_end;
- }
-
- int value_start = key_end + 1;
- int value_end = value_start;
- while (1) {
- if (cmdline_copy[value_end] == ',' || cmdline_copy[value_end] == 0)
- break;
- if (cmdline_copy[value_end] == '=')
- die();
- ++value_end;
- }
-
- char *key = heap_alloc(key_end - key_start + 1);
- memcpy(key, &cmdline_copy[key_start], key_end - key_start);
- key[key_end - key_start] = 0;
- pairs[i].key = key;
-
- char *value = heap_alloc(value_end - value_start + 1);
- memcpy(value, &cmdline_copy[value_start], value_end - value_start);
- value[value_end - value_start] = 0;
- pairs[i].value = value;
-
- key_start = value_end + 1;
-
- }
-
- cmdline_pairs = pairs;
-
- }
-
- debug_log("command line")
- for (int i = 0; i < cmdline_pair_count; ++i)
- debug_log(" %s %s", cmdline_pairs[i].key, cmdline_pairs[i].value)
-
- //set up interrupts
-
- init_timer();
- init_ps2();
-
- set_irq_handler(0x00, &on_pit_irq);
- set_irq_handler(0x01, &on_keyboard_irq);
- set_irq_handler(0x0c, &on_mouse_irq);
-
- init_interrupts();
-
- //set up syscalls
-
- init_syscalls();
- register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread);
- register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer);
- register_syscall(SYSCALL_OPEN_FILE, (void *)&syscall_open_file);
- register_syscall(SYSCALL_CLOSE_FILE, (void *)&syscall_close_file);
- register_syscall(SYSCALL_GET_FILE_SIZE, (void *)&syscall_get_file_size);
- register_syscall(SYSCALL_READ_FILE, (void *)&syscall_read_file);
- register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet);
- register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages);
- register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms);
- register_syscall(SYSCALL_START_ELF, (void *)&syscall_start_elf);
- register_syscall(SYSCALL_IPC_CREATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_dgram_receiver);
- register_syscall(SYSCALL_IPC_CREATE_DGRAM_SENDER, (void *)&syscall_ipc_create_dgram_sender);
- register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram);
- register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram);
- register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread);
- 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);
- register_syscall(SYSCALL_CHANGE_PAGE_PERMISSIONS, (void *)&syscall_change_page_permissions);
- register_syscall(SYSCALL_WAIT_FOR_KEYBOARD_PACKET, (void *)&syscall_wait_for_keyboard_packet);
-
- //probe pci devices
-
- probe_pci();
-
- //load root file system
-
- const char *root_drive_name = cmdline_look_up("root-drive");
- if (!root_drive_name)
- panic("root drive mising from cmdline")
- const struct drive_info *root_drive = look_up_drive(root_drive_name);
- if (!root_drive)
- panic("could not find root drive")
-
- const char *root_fs_type = cmdline_look_up("root-fs");
- if (!root_fs_type)
- panic("root fs missing from cmdline")
- struct fs_info *root_fs = heap_alloc(sizeof(struct fs_info));
- if (create_fs_info(root_drive, root_fs, root_fs_type) != FAR_SUCCESS)
- panic("could not create root file system info")
-
- set_fs("root", root_fs);
-
- //load init and start it
-
- init_scheduler();
-
- if (start_elf("root://calcite/apps/init/init.elf") == 0)
- panic("could not start init.elf")
-
- resume_next_continuation();
-
-}
diff --git a/src/kernel/framebuffer.c b/src/kernel/framebuffer.c
deleted file mode 100644
index a9e545d..0000000
--- a/src/kernel/framebuffer.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Calcite, src/kernel/framebuffer.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 "framebuffer.h"
-
-uint64_t fb_physical_base;
-uint8_t *fb_base;
-int fb_width;
-int fb_height;
-int fb_pitch;
diff --git a/src/kernel/framebuffer.h b/src/kernel/framebuffer.h
deleted file mode 100644
index 71bdb57..0000000
--- a/src/kernel/framebuffer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Calcite, src/kernel/framebuffer.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-extern uint64_t fb_physical_base;
-extern uint8_t *fb_base;
-extern int fb_width;
-extern int fb_height;
-extern int fb_pitch;
diff --git a/src/kernel/fs.c b/src/kernel/fs.c
deleted file mode 100644
index 0270dc6..0000000
--- a/src/kernel/fs.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/* Calcite, src/kernel/fs.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 "iso9660.h"
-#include "utility.h"
-#include "debug.h"
-#include "heap.h"
-#include "fs.h"
-
-#include
-
-enum fs_access_result create_fs_info(
- const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type) {
-
- if (strequ(fs_type, "iso9660"))
- return create_iso9660_info(drive, fs_out);
-
- return FAR_UNKNOWN_FS_TYPE;
-
-}
-
-struct set_fs {
- const char *name;
- const struct fs_info *fs;
-};
-
-struct set_fs *set_fses;
-int set_fses_buffer_length = 0;
-int set_fses_count = 0;
-
-#define SET_FSES_INITIAL_LENGTH 16
-
-void set_fs(const char *name, const struct fs_info *fs) {
-
- assert(get_fs(name) == 0)
-
- if (set_fses_buffer_length == 0) {
- set_fses = heap_alloc(SET_FSES_INITIAL_LENGTH * sizeof(struct set_fs));
- set_fses_buffer_length = SET_FSES_INITIAL_LENGTH;
- }
-
- else if (set_fses_count == set_fses_buffer_length) {
- struct set_fs *new_set_fses = heap_alloc(2 * set_fses_buffer_length * sizeof(struct set_fs));
- memcpy(new_set_fses, set_fses, set_fses_count * sizeof(struct set_fs));
- heap_dealloc(set_fses, set_fses_count * sizeof(struct set_fs));
- set_fses = new_set_fses;
- set_fses_buffer_length *= 2;
- }
-
- set_fses[set_fses_count].name = name;
- set_fses[set_fses_count].fs = fs;
- ++set_fses_count;
-
-}
-
-const struct fs_info *get_fs(const char *name) {
- for (int i = 0; i < set_fses_count; ++i)
- if (strequ(set_fses[i].name, name))
- return set_fses[i].fs;
- return 0;
-}
-
-enum fs_access_result look_up_file(
- const char *uri, const struct fs_info **fs_out, void **node_out) {
-
- int colon = 0;
- while (1) {
- if (uri[colon] == ':')
- break;
- if (uri[colon] == 0)
- panic("bad uri")
- ++colon;
- }
-
- if (uri[colon + 1] != '/' || uri[colon + 2] != '/')
- panic("bad uri")
-
- char *uri_copy = heap_alloc(colon + 1);
- memcpy(uri_copy, uri, colon);
- uri_copy[colon] = 0;
-
- *fs_out = get_fs(uri_copy);
-
- heap_dealloc(uri_copy, colon + 1);
-
- if (!*fs_out)
- return FAR_NOT_FOUND;
-
- return (*(*fs_out)->look_up_file)(*fs_out, node_out, &uri[colon + 3]);
-
-}
diff --git a/src/kernel/fs.h b/src/kernel/fs.h
deleted file mode 100644
index a60d679..0000000
--- a/src/kernel/fs.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Calcite, src/kernel/fs.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include "drives.h"
-
-#include
-#include
-
-struct fs_stat {
- uint64_t bytes;
-};
-
-struct fs_info {
-
- const struct drive_info *drive;
- const void *driver_info;
-
- enum fs_access_result (*free_node)(
- const struct fs_info *info, void *node);
-
- enum fs_access_result (*look_up_file)(
- const struct fs_info *info, void **node_out, const char *path);
-
- enum fs_access_result (*stat_file)(
- const struct fs_info *info, void *node, struct fs_stat *stat_out);
-
- //start is in bytes. bounds-checking is caller responsibility.
- enum fs_access_result (*read_file)(
- const struct fs_info *info, void *node,
- void *buffer, uint64_t start, uint64_t bytes);
-
-};
-
-enum fs_access_result create_fs_info(
- const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type);
-
-void set_fs(const char *name, const struct fs_info *fs);
-
-//returns 0 if no fs is set with that name.
-const struct fs_info *get_fs(const char *name);
-
-enum fs_access_result look_up_file(const char *uri, const struct fs_info **fs_out, void **node_out);
diff --git a/src/kernel/heap.c b/src/kernel/heap.c
deleted file mode 100644
index 4a9c1f9..0000000
--- a/src/kernel/heap.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Calcite, src/kernel/heap.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 "paging.h"
-#include "heap.h"
-
-struct dealloc_record {
- //0 for unused
- uint64_t length;
- void *start;
-};
-
-struct dealloc_record_page {
- struct dealloc_record records[255];
- struct dealloc_record_page *prev_page;
-};
-
-static struct dealloc_record_page *last_page;
-
-static void *map_pages(uint64_t page_count) {
-
- void *vma = find_free_kernel_region(page_count * 4096);
-
- for (uint64_t i = 0; i < page_count; ++i) {
- uint64_t pma = take_free_physical_page();
- map_in_kernel_page_table(pma, vma + i * 4096, 1, 0);
- }
-
- return vma;
-
-}
-
-static struct dealloc_record *get_unused_record() {
-
- for (struct dealloc_record_page *page = last_page;
- page != 0; page = page->prev_page)
- for (int i = 0; i < 255; ++i)
- if (page->records[i].length == 0)
- return &page->records[i];
-
- struct dealloc_record_page *page = map_pages(1);
- page->prev_page = last_page;
- last_page = page;
-
- page->records[0].start = (void *)page + 4096 - 8;
- page->records[0].length = 8;
-
- for (int i = 2; i < 255; ++i)
- page->records[i].length = 0;
-
- return &page->records[1];
-
-}
-
-void *heap_alloc(uint64_t length) {
-
- for (struct dealloc_record_page *page = last_page;
- page != 0; page = page->prev_page)
- for (int i = 0; i < 255; ++i)
- if (page->records[i].length == length) {
- page->records[i].length = 0;
- return page->records[i].start;
- }
-
- for (struct dealloc_record_page *page = last_page;
- page != 0; page = page->prev_page)
- for (int i = 0; i < 255; ++i)
- if (page->records[i].length > length) {
- page->records[i].length -= length;
- return page->records[i].start + page->records[i].length;
- }
-
- uint64_t pages = (length - 1) / 4096 + 1;
- void *vma = map_pages(pages);
-
- if (pages * 4096 != length) {
- struct dealloc_record *record = get_unused_record();
- record->start = vma + length;
- record->length = pages * 4096 - length;
- }
-
- return vma;
-
-}
-
-void heap_dealloc(void *start, uint64_t length) {
- struct dealloc_record *record = get_unused_record();
- record->start = start;
- record->length = length;
-}
diff --git a/src/kernel/heap.h b/src/kernel/heap.h
deleted file mode 100644
index a999f49..0000000
--- a/src/kernel/heap.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Calcite, src/kernel/heap.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-void *heap_alloc(uint64_t length);
-void heap_dealloc(void *start, uint64_t length);
diff --git a/src/kernel/input.c b/src/kernel/input.c
deleted file mode 100644
index ca7f831..0000000
--- a/src/kernel/input.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* Calcite, src/kernel/input.c
- * 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
- * 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 "scheduler.h"
-#include "process.h"
-#include "debug.h"
-#include "input.h"
-#include "heap.h"
-
-static int is_somebody_waiting_for_mouse_packet = 0;
-static struct continuation_info waiting_continuation_mouse;
-static struct mouse_packet *resume_by_reporting_to_mouse;
-
-static int total_unreported_x_movement = 0;
-static int total_unreported_y_movement = 0;
-
-void add_mouse_movement(int x, int y) {
-
- total_unreported_x_movement += x;
- total_unreported_y_movement += y;
-
- if (is_somebody_waiting_for_mouse_packet) {
- resume_by_reporting_to_mouse->x_change = total_unreported_x_movement;
- resume_by_reporting_to_mouse->y_change = total_unreported_y_movement;
- add_to_queue(&ready_continuations, &waiting_continuation_mouse);
- total_unreported_x_movement = 0;
- total_unreported_y_movement = 0;
- is_somebody_waiting_for_mouse_packet = 0;
- }
-
-}
-
-void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out) {
-
- assert(running_thread != 0);
-
- //TODO: handle these
- if (is_somebody_waiting_for_mouse_packet ||
- !is_mapped_writable(
- running_thread->process, packet_out, sizeof(struct mouse_packet)))
- panic("TODO")
-
- if (total_unreported_x_movement != 0 || total_unreported_y_movement != 0) {
- packet_out->x_change = total_unreported_x_movement;
- packet_out->y_change = total_unreported_y_movement;
- total_unreported_x_movement = 0;
- total_unreported_y_movement = 0;
- return;
- }
-
- is_somebody_waiting_for_mouse_packet = 1;
- resume_by_reporting_to_mouse = packet_out;
- yield(&waiting_continuation_mouse);
-
-}
-
-static int is_somebody_waiting_for_keyboard_packet = 0;
-static struct continuation_info waiting_continuation_keyboard;
-static int *resume_by_reporting_to_keyboard;
-
-//in packets
-#define INITIAL_KEYBOARD_BUFFER_LENGTH 1024
-
-static int *keyboard_buffer = 0;
-static int keyboard_buffer_length = 0;
-static int keyboard_buffer_used = 0;
-static int keyboard_buffer_read_pointer = 0;
-
-void add_keyboard_packet(int packet) {
-
- if (is_somebody_waiting_for_keyboard_packet) {
- *resume_by_reporting_to_keyboard = packet;
- add_to_queue(&ready_continuations, &waiting_continuation_keyboard);
- is_somebody_waiting_for_keyboard_packet = 0;
- }
-
- else if (keyboard_buffer_used < keyboard_buffer_length) {
- int write_pointer = (keyboard_buffer_read_pointer + keyboard_buffer_used) % keyboard_buffer_length;
- keyboard_buffer[write_pointer] = packet;
- ++keyboard_buffer_used;
- }
-
- else if (keyboard_buffer == 0) {
- keyboard_buffer = heap_alloc(INITIAL_KEYBOARD_BUFFER_LENGTH * sizeof(int));
- keyboard_buffer_length = INITIAL_KEYBOARD_BUFFER_LENGTH;
- keyboard_buffer[0] = packet;
- keyboard_buffer_used = 1;
- }
-
- else {
- int *new_buffer = heap_alloc(2 * keyboard_buffer_length * sizeof(int));
- memcpy(
- new_buffer, &keyboard_buffer[keyboard_buffer_read_pointer],
- keyboard_buffer_length - keyboard_buffer_read_pointer);
- memcpy(
- &new_buffer[keyboard_buffer_length - keyboard_buffer_read_pointer],
- keyboard_buffer, keyboard_buffer_read_pointer);
- heap_dealloc(keyboard_buffer, keyboard_buffer_length * sizeof(int));
- keyboard_buffer = new_buffer;
- keyboard_buffer_length *= 2;
- keyboard_buffer_read_pointer = 0;
- keyboard_buffer[keyboard_buffer_length / 2] = packet;
- ++keyboard_buffer_used;
- }
-
-}
-
-int syscall_wait_for_keyboard_packet() {
-
- if (keyboard_buffer_used > 0) {
- int value = keyboard_buffer[keyboard_buffer_read_pointer];
- keyboard_buffer_read_pointer = (keyboard_buffer_read_pointer + 1) % keyboard_buffer_length;
- --keyboard_buffer_used;
- return value;
- }
-
- if (is_somebody_waiting_for_keyboard_packet)
- panic("TODO")
-
- is_somebody_waiting_for_keyboard_packet = 1;
- int result;
- resume_by_reporting_to_keyboard = &result;
- yield(&waiting_continuation_keyboard);
- return result;
-
-}
diff --git a/src/kernel/input.h b/src/kernel/input.h
deleted file mode 100644
index ef215ea..0000000
--- a/src/kernel/input.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Calcite, src/kernel/input.h
- * 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
- * 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
-
-void add_mouse_movement(int x, int y);
-
-void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out);
-
-void add_keyboard_packet(int packet);
-
-int syscall_wait_for_keyboard_packet();
diff --git a/src/kernel/interrupts.asm b/src/kernel/interrupts.asm
deleted file mode 100644
index 769c2ab..0000000
--- a/src/kernel/interrupts.asm
+++ /dev/null
@@ -1,171 +0,0 @@
- ; Calcite, src/kernel/interrupts.asm
- ; 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
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-;both defined in interrupts.c
-extern isr_exception_c
-extern isr_irq_c
-
-section .rodata
-
-;referenced in interrupts.c
-global isrs
-isrs:
-%assign n 0
-%rep 48
- dq isr_ %+ n
-%assign n n + 1
-%endrep
-
-section .text
-
-%assign n 0
-
-%rep 32
-isr_ %+ n:
-%if n <= 0x07 || n = 0x09 || (n >= 0x0f && n <= 0x1c && n <> 0x11 && n <> 0x15) || n = 0x1f
- push qword 0
-%endif
- push qword n
- jmp isr_exception_common
-%assign n n + 1
-%endrep
-
-%rep 16
-isr_ %+ n:
- push rdi
- mov rdi, n - 32
- jmp isr_irq_common
-%assign n n + 1
-%endrep
-
-isr_exception_common:
- push rax
- push rbx
- push rcx
- push rdx
- push rdi
- push rsi
- push rbp
- push r8
- push r9
- push r10
- push r11
- push r12
- push r13
- push r14
- push r15
- mov rax, cr2
- push rax
- mov rax, cr3
- push rax
- mov rdi, rsp
-
- call isr_exception_c
-
- add rsp, 48
- pop r11
- pop r10
- pop r9
- pop r8
- add rsp, 8
- pop rsi
- pop rdi
- pop rdx
- pop rcx
- add rsp, 8
- pop rax
- add rsp, 16
- iretq
-
-isr_irq_common:
- push rax
- push rcx
- push rdx
- push rsi
- push r8
- push r9
- push r10
- push r11
-
- test dil, 0x08
- jnz .high
-
- call isr_irq_c
- mov al, 0x20
- out 0x20, al
- jmp .common_end
-
-.high:
- call isr_irq_c
- mov al, 0x20
- out 0xa0, al
- out 0x20, al
-
-.common_end:
- pop r11
- pop r10
- pop r9
- pop r8
- pop rsi
- pop rdx
- pop rcx
- pop rax
- pop rdi
-
- iretq
-
-;referenced in interrupts.c
-;rdi is pointer to gdt descriptor
-;rsi is pointer to idt descriptor
-;dx is offset of tss into gdt
-global init_interrupts_asm
-init_interrupts_asm:
-
- ;load tables
-
- lgdt [rdi]
- ltr dx
- lidt [rsi]
-
- ;initialize interrupt controllers
-
- mov al, 0x11
- out 0x20, al
- mov al, 0x20
- out 0x21, al
- mov al, 0x04
- out 0x21, al
- mov al, 0x01
- out 0x21, al
- mov al, 0x00
- out 0x21, al
-
- mov al, 0x11
- out 0xa0, al
- mov al, 0x28
- out 0xa1, al
- mov al, 0x02
- out 0xa1, al
- mov al, 0x01
- out 0xa1, al
- mov al, 0x00
- out 0xa1, al
-
- ret
diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c
deleted file mode 100644
index 6a9f4da..0000000
--- a/src/kernel/interrupts.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/* Calcite, src/kernel/interrupts.c
- * 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
- * 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 .
- */
-
-//relevant sources:
-// amd64 architecture programmer's manual, volume 2: system programming
-// https://osdev.wiki/wiki/Global_Descriptor_Table
-// https://osdev.wiki/wiki/Interrupt_Descriptor_Table
-// https://osdev.wiki/wiki/Task_State_Segment
-
-#include "interrupts.h"
-#include "process.h"
-#include "paging.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;
- uint64_t r12;
- uint64_t r11;
- uint64_t r10;
- uint64_t r9;
- uint64_t r8;
- uint64_t rbp;
- uint64_t rsi;
- uint64_t rdi;
- uint64_t rdx;
- uint64_t rcx;
- uint64_t rbx;
- uint64_t rax;
- uint64_t exception_number;
- //0 if exception pushes no error code
- uint64_t error_code;
- uint64_t rip;
- uint64_t cs;
- uint64_t rflags;
- uint64_t rsp;
- 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(const 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")
-
- while (1)
- __asm__ ("hlt");
-
-}
-
-//referenced in interrupts.asm
-void isr_exception_c(const struct exception_parameter *parameter) {
-
- if (running_thread != 0 &&
- parameter->exception_number == 0x0e &&
- (parameter->error_code & 0x1) == 0x0 &&
- parameter->cr2 >= (uint64_t)running_thread->stack_bottom + 4096 &&
- parameter->cr2 < (uint64_t)running_thread->stack_top) {
-
- void *vma = (void *)(parameter->cr2 & 0xfffffffffffff000);
- unmap_page_for_process(running_thread->process, vma);
- uint64_t pma = take_free_physical_page();
- map_page_for_process(
- running_thread->process, pma,
- vma, 1, 0, 1);
-
- }
-
- else
- unhandled_exception(parameter);
-
-}
-
-void (*irq_handlers[16])() = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-//referenced in interrupts.asm
-void isr_irq_c(uint64_t irq_number) {
- if (irq_handlers[irq_number])
- (*irq_handlers[irq_number])();
-}
-
-void set_irq_handler(uint8_t irq_line, void (*handler)(void)) {
- assert(irq_line < 16);
- irq_handlers[irq_line] = handler;
-}
-
-enum {
-
- GDT_AB_CODE_DATA_ACCESSED = 0x01,
- GDT_AB_DATA_WRITABLE = 0x02,
-
- GDT_AB_TYPE_TSS = 0x09,
- GDT_AB_TYPE_DATA = 0x10,
- GDT_AB_TYPE_CODE = 0x18,
-
- GDT_AB_DPL_ZERO = 0x00,
- GDT_AB_DPL_THREE = 0x60,
-
- GDT_AB_PRESENT = 0x80,
-
- GDT_FLAG_LONG = 0x20
-
-};
-
-struct [[gnu::packed]] gdt_entry {
- uint32_t zero;
- uint8_t zero_;
- uint8_t access_byte;
- uint8_t flags;
- uint8_t zero__;
-};
-
-struct [[gnu::packed]] gdt_entry_system {
- uint16_t limit_0;
- uint16_t base_0;
- uint8_t base_16;
- uint8_t access_byte;
- uint8_t flags;
- uint8_t base_24;
- uint32_t base_32;
- uint32_t zero;
-};
-
-static struct gdt_entry the_gdt[7];
-
-struct [[gnu::packed]] gdt_descriptor {
- uint16_t limit;
- void *base;
-};
-
-static struct gdt_descriptor the_gdt_descriptor = {
- .limit = 7 * sizeof(struct gdt_entry) - 1,
- .base = the_gdt
-};
-
-struct [[gnu::packed]] tss {
- uint32_t zero;
- uint64_t rsp0;
- uint64_t rsp1;
- uint64_t rsp2;
- uint64_t zero_;
- uint64_t ist1;
- uint64_t ist2;
- uint64_t ist3;
- uint64_t ist4;
- uint64_t ist5;
- uint64_t ist6;
- uint64_t ist7;
- uint64_t zero__;
- uint16_t zero___;
- uint16_t io_permission_map_offset;
-};
-
-static struct tss the_tss;
-
-//maybe these should be allocated dynamically with guard pages?
-#define INTERRUPT_STACK_SIZE (16 << 20)
-static uint8_t interrupt_stack_one[INTERRUPT_STACK_SIZE];
-static uint8_t interrupt_stack_two[INTERRUPT_STACK_SIZE];
-
-enum {
-
- //"interrupt" here just means disable interrupts on entry.
- //in our case, we use this for both irq's and exceptions.
- IDT_TYPE_INTERRUPT = 0x0e,
-
- IDT_TYPE_DPL_ZERO = 0x00,
- IDT_TYPE_DPL_THREE = 0x60,
-
- IDT_TYPE_PRESENT = 0x80
-
-};
-
-struct [[gnu::packed]] idt_entry {
- uint16_t offset_0;
- uint16_t segment_selector;
- uint8_t interrupt_stack_index;
- uint8_t type;
- uint16_t offset_16;
- uint32_t offset_32;
- uint32_t zero;
-};
-
-static struct idt_entry the_idt[256];
-
-struct [[gnu::packed]] idt_descriptor {
- uint16_t limit;
- void *base;
-};
-
-static struct idt_descriptor the_idt_descriptor = {
- .limit = 256 * sizeof(struct idt_entry) - 1,
- .base = the_idt
-};
-
-//defined in interrupts.asm
-extern void *isrs[48];
-
-//defined in interrupts.asm
-void init_interrupts_asm(
- struct gdt_descriptor *gdtr,
- struct idt_descriptor *idtr,
- uint16_t tssr);
-
-void init_interrupts() {
-
- the_tss.ist1 = (uint64_t)interrupt_stack_one + INTERRUPT_STACK_SIZE;
- the_tss.ist2 = (uint64_t)interrupt_stack_two + INTERRUPT_STACK_SIZE;
-
- //having the table past the end of the tss will lead to a gpf when the
- //cpu tries to access it, which is the same effect as a disabled port.
- the_tss.io_permission_map_offset = 256;
-
- uint64_t tss_base = (uint64_t)&the_tss;
- uint16_t tss_limit = sizeof(struct tss) - 1;
-
- struct gdt_entry_system *tss_entry = (struct gdt_entry_system *)&the_gdt[1];
- tss_entry->limit_0 = tss_limit;
- tss_entry->base_0 = tss_base & 0xffff;
- tss_entry->base_16 = (tss_base >> 16) & 0xff;
- tss_entry->access_byte =
- GDT_AB_TYPE_TSS | GDT_AB_DPL_ZERO | GDT_AB_PRESENT;
- tss_entry->flags = GDT_FLAG_LONG;
- tss_entry->base_24 = (tss_base >> 24) & 0xff;
- tss_entry->base_32 = tss_base >> 32;
-
- struct gdt_entry *user_data_entry = &the_gdt[3];
- user_data_entry->access_byte =
- GDT_AB_CODE_DATA_ACCESSED | GDT_AB_DATA_WRITABLE |
- GDT_AB_TYPE_DATA | GDT_AB_DPL_THREE | GDT_AB_PRESENT;
- user_data_entry->flags = GDT_FLAG_LONG;
-
- struct gdt_entry *user_code_entry = &the_gdt[4];
- user_code_entry->access_byte =
- GDT_AB_CODE_DATA_ACCESSED | GDT_AB_TYPE_CODE |
- GDT_AB_DPL_THREE | GDT_AB_PRESENT;
- user_code_entry->flags = GDT_FLAG_LONG;
-
- struct gdt_entry *kernel_code_entry = &the_gdt[5];
- kernel_code_entry->access_byte =
- GDT_AB_CODE_DATA_ACCESSED | GDT_AB_TYPE_CODE |
- GDT_AB_DPL_ZERO | GDT_AB_PRESENT;
- kernel_code_entry->flags = GDT_FLAG_LONG;
-
- struct gdt_entry *kernel_data_entry = &the_gdt[6];
- kernel_data_entry->access_byte =
- GDT_AB_CODE_DATA_ACCESSED | GDT_AB_DATA_WRITABLE |
- GDT_AB_TYPE_DATA | GDT_AB_DPL_ZERO | GDT_AB_PRESENT;
- kernel_data_entry->flags = GDT_FLAG_LONG;
-
- for (int i = 0; i < 48; ++i) {
- uint64_t offset = (uint64_t)isrs[i];
- the_idt[i].offset_0 = offset & 0xffff;
- the_idt[i].segment_selector = 0x0028;
- the_idt[i].interrupt_stack_index = i < 32 ? 0x02 : 0x01;
- the_idt[i].type =
- IDT_TYPE_INTERRUPT | IDT_TYPE_DPL_ZERO | IDT_TYPE_PRESENT;
- the_idt[i].offset_16 = (offset >> 16) & 0xffff;
- the_idt[i].offset_32 = offset >> 32;
- }
-
- init_interrupts_asm(&the_gdt_descriptor, &the_idt_descriptor, 0x08);
-
-}
diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h
deleted file mode 100644
index 3a1d227..0000000
--- a/src/kernel/interrupts.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Calcite, src/kernel/interrupts.h
- * 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
- * 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
-
-//switches to kernel gdt and idt, maps and unmasks irqs
-void init_interrupts();
-
-//the handler should have normal C linkage
-void set_irq_handler(uint8_t irq_line, void (*handler)());
diff --git a/src/kernel/ipc-dgram.c b/src/kernel/ipc-dgram.c
deleted file mode 100644
index 74c64fe..0000000
--- a/src/kernel/ipc-dgram.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/* Calcite, src/kernel/ipc-dgram.c
- * 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#include "ipc-dgram.h"
-#include "process.h"
-#include "scheduler.h"
-#include "utility.h"
-#include "debug.h"
-#include "heap.h"
-
-#include
-
-struct ipc_dgram_address_book {
- const char *address;
- struct ipc_dgram_box box;
- struct ipc_dgram_address_book *prev;
-};
-
-static struct ipc_dgram_address_book *last = 0;
-
-#define INITIAL_SEND_BACKLOG 4
-
-struct ipc_dgram_box *get_ipc_dgram_box(const char *address) {
-
- for (struct ipc_dgram_address_book *book = last; book != 0; book = book->prev)
- if (strequ(book->address, address))
- return &book->box;
-
- struct ipc_dgram_address_book *new = heap_alloc(sizeof(struct ipc_dgram_address_book));
-
- int address_length = 0;
- while (address[address_length] != 0)
- ++address_length;
-
- char *new_address = heap_alloc(address_length + 1);
- memcpy(new_address, address, address_length);
- new_address[address_length] = 0;
- new->address = new_address;
-
- new->box.is_receiver_held = 0;
- new->box.is_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;
- new->box.buffer_read_available = 0;
-
- new->prev = last;
- last = new;
- return &new->box;
-
-}
-
-int get_ipc_dgram_size(struct ipc_dgram_box *box) {
-
- if (box->is_someone_waiting_to_receive)
- return -1;
-
- while (box->buffer_read_available == 0) {
- box->is_someone_waiting_to_receive = 1;
- yield(&box->waiting_to_receive);
- }
-
- assert(box->buffer_read_available >= 4)
- return *(int *)&box->buffer[box->buffer_read_pointer];
-
-}
-
-void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer) {
-
- assert(box->is_someone_waiting_to_receive == 0)
- assert(box->buffer_read_available >= 4)
-
- int size = *(int *)&box->buffer[box->buffer_read_pointer];
-
- box->buffer_read_available -= 4;
- box->buffer_read_pointer = (box->buffer_read_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH;
-
- int size_rounded_up = ((size - 1) / 4 + 1) * 4;
-
- assert(box->buffer_read_available >= size_rounded_up)
-
- int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_pointer;
- if (size < first_block)
- first_block = size;
-
- memcpy(buffer, &box->buffer[box->buffer_read_pointer], first_block);
- if (first_block != size)
- memcpy(buffer + first_block, box->buffer, size - first_block);
-
- box->buffer_read_available -= size_rounded_up;
- box->buffer_read_pointer = (box->buffer_read_pointer + size_rounded_up) % IPC_DGRAM_BOX_BUFFER_LENGTH;
-
- struct continuation_info ci;
- while (take_from_queue(&box->waiting_to_send, &ci))
- add_to_queue(&ready_continuations, &ci);
-
-}
-
-enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes) {
-
- assert(bytes > 0)
-
- int bytes_round_up = ((bytes - 1) / 4 + 1) * 4;
-
- if (4 + bytes_round_up > IPC_DGRAM_BOX_BUFFER_LENGTH)
- return IPR_TOO_BIG;
-
- while (IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_available < 4 + bytes_round_up)
- yield_to_queue(&box->waiting_to_send);
-
- int write_pointer = (box->buffer_read_pointer + box->buffer_read_available) % IPC_DGRAM_BOX_BUFFER_LENGTH;
- assert(write_pointer + 4 <= IPC_DGRAM_BOX_BUFFER_LENGTH)
-
- *(int *)&box->buffer[write_pointer] = bytes;
-
- write_pointer = (write_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH;
-
- int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - write_pointer;
- if (bytes < first_block)
- first_block = bytes;
-
- memcpy(&box->buffer[write_pointer], data, first_block);
- if (first_block != bytes)
- memcpy(box->buffer, data + first_block, bytes - first_block);
-
- box->buffer_read_available += 4 + bytes_round_up;
-
- if (box->is_someone_waiting_to_receive) {
- box->is_someone_waiting_to_receive = 0;
- add_to_queue(&ready_continuations, &box->waiting_to_receive);
- }
-
- return IPR_SUCCESS;
-
-}
-
-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
deleted file mode 100644
index fd399f8..0000000
--- a/src/kernel/ipc-dgram.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Calcite, src/kernel/ipc-dgram.h
- * 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include "scheduler.h"
-
-#include
-
-//must be a multiple of 4
-#define IPC_DGRAM_BOX_BUFFER_LENGTH 1024
-
-struct ipc_dgram_box {
-
- int is_receiver_held;
- int is_private;
- int private_sides;
-
- int is_someone_waiting_to_receive;
- struct continuation_info waiting_to_receive;
-
- struct continuation_queue waiting_to_send;
-
- int buffer_read_pointer;
- int buffer_read_available;
-
- //packets are of the form:
- // int size;
- // uint8_t packet[size];
- // uint8_t padding[size % 4 == 0 ? 0 : 4 - size % 4];
- uint8_t buffer[IPC_DGRAM_BOX_BUFFER_LENGTH];
-
-};
-
-struct ipc_dgram_box *get_ipc_dgram_box(const char *address);
-
-//waits until a packet is available, then returns the size of that packet.
-//returns -1 if someone is already waiting.
-int get_ipc_dgram_size(struct ipc_dgram_box *box);
-
-//should only be called just after get_ipc_dgram_size
-//(i.e. with no intervening continuation resumptions)
-void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer);
-
-//only returns IPR_SUCCESS or IPR_TOO_BIG
-enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes);
-
-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/iso9660.c b/src/kernel/iso9660.c
deleted file mode 100644
index b89c070..0000000
--- a/src/kernel/iso9660.c
+++ /dev/null
@@ -1,382 +0,0 @@
-/* Calcite, src/kernel/iso9660.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 "iso9660.h"
-#include "kernel-public/files.h"
-#include "utility.h"
-#include "drives.h"
-#include "debug.h"
-#include "heap.h"
-#include "fs.h"
-
-//relevant sources:
-// https://www.ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf
-
-//case-insensitive equality
-static bool ciequ(char c1, char c2) {
- if (c1 >= 'a' && c1 <= 'z')
- c1 &= 0xdf;
- if (c2 >= 'a' && c2 <= 'z')
- c2 &= 0xdf;
- return c1 == c2;
-}
-
-struct iso9660_driver_info {
- const void *path_table;
- //in bytes
- int path_table_length;
-};
-
-static const struct iso9660_driver_info *get_driver_info(const struct fs_info *info) {
- return (const struct iso9660_driver_info *)info->driver_info;
-}
-
-struct iso9660_node {
- uint32_t first_block;
- //in bytes
- uint32_t length;
-};
-
-static enum fs_access_result free_node_iso9660(const struct fs_info *info, void *node) {
- (void)info;
- heap_dealloc(node, sizeof(struct iso9660_node));
- return FAR_SUCCESS;
-}
-
-static uint8_t directory_buffer[2048];
-
-static enum fs_access_result look_up_in_directory(
- const struct fs_info *info, void **node_out,
- const char *path, int path_length,
- uint32_t first_directory_block) {
-
- if (first_directory_block >= info->drive->block_count)
- return FAR_FORMAT_ERROR;
-
- switch ((*info->drive->read_blocks)(info->drive, directory_buffer, first_directory_block, 1)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- return FAR_HARDWARE_ERROR;
- default:
- assert(0);
- }
-
- int length = *(uint32_t *)&directory_buffer[10];
- if (length % 2048 != 0)
- return FAR_FORMAT_ERROR;
-
- uint32_t loaded_block = first_directory_block;
- int offset_in_block = 0;
- int total_offset = 0;
-
- while (1) {
-
- if (total_offset == length)
- return FAR_NOT_FOUND;
-
- if (offset_in_block == 2048 || directory_buffer[offset_in_block] == 0) {
- ++loaded_block;
- if (loaded_block >= info->drive->block_count)
- return FAR_FORMAT_ERROR;
- switch ((*info->drive->read_blocks)(info->drive, directory_buffer, loaded_block, 1)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- return FAR_HARDWARE_ERROR;
- default:
- assert(0);
- }
- total_offset = total_offset - offset_in_block + 2048;
- offset_in_block = 0;
- if (total_offset == length)
- return FAR_NOT_FOUND;
- }
-
- int dr_length = directory_buffer[offset_in_block];
- if (offset_in_block + dr_length > 2048)
- return FAR_FORMAT_ERROR;
-
- int id_length = directory_buffer[offset_in_block + 32];
- if (33 + id_length > dr_length)
- return FAR_FORMAT_ERROR;
-
- for (int i = 0; i < id_length; ++i)
- if (directory_buffer[offset_in_block + 33 + i] == ';') {
- id_length = i;
- break;
- }
-
- if (id_length != path_length)
- goto next_entry;
-
- for (int i = 0; i < id_length; ++i)
- if (!ciequ(directory_buffer[offset_in_block + 33 + i], path[i]))
- goto next_entry;
-
- struct iso9660_node *node = heap_alloc(sizeof(struct iso9660_node));
- node->first_block = *(uint32_t *)&directory_buffer[offset_in_block + 2];
- node->length = *(uint32_t *)&directory_buffer[offset_in_block + 10];
-
- *node_out = node;
- return FAR_SUCCESS;
-
- next_entry:
- offset_in_block += dr_length;
- total_offset += dr_length;
-
- }
-
-}
-
-static enum fs_access_result look_up_recursive(
- const struct fs_info *info, void **node_out,
- const char *path, int parent_path_table_offset,
- int parent_directory_number) {
-
- //FIXME: this function is pretty unoptimized
-
-recurse:
-
- int slash_location = 0;
- int path_length;
- while (1) {
- if (path[slash_location] == '/')
- break;
- if (path[slash_location] == 0) {
- path_length = slash_location;
- slash_location = -1;
- break;
- }
- ++slash_location;
- }
-
- const struct iso9660_driver_info *driver_info = get_driver_info(info);
-
- int on_offset = parent_path_table_offset;
- int on_number = parent_directory_number;
-
- if (slash_location == -1) {
- if (on_offset + 6 > driver_info->path_table_length)
- return FAR_NOT_FOUND;
- uint32_t first_directory_block = *(uint32_t *)(driver_info->path_table + on_offset + 2);
- return look_up_in_directory(info, node_out, path, path_length, first_directory_block);
- }
-
- while (1) {
-
- if (on_offset >= driver_info->path_table_length)
- return FAR_NOT_FOUND;
-
- int old_id_length = *(uint8_t *)(driver_info->path_table + on_offset);
- on_offset += 8 + old_id_length + (old_id_length & 0x01);
- ++on_number;
-
- if (on_offset + 8 > driver_info->path_table_length)
- return FAR_NOT_FOUND;
-
- int this_entry_parent = *(uint16_t *)(driver_info->path_table + on_offset + 6);
- if (this_entry_parent != parent_directory_number)
- continue;
-
- int this_entry_id_length = *(uint8_t *)(driver_info->path_table + on_offset);
- if (this_entry_id_length != slash_location)
- continue;
-
- if (on_offset + 8 + this_entry_id_length > driver_info->path_table_length)
- return FAR_NOT_FOUND;
-
- char *this_entry_id = (char *)(driver_info->path_table + on_offset + 8);
- for (int i = 0; i < slash_location; ++i)
- if (!ciequ(this_entry_id[i], path[i]))
- goto next_entry;
-
- path = path + slash_location + 1;
- parent_path_table_offset = on_offset;
- parent_directory_number = on_number;
- goto recurse;
-
- next_entry:
- ;
-
- }
-
-}
-
-static enum fs_access_result look_up_file_iso9660(
- const struct fs_info *info, void **node_out, const char *path) {
-
- return look_up_recursive(info, node_out, path, 0, 1);
-
-}
-
-static enum fs_access_result stat_file_iso9660(
- const struct fs_info *info, void *node, struct fs_stat *stat_out) {
-
- (void)info;
- struct iso9660_node *iso9660_node = (struct iso9660_node *)node;
- stat_out->bytes = iso9660_node->length;
- return FAR_SUCCESS;
-
-}
-
-static uint8_t read_buffer[2048];
-
-static enum fs_access_result read_file_iso9660(
- const struct fs_info *info, void *node, void *buffer, uint64_t start, uint64_t bytes) {
-
- struct iso9660_node *iso9660_node = (struct iso9660_node *)node;
- if (iso9660_node->first_block + (start + bytes - 1) / 2048 + 1 > info->drive->block_count)
- return FAR_FORMAT_ERROR;
-
- if (start % 2048 != 0) {
-
- switch ((*info->drive->read_blocks)(
- info->drive, read_buffer, iso9660_node->first_block + start / 2048, 1)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- return FAR_HARDWARE_ERROR;
- default:
- assert(0);
- }
-
- if (start + bytes <= ((start - 1) / 2048 + 1) * 2048) {
- memcpy(buffer, read_buffer + (start % 2048), bytes);
- return FAR_SUCCESS;
- }
-
- int to_copy = 2048 - (start % 2048);
- memcpy(buffer, read_buffer + (start % 2048), to_copy);
- buffer += to_copy;
- start += to_copy;
- bytes -= to_copy;
-
- }
-
- switch ((*info->drive->read_blocks)(
- info->drive, buffer,
- iso9660_node->first_block + start / 2048,
- bytes / 2048)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- return FAR_HARDWARE_ERROR;
- default:
- assert(0);
- }
-
- buffer += (bytes / 2048) * 2048;
- start += (bytes / 2048) * 2048;
- bytes %= 2048;
-
- if (bytes > 0) {
-
- switch ((*info->drive->read_blocks)(
- info->drive, read_buffer, iso9660_node->first_block + start / 2048, 1)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- return FAR_HARDWARE_ERROR;
- default:
- assert(0);
- }
-
- memcpy(buffer, read_buffer + (start % 2048), bytes);
-
- }
-
- return FAR_SUCCESS;
-
-}
-
-static uint8_t vd_buffer[2048];
-
-enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct fs_info *fs_out) {
-
- if (drive->block_size != 2048)
- panic("TODO")
-
- int descriptor_number = 0;
- while (1) {
-
- if ((uint64_t)(16 + descriptor_number) >= drive->block_count)
- return FAR_FORMAT_ERROR;
-
- switch ((*drive->read_blocks)(drive, vd_buffer, 16 + descriptor_number, 1)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- return FAR_HARDWARE_ERROR;
- default:
- assert(0)
- }
-
- if (vd_buffer[0] == 0xff)
- return FAR_FORMAT_ERROR;
-
- if (vd_buffer[0] == 0x01)
- break;
-
- ++descriptor_number;
-
- }
-
- uint32_t path_table_length = *(uint32_t *)&vd_buffer[132];
- uint32_t path_table_start = *(uint32_t *)&vd_buffer[140];
-
- uint64_t path_table_length_rounded_up = ((path_table_length - 1) / 2048 + 1) * 2048;
- void *path_table = heap_alloc(path_table_length_rounded_up);
-
- if (path_table_start >= drive->block_count)
- return FAR_FORMAT_ERROR;
-
- switch ((*drive->read_blocks)(
- drive, path_table, path_table_start,
- path_table_length_rounded_up / 2048)) {
- case DAR_SUCCESS:
- break;
- case DAR_HARDWARE_ERROR:
- heap_dealloc(path_table, path_table_length_rounded_up);
- return FAR_HARDWARE_ERROR;
- default:
- assert(0);
- }
-
- heap_dealloc(
- path_table + path_table_length,
- path_table_length_rounded_up - path_table_length);
-
- struct iso9660_driver_info *driver_info = heap_alloc(sizeof(struct iso9660_driver_info));
- driver_info->path_table = path_table;
- driver_info->path_table_length = path_table_length;
-
- fs_out->drive = drive;
- fs_out->driver_info = driver_info;
- fs_out->free_node = &free_node_iso9660;
- fs_out->look_up_file = &look_up_file_iso9660;
- fs_out->stat_file = &stat_file_iso9660;
- fs_out->read_file = &read_file_iso9660;
-
- debug_log("created iso9660 file system")
- debug_log(" drive name %s", drive->name)
- debug_log(" path table start block %d", path_table_start)
- debug_log(" path table block count %d", path_table_length_rounded_up / 2048)
-
- return FAR_SUCCESS;
-
-}
diff --git a/src/kernel/iso9660.h b/src/kernel/iso9660.h
deleted file mode 100644
index be3f5c7..0000000
--- a/src/kernel/iso9660.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Calcite, src/kernel/iso9660.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include "fs.h"
-
-enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct fs_info *fs_out);
diff --git a/src/kernel/paging.asm b/src/kernel/paging.asm
deleted file mode 100644
index 2d5dd6e..0000000
--- a/src/kernel/paging.asm
+++ /dev/null
@@ -1,49 +0,0 @@
- ; Calcite, src/kernel/paging.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-;both defined in paging.c
-extern kernel_p4_physical_address
-extern init_stack
-
-init_stack_length equ 16384
-
-section .text
-
-;referenced in paging.c
-extern invlpg
-invlpg:
- invlpg [rdi]
- ret
-
-extern switch_to_kernel_page_tables
-switch_to_kernel_page_tables:
-
- ;switch the page table
- mov rax, qword [kernel_p4_physical_address]
- mov cr3, rax
-
- ;set the stack
- mov rsp, init_stack + init_stack_length
-
- ;push 0 before jumping to continuation so that if the continuation
- ;does return (which it shouldn't), it returns to null instead of
- ;popping garbage and returning there
- push qword 0
- jmp rdi
diff --git a/src/kernel/paging.c b/src/kernel/paging.c
deleted file mode 100644
index e16deeb..0000000
--- a/src/kernel/paging.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/* Calcite, src/kernel/paging.c
- * 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
- * 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 "paging.h"
-#include "debug.h"
-
-#define MAX_PHYSICAL_GB 64ULL
-
-//this is a simple bitmap. physical_map[i / 8] & (1 << (i % 8)) is nonzero if
-//and only if the physical page with base i * 4096 is free.
-static uint8_t physical_map[MAX_PHYSICAL_GB << 15];
-
-//kernel virtual memory is the top gigabyte, i.e. one "page directory" worth
-//of pages. in the future i might make it more reactive, and allow new page
-//directories to be allocated as needed, but for now there is just the one
-//page directory, and all of the page tables in that directory are statically
-//allocated. i do not like the intel/amd names for the paging structures, so
-//i will use these names throughout the rest of this file:
-// p4 = "page map level 4"
-// p3 = "page directory pointer table"
-// p2 = "page directory"
-// p1 = "page table"
-
-//referenced from paging.asm
-uint64_t kernel_p4_physical_address;
-
-uint64_t kernel_p3_physical_address;
-
-alignas(4096) static uint64_t kernel_p4[512];
-alignas(4096) static uint64_t kernel_p3[512];
-alignas(4096) static uint64_t kernel_p2[512];
-alignas(4096) static uint64_t kernel_p1s[512 * 512];
-
-//referenced from paging.asm. at the end of init, this will either be marked
-//free or reused as an interrupt / syscall stack. if the length of this is
-//changed, it also needs to be changed in paging.asm. there is no guard page,
-//but i don't expect init to need much stack.
-alignas(4096) uint64_t init_stack[16384];
-
-void init_paging(uint64_t kernel_physical_base, void *kernel_virtual_base) {
-
- uint64_t kernel_load_offset =
- (uint64_t)kernel_virtual_base - kernel_physical_base;
-
- kernel_p4_physical_address = (uint64_t)kernel_p4 - kernel_load_offset;
- kernel_p3_physical_address = (uint64_t)kernel_p3 - kernel_load_offset;
-
- kernel_p4[511] = ((uint64_t)kernel_p3 - kernel_load_offset) | 0x3;
- for (int i = 0; i < 511; ++i)
- kernel_p4[i] = 0;
-
- kernel_p3[511] = ((uint64_t)kernel_p2 - kernel_load_offset) | 0x3;
- for (int i = 0; i < 511; ++i)
- kernel_p3[i] = 0;
-
- for (int i = 0; i < 512; ++i)
- kernel_p2[i] =
- ((uint64_t)kernel_p1s - kernel_load_offset + i * 4096) | 0x03;
-
- for (int i = 0; i < 512 * 512; ++i)
- kernel_p1s[i] = 0;
-
-}
-
-void mark_physical_memory_free(uint64_t base, uint64_t length) {
-
- if (base + length > MAX_PHYSICAL_GB << 30)
- length = (MAX_PHYSICAL_GB << 30) - base;
-
- while (length && (base & (0x7 << 12))) {
- physical_map[base >> 15] |= 1 << ((base >> 12) % 8);
- base += 4096;
- length -= 4096;
- }
-
- while (length >= 4096 * 8) {
- physical_map[base >> 15] = 0xff;
- base += 4096 * 8;
- length -= 4096 * 8;
- }
-
- while (length) {
- physical_map[base >> 15] |= 1 << ((base >> 12) % 8);
- base += 4096;
- length -= 4096;
- }
-
-}
-
-//defined in paging.asm
-void invlpg(void *address);
-
-void map_in_kernel_page_table(uint64_t physical_base, void *virtual_base, int writable, int executable) {
-
- uint64_t virtual_base_u64 = (uint64_t)virtual_base;
- assert(virtual_base_u64 >= 0xffffffffc0000000);
-
- uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
- assert(kernel_p1s[p1s_index] == 0);
-
- kernel_p1s[p1s_index] =
- physical_base | (writable ? 0x3 : 0x1) |
- (executable ? 0 : 0x8000000000000000);
-
- invlpg(virtual_base);
-
-}
-
-void create_kernel_guard_page(void *virtual_base) {
-
- uint64_t virtual_base_u64 = (uint64_t)virtual_base;
- assert(virtual_base_u64 >= 0xffffffffc0000000);
-
- uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
- assert(kernel_p1s[p1s_index] == 0);
-
- kernel_p1s[p1s_index] = 0x2;
-
-}
-
-void unmap_kernel_page(void *virtual_base) {
-
- uint64_t virtual_base_u64 = (uint64_t)virtual_base;
- assert(virtual_base_u64 >= 0xffffffffc0000000);
-
- uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
- assert(kernel_p1s[p1s_index] != 0);
-
- kernel_p1s[p1s_index] = 0;
-
- invlpg(virtual_base);
-
-}
-
-void unmap_and_free_kernel_page(void *virtual_base) {
-
- uint64_t virtual_base_u64 = (uint64_t)virtual_base;
- assert(virtual_base_u64 >= 0xffffffffc0000000);
-
- uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
- assert(kernel_p1s[p1s_index] != 0);
-
- uint64_t pma = kernel_p1s[p1s_index] & 0x7ffffffffffff000;
- mark_physical_memory_free(pma, 4096);
-
- kernel_p1s[p1s_index] = 0;
-
- invlpg(virtual_base);
-
-}
-
-void *find_free_kernel_region(uint64_t length) {
-
- length /= 4096;
-
- uint64_t next = 0;
-
- uint64_t run_start = 0;
- uint64_t run_length = 0;
-
- while (1) {
- if (run_length == length)
- return (void *)(run_start * 4096 + 0xffffffffc0000000);
- if (next == 512 * 512)
- panic("out of kernel virtual memory");
- if (kernel_p1s[next] == 0)
- ++run_length;
- else {
- run_length = 0;
- run_start = next + 1;
- }
- ++next;
- }
-
-}
-
-uint64_t take_free_physical_page() {
- for (uint64_t i = 0; i < (MAX_PHYSICAL_GB << 15); ++i)
- for (int j = 0; j < 8; ++j)
- if (physical_map[i] & (1 << j)) {
- physical_map[i] &= ~(1 << j);
- return (i << 15) + (j << 12);
- }
- panic("out of physical memory");
-}
-
-#define SYSCALL_STACK_BYTES (1 << 15)
-
-void *create_syscall_stack() {
- void *vma = find_free_kernel_region(SYSCALL_STACK_BYTES);
- for (uint64_t i = 0; i < SYSCALL_STACK_BYTES; i += 4096) {
- uint64_t pma = take_free_physical_page();
- map_in_kernel_page_table(pma, vma + i * 4096, 1, 0);
- }
- return vma + SYSCALL_STACK_BYTES;
-}
-
-void destroy_syscall_stack(void *stack_top) {
- for (uint64_t i = 0; i < SYSCALL_STACK_BYTES; i += 4096)
- unmap_and_free_kernel_page(stack_top - SYSCALL_STACK_BYTES + i * 4096);
-}
-
-uint64_t count_free_pram() {
- uint64_t total = 0;
- for (uint64_t i = 0; i < (MAX_PHYSICAL_GB << 15); ++i)
- for (int j = 0; j < 8; ++j)
- if (physical_map[i] & (1 << j))
- total += 4096;
- return total;
-}
-
-uint64_t count_free_kernel_vram() {
- uint64_t total = 0;
- for (uint64_t i = 0; i < 512 * 512; ++i)
- if (kernel_p1s[i] == 0)
- total += 4096;
- return total;
-}
diff --git a/src/kernel/paging.h b/src/kernel/paging.h
deleted file mode 100644
index 60f0ca9..0000000
--- a/src/kernel/paging.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Calcite, src/kernel/paging.h
- * 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
- * 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
-
-extern uint64_t kernel_p3_physical_address;
-
-//kernel physical and virtual bases are passed so that we can compute the
-//physical addresses of the kernel's statically allocated paging structures.
-void init_paging(uint64_t kernel_physical_base, void *kernel_virtual_base);
-
-//base and length should be page-aligned
-void mark_physical_memory_free(uint64_t base, uint64_t length);
-
-//maps one page. physical and virtual bases should be page-aligned.
-//virtual address should be within kernel range.
-void map_in_kernel_page_table(
- uint64_t physical_base, void *virtual_base, int writable, int executable);
-
-//virtual base should be page-aligned and within kernel range.
-void create_kernel_guard_page(void *virtual_base);
-
-//unmaps one page. base should be page-aligned and within kernel range.
-void unmap_kernel_page(void *virtual_base);
-
-//unmaps one page, and frees the corresponding physical page.
-//base should be page-aligned and within kernel range.
-void unmap_and_free_kernel_page(void *virtual_base);
-
-//returns a region of contiguous pages in kernel virtual memory where nothing
-//is mapped. length should be page-aligned.
-void *find_free_kernel_region(uint64_t length);
-
-uint64_t take_free_physical_page();
-
-//implemented in paging.asm. the continuation should be noreturn.
-[[noreturn]] void switch_to_kernel_page_tables(void (*continuation)());
-
-//returns the top
-void *create_syscall_stack();
-void destroy_syscall_stack(void *stack_top);
-
-//return value in bytes
-uint64_t count_free_pram();
-
-//return value in bytes
-uint64_t count_free_kernel_vram();
diff --git a/src/kernel/pata.c b/src/kernel/pata.c
deleted file mode 100644
index 0f34efa..0000000
--- a/src/kernel/pata.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/* Calcite, src/kernel/pata.c
- * 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
- * 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 "utility.h"
-#include "drives.h"
-#include "debug.h"
-#include "heap.h"
-#include "pata.h"
-#include "pci.h"
-
-//some relevant sources:
-// https://www.isdaman.com/alsos/hardware/hdc/pciide.pdf
-// ANSI: AT Attachment 8 - ATA/ATAPI Command Set
-// Seagate: SCSI Commands Reference Manual
-// OSDev Wiki: ATAPI, PCI IDE Controller
-
-enum pata_result {
- PR_SUCCESS,
- PR_NO_CONTROLLER,
- PR_ABORTED
-};
-
-enum pata_result wait_pio(uint16_t command_block_base) {
- while (1) {
- uint8_t status = inb(command_block_base + 7);
- //0x00 happens in qemu, 0x7f happens in virtualbox
- if (status == 0x00 || status == 0x7f)
- return PR_NO_CONTROLLER;
- if (status & 0x01) {
- uint8_t error = inb(command_block_base + 1);
- if (error == 0x04)
- return PR_ABORTED;
- panic("TODO")
- }
- if ((status & 0x80) || !(status & 0x08))
- continue;
- return PR_SUCCESS;
- }
-}
-
-//buffer should have room for 256 words.
-static enum pata_result
- pata_identify_packet_device(uint16_t command_block_base, uint8_t device_byte, uint16_t *buffer) {
-
- outb(command_block_base + 6, device_byte);
- outb(command_block_base + 7, 0xa1);
-
- enum pata_result result = wait_pio(command_block_base);
- if (result != PR_SUCCESS)
- return result;
-
- insw(command_block_base, buffer, 256);
- return PR_SUCCESS;
-
-}
-
-static enum pata_result patapi_read_capacity(
- uint16_t command_block_base, uint8_t device_byte, uint32_t *max_block_out, uint32_t *block_size_out) {
-
- outb(command_block_base + 6, device_byte);
- outb(command_block_base + 1, 0);
- outb(command_block_base + 3, 0);
- outb(command_block_base + 4, 8);
- outb(command_block_base + 5, 0);
- outb(command_block_base + 7, 0xa0);
-
- enum pata_result result = wait_pio(command_block_base);
- if (result != PR_SUCCESS)
- return result;
-
- uint8_t cmd[12] = {
- 0x25, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0 };
- outsw(command_block_base, cmd, 6);
-
- result = wait_pio(command_block_base);
- if (result != PR_SUCCESS)
- return result;
-
- uint32_t actual_response_size =
- inb(command_block_base + 4) | (inb(command_block_base + 5) << 8);
- if (actual_response_size != 8)
- panic("TODO")
-
- uint32_t response[2];
- insw(command_block_base, response, 4);
-
- *max_block_out = end_swap_u32(response[0]);
- *block_size_out = end_swap_u32(response[1]);
- return PR_SUCCESS;
-
-}
-
-static enum pata_result patapi_read(
- uint16_t command_block_base, uint8_t device_byte, uint16_t block_size,
- uint32_t start_block, uint32_t block_count, void *buffer) {
-
- outb(command_block_base + 6, device_byte);
- outb(command_block_base + 1, 0);
- outb(command_block_base + 3, 0);
- outb(command_block_base + 4, block_size & 0xff);
- outb(command_block_base + 5, block_size >> 8);
- outb(command_block_base + 7, 0xa0);
-
- enum pata_result result = wait_pio(command_block_base);
- if (result != PR_SUCCESS)
- return result;
-
- uint8_t cmd[12] = {
- 0xa8, 0,
- start_block >> 24,
- (start_block >> 16) & 0xff,
- (start_block >> 8) & 0xff,
- start_block & 0xff,
- block_count >> 24,
- (block_count >> 16) & 0xff,
- (block_count >> 8) & 0xff,
- block_count & 0xff,
- 0, 0 };
- outsw(command_block_base, cmd, 6);
-
- for (uint32_t i = 0; i < block_count; ++i) {
-
- result = wait_pio(command_block_base);
- if (result != PR_SUCCESS)
- return result;
-
- uint32_t actual_response_size =
- inb(command_block_base + 4) | (inb(command_block_base + 5) << 8);
-
- if (actual_response_size != block_size)
- panic("TODO")
-
- insw(command_block_base, buffer + i * block_size, block_size / 2);
-
- }
-
- return PR_SUCCESS;
-
-}
-
-static uint16_t ipd_buffer[256];
-
-struct pata_driver_info {
- uint16_t command_block_base;
- uint8_t device_byte;
-};
-
-static enum drive_access_result read_blocks_patapi(
- const struct drive_info *drive_info,
- void *buffer, uint64_t start, uint64_t count) {
-
- if (count >= (1ULL << 32) || start >= (1ULL << 32))
- panic("TODO")
-
- const struct pata_driver_info *driver_info = drive_info->driver_info;
-
- return
- patapi_read(
- driver_info->command_block_base, driver_info->device_byte,
- drive_info->block_size, start, count, buffer) == PR_SUCCESS
- ? DAR_SUCCESS : DAR_HARDWARE_ERROR;
-
-}
-
-int next_pata_drive_number = 0;
-
-static void probe_pata_drive(uint16_t command_block_base, uint8_t device_byte) {
-
- if (pata_identify_packet_device(command_block_base, device_byte, ipd_buffer) != PR_SUCCESS)
- return;
-
- uint32_t max_block, block_size;
- if (patapi_read_capacity(
- command_block_base, device_byte, &max_block, &block_size) != PR_SUCCESS)
- return;
-
- if (next_pata_drive_number > 9)
- panic("TODO")
-
- struct drive_info *di = add_drive();
-
- char *name = heap_alloc(6);
- memcpy(name, "pata", 4);
- name[4] = '0' + next_pata_drive_number++;
- name[5] = 0;
- di->name = name;
-
- di->block_size = block_size;
- di->block_count = max_block + 1;
-
- struct pata_driver_info *driver_info =
- heap_alloc(sizeof(struct pata_driver_info));
- driver_info->command_block_base = command_block_base;
- driver_info->device_byte = device_byte;
- di->driver_info = driver_info;
-
- di->read_blocks = &read_blocks_patapi;
-
- debug_log("added pata drive")
- debug_log(" drive name %s", di->name)
- debug_log(" command block base 0x%h", command_block_base, 4)
- debug_log(" device byte 0x%h", device_byte, 2)
- debug_log(" block size %d", di->block_size)
- debug_log(" block count %d", di->block_count)
- debug_log(" total size %B", di->block_count * di->block_size)
-
-}
-
-void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc) {
-
- if (pci_class_etc & 0x00000100) {
- uint32_t bar = read_pci_config(pci_address_base + 0x10);
- if ((bar & 0xffff0003) != 0x00000001)
- panic("TODO")
- probe_pata_drive(bar & 0xfffc, 0x00);
- probe_pata_drive(bar & 0xfffc, 0x10);
- }
- else {
- probe_pata_drive(0x01f0, 0x00);
- probe_pata_drive(0x01f0, 0x10);
- }
-
- if (pci_class_etc & 0x00000400) {
- uint32_t bar = read_pci_config(pci_address_base + 0x18);
- if ((bar & 0xffff0003) != 0x00000001)
- panic("TODO")
- probe_pata_drive(bar & 0xfffc, 0x00);
- probe_pata_drive(bar & 0xfffc, 0x10);
- }
- else {
- probe_pata_drive(0x0170, 0x00);
- probe_pata_drive(0x0170, 0x10);
- }
-
-}
diff --git a/src/kernel/pata.h b/src/kernel/pata.h
deleted file mode 100644
index 908e3ae..0000000
--- a/src/kernel/pata.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Calcite, src/kernel/pata.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-//probes for pata drives on this "card" and adds any that exist to drives.h
-void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc);
diff --git a/src/kernel/pci.asm b/src/kernel/pci.asm
deleted file mode 100644
index 5f3c739..0000000
--- a/src/kernel/pci.asm
+++ /dev/null
@@ -1,40 +0,0 @@
- ; Calcite, src/kernel/pci.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .text
-
-global read_pci_config
-read_pci_config:
- mov dx, 0x0cf8
- mov eax, edi
- out dx, eax
- mov dx, 0x0cfc
- in eax, dx
- ret
-
-global write_pci_config
-write_pci_config:
- mov dx, 0x0cf8
- mov eax, edi
- out dx, eax
- mov dx, 0x0cfc
- mov eax, esi
- out dx, eax
- ret
diff --git a/src/kernel/pci.c b/src/kernel/pci.c
deleted file mode 100644
index ac705f5..0000000
--- a/src/kernel/pci.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Calcite, src/kernel/pci.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 "debug.h"
-#include "pata.h"
-#include "pci.h"
-
-#include
-
-static void probe_bus(uint32_t bus_address_base) {
-
- for (uint32_t device_address_base = bus_address_base;
- device_address_base < bus_address_base + 0x00010000;
- device_address_base += 0x00000800)
- for (uint32_t function_address_base = device_address_base;
- function_address_base < device_address_base + 0x00000800;
- function_address_base += 0x00000100) {
-
- uint8_t header_type = (read_pci_config(function_address_base | 0x0c) >> 16) & 0xff;
- if (header_type == 0xff)
- break;
-
- if ((header_type & 0x7f) == 0x01)
- //this is a pci-to-pci bridge
- probe_bus((read_pci_config(function_address_base | 0x18) >> 8) & 0xff);
-
- else if ((header_type & 0x7f) == 0x00) {
- //this is a normal function
- uint32_t class_etc = read_pci_config(function_address_base | 0x08);
- debug_log("pci device with class %h:%h", class_etc >> 24, 2, class_etc >> 16, 2);
- switch (class_etc & 0xffff0000) {
- case 0x01010000:
- probe_pata_drives(function_address_base, class_etc);
- break;
- }
- }
-
- if (!(header_type & 0x80))
- break;
-
- }
-
-}
-
-void probe_pci() {
- probe_bus(0x80000000);
-}
diff --git a/src/kernel/pci.h b/src/kernel/pci.h
deleted file mode 100644
index dc1ddc1..0000000
--- a/src/kernel/pci.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Calcite, src/kernel/pci.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-//reads one dword from config space.
-//address is 0x8000000 | (bus << 16) | (device << 11) | (function << 8) | (bytes into that function's space).
-//bytes into that function's space must be dword-aligned.
-uint32_t read_pci_config(uint32_t address);
-
-//writes one dword to config space.
-//address is 0x8000000 | (bus << 16) | (device << 11) | (function << 8) | (bytes into that function's space).
-//bytes into that function's space must be dword-aligned.
-void write_pci_config(uint32_t address, uint32_t value);
-
-void probe_pci();
diff --git a/src/kernel/process.asm b/src/kernel/process.asm
deleted file mode 100644
index d3c6bcf..0000000
--- a/src/kernel/process.asm
+++ /dev/null
@@ -1,55 +0,0 @@
- ; Calcite, src/kernel/process.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .bss
-
- resb 16384
-temporary_stack:
-
-section .text
-
-;referenced in process.c
-global thread_start
-thread_start:
- mov cr3, rbp
- mov rcx, r12
- mov r11, 0x200
- mov rdi, r13
-
- xor rax, rax
- xor rbx, rbx
- xor rdx, rdx
- xor rsi, rsi
- xor rbp, rbp
- xor r8, r8
- xor r9, r9
- xor r10, r10
- xor r12, r12
- xor r13, r13
- xor r14, r14
- xor r15, r15
-
- o64 sysret
-
-;referenced in process.c
-global with_temporary_stack
-with_temporary_stack:
- mov rsp, temporary_stack
- jmp rdi
diff --git a/src/kernel/process.c b/src/kernel/process.c
deleted file mode 100644
index 10df5a1..0000000
--- a/src/kernel/process.c
+++ /dev/null
@@ -1,1098 +0,0 @@
-/* Calcite, src/kernel/process.c
- * 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
- * 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 "framebuffer.h"
-#include "ipc-dgram.h"
-#include "scheduler.h"
-#include "process.h"
-#include "utility.h"
-#include "paging.h"
-#include "debug.h"
-#include "heap.h"
-#include "fs.h"
-
-#include
-#include
-#include
-#include
-
-void create_process(struct process *process_out) {
-
- process_out->p4_physical_base = take_free_physical_page();
- process_out->p4_virtual_base = find_free_kernel_region(4096);
- map_in_kernel_page_table(
- process_out->p4_physical_base,
- process_out->p4_virtual_base,
- 1, 0);
-
- process_out->p3_physical_base = take_free_physical_page();
- process_out->p3_virtual_base = find_free_kernel_region(4096);
- map_in_kernel_page_table(
- process_out->p3_physical_base,
- process_out->p3_virtual_base,
- 1, 0);
-
- process_out->p4_virtual_base[0] = process_out->p3_physical_base | 0x7;
- process_out->p4_virtual_base[511] = kernel_p3_physical_address | 0x3;
- for (int i = 1; i < 511; ++i)
- process_out->p4_virtual_base[i] = 0;
-
- for (int i = 0; i < 512; ++i) {
- process_out->p3_virtual_base[i] = 0;
- process_out->p2_virtual_bases[i] = 0;
- process_out->p1_virtual_bases[i] = 0;
- process_out->owned_pages_bitmaps[i] = 0;
- }
-
- process_out->n_threads = 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));
-
-}
-
-static void get_page_table_entry(
- struct process *process, void *virtual_base, int *p1i_out, int *p2i_out, int *p3i_out) {
-
- uint64_t vma = (uint64_t)virtual_base;
- assert(vma % 4096 == 0)
- assert(vma != 0)
- assert(vma < 0x0000008000000000)
-
- int p1i = (vma >> 12) & 0x1ff;
- int p2i = (vma >> 21) & 0x1ff;
- int p3i = (vma >> 30) & 0x1ff;
-
- if (process->p3_virtual_base[p3i] == 0) {
- uint64_t p2_pma = take_free_physical_page();
- uint64_t *p2_vma = find_free_kernel_region(4096);
- map_in_kernel_page_table(p2_pma, p2_vma, 1, 0);
- process->p3_virtual_base[p3i] = p2_pma | 0x7;
- process->p2_virtual_bases[p3i] = p2_vma;
- process->p1_virtual_bases[p3i] = heap_alloc(4096);
- process->owned_pages_bitmaps[p3i] = heap_alloc(4096);
- for (int i = 0; i < 512; ++i) {
- p2_vma[i] = 0;
- process->p1_virtual_bases[p3i][i] = 0;
- process->owned_pages_bitmaps[p3i][i] = 0;
- }
- }
-
- if (process->p2_virtual_bases[p3i][p2i] == 0) {
- uint64_t p1_pma = take_free_physical_page();
- uint64_t *p1_vma = find_free_kernel_region(4096);
- map_in_kernel_page_table(p1_pma, p1_vma, 1, 0);
- process->p2_virtual_bases[p3i][p2i] = p1_pma | 0x7;
- process->p1_virtual_bases[p3i][p2i] = p1_vma;
- process->owned_pages_bitmaps[p3i][p2i] = heap_alloc(64);
- for (int i = 0; i < 512; ++i)
- p1_vma[i] = 0;
- for (int i = 0; i < 64; ++i)
- process->owned_pages_bitmaps[p3i][p2i][i] = 0;
- }
-
- *p1i_out = p1i;
- *p2i_out = p2i;
- *p3i_out = p3i;
-
-}
-
-void map_page_for_process(
- struct process *process, uint64_t physical_base,
- void *virtual_base, int writable, int executable, int owned) {
-
- assert(physical_base % 4096 == 0)
-
- int p1i, p2i, p3i;
- get_page_table_entry(process, virtual_base, &p1i, &p2i, &p3i);
-
- assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0)
-
- process->p1_virtual_bases[p3i][p2i][p1i] =
- physical_base | 0x5 | (writable ? 0x2 : 0x0) |
- (executable ? 0 : 0x8000000000000000);
-
- if (owned)
- process->owned_pages_bitmaps[p3i][p2i][p1i / 8] |= 1 << (p1i % 8);
-
-}
-
-void syscall_change_page_permissions(void *vma, int writable, int executable) {
-
- assert(running_thread != 0)
-
- if (((uint64_t)vma & 0xfff) != 0 || (uint64_t)vma >= 0x0000008000000000 || vma == 0 ||
- writable < 0 || writable > 1 || executable < 0 || executable > 1)
- syscall_illegal_args();
-
- int p1i, p2i, p3i;
- get_page_table_entry(
- running_thread->process, vma, &p1i, &p2i, &p3i);
-
- uint64_t *entry = &running_thread->process->p1_virtual_bases[p3i][p2i][p1i];
-
- if (!(*entry & 0x1))
- syscall_illegal_args();
-
- *entry =
- (*entry & 0x7ffffffffffffffd) |
- (executable ? 0 : 0x8000000000000000) |
- (writable ? 0x2 : 0x0);
-
-}
-
-void create_guard_page_for_process(struct process *process, void *virtual_base) {
-
- int p1i, p2i, p3i;
- get_page_table_entry(process, virtual_base, &p1i, &p2i, &p3i);
-
- assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0)
-
- process->p1_virtual_bases[p3i][p2i][p1i] = 0x6;
-
-}
-
-void unmap_page_for_process(
- struct process *process, void *virtual_base) {
-
- uint64_t vma = (uint64_t)virtual_base;
- assert(vma % 4096 == 0)
- assert(vma != 0)
- assert(vma < 0x0000008000000000)
-
- int p1i = (vma >> 12) & 0x1ff;
- int p2i = (vma >> 21) & 0x1ff;
- int p3i = (vma >> 30) & 0x1ff;
-
- assert(
- process->p1_virtual_bases[p3i] &&
- process->p1_virtual_bases[p3i][p2i] &&
- process->p1_virtual_bases[p3i][p2i][p1i])
-
- if (process->owned_pages_bitmaps[p3i][p2i][p1i / 8] & (1 << (p1i % 8))) {
- process->owned_pages_bitmaps[p3i][p2i][p1i / 8] &= ~(1 << (p1i % 8));
- uint64_t pma = process->p1_virtual_bases[p3i][p2i][p1i] & 0x7ffffffffffff000;
- mark_physical_memory_free(pma, 4096);
- }
-
- process->p1_virtual_bases[p3i][p2i][p1i] = 0;
-
-}
-
-static int is_mapped_with_flags(struct process *process, const void *start, uint64_t length, uint64_t flags) {
-
- uint64_t vma_start = (uint64_t)start;
- uint64_t vma_end = vma_start + length;
- vma_start = (vma_start / 4096) * 4096;
- vma_end = ((vma_end - 1) / 4096 + 1) * 4096;
-
- for (uint64_t vma = vma_start; vma < vma_end; vma += 4096) {
-
- if (vma == 0 || vma >= 0x0000008000000000)
- return 0;
-
- int p1i = (vma >> 12) & 0x1ff;
- int p2i = (vma >> 21) & 0x1ff;
- int p3i = (vma >> 30) & 0x1ff;
-
- if (!process->p1_virtual_bases[p3i] ||
- !process->p1_virtual_bases[p3i][p2i] ||
- !process->p1_virtual_bases[p3i][p2i][p1i] ||
- (process->p1_virtual_bases[p3i][p2i][p1i] & flags) != flags)
- return 0;
-
- }
-
- return 1;
-
-}
-
-int is_mapped_writable(struct process *process, const void *start, uint64_t length) {
- return is_mapped_with_flags(process, start, length, 0x7);
-}
-
-int is_mapped_readable(struct process *process, const void *start, uint64_t length) {
- return is_mapped_with_flags(process, start, length, 0x5);
-}
-
-int is_mapped_readable_string(struct process *process, const char *start) {
-
- if (!is_mapped_readable(process, start, 1))
- return 0;
-
- while (1) {
- if ((uint64_t)start % 4096 == 0)
- if (!is_mapped_readable(process, start, 1))
- return 0;
- if (*start == 0)
- return 1;
- ++start;
- }
-
-}
-
-void *find_free_process_region(
- struct process *process, uint64_t page_count) {
-
- uint64_t start = 512 * 512 * 4096;
- uint64_t length = 0;
-
- while (1) {
-
- if (length >= page_count * 4096)
- break;
-
- uint64_t vma = start + length;
- if (vma >= 0x0000008000000000)
- goto no_mem;
-
- int p1i = (vma >> 12) & 0x1ff;
- int p2i = (vma >> 21) & 0x1ff;
- int p3i = (vma >> 30) & 0x1ff;
-
- if (p2i == 0 && p1i == 0 && process->p2_virtual_bases[p3i] == 0) {
- length += 512 * 512 * 4096;
- continue;
- }
-
- if (p1i == 0 && process->p1_virtual_bases[p3i][p2i] == 0) {
- length += 512 * 4096;
- continue;
- }
-
- if (process->p1_virtual_bases[p3i][p2i][p1i] == 0) {
- length += 4096;
- continue;
- }
-
- start = start + length + 4096;
- length = 0;
-
- }
-
- return (void *)start;
-
-no_mem:
- panic("process out of virtual memory")
-
-}
-
-int load_elf(
- struct process *process, uint64_t *entry_out,
- const struct fs_info *fs_info, void *fs_node) {
-
- struct fs_stat stat;
- if ((*fs_info->stat_file)(fs_info, fs_node, &stat) != FAR_SUCCESS)
- return 0;
-
- if (stat.bytes < 58)
- return 0;
-
- uint8_t head_part[34];
- if ((*fs_info->read_file)(fs_info, fs_node, head_part, 24, 34) != FAR_SUCCESS)
- return 0;
-
- *entry_out = *(uint64_t *)head_part;
-
- uint64_t phead_start = *(uint64_t *)(head_part + 8);
- uint16_t phead_entry_size = *(uint16_t *)(head_part + 30);
- uint16_t phead_entry_count = *(uint16_t *)(head_part + 32);
-
- if (stat.bytes < phead_start + phead_entry_count * phead_entry_size)
- panic("malformed elf")
-
- for (uint16_t i = 0; i < phead_entry_count; ++i) {
-
- uint64_t entry[6];
- if ((*fs_info->read_file)(
- fs_info, fs_node, entry,
- phead_start + i * phead_entry_size,
- 6 * sizeof(uint64_t)) != FAR_SUCCESS)
- return 0;
-
- if ((entry[0] & 0xffffffff) != 1)
- continue;
-
- int executable = (entry[0] >> 32) & 0x1;
- int writable = (entry[0] >> 33) & 0x1;
-
- uint64_t file_start = entry[1];
- void *virtual_start = (void *)entry[2];
- uint64_t file_length = entry[4];
- uint64_t virtual_length = ((entry[5] - 1) / 4096 + 1) * 4096;
-
- if (stat.bytes < file_start + file_length)
- return 0;
- if (file_length > virtual_length)
- return 0;
-
- if ((uint64_t)virtual_start % 4096 != 0 || virtual_length % 4096 != 0)
- return 0;
-
- for (uint64_t i = 0; i < virtual_length; i += 4096) {
-
- uint64_t pma = take_free_physical_page();
- map_page_for_process(
- process, pma, virtual_start + i, writable, executable, 1);
-
- void *kvma = find_free_kernel_region(4096);
- map_in_kernel_page_table(pma, kvma, 1, 0);
-
- if (i + 4096 <= file_length) {
- if ((*fs_info->read_file)(
- fs_info, fs_node, kvma,
- file_start + i, 4096) != FAR_SUCCESS)
- return 0;
- }
- else if (i >= file_length)
- memzero(kvma, 4096);
- else {
- if ((*fs_info->read_file)(
- fs_info, fs_node, kvma,
- file_start + i,
- file_length & 0xfff) != FAR_SUCCESS)
- return 0;
- memzero(kvma + (file_length & 0xfff), 4096 - (file_length & 0xfff));
- }
-
- unmap_kernel_page(kvma);
-
- }
-
- }
-
- return 1;
-
-}
-
-//defined in process.asm. enters user mode with:
-// cr3 = value of rbp when we jump here
-// rsp = value of rsp when we jump here
-// rip = rcx = value of r12 when we jump here
-// rflags = r11 = 0x200 (IF)
-// rdi = value of r13 when we jump here
-// all other registers zeroed
-extern uint8_t thread_start;
-
-//returns 0 on failure
-struct process *start_elf(const char *uri) {
-
- struct process *process = heap_alloc(sizeof(struct process));
- create_process(process);
- debug_log("new process")
- debug_log(" path %s", uri)
- debug_log(" process struct 0x%h", process, 16)
-
- const struct fs_info *fs_info;
- void *fs_node;
- uint64_t entry;
-
- if (look_up_file(uri, &fs_info, &fs_node) != FAR_SUCCESS ||
- !load_elf(process, &entry, fs_info, fs_node)) {
- destroy_process(process);
- return 0;
- }
-
- if ((*fs_info->free_node)(fs_info, fs_node) != FAR_SUCCESS)
- panic("TODO")
-
- struct thread *thread = heap_alloc(sizeof(struct thread));
- create_thread(process, thread);
-
- struct continuation_info ci;
- ci.rip = (uint64_t)&thread_start;
- ci.rbp = thread->process->p4_physical_base;
- ci.rsp = (uint64_t)thread->stack_top;
- ci.r12 = entry;
- ci.r13 = 0;
- ci.running_thread = thread;
-
- add_to_queue(&ready_continuations, &ci);
-
- return process;
-
-}
-
-void syscall_create_thread(void (*f)(uint64_t), uint64_t x) {
-
- assert(running_thread != 0)
-
- struct thread *thread = heap_alloc(sizeof(struct thread));
- create_thread(running_thread->process, thread);
-
- struct continuation_info ci;
- ci.rip = (uint64_t)&thread_start;
- ci.rbp = thread->process->p4_physical_base;
- ci.rsp = (uint64_t)thread->stack_top;
- ci.r12 = (uint64_t)f;
- ci.r13 = x;
- ci.running_thread = thread;
-
- add_to_queue(&ready_continuations, &ci);
-
-}
-
-int syscall_start_elf(const char *path, const struct process_start_info *info) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_readable_string(running_thread->process, path) ||
- !is_mapped_readable(
- running_thread->process, info, sizeof(struct process_start_info)) ||
- !is_mapped_readable(
- running_thread->process,
- info->forwared_envvars,
- info->forwared_envvar_count * sizeof(const char *)) ||
- !is_mapped_readable(
- running_thread->process,
- info->set_envvars,
- 2 * info->set_envvar_count * sizeof(const char *)))
- syscall_illegal_args();
-
- for (int i = 0; i < info->forwared_envvar_count; ++i)
- if (!is_mapped_readable_string(running_thread->process, info->forwared_envvars[i]))
- syscall_illegal_args();
-
- for (int i = 0; i < 2 * info->set_envvar_count; ++i)
- if (!is_mapped_readable_string(running_thread->process, info->set_envvars[i]))
- syscall_illegal_args();
-
- struct process *process = start_elf(path);
- if (process == 0)
- return 0;
-
- for (int i = 0; i < info->forwared_envvar_count; ++i) {
- struct process_env_pair *pair = get_envvar(running_thread->process, info->forwared_envvars[i]);
- if (pair != 0)
- set_envvar(process, info->forwared_envvars[i], pair->value);
- }
-
- for (int i = 0; i < info->set_envvar_count; ++i)
- set_envvar(process, info->set_envvars[2 * i], info->set_envvars[2 * i + 1]);
-
- return 1;
-
-}
-
-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)
-
- 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);
-
- 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)
- if (process->env_pairs[i].key_length != 0) {
- heap_dealloc(process->env_pairs[i].key, process->env_pairs[i].key_length + 1);
- heap_dealloc(process->env_pairs[i].value, process->env_pairs[i].value_length + 1);
- }
- heap_dealloc(
- process->env_pairs, process->env_pairs_buffer_size * sizeof(struct process_env_pair));
- }
-
- for (int p3i = 0; p3i < 512; ++p3i)
- if (process->p3_virtual_base[p3i]) {
- for (int p2i = 0; p2i < 512; ++p2i)
- if (process->p2_virtual_bases[p3i][p2i]) {
- for (int p1i = 0; p1i < 512; ++p1i) {
- uint64_t pma =
- process->p1_virtual_bases[p3i][p2i][p1i] & 0x7ffffffffffff000;
- if (process->owned_pages_bitmaps[p3i][p2i][p1i / 8] & (1 << (p1i % 8)))
- mark_physical_memory_free(pma, 4096);
- }
- unmap_and_free_kernel_page(process->p1_virtual_bases[p3i][p2i]);
- heap_dealloc(process->owned_pages_bitmaps[p3i][p2i], 64);
- }
- unmap_and_free_kernel_page(process->p2_virtual_bases[p3i]);
- heap_dealloc(process->p1_virtual_bases[p3i], 4096);
- heap_dealloc(process->owned_pages_bitmaps[p3i], 4096);
- }
- unmap_and_free_kernel_page(process->p3_virtual_base);
- unmap_and_free_kernel_page(process->p4_virtual_base);
- heap_dealloc(process, sizeof(struct process));
-
- debug_log("process destroyed")
- debug_log(" process struct 0x%h", process, 16)
- debug_log(" free physical memory %B", count_free_pram())
- debug_log(" free kernel virtual memory %B", count_free_kernel_vram())
-
-}
-
-void destroy_thread(struct thread *thread) {
-
- assert(thread->process->n_threads >= 1)
-
- --thread->process->n_threads;
-
- unmap_kernel_page(thread->syscall_stack_bottom);
- for (void *p = thread->syscall_stack_bottom + 4096; p < thread->syscall_stack_top; p += 4096)
- unmap_and_free_kernel_page(p);
-
- if (thread->process->n_threads == 0)
- destroy_process(thread->process);
- else
- for (void *p = thread->stack_bottom; p < thread->stack_top; p += 4096)
- unmap_page_for_process(thread->process, p);
-
- heap_dealloc(thread, sizeof(struct thread));
-
- debug_log("thread destroyed")
- debug_log(" thread struct 0x%h", thread, 16)
- debug_log(" free physical memory %B", count_free_pram())
- debug_log(" free kernel virtual memory %B", count_free_kernel_vram())
-
-}
-
-#define MAX_STACK_SIZE ((16 << 20) - 4096)
-#define INITIAL_STACK_SIZE 16384
-
-#define SYSCALL_STACK_SIZE 12288
-
-void create_thread(struct process *process, struct thread *thread_out) {
-
- void *stack_bottom_vma =
- find_free_process_region(process, MAX_STACK_SIZE + 4096);
-
- for (int i = 0; i < MAX_STACK_SIZE - INITIAL_STACK_SIZE + 4096; i += 4096)
- create_guard_page_for_process(process, stack_bottom_vma + i);
-
- for (int i = MAX_STACK_SIZE - INITIAL_STACK_SIZE + 4096; i < MAX_STACK_SIZE + 4096; i += 4096) {
-
- uint64_t pma = take_free_physical_page();
- map_page_for_process(
- process, pma,
- stack_bottom_vma + i,
- 1, 0, 1);
-
- void *kvma = find_free_kernel_region(4096);
- map_in_kernel_page_table(pma, kvma, 1, 0);
- memzero(kvma, 4096);
- unmap_kernel_page(kvma);
-
- }
-
- void *syscall_stack_bottom = find_free_kernel_region(SYSCALL_STACK_SIZE + 4096);
-
- create_kernel_guard_page(syscall_stack_bottom);
-
- for (int i = 4096; i < 4096 + SYSCALL_STACK_SIZE; i += 4096) {
- uint64_t pma = take_free_physical_page();
- map_in_kernel_page_table(
- pma, syscall_stack_bottom + i, 1, 0);
- }
-
- thread_out->process = process;
- thread_out->stack_bottom = stack_bottom_vma;
- thread_out->stack_top = stack_bottom_vma + 4096 + MAX_STACK_SIZE;
- thread_out->syscall_stack_bottom = syscall_stack_bottom;
- thread_out->syscall_stack_top = syscall_stack_bottom + 4096 + SYSCALL_STACK_SIZE;
-
- ++process->n_threads;
-
- debug_log("new thread")
- debug_log(" process struct 0x%h", process, 16)
- debug_log(" thread struct 0x%h", thread_out, 16)
- debug_log(" free physical memory %B", count_free_pram())
- debug_log(" free kernel virtual memory %B", count_free_kernel_vram())
-
-}
-
-[[noreturn]] void syscall_illegal_args() {
- panic("bad syscall")
-}
-
-struct thread *running_thread = 0;
-
-//defined in process.asm.
-//function should not return.
-[[noreturn]] void with_temporary_stack(void (*function)());
-
-[[noreturn]] static void syscall_end_thread_with_temporary_stack() {
-
- assert(running_thread != 0)
-
- struct thread *the_thread = running_thread;
- running_thread = 0;
-
- destroy_thread(the_thread);
- resume_next_continuation();
-
-}
-
-[[noreturn]] void syscall_end_thread() {
- with_temporary_stack(&syscall_end_thread_with_temporary_stack);
-}
-
-void syscall_map_framebuffer(struct framebuffer_info *info_out) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_writable(
- running_thread->process,
- info_out, sizeof(struct framebuffer_info)))
- syscall_illegal_args();
-
- uint64_t pages_needed = (fb_pitch * fb_height - 1) / 4096 + 1;
-
- void *base = find_free_process_region(running_thread->process, pages_needed);
-
- for (uint64_t i = 0; i < pages_needed; ++i)
- map_page_for_process(
- running_thread->process,
- fb_physical_base + i * 4096,
- base + i * 4096, 1, 0, 0);
-
- info_out->fb_base = base;
- info_out->fb_pitch = fb_pitch;
- info_out->fb_height = fb_height;
- info_out->fb_width = fb_width;
-
-}
-
-enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_readable_string(running_thread->process, path) ||
- !is_mapped_writable(running_thread->process, handle_out, sizeof(file_handle_t)))
- syscall_illegal_args();
-
- const struct fs_info *fs;
- void *node;
- enum fs_access_result result = look_up_file(path, &fs, &node);
- if (result != FAR_SUCCESS)
- return result;
-
- struct fs_stat stat;
- result = (*fs->stat_file)(fs, node, &stat);
- if (result != FAR_SUCCESS) {
- (*fs->free_node)(fs, node);
- return result;
- }
-
- 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;
-
-}
-
-void syscall_close_file(file_handle_t handle) {
-
- assert(running_thread != 0)
-
- 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;
- }
-
-}
-
-enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *bytes_out) {
-
- assert(running_thread != 0)
-
- struct process_file_info *file = look_up_handle(&running_thread->process->file_handles, handle);
- if (file == 0)
- return FAR_BAD_HANDLE;
-
- *bytes_out = file->stat.bytes;
- return FAR_SUCCESS;
-
-}
-
-enum fs_access_result syscall_read_file(struct read_file_parameter *parameter) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_readable(
- running_thread->process, parameter, sizeof(struct read_file_parameter)) ||
- !is_mapped_writable(
- running_thread->process, parameter->buffer, parameter->bytes))
- syscall_illegal_args();
-
- struct process_file_info *file =
- look_up_handle(&running_thread->process->file_handles, parameter->handle);
-
- if (file == 0)
- return FAR_BAD_HANDLE;
-
- if (parameter->start + parameter->bytes > file->stat.bytes)
- return FAR_OUT_OF_BOUNDS;
-
- return (*file->fs->read_file)(file->fs, file->node, parameter->buffer, parameter->start, parameter->bytes);
-
-}
-
-void *syscall_map_pages(uint64_t count) {
-
- assert(running_thread != 0)
-
- void *vma = find_free_process_region(running_thread->process, count);
- for (uint64_t i = 0; i < count; ++i) {
- uint64_t pma = take_free_physical_page();
- map_page_for_process(
- running_thread->process,
- pma, vma + i * 4096,
- 1, 0, 1);
- }
-
- return vma;
-
-}
-
-enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
- const char *address, ipc_dgram_receiver_handle_t *handle_out) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_readable_string(running_thread->process, address) ||
- !is_mapped_writable(
- running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t)))
- syscall_illegal_args();
-
- struct ipc_dgram_box *box = get_ipc_dgram_box(address);
- if (box->is_receiver_held)
- return IPR_IN_USE;
-
- box->is_receiver_held = 1;
-
- 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;
-
-}
-
-enum ipc_dgram_result syscall_ipc_create_dgram_sender(
- const char *address, ipc_dgram_sender_handle_t *handle_out) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_readable_string(running_thread->process, address) ||
- !is_mapped_writable(
- running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t)))
- syscall_illegal_args();
-
- struct ipc_dgram_box *box = get_ipc_dgram_box(address);
-
- 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;
-
-}
-
-enum ipc_dgram_result syscall_ipc_receive_dgram(
- ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_writable(running_thread->process, bytes, sizeof(int)) ||
- !is_mapped_writable(running_thread->process, buffer, *bytes))
- syscall_illegal_args();
-
- 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;
-
- int actual_bytes = get_ipc_dgram_size(info->box);
- assert(actual_bytes > 0);
-
- if (actual_bytes > *bytes) {
- *bytes = actual_bytes;
- return IPR_TOO_BIG;
- }
-
- receive_ipc_dgram(info->box, buffer);
- *bytes = actual_bytes;
- return IPR_SUCCESS;
-
-}
-
-enum ipc_dgram_result syscall_ipc_send_dgram(
- ipc_dgram_sender_handle_t handle, const void *data, int bytes) {
-
- assert(running_thread != 0)
-
- if (bytes <= 0 || !is_mapped_readable(running_thread->process, data, bytes))
- syscall_illegal_args();
-
- 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;
-
- return send_ipc_dgram(info->box, data, bytes);
-
-}
-
-#define INITIAL_ENVVAR_BUFFER_SIZE 32
-
-void set_envvar(struct process *process, const char *key, const char *value) {
-
- if (!process->env_pairs) {
- process->env_pairs = heap_alloc(INITIAL_ENVVAR_BUFFER_SIZE * sizeof(struct process_env_pair));
- process->env_pairs_buffer_size = INITIAL_ENVVAR_BUFFER_SIZE;
- for (int i = 0; i < INITIAL_ENVVAR_BUFFER_SIZE; ++i)
- process->env_pairs[i].key_length = 0;
- }
-
- int key_length = 0;
- while (key[key_length] != 0)
- ++key_length;
-
- int value_length = 0;
- while (value[value_length] != 0)
- ++value_length;
-
- assert(key_length != 0)
- assert(value_length != 0)
-
- for (int i = 0; i < process->env_pairs_buffer_size; ++i) {
- if (process->env_pairs[i].key_length != key_length)
- continue;
- for (int j = 0; j < key_length; ++j)
- if (process->env_pairs[i].key[j] != key[j])
- continue;
- heap_dealloc(process->env_pairs[i].value, process->env_pairs[i].value_length + 1);
- char *value_copy = heap_alloc(value_length + 1);
- memcpy(value_copy, value, value_length + 1);
- process->env_pairs[i].value = value_copy;
- process->env_pairs[i].value_length = value_length;
- return;
- }
-
- struct process_env_pair *pair;
- for (int i = 0; i < process->env_pairs_buffer_size; ++i)
- if (process->env_pairs[i].key_length == 0) {
- pair = &process->env_pairs[i];
- goto got_pair;
- }
-
- double_buffer_zero(
- (void **)&process->env_pairs,
- &process->env_pairs_buffer_size,
- sizeof(struct process_env_pair));
-
- pair = &process->env_pairs[process->env_pairs_buffer_size / 2];
-
-got_pair:
- char *key_copy = heap_alloc(key_length + 1);
- memcpy(key_copy, key, key_length + 1);
- pair->key = key_copy;
- pair->key_length = key_length;
- char *value_copy = heap_alloc(value_length + 1);
- memcpy(value_copy, value, value_length + 1);
- pair->value = value_copy;
- pair->value_length = value_length;
-
-}
-
-struct process_env_pair *get_envvar(struct process *process, const char *key) {
-
- int key_length = 0;
- while (key[key_length] != 0)
- ++key_length;
-
- assert(key_length > 0)
-
- for (int i = 0; i < process->env_pairs_buffer_size; ++i) {
- if (process->env_pairs[i].key_length != key_length)
- continue;
- for (int j = 0; j < key_length; ++j)
- if (process->env_pairs[i].key[j] != key[j])
- continue;
- return &process->env_pairs[i];
- }
-
- return 0;
-
-}
-
-void syscall_get_envvar(const char *key, char *value_out, int *value_space) {
-
- assert(running_thread != 0)
-
- if (!is_mapped_readable_string(running_thread->process, key) ||
- !is_mapped_writable(running_thread->process, value_space, sizeof(int)) ||
- *value_space <= 0 ||
- !is_mapped_writable(running_thread->process, value_out, *value_space))
- syscall_illegal_args();
-
- struct process_env_pair *pair = get_envvar(running_thread->process, key);
-
- if (pair == 0) {
- *value_space = 0;
- return;
- }
-
- if (*value_space < pair->value_length + 1) {
- *value_space = pair->value_length + 1;
- return;
- }
-
- *value_space = pair->value_length + 1;
- 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
deleted file mode 100644
index 20aa9bc..0000000
--- a/src/kernel/process.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/* Calcite, src/kernel/process.h
- * 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include "scheduler.h"
-#include "utility.h"
-#include "fs.h"
-
-#include
-#include
-#include
-#include
-#include
-
-struct ipc_dgram_box;
-
-struct process_file_info {
- const struct fs_info *fs;
- void *node;
- struct fs_stat stat;
-};
-
-struct process_ipc_dgram_handle_info {
- 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 {
- //0 for unused
- int key_length;
- int value_length;
- char *key;
- char *value;
-};
-
-//layout used in scheduler.asm
-struct process {
-
- uint64_t p4_physical_base;
- uint64_t *p4_virtual_base;
- uint64_t p3_physical_base;
- uint64_t *p3_virtual_base;
-
- int n_threads;
-
- 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;
-
- //0 for missing levels. just bottom p3 of address space.
- uint64_t *p2_virtual_bases[512];
- uint64_t **p1_virtual_bases[512];
-
- //also just bottom p3. bit set indicates we should free
- //the physical page when we clean up this process.
- uint8_t **owned_pages_bitmaps[512];
-
-};
-
-//layout used in syscalls.asm and scheduler.asm
-struct thread {
-
- struct process *process;
-
- //both page-aligned
- void *stack_bottom;
- void *stack_top;
-
- //both page-aligned
- void *syscall_stack_bottom;
- void *syscall_stack_top;
-
-};
-
-extern struct thread *running_thread;
-
-void create_process(struct process *process_out);
-void create_thread(struct process *process, struct thread *thread_out);
-
-//physical and virtual bases must be page-aligned.
-//virtual base must be in bottom p3 and not zero.
-void map_page_for_process(
- struct process *process, uint64_t physical_base,
- void *virtual_base, int writable, int executable, int owned);
-
-//physical and virtual bases must be page-aligned.
-void create_guard_page_for_process(struct process *process, void *virtual_base);
-
-//virtual base must be page-aligned, in bottom p3, and not zero.
-//also frees physical page if owned by process.
-void unmap_page_for_process(
- struct process *process, void *virtual_base);
-
-//finds a free memory region in the bottom p3
-//of the address space, and not the bottom p2.
-void *find_free_process_region(
- struct process *process, uint64_t page_count);
-
-//returns 0 on failure, 1 on success.
-int load_elf(
- struct process *process, uint64_t *entry_out,
- const struct fs_info *fs_info, void *fs_node);
-
-//returns 0 on failure.
-//creates a process and a thread in that process, loads the elf into the process,
-//and schedules a ready task that sets the running thread to the new thread and
-//starts user mode at the elf's entry point.
-struct process *start_elf(const char *uri);
-
-int syscall_start_elf(const char *path, const struct process_start_info *info);
-
-void destroy_process(struct process *process);
-//must not be called with syscall stack of the thread being destroyed
-void destroy_thread(struct thread *thread);
-
-[[noreturn]] void syscall_illegal_args();
-
-//returs 1 if [start, start + length) is writable by process, otherwise 0.
-int is_mapped_writable(struct process *process, const void *start, uint64_t length);
-
-//returs 1 if [start, start + length) is readable by process, otherwise 0.
-int is_mapped_readable(struct process *process, const void *start, uint64_t length);
-
-//return 1 if the entire null-terminated string starting at start is readable by process, otherwise 0.
-int is_mapped_readable_string(struct process *process, const char *start);
-
-[[noreturn]] void syscall_end_thread();
-
-void syscall_map_framebuffer(struct framebuffer_info *info_out);
-
-enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out);
-void syscall_close_file(file_handle_t handle);
-enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *bytes_out);
-enum fs_access_result syscall_read_file(struct read_file_parameter *parameter);
-
-void *syscall_map_pages(uint64_t count);
-
-void syscall_change_page_permissions(void *vma, int writable, int executable);
-
-enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
- const char *address, ipc_dgram_receiver_handle_t *handle_out);
-
-enum ipc_dgram_result syscall_ipc_create_dgram_sender(
- const char *address, ipc_dgram_sender_handle_t *handle_out);
-
-//on entry, bytes is maximum accepted packet length.
-//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length.
-//actual packet length will always be positive.
-enum ipc_dgram_result syscall_ipc_receive_dgram(
- ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes);
-
-//bytes must be positive.
-enum ipc_dgram_result syscall_ipc_send_dgram(
- ipc_dgram_sender_handle_t handle, const void *data, int bytes);
-
-//f should not return.
-void syscall_create_thread(void (*f)(uint64_t x), uint64_t x);
-
-void set_envvar(struct process *process, const char *key, const char *value);
-
-//0 if unset
-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/ps2.asm b/src/kernel/ps2.asm
deleted file mode 100644
index 48b2078..0000000
--- a/src/kernel/ps2.asm
+++ /dev/null
@@ -1,71 +0,0 @@
- ; Calcite, src/kernel/ps2.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .text
-
-global init_ps2
-init_ps2:
- mov al, 0x60
- out 0x64, al
-
-.wait_ready:
- in al, 0x64
- test al, 0x02
- jnz .wait_ready
-
- mov al, 0x07
- out 0x60, al
-
- mov al, 0xd4
- out 0x64, al
-
-.wait_ready_2:
- in al, 0x64
- test al, 0x02
- jnz .wait_ready_2
-
- mov al, 0xf4
- out 0x60, al
-
-.wait_ready_3:
- in al, 0x64
- test al, 0x01
- jz .wait_ready_3
-
- in al, 0x60
- ret
-
-;referenced in ps2.c
-;return type is int
-;returns -1 if no byte available
-global read_ps2_byte
-read_ps2_byte:
-
- in al, 0x64
- test al, 0x01
- jz .no_data
-
- xor eax, eax
- in al, 0x60
- ret
-
-.no_data:
- mov eax, -1
- ret
diff --git a/src/kernel/ps2.c b/src/kernel/ps2.c
deleted file mode 100644
index 5abe7c8..0000000
--- a/src/kernel/ps2.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/* Calcite, src/kernel/ps2.c
- * 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
- * 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 "input.h"
-#include "ps2.h"
-
-#include
-#include
-
-//defined in ps2.asm
-//returns -1 if no byte available
-int read_ps2_byte();
-
-static uint8_t keyboard_packet[7];
-static int keyboard_packet_length = 0;
-
-void on_keyboard_irq() {
-
- int byte = read_ps2_byte();
- if (byte == -1)
- return;
-
- if (keyboard_packet_length == 0 && byte != 0xe0 && byte != 0xe1 && byte != 0xf0) {
- add_keyboard_packet(KEY_FLAG_MAKE | byte);
- return;
- }
-
- if (keyboard_packet_length == 1 && keyboard_packet[0] == 0xe0 && byte != 0xf0 && byte != 0x12) {
- add_keyboard_packet(KEY_FLAG_MAKE | 0x80 | byte);
- keyboard_packet_length = 0;
- return;
- }
-
- if (keyboard_packet_length == 1 && keyboard_packet[0] == 0xf0) {
- add_keyboard_packet(byte);
- keyboard_packet_length = 0;
- return;
- }
-
- if (keyboard_packet_length == 2 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0xf0 && byte != 0x7c) {
- add_keyboard_packet(0x80 | byte);
- keyboard_packet_length = 0;
- return;
- }
-
- if (keyboard_packet_length == 3 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0x12 &&
- keyboard_packet[2] == 0xe0 && byte == 0x7c) {
- add_keyboard_packet(KEY_FLAG_MAKE | KEY_PRINT_SCREEN);
- keyboard_packet_length = 0;
- return;
- }
-
- if (keyboard_packet_length == 5 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0xf0 &&
- keyboard_packet[2] == 0x7c && keyboard_packet[3] == 0xe0 && keyboard_packet[4] == 0xf0 && byte == 0x12) {
- add_keyboard_packet(KEY_PRINT_SCREEN);
- keyboard_packet_length = 0;
- return;
- }
-
- if (keyboard_packet_length == 7 && keyboard_packet[0] == 0xe1 && keyboard_packet[1] == 0x14 &&
- keyboard_packet[2] == 0x77 && keyboard_packet[3] == 0xe1 && keyboard_packet[4] == 0xf0 &&
- keyboard_packet[5] == 0x14 && keyboard_packet[6] == 0xf0 && byte == 0x77) {
- add_keyboard_packet(KEY_FLAG_MAKE | KEY_PAUSE_BREAK);
- add_keyboard_packet(KEY_PAUSE_BREAK);
- keyboard_packet_length = 0;
- return;
- }
-
- if (keyboard_packet_length == 7) {
- keyboard_packet_length = 0;
- return;
- }
-
- keyboard_packet[keyboard_packet_length++] = byte;
-
-}
-
-static uint8_t mouse_packet[3];
-static int mouse_packet_length = 0;
-
-void on_mouse_irq() {
-
- int byte = read_ps2_byte();
- if (byte == -1)
- return;
-
- mouse_packet[mouse_packet_length] = byte;
- if (mouse_packet_length < 2) {
- ++mouse_packet_length;
- return;
- }
- mouse_packet_length = 0;
-
- int x_change = mouse_packet[1];
- if (mouse_packet[0] & 0x10)
- x_change -= 256;
-
- int y_change = mouse_packet[2];
- if (mouse_packet[0] & 0x20)
- y_change -= 256;
-
- add_mouse_movement(x_change, y_change);
-
-}
diff --git a/src/kernel/ps2.h b/src/kernel/ps2.h
deleted file mode 100644
index 235caf2..0000000
--- a/src/kernel/ps2.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Calcite, src/kernel/ps2.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-void init_ps2();
-
-void on_keyboard_irq();
-void on_mouse_irq();
diff --git a/src/kernel/scheduler.asm b/src/kernel/scheduler.asm
deleted file mode 100644
index 61bd1d1..0000000
--- a/src/kernel/scheduler.asm
+++ /dev/null
@@ -1,82 +0,0 @@
- ; Calcite, src/kernel/scheduler.asm
- ; 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
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-extern resume_next_continuation
-extern running_thread
-extern add_to_queue
-
-;referenced in scheduler.c
-global resume_continuation
-resume_continuation:
-
- mov rax, qword [rdi]
- mov rbx, qword [rdi + 8]
- mov rbp, qword [rdi + 16]
- mov rsp, qword [rdi + 24]
- mov r12, qword [rdi + 32]
- mov r13, qword [rdi + 40]
- mov r14, qword [rdi + 48]
- mov r15, qword [rdi + 56]
- mov rdi, qword [rdi + 64]
- mov qword [running_thread], rdi
- mov rdi, qword [rdi]
- mov rdi, qword [rdi]
- mov cr3, rdi
-
- jmp rax
-
-global yield
-yield:
- mov qword [rdi], .ret
- mov qword [rdi + 8], rbx
- mov qword [rdi + 16], rbp
- mov qword [rdi + 24], rsp
- mov qword [rdi + 32], r12
- mov qword [rdi + 40], r13
- mov qword [rdi + 48], r14
- mov qword [rdi + 56], r15
- mov qword rsi, qword [running_thread]
- mov qword [rdi + 64], rsi
-
- jmp resume_next_continuation
-
-.ret:
- ret
-
-global yield_to_queue
-yield_to_queue:
- mov qword [rsp - 72], .ret
- mov qword [rsp - 64], rbx
- mov qword [rsp - 56], rbp
- mov qword [rsp - 48], rsp
- mov qword [rsp - 40], r12
- mov qword [rsp - 32], r13
- mov qword [rsp - 24], r14
- mov qword [rsp - 16], r15
- mov rdx, qword [running_thread]
- mov qword [rsp - 8], rdx
-
- sub rsp, 72
- mov rsi, rsp
- call add_to_queue
- jmp resume_next_continuation
-
-.ret:
- ret
diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c
deleted file mode 100644
index 1994539..0000000
--- a/src/kernel/scheduler.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Calcite, src/kernel/scheduler.c
- * 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
- * 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 "scheduler.h"
-#include "process.h"
-#include "utility.h"
-#include "debug.h"
-#include "heap.h"
-
-struct continuation_queue ready_continuations;
-
-void init_scheduler() {
- create_queue(&ready_continuations, 128);
-}
-
-//defined in scheduler.asm
-[[noreturn]] void resume_continuation(const struct continuation_info *info);
-
-[[noreturn]] void resume_next_continuation() {
-
- running_thread = 0;
-
- struct continuation_info ci;
- while (!take_from_queue(&ready_continuations, &ci)) {
- __asm__ ("sti");
- __asm__ ("hlt");
- __asm__ ("cli");
- }
-
- assert(running_thread == 0)
- assert(ci.running_thread != 0)
- resume_continuation(&ci);
-
-}
-
-void create_queue(struct continuation_queue *queue, int buffer_size) {
- queue->cis = heap_alloc(buffer_size * sizeof(struct continuation_info));
- queue->next_read_index = 0;
- queue->buffer_size = 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) {
-
- struct continuation_info *new_buffer =
- heap_alloc(2 * queue->buffer_size * sizeof(struct continuation_info));
-
- memcpy(
- new_buffer, &queue->cis[queue->next_read_index],
- (queue->buffer_size - queue->next_read_index) * sizeof(struct continuation_info));
- memcpy(
- &new_buffer[queue->buffer_size - queue->next_read_index],
- queue->cis, queue->next_read_index * sizeof(struct continuation_info));
-
- heap_dealloc(queue->cis, queue->buffer_size * sizeof(struct continuation_info));
- queue->cis = new_buffer;
- queue->buffer_size *= 2;
- queue->next_read_index = 0;
-
- }
-
- memcpy(
- &queue->cis[(queue->next_read_index + queue->count) % queue->buffer_size],
- ci, sizeof(struct continuation_info));
- ++queue->count;
-
-}
-
-int take_from_queue(struct continuation_queue *queue, struct continuation_info *ci) {
-
- if (queue->count == 0)
- return 0;
-
- memcpy(ci, &queue->cis[queue->next_read_index], sizeof(struct continuation_info));
- queue->next_read_index = (queue->next_read_index + 1) % queue->buffer_size;
- --queue->count;
- return 1;
-
-}
diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h
deleted file mode 100644
index 59c3cb6..0000000
--- a/src/kernel/scheduler.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Calcite, src/kernel/scheduler.h
- * 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
- * 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
-
-struct thread;
-
-//scheduler.asm depends on layout
-struct continuation_info {
- uint64_t rip;
- uint64_t rbx;
- uint64_t rbp;
- uint64_t rsp;
- uint64_t r12;
- uint64_t r13;
- uint64_t r14;
- uint64_t r15;
- struct thread *running_thread;
-};
-
-void init_scheduler();
-
-[[noreturn]] void resume_next_continuation();
-
-struct continuation_queue {
- struct continuation_info *cis;
- int next_read_index;
- int buffer_size;
- int count;
-};
-
-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);
-
-//if queue is empty, returns 0. otherwise, copies next read to ci, advances read, and returns 1.
-int take_from_queue(struct continuation_queue *queue, struct continuation_info *ci);
-
-//saves a continuation that returns from this function to save_current_continuation_to, then resumes
-//the next ready continuation (thus doesn't return until save_current_continuation_to is resumed).
-void yield(struct continuation_info *save_current_continuation_to);
-
-//same as yield, but instead of saving continuation to a pointer, saves it to the end of the queue.
-void yield_to_queue(struct continuation_queue *queue);
diff --git a/src/kernel/serial.asm b/src/kernel/serial.asm
deleted file mode 100644
index a4731dc..0000000
--- a/src/kernel/serial.asm
+++ /dev/null
@@ -1,73 +0,0 @@
- ; Calcite, src/kernel/serial.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .text
-
-global init_serial
-init_serial:
- mov dx, 0x03f9
- mov al, 0x00
- out dx, al
-
- mov dl, 0xfb
- mov al, 0x80
- out dx, al
-
- mov dl, 0xf8
- mov al, 0x03
- out dx, al
-
- mov dl, 0xf9
- mov al, 0x00
- out dx, al
-
- mov dl, 0xfb
- mov al, 0x03
- out dx, al
-
- mov dl, 0xfa
- mov al, 0xc7
- out dx, al
-
- ret
-
-global write_serial_string_n
-write_serial_string_n:
- test esi, esi
- jz .ret
- mov ecx, esi
-
-.wait_ready:
- mov dx, 0x03fd
- in al, dx
- test al, 0x20
- jnz .ready
- pause
- jmp .wait_ready
-
-.ready:
- mov dl, 0xf8
- mov al, byte [rdi]
- out dx, al
-
- inc rdi
- loop .wait_ready
-.ret:
- ret
diff --git a/src/kernel/serial.c b/src/kernel/serial.c
deleted file mode 100644
index 0c9f4de..0000000
--- a/src/kernel/serial.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Calcite, src/kernel/serial.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 "serial.h"
-#include "debug.h"
-
-void write_serial_string(const char *string) {
- int n = 0;
- while (string[n] != 0)
- ++n;
- write_serial_string_n(string, n);
-}
-
-void write_serial_integer(int integer) {
-
- if (integer == 0) {
- write_serial_string_n("0", 1);
- return;
- }
-
- char buffer[10];
- char *ptr = &buffer[9];
-
- while (integer != 0) {
- *ptr = '0' + integer % 10;
- integer /= 10;
- --ptr;
- }
-
- write_serial_string_n(ptr + 1, (buffer + 10) - (ptr + 1));
-
-}
-
-void write_serial_hex(uint64_t value, int places) {
- assert(places <= 16)
- char buffer[16];
- for (int i = 0; i < places; ++i)
- buffer[i] = "0123456789abcdef"[(value >> (4 * (places - i - 1))) & 0xf];
- write_serial_string_n(buffer, places);
-}
diff --git a/src/kernel/serial.h b/src/kernel/serial.h
deleted file mode 100644
index a70cfdf..0000000
--- a/src/kernel/serial.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Calcite, src/kernel/serial.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-
-void init_serial();
-void write_serial_string(const char *string);
-void write_serial_string_n(const char *string, int n);
-void write_serial_integer(int integer);
-void write_serial_hex(uint64_t value, int places);
diff --git a/src/kernel/syscalls.asm b/src/kernel/syscalls.asm
deleted file mode 100644
index a064002..0000000
--- a/src/kernel/syscalls.asm
+++ /dev/null
@@ -1,87 +0,0 @@
- ; Calcite, src/kernel/syscalls.asm
- ; 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
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-;defined in syscalls.c
-extern syscall_entry_c
-
-extern running_thread
-
-section .text
-
-;system call number is in rax.
-;system call arguments are in rdi, rsi, rdx.
-;system call returns a value in rax.
-;depends on layout of struct thread from process.h
-syscall_entry:
- mov r8, qword [running_thread]
- test r8, r8
- jz .assert_fail
-
- mov r8, qword [r8 + 32]
- mov qword [r8 - 8], rsp
- mov rsp, r8
- sub rsp, 8
- push rcx
- push r11
-
- mov rcx, rax
- cli
- call syscall_entry_c
- sti
-
- xor rdi, rdi
- xor rsi, rsi
- xor rdx, rdx
- xor r8, r8
- xor r9, r9
- xor r10, r10
-
- pop r11
- pop rcx
- pop rsp
- o64 sysret
-
-.assert_fail:
- ud2
-
-global init_syscalls
-init_syscalls:
- mov ecx, 0xc0000080
- rdmsr
- or al, 0x01
- wrmsr
-
- mov edx, 0x00130028
- xor eax, eax
- mov ecx, 0xc0000081
- wrmsr
-
- mov rdx, syscall_entry
- mov eax, edx
- shr rdx, 32
- mov ecx, 0xc0000082
- wrmsr
-
- xor edx, edx
- xor eax, eax
- mov ecx, 0xc0000084
- wrmsr
-
- ret
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
deleted file mode 100644
index 8132dfd..0000000
--- a/src/kernel/syscalls.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Calcite, src/kernel/syscalls.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 "syscalls.h"
-#include "debug.h"
-
-#define MAX_SYSCALL_NUMBER 99
-
-static uint64_t (*syscall_handlers[MAX_SYSCALL_NUMBER])(
- uint64_t, uint64_t, uint64_t);
-
-void register_syscall(
- int number,
- uint64_t (*handler)(uint64_t, uint64_t, uint64_t)) {
-
- assert(number >= 0 && number <= MAX_SYSCALL_NUMBER)
- assert(syscall_handlers[number] == 0)
-
- syscall_handlers[number] = handler;
-
-}
-
-//referenced in syscalls.asm
-uint64_t syscall_entry_c(
- uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t number) {
-
- if (number > MAX_SYSCALL_NUMBER || syscall_handlers[number] == 0)
- panic("TODO: bad syscall");
-
- return (*syscall_handlers[number])(arg1, arg2, arg3);
-
-}
diff --git a/src/kernel/syscalls.h b/src/kernel/syscalls.h
deleted file mode 100644
index 3171a7a..0000000
--- a/src/kernel/syscalls.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Calcite, src/kernel/syscalls.h
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#pragma once
-
-#include
-#include
-
-void init_syscalls();
-
-void register_syscall(
- int number,
- uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3));
diff --git a/src/kernel/timer.asm b/src/kernel/timer.asm
deleted file mode 100644
index 45e1c27..0000000
--- a/src/kernel/timer.asm
+++ /dev/null
@@ -1,37 +0,0 @@
- ; Calcite, src/kernel/timer.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .text
-
-global init_timer
-init_timer:
-
- ;generate an irq every 1193 cycles (~ every millisecond)
-
- mov al, 0x34
- out 0x43, al
-
- mov al, 0xa9
- out 0x40, al
-
- mov al, 0x04
- out 0x40, al
-
- ret
diff --git a/src/kernel/timer.c b/src/kernel/timer.c
deleted file mode 100644
index 80e7278..0000000
--- a/src/kernel/timer.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Calcite, src/kernel/timer.c
- * 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
- * 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 "scheduler.h"
-#include "timer.h"
-#include "heap.h"
-
-struct sleeping_list_entry {
- struct sleeping_list_entry *next;
- uint64_t sleeping_until;//pit irqs since boot
- struct continuation_info continuation;
-};
-
-//entries are always sorted increasing by sleeping_until
-static struct sleeping_list_entry *first_entry = 0;
-static struct sleeping_list_entry *last_entry = 0;
-
-//pit irq happens ~ once per millisecond
-static uint64_t pit_irqs_since_boot = 0;
-
-static struct sleeping_list_entry *local_sleeping_list_entry_pool = 0;
-
-void on_pit_irq() {
-
- ++pit_irqs_since_boot;
-
- while (first_entry != 0 && pit_irqs_since_boot >= first_entry->sleeping_until) {
-
- add_to_queue(&ready_continuations, &first_entry->continuation);
-
- struct sleeping_list_entry *old_first = first_entry;
- first_entry = old_first->next;
- *(struct sleeping_list_entry **)old_first = local_sleeping_list_entry_pool;
- local_sleeping_list_entry_pool = old_first;
-
- if (first_entry == 0)
- last_entry = 0;
-
- }
-
-}
-
-//until in pit irqs since boot.
-//must be called with interrupts disabled.
-//on return, interrupts are enabled.
-static void sleep_until(uint64_t until) {
-
- struct sleeping_list_entry *entry;
- if (local_sleeping_list_entry_pool != 0) {
- entry = local_sleeping_list_entry_pool;
- local_sleeping_list_entry_pool = *(struct sleeping_list_entry **)entry;
- }
- else
- entry = heap_alloc(sizeof(struct sleeping_list_entry));
-
- entry->sleeping_until = until;
-
- if (first_entry == 0) {
- entry->next = 0;
- first_entry = entry;
- last_entry = entry;
- }
-
- else if (first_entry->sleeping_until >= until) {
- entry->next = first_entry;
- first_entry = entry;
- }
-
- else if (last_entry->sleeping_until <= until) {
- entry->next = 0;
- last_entry->next = entry;
- last_entry = entry;
- }
-
- else {
- struct sleeping_list_entry *just_before = first_entry;
- while (just_before->next->sleeping_until <= until)
- just_before = just_before->next;
- entry->next = just_before->next;
- just_before->next = entry;
- }
-
- yield(&entry->continuation);
-
-}
-
-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
deleted file mode 100644
index c4466e2..0000000
--- a/src/kernel/timer.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Calcite, src/kernel/timer.h
- * 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
- * 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
-
-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.asm b/src/kernel/utility.asm
deleted file mode 100644
index a29b82d..0000000
--- a/src/kernel/utility.asm
+++ /dev/null
@@ -1,62 +0,0 @@
- ; Calcite, src/kernel/utility.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .text
-
-global memcpy
-memcpy:
- mov rcx, rdx
- rep movsb
- ret
-
-global memzero
-memzero:
- xor al, al
- mov rcx, rsi
- rep stosb
- ret
-
-global outb
-outb:
- mov dx, di
- mov al, sil
- out dx, al
- ret
-
-global inb
-inb:
- mov dx, di
- in al, dx
- ret
-
-global outsw
-outsw:
- mov rcx, rdx
- mov dx, di
- rep outsw
- ret
-
-global insw
-insw:
- mov rcx, rdx
- mov dx, di
- mov rdi, rsi
- rep insw
- ret
diff --git a/src/kernel/utility.c b/src/kernel/utility.c
deleted file mode 100644
index ed5245e..0000000
--- a/src/kernel/utility.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/* Calcite, src/kernel/utility.c
- * 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
- * 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 "utility.h"
-#include "heap.h"
-
-int strequ(const char *str1, const char *str2) {
- while (1) {
- if (!*str1 && !*str2)
- return 1;
- if (*str1 != *str2)
- return 0;
- ++str1;
- ++str2;
- }
-}
-
-uint32_t end_swap_u32(uint32_t value) {
- return
- (value >> 24) |
- (((value >> 16) & 0xff) << 8) |
- (((value >> 8) & 0xff) << 16) |
- ((value & 0xff) << 24);
-}
-
-void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry) {
- void *new_buffer = heap_alloc(2 * *length * bytes_per_entry);
- memcpy(new_buffer, *buffer, *length * bytes_per_entry);
- heap_dealloc(*buffer, *length * bytes_per_entry);
- memzero(new_buffer + *length * bytes_per_entry, *length * 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
deleted file mode 100644
index e9f35b8..0000000
--- a/src/kernel/utility.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Calcite, src/kernel/utility.h
- * 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
- * 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
-
-//returns 1 if equal, 0 if not.
-int strequ(const char *str1, const char *str2);
-
-void memcpy(void *to, const void *from, uint64_t bytes);
-void memzero(void *start, uint64_t bytes);
-
-//swaps the endianness of the value
-uint32_t end_swap_u32(uint32_t value);
-
-//1. allocates a new buffer with 2 * length * bytes_per_entry bytes
-//2. copies length * bytes_per_entry bytes from old buffer to new buffer
-//3. deallocates old buffer
-//4. zeroes rest of new buffer
-//5. sets buffer and length to new buffer and twice length
-void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry);
-
-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/service/hello/hello.cxx b/src/service/hello/hello.cxx
new file mode 100644
index 0000000..330d4d7
--- /dev/null
+++ b/src/service/hello/hello.cxx
@@ -0,0 +1,22 @@
+/* src/service/hello/hello.cxx
+ * A temporary dummy service.
+ *
+ * Copyright 2026 Benji Dial
+ * This file is part of the Calcite Operating System.
+ *
+ * Calcite 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, version 3 only.
+ *
+ * Calcite 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
+ * Calcite. If not, see .
+ */
+
+extern "C" [[noreturn]] void __entry() {
+ while (true)
+ ;
+}
diff --git a/src/user-apps/link.ld b/src/service/link.ld
similarity index 80%
rename from src/user-apps/link.ld
rename to src/service/link.ld
index 84c8d32..c8fea08 100644
--- a/src/user-apps/link.ld
+++ b/src/service/link.ld
@@ -1,7 +1,7 @@
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
-ENTRY(_start)
+ENTRY(__entry)
PHDRS {
rx PT_LOAD FLAGS(5);
@@ -11,29 +11,30 @@ PHDRS {
SECTIONS {
- . = 0x400000;
+ . = 0xffffff0000000000;
.text : {
*(.text .text.*)
} : rx
-
. = ALIGN(4096);
.rodata : {
*(.rodata .rodata.*)
} : ro
-
. = ALIGN(4096);
.data : {
*(.data .data.*)
} : rw
-
. = ALIGN(4096);
-
.bss : {
*(.bss .bss.*)
*(COMMON)
} : rw
+ . = ALIGN(4096);
+
+ /DISCARD/ : {
+ *(.eh_frame)
+ }
}
diff --git a/src/user-apps/init/init.c b/src/user-apps/init/init.c
deleted file mode 100644
index 8f6f280..0000000
--- a/src/user-apps/init/init.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* Calcite, src/user-apps/init/init.c
- * 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
- * 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
-
-#define MAX_LINE_LENGTH 1023
-
-void main() {
-
- struct file_stream *rc;
- if (open_file_stream("root://calcite/init.rc", &rc) != FAR_SUCCESS)
- return;
-
- int at_file_end = 0;
-
- while (!at_file_end) {
-
- char line[MAX_LINE_LENGTH + 1];
- int line_length = 0;
-
- while (1) {
-
- char ch = read_file_stream_byte(rc);
-
- if (ch == -1) {
- at_file_end = 1;
- break;
- }
- if (ch == '\n')
- break;
- if (line_length == MAX_LINE_LENGTH) {
- while (1) {
- ch = read_file_stream_byte(rc);
- if (ch == -1 || ch == '\n')
- break;
- }
- break;
- }
-
- line[line_length++] = ch;
-
- }
-
- if (line_length == 0 || line[0] == '#')
- continue;
-
- line[line_length] = 0;
-
- struct process_start_info psi;
- psi.forwared_envvar_count = 0;
- psi.set_envvar_count = 0;
-
- start_elf(line, &psi);
-
- }
-
-}
diff --git a/src/user-apps/init/libraries.txt b/src/user-apps/init/libraries.txt
deleted file mode 100644
index 8503661..0000000
--- a/src/user-apps/init/libraries.txt
+++ /dev/null
@@ -1 +0,0 @@
-calcite
diff --git a/src/user-apps/shell/assembler.c b/src/user-apps/shell/assembler.c
deleted file mode 100644
index b07a602..0000000
--- a/src/user-apps/shell/assembler.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Calcite, src/user-apps/shell/assembler.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 "assembler.h"
-
-#include
-#include
-#include
-
-static uint8_t *function_buffer = 0;
-static int function_buffer_length;
-static int function_buffer_used;
-
-struct relocation_info {
- int offset_into_function;
- int from_offset_into_function;
- void *to;
- struct relocation_info *previous;
-};
-
-static struct relocation_info *relocations = 0;
-static char *function_name = 0;
-static int function_name_length;
-
-struct function_alloc {
- void *start;
- int bytes;
- struct function_alloc *previous;
-};
-
-static struct function_alloc *allocs = 0;
-
-#define FUNCTION_BUFFER_INITIAL_SIZE 4096
-
-void start_function(const char *name, int name_length) {
-
- if (function_buffer == 0) {
- function_buffer_length = FUNCTION_BUFFER_INITIAL_SIZE;
- function_buffer = heap_alloc(function_buffer_length);
- }
-
- function_buffer_used = 0;
- function_name_length = name_length;
-
- char *name_copy = heap_alloc(name_length);
- memcpy(name_copy, name, name_length);
- function_name = name_copy;
-
-}
-
-static void destroy_relocations() {
- struct relocation_info *r = relocations;
- while (r != 0) {
- struct relocation_info *p = r->previous;
- heap_dealloc(r, sizeof(struct relocation_info));
- r = p;
- }
- relocations = 0;
-}
-
-static void destroy_function_allocs(int and_free) {
- struct function_alloc *a = allocs;
- while (a != 0) {
- struct function_alloc *p = a->previous;
- if (and_free)
- heap_dealloc(a->start, a->bytes);
- heap_dealloc(a, sizeof(struct function_alloc));
- a = p;
- }
- allocs = 0;
-}
-
-const char *get_function_name() {
- return function_name;
-}
-
-int get_function_name_length() {
- return function_name_length;
-}
-
-//must be multiple of 4096.
-#define MIN_RX_SPACE (1 << 20)
-
-static void *rx_pointer;
-static int rx_space = 0;
-
-void (*end_function())() {
-
- void *function_start = 0;
-
- if (function_buffer_used <= rx_space) {
- function_start = rx_pointer;
- rx_pointer += function_buffer_used;
- rx_space -= function_buffer_used;
- }
-
- else {
- int to_allocate = ((function_buffer_length - 1) / 4096 + 1) * 4096;
- if (to_allocate < MIN_RX_SPACE)
- to_allocate = MIN_RX_SPACE;
- void *pages = map_pages(to_allocate / 4096);
- for (int i = 0; i < to_allocate; i += 4096)
- change_page_permissions(pages + i, 1, 1);
- function_start = pages;
- rx_pointer = pages + function_buffer_used;
- rx_space = to_allocate - function_buffer_used;
- }
-
- memcpy(function_start, function_buffer, function_buffer_used);
-
- for (struct relocation_info *r = relocations; r != 0; r = r->previous) {
- int32_t *at = function_start + r->offset_into_function;
- void *from = function_start + r->from_offset_into_function;
- *at = r->to - from;
- }
-
- destroy_relocations();
- destroy_function_allocs(0);
- function_name = 0;
-
- return function_start;
-
-}
-
-void abort_function() {
- destroy_relocations();
- destroy_function_allocs(1);
- if (function_name != 0) {
- heap_dealloc(function_name, function_name_length);
- function_name = 0;
- }
-}
-
-void *function_data_alloc(int bytes) {
- void *start = heap_alloc(bytes);
- struct function_alloc *a = heap_alloc(sizeof(struct function_alloc));
- a->start = start;
- a->bytes = bytes;
- a->previous = allocs;
- allocs = a;
- return start;
-}
-
-static void emit_bytes(const uint8_t *bytes, int count) {
-
- if (function_buffer_used + count > function_buffer_length) {
- int new_length = function_name_length * 2;
- while (function_buffer_used + count > function_buffer_length)
- new_length *= 2;
- void *new_buffer = heap_alloc(new_length);
- memcpy(new_buffer, function_buffer, function_buffer_used);
- heap_dealloc(function_buffer, function_buffer_length);
- function_buffer = new_buffer;
- function_buffer_length = new_length;
- }
-
- memcpy(function_buffer + function_buffer_used, bytes, count);
- function_buffer_used += count;
-
-}
-
-static struct relocation_info *new_relocation() {
- struct relocation_info *r = heap_alloc(sizeof(struct relocation_info));
- r->previous = relocations;
- relocations = r;
- return r;
-}
-
-void emit_ret() {
- uint8_t byte = 0xc3;
- emit_bytes(&byte, 1);
-}
-
-void emit_call(void (*to)()) {
- struct relocation_info *r = new_relocation();
- r->offset_into_function = function_buffer_used + 1;
- r->from_offset_into_function = function_buffer_used + 5;
- r->to = to;
- uint8_t bytes[5];
- bytes[0] = 0xe8;
- emit_bytes(bytes, 5);
-}
-
-void emit_mov_edi(int value) {
- uint8_t bytes[5];
- bytes[0] = 0xbf;
- *(int32_t *)&bytes[1] = value;
- emit_bytes(bytes, 5);
-}
-
-void emit_mov_esi(int value) {
- uint8_t bytes[5];
- bytes[0] = 0xbe;
- *(int32_t *)&bytes[1] = value;
- emit_bytes(bytes, 5);
-}
-
-void emit_mov_rdi(uint64_t value) {
- uint8_t bytes[10];
- bytes[0] = 0x48;
- bytes[1] = 0xbf;
- *(uint64_t *)&bytes[2] = value;
- emit_bytes(bytes, 10);
-}
diff --git a/src/user-apps/shell/assembler.h b/src/user-apps/shell/assembler.h
deleted file mode 100644
index f5f25f7..0000000
--- a/src/user-apps/shell/assembler.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Calcite, src/user-apps/shell/assembler.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
-
-//name is copied
-void start_function(const char *name, int name_length);
-
-const char *get_function_name();
-int get_function_name_length();
-
-//returns interpret
-void (*end_function())();
-void abort_function();
-
-void *function_data_alloc(int bytes);
-
-void emit_ret();
-void emit_call(void (*to)());
-void emit_mov_edi(int value);
-void emit_mov_esi(int value);
-void emit_mov_rdi(uint64_t value);
diff --git a/src/user-apps/shell/builtin-words.c b/src/user-apps/shell/builtin-words.c
deleted file mode 100644
index f177c44..0000000
--- a/src/user-apps/shell/builtin-words.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/* Calcite, src/user-apps/shell/builtin-words.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 "builtin-words.h"
-#include "assembler.h"
-#include "runtime.h"
-
-static void words_interpret() {
- print_all_words();
-}
-
-static void dup_interpret() {
- int x = pop();
- push(x);
- push(x);
-}
-
-static void drop_interpret() {
- pop();
-}
-
-static void swap_interpret() {
- int y = pop();
- int x = pop();
- push(y);
- push(x);
-}
-
-static void rot_interpret() {
- int z = pop();
- int y = pop();
- int x = pop();
- push(y);
- push(z);
- push(x);
-}
-
-static void plus_interpret() {
- int y = pop();
- int x = pop();
- push(x + y);
-}
-
-static void minus_interpret() {
- int y = pop();
- int x = pop();
- push(x - y);
-}
-
-static void times_interpret() {
- int y = pop();
- int x = pop();
- push(x * y);
-}
-
-static void divide_interpret() {
- int y = pop();
- int x = pop();
- if (y == 0)
- error("division by zero");
- push(x / y);
-}
-
-static void mod_interpret() {
- int y = pop();
- int x = pop();
- if (y == 0)
- error("division by zero");
- push(x % y);
-}
-
-static void print_integer(int x) {
-
- if (x == 0) {
- terminal_write("0 ", 2);
- return;
- }
-
- int is_negative;
- if (x < 0) {
- x = -x;
- is_negative = 1;
- }
- else
- is_negative = 0;
-
- char text[12];
- text[11] = ' ';
- int text_ptr = 11;
- while (x > 0) {
- --text_ptr;
- text[text_ptr] = '0' + x % 10;
- x /= 10;
- }
-
- if (is_negative) {
- --text_ptr;
- text[text_ptr] = '-';
- }
-
- terminal_write(&text[text_ptr], 12 - text_ptr);
-
-}
-
-static void dot_s_interpret() {
- int *stack_pointer = get_data_stack_pointer();
- int *stack_top = get_data_stack_top();
- for (int *i = stack_top - 1; i >= stack_pointer; --i)
- print_integer(*i);
-}
-
-static void dot_interpret() {
- print_integer(pop());
-}
-
-static void dot_quote_interpret() {
- const char *start;
- int length;
- read_until('"', &start, &length);
- terminal_write(start, length);
-}
-
-static void dot_quote_compile() {
- const char *start;
- int length;
- read_until('"', &start, &length);
- void *address = function_data_alloc(length);
- memcpy(address, start, length);
- emit_mov_rdi((uint64_t)address);
- emit_mov_esi(length);
- emit_call((void *)&terminal_write);
-}
-
-static void cr_interpret() {
- terminal_write("\n", 1);
-}
-
-static void colon_interpret() {
-
- const char *name_start;
- int name_length;
- read_input_word(&name_start, &name_length);
- if (name_length == 0)
- error("colon at end of input");
-
- start_function(name_start, name_length);
- compiling = 1;
-
-}
-
-static void semicolon_compile() {
-
- emit_ret();
-
- const char *function_name = get_function_name();
- int function_name_length = get_function_name_length();
- void (*interpret)() = end_function();
-
- struct word *word = new_word();
- word->name_length = function_name_length;
- word->name = function_name;
- word->interpret = interpret;
- word->compile = COMPILE_EMIT_CALL;
-
- compiling = 0;
-
-}
-
-static void add(const char *name, void (*interpret)(), void (*compile)()) {
- int name_length = 0;
- while (name[name_length] != 0)
- ++name_length;
- struct word *w = new_word();
- w->name_length = name_length;
- w->name = name;
- w->interpret = interpret;
- w->compile = compile;
-}
-
-static void add_compile_only(const char *name, void (*compile)()) {
- add(name, INTERPRET_ERROR, compile);
-}
-
-static void add_interpret_only(const char *name, void (*interpret)()) {
- add(name, interpret, COMPILE_ERROR);
-}
-
-static void add_emit_call(const char *name, void (*interpret)()) {
- add(name, interpret, COMPILE_EMIT_CALL);
-}
-
-void add_builtin_words() {
- add_emit_call( "words", &words_interpret);
- add_emit_call( "dup", &dup_interpret);
- add_emit_call( "drop", &drop_interpret);
- add_emit_call( "swap", &swap_interpret);
- add_emit_call( "rot", &rot_interpret);
- add_emit_call( "+", &plus_interpret);
- add_emit_call( "-", &minus_interpret);
- add_emit_call( "*", ×_interpret);
- add_emit_call( "/", ÷_interpret);
- add_emit_call( "mod", &mod_interpret);
- add_emit_call( ".s", &dot_s_interpret);
- add_emit_call( ".", &dot_interpret);
- add( ".\"", &dot_quote_interpret, &dot_quote_compile);
- add_emit_call( "cr", &cr_interpret);
- add_interpret_only(":", &colon_interpret);
- add_compile_only( ";", &semicolon_compile);
-}
diff --git a/src/user-apps/shell/builtin-words.h b/src/user-apps/shell/builtin-words.h
deleted file mode 100644
index 335fb4d..0000000
--- a/src/user-apps/shell/builtin-words.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Calcite, src/user-apps/shell/builtin-words.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 add_builtin_words();
diff --git a/src/user-apps/shell/libraries.txt b/src/user-apps/shell/libraries.txt
deleted file mode 100644
index 8503661..0000000
--- a/src/user-apps/shell/libraries.txt
+++ /dev/null
@@ -1 +0,0 @@
-calcite
diff --git a/src/user-apps/shell/main.c b/src/user-apps/shell/main.c
deleted file mode 100644
index bf7435e..0000000
--- a/src/user-apps/shell/main.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Calcite, src/user-apps/shell/main.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 "builtin-words.h"
-#include "runtime.h"
-
-#include
-
-void main() {
-
- init_terminal();
- save_return_stack();
- create_data_stack();
- add_builtin_words();
-
- terminal_write("words: ", 7);
- print_all_words();
- terminal_write("\n", 1);
-
- main_loop();
-
-}
diff --git a/src/user-apps/shell/runtime.asm b/src/user-apps/shell/runtime.asm
deleted file mode 100644
index 6f3fceb..0000000
--- a/src/user-apps/shell/runtime.asm
+++ /dev/null
@@ -1,40 +0,0 @@
- ; Calcite, src/user-apps/shell/runtime.asm
- ; 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 .
-
-
-bits 64
-default rel
-
-section .bss
-
-saved_return_stack:
- resq 1
-
-section .text
-
-global save_return_stack
-save_return_stack:
- mov rax, rsp
- and ax, 0xf000
- add rax, 0x1000
- mov qword [saved_return_stack], rax
- ret
-
-global restore_return_stack
-restore_return_stack:
- mov rsp, qword [saved_return_stack]
- push qword 0
- jmp rdi
diff --git a/src/user-apps/shell/runtime.c b/src/user-apps/shell/runtime.c
deleted file mode 100644
index b983390..0000000
--- a/src/user-apps/shell/runtime.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Calcite, src/user-apps/shell/runtime.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 "assembler.h"
-#include "runtime.h"
-
-#include
-#include
-
-static int *data_stack_bottom;
-static int *data_stack_top;
-static int *data_stack_pointer;
-
-#define INITIAL_DATA_STACK_INTS 1024
-
-void create_data_stack() {
- data_stack_bottom = heap_alloc(INITIAL_DATA_STACK_INTS * sizeof(int));
- data_stack_top = data_stack_bottom + INITIAL_DATA_STACK_INTS;
- data_stack_pointer = data_stack_top;
-}
-
-void push(int value) {
-
- if (data_stack_pointer == data_stack_bottom) {
- int old_size = data_stack_top - data_stack_bottom;
- int new_size = old_size * 2;
- int *new_stack = heap_alloc(new_size * sizeof(int));
- memcpy(new_stack + old_size, data_stack_bottom, old_size * sizeof(int));
- heap_dealloc(data_stack_bottom, old_size * sizeof(int));
- data_stack_bottom = new_stack;
- data_stack_top = new_stack + new_size;
- data_stack_pointer = new_stack + old_size;
- }
-
- *--data_stack_pointer = value;
-
-}
-
-int pop() {
-
- if (data_stack_pointer == data_stack_top)
- error("stack underflow");
-
- return *(data_stack_pointer++);
-
-}
-
-int *get_data_stack_pointer() {
- return data_stack_pointer;
-}
-
-int *get_data_stack_top() {
- return data_stack_top;
-}
-
-//null-terminated
-static const char *input_buffer;
-static const char *input_pointer;
-
-void set_input(const char *buffer) {
- input_buffer = buffer;
- input_pointer = buffer;
-}
-
-static int is_ws(char ch) {
- return ch == ' ';
-}
-
-void read_input_word(const char **start_out, int *length_out) {
- const char *start = input_pointer;
- while (start[0] != 0 && is_ws(start[0]))
- ++start;
- int length = 0;
- while (start[length] != 0 && !is_ws(start[length]))
- ++length;
- *start_out = start;
- *length_out = length;
- if (start[length] == 0)
- input_pointer = &start[length];
- else
- input_pointer = &start[length + 1];
-}
-
-void read_until(char end, const char **start_out, int *length_out) {
- const char *start = input_pointer;
- int length = 0;
- while (start[length] != 0 && start[length] != end)
- ++length;
- if (start[length] == 0)
- error("unexpected eof");
- *start_out = start;
- *length_out = length;
- input_pointer = &start[length + 1];
-}
-
-static const struct word *dictionary = 0;
-
-const struct word *look_up_word(int name_length, const char *name) {
- for (const struct word *word = dictionary; word != 0; word = word->previous) {
- if (word->name_length == name_length) {
- for (int i = 0; i < name_length; ++i)
- if (word->name[i] != name[i])
- goto next_word;
- return word;
- }
- next_word:
- ;
- }
- return 0;
-}
-
-struct word *new_word() {
- struct word *word = heap_alloc(sizeof(struct word));
- word->previous = dictionary;
- dictionary = word;
- return word;
-}
-
-void print_all_words() {
- for (const struct word *w = dictionary; w != 0; w = w->previous) {
- terminal_write(w->name, w->name_length);
- terminal_write(" ", 1);
- }
-}
-
-[[noreturn]] void error(const char *message) {
- terminal_write("*** error: ", 11);
- int length = 0;
- while (message[length] != 0)
- ++length;
- terminal_write(message, length);
- terminal_write("\n", 1);
- abort_function();
- restore_return_stack(&main_loop);
-}
-
-static void interpret_all() {
-
- while (1) {
-
- const char *start;
- int length;
- read_input_word(&start, &length);
- if (length == 0)
- return;
-
- const struct word *word = look_up_word(length, start);
- if (word != 0) {
- if (compiling) {
- if (word->compile == COMPILE_ERROR)
- error("interpret-only word in compile context");
- else if (word->compile == COMPILE_EMIT_CALL)
- emit_call(word->interpret);
- else
- (*word->compile)();
- }
- else {
- if (word->interpret == INTERPRET_ERROR)
- error("compile-only word in interpret context");
- else
- (*word->interpret)();
- }
- continue;
- }
-
- int is_negative;
- if (start[0] == '-') {
- is_negative = 1;
- ++start;
- --length;
- }
- else
- is_negative = 0;
-
- int abs = 0;
- for (int i = 0; i < length; ++i) {
- if (start[i] < '0' || start[i] > '9')
- error("unknown word");
- abs = abs * 10 + start[i] - '0';
- }
-
- int x = is_negative ? -abs : abs;
-
- if (compiling) {
- emit_mov_edi(x);
- emit_call((void *)&push);
- }
- else
- push(x);
-
- }
-
-}
-
-[[noreturn]] void main_loop() {
- compiling = 0;
- while (1) {
- terminal_write("> ", 2);
- set_input(terminal_readline());
- interpret_all();
- }
-}
-
-int compiling;
diff --git a/src/user-apps/shell/runtime.h b/src/user-apps/shell/runtime.h
deleted file mode 100644
index 58dffe4..0000000
--- a/src/user-apps/shell/runtime.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Calcite, src/user-apps/shell/runtime.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 save_return_stack();
-//and_call should not return.
-[[noreturn]] void restore_return_stack(void (*and_call)());
-
-void create_data_stack();
-void push(int value);
-int pop();
-
-int *get_data_stack_pointer();
-int *get_data_stack_top();
-
-//null-terminated. not copied.
-void set_input(const char *buffer);
-
-//length_out is set to 0 on eof. skips all whitespace before and up to one character after.
-void read_input_word(const char **start_out, int *length_out);
-
-//errors on eof before end. skips end. end not included in length.
-void read_until(char end, const char **start_out, int *length_out);
-
-struct word {
- int name_length;
- const char *name;
- void (*interpret)();
- void (*compile)();
- const struct word *previous;
-};
-
-#define INTERPRET_ERROR ((void (*)())-1)
-
-#define COMPILE_ERROR ((void (*)())-1)
-#define COMPILE_EMIT_CALL ((void (*)())-2)
-
-//returns 0 if not found. name does not need to be null-terminated.
-const struct word *look_up_word(int name_length, const char *name);
-struct word *new_word();
-
-void print_all_words();
-
-[[noreturn]] void error(const char *message);
-
-extern int compiling;
-
-[[noreturn]] void main_loop();
diff --git a/src/user-apps/terminal/libraries.txt b/src/user-apps/terminal/libraries.txt
deleted file mode 100644
index 12c28c2..0000000
--- a/src/user-apps/terminal/libraries.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-calcite
-flanterm
diff --git a/src/user-apps/terminal/terminal.c b/src/user-apps/terminal/terminal.c
deleted file mode 100644
index 606ce51..0000000
--- a/src/user-apps/terminal/terminal.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/* 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
-#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 char keytable[2048];
-
-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);
-
- }
-
-}
-
-static int flags = 0;
-static int entering_caps_lock = 0;
-static int entering_num_lock = 0;
-
-[[noreturn]] static void input_thread(uint64_t) {
- while (1) {
-
- int key = wait_for_keyboard_packet();
- int shift = flags & (TKF_LEFT_SHIFT | TKF_RIGHT_SHIFT);
- int caps = flags & TKF_CAPS_LOCK;
- int num = flags & TKF_NUM_LOCK;
- char printable = keytable[(key & 0xff) | (shift ? 0x100 : 0x0) | (caps ? 0x200 : 0x0) | (num ? 0x400 : 0x0)];
-
- if (key == KEY_LEFT_SHIFT)
- flags &= ~TKF_LEFT_SHIFT;
- else if (key == KEY_RIGHT_SHIFT)
- flags &= ~TKF_RIGHT_SHIFT;
- else if (key == KEY_LEFT_CTRL)
- flags &= ~TKF_LEFT_CTRL;
- else if (key == KEY_RIGHT_CTRL)
- flags &= ~TKF_RIGHT_CTRL;
- else if (key == KEY_LEFT_ALT)
- flags &= ~TKF_LEFT_ALT;
- else if (key == KEY_RIGHT_ALT)
- flags &= ~TKF_RIGHT_ALT;
- else if (key == KEY_CAPS_LOCK) {
- if (!entering_caps_lock)
- flags &= ~TKF_CAPS_LOCK;
- else
- entering_caps_lock = 0;
- }
- else if (key == KEY_NUM_LOCK) {
- if (!entering_num_lock)
- flags &= ~TKF_NUM_LOCK;
- else
- entering_num_lock = 0;
- }
- else if (key == (KEY_FLAG_MAKE | KEY_LEFT_SHIFT))
- flags |= TKF_LEFT_SHIFT;
- else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_SHIFT))
- flags |= TKF_RIGHT_SHIFT;
- else if (key == (KEY_FLAG_MAKE | KEY_LEFT_CTRL))
- flags |= TKF_LEFT_CTRL;
- else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_CTRL))
- flags |= TKF_RIGHT_CTRL;
- else if (key == (KEY_FLAG_MAKE | KEY_LEFT_ALT))
- flags |= TKF_LEFT_ALT;
- else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_ALT))
- flags |= TKF_RIGHT_ALT;
- else if (key == (KEY_FLAG_MAKE | KEY_CAPS_LOCK)) {
- if (!(flags & TKF_CAPS_LOCK)) {
- flags |= TKF_CAPS_LOCK;
- entering_caps_lock = 1;
- }
- }
- else if (key == (KEY_FLAG_MAKE | KEY_NUM_LOCK)) {
- if (!(flags & TKF_NUM_LOCK)) {
- flags |= TKF_NUM_LOCK;
- entering_num_lock = 1;
- }
- }
-
- int packet = flags | (key << 8) | printable;
- ipc_send_dgram(input_handle, &packet, sizeof(int));
-
- }
-}
-
-void main() {
-
- file_handle_t keytable_handle;
- if (open_file("root://calcite/qwerty.bin", &keytable_handle) == FAR_SUCCESS) {
- struct read_file_parameter param;
- param.handle = keytable_handle;
- param.buffer = keytable;
- param.start = 0;
- param.bytes = 2048;
- read_file(¶m);
- close_file(keytable_handle);
- }
-
- 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
deleted file mode 100644
index 516f1a1..0000000
--- a/src/user-libs/calcite/dispatch.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/* 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/entry.c b/src/user-libs/calcite/entry.c
deleted file mode 100644
index 71dd204..0000000
--- a/src/user-libs/calcite/entry.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Calcite, src/user-libs/calcite/entry.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
-
-void main();
-
-[[noreturn]] void _start() {
- main();
- end_thread();
-}
diff --git a/src/user-libs/calcite/file-streams.c b/src/user-libs/calcite/file-streams.c
deleted file mode 100644
index 3f3bdd5..0000000
--- a/src/user-libs/calcite/file-streams.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/* Calcite, src/user-libs/calcite/file-streams.c
- * Copyright 2025 Benji Dial
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-
-static uint64_t min(uint64_t a, uint64_t b) {
- return a < b ? a : b;
-}
-
-//in bytes
-#define FS_BUFFER_SIZE 8192
-
-struct file_stream {
-
- file_handle_t handle;
-
- //both in bytes
- uint64_t length;
- uint64_t pointer;
-
- int is_there_buffer;
- //in bytes
- uint64_t buffer_start;
- uint8_t buffer[FS_BUFFER_SIZE];
-
-};
-
-enum fs_access_result open_file_stream(const char *path, struct file_stream **file_stream_out) {
-
- file_handle_t handle;
- enum fs_access_result result = open_file(path, &handle);
- if (result != FAR_SUCCESS)
- return result;
-
- uint64_t length;
- result = get_file_size(handle, &length);
- if (result != FAR_SUCCESS) {
- close_file(handle);
- return result;
- }
-
- *file_stream_out = heap_alloc(sizeof(struct file_stream));
- (*file_stream_out)->handle = handle;
- (*file_stream_out)->length = length;
- (*file_stream_out)->pointer = 0;
- (*file_stream_out)->is_there_buffer = 0;
- return FAR_SUCCESS;
-
-}
-
-void close_file_stream(struct file_stream *file_stream) {
- close_file(file_stream->handle);
- heap_dealloc(file_stream, sizeof(struct file_stream));
-}
-
-enum fs_access_result get_in_buffer(struct file_stream *file_stream, uint64_t pointer) {
-
- uint64_t bstart = (pointer / FS_BUFFER_SIZE) * FS_BUFFER_SIZE;
- uint64_t bbytes = min(FS_BUFFER_SIZE, file_stream->length - bstart);
-
- if (file_stream->is_there_buffer && file_stream->buffer_start == bstart)
- return FAR_SUCCESS;
-
- enum fs_access_result result =
- read_file_splat(file_stream->handle, file_stream->buffer, bstart, bbytes);
-
- if (result == FAR_SUCCESS) {
- file_stream->is_there_buffer = 1;
- file_stream->buffer_start = bstart;
- return FAR_SUCCESS;
- }
-
- file_stream->is_there_buffer = 0;
- return result;
-
-}
-
-enum fs_access_result read_file_stream(struct file_stream *file_stream, void *buffer, uint64_t bytes) {
-
- if (bytes == 0)
- return FAR_SUCCESS;
-
- if (file_stream->pointer + bytes > file_stream->length)
- return FAR_OUT_OF_BOUNDS;
-
- enum fs_access_result result = get_in_buffer(file_stream, file_stream->pointer);
- if (result != FAR_SUCCESS)
- return result;
-
- uint64_t in_buffer = min(bytes, FS_BUFFER_SIZE - (file_stream->pointer - file_stream->buffer_start));
- memcpy(
- buffer,
- file_stream->buffer + file_stream->pointer - file_stream->buffer_start,
- in_buffer);
-
- uint64_t pointer = file_stream->pointer + in_buffer;
- uint64_t left = bytes - in_buffer;
- void *buffer_pointer = buffer + in_buffer;
-
- while (left >= FS_BUFFER_SIZE) {
- enum fs_access_result result =
- read_file_splat(file_stream->handle, buffer_pointer, pointer, FS_BUFFER_SIZE);
- if (result != FAR_SUCCESS)
- return result;
- pointer += FS_BUFFER_SIZE;
- left -= FS_BUFFER_SIZE;
- buffer_pointer += FS_BUFFER_SIZE;
- }
-
- if (left > 0) {
- enum fs_access_result result = get_in_buffer(file_stream, pointer);
- if (result != FAR_SUCCESS)
- return result;
- memcpy(buffer_pointer, file_stream->buffer, left);
- }
-
- file_stream->pointer += bytes;
- return FAR_SUCCESS;
-
-}
-
-int read_file_stream_byte(struct file_stream *file_stream) {
-
- if (file_stream->pointer == file_stream->length)
- return -1;
-
- enum fs_access_result result = get_in_buffer(file_stream, file_stream->pointer);
- if (result != FAR_SUCCESS)
- return -1;
-
- return file_stream->buffer[file_stream->pointer++ - file_stream->buffer_start];
-
-}
diff --git a/src/user-libs/calcite/format.c b/src/user-libs/calcite/format.c
deleted file mode 100644
index 650e9e9..0000000
--- a/src/user-libs/calcite/format.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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
deleted file mode 100644
index 766bbf5..0000000
--- a/src/user-libs/calcite/memory.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/* Calcite, src/user-libs/calcite/memory.c
- * 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
- * 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
-
-struct block_list_entry {
- struct block_list_entry *prev;
- struct block_list_entry *next;
-};
-
-struct block_list {
- struct block_list_entry *first_entry;
- struct block_list_entry *last_entry;
-};
-
-#define BASE_BLOCK_SIZE 16ULL
-#define BLOCK_LEVELS 32
-//BASE_BLOCK_SIZE << MIN_MAP_BLOCK_LEVEL must be at least 4096
-#define MIN_MAP_BLOCK_LEVEL 16
-
-//block_lists[n] is a list of free blocks of size BASE_BLOCK_SIZE * 2^n bytes
-static struct block_list block_lists[BLOCK_LEVELS];
-
-void *heap_alloc(uint64_t bytes) {
-
- bytes = ((bytes - 1) / BASE_BLOCK_SIZE + 1) * BASE_BLOCK_SIZE;
-
- //TODO: handle this case?
- if (bytes > (BASE_BLOCK_SIZE << (BLOCK_LEVELS - 1)))
- return 0;
-
- int block_n_minimum = 0;
- uint64_t block_size_minimum = BASE_BLOCK_SIZE;
- while (block_size_minimum < bytes) {
- ++block_n_minimum;
- block_size_minimum *= 2;
- }
-
- int block_n = block_n_minimum;
- uint64_t block_size = block_size_minimum;
- while (1) {
- if (block_n == BLOCK_LEVELS) {
- block_n = -1;
- break;
- }
- if (block_lists[block_n].first_entry != 0)
- break;
- ++block_n;
- block_size *= 2;
- }
-
- if (block_n == -1) {
- block_n = block_n_minimum > MIN_MAP_BLOCK_LEVEL ? block_n_minimum : MIN_MAP_BLOCK_LEVEL;
- block_size = BASE_BLOCK_SIZE << block_n;
- void *start = map_pages(block_size / 4096);
- if (block_size != bytes)
- heap_dealloc(start + bytes, block_size - bytes);
- return start;
- }
-
- struct block_list_entry *e = block_lists[block_n].first_entry;
- if (e->next)
- e->next->prev = 0;
- else
- block_lists[block_n].last_entry = 0;
- block_lists[block_n].first_entry = e->next;
-
- void *start = e;
- if (block_size != bytes)
- heap_dealloc(start + bytes, block_size - bytes);
-
- return start;
-
-}
-
-static void heap_dealloc_aligned(void *start, uint64_t block_n, uint64_t block_size) {
-recurse:
-
- if (block_size != BLOCK_LEVELS - 1) {
-
- void *buddy = (void *)((uint64_t)start ^ block_size);
-
- for (struct block_list_entry *e = block_lists[block_n].first_entry; e != 0; e = e->next)
- if (e == buddy) {
- if (e->next)
- e->next->prev = e->prev;
- else
- block_lists[block_n].last_entry = e->prev;
- if (e->prev)
- e->prev->next = e->next;
- else
- block_lists[block_n].first_entry = e->next;
- start = (void *)((uint64_t)start & ~block_size);
- ++block_n;
- block_size *= 2;
- goto recurse;
- }
-
- }
-
- if (block_lists[block_n].first_entry == 0) {
- struct block_list_entry *e = start;
- e->next = 0;
- e->prev = 0;
- block_lists[block_n].first_entry = e;
- block_lists[block_n].last_entry = e;
- return;
- }
-
- if (start < (void *)block_lists[block_n].first_entry) {
- struct block_list_entry *e = start;
- e->next = block_lists[block_n].first_entry;
- e->prev = 0;
- block_lists[block_n].first_entry->prev = e;
- block_lists[block_n].first_entry = e;
- return;
- }
-
- if (start > (void *)block_lists[block_n].last_entry) {
- struct block_list_entry *e = start;
- e->next = 0;
- e->prev = block_lists[block_n].last_entry;
- block_lists[block_n].last_entry->next = e;
- block_lists[block_n].last_entry = e;
- return;
- }
-
- struct block_list_entry *after = block_lists[block_n].first_entry;
- while ((void *)after < start)
- after = after->next;
- struct block_list_entry *before = after->prev;
- struct block_list_entry *e = start;
-
- e->next = after;
- e->prev = before;
- after->prev = e;
- before->next = e;
-
-}
-
-void heap_dealloc(void *start, uint64_t bytes) {
-
- bytes = ((bytes - 1) / BASE_BLOCK_SIZE + 1) * BASE_BLOCK_SIZE;
- //bytes is guaranteed to be at most BASE_BLOCK_SIZE << (BLOCK_LEVELS - 1)
-
- int block_n = 0;
- uint64_t block_size = BASE_BLOCK_SIZE;
-
- while (block_size <= bytes) {
- if ((uint64_t)start & block_size) {
- heap_dealloc_aligned(start, block_n, block_size);
- start += block_size;
- bytes -= block_size;
- }
- ++block_n;
- block_size *= 2;
- }
-
- while (block_n >= 0) {
- if (block_size <= bytes) {
- heap_dealloc_aligned(start, block_n, block_size);
- start += block_size;
- bytes -= block_size;
- }
- --block_n;
- block_size /= 2;
- }
-
-}
-
-void memcpy(void *to, const void *from, uint64_t bytes) {
- uint8_t *to8 = to;
- const uint8_t *from8 = from;
- 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.asm b/src/user-libs/calcite/syscalls.asm
deleted file mode 100644
index 0c00a5d..0000000
--- a/src/user-libs/calcite/syscalls.asm
+++ /dev/null
@@ -1,28 +0,0 @@
- ; Calcite, src/user-libs/calcite/syscalls.asm
- ; Copyright 2025 Benji Dial
- ;
- ; This program is free software: you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, either version 3 of the License, or
- ; (at your option) any later version.
- ;
- ; This program is distributed in the hope that it will be useful, but
- ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- ; for more details.
- ;
- ; You should have received a copy of the GNU General Public License along
- ; with this program. If not, see .
-
-
-bits 64
-default rel
-
-section .text
-
-;referenced in syscalls.c
-global do_syscall
-do_syscall:
- mov rax, rcx
- syscall
- ret
diff --git a/src/user-libs/calcite/syscalls.c b/src/user-libs/calcite/syscalls.c
deleted file mode 100644
index ad6bba0..0000000
--- a/src/user-libs/calcite/syscalls.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/* Calcite, src/user-libs/calcite/syscalls.c
- * 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
- * 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
-
-//defined in syscalls.asm
-uint64_t do_syscall
- (uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t number);
-
-[[noreturn]] void end_thread() {
- do_syscall(0, 0, 0, SYSCALL_END_THREAD);
- __builtin_unreachable();
-}
-
-void map_framebuffer(struct framebuffer_info *info_out) {
- do_syscall((uint64_t)info_out, 0, 0, SYSCALL_MAP_FRAMEBUFFER);
-}
-
-enum fs_access_result open_file(const char *path, file_handle_t *handle_out) {
- return do_syscall((uint64_t)path, (uint64_t)handle_out, 1, SYSCALL_OPEN_FILE);
-}
-
-void close_file(file_handle_t handle) {
- do_syscall(handle, 0, 0, SYSCALL_CLOSE_FILE);
-}
-
-enum fs_access_result get_file_size(file_handle_t handle, uint64_t *bytes_out) {
- return do_syscall(handle, (uint64_t)bytes_out, 0, SYSCALL_GET_FILE_SIZE);
-}
-
-enum fs_access_result read_file(struct read_file_parameter *parameter) {
- return do_syscall((uint64_t)parameter, 0, 0, SYSCALL_READ_FILE);
-}
-
-enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes) {
- struct read_file_parameter parameter = { .handle = handle, .buffer = buffer, .start = start, .bytes = bytes };
- return do_syscall((uint64_t)¶meter, 0, 0, SYSCALL_READ_FILE);
-}
-
-void wait_for_mouse_packet(struct mouse_packet *packet_out) {
- do_syscall((uint64_t)packet_out, 0, 0, SYSCALL_WAIT_FOR_MOUSE_PACKET);
-}
-
-void *map_pages(uint64_t count) {
- return (void *)do_syscall(count, 0, 0, SYSCALL_MAP_PAGES);
-}
-
-void sleep_ms(uint64_t ms_to_sleep) {
- do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS);
-}
-
-int start_elf(const char *path, const struct process_start_info *info) {
- return do_syscall((uint64_t)path, (uint64_t)info, 0, SYSCALL_START_ELF);
-}
-
-enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out) {
- return do_syscall(
- (uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_RECEIVER);
-}
-
-enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out) {
- return do_syscall(
- (uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_SENDER);
-}
-
-enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) {
- return do_syscall(
- handle, (uint64_t)buffer, (uint64_t)bytes, SYSCALL_IPC_RECEIVE_DGRAM);
-}
-
-enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes) {
- return do_syscall(handle, (uint64_t)data, bytes, SYSCALL_IPC_SEND_DGRAM);
-}
-
-void create_thread(void (*f)(uint64_t x), uint64_t x) {
- do_syscall((uint64_t)f, x, 0, SYSCALL_CREATE_THREAD);
-}
-
-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);
-}
-
-void change_page_permissions(void *vma, int writable, int executable) {
- do_syscall((uint64_t)vma, writable, executable, SYSCALL_CHANGE_PAGE_PERMISSIONS);
-}
-
-int wait_for_keyboard_packet() {
- return do_syscall(0, 0, 0, SYSCALL_WAIT_FOR_KEYBOARD_PACKET);
-}
diff --git a/src/user-libs/calcite/terminal.c b/src/user-libs/calcite/terminal.c
deleted file mode 100644
index 935be1b..0000000
--- a/src/user-libs/calcite/terminal.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/* 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
-#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 uint8_t *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;
-
- uint8_t *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 void *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 void *data, int bytes) {
-
- if (bytes == 0)
- return;
-
- const uint8_t *data8 = (const uint8_t *)data;
-
- int last_newline = bytes - 1;
- while (last_newline >= 0 && data8[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_flush() {
- if (output_buffer_used != 0) {
- send_output(output_buffer, output_buffer_used);
- output_buffer_used = 0;
- }
-}
-
-int terminal_readkey() {
-
- if (!set_up_terminal_input)
- return -1;
-
- terminal_flush();
- int result;
- int bytes = sizeof(int);
- ipc_receive_dgram(input_handle, &result, &bytes);
- //TODO: handle case where terminal misbehaves and sends more than sizeof(int)
-
- return result;
-
-}
-
-static char *readline_buffer = 0;
-static int readline_buffer_length = 0;
-static int readline_buffer_used = 0;
-static int readline_cursor = 0;
-
-static void room_for_one_more() {
-
- if (readline_buffer == 0) {
- readline_buffer_length = 1024;
- readline_buffer = heap_alloc(readline_buffer_length);
- }
-
- else if (readline_buffer_used == readline_buffer_length) {
- char *new_buffer = heap_alloc(readline_buffer_length * 2);
- memcpy(new_buffer, readline_buffer, readline_buffer_length);
- heap_dealloc(readline_buffer, readline_buffer_length);
- readline_buffer = new_buffer;
- readline_buffer_length *= 2;
- }
-
-}
-
-const char *terminal_readline() {
-
- readline_buffer_used = 0;
- readline_cursor = 0;
-
- while (1) {
-
- int key_packet = terminal_readkey();
- if (!(key_packet & (KEY_FLAG_MAKE << 8)) ||
- (key_packet & (TKF_LEFT_CTRL | TKF_RIGHT_CTRL | TKF_LEFT_ALT | TKF_RIGHT_ALT)))
- continue;
- int key = (key_packet >> 8) & 0xff;
- int num_lock = key_packet & TKF_NUM_LOCK;
-
- if (key == KEY_LEFT || (!num_lock && key == KEY_NUM_4)) {
- if (readline_cursor != 0) {
- --readline_cursor;
- terminal_write("\x1b[D", 3);
- }
- }
-
- else if (key == KEY_RIGHT || (!num_lock && key == KEY_NUM_6)) {
- if (readline_cursor != readline_buffer_used) {
- ++readline_cursor;
- terminal_write("\x1b[C", 3);
- }
- }
-
- else if (key == KEY_BACKSPACE) {
- if (readline_cursor != 0) {
- for (int i = readline_cursor; i < readline_buffer_used; ++i)
- readline_buffer[i - 1] = readline_buffer[i];
- --readline_cursor;
- --readline_buffer_used;
- terminal_write("\x1b[D", 3);
- terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
- terminal_write(" ", 1);
- for (int i = readline_cursor; i < readline_buffer_used + 1; ++i)
- terminal_write("\x1b[D", 3);
- }
- }
-
- else if (key == KEY_DELETE || (!num_lock && key == KEY_NUM_PERIOD)) {
- if (readline_cursor != readline_buffer_used) {
- ++readline_cursor;
- for (int i = readline_cursor; i < readline_buffer_used; ++i)
- readline_buffer[i - 1] = readline_buffer[i];
- --readline_cursor;
- --readline_buffer_used;
- terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
- terminal_write(" ", 1);
- for (int i = readline_cursor; i < readline_buffer_used + 1; ++i)
- terminal_write("\x1b[D", 3);
- }
- }
-
- else if (key == KEY_ENTER || key == KEY_NUM_ENTER) {
- room_for_one_more();
- readline_buffer[readline_buffer_used] = 0;
- for (int i = readline_cursor; i < readline_buffer_used; ++i)
- terminal_write("\x1b[C", 3);
- terminal_write("\n", 1);
- return readline_buffer;
- }
-
- else if (key == KEY_TAB)
- continue;
-
- else if (key == KEY_HOME || (!num_lock && key == KEY_NUM_7)) {
- for (int i = 0; i < readline_cursor; ++i)
- terminal_write("\x1b[D", 3);
- readline_cursor = 0;
- }
-
- else if (key == KEY_END || (!num_lock && key == KEY_NUM_1)) {
- for (int i = readline_cursor; i < readline_buffer_used; ++i)
- terminal_write("\x1b[C", 3);
- readline_cursor = readline_buffer_used;
- }
-
- else {
- char value = key_packet & 0xff;
- if (value != 0) {
- room_for_one_more();
- for (int i = readline_buffer_used; i > readline_cursor; --i)
- readline_buffer[i] = readline_buffer[i - 1];
- readline_buffer[readline_cursor] = value;
- ++readline_buffer_used;
- terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
- ++readline_cursor;
- for (int i = readline_buffer_used; i > readline_cursor; --i)
- terminal_write("\x1b[D", 3);
- }
- }
-
- }
-
-}
diff --git a/src/user-libs/silver/image.c b/src/user-libs/silver/image.c
deleted file mode 100644
index c78eb18..0000000
--- a/src/user-libs/silver/image.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Calcite, src/user-libs/silver/image.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
-
-void create_image(int width, int height, struct image **image_out) {
- *image_out = heap_alloc(sizeof(struct image));
- (*image_out)->width = width;
- (*image_out)->height = height;
- (*image_out)->pixels = heap_alloc(width * height * sizeof(struct pixel));
-}
-
-void destroy_image(struct image *image) {
- heap_dealloc(image->pixels, image->width * image->height * sizeof(struct pixel));
- heap_dealloc(image, sizeof(struct image));
-}
diff --git a/src/user-libs/silver/pam.c b/src/user-libs/silver/pam.c
deleted file mode 100644
index 2d5625c..0000000
--- a/src/user-libs/silver/pam.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Calcite, src/user-libs/silver/pam.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
-
-static int starts_with(const char *big_string, const char *little_string) {
- while (*little_string) {
- if (*big_string != *little_string)
- return 0;
- ++big_string;
- ++little_string;
- }
- return 1;
-}
-
-static int strequ(const char *str1, const char *str2) {
- while (1) {
- if (!*str1 && !*str2)
- return 1;
- if (*str1 != *str2)
- return 0;
- ++str1;
- ++str2;
- }
-}
-
-static int parse_uint(const char *str) {
- int i = 0;
- while (*str)
- i = i * 10 + *(str++) - '0';
- return i;
-}
-
-enum pam_tupltype {
- PTT_RGB_ALPHA
-};
-
-#define MAX_LINE_LENGTH 1023
-
-int load_pam(const char *path, struct image **image_out) {
-
- struct file_stream *stream;
- if (open_file_stream(path, &stream) != FAR_SUCCESS)
- return 0;
-
- if (read_file_stream_byte(stream) != 'P' ||
- read_file_stream_byte(stream) != '7' ||
- read_file_stream_byte(stream) != '\n') {
- close_file_stream(stream);
- return 0;
- }
-
- int width = -1, height = -1, depth = -1, maxval = -1, tupltype = -1;
-
- while (1) {
-
- char line[MAX_LINE_LENGTH + 1];
- int line_length = 0;
- while (1) {
- int c = read_file_stream_byte(stream);
- if (c == -1) {
- close_file_stream(stream);
- return 0;
- }
- if (c == '\n') {
- line[line_length] = 0;
- break;
- }
- if (line_length == MAX_LINE_LENGTH) {
- close_file_stream(stream);
- return 0;
- }
- line[line_length++] = c;
- }
-
- if (line[0] == '#')
- continue;
-
- if (starts_with(line, "WIDTH "))
- width = parse_uint(&line[6]);
- else if (starts_with(line, "HEIGHT "))
- height = parse_uint(&line[7]);
- else if (starts_with(line, "DEPTH "))
- depth = parse_uint(&line[6]);
- else if (starts_with(line, "MAXVAL "))
- maxval = parse_uint(&line[7]);
- else if (strequ(line, "TUPLTYPE RGB_ALPHA"))
- tupltype = PTT_RGB_ALPHA;
- else if (strequ(line, "ENDHDR"))
- break;
-
- }
-
- if (width == -1 || height == -1 || depth != 4 || maxval != 255 || tupltype != PTT_RGB_ALPHA) {
- close_file_stream(stream);
- return 0;
- }
-
- create_image(width, height, image_out);
-
- if (read_file_stream(
- stream,
- (*image_out)->pixels,
- width * height * 4) != FAR_SUCCESS) {
- close_file_stream(stream);
- destroy_image(*image_out);
- return 0;
- }
-
- close_file_stream(stream);
- return 1;
-
-}