From bafa8724152704010100aec9e86dea0c1e8c4f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Coutada?= Date: Mon, 6 Jul 2026 22:53:38 +0100 Subject: [PATCH] refactor(kernel): rewrite Stage 1 kernel in C++ - Rename kernel.c -> kernel.cpp and uart.c -> uart.cpp - Add universalisos::uart namespace with constexpr register definitions - Use extern "C" linkage for kernel_main() called from boot.S - Compile with arm-none-eabi-g++ using C++17 freestanding flags (-fno-exceptions, -fno-rtti, -fno-threadsafe-statics, -fno-use-cxa-atexit) - Update Makefile to use g++ and .cpp build rules --- AURELIO_INTEGRATION.md | 4 +-- README.md | 3 +- kernel/Makefile | 19 +++++++---- kernel/README.md | 12 +++---- kernel/arch/arm/uart.c | 32 ------------------ kernel/arch/arm/uart.cpp | 73 ++++++++++++++++++++++++++++++++++++++++ kernel/arch/arm/uart.h | 12 ++++--- kernel/kernel.c | 17 ---------- kernel/kernel.cpp | 17 ++++++++++ 9 files changed, 120 insertions(+), 69 deletions(-) delete mode 100644 kernel/arch/arm/uart.c create mode 100644 kernel/arch/arm/uart.cpp delete mode 100644 kernel/kernel.c create mode 100644 kernel/kernel.cpp diff --git a/AURELIO_INTEGRATION.md b/AURELIO_INTEGRATION.md index 8b8ccbf0b..8a8830b1d 100644 --- a/AURELIO_INTEGRATION.md +++ b/AURELIO_INTEGRATION.md @@ -564,10 +564,10 @@ class AurelioAgentSchema: ### Stage 1: Bare-Metal Hypervisor Skeleton ✅ Implemented **Deliverables**: -- Bootable bare-metal kernel for QEMU ARM virt +- Bootable bare-metal kernel for QEMU ARM virt, written in **C++** - Assembly startup with stack/BSS setup - PL011 UART driver for serial output -- Build system using `arm-none-eabi-gcc` +- Build system using `arm-none-eabi-g++` **Code Components**: | File | Purpose | diff --git a/README.md b/README.md index 541b3caaf..faf297458 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ See [BUILD_ENVIRONMENT.md](BUILD_ENVIRONMENT.md) for detailed setup instructions ## Hypervisor Kernel (C) The `kernel/` directory contains the actual Universalisos type-1 hypervisor -implementation. Stage 1 is a bootable bare-metal skeleton for QEMU ARM virt: +implementation, written in **C++**. Stage 1 is a bootable bare-metal skeleton +for QEMU ARM virt: ```bash cd kernel diff --git a/kernel/Makefile b/kernel/Makefile index 551ec958c..b28057087 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,18 +1,23 @@ -# Universalisos kernel build for QEMU ARM virt +# Universalisos kernel build for QEMU ARM virt (C++) CROSS_COMPILE ?= arm-none-eabi- -CC := $(CROSS_COMPILE)gcc +CXX := $(CROSS_COMPILE)g++ LD := $(CROSS_COMPILE)ld OBJCOPY := $(CROSS_COMPILE)objcopy OBJDUMP := $(CROSS_COMPILE)objdump -CFLAGS := \ +CXXFLAGS := \ -mcpu=cortex-a15 \ -marm \ -O2 \ + -std=c++17 \ -ffreestanding \ -nostdlib \ -nostartfiles \ + -fno-exceptions \ + -fno-rtti \ + -fno-threadsafe-statics \ + -fno-use-cxa-atexit \ -Wall \ -Wextra \ -Werror \ @@ -31,16 +36,16 @@ OBJS := \ all: kernel.bin kernel.elf kernel.elf: $(OBJS) arch/arm/linker.ld - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) kernel.bin: kernel.elf $(OBJCOPY) -O binary $< $@ %.o: %.S - $(CC) $(CFLAGS) -c -o $@ $< + $(CXX) $(CXXFLAGS) -c -o $@ $< -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< dump: kernel.elf $(OBJDUMP) -d $< diff --git a/kernel/README.md b/kernel/README.md index 573f2dd98..0d1881661 100644 --- a/kernel/README.md +++ b/kernel/README.md @@ -1,8 +1,8 @@ -# Universalisos Kernel — Stage 1 Bare-Metal Skeleton +# Universalisos Kernel — Stage 1 Bare-Metal Skeleton (C++) This is the first bootable type-1 hypervisor milestone for Universalisos: a -minimal bare-metal kernel that runs on QEMU's ARM `virt` machine and prints to -the PL011 UART. +minimal bare-metal kernel written in **C++** that runs on QEMU's ARM `virt` +machine and prints to the PL011 UART. ## Building @@ -30,7 +30,7 @@ Expected output: ``` Universalisos type-1 hypervisor booted. -Stage 1: bare-metal skeleton running on QEMU ARM virt. +Stage 1: bare-metal C++ skeleton running on QEMU ARM virt. ``` Press `Ctrl+A` then `X` to exit QEMU. @@ -47,9 +47,9 @@ make dump |------|---------| | `arch/arm/boot.S` | Assembly entry point, stack/BSS setup | | `arch/arm/linker.ld` | Memory layout for QEMU virt (load at 0x40000000) | -| `arch/arm/uart.c` | PL011 UART driver | +| `arch/arm/uart.cpp` | PL011 UART driver | | `arch/arm/uart.h` | UART interface | -| `kernel.c` | `kernel_main()` entry point | +| `kernel.cpp` | `kernel_main()` entry point | ## Design notes diff --git a/kernel/arch/arm/uart.c b/kernel/arch/arm/uart.c deleted file mode 100644 index c043b0ed9..000000000 --- a/kernel/arch/arm/uart.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * PL011 UART driver for QEMU ARM virt machine. - * - * The virt machine exposes a PL011 UART at 0x09000000. - */ - -#include "uart.h" - -#define UART0_BASE 0x09000000U -#define UART_DR (*(volatile unsigned int *)(UART0_BASE + 0x00U)) -#define UART_FR (*(volatile unsigned int *)(UART0_BASE + 0x18U)) -#define UART_FR_TXFF (1U << 5) - -void uart_init(void) -{ - /* QEMU initializes the UART for us. */ -} - -void uart_putc(char c) -{ - while ((UART_FR & UART_FR_TXFF) != 0U) { - /* Wait until the transmit FIFO is not full. */ - } - UART_DR = (unsigned int)c; -} - -void uart_puts(const char *s) -{ - while (*s != '\0') { - uart_putc(*s++); - } -} diff --git a/kernel/arch/arm/uart.cpp b/kernel/arch/arm/uart.cpp new file mode 100644 index 000000000..752868982 --- /dev/null +++ b/kernel/arch/arm/uart.cpp @@ -0,0 +1,73 @@ +/* + * PL011 UART driver for QEMU ARM virt machine. + * + * The virt machine exposes a PL011 UART at 0x09000000. + */ + +#include "uart.h" + +namespace universalisos::uart { + +namespace { + +constexpr unsigned int UART0_BASE = 0x09000000U; + +constexpr unsigned int REG_DR = 0x00U; // Data Register +constexpr unsigned int REG_FR = 0x18U; // Flag Register +constexpr unsigned int REG_IBRD = 0x24U; // Integer Baud Rate Divisor +constexpr unsigned int REG_FBRD = 0x28U; // Fractional Baud Rate Divisor +constexpr unsigned int REG_LCR_H = 0x2CU; // Line Control Register +constexpr unsigned int REG_CR = 0x30U; // Control Register +constexpr unsigned int REG_IMSC = 0x38U; // Interrupt Mask Set/Clear +constexpr unsigned int REG_ICR = 0x44U; // Interrupt Clear Register + +constexpr unsigned int FR_TXFF = 1U << 5; // Transmit FIFO full +constexpr unsigned int CR_UARTEN = 1U << 0; // UART enable +constexpr unsigned int CR_TXE = 1U << 8; // Transmit enable +constexpr unsigned int CR_RXE = 1U << 9; // Receive enable +constexpr unsigned int LCR_H_WLEN_8 = 3U << 5; // 8 data bits +constexpr unsigned int LCR_H_FEN = 1U << 4; // Enable FIFOs + +inline volatile unsigned int ®(unsigned int offset) +{ + return *reinterpret_cast(UART0_BASE + offset); +} + +} // namespace + +void init() +{ + // Disable UART while configuring. + reg(REG_CR) = 0U; + + // Clear pending interrupts. + reg(REG_ICR) = 0x7FFU; + + // 8 data bits, no parity, one stop bit, FIFO enabled. + reg(REG_LCR_H) = LCR_H_WLEN_8 | LCR_H_FEN; + + // Baud rate divisor for 115200 with a 24 MHz UARTCLK. + // QEMU ignores the actual baud value, but a valid divisor is required. + reg(REG_IBRD) = 13U; + reg(REG_FBRD) = 1U; + + // Enable UART, TX and RX. + reg(REG_CR) = CR_UARTEN | CR_TXE | CR_RXE; +} + +void putc(char c) +{ + while ((reg(REG_FR) & FR_TXFF) != 0U) { + // Wait until the transmit FIFO is not full. + } + reg(REG_DR) = static_cast(c); +} + +void puts(const char *s) +{ + while (*s != '\0') { + putc(*s++); + } +} + +} // namespace universalisos::uart diff --git a/kernel/arch/arm/uart.h b/kernel/arch/arm/uart.h index 002696350..3a026803a 100644 --- a/kernel/arch/arm/uart.h +++ b/kernel/arch/arm/uart.h @@ -1,12 +1,16 @@ /* - * PL011 UART driver header for QEMU ARM virt machine. + * PL011 UART driver for QEMU ARM virt machine. */ #ifndef UNIVERSALISOS_UART_H #define UNIVERSALISOS_UART_H -void uart_init(void); -void uart_putc(char c); -void uart_puts(const char *s); +namespace universalisos::uart { + +void init(); +void putc(char c); +void puts(const char *s); + +} // namespace universalisos::uart #endif /* UNIVERSALISOS_UART_H */ diff --git a/kernel/kernel.c b/kernel/kernel.c deleted file mode 100644 index db484fea8..000000000 --- a/kernel/kernel.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Universalisos kernel entry point. - */ - -#include "arch/arm/uart.h" - -void kernel_main(void) -{ - uart_init(); - uart_puts("Universalisos type-1 hypervisor booted.\r\n"); - uart_puts("Stage 1: bare-metal skeleton running on QEMU ARM virt.\r\n"); - - /* Idle loop. */ - for (;;) { - __asm__ __volatile__("wfi"); - } -} diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp new file mode 100644 index 000000000..33f2544a2 --- /dev/null +++ b/kernel/kernel.cpp @@ -0,0 +1,17 @@ +/* + * Universalisos kernel entry point. + */ + +#include "arch/arm/uart.h" + +extern "C" void kernel_main(void) +{ + universalisos::uart::init(); + universalisos::uart::puts("Universalisos type-1 hypervisor booted.\r\n"); + universalisos::uart::puts("Stage 1: bare-metal C++ skeleton running on QEMU ARM virt.\r\n"); + + /* Idle loop. */ + for (;;) { + __asm__ __volatile__("wfi"); + } +}