xv6 采用 qemu 作为运行平台,其 qemu 运行选项如下:
-machine virt: 机器类型设置为 RISC-V VirtIO board- virt 没有任何网络和存储设备,需要自己定义
- 当指定机器类型为 sifive_u 或 virt 时,有三种不同的 bios 选项:
default/none/<filename>
-bios none: 设置 BIOS 文件名defaultRISC-V qemu bios 的默认项,即自动加载默认的 OpenSBI firmware (包含在 QEMU 中),用户只需要指定内核文件none表示 qemu 不会自动加载任何 firmware,由用户负责加载需要的所有的镜像- 其他 则表示 firmware 文件名
-kernel $K/kernel: 设置内核映像- 要求镜像是 Linux 内核映像或者是 multiboot 格式
-m 128M: guest memory 大小为 128MB-smp $(CPUS): 设置 CPU 核数-nographic: 关闭图像输出,将串口 I/O 重定向到终端-global virtio-mmio.force-legacy=false: 设置 virtio-mmio 驱动的 force-legacy 属性全局默认为 false-drive file=fs.img,if=none,format=raw,id=x0: 设置 fs.img 作为 drive 的映像文件,输入文件类型 if=none, format=raw, 设置 id=x0-device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0: 添加设备 virtio-blk-device, 并设置该设备的属性 drive 为 x0, bus 为 virtio-mmio-bus.0
- power on : qemu 启动,cpu pc 设置到 kernel 的 _entry 处(
0x80000000)
- 初始化
- 运行 boot loader
- 存储于 ROM
- 将 xv6 kernel 加载到内存中(地址:
0x80000000)
0x0:0x80000000中包含 IO 设备的地址空间
- 进入 xv6 entry point
xv6 entry
_entry(inkernel/entry.S)- 为当前硬件线程设置: 设置 initial stack 栈顶指针
sp = stack0 + 4096 * (hartid + 1)- stack0 表示第 0 个栈的栈底 (in
kernel/start.c)
- stack0 表示第 0 个栈的栈底 (in
- 进入 start 函数
- 为当前硬件线程设置: 设置 initial stack 栈顶指针
start(inkernel/start.c)
准备切换到 supervisor mode- 将 mastatus 中的 MPP 置为 supervisor mode
- 将
main函数地址写入epc寄存器 - 设置 satp mode 为 Bare mode
- 将所有的异常和中断转交 supervisor mode 处理
- 配置物理内存保护:
pmpaddr0,pmpcfg0 - 编程时钟芯片以生成时钟中断:
timerinit()- 置位 mie.STIE :允许时钟中断 (事实上不需要,因为之前对 sie 的写入同样会影响到 mie)
- 使能 sstc 扩展(
stimecmp):menvcfg - 允许 S mode 使用
stimecmp和timecsr :mcounteren - 请求第一个时钟中断:设置
stimecmp
- 执行
mret(以 supervisor mode 跳转到 main 函数)
main(inkernel/main.c)- 初始化设备和子系统
- 创建第一个进程
userinitinkernel/proc.c
- 第一个进程
user/initcode.S- 将 exec 系统调用号加载到 a7 寄存器
- 调用 ecall 重新进入内核
- 内核执行 exec 系统调用 in
kernel/syscall.c - 返回用户空间,此时进程为
/initinuser/init.c- 创建新的设备文件 console
- 打开文件描述符0(console), 1(stdout), 2(stderr)
- 在 console 中启动 shell
系统正式启动。