Bootloader
简介
Bootloader作为最早执行的代码,职责是进行平台相关的设置,为Kernel创造运行环境。某种意义上,Kernel是Bootloader的用户程序。
RustOS开发之初,借用的是《Writing an OS in Rust》第一版的代码,而现在作者已经推出了第二版,第一版不再维护。两个版本的Bootloader有很大不同:
第一版中使用GRUB,它可以完成从磁盘加载内核代码,及进入32位保护模式的工作,并以multiboot2格式提供必要信息。剩余部分Boot代码,完成进入64位long mode的工作,它和kernel共存于同一个Rust项目中,使用nasm编译。整个构建流程需要依赖很多Rust工具链以外的程序。
第二版中抛弃了GRUB,作者用Rust和汇编从头写了一个bootloader,从磁盘加载内核代码,进入64位long mode,并根据kernel elf设置好页表,以自定义格式向kernel提供信息。整个bootloader作为一个独立项目存在。此外作者还写了bootimage工具,用来编译kernel并和bootloader打包在一起生成bin,统一了构建流程。
RustOS for x86_64此前一直在使用第一版的boot流程,直到2018.09.12才迁移到bootimage工具链。此次改动后,配置开发环境的难度大大降低(尤其对macOS),Boot和Kernel也实现了分离。
至于RISCV,参考ucore的做法使用Berkeley bootloader(BBL),作为M态运行环境,而Kernel运行在S态。目前BBL直接借用ucore中的C代码,以后也许可以仿照x86_64的做法,编写Rust版本的bootloader。
哦对了,如果要跑在我们自己写的RV32I CPU上的话,只需有一个极小的bootloader。
代码及运行过程
x86_64 第一版(已废弃)
执行过程:
GRUB识别multiboot_header,从磁盘中加载内核到内存,进入32位保护模式
boot.asm:加载GDT,构造初始页表并开启页机制
long_mode_init.asm:进入64位long mode,设置rsp寄存器,跳转到Rust代码
多核启动过程:
entryother.asm:依次从16位->32位->64位,开启页机制,跳转到Rust代码(借用自xv6)
上述多核boot代码被放置到0x7000,在第一个核启动后,通过lapic向其它核发送信号,让它们从0x7000开始执行。
具体可参考博客第一版前两篇文章:
x86_64 第二版
执行过程,分为三阶段:
boot.s:从0x7c00起<512B的代码,被BIOS放入内存后执行。工作是进入保护模式,从磁盘中加载bootloader剩余代码到内存。
second_stage.s:将磁盘中的kernel(elf格式文件)加载到内存(0x400000),设置初始页表,开启页机制,进入64位long mode。
main.rs:解析elf文件并在页表中建立映射。写入BootInfo信息,跳转到kernel入口点。
目前此bootloader尚无官方文档☹️
RISCV for QEMU
bbl位于 riscv-pk
文件夹,相比官方仓库,经过了精简和修改。
bbl执行完毕后,跳转到固定地址(0x80020000),代码在此,之后仅需设置sp寄存器,然后跳转到Rust代码。
bbl除了作为Bootloader,还提供M态运行环境,S态的Kernel可通过系统调用ecall使用bbl的服务,包括读写串口、CPU核间通信等。
RISCV for FPGA
一个极小bootloader,代替bbl。
TODO
Last updated