Booting the Kernel
When a computer is turned on, the CPU initialises itself and hardware. When happens before the OS starts:
- In QEMU
virtmachine, OpenSBI is the equivalent of BIOS/UEFI
Linker Script
A linker script is a file which defines the memory layout of executable files. Based on the layout, the linker assigns memory addresses to functions and variables.
ENTRY(boot)
SECTIONS {
. = 0x80200000
.text: ALIGN(4) {
KEEP(*(.text.boot))
*(.text .text.*);
}
.rodata: ALIGN(4) {
*(.rodata .rodata.*);
}
.data: ALIGN(4) {
*(.data .data.*);
}
.bss : ALIGN(4) {
__bss = .;
*(,bss .bss.* .sbss .sbss.*);
__bss_end = .;
}
. = ALIGN(4);
. += 128 * 1024
__stack_top = .;
}
| Section | Purpose | Initial Value | Occupies Disk Space? |
|---|---|---|---|
.text |
Machine Instructions | N/A | Yes |
.rodata |
Constants/Strings | Fixed by Dev | Yes |
.data |
Initialized Globals | Non-zero | Yes |
.bss |
Uninitialized Globals | Zero | No (only size is stored) |
| ### Program Layout |
[14] .text PROGBITS 0000000000001040 00001040
[16] .rodata PROGBITS 0000000000002000 00002000
[24] .data PROGBITS 0000000000004000 00003000
[25] .bss NOBITS 0000000000004014 00003014
Minimal Kernel Code
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef uint32_t size_t;
extern char __bss[], __bss_end[], __stack_top[]
void *memset(void *buf, char c, size_t n) {
uint8_t *p = (uint8_t)* buf;
while(n--) {
*p++ = c;
}
return buf;
}
void kernel_main(void) {
memset(__bss, 0, (size_t) __bss_end - (size_t) __bss);
for(;;);
}
__attribute___((section(".text.boot")))
__attribute__((naked))
void boot(void) {
__asm__ __volatile__(
"mv sp, %[stack_top]\n" // set the stack pointer
"j kernel_main\n" // Jump to the kernel main function
:
: [stack_top] "r" (__stack_top) // Pass the stack top address as %[stack_top]
);
}
The kernel entry point
The execution of the kernel starts from the boot function, which is specified as the entry point in the linker script. In this function, the stack sp is set to the end address of the stack area defined in the linker script. Then it jumps to kernel_main function. It is important to note that the stack grows to zero, meaning it is decremented as it is used. Therefore, the end address if the stack area must be set.
*Boot function attributes
*