Bastion OS: Phase 0

Bastion OS: Phase 0

So, I decided to write my own OS from scratch. I'm doing it for study purpose and for fun, nothing more. And I will try to document as much about it as possible.

Goals and ideas

First of all, I want to outline goals and ideas of this project. Main idea is create OS, that will boot on at least two architectures (x86_64 and aarch64) via UEFI, have support of ELF format and be POSIX-compatible.

Also I want to have clear, expandable architecture.

References

My key references are Linux kernel, Serenity OS, Falcon OS and other.

Tool set

Programming language is C++, because i have extensive experience in C, and why not take C++ (and have advantages like RAII, namespaces, templates, oop and other). At first moments, C++ will be very stripped down, because we haven't any memory allocation (and even virtual memory).

Also i will use python as script language for some things like configuring.

In future i want to use other programming languages (like rust) for extensions (like Linux modules).

Compiler is LLVM, because it can compile program to PE32+ directly (that is must-have for UEFI boot loading). In future i plan move to GCC.

Build system is of course Make.

For testing i use qemu.

And i use Claude AI for writing some stuff i don't want to care about, like scripts, writing comments, writing simple code and other.

Design

Model

Model is Monolithic kernel, because it's most simple and fast model.

Like in Linux, i plan to create module-extensions interface.

Task Model

Multitasking model.

Scheduling

At first time it will simple round-robin scheduler. But in future i plan to create more smarter system.

System Calls

I will use POSIX syscalls, because i don't want to rewrite whole libc or use mlibc.

Because of it i want to run ELF applications, that were built for Linux (like GNU software) on Bastion without any problems.

File system hierarchy

It's hard moment, because from one point i want to creating something new, but from other point i want to run GNU software and because of it i need to have FHS. I will make decision about it later.

Target Architectures

For the beginning i choose x86_64 and aarch64.

Booting

For booting i will use UEFI (on aarch64 too via uboot), because it's very simple and understandable.

In future i plan to add legacy boot and arch-specific boot (like u-boot's boot protocol).

Developing Phases

Phase 0 - Design

Concept development phase, where i choose design and core things about this project.

Phase I - Boot

UEFI boot

My main task here is create bootable efi application for kernel, create skeleton of kernel and entry point of kernel for both archs.

So, what we need to have after uefi:

  1. Physical memory map (which regions are usable)
  2. GOP or UART console
  3. RSDP pointer (for ACPI table parsing) for x86 or Device Tree pointer for arm
  4. Kernel's own physical/virtual address

And see "hello world" in qemu from kernel at the end of phase.

Phase II - CPU

Arch-specific CPU setup.

For x86_64:

  • Load a GDT
  • Set up IDT - 256 entries, wire ISR stubs in assembly, that push error codes uniformly, then call dispatch_interrupt(InterruptFrame&) handler
  • Configure paging: PML4 page table hierarchy, higher-half kernel mapping(canonical address like 0xFFFF800000000000+), recursive or direct-map strategy for page table self-reference
  • Enable and configure the local APIC + I/O APIC (from MADT ACPI table), replace the legacy PIC

For AArch64:

  • Set up exception vectors(VBAR_EL1) - 4 exception types x 4 source levels = 16 vectors
  • Configure the MMU: TCR_EL1, MAIR_EL1, TTBR0_EL1/ TTBR1_EL1 (user/kernel split), 4-level page tables (4KB granule, 48-bit VA)
  • Set up the GIC(Generic Interrupt Controller) v2 or v3 from device tree info

Phase III - Memory

PMM - Physical Memory Manager

  • Parse the boot memory map (that we did in I phase), build a buddy allocator or bitmap allocator over free regions
  • Track allocation in page-sized (4KiB) granules
  • Provide alloc_page() / free_page functions

VMM - Virtual Memory Manager

  • Implement VirtualAddressSpace object, that wraps a page table root
  • Operations map(VirtAddr, PhysAddr, flags), unmap(VirtAddr), translate(VirtAddr) -> PhysAddr
  • Kernel its own address space; each process will get one later
  • Both archs use 4-level tables with similar structure - abstract the entry format

Kernel Heap

  • Implement a slab allocator or a simple kmalloc/kfree on top of the VMM
  • Overload global operator new/delete to use it - this unlocks C++ STL and ability to allocate kernel objects in runtime(for example drivers)

Phase IV - Interrupts, Timers & Scheduling

Timer

  • x86_64: APIC Timer (calibrated against HPET or PIT) or TSC deadline mode
  • AArch64: Generic Timer (CNTPCT_EL0, CNTP_TVAL_EL0)

Scheduler

  • At begining, i want to use simple round-robin with a ready one queue
  • Each task has: a kernel stack, saved register context, an address space
  • Context switch is arch-specific assembly: save/restore registers + swap stack pointer + swap page table root
  • Preemption via timer interrupt

Phase V - UserSpace

ELF Parser

  • Parse and validate ELF64 header
  • Iterate program headers(PT_LOAD segments), map them into the process address space at their p_vaddr with correct permissions (rwx from p_flags)
  • Set entry point from e_entry

Userspace transition

  • Allocate a user stack, set up the initial stack frame (argc, argv, envp, auxv)
  • x86_64: sysretq or iretq to ring3
  • aarch64: eret to EL0

SysCall

  • x86_64: syscall/sysret via MSRr(LSTAR, STAR, SFMASK)
  • aarch64: svc instruction, handled in the EL1 syncronous exception vector
  • Define a syscall table - start with basic write(), read(), exit(), mmap(), fork()/spawn()/clone()

Phase VI - Drivers

Essential drivers:

  • UART/Serial
  • Framebuffer console
  • USB keyboard (or PS/2 for qemu testing)
  • Virtio-blk (block device in QEMU - much simpler than AHCI/NVMe)

Phase VII - Libc

  • add dynamic linking and shared libs
  • porting full libc
  • do full POSIX support

In this phase i must have ability to port GNU utils.

Phase VIII - Network

Add basic support of virtio-net driver and tcp/ip stack

Phase IX - SMP

Add support of multicore/SMP

Phase X - Modules

Add extensions

Phase XI- Graphic

Add Window drawing

So, let's start!