10 KiB
10 KiB
marp |
---|
true |
xv6-k210 移植报告
- 报告人: 车春池
- 华中科技大学本科三年级
- 时间: 2020-12-26
- 地点: 北京中科院计算所
(`-') (`-') <-.(`-')
(OO )_.-> _(OO ) __( OO)
(_| \_)--.,--.(_/,-.\ ,--. (`-') '-'. ,--. .----. .--. .----.
\ `.' / \ \ / (_/ / .' ( OO).-> | .' / \_,-. | /_ | / .. \
\ .') \ / / . / -. (,------. | /) .' .' | | | / \ .
.' \ _ \ /_)' .-. \ `------' | . ' .' /_ | | ' \ / '
/ .'. \ \-'\ / \ `-' / | |\ \ | | | | \ `' /
`--' '--' `-' `----' `--' '--' `------' `--' `---''
前言
xv6
和xv6-riscv
是MIT
写的教学操作系统,在 github 上开源xv6
官方目前只支持qemu
平台k210
:kendryte
产商于 2018 年发布的RISC-V
双核 64 位处理器RustSBI
:华科网安同学洛佳
提供的强大的Rust
嵌入式项目,为移植工作提供很大帮助
当前成果
运行流程
- 需要
RISC-V
编译链,用它来编译二进制内核镜像,可以是riscv64-unknown-elf
或者riscv64-linux-gnu
- 需要搭载
k210
芯片的开发板:Maixpy
系列,淘宝有售 - SD 卡:可选,如果不跑文件系统的话
- 在
github
上获得项目源码:git clone https://github.com/SKTT1Ryze/xv6-k210
- 用读卡器把 SD 卡连接到电脑
- 进入到
xv6-k210
目录下运行命令make sdcard
,然后Makefile
会打包一个文件系统镜像写入到 SD 卡 - 将 SD 卡插到
k210
板子上,并用 type-C 线连接k210
板子到电脑上 - 在
xv6-k210
目录下运行命令make run
- 查看
USB
端口:ls /dev/ | grep USB
K210
- 两个核心,双核对等,具备独立 FPU
- 64 位处理核
- 标称频率为 400MHZ
- 双精度浮点处理单元
- PLIC 支持高级中断管理
- CLINT 支持 CPU 内置定时器中断与跨核心中断
- 8M 的片上 SRAM
K210 SRAM
模块名称 | 映射类型 | 开始地址 | 结束地址 | 空间大小 |
---|---|---|---|---|
通用 SRAM 存储器 | 经 CPU 缓存 | 0x80000000 | 0x805FFFFF | 0x600000 |
AI SRAM 存储器 | 经 CPU 缓存 | 0x80600000 | 0x807FFFFF | 0x200000 |
通用 SRAM 存储器 | 非 CPU 缓存 | 0x40000000 | 0x405FFFFF | 0x6000000 |
AI SRAM 存储器 | 非 CPU 缓存 | 0x40600000 | 0x407FFFFF | 0x200000 |
K210 VS QEMU
- 指令集标准版本不同,qemu 是 v1.11,k210 是 v1.9.1
MMIO
内存布局不同- 虚拟文件系统 vs SD 卡上的文件系统
RustSBI
- 脱胎于 2020 年暑假鹏城实验室的“rCore代码之夏”活动
- 操作系统二进制接口标准(SBI 标准)的一种实现
- Rust 语言编写
- 模块化设计,方便支持新的平台
- 进入国际 RISC-V SBI 实现列表,获得编号 4:
Implementation ID | Name |
---|---|
0 | Berkeley Boot Loader (BBL) |
1 | OpenSBI |
2 | Xvisor |
3 | KVM |
4 | RustSBI |
5 | Diosix |
移植工作一览
- 多核启动
- 内存分配
- 虚拟内存管理
- 中断处理
- S 态外部中断
- 用户多进程
- SD 卡驱动
- 文件系统
- 软硬件协同调试
- 中文文档
多核启动
- 链接脚本 k210.ld:调整内核镜像布局和指定内核入口点
RustSBI
将处理器核从M
态切换到S
态,然后跳转到内核起始地址- entry_k210.S:操作系统内核运行的第一段代码,对应链接脚本中的内核入口点
main.c
:进入supervisor
模式(S 态)RustSBI
提供接口,使得可以在启动的第 0 个核中唤醒其他核
多核启动
内存分配
kalloc.c
- 可分配的内存空间范围:
kernel_end
~PHYSTOP
- 内存分配初始化:void kinit(),初始化锁,开拓内存空间
- void* kalloc():4096 字节
- 内核维护一个链表:空闲链表
虚拟内存管理
vm.c
- 维护一个内核页表
kernel_pagetable
- 在 void kvminit() 里面进行
内核空间
虚拟地址到物理地址的映射 - 进行 MMIO,内核空间,用户态空间, RustSBI 等内存空间的映射
- 在 void kvminithart() 里面将内核页表的首地址放到
satp
寄存器,然后运行sfence.vma
指令
虚拟内存管理
但是!! K210 采用的是 1.9 版本的 RISC-V 标准,不存在 sfence.vma 这条指令,只有旧版指令 sfence.vm
怎么办?求助 RustSBI
虚拟内存管理
RustSBI 如何解决这个问题?
sfence.vma
作为异常指令被RustSBI
捕获- 读取
satp
(读取ppn
) - 根据第二步读取的值写
sptbr
(v1.9 指令集版本使用的寄存器,相当于satp
)设置页表 - 设置
mstatus
中相应的位开启分页(内核无法做这个工作) - 运行旧版指令
sfence.vm
清空TLB
- 写
mepc
并返回到sfence.vma
的下一条指令去运行(mepc + 4
)
虚拟内存管理
中断处理
trap.c
- void kerneltrap():S 态中断或异常陷入这个函数,在这里处理或者转发
- void usertrap():U 态中断或异常陷入这里
- int devintr():判断中断的来源(比如时钟中断返回 2)
中断处理
中断处理初始化工作:
- void trapinithart()
- 将
kernelvec
地址写入到stvec
kernelvec
函数在kernelvec.S
汇编文件里面,保存上下文,进入kerneltrap
,恢复上下文,返回- 设置
sstatus.sie
位 - 设置
sie
开启外部中断,时钟中断和软件中断
时钟中断
通过 RustSBI 设置时钟中断
timer.c
- 参考
rCore
实现 - void set_next_timeout():设置下一次时钟中断的时间
- void timerinit():初始化时钟中断
- void timer_tick():触发时钟中断的时候进入这里,这里也会设置下一次时钟中断
- 神奇的 bug:set_next_timeout 中的 printf("");
S 态外部中断
以下参考吴一凡学长的文档,如有雷同,纯属照搬。
为什么需要移植 S 态外部中断?
- RustSBI 运行在 M 态,OS 内核运行在 S 态
- 想要使用 S 态众多的数据结构来对外部中断进行处理
移植 S 态外部中断遇到的问题:k210
上没有 S 态外部中断!
怎么办?
求助 RustSBI
S 态外部中断
RustSBI 使用黑科技解决 K210 上 S 态外部中断
感谢吴一凡学长移植 rCore
到 k210
上的文档和洛佳同学的解惑。
不敢班门弄斧,只说一下步骤:
RustSBI
提供一个接口
match cause {
Trap::Exception(Exception::SupervisorEnvCall) => {
if trap_frame.a7 == 0x0A000004 && trap_frame.a6 == 0x210 {
// We use implementation specific sbi_rustsbi_k210_sext function (extension
// id: 0x0A000004, function id: 0x210) to register S-level interrupt handler
// for K210 chip only. This chip uses 1.9.1 version of privileged spec,
// which did not declare any S-level external interrupts.
...
} else {
...
}}}
- S 态外部中断处理函数
supervisor_external_handler
- 将这个函数指针作为参数调用
RustSBI
提供的接口 - 外部中断将会在
supervisor_external_handler
中处理
用户多进程
proc.c
&trap.c
&trampoline.S
&swtch.S
- 内核维护一个进程队列
- 每个进程拥有独立的页表和独立的用户态空间
- 在 scheduler() 进行进程调度
- 通过 swtch(struct context*, struct context*) 进行上下文切换
- 每次时钟中断内核会调用 yield() 函数,这个函数里面调用 swtch 进行进程切换
- 双核跑多进程
- 目前的用户进程是写死在内核里面的,后面文件系统移植好了才能真正意义上跑用户进程
- 移植文档: https://github.com/SKTT1Ryze/xv6-k210/wiki/Process-Management
用户多进程
SD 卡驱动
sdcard.c
- 官方 SDK demo 实现了读取 sd 卡的例程
- 暴力出奇迹,移植官方 SDK 到内核中
- 问题:官方 SDK 编译链不同,代码太多,依赖关系复杂
- 通过 SPI 协议与 SD 卡通信
- 和很多外设打交道,比如 GPIO,FPIO 等等,还需要调整时钟信号
- 最终将官方 SDK 部分模块移植到了内核中,成功通过 SD 卡读取测试
- SD 卡读写还不稳定
- C 开发环境缺乏包管理工具
文件系统
- 由于 SD 卡读写不稳定的问题还没解决,还没完成
- xv6 文件系统 or Fat32 文件系统 or 全都要
软硬协同调试
- 硬件调试非常非常麻烦,希望有软件调试环境
Makefile
+ 条件编译make run [platform=xxx]
- 类似于封装硬件抽象层
RustSBI
基本上处理了大部分平台无关性- 软件调试,一键部署
依赖于 RustSBI 的模块
- 多核启动
- 内存分配
- 虚拟内存管理(
sfence.vma
指令) - 时钟中断
- S 态外部中断(其实有 bug 还没修)
- 用户多进程
- SD 卡驱动
- 文件系统
- 平台无关性
中文文档
- 移植文档
xv6-riscv
原理文档
致谢
- 感谢比赛方和陈渝老师提供这次演讲机会,并且感谢邵志远老师一直以来对我的支持
xv6-k210
项目得到了很多同学的支持,特别是华科 18 级计卓班的刘一鸣和陆思彤同学,进程管理这部分的调试工作大部分是他们在做- 感谢
RustSBI
项目 - 感谢
rCore-Tutorial
项目和今年暑假在鹏城实验室的代码之夏
活动,让我踏入了Rust
语言和体系结构的大门 - 感谢洛佳同学和吴一凡学长技术上的帮助
谢谢各位
华中科技大学
计算机科学与技术学院
校交 1801 班
车春池