Page 1 of 1

[SOLVED]An unexpected behavior of stvec on my os

Posted: Tue Nov 18, 2025 11:30 am
by theflysong
I'm writing an kernel in risc-v64. When I was writing my intterupt handling system and attempted to use the 'vectored' mode of 'stvec`, things went wrong somewhere. The intterupt handling system just doesn't work. I tries to use gdb and finds out that the pc was once set into the exact value of `stvec`, instead of `stvec & ~0x03`.

Image

I've worked on this problem for hours and nothing has progressed. I even suspected that there was a problem with my qemu and compiled a new one, but nothing changed.

Here's my kernel sourcecode and the way I build and run it.
the related files are `kernel/arch/riscv64/int/exception.h` `kernel/arch/riscv64/int/exception.c` `kernel/arch/riscv64/int/trap.h` `kernel/main.c`
https://github.com/sustcore-team/sustcore
It's worth reminding that I've written my comments and printing messages in Chinese so it'll be a little confused if there are any garbled text.

Also, I'll paste some part of code in the below:

`kernel/arch/riscv64/int/exception.h`

Code: Select all

#pragma once

#include <sus/bits.h>

typedef void (*ISRService)(void);

#define DIRECT 0
#define VECTORED 1
#define IVT_MODE DIRECT

#if IVT_MODE == VECTORED
#define IVT_ENTRIES (16)
extern dword IVT[IVT_ENTRIES];
#endif

void init_ivt();
`kernel/arch/riscv64/int/exception.c`

Code: Select all


...

#if IVT_MODE == VECTORED
__attribute__((aligned(4), section(".text")))
dword IVT[IVT_ENTRIES] = {};

static dword emit_j_ins(const dword offset) {
    if (offset & 0x3) {
        // 偏移量必须是4字节对齐的
        return 0;
    }

    const dword j_opcode = 0x6F;
    const dword imm20    = (offset >> 20) & 0x1;
    const dword imm10_1  = (offset >> 1 ) & 0x3FF;
    const dword imm11    = (offset >> 11) & 0x1;
    const dword imm19_12 = (offset >> 12) & 0xFF;

    const dword imm =
        (imm20    << 31 )|
        (imm10_1  << 21 )|
        (imm11    << 20 )|
        (imm19_12 << 12 );

    return imm | j_opcode;
}

static dword emit_ivt_entry(ISRService isr_func, int idx) {
    qword q_off = (qword)isr_func - (qword)IVT - (idx * sizeof(dword));
    return emit_j_ins((dword)q_off);
}
#endif

void init_ivt() {
#if IVT_MODE == VECTORED
    // 设置为vectored模式
    umb_t ivt_addr = (umb_t)IVT;
    if (ivt_addr & 0x3) {
        log_info("错误: IVT地址未对齐!");
        return;
    }
    umb_t stvec = (ivt_addr & ~0b11) | 0b01;

    for (int i = 0 ; i < IVT_ENTRIES; i++) {
        IVT[i] = emit_ivt_entry(general_isr, i);
    }

    log_info("general_isr 地址: 0x%lx", (umb_t)general_isr);
    log_info("general_exception 地址: 0x%lx", (umb_t)general_exception);
    log_info("IVT 地址: 0x%lx", (umb_t)IVT);
#elif IVT_MODE == DIRECT
    // 采用direct模式
    umb_t stvec = (umb_t)general_isr;
    if (stvec & 0x3) {
        log_info("错误: stvec地址未对齐!");
        return;
    }
    log_info("general_isr 地址: 0x%lx", (umb_t)general_isr);
#endif

    asm volatile("csrw stvec, %0" : : "r"(stvec));
}

#define RISCV_CPU_INTERRUPT_MASK (1ull << 63)

ISR_SERVICE_ATTRIBUTE
void general_isr(void) {
    ISR_SERVICE_START(general_isr, 128);


    if (scause & RISCV_CPU_INTERRUPT_MASK) {
        log_info("这是一个中断");
    } else {
        general_exception(scause, sepc, stval, reglist_ptr);
    }

    ISR_SERVICE_END(general_isr);
}

enum {
    EXCEPTION_INST_MISALIGNED     = 0,   // 指令地址不对齐
    EXCEPTION_INST_ACCESS_FAULT   = 1,   // 指令访问错误
    EXCEPTION_ILLEGAL_INST        = 2,   // 非法指令
    EXCEPTION_BREAKPOINT          = 3,   // 断点
    EXCEPTION_LOAD_MISALIGNED     = 4,   // 加载地址不对齐
    EXCEPTION_LOAD_ACCESS_FAULT   = 5,   // 加载访问错误
    EXCEPTION_STORE_MISALIGNED    = 6,   // 存储地址不对齐
    EXCEPTION_STORE_ACCESS_FAULT  = 7,   // 存储访问错误
    EXCEPTION_ECALL_U             = 8,   // 用户模式环境调用
    EXCEPTION_ECALL_S             = 9,   // 监管模式环境调用
    EXCEPTION_ECALL_M             = 11,  // 机器模式环境调用
    EXCEPTION_INST_PAGE_FAULT     = 12,  // 指令页错误
    EXCEPTION_LOAD_PAGE_FAULT     = 13,  // 加载页错误
    EXCEPTION_STORE_PAGE_FAULT    = 15   // 存储页错误
};

...
[\code]

`kernel/main.c`
[code]
...

// 触发非法指令异常
__attribute__((noinline))
int trigger_illegal_instruction(void) {
    asm volatile (".word 0x00000000");  // 全零是非法指令
    return -1;
}

//------------------ 调试异常处理程序 --------

/**
 * @brief 内核主函数
 * 
 * @return int 
 */
int main(void) {
    log_info("Hello RISCV World!");
    log_info("Hart ID: %u", (unsigned int)hart_id);
    log_info("DTB Ptr: 0x%016lx", (unsigned long)dtb_ptr);

    log_info("开始验证设备树...");

    void *fdt = (void *)dtb_ptr;
    int ret = fdt_check_initial(fdt);
    if (ret != 0) {
        log_error("设备树校验失败: %d", ret);
        return -1;
    }

    log_info("设备树校验成功!");
    log_info("开始遍历设备树节点...");

    traverse_nodes(fdt);

    log_info("设备树节遍历完成!");

    log_info("开始测试非法指令异常处理...");
    init_ivt();
    int a = trigger_illegal_instruction();
    log_info("非法指令异常测试结果: %d", a);

    return 0;
}
...
[\code]

Re: An unexpected behavior of stvec on my os

Posted: Tue Nov 18, 2025 3:49 pm
by Demindiro
I haven't managed to build the project yet. I don't see anything wrong at first sight, but some thoughts:
  • It is possible the debugger is simply getting confused. instruction-to-source mappings aren't always perfect.
    In fact, 0xd is 0b1101. This isn't a valid instruction address as instructions must be aligned on a 2-byte boundary (i.e. low bit always 0).
    (it is valid as stvec value though, of course. mode = 1)
  • It would probably be most interesting to set a breakpoint at init_ivt, then step through the assembly. In particular, check the values of the registers right before and right after the stvec CSR is set.
    If nothing looks suspicious there, just continue single-stepping until you hit the illegal instruction.
You can also do x/10i 0x8020022c in GDB or the QEMU monitor to see if the expected instructions are present in the vector table.

I also suggest you add -d int to the QEMU arguments.

Re: An unexpected behavior of stvec on my os

Posted: Tue Nov 18, 2025 4:21 pm
by Demindiro
Finally got to build it and it seems to work fine?

Code: Select all

[SUSTCore/INFO]设备树节遍历完成!
[SUSTCore/INFO]开始测试非法指令异常处理...
[SUSTCore/INFO]general_isr 地址: 0x8020026c
[SUSTCore/INFO]异常处理程序被调用!
[SUSTCore/INFO]scause: 0x2, sepc: 0x80200108, stval: 0x0
[SUSTCore/INFO]reglist_ptr: 0x80208ec8
[SUSTCore/INFO]x1: 0x8020021a
[SUSTCore/INFO]x2: 0x80208fd0
[SUSTCore/INFO]x3: 0x0
[SUSTCore/INFO]x4: 0x80048000
[SUSTCore/INFO]x5: 0x8020a000
[SUSTCore/INFO]x6: 0x4442434e
[SUSTCore/INFO]x7: 0x1000
[SUSTCore/INFO]x8: 0x0
[SUSTCore/INFO]x9: 0x8fe00000
[SUSTCore/INFO]x10: 0x2e
[SUSTCore/INFO]x11: 0x2e
[SUSTCore/INFO]x12: 0x0
[SUSTCore/INFO]x13: 0x0
[SUSTCore/INFO]x14: 0x0
[SUSTCore/INFO]x15: 0x0
[SUSTCore/INFO]x16: 0x0
[SUSTCore/INFO]x17: 0x4442434e
[SUSTCore/INFO]x18: 0x80200000
[SUSTCore/INFO]x19: 0x0
[SUSTCore/INFO]x20: 0x8fe00000
[SUSTCore/INFO]x21: 0x0
[SUSTCore/INFO]x22: 0x6800
[SUSTCore/INFO]x23: 0x1
[SUSTCore/INFO]x24: 0x2000
[SUSTCore/INFO]x25: 0x80043710
[SUSTCore/INFO]x26: 0x0
[SUSTCore/INFO]x27: 0x0
[SUSTCore/INFO]x28: 0x0
[SUSTCore/INFO]x29: 0x80047f00
[SUSTCore/INFO]x30: 0x20
[SUSTCore/INFO]x31: 0x4
[SUSTCore/INFO]sepc: 0x80200108
[SUSTCore/INFO]sstatus: 0x6100
[SUSTCore/INFO]异常发生在S-Mode
[SUSTCore/INFO]异常类型: 非法指令 (2)
[SUSTCore/INFO]非法指令异常测试结果: -1
I can't read Chinese but the output seem to match what I'd expect, in particular general_isr and 非法指令异常测试结果: -1

EDIT: just noticed you still had DIRECT set. Set it to VECTORED and build it with make -B, still seems to work as expected.

Re: An unexpected behavior of stvec on my os

Posted: Tue Nov 18, 2025 6:34 pm
by theflysong
Demindiro wrote: Tue Nov 18, 2025 4:21 pm Finally got to build it and it seems to work fine?

Code: Select all

[SUSTCore/INFO]设备树节遍历完成!
[SUSTCore/INFO]开始测试非法指令异常处理...
[SUSTCore/INFO]general_isr 地址: 0x8020026c
[SUSTCore/INFO]异常处理程序被调用!
[SUSTCore/INFO]scause: 0x2, sepc: 0x80200108, stval: 0x0
[SUSTCore/INFO]reglist_ptr: 0x80208ec8
[SUSTCore/INFO]x1: 0x8020021a
[SUSTCore/INFO]x2: 0x80208fd0
[SUSTCore/INFO]x3: 0x0
[SUSTCore/INFO]x4: 0x80048000
[SUSTCore/INFO]x5: 0x8020a000
[SUSTCore/INFO]x6: 0x4442434e
[SUSTCore/INFO]x7: 0x1000
[SUSTCore/INFO]x8: 0x0
[SUSTCore/INFO]x9: 0x8fe00000
[SUSTCore/INFO]x10: 0x2e
[SUSTCore/INFO]x11: 0x2e
[SUSTCore/INFO]x12: 0x0
[SUSTCore/INFO]x13: 0x0
[SUSTCore/INFO]x14: 0x0
[SUSTCore/INFO]x15: 0x0
[SUSTCore/INFO]x16: 0x0
[SUSTCore/INFO]x17: 0x4442434e
[SUSTCore/INFO]x18: 0x80200000
[SUSTCore/INFO]x19: 0x0
[SUSTCore/INFO]x20: 0x8fe00000
[SUSTCore/INFO]x21: 0x0
[SUSTCore/INFO]x22: 0x6800
[SUSTCore/INFO]x23: 0x1
[SUSTCore/INFO]x24: 0x2000
[SUSTCore/INFO]x25: 0x80043710
[SUSTCore/INFO]x26: 0x0
[SUSTCore/INFO]x27: 0x0
[SUSTCore/INFO]x28: 0x0
[SUSTCore/INFO]x29: 0x80047f00
[SUSTCore/INFO]x30: 0x20
[SUSTCore/INFO]x31: 0x4
[SUSTCore/INFO]sepc: 0x80200108
[SUSTCore/INFO]sstatus: 0x6100
[SUSTCore/INFO]异常发生在S-Mode
[SUSTCore/INFO]异常类型: 非法指令 (2)
[SUSTCore/INFO]非法指令异常测试结果: -1
I can't read Chinese but the output seem to match what I'd expect, in particular general_isr and 非法指令异常测试结果: -1

EDIT: just noticed you still had DIRECT set. Set it to VECTORED and build it with make -B, still seems to work as expected.
I think we've come to the most weired part: Your output is actually what I expected. However, I still couldn't run it succussfully. I fact, when I add '-d int' into my qemu running options, it gave me

Code: Select all

riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000002, epc:0x0000000080200108, tval:0x0000000000000000, desc=illegal_instruction
riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000005, epc:0x000000008020022d, tval:0x0000000000000000, desc=fault_load
...
I thought it means the $pc was actually set into 0x000000008020022d, which is not aligned, therefore caused an exception, and then caused the same exception again and again in the same way...

I've just cloned my remote repertory into another path, set `MODE=VECTORED`, build and run my kernel again, but nothings changed.
What's worse, I've called one of my friends to run my kernel, and somehow, the kernel works well in his computer too.

I guess there is nothing worse then a bug that can not be reproduced, and specific to my computer.
I couldn't be the fault of compiler. I believed there is actually something wrong with my qemu. But I've just updated into 10.0.3, which is newly posted, and nothing changed.
I don't know if it is related to this problem, but I've run my qemu in WSL2-Ubuntu 22.04, however, I still think it's impossible to be related to this problem.

-----------------------

It seems that it's not the problem of WSL. As one of my friends ran my kernel (use the kernel binary file i build) on his WSL2 archlinux.
Maybe it's a problem with Ubuntu? Or my qemu version is still too old? I don't know what's the case.

-----------------------

Problems have solved: My qemu is too old (even though it's version is 10.0.3) and when I updated to qemu 10.1.0, everything went well.