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 第二版

官方仓库

执行过程,分为三阶段:

  1. boot.s:从0x7c00起<512B的代码,被BIOS放入内存后执行。工作是进入保护模式,从磁盘中加载bootloader剩余代码到内存。

  2. second_stage.s:将磁盘中的kernel(elf格式文件)加载到内存(0x400000),设置初始页表,开启页机制,进入64位long mode。

  3. 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