diff --git a/.idea/editor.xml b/.idea/editor.xml
index 8d0e15e..51e9788 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -139,7 +139,7 @@
-
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..702845c
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/kernel/common.h b/src/kernel/common.h
new file mode 100644
index 0000000..073ce89
--- /dev/null
+++ b/src/kernel/common.h
@@ -0,0 +1,11 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+/**
+ * Sends a kernel panic message through the UART and hang the core.
+ * @param format The format of the message
+ * @param ... The parameters of the message
+ */
+void panic(const char *format, ...);
+
+#endif
\ No newline at end of file
diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c
index 4088254..f783d72 100644
--- a/src/kernel/kernel.c
+++ b/src/kernel/kernel.c
@@ -1,46 +1,12 @@
-/*
- * =====================================================================================
- *
- * Filename: kernel.c
- *
- * Description: Kernel entry point
- *
- * Version: 1.0
- * Created: 2026-02-16 10:13:30 PM
- * Revision: none
- * Compiler: gcc
- *
- * Author: William Nolin,
- *
- * =====================================================================================
- */
-#include
-#include
-
#include "syscon.h"
+#include "uart.h"
-unsigned char *uart = (unsigned char *)0x10000000;
-void putchar(char c) {
- *uart = c;
- return;
-}
-
-void print(const char * str) {
- while(*str != '\0') {
- putchar(*str);
- str++;
- }
- return;
-}
+#define ARCH "RISC-V 64"
void kmain(void) {
- power_off();
+ uart_init();
+ uart_printf("Hello world, %s!\n", ARCH);
- // print("Hello world!\r\n");
- // while(1) {
- // // Read input from the UART
- // putchar(*uart);
- // }
- // return;
+ power_off();
}
diff --git a/src/kernel/riscv64/common.c b/src/kernel/riscv64/common.c
new file mode 100644
index 0000000..0621732
--- /dev/null
+++ b/src/kernel/riscv64/common.c
@@ -0,0 +1,21 @@
+#include "../common.h"
+
+#include
+
+#include "../uart.h"
+
+void panic(const char *format, ...) {
+ uart_puts("Kernel panic!");
+ uart_puts("Reason:");
+
+ va_list args = nullptr;
+ va_start(args, format);
+
+ // Print the reason
+ uart_vprintf(format, args);
+
+ va_end(args);
+
+ // Hang
+ asm volatile("wfi");
+}
diff --git a/src/kernel/riscv64/qemu/syscon.c b/src/kernel/riscv64/qemu/syscon.c
index 2515271..82633d2 100644
--- a/src/kernel/riscv64/qemu/syscon.c
+++ b/src/kernel/riscv64/qemu/syscon.c
@@ -1,13 +1,16 @@
#include
#include "../../syscon.h"
+#include "../../uart.h"
// QEMU exposes a syscon-compatible device at address 0x1000000
#define SYSCON_ADDR 0x100000
void power_off() {
+ uart_puts("Power off requested");
*(uint32_t *) SYSCON_ADDR = 0x5555;
}
void reboot() {
+ uart_puts("Reboot requested");
*(uint32_t *) SYSCON_ADDR = 0x7777;
}
diff --git a/src/kernel/riscv64/qemu/uart.c b/src/kernel/riscv64/qemu/uart.c
new file mode 100644
index 0000000..3056318
--- /dev/null
+++ b/src/kernel/riscv64/qemu/uart.c
@@ -0,0 +1,81 @@
+#include
+#include "../../uart.h"
+
+// QEMU emulates the NS16550A UART
+// The UART is memory mapped at 0x1000000
+#define UART_ADDR 0x10000000
+
+void uart_init() {
+ volatile uint8_t *ptr = (uint8_t *) UART_ADDR;
+
+ // Set the word length to 8 bits
+ ptr[3] = 0b11;
+
+ // Enable FIFO
+ ptr[2] = 0b1;
+
+ // Enable receiver buffer interrupts
+ ptr[1] = 0b1;
+
+ // For real hardware, we would need to set a baud rate
+}
+
+void uart_putchar(const char c) {
+ *(uint8_t *) UART_ADDR = c;
+}
+
+static void uart_print(const char *str) {
+ while (*str) {
+ uart_putchar(*str);
+ str++;
+ }
+}
+
+void uart_puts(const char *line) {
+ uart_print(line);
+ uart_putchar('\n');
+}
+
+// Basic implementation of printf supporting the following formats:
+// - c: Character
+// - s: String of characters
+// - %: Literal '%'
+void uart_vprintf(const char *restrict format, va_list args) {
+ while (*format) {
+ if (*format == '%') {
+ // Ignore this character as it indicates the format of a parameter
+ format++;
+ if (!*format) {
+ // There is nothing after '%'
+ return;
+ }
+
+ switch (*format) {
+ case 'c': // Character
+ uart_putchar(va_arg(args, int));
+ break;
+ case 's': // String
+ uart_print(va_arg(args, char*));
+ break;
+ case '%': // Literal '%'
+ uart_putchar('%');
+ break;
+ default: // Unsupported format
+ uart_putchar('%');
+ uart_putchar(*format);
+ break;
+ }
+ } else {
+ uart_putchar(*format);
+ }
+
+ format++;
+ }
+}
+
+void uart_printf(const char *restrict format, ...) {
+ va_list args = nullptr;
+ va_start(args, format);
+ uart_vprintf(format, args);
+ va_end(args);
+}
diff --git a/src/kernel/uart.c b/src/kernel/uart.c
deleted file mode 100644
index 21d8258..0000000
--- a/src/kernel/uart.c
+++ /dev/null
@@ -1,3 +0,0 @@
-//
-// Created by william on 2026-02-21.
-//
\ No newline at end of file
diff --git a/src/kernel/uart.h b/src/kernel/uart.h
new file mode 100644
index 0000000..d554399
--- /dev/null
+++ b/src/kernel/uart.h
@@ -0,0 +1,34 @@
+#ifndef UART_H
+#define UART_H
+#include
+
+/** Initializes a UART device. */
+void uart_init();
+
+/**
+ * Sends a character to the UART.
+ * @param c The character to put
+ */
+void uart_putchar(char c);
+
+/**
+ * Sends a line to the UART.
+ * @param line The line to put
+ */
+void uart_puts(const char *line);
+
+/**
+ * Sends a string to the UART following a format.
+ * @param format The format of the string
+ * @param args The parameters of the string
+ */
+void uart_vprintf(const char *restrict format, va_list args);
+
+/**
+ * Sends a string to the UART following a format.
+ * @param format The format of the string
+ * @param ... The parameters of the string
+ */
+void uart_printf(const char *restrict format, ...);
+
+#endif
\ No newline at end of file