Boots under QEMU on all three ISAs from one clang build: - shared C core (kmain, console) calls a small per-arch contract (arch.h) - per-arch asm entry + trap/vector table + UART driver - parallel Makefile (make -j), 'make run-<arch>', 'make sizes' M1 = boot, UART up, trap vector installed, banner from kmain().
65 lines
2.7 KiB
Markdown
65 lines
2.7 KiB
Markdown
# minos — a minimal multi-arch kernel
|
|
|
|
One tiny OS core, three ISAs: **x86 (i386)**, **RISC-V (rv64)**, **ARM
|
|
(Cortex-A9 / Zynq-7000)**. Built by a single `clang` in parallel so you can
|
|
compare how the same C behaves across architectures.
|
|
|
|
Milestone **M1** (this state): each target boots, brings up its UART, sets its
|
|
interrupt/trap vector, and prints a banner from shared C (`kmain`).
|
|
|
|
## Design
|
|
|
|
The only boundary is a small per-arch **contract** (`include/arch.h`):
|
|
|
|
```
|
|
arch_early_init() bring up the UART
|
|
arch_uart_putc(c) emit one byte
|
|
arch_set_trap_vector() install IDT / mtvec / VBAR
|
|
arch_name() banner string
|
|
arch_halt() hlt / wfi
|
|
```
|
|
|
|
Everything in `common/` is portable C and calls only that contract. Each
|
|
`arch/<name>/` supplies the irreducible assembly (`boot.S`, trap/vector table)
|
|
plus a C file implementing the contract and a linker script.
|
|
|
|
```
|
|
common/ main.c (kmain), console.c (putc/puts/puthex)
|
|
include/ arch.h (the contract), console.h
|
|
arch/x86/ boot.S (multiboot) arch.c (COM1 0x3F8, IDT) link.ld (1 MiB)
|
|
arch/riscv/ boot.S (_start) trap.S (mtvec) arch.c (UART 0x10000000)
|
|
arch/arm/ boot.S (_start) vectors.S (VBAR) arch.c (Cadence UART 0xE0000000)
|
|
```
|
|
|
|
## Build & run
|
|
|
|
```sh
|
|
make -j # build all three in parallel
|
|
make run-x86 # boot under QEMU (Ctrl-A X to quit)
|
|
make run-riscv
|
|
make run-arm
|
|
make sizes # compare stripped binary size
|
|
make clean
|
|
```
|
|
|
|
## The interesting comparison (M1)
|
|
|
|
| Aspect | x86 (i386) | RISC-V (rv64) | ARM (Cortex-A9) |
|
|
|-------------------|-------------------------|-----------------------|--------------------------|
|
|
| Entry protocol | Multiboot1 header | `_start` @ 0x80000000 | `_start` @ DDR |
|
|
| Console | port I/O `outb` 0x3F8 | MMIO 16550 0x10000000 | MMIO Cadence 0xE0000000 |
|
|
| Trap vector | IDT via `lidt` | `mtvec` CSR | `VBAR` (CP15) |
|
|
| Multi-core gate | (single by default) | `mhartid` check | `MPIDR` check |
|
|
| Halt | `hlt` | `wfi` | `wfi` |
|
|
| Idle byte size | ~890 | ~816 | ~800 |
|
|
|
|
## Real hardware (next)
|
|
|
|
- **RISC-V**: the only change is the UART base — `0x10000000` (QEMU virt) →
|
|
`0x02500000` (Allwinner D1 / Lichee RV). Loadable via `xfel`.
|
|
- **ARM/Zynq**: same Cadence UART base as the real PS; boot via U-Boot/JTAG.
|
|
- **x86**: boot the multiboot ELF from GRUB.
|
|
|
|
## Roadmap
|
|
- **M2**: real trap handlers + a timer tick per arch.
|
|
- **M3**: expose a tiny syscall-style interface (the "own interface").
|