[OS Project] Chap4. Booting the Kernel

2025. 1. 13. 23:39ComputerScience/OperatingSystem

 

 

 

 

 

2024. 01. 13. Monday 

우선 프로젝트를 원격으로 연결해두었다. :) 

GitHub Desktop 앱도 오랜만에 들어가본다 , , 

https://github.com/SohyeonKim-dev/OperatingSystem

 

GitHub - SohyeonKim-dev/OperatingSystem: OS 만들기 프로젝트

OS 만들기 프로젝트 . Contribute to SohyeonKim-dev/OperatingSystem development by creating an account on GitHub.

github.com

 

 

 

 

 

File Structure

 

 

 

 

 

👩‍💻 brew로 qemu 설치 

brew install llvm lld qemu

 

 

 

 

 

Let's boot OpenSBI

셸 스크립트 파일 옵션들 설명

 

 

 

 

 

OpenSBI

: Open Source Supervisor Binary Interface

The Supervisor Binary Interface(SBI) is an API for OS kernels,
but defines what the firmware(OpenSBI) provides to an OS.

https://github.com/riscv-software-src/opensbi

 

GitHub - riscv-software-src/opensbi: RISC-V Open Source Supervisor Binary Interface

RISC-V Open Source Supervisor Binary Interface. Contribute to riscv-software-src/opensbi development by creating an account on GitHub.

github.com

 

 

 

 

 

OpenSBI (default firmware)

 

 

 

 

 

Linker script

  • The entry point of the kernel is the boot function.
  • The base address is 0x80200000.
  • The .text.boot section is always placed at the beginning.
  • Each section is placed in the order of .text, .rodata, .data, and .bss.
  • The kernel stack comes after the .bss section, and its size is 128KB.
ENTRY(boot)

SECTIONS {
    . = 0x80200000;

    .text :{
        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; /* 128KB */
    __stack_top = .;
}

 

 

 

 

 

 

  • ENTRY(boot) declares that the boot function is the entry point of the program. Then, the placement of each section is defined within the SECTIONS block.
  • The *(.text .text.*) directive places the .text section and any sections starting with .text. from all files (*) at that location.
  • The . symbol represents the current address. It automatically increments as data is placed, such as with *(.text). The statement . += 128 * 1024 means "advance the current address by 128KB". The ALIGN(4) directive ensures that the current address is adjusted to a 4-byte boundary.
  • Finally, __bss = . assigns the current address to the symbol __bss. In C language, you can refer to a defined symbol using extern char symbol_name.

 

 

 

 

 

Minimal kernel

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 pointer (sp) is set to the end address of the stack area defined in the linker script. Then, it jumps to the kernel_main function. It's important to note that the stack grows towards zero, meaning it is decremented as it is used. Therefore, the end address (not the start address) of the stack area must be set.

 

boot function attributes 

The boot function has two special attributes.

The __attribute__((naked)) attribute instructs the compiler not to generate unnecessary code before and after the function body, such as a return instruction. This ensures that the inline assembly code is the exact function body.

The boot function also has the __attribute__((section(".text.boot"))) attribute, which controls the placement of the function in the linker script. Since OpenSBI simply jumps to 0x80200000 without knowing the entry point, the boot function needs to be placed at 0x80200000.

 

extern char to get linker script symbols 

At the beginning of the file, each symbol defined in the linker script is declared using extern char. Here, we are only interested in obtaining the addresses of the symbols, so using char type is not that important.

We can also declare it as extern char __bss;, but __bss alone means "the value at the 0th byte of the .bss section" instead of "the start address of the .bss section". Therefore, it is recommended to add [] to ensure that __bss returns an address and prevent any careless mistakes.

 

.bss section initialization 

In the kernel_main function, the .bss section is first initialized to zero using the memsetfunction. Although some bootloaders may recognize and zero-clear the .bss section, but we initialize it manually just in case. Finally, the function enters an infinite loop and the kernel terminates.

 

 

 

 

 

Kernel Debugging

pc 80200014 shows the current program counter, the address of the instruction being executed.

Let's use the disassembler (llvm-objdump) to narrow down the specific line of code: