This project implements a basic multiprogramming operating system capable of performing context switches between multiple processes using a Round-Robin scheduler. The system is designed to run on ARM-based architectures, specifically supporting VersatilePB (QEMU) - ARM926EJ-S and BeagleBone Black - ARM Cortex-A8 (AM335x).
- Timer Support
- PCB Context Management
- Round-Robin Scheduling
- Kernel and User Separation
- System Call Interface (ABI)
- Memory Protection Unit (MMU configured as MPU)
- Hardware Fault Isolation
make bbbBeagleBone Blackmake qemuVersatilePB - Qemumake qemu-debugVersatilePB - Qemu with GDB
Note:
- This program is meant to be ran on Linux and requires
gcc-arm-none-eabi 13.2.1, optionallygdb/gdb-multiarchandqemu-system-arm. - Tested on Ubuntu and Fedora Linux.
While connected to a terminal like CoolTerm via UART at 1152000 bauds, run:
loady 0x82000000- Send the file at ./build/bin/os.bin
go 0x82000000
In another terminal:
- run
gdb/gdb-multiarch build/bin/os.elf target remote localhost:3333
Helpful debugging options
layout regsbreak <function name or number>continue
| Output | Event |
|---|---|
! |
Kernel Panic |
. |
Yield Scheduling |
... |
Normal Scheduling |
MODE_SWITCH KERNEL_TO_USER pid=<first> reason=initial_launch |
Initial Boot Path (Kernel → User) |
MODE_SWITCH USER_TO_KERNEL pid=<n> reason=timer_irq |
Interrupt Path (User → Kernel) |
MODE_SWITCH KERNEL_TO_USER pid=<m> reason=dispatch |
Interrupt Path (Kernel → User) |
MODE_SWITCH USER_TO_KERNEL pid=<n> reason=syscall id=<id> |
Syscall Path (User → Kernel) |
MODE_SWITCH KERNEL_TO_USER pid=<m> reason=syscall_return id=<id> rc=<rc> |
Syscall Path (Kernel → User) |
MODE_SWITCH USER_TO_KERNEL pid=<n> reason=fault type=<type> |
Exception Path (User → Kernel) |
MODE_SWITCH KERNEL_TO_USER pid=<m> reason=fault_recovery |
Exception Path (Kernel → User) |
The scheduler enforces a Round-Robin scheduling policy. It manages CPU time allocation by monitoring a quantum and handling process transitions based on three main triggers:
- Quantum Expiry: When the
quantumreaches zero, the current process is preempted to allow another process to run. - Process Termination: If the current process enters the
PROCESS_TERMINATEDstate, a new process is immediately scheduled. - Voluntary Yield: A process can choose to give up its remaining time via the
SYS_YIELDsystem call.
If any of these conditions are met, the scheduler moves to the next process in the ready queue and resets the quantum. When idling, the OS process will continue running.
Each process is allocated a dedicated memory region starting at a base address determined by the platform and its PID. The entry point for each process is at the beginning of its allocated 1MB region.
| PID | Process Name | Memory Start Address (VersatilePB) | Memory Start Address (BeagleBone Black) |
|---|---|---|---|
| 0 | Kernel | 0x00000000 |
0x82000000 |
| 1 | OS Process | 0x00100000 |
0x82100000 |
| 2 | User Process 1 | 0x00200000 |
0x82200000 |
| 3 | User Process 2 | 0x00300000 |
0x82300000 |
| ... | ... | ... | ... |
The PCB stores the context and metadata for each process, ensuring that the system can save and restore its state during context switches.
| Variable | Description |
|---|---|
pid |
Unique numeric identifier for the process. |
state |
Current lifecycle state (e.g., READY, RUNNING, WAITING). |
regs[13] |
Array storing the general-purpose registers (R0 to R12). |
pc |
Program Counter; the address of the next instruction to be executed. |
sp |
Stack Pointer; points to the top of the process's private stack. |
lr |
Link Register; stores the return address for function calls and exceptions. |
spsr |
Saved Processor Status Register; used to restore the CPU mode and flags. |
syscall_id |
Stores the ID of the current or most recent system call. |
fault_type |
Records the type of hardware exception encountered by the process. |
termination_reason |
Indicates why a process ended (Normal exit, Syscall, or Fault). |
exit_code |
The status code returned to the kernel upon termination. |
The kernel manages the lifecycle of each task through a set of defined states stored in the PCB.
| State | Value | Description |
|---|---|---|
PROCESS_NEW |
0 | The process is being created and its PCB is being initialized. |
PROCESS_READY |
1 | The process is waiting in the ready_queue to be assigned to the CPU. |
PROCESS_RUNNING |
2 | The process instructions are currently being executed by the processor. |
PROCESS_WAITING |
3 | The process is waiting for an event to occur. |
PROCESS_SUSPENDED |
4 | The process execution is paused but the context is preserved in memory. |
PROCESS_TERMINATED |
5 | The process has completed execution or has been aborted due to a fault. |
The system uses registers R0-R3 for system call interfacing. R0 is used to pass the Syscall ID and also stores the return code upon completion.
| Syscall Name | ID (R0) | Arg 1 (R1) | Arg 2 (R2) | Arg 3 (R3) | Return Code (R0) | Description |
|---|---|---|---|---|---|---|
SYS_YIELD |
1 | - | - | - | RC_SUCCES |
Voluntarily yield CPU to the next process. |
SYS_EXIT |
2 | Exit Code | - | - | Exit Code | Terminate the current process with an exit code. |
SYS_WRITE |
3 | File Descriptor | Buffer | Length | Bytes / Error | Write string to UART (FD 1 supported). |
| Return Code | Value | Description |
|---|---|---|
RC_SUCCESS |
0 | Operation completed successfully. |
RC_INVALID_SYSCALL |
-1 | The requested Syscall ID is not recognized. |
RC_INVALID_ARGUMENT |
-2 | Provided arguments are invalid. (FD != 1or 0 > Length > 256) |
RC_INVALID_USR_PTR |
-3 | Memory access violation (Buffer outside process boundary). |
RC_INVALID_ORIGIN |
-4 | Invalid syscall origin (Triggered by kernel). |
If a process requests an unknown Syscall ID, the kernel will immediately transition the process to the PROCESS_TERMINATED state, set the return register to RC_INVALID_SYSCALL, and trigger the scheduler to pick the next available task.
If a process triggers a syscall while on kernel mode, then the same procedure as unknown system behavior follows, except that the return code is set to RC_INVALID_ORIGIN. This is because syscalls are meant to be triggered while on user mode, and if someone with kernel privileges triggers it, then it means something went wrong.
When a hardware exception or processor fault occurs, the fault_dispatcher categorizes the error and enforces a system safety policy.
| Fault Classification / Type | Value | Outcome | Comment |
|---|---|---|---|
FAULT_NONE |
0 | - | Default Value |
FAULT_ALIGNMENT_ERROR |
1 | Terminate | MMU |
FAULT_ACCESS_FLAG |
2 | Terminate | MMU |
FAULT_INVALID_MAPPING |
3 | Terminate | Translation Fault / MMU |
FAULT_PRIV_VIOLATION |
4 | Terminate | Domain Fault / MMU |
FAULT_PERMISSION |
5 | Terminate | MMU |
FAULT_SYNC_EXT_ABORT |
6 | Terminate | Synchronous External Abort |
FAULT_ASYNC_EXT_ABORT |
7 | Terminate | Asynchronous External Abort |
FAULT_TTB_WALK_SEA |
8 | Terminate | Translation Table Walk (Sync External Abort) |
FAULT_TTB_WALK_SPE |
9 | Terminate | Translation Table Walk (Sync Parity Error) |
FAULT_MEM_ACCESS_SPE |
10 | Terminate | Memory Access (Synchronous Parity Error) |
FAULT_MEM_ACCESS_APE |
11 | Terminate | Memory Access (Asynchronous Parity Error) |
FAULT_DEBUG_EVENT |
12 | Terminate | - |
FAULT_INST_CACHE_MAINT |
13 | Terminate | Instruction Cache Maintenance |
FAULT_IMP_DEF_LD |
14 | Terminate | Implementation Defined (Lockdown) |
FAULT_IMP_DEF_CA |
15 | Terminate | Implementation Defined (Coprocessor Abort) |
FAULT_UNKNOWN |
-1 | Terminate | - |
FAULT_UND_INST |
-2 | Terminate | Undefined Instruction |
Recovery Strategy
The current kernel policy is Fail-Stop for individual processes. If any fault is detected:
- The current process is marked as
PROCESS_TERMINATED. - The
fault_typeandtermination_reasonare recorded in the process's PCB. - The scheduler swaps out the current process and selects the next
PROCESS_READYprocess. - The system continues running other processes.
Notes:
- Test on BeagleBone Black, otherwise the exceptions might not trigger on qemu.
- MMU faults require the MMU to be enabled and set up.
| Termination Reason | Value | Description |
|---|---|---|
EXIT_NORMAL |
0 | Process terminated normally. Default Value. |
EXIT_SYSCALL |
1 | Process terminated after a syscall (Successful or Failed). |
EXIT_FAULT |
2 | Process terminated after a fault. |