2025. 1. 13. 23:39ㆍComputerScience/OperatingSystem
2024. 01. 13. Monday
우선 프로젝트를 원격으로 연결해두었다. :)
GitHub Desktop 앱도 오랜만에 들어가본다 , ,
https://github.com/SohyeonKim-dev/OperatingSystem
👩💻 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
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:
'ComputerScience > OperatingSystem' 카테고리의 다른 글
[OS Project] Chap6. C Standard Library (0) | 2025.01.17 |
---|---|
[OS Project] Chap5. Hello World! (0) | 2025.01.16 |
[OS Project] 운영체제 구현하기 프로젝트 (0) | 2025.01.10 |
[PIM] HEAM: Hashed Embedding Acceleration Using Processing-In-Memory (0) | 2024.06.25 |
[PIM] Processing-in-memory: A workload-driven perspective (0) | 2024.05.21 |