mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
docs: restructure architecture documentation and add new guides
This commit is contained in:
58
docs/architecture/system/core.md
Normal file
58
docs/architecture/system/core.md
Normal file
@@ -0,0 +1,58 @@
|
||||
<div align="center">
|
||||
<h1>Core Architecture</h1>
|
||||
<p><em>Overview of BoredOS kernel layout, boot process, and userspace transition.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
BoredOS is a 64-bit hobbyist operating system designed for the x86_64 architecture. While it features kernel-space drivers and a built-in window manager, it supports fully-isolated userspace applications and includes a networking stack.
|
||||
|
||||
This document serves as an overview of the core architecture and the layout of the kernel source code.
|
||||
|
||||
## Source Code Layout (`src/`)
|
||||
|
||||
The OS heavily relies on module separation. The `src/` directory is logically split into several domains:
|
||||
|
||||
- **`arch/`**: Contains the assembly routines needed for bootstrapping the system (`boot.asm`) and setting up the CPU state for userland execution (`process_asm.asm`). It also handles architecture-specific mechanisms like the Global Descriptor Table (GDT) and Interrupt Descriptor Table (IDT).
|
||||
- **`core/`**: The initialization sequence of the OS lives here. `main.c` is the entry point from the bootloader. This directory also contains essential kernel utilities (`kutils.c`), panic handlers (`panic.c`), and built-in command parsing logic (`cmd.c`).
|
||||
- **`dev/`**: Device drivers. This includes the PCI scanner, disk management infrastructure, input drivers (keyboard and mouse), and the Real Time Clock (RTC).
|
||||
- **`fs/`**: Filesystem implementations. The system uses a Virtual File System (VFS) abstraction alongside an in-memory FAT32 filesystem with support for drives over ATA that are formatted as FAT32 (plain/MBR).
|
||||
- **`mem/`**: Physical and virtual memory management. It controls page frame allocation, paging, and kernel heap operations.
|
||||
- **`net/`**: The networking stack. BoredOS relies on `lwIP` for processing IPv4 and TCP/UDP traffic, interacting with a range of NICs via `net/nic/`.
|
||||
- **`sys/`**: System calls and process management. The ELF loader resides here, alongside the Symmetric Multi-Processing (**smp.c**) bringup and Local APIC (**lapic.c**) management logic.
|
||||
- **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering.
|
||||
- **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`).
|
||||
|
||||
## Boot Process
|
||||
|
||||
BoredOS uses **Limine** as its primary bootloader.
|
||||
|
||||
1. **Limine Initialization**: The machine firmware (BIOS or UEFI) loads Limine. Limine parses `limine.conf`, sets up an early graphical framebuffer, and reads the kernel ELF file into memory.
|
||||
2. **Multiboot2 & SMP Protocol**: The kernel expects the Limine boot protocol. It makes a specific **SMP Request** to Limine to locate and bring up all available CPU cores.
|
||||
3. **Kernel Entry (`main.c`)**: The entry point `_start` is called on the Bootstrap Processor (BSP). It initializes the serial port, GDT/IDT, memory management, and paging.
|
||||
4. **AP Bringup**: The BSP calls `smp_init()`, which sends the Startup Inter-Processor Interrupt (SIPI) sequence to all Application Processors (APs). Each AP initializes its own local GDT, TSS, and Page Tables before entering an idle loop.
|
||||
5. **Driver Initialization**: PCI buses are scanned, finding the network card or disk controllers. The filesystem is mounted.
|
||||
6. **Window Manager**: The UI is drawn on top of the Limine-provided framebuffer.
|
||||
|
||||
## Multi-Core & Scheduling
|
||||
|
||||
BoredOS utilizes Symmetric Multi-Processing (SMP) to distribute workloads across all available CPU cores.
|
||||
|
||||
- **LAPIC & IPIs**: Each CPU has its own Local APIC. The kernel uses Inter-Processor Interrupts (IPIs) for inter-core communication, specifically for triggering the scheduler on other cores (`vector 0x41`).
|
||||
- **Scheduler**: A round-robin scheduler runs on each core. Processes are pinned to specific CPUs (CPU Affinity) to maintain cache locality. The BSP timer interrupt (`60Hz`) broadcasts a scheduling IPI to all core to ensure balanced execution.
|
||||
- **Spinlocks**: Since multiple cores can access kernel structures (VFS, Process List) simultaneously, the kernel uses **interrupt-safe spinlocks** to prevent race conditions.
|
||||
|
||||
## Userland Transition
|
||||
|
||||
The OS supports privilege separation (Ring 0 vs. Ring 3). When an application is launched, the kernel:
|
||||
|
||||
1. Loads the ELF file from the filesystem.
|
||||
2. Assigns the process to a CPU core via a round-robin distribution strategy.
|
||||
3. Allocates a new virtual address space (Page Directory) for the process.
|
||||
4. Maps the executable segments according to the ELF headers.
|
||||
5. Switches to User Mode (Ring 3) via the `iretq` instruction.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Programs interact with the core kernel using system calls (`syscall.c`). Multitasking is achieved by pre-empting user processes on their respective cores.
|
||||
|
||||
---
|
||||
66
docs/architecture/system/processes.md
Normal file
66
docs/architecture/system/processes.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Process Management & Scheduling
|
||||
|
||||
BoredOS implements a lightweight, symmetric multiprocessing (SMP) capable multitasking environment. This document outlines the architecture of the scheduler, process structures, context switching, and ELF binary loading.
|
||||
|
||||
## 1. Process Structure (`process_t`)
|
||||
|
||||
The core of the process management system is the `process_t` structure, defined in `src/sys/process.h`. Due to kernel memory constraints, BoredOS supports a maximum of 16 concurrent processes (`MAX_PROCESSES`), stored in a statically allocated array.
|
||||
|
||||
Key fields include:
|
||||
- **Identification:** `pid`, `parent_pid`, `pgid` (Process Group ID), and `name`.
|
||||
- **Memory & Context:**
|
||||
- `rsp`: The saved stack pointer during a context switch.
|
||||
- `pml4_phys`: The physical address of the Page Map Level 4 table (VMM root) for this process.
|
||||
- `kernel_stack` & `user_stack_alloc`: Pointers to allocated stack memory.
|
||||
- **Scheduler State:** `ticks`, `sleep_until`, `is_idle`, `cpu_affinity`.
|
||||
- **Resources:**
|
||||
- `fds`: File descriptor table tracking open files, pipes, and sockets (up to `MAX_PROCESS_FDS` = 16).
|
||||
- `gui_events`: A circular queue for Window Manager events (keyboard, mouse).
|
||||
- **Signals:** POSIX-like signal tracking via `signal_mask` and `signal_pending`.
|
||||
|
||||
## 2. The Scheduler
|
||||
|
||||
BoredOS uses a **Preemptive Round-Robin** scheduler implemented as a circular linked list.
|
||||
|
||||
### Symmetric Multiprocessing (SMP)
|
||||
Each CPU core maintains its own `current_process` pointer (`current_process[my_cpu]`). When a new user process is spawned via `process_create_elf`, the kernel assigns it to an Application Processor (AP) core using a simple round-robin assignment policy (`next_cpu_assign`), avoiding Core 0 (BSP) which is typically reserved for kernel tasks and driver interrupts.
|
||||
|
||||
### The `process_schedule` Loop
|
||||
When the timer interrupt fires, it calls `process_schedule(current_rsp)`:
|
||||
1. It saves the `current_rsp` into the current process's structure.
|
||||
2. It handles cleanup of killed processes (`kill_pending`).
|
||||
3. It traverses the circular linked list (`cur->next`) looking for a process where `cpu_affinity == my_cpu`.
|
||||
4. It checks if the process is sleeping (`sleep_until > now`).
|
||||
5. It switches the hardware context:
|
||||
- Updates the Task State Segment (TSS) ring 0 stack pointer.
|
||||
- Switches the page directory by writing the new `pml4_phys` to `CR3`.
|
||||
- Returns the new process's `rsp`, which the interrupt handler then pops into registers.
|
||||
|
||||
## 3. Context Switching
|
||||
|
||||
Context switching is achieved by manually constructing an interrupt stack frame (IRETQ frame).
|
||||
|
||||
When a process is created, the kernel sets up the top of its kernel stack with:
|
||||
- `SS` (Stack Segment: `0x1B` for user, `0x10` for kernel)
|
||||
- `RSP` (The process's stack pointer)
|
||||
- `RFLAGS` (`0x202` to ensure interrupts are enabled)
|
||||
- `CS` (Code Segment: `0x23` for user, `0x08` for kernel)
|
||||
- `RIP` (The entry point of the binary or function)
|
||||
- Zeroed space for General Purpose Registers and a 512-byte `fxsave` region for FPU/SSE state.
|
||||
|
||||
When `process_schedule` returns the new `rsp`, the assembly interrupt stub uses `pop` instructions to restore the general-purpose registers, and finally executes `iretq`, transitioning execution to the new process seamlessly.
|
||||
|
||||
## 4. ELF Loading
|
||||
|
||||
Userland applications in BoredOS are standard 64-bit ELF binaries.
|
||||
|
||||
The function `process_create_elf` orchestrates this:
|
||||
1. **Memory Allocation:** Creates a new PML4 page table for the user process.
|
||||
2. **Parsing:** Calls `elf_load(filepath, pml4, &size)` to parse the ELF headers, allocate required physical memory, and copy the executable segments (text, data, bss) into the process's virtual address space at the locations specified by the ELF program headers.
|
||||
3. **Stack Setup:** Allocates a 256KB user stack mapped at `0x800000`.
|
||||
4. **Argument Passing:** Parses the `args_str` passed to the executable and pushes an `argv` array onto the newly allocated user stack.
|
||||
5. **Execution:** Sets the stack frame's `RIP` to the ELF entry point and links the process into the scheduler's run queue.
|
||||
|
||||
## 5. Process Termination
|
||||
|
||||
When a process exits (or is killed), it is not immediately freed. The scheduler sets `kill_pending = true`. The actual destruction of the PML4 table and stack allocations is deferred to the next tick inside `process_schedule` to avoid freeing the memory of the code currently executing the cleanup.
|
||||
Reference in New Issue
Block a user