huangchunzhen / Tcore

Tcore是我在暑假参与清华陈渝教授带领的summer school时和同来参与研修的东南大学李可然同学决定一起做的在一个基于Rcore衍生项目,终极目标是一起做出一个基于Riscv的Cpu并且开发一个可以移植到该Cpu上完整的操作系统,将操作系统继续钻研下去
13 stars 3 forks source link

你好,我在以RocketChip为原型的K210芯片基础上重实现了Linux0.11,目前内核可以正常运行,想要和你们进行一些探讨和交流 #9

Open lizhirui opened 4 years ago

lizhirui commented 4 years ago
我是聊城大学大三的一名本科生。从github上看到你们正在设计一个基于RISCV的CPU并且正在开发一个可以移植到该CPU上的完整的操作系统,而最近两个月,我学习了RISC-V架构,并在以RocketChip为原型的K210芯片基础上重实现了Linux0.11。因此想要和你们探讨一下。

   首先我根据1.9.1版本的特权指令集规范对CSR以及MMU的操作进行了封装,一开始本来准备让内核跑在Supervisor Mode下,因为在这个模式下,MMU是起作用的,而在Machine Mode下MMU是无效的,但由于K210芯片无法将External Interrupt重定向到Supervisor Mode,同时,通过实验测试以及阅读对应Spike源代码发现,其Machine Mode下的中断,如果未被重定向到Supervisor Mode下的话,则进入Supervisor Mode后mstatus.mie标志会失去对中断的全局控制权,并且此时sstatus.sie也无控制权,整个CPU失去了全局中断控制标志,因此最终选择将操作系统内核跑在了Machine Mode下,并将MMU设置为Sv39分页模式,而用户程序运行在User Mode下。

   我将0x00000000~0x7FFFFFFF作为仅内核使用和0号/1号进程的内存空间,在通过上下文切换到其他进程后,这部分区域的User访问权限会被关闭,同时,选择将0xC0000000起的区域作为用户进程的私有地址空间,由于Linux0.11是严重依赖x86架构的,因此在令VPN[2]为3的前提下,令根页表指向的下一级Level为所谓页目录,而令页目录的下一级Level叫做页表项(模仿x86架构)。同时在task_struct中添加了一个叫做page_dir_table的字段用于存储每个进程的私有页表,其对应大小为4页,可以容纳4*512=2048个页表项,覆盖2048 * 4096 = 8MB(0xC0000000~0xC07FFFFF)的内存区域,这对于这个系统来说已经足够了(K210片内只有8MB SRAM,其中顶部的2MB还属于KPU用)。

   该系统经过处理后已经支持嵌套中断,并在task_struct中重构了tss结构使之适合RISC-V架构使用。

我采用K210芯片中的其中一个Timer用于任务调度,其周期为10ms,每个任务都有独立的用户态堆栈和内核态堆栈,由于switch_to函数使用汇编语言编写并依赖于x86处理器的TSS机制,而RISC-V架构并不支持该机制,因此将该函数重构,手工备份了现场环境,并处理好了返回路径不一致问题(上下文切换可能来自Exception(即exception_environment_call_from_u_mode),也可能来自Interrupt(External Interrupt,由plic管理的Timer中断),对于Exception来说只需要按照正常路径返回即可,而Interrupt则需要去设置timer和plic相关的eoi标志,标记本次中断处理已完成,防止中断再次触发。放置了一个全局变量用来标识上下文切换来源,并且该标识不受上下文切换的影响而改变,因此通过它可以决定正确的返回中断路径(由一个叫做interupt_recover的函数在上下文切换后进行了处理))。

该芯片由于在启动后会自行将Flash中的程序加载到RAM的0x80000000处并从该地址处开始执行,因此不需要编写引导程序,镜像由内核镜像和文件系统拼接完成,后者拼接在前者的500KB边界处,大小为360KB,文件系统为Minix1.0版文件系统(最大文件长度14)。

我利用exception_instruction_access_fault、exception_load_access_fault以及exception_store_or_amo_address_fault通过调用memory.c中的do_no_page和do_wp_page函数实现了需求页加载和写时复制功能。

我规定了如下系统调用规范:通过a0、a1、a2分别传递第一、第二、第三参数,通过a7传递系统调用号,通过a0传递系统调用的返回值,通过ecall指令发起系统调用。

由于目前没有可以为RISC-V处理器编译出a.out可执行文件的编译器/链接器(Linux0.11仅支持该类型的可执行文件,且必须是ZMAGIC,不分段,同时不支持地址重定向),因此通过objcopy将elf转换为bin,并通过objdump对elf进行反汇编从而得到符号_start,并写了一个程序,读取_start的地址,构造a.out头部并将bin数据加入处理好数据对齐问题从而构成a.out格式的可执行文件。目前写了一个简易版的shell(由于当年的全套工具貌似已经丢失,网络上无法获得源代码),内部实现了一个简易版的ls命令(截图在下面)以及exit命令,同时将合成的bin文件放置在了 附件中,可以烧入K210芯片进行测试(串口波特率115200,建议使用putty作为终端工具,并选中”Terminal -> Keyboard”中的“The Control-?(127)”,这样退格功能可以正常运行,做了个延时启动(为了调试方便),所以上电复位几秒后串口才会有显示)。

在trap入口处负责堆栈的切换,若先前的特权级为User,则切换到Kernel Stack,否则不进行切换。

基于以上原因和情况,希望和你们探讨一下相关的问题,并有机会可以一起做点什么

运行截图 image.zip