第〇章 从零开始开发UEFI引导的64位操作系统内核

声明:此文章为本人在知乎首发的原创文章

一、概述

这可能是中文互联网最奇怪的内核开发教程了:

  • 这个教程不会教你如何写bootloader——它本不属于内核。
  • 这个教程不会教你x86架构的启动过程——16位实模式到32位保护模式到开启ia32e模式——这应该是bootloader需要考虑的。
  • 这个教程不会直接告诉你内核的各个模块应该怎么写——譬如内存管理、进程调度、中断机制——当需要实现一个个实实在在的功能时,它们自然会出现:你总是知道你为什么要写这段代码。
  • 这个教程不会教你如何构建内核编译系统——这本应是各位读者自己完成的——自己发挥的空间非常大。

某些不太需要熟练掌握的知识,会在需要的地方,插入其它的教程链接以便读者学习;而一些需要熟练掌握的知识,则不会插入链接。


二、前置知识

  • C语言是必须会的,本教程大多数代码都使用C语言。
  • 少许rust,这是我的个人偏好,用来取代c++;有些时候rust比c++要方便一些;rust代码的数量会很少,不会给读者太大的负担。
  • 汇编语言,负责管理cpu状态的指令必须直接使用。
  • 一种构建脚本(makecmakexmake等)
  • 合适的开发工具,功能过于复杂的IDE和功能过于简单的文本编辑器都不适合开发内核,推荐使用vscode。
  • 基础的Linux终端命令。
  • nano编辑器,编辑一些需要root权限的文件,你也可以选择使用vimemacs,不过nano最简单,用法都在界面中罗列出来。

不熟悉Linux的读者进:初学者必学的20个Linux基础命令


三、开发环境

我们需要一个Linux环境,Linux上有很多自带的开发工具,而使用Windows需要自己寻找能够替代的软件。
对于Windows用户来说,现在搭建一个Linux环境非常简单,我们不再需要花费大量和精力时间安装和配置一个虚拟机,docker和wsl都是很好的选择。

gcc
make/cmake/xmake
grub - 在docker和wsl环境下可能不会自带bootloader
qemu - 用于测试内核的虚拟机
git
rustup - 安装rust工具链
openssh - 运行在Windows上的vscode可以通过ssh连接打开项目
nbd - 挂载具有特定格式的虚拟磁盘镜像
gnome-boxes - 提供UEFI镜像

vscode可以通过插件Remote Development连接ssh、docker容器等,让我们能够像在本机上一样开发项目,远程连接时打开的终端也是远程环境中的终端:WSL+VSCode食用指南
使用Windows的读者需要在本机安装qemu而不是在docker或者wsl中安装qemu。

另外评论区有读者反映wsl和docker不太好用,这可能在不同的CPU、不同的windows版本上都有不同的效果,如果wsl和docker在你的环境中不好用,可以安装一个linux发行版的虚拟机:(贴出manjaro仅仅因为我比较喜欢arch系,和deb系相比仅仅是网络上的资料少一些,使用体验绝对不差)
Manjaro安装与配置指南
VMware虚拟机 安装Ubuntu 22.04系统教程(图文步骤,贼详细)
(夹带私货)


三、一些准备工作

1. 我们需要UEFI镜像,使得qemu能够以UEFI模式启动

gnome-boxes自带一系列UEFI镜像,安装后可以使用find命令找到文件OVMF_CODE.fd的路径,并将其复制到项目中来。
例如:

cd /
sudo find . -name "OVFM_CODE.fd"

搜索结果中会有路径包含edk2/x64的,这个文件就是以64位启动的UEFI镜像。
使用Linux发行版的读者可以直接用图形界面工具来完成这个过程。

2. 我们需要准备一个可以UEFI启动的虚拟磁盘文件

创建并格式化磁盘

创建一个qcow2格式虚拟磁盘

qemu-img create -f qcow2 镜像文件名 512M

然后挂载为nbd设备

sudo modprobe nbd
sudo qemu-nbd --connect=/dev/nbd0 镜像文件名

这时/dev/nbd0就可以作为正常的磁盘使用了。
接下来需要为这个磁盘分区,分区建议如下:

分区号分区类型分区大小分区设备名
1EFI系统100MB/dev/nbd0p1
2Linux root剩余空间/dev/nbd0p2

例如使用fdisk命令进行分区:sudo fdisk /dev/nbd0
使用fdisk分区的示例然后sudo partprobe /dev/nbd0确保新分的分区都被检测到。
给两个分区都写入fat32文件系统:

sudo mkfs.fat -F 32 /dev/nbd0p1
sudo mkfs.fat -F 32 /dev/nbd0p2

安装uefi启动的grub

首先在项目的目录下将两个分区分别挂载:

sudo mount -m /dev/nbd0p2 ./mnt         # 在当前目录创建mnt目录并将设备挂载
sudo mount -m /dev/nbd0p1 ./mnt/boot

写入grub

sudo grub-install --target=x86_64-efi --efi-directory=./mnt/boot/EFI --bootloader-id=GRUB --recheck --boot-directory=./mnt/boot /dev/nbd0

不过安装好的需要经过一些配置才能正常启动,因此我准备了一个repo 指向BIOS的野指针/well-confugured-grub,将它clone下来直接将内容复制到mnt/boot/grub/中即可:

git clone https://gitee.com/pointer-to-bios/well-confugured-grub.git
sudo cp -r well-configured-grub/* ./mnt/boot/grub/

在grub.cfg中,可以找到以下内容:

### BEGIN ###

# 在此编写menuentry

### END ###

只需要自己编写一个menuentry即可:

menuentry "你的系统名" {
    multiboot2 /内核文件
}

启动时,你的系统名将会显示在grub启动界面上。
本教程使用multiboot2标准作为内核的引导标准,因此使用multiboot2命令。
只要将构建好的内核文件放到mnt/boot/下就可以引导内核了,不过暂时不把内核放进去这个启动项也会显示。
配置好后将两个分区卸载:

sudo umount /dev/nbd0p1
sudo umount /dev/nbd0p2

最后将设备/dev/nbd0卸载:

sudo qemu-nbd --disconnect /dev/nbd0

这样,一个可引导的虚拟磁盘镜像就准备好了,wsl中没安装图形界面或使用docker的读者需要先将虚拟磁盘镜像和UEFI镜像从wsl或docker中移动到宿主机中,用qemu启动它:

qemu-system-x86_64 镜像文件 -m 1G -bios OVMF_CODE.fd

这个命令创建了一个UEFI引导的具有1GB内存的qemu虚拟机,运行这个命令可以看到grub启动界面:grub启动界面

一个挂载、卸载、启动可引导虚拟磁盘的make脚本

分区和写入grub的过程只需要执行一次,但是虚拟磁盘的挂载和卸载以及把内核放进虚拟磁盘的操作会在开发过程中频繁使用,可以编写一个构建脚本简化这些过程。我准备了一个make脚本供读者参考(这个make脚本没有写入grub的功能):

TARGET = ../src/metaverse.elf

.PHONY: run debug load mount unmount

BIOS = bios/OVMF_CODE.fd

# 运行
run:
	@sudo modprobe nbd
	@make load
	@qemu-system-x86_64 -m 4G metaverse.img -enable-kvm -bios ${BIOS}

# gdb调试
debug:
	@sudo modprobe nbd
	@make load
	@qemu-system-x86_64 -m 4G metaverse.img -enable-kvm -bios ${BIOS} -s -S

# 创一个镜像
create:
	@qemu-img create -f qcow2 metaverse.img 512M
	@sudo modprobe nbd
	@sudo qemu-nbd --connect=/dev/nbd0 metaverse.img
	@sudo partprobe /dev/nbd0
	@echo "你需要为这个虚拟磁盘写入一个GPT分区表并分出efi分区和根分区。"
	@sudo fdisk /dev/nbd0
	@sudo mkfs.fat -F 32 /dev/nbd0p1
	@sudo mkfs.fat -F 32 /dev/nbd0p2
	@sudo mount /dev/nbd0p2 mnt/
	@sudo mount -m /dev/nbd0p1 mnt/boot/
	@sudo mkdir mnt/boot/EFI/
	@make unmount

# 将内核文件放到镜像中
load:
	@echo loading kernel ...
	@make mount
	@sudo cp ${TARGET} mnt/boot/
	@make unmount

# 挂载镜像
mount:
	@sudo modprobe nbd
	@sudo qemu-nbd --connect=/dev/nbd0 metaverse.img
	@sudo partprobe /dev/nbd0
	@sudo mount /dev/nbd0p2 mnt/
	@sudo mount /dev/nbd0p1 mnt/boot/

# 卸载镜像
unmount:
	@sync
	@sudo umount /dev/nbd0p1
	@sudo umount /dev/nbd0p2
	@sudo qemu-nbd --disconnect /dev/nbd0

至此准备工作完成。

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

指向BIOS的野指针

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值