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
This commit is contained in:
parent
6a33d926f9
commit
bafa872415
9 changed files with 120 additions and 69 deletions
|
|
@ -564,10 +564,10 @@ class AurelioAgentSchema:
|
||||||
### Stage 1: Bare-Metal Hypervisor Skeleton ✅ Implemented
|
### Stage 1: Bare-Metal Hypervisor Skeleton ✅ Implemented
|
||||||
|
|
||||||
**Deliverables**:
|
**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
|
- Assembly startup with stack/BSS setup
|
||||||
- PL011 UART driver for serial output
|
- PL011 UART driver for serial output
|
||||||
- Build system using `arm-none-eabi-gcc`
|
- Build system using `arm-none-eabi-g++`
|
||||||
|
|
||||||
**Code Components**:
|
**Code Components**:
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,8 @@ See [BUILD_ENVIRONMENT.md](BUILD_ENVIRONMENT.md) for detailed setup instructions
|
||||||
## Hypervisor Kernel (C)
|
## Hypervisor Kernel (C)
|
||||||
|
|
||||||
The `kernel/` directory contains the actual Universalisos type-1 hypervisor
|
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
|
```bash
|
||||||
cd kernel
|
cd kernel
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
# Universalisos kernel build for QEMU ARM virt
|
# Universalisos kernel build for QEMU ARM virt (C++)
|
||||||
|
|
||||||
CROSS_COMPILE ?= arm-none-eabi-
|
CROSS_COMPILE ?= arm-none-eabi-
|
||||||
CC := $(CROSS_COMPILE)gcc
|
CXX := $(CROSS_COMPILE)g++
|
||||||
LD := $(CROSS_COMPILE)ld
|
LD := $(CROSS_COMPILE)ld
|
||||||
OBJCOPY := $(CROSS_COMPILE)objcopy
|
OBJCOPY := $(CROSS_COMPILE)objcopy
|
||||||
OBJDUMP := $(CROSS_COMPILE)objdump
|
OBJDUMP := $(CROSS_COMPILE)objdump
|
||||||
|
|
||||||
CFLAGS := \
|
CXXFLAGS := \
|
||||||
-mcpu=cortex-a15 \
|
-mcpu=cortex-a15 \
|
||||||
-marm \
|
-marm \
|
||||||
-O2 \
|
-O2 \
|
||||||
|
-std=c++17 \
|
||||||
-ffreestanding \
|
-ffreestanding \
|
||||||
-nostdlib \
|
-nostdlib \
|
||||||
-nostartfiles \
|
-nostartfiles \
|
||||||
|
-fno-exceptions \
|
||||||
|
-fno-rtti \
|
||||||
|
-fno-threadsafe-statics \
|
||||||
|
-fno-use-cxa-atexit \
|
||||||
-Wall \
|
-Wall \
|
||||||
-Wextra \
|
-Wextra \
|
||||||
-Werror \
|
-Werror \
|
||||||
|
|
@ -31,16 +36,16 @@ OBJS := \
|
||||||
all: kernel.bin kernel.elf
|
all: kernel.bin kernel.elf
|
||||||
|
|
||||||
kernel.elf: $(OBJS) arch/arm/linker.ld
|
kernel.elf: $(OBJS) arch/arm/linker.ld
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
||||||
|
|
||||||
kernel.bin: kernel.elf
|
kernel.bin: kernel.elf
|
||||||
$(OBJCOPY) -O binary $< $@
|
$(OBJCOPY) -O binary $< $@
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.cpp
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
dump: kernel.elf
|
dump: kernel.elf
|
||||||
$(OBJDUMP) -d $<
|
$(OBJDUMP) -d $<
|
||||||
|
|
|
||||||
|
|
@ -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
|
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
|
minimal bare-metal kernel written in **C++** that runs on QEMU's ARM `virt`
|
||||||
the PL011 UART.
|
machine and prints to the PL011 UART.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ Expected output:
|
||||||
|
|
||||||
```
|
```
|
||||||
Universalisos type-1 hypervisor booted.
|
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.
|
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/boot.S` | Assembly entry point, stack/BSS setup |
|
||||||
| `arch/arm/linker.ld` | Memory layout for QEMU virt (load at 0x40000000) |
|
| `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 |
|
| `arch/arm/uart.h` | UART interface |
|
||||||
| `kernel.c` | `kernel_main()` entry point |
|
| `kernel.cpp` | `kernel_main()` entry point |
|
||||||
|
|
||||||
## Design notes
|
## Design notes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
73
kernel/arch/arm/uart.cpp
Normal file
73
kernel/arch/arm/uart.cpp
Normal file
|
|
@ -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<volatile unsigned int *>(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<unsigned int>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void puts(const char *s)
|
||||||
|
{
|
||||||
|
while (*s != '\0') {
|
||||||
|
putc(*s++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace universalisos::uart
|
||||||
|
|
@ -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
|
#ifndef UNIVERSALISOS_UART_H
|
||||||
#define UNIVERSALISOS_UART_H
|
#define UNIVERSALISOS_UART_H
|
||||||
|
|
||||||
void uart_init(void);
|
namespace universalisos::uart {
|
||||||
void uart_putc(char c);
|
|
||||||
void uart_puts(const char *s);
|
void init();
|
||||||
|
void putc(char c);
|
||||||
|
void puts(const char *s);
|
||||||
|
|
||||||
|
} // namespace universalisos::uart
|
||||||
|
|
||||||
#endif /* UNIVERSALISOS_UART_H */
|
#endif /* UNIVERSALISOS_UART_H */
|
||||||
|
|
|
||||||
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
kernel/kernel.cpp
Normal file
17
kernel/kernel.cpp
Normal file
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue