_start; on QEMU the -kernel flag loads the ELF directly and begins executing at the entry point declared in the linker script (ENTRY(_start)).
_start — ARM low-level setup
_start in root.s runs before any C code. It has three jobs: install the vector table, configure both hardware stacks, and call main().
Install the vector table
The Cortex-A8 supports a relocatable vector table via the VBAR (Vector Base Address Register) in CP15. The vector table itself is an 8-entry branch table defined immediately before
_start loads the address of vector_table and writes it with an MCR instruction:_start in the same file:.align 5 ensures the table starts on a 32-byte boundary, which is required by the ARM architecture for VBAR.Configure the IRQ stack
ARM uses banked registers for different processor modes. Each mode has its own IRQs are disabled (
sp and lr. _start must switch to IRQ mode to set its stack pointer, then switch back:_irq_stack_top is a linker symbol defined at the top of a 4096-byte .bss array in root.s:#0x80) throughout this setup to prevent an interrupt firing before the stacks are ready.Configure the SVC stack and call main()
After setting the IRQ stack,
_start switches to SVC mode (the mode C code runs in) and sets the SVC stack pointer to _stack_top. This symbol is derived in the linker script from the top of os_ram:r0 still holds the original CPSR from mrs r0, cpsr at the top of _start, so the mode bits from r0 are reused to construct both the IRQ-mode and SVC-mode CPSR values.If main() ever returns (it does not under normal operation), execution falls through to:main() — OS kernel initialization
main() in os.c runs entirely in SVC mode with IRQs still disabled. It initializes every subsystem before enabling the timer interrupt.
Disable the watchdog
On BeagleBone Black the hardware watchdog starts ticking at boot. If not disabled (or periodically serviced), it resets the board after approximately one minute:On QEMU this is a no-op.
Initialize the ready queue
The scheduler uses a circular singly-linked list managed through a
ProcessQueue struct. sched_queue_init sets the queue to empty:Initialize the three processes
Three The entry point addresses come from the
Process structs — p0 (OS), p1, p2 — are initialized with process_init(). Each call sets the process entry point (pc), initial stack pointer (sp), and a default SPSR:process_init zeros all general-purpose registers and sets spsr = 0x00000013 (SVC mode, IRQs disabled):.venv file for the current target:| Process | BeagleBone PC | QEMU PC |
|---|---|---|
| p0 (OS) | 0x82000000 | 0x00010000 |
| p1 | 0x82100000 | 0x00060000 |
| p2 | 0x82200000 | 0x00080000 |
Enqueue user processes
Only
p1 and p2 are placed in the ready queue. p0 (the OS) is set directly as CurrProcess — it is the currently executing process and does not need to be scheduled in:sched_enqueue inserts at the tail of the circular list. After both enqueues the queue contains p1 → p2 → p1 → ... (circular).Start the hardware timer
timer_init() programs the platform timer (DMTimer2 on BeagleBone, SP804 on QEMU) to fire a periodic IRQ. The timer base address is PLATFORM_TIMER_BASE, injected at compile time:schedule() is never called and only p0 runs.Enable IRQs and enter the OS loop
With all data structures and hardware ready, The OS then enters its main loop, periodically printing a heartbeat message:The
main() clears the I-bit in CPSR to allow interrupts:disable_irq / enable_irq pair around PRINT prevents a context switch from interrupting a partially-written UART transmission.IRQ handler — preemptive context switch
Every timer tick triggersirq_handler in root.s. This is the core of the preemptive scheduler. The handler runs in IRQ mode, saves the interrupted process, picks the next one, and restores it — all without returning to C via a normal function call.
Save scratch registers to the IRQ stack
On entry to IRQ mode, This pushes r0–r12 (13 registers) and
lr_irq holds PC_interrupted + 4. The handler first saves all registers that might be clobbered:lr_irq onto the IRQ stack (56 bytes total).Save the interrupted process PCB
The handler reads To save
CurrProcess and copies the saved values from the IRQ stack into the PCB fields:sp_svc and lr_svc of the interrupted process, the handler must temporarily switch to SVC mode (because these are banked registers):Clear the timer interrupt and run the scheduler
schedule() in os.c enqueues the current process and dequeues the next:Restore the next process and return
After
schedule() updates CurrProcess, the handler loads the new process’s saved context:movs pc, lr is the ARM idiom for returning from an exception: in a privileged mode it copies SPSR into CPSR and branches to lr in a single instruction, atomically restoring the interrupted process.