diff --git a/compile_flags.txt b/compile_flags.txt
index fce5308..eb64b69 100644
--- a/compile_flags.txt
+++ b/compile_flags.txt
@@ -4,3 +4,5 @@
dependencies/limine
-I
include
+-D
+CALCITE_DEBUG
diff --git a/make-build.sh b/make-build.sh
index a6fb0c2..3630d18 100644
--- a/make-build.sh
+++ b/make-build.sh
@@ -3,23 +3,23 @@
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_EXTRA_FLAGS="-O0 -ggdb ${COMMON_CC_EXTRA_FLAGS}"
+ COMMON_CC_FLAGS="-O0 -ggdb -D CALCITE_DEBUG ${COMMON_CC_FLAGS}"
elif [ "$1" = release ]; then
- COMMON_CC_EXTRA_FLAGS="-O3 ${COMMON_CC_EXTRA_FLAGS}"
- COMMON_LD_EXTRA_FLAGS="-s ${COMMON_LD_EXTRA_FLAGS}"
+ 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.
exit
fi
-COMMON_CC_FLAGS="-std=c23 -ffreestanding -I include ${COMMON_CC_EXTRA_FLAGS}"
KERNEL_CC_FLAGS="-mno-sse -I dependencies/limine ${COMMON_CC_FLAGS}"
#in the future user code will be allowed to use sse
USER_CC_FLAGS="-mno-sse ${COMMON_CC_FLAGS}"
-COMMON_LD_FLAGS="${COMMON_LD_EXTRA_FLAGS}"
-
if [ -e build.ninja ]; then
echo build.ninja already exists.
exit
diff --git a/src/kernel/debug.c b/src/kernel/debug.c
new file mode 100644
index 0000000..72ad60f
--- /dev/null
+++ b/src/kernel/debug.c
@@ -0,0 +1,109 @@
+/* Calcite, src/kernel/debug.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"
+
+#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);
+ 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/panic.h b/src/kernel/debug.h
similarity index 63%
rename from src/kernel/panic.h
rename to src/kernel/debug.h
index fbd1626..82f80d4 100644
--- a/src/kernel/panic.h
+++ b/src/kernel/debug.h
@@ -1,4 +1,4 @@
-/* Calcite, src/kernel/panic.h
+/* Calcite, src/kernel/debug.h
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
@@ -17,10 +17,23 @@
#pragma once
-[[noreturn]] void panic_core(
- const char *file, const char *function, int line, const char *message);
+void log_core(const char *file, const char *function, const char *format, ...);
-#define panic(message) panic_core(__FILE__, __func__, __LINE__, message);
+#ifdef CALCITE_DEBUG
+#define debug_log(...) { \
+ log_core(__FILE__, __func__, __VA_ARGS__); \
+}
+#elif CALCITE_RELEASE
+#define debug_log(format, ...) {}
+#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) \
{ \
diff --git a/src/kernel/entry.c b/src/kernel/entry.c
index 1382190..b725c2d 100644
--- a/src/kernel/entry.c
+++ b/src/kernel/entry.c
@@ -23,8 +23,9 @@
#include "utility.h"
#include "drives.h"
#include "paging.h"
+#include "serial.h"
+#include "debug.h"
#include "input.h"
-#include "panic.h"
#include "timer.h"
#include "heap.h"
#include "pci.h"
@@ -187,6 +188,8 @@ static const char *cmdline_look_up(const char *key) {
[[noreturn]] static void with_kernel_page_tables() {
+ init_serial();
+
//store cmdline as key-value pairs
if (cmdline_copy[0] == 0)
@@ -242,6 +245,10 @@ static const char *cmdline_look_up(const char *key) {
}
+ 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();
@@ -302,6 +309,10 @@ static const char *cmdline_look_up(const char *key) {
if (start_elf("root://calcite/apps/init/init.elf") == 0)
panic("could not start init.elf")
+ debug_log("about to switch to init")
+ debug_log(" free physical memory %B", count_free_pram())
+ debug_log(" free kernel virtual memory %B", count_free_kernel_vram())
+
resume_next_continuation();
}
diff --git a/src/kernel/fs.c b/src/kernel/fs.c
index b13a0eb..0270dc6 100644
--- a/src/kernel/fs.c
+++ b/src/kernel/fs.c
@@ -17,7 +17,7 @@
#include "iso9660.h"
#include "utility.h"
-#include "panic.h"
+#include "debug.h"
#include "heap.h"
#include "fs.h"
diff --git a/src/kernel/input.c b/src/kernel/input.c
index 0913696..93e9841 100644
--- a/src/kernel/input.c
+++ b/src/kernel/input.c
@@ -17,8 +17,8 @@
#include "scheduler.h"
#include "process.h"
+#include "debug.h"
#include "input.h"
-#include "panic.h"
static int is_somebody_waiting_for_mouse_packet = 0;
static struct continuation_info waiting_continuation;
diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c
index 9792863..c10326f 100644
--- a/src/kernel/interrupts.c
+++ b/src/kernel/interrupts.c
@@ -22,7 +22,7 @@
// https://osdev.wiki/wiki/Task_State_Segment
#include "interrupts.h"
-#include "panic.h"
+#include "debug.h"
struct [[gnu::packed]] exception_parameter {
uint64_t r15;
diff --git a/src/kernel/ipc-dgram.c b/src/kernel/ipc-dgram.c
index 3472b26..083a09c 100644
--- a/src/kernel/ipc-dgram.c
+++ b/src/kernel/ipc-dgram.c
@@ -18,7 +18,7 @@
#include "ipc-dgram.h"
#include "scheduler.h"
#include "utility.h"
-#include "panic.h"
+#include "debug.h"
#include "heap.h"
#include
diff --git a/src/kernel/iso9660.c b/src/kernel/iso9660.c
index 6e7b83f..b89c070 100644
--- a/src/kernel/iso9660.c
+++ b/src/kernel/iso9660.c
@@ -16,9 +16,10 @@
*/
#include "iso9660.h"
+#include "kernel-public/files.h"
#include "utility.h"
#include "drives.h"
-#include "panic.h"
+#include "debug.h"
#include "heap.h"
#include "fs.h"
@@ -370,6 +371,12 @@ enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct
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/paging.c b/src/kernel/paging.c
index 5783fbb..66c16c7 100644
--- a/src/kernel/paging.c
+++ b/src/kernel/paging.c
@@ -16,7 +16,7 @@
*/
#include "paging.h"
-#include "panic.h"
+#include "debug.h"
#define MAX_PHYSICAL_GB 64ULL
@@ -203,3 +203,20 @@ 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
index f7bf4c4..eaee047 100644
--- a/src/kernel/paging.h
+++ b/src/kernel/paging.h
@@ -52,3 +52,9 @@ uint64_t take_free_physical_page();
//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
index 4b1e496..b005b2d 100644
--- a/src/kernel/pata.c
+++ b/src/kernel/pata.c
@@ -15,12 +15,12 @@
* with this program. If not, see .
*/
-#include "pci.h"
#include "utility.h"
#include "drives.h"
-#include "panic.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
@@ -211,6 +211,14 @@ static void probe_pata_drive(uint16_t command_block_base, uint8_t device_byte) {
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) {
diff --git a/src/kernel/pci.c b/src/kernel/pci.c
index 1f6eed1..ac705f5 100644
--- a/src/kernel/pci.c
+++ b/src/kernel/pci.c
@@ -15,6 +15,7 @@
* with this program. If not, see .
*/
+#include "debug.h"
#include "pata.h"
#include "pci.h"
@@ -40,6 +41,7 @@ static void probe_bus(uint32_t bus_address_base) {
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);
diff --git a/src/kernel/process.c b/src/kernel/process.c
index 6867c4b..73a696c 100644
--- a/src/kernel/process.c
+++ b/src/kernel/process.c
@@ -21,7 +21,7 @@
#include "process.h"
#include "utility.h"
#include "paging.h"
-#include "panic.h"
+#include "debug.h"
#include "heap.h"
#include "fs.h"
@@ -399,6 +399,12 @@ void syscall_create_thread(void (*f)(uint64_t), uint64_t x) {
add_to_queue(&ready_continuations, &ci);
+ debug_log("started thread")
+ debug_log(" process struct at 0xffffffff.%h", thread->process, 8)
+ debug_log(" thread struct at 0xffffffff.%h", thread, 8)
+ debug_log(" free physical memory %B", count_free_pram())
+ debug_log(" free kernel virtual memory %B", count_free_kernel_vram())
+
}
int syscall_start_elf(const char *path, const struct process_start_info *info) {
@@ -439,6 +445,12 @@ int syscall_start_elf(const char *path, const struct process_start_info *info) {
for (int i = 0; i < info->set_envvar_count; ++i)
set_envvar(process, info->set_envvars[2 * i], info->set_envvars[2 * i + 1]);
+ debug_log("started process")
+ debug_log(" path %s", path)
+ debug_log(" process struct at 0xffffffff.%h", process, 8)
+ debug_log(" free physical memory %B", count_free_pram())
+ debug_log(" free kernel virtual memory %B", count_free_kernel_vram())
+
return 1;
}
diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c
index 6fa825c..ccaa8a6 100644
--- a/src/kernel/scheduler.c
+++ b/src/kernel/scheduler.c
@@ -18,7 +18,7 @@
#include "scheduler.h"
#include "process.h"
#include "utility.h"
-#include "panic.h"
+#include "debug.h"
#include "heap.h"
struct continuation_queue ready_continuations;
diff --git a/src/kernel/serial.asm b/src/kernel/serial.asm
new file mode 100644
index 0000000..c9ddc1e
--- /dev/null
+++ b/src/kernel/serial.asm
@@ -0,0 +1,73 @@
+ ; 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
+ movzx rcx, 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
new file mode 100644
index 0000000..0c9f4de
--- /dev/null
+++ b/src/kernel/serial.c
@@ -0,0 +1,54 @@
+/* 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/panic.c b/src/kernel/serial.h
similarity index 71%
rename from src/kernel/panic.c
rename to src/kernel/serial.h
index 74844dd..a70cfdf 100644
--- a/src/kernel/panic.c
+++ b/src/kernel/serial.h
@@ -1,4 +1,4 @@
-/* Calcite, src/kernel/panic.c
+/* Calcite, src/kernel/serial.h
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
@@ -15,16 +15,12 @@
* with this program. If not, see .
*/
-[[noreturn]] void panic_core(
- const char *file, const char *function, int line, const char *message) {
+#pragma once
- //TODO
+#include
- (void)file;
- (void)function;
- (void)line;
- (void)message;
- while (1)
- __asm__ ("hlt");
-
-}
+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.c b/src/kernel/syscalls.c
index 18e6124..8132dfd 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -16,7 +16,7 @@
*/
#include "syscalls.h"
-#include "panic.h"
+#include "debug.h"
#define MAX_SYSCALL_NUMBER 99