How KUDOS Bootstraps¶
This section explains the bootup process of KUDOS from the first instruction ever executed by the CPU(s) to the point when userland processes can be started. This is not an introduction to running KUDOS in YAMS, for that, we refer you to Using KUDOS.
Modern CPUs have at least two modes of operation: a kernel mode, which allows for all assembly instructions, including privileged instructions, to be executed, and user mode, which does not allow privileged instructions to be executed.
Assembly code is both architecture-specific, and difficult to write and maintain. We therefore wish to keep the amount of assembly code in the kernel source code to the absolute minimum, running C code as soon as possible after boot.
In order to run C code, an expandable region of memory called a “stack” is required. When a function is called, depending upon the specific call conventions, function parameters are pushed onto the stack (or placed in registers), and space for local variables is allocated. The return address of the function, i.e. the address of the next instruction to be executed after the function completes execution is also pushed onto the stack.
The stack pointer is a CPU register that contains the memory address corresponding to the top of the stack. The program counter / instruction pointer registers contain the memory address of the next instruction to be executed by the CPU. This instruction is fetched from memory, and the CPU performs the corresponding operation on the values contained in the specified CPU registers. Machine instructions can read and write data from specified memory addresses into CPU registers.
Booting KUDOS/x86_64 with GRUB2¶
On boot, the BIOS, which is mapped to a specific location in memory, is run. The BIOS detects what hardware is present, placing this information in memory, and runs a bootloader on a specified device, e.g. a hard disk.
GRUB is a generic bootloader, which can be used by operating systems that support the multiboot specification, such as KUDOS. When the bootloader starts, the CPU is in 16-bit real mode.
GRUB loads a kernel image, and begins its execution at the entry point, which was defined when linking, by setting the instruction pointer to this memory address. It starts this execution in 32-bit protected mode. Protected mode provides memory protection, i.e. user and kernel modes, such that processes cannot interfere with one another’s execution. This allows for a multi-tasking operating system, as the kernel is protected from interference by processes running in user mode.
There is a clash of terminology here; a CPU in protected mode has the ability to switch between user and kernel mode, whereas real mode provides no such protection.
The entry point is kudos/init/x86_64/_boot.S
, which sets up long mode (64
bit mode), and a stack for the execution of C code, before jumping to the
architecture-specific init
function, located in
kudos/init/x86_64/main.c
.
Starting Subsystems¶
To provide operating system service, various subsystems are required, in order
to coordinate resource usage. These are started in the respective,
architecture-specific init
functions in init/$ARCH/main.c
.
stalloc
- Provides permanent, static kernel memory allocation.
polltty
- Allows input from the keyboard, and text output to the display. This is a polling device, so getting input/putting output requires repeatedly checking the status of the keyboard/display in a busy-wait loop.
interrupt
- Allows devices and user processes to interrupt the CPU when they have an event that must be handled. This can be used to prevent the CPU from having to poll. The same subsystem handles exceptions.
thread_table
- Allows the operating system to have mutiple threads of execution. It is initialised, by creating a thread table to keep track of the threads. A thread of execution is (the state of) some running code, e.g. registers such as the instruction pointer.
sleep_queue
- A syncronization mechanism, allowing threads to wait on resources currently in use by other threads.
semaphore
- Another, high-level, inter-thread syncronization mechanism.
device
- Stores information about I/O devices, such as the TTY device, in a device table. In KUDOS, devices implement generic interfaces, minimizing code duplication.
vfs
- The Virtual File-System (VFS) is a generic file-system abstraction over more specific file-systems, such as the KUDOS Trivial File System (TFS).
sheduler
- Performs the actual switching of threads on and off CPU cores, saving the context of the thread, such as its registers.
vm
- Allows the operating system to place pages non-contiguously in memory, by
mapping addresses from physical addresses to virtual addresses. Once this
subsystem has been initialised, the
stalloc
subsystem is disabled.
Finally, a new thread is created and run on the CPU instead of the current
thread. On kudos-mips32
the other CPUs are now released from the wait-loop.
The new thread executes the architecture-independent function
init_startup_thread
(defined in kudos/init/common.c
) which sets up the
last two things:
- All filesystems implementing the VFS interface are made available, or
mounted, by
vfs_mount_all
. - The program corresponding to the
initprog
argument given at boot is loaded into memory, and execution continues at the address of the first instruction in this program. If noinitprog
argument was given, the functioninit_startup_fallback
is called instead.