一个最小的内核
在 上一节中我们搭建了编写内核程序的最基本的项目结构,并且使用了nightly版的Rust编译器,在本节中我们构建一个最小的适用于x86结构的64位系统,我们
启动
当你按下电源按钮的时候,它开始执行存储在主板ROM中的固件代码,此代码执行开机自检,检测可用的RAM,然后预处理CPU和硬件,接下来,引导系统将会接管,它将会完成模式转换,搜索内核入口地址并跳入内核
在x86
有2种固件:第一种为基本输入输出系统(Basic Input Output System, BIOS),和新出现的统一可扩展固件接口(Unified Extensible Firmware Interface, UEFI), BIOS已经有些年头了,但是很简单,它可以支持1980年以后的所有x86机器,UEFI更现代化,具有更多的功能,但是设置起来也更复杂
大多数的x86系统都支持BIOS,包括最新的UEFI,UEFI可以模拟BIOS,这很棒,因为你可以在最近几个世纪的所有机器上使用相同的引导逻辑,但是,这种广泛的兼容性同时也是BIOS引导的最大缺点,因为在引导之前将CPU置于称为实模式的16位兼容模式,以便1980年代的古老引导程序仍然可以使用。
我们暂时采用phil-opp编写的bootimage(x86_64),后面我们会自己实现一个
在Cargo.toml中添加如下内容
[dependencies]
bootloader = "0.8.0"
只添加依赖不能创建一个Bootloader镜像,我们需要在bootloader编译完后链接到我们的内核,cargo不知支持后期制作脚本
为了解决这个问题,我们需要一个bootimage
工具,该工具首先编译内核和引导程序,然后将它们链接在一起以创建可引导磁盘映像,执行以下命令来安装该工具
cargo install bootimage --version "^0.7.7"
^0.7.7
叫做caret requirement, 它的含义是0.7.7版本或以后的版本,例如在现有的版本中发现一个bug并发布了0.7.8或0.7.9版本,则cargo将自动使用最新版本,只要它仍然是0.7.x版本即可
但是,它不会选择版本0.8.0,因为它不被认为兼容
请注意,默认情况下,Cargo.toml中的依赖项要求是脱字符,此规则应用于我们的引导加载程序依赖项
为了能够使用bootimage来编译bootloader,我们需要llvm-tools-preview
组件可以通过执行rustup component add llvm-tools-preview
来安装它(由于众所周知的原因,安装超级慢╮(╯▽╰)╭)
编译
cargo可以通过--target
参数指定不同的系统,该参数使用target triple
表示了CPU架构,发行商,系统和ABI
例如 我们的CPU结构是x86_64的发行商为苹果,系统是macos可以用以下形式表示
x86_64-apple-darwin
然后我们就可以使用以下命令来编译了(我的系统是Ubuntu)
cargo build --target x86_64-unknown-linux-gnu
现在我们只有一个参数命令就这么长了,后面参数还会添加许多参数,这样有些麻烦,我们可以写成shell脚本或使用make
LLVM
在编译之前我们需要了解以下LLVM的一些参数
Target Triple
使用target triple字符串描述要编译的目标机器语法格式如下
ARCHITECTURE-VENDOR-OPERATING_SYSTEM
ARCHITECTURE-VENDOR-OPERATING_SYSTEM-ENVIRONMENT
这样LLVM在编译的时候会将它传递到后端,以便LLVM为适当的体系结构生成代码
因为我们的程序要在裸机上直接运行,所以就不能使用本机系统的target triple我们可以使用llvm-target=x86_64-unknown-none
即可,我们没有指定平台所以我们需要提供其他参数来告诉LLVM如何编译
Data Layout
LLVM需要大多数字段才能为该平台生成代码。例如data-layout
字段定义各种整数,浮点数和指针类型的大小。
使用Data Layout指定特定于目标的数据布局字符串,该字符串指定如何在内存中布局数据,语法如下
布局规范由一系列规范列表组成,并用减号字符(’-’)分隔。每个规范都以字母开头,并且可以在字母之后包含其他信息,以定义数据布局的某些方面。规范的格式如下
E