riscv-mxr

Introduce riscv mxr bit.

Normal MXR

MXR(Make eXecutable Readable)是mstatus中的一个bit。

RISCV SPEC规定:

  • MXR=0, 只有PTE.R=1,load才能成功
  • MXR=1,PTE.R=1或者PTE.X=1,load都能成功
  • 当基于页面的虚拟内存无效时,MXR不起作用

所以它只影响DTLB,而不影响ITLB。

场景:

  • LOAD指令,M模式,或者S模式Bare模式,MXR不起作用
  • LOAD指令,S模式SV39/SV48,MXR==0,PTE.R==0 & PTE.X==1,产生load page fault
  • LOAD指令,S模式SV39/SV48,MXR==1,PTE.R==0 & PTE.X==1,成功,不产生load page fault

Hypervisor MXR

在Hypervisor的情况下,不光mstatus中有mxr,vsstatus中也有mxr。

RISCV SPEC规定:

  • HS-level status.MXR影响所有stage(VS-STAGE and G-STAGE)
  • vsstatus.MXR只影响第一级stage(VS-STAGE)

补充说明hypervisor下:

  • VS STAGE 发生FAULT报 page fault
  • G STAGE 发生FAULT 报 guest page fault

场景:

  • vsstatus.MXR=0, sstatus.MXR=0
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 失败,page fault
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 失败,guest page fault
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 失败,page fault
  • vsstatus.MXR=0, sstatus.MXR=1
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 成功
  • vsstatus.MXR=1, sstatus.MXR=0
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 失败,guest page fault
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 失败,guest page fault
  • vsstatus.MXR=1, sstatus.MXR=1
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 成功

判断条件:

  • vs-stage时:拿来判断的mxr = sstatus.mxr | vsstatus.mxr
  • g-stage时:拿来判断的条件 mxr = sstatus.mxr

spike hypervisor mxr处理

来看看在spike中对mxr的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_type, bool virt, bool hlvx)
{
...
reg_t arch_mstatus = proc->state.v ? proc->state.vsstatus : proc->state.mstatus;
bool mxr = arch_mstatus & MSTATUS_MXR;
...
}

reg_t mmu_t:walk(reg_t addr, access_type type, reg_t mode, bool virt, bool hlvx)
{
...
reg_t arch_vsstatus = proc->state.v ? proc->state.mstatus : proc->state.vsstatus;
reg_t arch_mstatus = proc->state.v ? proc->state.vsstatus : proc->state.mstatus;
bool mxr = (arch_mstatus | (virt ? arch_vsstatus : 0)) & MSTATUS_MXR;
...
}

从代码上可以看出:

  • walk 执行的是vs-stage,s2xlate执行的是g-stage。
  • proc->state.v表示当前是在vs/vu虚拟状态下

这里奇怪的是:会根据proc->state.v来决定选vsstatus还是mstatus。比如在g-stage时,v=1,选vsstatus,v=0,选mstatus。这里和上面spec规定的好像相反了,spec规定g-stage只和mstatus.MXR相关,为什么v=1时要选vsstatus呢??

这是因为在spike中,当切换virtualization时(需要满足一定条件),会调换mstatus和vsstatus。具体参见processer.cc

  • 当HS时,mstatus就是mstatus,vsstatus就是vsstatus
  • 当从HS切换到VS时,将mstatus和vsstatus调换。变成了:mstatus其实是vsstatus,vsstatus其实是mstatus。
  • 当VS切换回HS时,再将mstatus和vsstatus调换。变成了:mstatus就是mstatus,vsstatus就是vsstatus

这么做的目的是:在VS模式下,软件仍然会访问sstatus,但这是访问的sstatus其实是需要访问vsstatus。所以如果按照上述规则,做个调换,在VS模式下访问的sstatus其实就是vsstatus,这样简化了设计。

因此,如果设计也按照上述规则来做的话,逻辑会变成这样:

V=0时:

  • 和vsstatus.MXR不相关,都使用sstatus.MXR,只会有一个stage页表转换。
  • sstatus.MXR==0,PTE.R==0 & PTE.X==1,产生load page fault
  • sstatus.MXR==1,PTE.R==0 & PTE.X==1,成功,不产生load page fault

V=1时:

  • sstatus.MXR(其实是vsstatus.MXR)=0, vsstatus.MXR(其实是sstatus.MXR)=0
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 失败,page fault
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 失败,guest page fault
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 失败,page fault
  • sstatus.MXR(其实是vsstatus.MXR)=0, vsstatus.MXR(其实是sstatus.MXR)=1
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 成功
  • sstatus.MXR(其实是vsstatus.MXR)=1, vsstatus.MXR(其实是sstatus.MXR)=0
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 失败,guest page fault
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 失败,guest page fault
  • sstatus.MXR(其实是vsstatus.MXR)=1, vsstatus.MXR(其实是sstatus.MXR)=1
    • vs-stage: PTE.R==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R=1, 成功
    • vs-stage: PTE.R==1, g-stage: PTE.R==0 & PTE.X==1, 成功
    • vs-stage: PTE.R==0 & PTE.X==1, g-stage: PTE.R==0 & PTE.X==1, 成功
  • 判断条件:
    • vs-stage时:拿来判断的mxr = vsstatus.mxr | sstatus.mxr
    • g-stage时:拿来判断的条件 mxr = vsstatus.mxr