惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
Recorded Future
Recorded Future
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
博客园 - Franky
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
T
Troy Hunt's Blog
The Register - Security
The Register - Security
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
S
Security Affairs
博客园 - 司徒正美
S
Schneier on Security
I
InfoQ
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
腾讯CDC
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
V
Vulnerabilities – Threatpost
C
Check Point Blog
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cisco Blogs
Schneier on Security
Schneier on Security
O
OpenAI News
K
Kaspersky official blog

VnYzm的博客

游戏玩后感:ReLief:献给亲爱的你 我的周边(谷子)分享 游戏玩后感:Kanon 简谱:致真实的你 《Rust中常见的有关生命周期的误解》学习笔记 简谱:StarMap 简谱:かく咲きたらばいと恋ひめやも 简谱:东风 简谱:无法诉说的思念 简谱:Girlish 游戏玩后感:时钟机关的Layline 简谱:风之琶音 简谱:星空的记忆 简谱:因为遇见了你 简谱:月童 番茄简谱脚本转调器 游戏玩后感:青空下的约定:Refine 游戏玩后感:在这苍穹展翅 书籍读后感:控制论与科学方法论 游戏玩后感:恋爱表达式 游戏玩后感:樱之诗 MLIR-tutorial学习笔记 游戏玩后感:潜伏之赤途 游戏玩后感:纯爱咖啡厅:帕露菲重制版 游戏玩后感:智以泪聚 游戏玩后感:初雪樱 游戏玩后感:告别回忆:从今以后 游戏玩后感:梦灯花 游戏玩后感:金辉恋曲四重奏 游戏玩后感:五彩斑斓的世界 昇腾310P使用记录 游戏玩后感:AIR 游戏玩后感:弹丸论破 游戏玩后感:流景之海的艾佩莉亚 Xilinx_HLS上板过程记录 游戏玩后感:告别回忆2 游戏玩后感:恋爱绮谭 Faiss和Rapidsai_Raft使用记录 游戏玩后感:近月少女的礼仪 游戏玩后感:樱色之云,绯色之恋 游戏玩后感:幸运草的约定 游戏玩后感:星之梦、候鸟和丸子与银河龙 游戏玩后感:白色相簿2 Windows上使用VTune分析PyTorchExtension调用的Cpp程序 SpinalHDL上板过程记录 游戏玩后感:仰望夜空的星辰 最简单的算卦方法之一:梅花易数法 游戏玩后感:苍之彼方的四重奏 krkr引擎解包工具介绍 自定义CUDA实现PyTorch算子的四种简单方法 游戏玩后感:星空的记忆 游戏玩后感:9nine 游戏玩后感:AtriMyDearMoments 游戏玩后感:极限脱出 游戏玩后感:魔女的夜宴 SSH实现多跳代理 动漫观后感:向山进发 flv重封装H264、AAC流 动漫观后感:夏日重现 CSP模板 游戏玩后感:海沙风云 动漫观后感:灵能百分百 游戏玩后感:交响乐之雨 游戏玩后感:爱上火车LastRun 游戏玩后感:LittleBustersEX 游戏玩后感:SummerPockets 游戏玩后感:逆转裁判 Ultra96V2开发板简单使用 SpinalWorkshop实验笔记(三) SpinalWorkshop实验笔记(二) SpinalWorkshop实验笔记(一) PYNQ开发板上使用USB声卡+OSS兼容层播放音频 TestOS移植K210开发板 rCore-Tutorial-Book-v3学习笔记(七) 动漫观后感:凉宫春日的忧郁 rCore-Tutorial-Book-v3学习笔记(♭七) rCore-Tutorial-Book-v3学习笔记(六) rCore-Tutorial-Book-v3学习笔记(五) rCore-Tutorial-Book-v3学习笔记(四) rCore-Tutorial-Book-v3学习笔记(三) rCore-Tutorial-Book-v3学习笔记(二) 游戏玩后感:RewritePlus MIT-6.S081-2020实验(xv6-riscv64)十一:net MIT-6.S081-2020实验(xv6-riscv64)十:mmap MIT-6.S081-2020实验(xv6-riscv64)九:fs MIT-6.S081-2020实验(xv6-riscv64)八:lock MIT-6.S081-2020实验(xv6-riscv64)七:thread MIT-6.S081-2020实验(xv6-riscv64)六:cow MIT-6.S081-2020实验(xv6-riscv64)五:lazy MIT-6.S081-2020实验(xv6-riscv64)四:traps MIT-6.S081-2020实验(xv6-riscv64)三:pgtbl MIT-6.S081-2020实验(xv6-riscv64)二:syscall 动漫观后感:吹响吧上低音号 MIT-6.S081-2020实验(xv6-riscv64)一:util 快速生成网络mp4视频缩略图技术 用plantuml画图示例 QQ缩略图和大图不同实现 Python制作字符图片 动漫观后感:命运石之门 使用OpenCV对图片进行特征点检测和匹配
rCore-Tutorial-Book-v3学习笔记(一)
VnYzm · 2021-02-24 · via VnYzm的博客

概述

最近看到清华的一个操作系统教程rCore-Tutorial-Book,和其他实验不同的是,这个教程介绍的是完全从零开始实现一个Riscv操作系统。教程所用的编程语言是Rust,但是我的Rust水平只到勉强能看懂代码的地步,所以打算用C语言照着实现一遍。虽然说是照着实现,但不同的语言还是会带来不少细节的不同,相比于同个语言照抄代码还是能注意到不少平常没在意的东西。因此开个坑,记录一下遇到的问题,代码放在Github上了。由于是练习,代码写得比较乱。

第一部分是实现一个最小化内核,即能让qemu-system-riscv跑起来并输出Hello world!然后退出就算成功。得益于SBI的帮助,我们可以少研究很多东西。这里大致介绍一下SBI,SBI指的是一套辅助操作系统内核编程的工具,它包含两部分:

  • boot loader:即在机器态里初始化裸机上的一些寄存器和硬件设备,把操作系统内核读取到对应的内存区域,然后进入内核态(Supervisor态,直译为监管者态,因为是操作系统内核主要运行的特权级,后面均称内核态),开始执行内核的第一条指令;

  • 处理内核态系统调用的机器态代码:在内核态设置好存储调用号和参数的寄存器,然后执行指令ecall,系统就会进入机器态,由SBI执行一些机器态才能做的操作,然后返回内核态。

没有SBI,机器态相关的代码就得自己写了,xv6就是这样做的,所以xv6除了进程、文件、内存管理这些模块,还有一些充满晦涩代码的模块,这些就是在处理机器态和硬件相关的操作;riscv-pk的系统引导用的是BBL(Berkeley Boot Loader),需要机器态做的任务则转发给spike模拟器的htif模块,由宿主系统执行这些任务。

本项目我用的是RustSBI,和教程用的一样,虽然是用Rust写的,但是已经打包成二进制文件了,可以直接使用。原先我打算使用qemu自带的OpenSBI,但是不知道为什么,在调用OpenSBI的退出程序功能时,qemu会报错,没法正常退出,RustSBI则不会。

内容

首先是SBI的系统调用,由于涉及寄存器操作,需要用到内联汇编:

isize sbi_call(usize id, usize a0, usize a1, usize a2) {
    isize ret;
    asm volatile (
            "mv x10, %1\n"
            "mv x11, %2\n"
            "mv x12, %3\n"
            "mv x17, %4\n"
            "ecall\n"
            "mv %0, x10\n"
            :"=r"(ret)
            :"r"(a0), "r"(a1), "r"(a2), "r"(id)
            :"memory", "x10", "x11", "x12", "x17"
            );
    return ret;
}

本项目中为了简化代码以及与教程保持一致,把unsigned long long定义成usize,long long定义成isize了。这里要注意的是内联汇编的格式,和Rust不同,C语言不能在内联汇编的函数中绑定变量和寄存器(如果写的是x86汇编好像可以,riscv就不行了)(可以在声明变量的同时指定该变量必须用某寄存器存,但语法比较麻烦),所以需要先把变量存到对应寄存器才可以。这样最后一个冒号右侧的限制符也必须添加上这三个寄存器的名字,否则编译器可能会编译出错误的代码,打个比方说,没有限制符,上面的程序编译出来的结果可能会在内联汇编前面用x10存a2,那么进入内联汇编后程序首先将a0赋给x10,a2就被覆盖掉了,程序就出错了。

然后是教程中说的一个bss段清零的操作:

extern char sbss, ebss;
void clear_bss() {
    for (char *i = &sbss; i < &ebss; i++) *i = 0;
}

这里sbss、ebss都是来自linker script的符号。符号可以定义在C程序中,可以定义在汇编代码中,可以定义在linker script中,只要符号的强定义不是在当前C程序,那么对于当前C程序,这个符号可以解释成任何类型,因为它只是一个位置标识。在上面的代码中,我把sbss和ebss解释成linker script中这两个符号指向的第一个字节,那么就只要对这两个字节的地址之间的空间清零就行了。教程里面是把两个符号解释成地址,我觉得C语言应该也一样,即下面的写法和上面应该是等价的:

void sbss();
void ebss();
void clear_bss() {
    for (char *i = (char *)sbss; i < (char *)ebss; i++) *i = 0;
}

然后就是这个函数正确编译需要在编译选项里加-mcmodel=medany,不然会报错,具体原因我没看懂,好像是默认对符号地址有什么限制。

最后是编译选项,我写了个makefile:

default: os.bin
    riscv64-unknown-elf-gcc os.c printf.c entry.S -T linker.ld -ffreestanding -nostdlib -g -o os -mcmodel=medany
    riscv64-unknown-elf-objcopy os --strip-all -O binary os.bin
    qemu-system-riscv64 -machine virt -nographic -bios rustsbi-qemu.bin -device loader,file=os.bin,addr=0x80200000

这里-ffreestanding的意思是允许重新定义标准库里已经有的函数,比如我自己定义了一个printf函数(主要内容是从xv6复制的,这里就显现出Rust的好了,Rust的格式化输出是定义在语言内部的,只需要重写字符串的输出方式,C的整个格式化都得重写),和stdio.h那个同名,不加这个编译选项就会报错。在编译完后,需要用objcopy把程序的elf元信息去掉,因为裸机只能理解代码,不能解析elf格式,经过objcopy后整个文件上来就是二进制代码,裸机可以直接执行。最后是SBI把内核放到的位置,我放在0x8020_0000,和教程的0x8002_0000不太一样,相应的linker script里的内容也要改。

顺便提一下如何使用gdb调试,首先编译的时候必须用-g往二进制文件里添加调试符号表,接着在最后执行qemu的时候添加选项-s -S意思是监听调试端口1234,同时在执行第一句汇编指令前停下来等待gdb连接。然后打开另一个终端,运行riscv-elf-gdb 二进制文件,gdb的程序名不一定是这个,只要是riscv目标版本的都可以,二进制文件指的是没有经过objcopy,gcc直接编译出来的文件。进入gdb后运行命令target remote :1234,连上qemu以后就可以进行查看代码、加断点、单步执行等操作了。