Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.3 节 RISC-V 架构的 Difftest (Spike) 问题 #15

Open
taoky opened this issue Aug 29, 2022 · 6 comments
Open

4.3 节 RISC-V 架构的 Difftest (Spike) 问题 #15

taoky opened this issue Aug 29, 2022 · 6 comments

Comments

@taoky
Copy link

taoky commented Aug 29, 2022

标准RISC-V的分页机制需要在S模式及U模式下才能开启, 而在M模式下的访存并不会进行MMU的地址转换. 但我们在NEMU中进行了简化, 允许M模式的访存也进行地址转换, 这样可以避免引入S模式相关的细节……

然而在 nemu/tools/spike-diff/repo/riscv/mmu.h 的 decode_vm_info() 函数中,如果目前模式是 M 模式,那么会直接返回全零的 vm_info

https://github.com/NJU-ProjectN/riscv-isa-sim/blob/4740dedcfd58f1b881d570cca0f8b697e17b8a5f/riscv/mmu.h#L496-L499

inline vm_info decode_vm_info(int xlen, bool stage2, reg_t prv, reg_t satp)
{
  if (prv == PRV_M) {
    return {0, 0, 0, 0, 0};

这导致了 M 模式下作为对照的 Spike 不会去做地址转换:

https://github.com/NJU-ProjectN/riscv-isa-sim/blob/4740dedcfd58f1b881d570cca0f8b697e17b8a5f/riscv/mmu.cc#L384-L390

reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool hlvx)
{
  reg_t page_mask = (reg_t(1) << PGSHIFT) - 1;
  reg_t satp = (virt) ? proc->get_state()->vsatp : proc->get_state()->satp;
  vm_info vm = decode_vm_info(proc->max_xlen, false, mode, satp);
  if (vm.levels == 0)
    return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, type, virt, hlvx) & ~page_mask; // zero-extend from xlen

从而导致 difftest 出错,但是 gitbook 中似乎没有提到这一点。如果 PA 的 NEMU 不打算去实现 S 模式的话,这里的修改恐怕是必须的,可能可以加一个 macro 来弄。

@sashimi-yzh
Copy link
Contributor

AM的代码里面设置了MPRV, Spike应该会做地址转换, 我这边是可以开difftest跑的

@taoky
Copy link
Author

taoky commented Aug 29, 2022

AM的代码里面设置了MPRV, Spike应该会做地址转换, 我这边是可以开difftest跑的

emm 看来我得回到之前的 commit 看一下。另外 https://github.com/NJU-ProjectN/riscv-isa-sim/blob/4740dedcfd58f1b881d570cca0f8b697e17b8a5f/riscv/mmu.cc#L58-L71

  reg_t mode = proc->state.prv;
  if (type != FETCH) {
    if (!proc->state.debug_mode && get_field(proc->state.mstatus, MSTATUS_MPRV)) {
      mode = get_field(proc->state.mstatus, MSTATUS_MPP);
      if (get_field(proc->state.mstatus, MSTATUS_MPV) && mode != PRV_M)
        virt = true;
    }
    if (xlate_flags & RISCV_XLATE_VIRT) {
      virt = true;
      mode = get_field(proc->state.hstatus, HSTATUS_SPVP);
    }
  }

  reg_t paddr = walk(addr, type, mode, virt, hlvx) | (addr & (PGSIZE-1));

从代码看,即使设置了 MPRV,取指仍然不会做地址转换。

@sashimi-yzh
Copy link
Contributor

sashimi-yzh commented Aug 29, 2022

在内核中取指是恒等映射, 转换前后的结果一样, Spike那边不做也可以

@taoky
Copy link
Author

taoky commented Aug 29, 2022

回到了完成 4.4 之前的 commit 看了一下,在 diff_init() 里加上 p->debug = true 之后报错如下:

core   0: 0x000000004000f938 (0x02065613) srli    a2, a2, 32
core   0: 0x000000004000f93c (0x00000073) ecall
core   0: exception trap_user_ecall, epc 0x000000004000f93c
core   0: 0x0000000080001c00 (0xee010113) addi    sp, sp, -288
core   0: 0x0000000080001c04 (0x00113423) sd      ra, 8(sp)
core   0: exception trap_store_access_fault, epc 0x0000000080001c04
core   0:           tval 0x000000007ffffd98

所以情况应该是,在完成 4.4 节的内容之前 U 模式 ecall 到 M 模式之后 __am_asm_trap 使用的 sp 还是用户栈,但是状态已经是 M 模式了,所以访问出错。

@sashimi-yzh
Copy link
Contributor

这确实是个问题, 感觉需要在进入__am_asm_trap之后马上设置MPRV位, 然后再访问sp, 你可以试试这样是否能解决问题吗?

@taoky
Copy link
Author

taoky commented Aug 29, 2022

这确实是个问题, 感觉需要在进入__am_asm_trap之后马上设置MPRV位, 然后再访问sp, 你可以试试这样是否能解决问题吗?

因为进入 __am_asm_trap 的时候不能用通用寄存器临时放数据,并且 csr 寄存器的立即数指令都只能五位无符号扩展,所以我改成了这样子:

__am_asm_trap:
  csrrw a0, mscratch, a0  // swap a0 and mscratch
  li a0, (1 << 17)  // MPRV
  csrs mstatus, a0
  csrrw a0, mscratch, a0  // swap back
  // ...

实现 mscratch 寄存器之后看起来可以跑,但是有另一个我自己实现的小问题:我自己实现的 klib 的 printf 系列函数用了 GNU 的嵌套函数扩展,代码大概像这样:

static int vprintf_base(int (*put)(char s), const char *fmt, va_list ap) {
  // ...
}

void __riscv_flush_icache() {
  // nested function support requires this function
  // the nemu does not have icache/dcache now
  // so just ignore it
}

int putch_i(char ch) {
  putch(ch);
  return 0;
}

int printf(const char *fmt, ...) {
  va_list ap;

  va_start(ap, fmt);
  int ret = vprintf_base(putch_i, fmt, ap);
  va_end(ap);

  return ret;
}

int vsprintf(char *out, const char *fmt, va_list ap) {
  int put_out(char s) {
    *out = s; out++;
    return 0;
  }
  return vprintf_base(put_out, fmt, ap);
}

修改之后发现会在栈上 trap_instruction_access_fault:

core   0: 0x0000000080002264 (0x000900e7) jalr    s2
core   0: exception trap_instruction_access_fault, epc 0x000000007ffffbd8
core   0:           tval 0x000000007ffffbd8
...
pc: 0x7ffffbdc
$0: 0x00000000	ra: 0x80002268	sp: 0x7ffffb20	gp: 0x00000000
tp: 0x00000000	t0: 0x00000008	t1: 0x7ffffcc0	t2: 0x7ffffbd8
s0: 0x00000000	s1: 0x80003f01	a0: 0x00000057	a1: 0x80003f00
a2: 0x7ffffc90	a3: 0x0000012c	a4: 0x00000025	a5: 0x00000057
a6: 0x80000a34	a7: 0x00000000	s2: 0x7ffffbd8	s3: 0x7ffffc90
s4: 0xffffffff800004d1	s5: 0x8000480c	s6: 0x7ffffb30	s7: 0x00000009
s8: 0x00000000	s9: 0x00000000	s10: 0x00000000	s11: 0x00000000
t3: 0x00000000	t4: 0x00000000	t5: 0x00000000	t6: 0x00000000

我还没有仔细看它的汇编,但是搜索发现这个扩展似乎需要在栈上运行代码 (trampoline),这有可能是我的实现在 difftest 即使一开始就加上 MPRV 也还是会出错的原因。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants