Skip to content

zhenyanjie/os-4-risc-v

 
 

Repository files navigation

#1月13日更新 首先,强烈建议在次基础上继续实现的各位,使用spike1.9或qemu吧,虽然开始的时候可能会有一些问题,但是后面的实现会轻松得多,逻辑方面也会更加通顺。毕竟riscv在不断地完善自我。而且很重要的是,spike1.9支持gdb调试!这个就比只能在汇编码级别进行调试的spike1.7强得多了。。。

###ucore lab5移植 lab5和lab4是进程管理(包括内核进程和用户进程)。所以直接移植了lab5。相对于前面的移植,lab5的移植还是有很多问题在,下面会简单介绍lab5的问题和实现

  1. 首先,对于用户进程和内核进程来说,它们两个的地址空间应当是完全相隔开的,这样便于权限以及内存保护。在ucore中,是通过两个ld脚本链接文件实现的,首先利用user.ld链接好user的文件,然后在链接内核镜像的时候,通过-b选项将链接好的user镜像通过elf的形式读取进来,再将它按照地址分布放在kernel镜像正确的地址上,这样两部分地址就完全互不干扰了。但是,在我实现的时候,不知道什么原因,-b选项始终无法使用,始终提示是segmentation fault。没有办法,所以在本次实现的时候,将内核进程和用户进程链接在了一起。所以用户进程和内核进程唯一的区别就只有它们使用的栈空间以及它们调用时的思路了。这是一个根本上的错误,所以其实我的这个实现只是一个参考而已,虽然可以跑,但是需要大改动。同时,也正是因为内核和用户地址空间无法分离,导致出现了很多问题,比如在ucore中,用来进行内存映射管理的是mm结构体,每一个进程都是有自己的mm的,用户进程的mm应当只包含了自己所在的地址。但是由于无法分开内核进程和用户进程,本实现强行修改了一些mm中的地址检查以及代码逻辑,目的是既能将进程运行起来,又不过大地改动ucore本身“使用mm进行内存管理”的逻辑。

  2. riscv和x86在中断、calling convention以及虚存管理方面有很多不同之处,所以围绕进程管理,出现了很多问题,不得不进行很多修改。核心的问题在于“内核栈和用户栈是不同的“。为了便于理解,首先介绍一下ucore,即x86进行内核进程和用户进程之间切换时的逻辑。 (1)对于每一个用户进程,它拥有独立的两个栈,用户栈和内核栈,它们是完全独立的,运行在平常的状态时,即用户态时,使用的一直是用户栈,只有当发生系统调用或其他自陷从用户态跳转到内核态的时候,才会使用内核栈,而当从系统调用返回用户态的时候,会再次将栈变回用户栈。 (2)x86实现上述逻辑的方式实际上是自动的,首先,在切换到某个用户进程的时候,操作系统会将tss的esp设置为该进程的内核栈地址。在该用户进程利用int进行系统调用的时候,会自动地将栈地址切换为tss的esp的地址并将原来用户栈的地址放在内核栈中,这样便切换到了内核栈。而当需要从系统调用返回的时候,ucore会调用iret指令,iret指令非常智能,当它发现权限(保存于cs,ds段寄存器中)发生变化的时候,会自动地从栈中读取esp的值赋值给esp寄存器,这样,就从内核态又切换回了用户态。但是对于riscv来讲,这些栈的切换都不是自动的,所以不得不在代码中加入了大量的判断逻辑,以判断出是否进行了用户和内核态之间的切换,从而进行栈的正确切换。 (3)riscv1.7以及它的spike模拟器并不完善,在从user态进行ecall,即系统调用时,它会将权限切换到machine中(理论上应当是切换到supervisor中的),而从supervisor中进行ecall时同样如此。这就使得supervisor和user两个状态没有什么区分度。但如果只使用supervisor态和machine态(将这两个权限级对应x86的用户态和内核态)同样会有问题:当内核运行于machine态的时候,riscv会将每个地址直接对应裸机地址,也就是说虚拟地址翻译失效了。这样更是造成了很多地址访问方面的问题。本移植使用的就是supervisor态和machine态,通过一系列判断,修改,是成功实现了内核态与用户态之间堆栈的切换的,但是逻辑就变得比较复杂。各位可以参考实际的代码。 (4)实际上,riscv是提供了一个在machine状态下进行虚拟地址管理的方法的,即mstatus的MPRV这一位(具体作用可参看riscv的特权spec说明文档),同时,riscv还提供了mscratch寄存器用来保存进程上下文,具体使用方式我也不是十分理解。在我进行实现的时候,忽略了这两个riscv专有的逻辑,但是推荐时间充裕,能力更强的同学,我觉得使用这两个专有逻辑可能更加符合riscv的思路。不同体系结构之间的系统移植,比起直接照搬另一个体系的逻辑,应当还是进行逻辑上的改动比较正确和有效率一些。毕竟ucore使用的完全是x86的逻辑,而riscv也不会平白无故地多设置这样两个特殊的逻辑。 (5)这次更新中,加入了print_trapframe逻辑的实现(尽管和进程管理没什么关系)

#1月1日更新 ###ucore lab3移植 lab2和lab3分别是物理内存和虚拟内存管理,所以我将两者放到了一起,只做了lab3的移植。现已移植成功,代码已经上传,主要内容包括实现32位页式虚拟内存管理、Machine与Supervisor态之间的互换。spike模拟器并没有硬件内存的映射,所以无法进行磁盘管理,也就没有实现交换空间。由于这一节虽然要实现的功能不多,需要修改的地方却较为细碎,而且很容易出现寻址错误,所以不再介绍每一个需要修改的文件,只阐述修改的大致思路以及需要注意的几个坑。

  1. 首先是和spike模拟器以及riscv工具链有关的错误和坑。 (1)根据spike的代码逻辑,它默认的模拟主机为64位,而spike模拟的64位的riscv是不支持32位页式管理的(spike也没有实现段式内存管理,即mbb和mbbid)。所以使用spike时一定要将--isa参数设为RV32.(也可以在代码中修改RV的值,然后重新build spike,这样就不需要每次都多输入isa参数了)。 (2)riscv-tool中的一系列工具,包括编译器,objdump等build时默认是64位的,和32位的ucore兼容起来容易出错,推荐将build.sh的with_xlen修改为32,重新build工具链。(为了提升build速度,可以将build.common中的MAKE加上-j16参数,否则一套工具实在build太慢) (3)spike中页式寻址的代码有错误(坑死我了)。mmu.cc中的walk函数,即是用来寻址页式地址的,reg_t pte = ptesize == 4 ? (uint32_t)ppte : (uint64_t)ppte; 下面的一行代码应修改为ppn = pte >> PGSHIFT。 (4)根据riscv的设计,页式管理时检查权限要检查PTE_TYPE的,而这个地方,中间页表和底层页表是冲突的。中间页表必须设为PTE_TABLE,而底层页表则是根据权限设定值。所以ucore的虚拟页表VPT和虚拟页目录表VPD都无法使用了(因为检察权限时会有冲突)。 (5)若是修改了spike模拟器的代码,重新build的时候,必须将riscv-fesvr文件夹中的工具也一起build才会有用,否则代码修改了也没有作用。(就直接使用build.sh吧,不会出错,加入了j16的参数后build速度也不慢)

  2. riscv的页表根目录地址存在于sptbr寄存器之中,而且必须要在H、S或者U权限的时候,页式寻址才会有效(spike 没有实现H权限)。可是一旦进入S状态,mstatus等寄存器就无法再修改和读取,所以需要写函数在两个状态之间切换。(即代码中的M2S和S2M函数)。

  3. 由于spike不支持段式内存管理,需要修改PADDR等再虚拟地址和物理地址间进行转换的函数,同时,页表的权限赋值也不同,需要根据RISCV规定进行修改。

  4. e820map是x86专用的逻辑,riscv暂时还没有任何类似的东西,所以需要修改。此lab中直接使用了一个e820map结构体并直接赋值(需要结合kernel.ld文件的修改一起进行)。

  5. assert中的一系列assert检查的内容,由于riscv和x86映射的地址不同,需要进行一系列修改。

  6. 新加入encoding.h头文件,和spike头文件中的encoding是一样的。只为调试方便才将它从spike的目录下复制到ucore/libs之下。

  7. spike没有实现stimecmp,所以只支持M状态下的时钟中断。所以在代码的最后我将ucore切换回了M之下进行ucore的时钟中断逻辑(这样页式管理就没有了)。

  8. 其他一些和专门的机器以及寄存器相关的部分在此就不再介绍,代码修改量应当不大,大部分ucore的逻辑都是可以沿用的。只是调试时有些困难,因为spike 1.7并不支持gdb......

#12月26日更新 ###ucore lab1移植 lab1已经移植成功,由于种种原因,基于spike pri-1.7版本,工具链地址为 https://github.com/riscv/riscv-tools/tree/priv-1.7 根据README的quickstart即可安装 。主要参考了基于riscv的FreeRTOS以及linux写法。代码已上传,安装并配置spike以及riscv的工具链1.7成功后,再将对应的工具路径放到PATH变量下,使用./buildandtest即可编译成功,然后使用spike bin/kernel即可正常运行内核。由于时间原因,此次移植仅以“能够运行”以及“能够正常实现启动,打模拟主机为64位,而spike模拟的64位的riscv是不支持32位页式管理的(spike也没有实现段式内存管理,即mbb和mbbid)。所以使用spike时一定要将–isa参数设为RV32.(也可以在代码中修改RV的值,然后重新build spike,这样就不需要每次都多输入isa参数了)。 (2)riscv-tool中的一系列工具,包括编译器,objdump等build时默认是64位的,和32位的ucore兼容起来容易出错,推荐将build.sh的with_xlen修改为32,重新build工具链。(为了提升build速度,可以将build.common中的MAKE加上-j16参数,否则一套工具实在build太慢) (3)spike中页式寻址的代码有错误(坑死我了)。mmu.cc中的walk函数,即是用来寻址页式地址的,reg_t pte = ptesize == 4 ? (uint32_t)ppte : (uint64_t)ppte; 下面的一行代码应印,时间,中断”为目的。所以内部命名规则及代码没有大量修改,所以可能内部有些许废代码。下面大致介绍下主要做了修改的文件

  1. kernel.ld:链接文件。主要修改平台,代码entry等内容。由于riscv的内存映射并没有太多规定,在查看了spike源码后没有发现详细的地址映射规定。所以仿照FreeRTOS修改了kernel.ld,ucore中原有的符号和section的相对位置没有变化。但链接脚本中仍有两个要注意的:首先是代码段开始地址。由于riscv默认在启动时中断的入口在0x100处,所以.text段一定要从0x100开始;其次global pointer一定要provide出一个符号位置。它的作用是辅助寻址数据,个人理解应尽量将它设在全局data的正中,有利于效率,但是不设于此影响也不大。

  2. Makefile:根据需要进行修改,修改prefix,以及需要编译的文件,还修改了必要的ldflags和cflags

  3. 启动:启动基本上需要完全修改。因为目标平台完全不同。此处仿照的仍然是FreeRTOS的启动过程。ucore是仿照了x86从上电到取出系统的全过程,所以使用dd命令将内核和bootloader完全分成了两个部分。此处为求方便,直接编译了内核,并使用spike从内核开始启动。所以,删去了bootmain,直接使用bootasm,进行中断设置以及启动内核前的准备工作。

  4. init.c中的kern_init,由于riscv中没有所谓“中断向量表”等,同时寄存器等也完全同8086不同,所以此处注释掉了一些函数。

  5. print部分(stdio.h和stdio.c):ucore中的print是使用了串口和并口等一系列设置进行打印的。而FreeRTOS则是使用了mtohost和mfromhost两个寄存器,通过系统调用进行打印。仿照FreeRTOS做了修改

  6. 中断部分:为了使用系统调用和处理时钟中断,trap.c和bootasm中进行了大量的相关修改。同时,为了更好地处理时钟中断,参照了FreeRTOS,单独分出一个Interrupt.S文件进行中断的处理。

  7. 内存管理和asm内嵌汇编。内存部分本lab不做考虑,所以一切妨碍编译的部分以及完全是X86架构下才有的asm汇编都注释掉。此外,一些文件中的asm汇编,可以使用RISCV下汇编代替的,则做了修改(如X86下大量的函数)

os-4-risc-v

###some problems

  1. FreeRTOS的Makefile文件RISCV变量位置错误(或者说不一定正确)
  2. 在riscv-privileged-v1.9.1架构中,和1.7架构相比(FreeRTOS所使用的版本),一些CSR被取消了(例如mtime和mtimecmp),然而文档却没有注明这一点...至少我没有在这两个csr分析的地方找到任何标注...
  3. mtohost和mfromhost同样是两个1.7定义的CSR,似乎是可以进行调试,同时在系统调用时需要,但是文件对它们的说明很简单以至于难以明白究竟是做什么的,详细资料暂时没有找到
  4. eret指令需要修改

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Assembly 81.1%
  • C 17.0%
  • Makefile 1.2%
  • Other 0.7%