制作一个RISC-V的操作系统十六-系统调用

发布于:2024-05-03 ⋅ 阅读:(30) ⋅ 点赞:(0)

用户态和内核态

在这里插入图片描述

mstatus设置

此时UIE设置为1和MPIE为1,MPP设置为0
代表当前权限允许UIE中断发生,并且在第一个mret后将权限恢复为用户态,同时MIE也保持开启
在这里插入图片描述

模式切换

ecall相当于一次触发异常,然后在对应的异常中去处理对应的系统调用
在这里插入图片描述

核心流程

  • 首先在进程中使用到gethartid函数
  • gethartid函数保存a7寄存器为调用号,然后ecall进行系统调用,ret再回到调用gethartid函数的下一条地址
  • ecall会触发中断进入trap_vector,此时的mepc是ecall地址,mecause是8
  • trap_vector会保存当前上下文,然后增加一个指向保存了的上下文的地址作为trap_hander的第三个参数,trap_hander结束后会将返回值写到mepc中,然后mret到mepc对应的位置,此时是ecall+4的地址
  • trap_hander中会执行syscall函数,参数是传入的之前刚开始保存了的上下文,然后syscall函数中的会对该上下文的a0寄存器赋值为hart的id
  • 此时,回到最后恢复保存的上下文的地方,也就是trap_vector那里,恢复上下文后,此时返回值正好是hart的id,然后mepc也被选择到了ecall+4,那么返回到上一级为gethartid的ret函数,此时返回值也是hart的id,即得到hartid
    在这里插入图片描述
    原则a6其实也可以作为参数
    在这里插入图片描述

封装

就是用户态使用的函数用到的头文件和内核态使用的函数用到的头文件建议分别封装到各个文件中,在用户态中的作为C库,内核态中的内核维护,如果头文件相关定义不一致可能会出现问题
在这里插入图片描述

代码

https://github.com/FULLK/risllkos/tree/main/Fullkenerl11

#ifdef CONFIG_SYSCALL
    li      t0, 0xffffffff          # Load the value 0xffffffff into register t0
    csrw    pmpaddr0, t0           # Write this value to the PMP address register 0
    li      t0, 0xf                 # Load the value 0xf (binary 1111) into register t0
    csrw    pmpcfg0, t0             # Write this value to the PMP configuration register 0
#endif

背景解释

在RISC-V架构中,PMP(Physical Memory Protection)是用于控制不同特权级别访问内存的能力的一个特性。它通过一系列配置寄存器(PMP配置寄存器如pmpcfg0)和地址寄存器(PMP地址寄存器如pmpaddr0)来定义内存区域的访问权限(读、写、执行)以及哪些特权模式可以访问这些区域。

代码示例解析

假设你正在开发一个RISC-V的操作系统,并且使用QEMU作为模拟器来测试你的代码。当你升级到QEMU 6.0及以上版本时,遇到了一个与PMP配置相关的问题:如果没有设置任何PMP条目,从Supervisor (S) 或 User (U) 模式进行的内存访问会导致异常。

解释

  • li t0, 0xffffffff: 这行代码将寄存器t0赋值为0xffffffff,这个值在32位地址空间中代表一个非常大的地址(几乎接近地址空间的最大值)。按照标准,这个值本意是用来设置PMP条目的上界地址,但由于一些QEMU实现细节,将其设置为全1时可能被解释为覆盖整个地址空间的特殊情况。

  • csrw pmpaddr0, t0: 此指令将寄存器t0的值写入到PMP地址寄存器0(pmpaddr0),试图设置PMP的第一个条目覆盖整个地址空间的访问权限。

  • li t0, 0xf: 这行代码设置寄存器t0为0xf,二进制形式为1111。在PMP配置中,每个位对应一个PMP条目的访问权限(读、写、执行),因此0xf意味着开启所有权限。

  • csrw pmpcfg0, t0: 最后,这行代码将寄存器t0的值写入到PMP配置寄存器0(pmpcfg0),激活了第一个PMP条目并设置了读、写、执行的权限。

目的

这段代码的主要目的是作为临时解决方案,确保即使在没有详细配置PMP条目的情况下,用户和监督模式下的程序也能正常访问内存,以避免在使用QEMU 6.0及以上版本时遇到的异常问题。它通过配置一个PMP条目来覆盖整个地址空间,并赋予所有访问权限,从而绕过了新版本QEMU对未配置PMP条目的严格检查。

在RISC-V架构中,PMP(Physical Memory Protection,物理内存保护)机制通过一系列配置寄存器来控制对物理内存的访问权限,以此增强系统的安全性。PMP寄存器分为地址寄存器(如pmpaddrN)和配置寄存器(如pmpcfgN),共同定义了多个内存访问控制区域。

当说“试图设置PMP的第一个条目覆盖整个地址空间的访问权限”时,这指的是通过编程PMP的地址和配置寄存器来创建一个PMP规则,这个规则旨在允许对整个物理地址空间执行读、写和执行操作,而不受任何访问限制。具体到代码示例:

  • li t0, 0xffffffff:这一行是装载指令,将立即数0xffffffff(即32位地址空间中的最大值减一)送入寄存器t0。在TOR(Top of Range)匹配模式下,PMP地址寄存器中的值通常表示区间的上界地址。理论上,全1的上界地址可能被解释为企图涵盖整个地址空间的意图,尽管实际上因为地址对齐和边界处理,它可能无法精确覆盖到地址空间的最后一个字节。

  • csrw pmpaddr0, t0:这条指令将t0寄存器中的值写入到PMP地址寄存器0(pmpaddr0),试图设置PMP的第一个条目的上界地址。

  • li t0, 0xfcsrw pmpcfg0, t0:这两行是配置PMP条目的权限。0xf(十六进制)等于二进制的1111,分别代表了读®、写(W)、执行(X)和开启(L)权限位。通过写入到pmpcfg0,为PMP条目0设置了全权限。

然而,根据RISC-V的PMP规范,单个PMP条目通常不能直接覆盖整个地址空间,特别是在TOR模式下,pmpaddr0寄存器存储的是第一个区间的上界物理地址的高32位,即使设置为全1,也无法完美覆盖到地址空间的最末尾。而且,如之前讨论中提及的,QEMU的特定实现方式可能使得pmpaddr0 = 0xffffffff在某些情境下被特殊处理,看似覆盖了整个地址空间。