rCore-Tutorial icon indicating copy to clipboard operation
rCore-Tutorial copied to clipboard

关于 os/src/sbi.rs::shutdown 后 qemu 和 gdb 问题以及相关一些探索和疑问

Open generalibm opened this issue 4 years ago • 4 comments

问题描述

下载最新代码后,切换到 lab-1 分支,make run 出现如下错误信息:

...
src/main.rs:44: 'end of rust_main'
sbi_trap_error: hart0: trap handler failed (error -2)
sbi_trap_error: hart0: mcause=0x0000000000000007 mtval=0x0000000000100000
sbi_trap_error: hart0: mepc=0x0000000080003d3e mstatus=0x8000000000007800
sbi_trap_error: hart0: ra=0x0000000080008228 sp=0x000000008001ec98
sbi_trap_error: hart0: gp=0x0000000000000000 tp=0x000000008001f000
sbi_trap_error: hart0: s0=0x000000008001eca8 s1=0x0000000000000040
sbi_trap_error: hart0: a0=0x0000000000000000 a1=0x0000000080003d2a
sbi_trap_error: hart0: a2=0x0000000080003d2a a3=0x0000000080003d2a
sbi_trap_error: hart0: a4=0x0000000000100000 a5=0x0000000000005555
sbi_trap_error: hart0: a6=0x0000000000003d2a a7=0x000000008000e0e0
sbi_trap_error: hart0: s2=0x0000000000000000 s3=0x000000008001f000
sbi_trap_error: hart0: s4=0x0000000000000000 s5=0x0000000000000000
sbi_trap_error: hart0: s6=0x0000000000000001 s7=0x0000000000000000
sbi_trap_error: hart0: s8=0x0000000000000000 s9=0x0000000000000000
sbi_trap_error: hart0: s10=0x0000000000000000 s11=0x0000000000000008
sbi_trap_error: hart0: t0=0x0000000000000000 t1=0x0000000000000000
sbi_trap_error: hart0: t2=0x000000008021622c t3=0x0000000000000000
sbi_trap_error: hart0: t4=0x0000000000000000 t5=0x0000000000000000
sbi_trap_error: hart0: t6=0x0000000000000000

之后,gbd 和 qemu 陷入卡死状态,目前我的处理方式是 kill -9

我的环境

Tool Version
mac OS 11.1
rustc 1.46.0-nightly
qemu-system-riscv64 5.1.0
openSBI 0.7
SBI 0.2

我的追踪过程

  1. 根据 os/src/main.rs:44: 'end of rust_main' 提示,追踪到 os/src/panic.rs::panic_handler:26shutdown

  1. os/src/sbi.rs:44shutdown 调用 sbi_call(8,0,0,0)。根据 os/src/sbi.rs:7 的定义,发现是使用了 ecall 请求 M 态(即SBI)服务,在这里是 qemu 下的 openSBI v0.7,该版本实现了 RISCV SBI 的 v0.2 版本。

  1. 自然而言会想到查看 SBI 手册,在这里找到了关于 shutdown 的说明。起初,看到 Replacement EID 这一列时,认为可能是我的机器环境比实验手册中环境要新一些,故阅读完该文档之后,实现了一个新版本的 sbi_shutdown 取代此前os/src/panic.rs::panic_handler:26处的 shutdown,代码如下:
#[inline(always)]
fn sbi_call(eid: usize,fid: usize,  arg0: usize, arg1: usize, arg2: usize) -> Sbiret {
    let mut error;
    let mut value;
    unsafe {
        llvm_asm!("ecall"
            : "={x10}" (error), "={x11}" (value)
            : "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (eid), "{x16}" (fid)
            : "memory"      // 如果汇编可能改变内存,则需要加入 memory 选项
            : "volatile");     // 防止编译器做激进的优化(如调换指令顺序等破坏 SBI 调用行为的优化)
    }
    Sbiret{error, value}
}

pub struct Sbiret {
    pub error: i64,
    pub value: i64,
}

const SBI_SYSTEM_RESET: usize = 0x53525354;

pub fn sbi_shutdown() -> Sbiret {
    sbi_call(SBI_SYSTEM_RESET, 0, 0, 0,0)
}

而后,在 os/src/panic.rs::panic_handler:27 处打印 struct Sbiret 的值,结果为 sbi_ret.error = -2 sbi_ret.value = 0;根据文档,查到 -2 代表SBI_ERR_NOT_SUPPORTED,即 SBI v0.2 不支持这个用法。于是放弃了继续改写的打算,转而查找 shutdownsbi_trap_error: hart0: trap handler failed (error -2) 原因。


  1. 经过各种 搜索引擎 的对关键字 sbi_trap_error trap handler failed error 检索之后,发现相关内容数量少且关系不大。只好回到 rCore 本身。

  1. 在 rCore 的 lab-1 代码中,对 shutdownsbi_call 进行了断点调试,主要涉及 os/src/panic.rsos/src/sbi.rs 两个文件,其中将 os/src/interrupt/mod.rs:15timer::init() 注释掉了,因为这个时钟也是通过 ecall 来实现的,因此会多次影响调试时的程序运行栈。结果证明是个 loop,只好另谋方案。

  1. 虽然在 rCore 代码上经过了一番努力和尝试之后还是无果,但是可以得出结论问题不在 rCore ,而是在 SBI ,于是,我把目光转向到 openSBI 的实现上,希望能够找到一些思路。

  1. opensbi/lib/sbi/sbi_trap.c::216 中,终于看到了熟悉的 trap handler failed 的身影,根据函数名 sbi_trap_handler 以及该函数注释,不难发现该函数和熟悉的rCore上的 sbi_call 完美契合。在 opensbi/lib/sbi/sbi_trap.c::267 中调用了在同一个文件中的函数 sbi_trap_redirect ,此后将函数的返回值作为判断条件是否进行 trap_error 处理;在 opensbi/lib/sbi/sbi_trap.c::sbi_trap_error 中可以发现,这里的处理即是打印各种信息,而这和 rCore make run 之后的错误信息完全一致;此后进入 sbi_hart_hang, 该函数内部是一个 while (1) 循环,这也就解释了 gdb 和 qemu 卡死的原因。因此,知道 sbi_trap_redirect 的返回值代表的含义就能发现问题的原因了。

  1. sbi_trap_redirect 中,返回值只有两个 0 (opensbi/lib/sbi/sbi_trap.c::194) 和 SBI_ENOTSUPP (opensbi/lib/sbi/sbi_trap.c::98)。而根据 make run 的错误信息,以及 opensbi/lib/sbi/sbi_trap.c:29 不难知道这个不是 0 而是 -2,因此可以得出假设 SBI_ENOTSUPP = 2

  1. 果然在 opensbi/include/sbi/sbi.error.h:19 发现了 #define SBI_ENOTSUPP SBI_ERR_NOT_SUPPORTED,又在 opensbi/include/sbi/sbi_ecall/interface.h:88 中发现 #define SBI_ERR_NOT_SUPPORTED -2。于是假设 SBI_ENOTSUPP = 2 得到了证实。原本找到错误的原因应该举杯欢庆,但是对 SBI_ERR_NOT_SUPPORTED 的字面意思又难以高兴起来,其意为 rCore 中的 os/src/sbi.rs::shutdownsbi_call(8,0,0,0) 也是不支持。和最初想要自己动手写一个 sbi_shutdown 一样,openSBI v0.7 目前都不支持。被这个问题困扰了一段时间之后,接下来的思路就是再搭建一个环境。

  1. 在搭建 virtual box 和 ubuntu18.04 的等待过程中,突然有了个疑问:之前看到的 SBI 文档是 v0.2 吗?应该是 master 分支上吧。于是,转向查找 SBI v0.2 文档

  1. 在 SBI v0.2 文档中 sbi_shutdown 中,对于 Replacement Extension 并没有指定,因此最初重写 sbi_shutdown 后,返回值表明没有实现这个是合理的。但是,在 Extension ID 列中,关于 sbi_shutdown 指定的值确是 0x08,而经过上面的探究已经 openSBI 也给出了不支持的回答。至此,问题似乎明晰又不明晰。在看完 SBI v0.2 文档之后,发现 HSM实现 sbi_hart_stop,根据其解释

Returns ownership of the calling hart back to the SBI implementation

结合 sbi_shutdown 的解释

Puts all the harts to shut down state from supervisor point of view.

似乎可以通过 sbi_hart_stop 实现 sbi_shutdown


  1. 于是,再次重写 sbi_shutdown 如下
const HSMMID:usize = 0x48534D;
const HSM_STOP_FID:usize = 1;

pub fn sbi_hart_stop() -> Sbiret {
    sbi_call(HSMMID, HSM_STOP_FID, 0, 0, 0)
}

然后,用 sbi_hart_stop 替代 os/src/panic.rs:26 处的 shutdown


  1. make run 之后光标停在了 src/main.rs:44: 'end of rust_main' 之后。至此,shutdown 问题得到了解决,但是不确定的是这个方案优劣几何。

  1. 既然,sbi_shutdownecall 对于 EID = 8 不支持,那么 EID = 0 的时钟是否也不支持呢?在将 os/src/mod.rs:15timer::init(); 解除注释后,再把 os/src/main.rs:44中的 panic("end of rust_main") 改为 loop {} 后,make run 发现打印出各种 ticks ,说明 open SBI v0.7 对于 EID = 0 情况的 ecall 是支持的。

涉及文件

rCore-Tutorial 的 lab-1 下

  • os/src/sbi.rs
  • os/src/panic.rs
  • os/src/main.rs

相关段落

  • 修改后的文件 os/src/sbi.rs
//! 调用 Machine 层的操作
// 目前还不会用到全部的 SBI 调用,暂时允许未使用的变量或函数
#![allow(unused)]

/// SBI 调用
#[inline(always)]
fn sbi_call(eid: i32,fid: i32,  arg0: usize, arg1: usize, arg2: usize) -> Sbiret {
    let mut error;
    let mut value;
    unsafe {
        llvm_asm!("ecall"
            : "={x10}" (error), "={x11}" (value)
            : "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (eid), "{x16}" (fid)
            : "memory"      // 如果汇编可能改变内存,则需要加入 memory 选项
            : "volatile"); // 防止编译器做激进的优化(如调换指令顺序等破坏 SBI 调用行为的优化)
    }
    Sbiret{error, value}
}

pub struct Sbiret {
    pub error: i64,
    pub value: i64,
}

const SBI_HSM_STOP_EID:i32 = 0x48534D;
const SBI_HSM_STOP_FID:i32 = 1;

const SBI_SET_TIMER: i32 = 0;
const SBI_CONSOLE_PUTCHAR: i32 = 1;
const SBI_CONSOLE_GETCHAR: i32 = 2;
const SBI_CLEAR_IPI: i32 = 3;
const SBI_SEND_IPI: i32 = 4;
const SBI_REMOTE_FENCE_I: i32 = 5;
const SBI_REMOTE_SFENCE_VMA: i32 = 6;
const SBI_REMOTE_SFENCE_VMA_ASID: i32 = 7;
const SBI_SHUTDOWN: i32 = 8;

/// 向控制台输出一个字符
///
/// 需要注意我们不能直接使用 Rust 中的 char 类型
pub fn console_putchar(c: usize) {
    sbi_call(SBI_CONSOLE_PUTCHAR, 0, c, 0, 0);
}

/// 从控制台中读取一个字符
///
/// 没有读取到字符则返回 -1
pub fn console_getchar() -> Sbiret {
    sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0, 0)
}

/// 调用 SBI_SHUTDOWN 来关闭操作系统(直接退出 QEMU)
pub fn shutdown() -> ! {
    sbi_call(SBI_SHUTDOWN, 0, 0, 0,0);
    unreachable!()
}

/// 设置下一次时钟中断的时间
pub fn set_timer(time: usize) {
    sbi_call(SBI_SET_TIMER, 0, time, 0,0);
}

/// 关闭 hart ,等价于 SBI_SHUTDOWN ?
/// TODO: need to verify
pub fn sbi_hart_stop() -> Sbiret {
    sbi_call(SBI_HSM_STOP_EID, SBI_HSM_STOP_FID, 0, 0, 0)
}
  • 修改后的函数 os/src/sbi.rs::panic_handler
#[panic_handler]
fn panic_handler(info: &PanicInfo) -> ! {
    // `\x1b[??m` 是控制终端字符输出格式的指令,在支持的平台上可以改变文字颜色等等,这里使用红色
    // 参考:https://misc.flogisoft.com/bash/tip_colors_and_formatting
    //
    // 需要全局开启 feature(panic_info_message) 才可以调用 .message() 函数
    if let Some(location) = info.location() {
        println!(
            "\x1b[1;31m{}:{}: '{}'\x1b[0m",
            location.file(),
            location.line(),
            info.message().unwrap()
        );
    } else {
        println!("\x1b[1;31mpanic: '{}'\x1b[0m", info.message().unwrap());
    }

    // This call is not expected to return under normal conditions.
    // Returns SBI_ERR_FAILED through sbiret.error only if it fails, 
    // where SBI_ERR_FAILED = -1.
    let sbiret = sbi_hart_stop();
    println!("sbiret.error = {}, sbiret.value = {}", sbiret.error, sbiret.value);
    
    unreachable!()
}

遇到问题

对于本问题的分析如上。但是对于目前给出 shutdown 的方案仍然感到疑惑。

generalibm avatar Jan 15 '21 14:01 generalibm

看起来这是 OpenSBI 和 Qemu 的兼容性问题。 这是该项目的新一轮迭代截至第一章的代码,最终也调用了 SBI 的 shutdown 接口。这一版中,我们将 SBI 实现替换为 RustSBI,它完全使用 Rust 开发,更加简洁,且完全足以用于教学。你可以试试这个 ch1 分支在你原来的环境下可否正常运行。这是第一章的文档

wyfcyx avatar Jan 15 '21 14:01 wyfcyx

@wyfcyx 谢谢,在我的机器下的运行结果和文档中有些出入,具体说来,我这里运行的结果如下:

[rustsbi] Version 0.1.0
.______       __    __      _______.___________.  _______..______   __
|   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
|  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
|      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
|  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
| _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|

[rustsbi] Platform: QEMU
[rustsbi] misa: RV64ACDFIMSU
[rustsbi] mideleg: 0x222
[rustsbi] medeleg: 0xb109
[rustsbi] Kernel entry: 0x80020000
Hello, world!
.text [0x80020000, 0x80022000)
.rodata [0x80022000, 0x80023000)
.data [0x80023000, 0x80023000)
boot_stack [0x80023000, 0x80033000)
.bss [0x80033000, 0x80033000)
Panicked at src/main.rs:46 Shutdown machine!
panicked at 'Unhandled exception! mcause: Exception(StoreFault), mepc: 000000008000261c, mtval: 0000000000100000', platform/qemu/src/main.rs:395:18

我暂时先发出这个结果,等研究完文档上的说明之后再查查什么原因。再次感谢!!!

generalibm avatar Jan 15 '21 15:01 generalibm

根据上述结果最后一行的信息,定位到了 RUSTSBI 原码处,再根据 platform/qemu/src/main.rs:355 解释,应当是 RUSTSBI 认为 rCore 在 shutdown 处发出的 ecall(8,0,0,0) 为非法指令。

目前的解决方法是,安装 qemu v5.0.0 。

备注:安装 qemu v5.0.0 之后的 rCore-Tutorial 和 rCore-Tutorial-v3 均能正常 shutdown 。

generalibm avatar Jan 16 '21 12:01 generalibm

最近在 qemu v5.1.0 上遇到了同样的问题,最后发现原因如下:

qemu 中 shutdown 的实现是让 guest OS 对 sifive,test1 设备进行 MMIO 写操作,在 qemu v5.1.0 中要求对该设备的 MMIO 必须是 32 位访问,而 OpenSBI 使用了 16 位的写操作 (这里),导致在 M 态发生 Store/AMO access fault 异常。

在 QEMU v5.2.0 之后该问题被修复,详见该补丁:https://github.com/qemu/qemu/commit/ab3d207fe89bc0c63739db19e177af49179aa457

equation314 avatar Nov 04 '21 08:11 equation314