Skip to content

Booting the Kernel

When a computer is turned on, the CPU initialises itself and hardware. When happens before the OS starts:

  • In QEMU virt machine, 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

*