From b3bd9cdd6347770d1e2f0719e777d1a233d3f45a Mon Sep 17 00:00:00 2001 From: XiakaiPan <13212017962@163.com> Date: Fri, 2 Dec 2022 20:04:28 +0800 Subject: [PATCH 1/4] article: trap-impl-1-v1 add original article --- articles/20221120-riscv-kvm-int-impl-1.md | 667 ++++++++++++++++++++++ 1 file changed, 667 insertions(+) create mode 100644 articles/20221120-riscv-kvm-int-impl-1.md diff --git a/articles/20221120-riscv-kvm-int-impl-1.md b/articles/20221120-riscv-kvm-int-impl-1.md new file mode 100644 index 0000000..2ebca55 --- /dev/null +++ b/articles/20221120-riscv-kvm-int-impl-1.md @@ -0,0 +1,667 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [pangu]
+> Author: XiakaiPan <13212017962@163.com>
+> Date: 20221201
+> Revisor: Walimis
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [RISC-V 虚拟化技术调研与分析](https://gitee.com/tinylab/riscv-linux/issues/I5E4VB)
+> Sponsor: PLCT Lab, ISCAS + +# RISC-V 中断处理的实现(一) + +## 前言 + +本文就 Spike 和 QEMU 中与 RISC-V Trap 处理相关的实现细节进行了较为深入的挖掘,包括 PLIC 和 CLINT 等在模拟器中的实现。从模拟器的角度来看,当前它们对于虚拟化扩展的支持,是在其内部中断处理机制的基础上,添加了虚拟化所需的 CSR 以及对应的 Trap 判断与触发逻辑。 + +## 软件版本 + +| Software | Version | +|------------|------------------------------------------| +| [QEMU][5] | 7.1.0 | +| [Spike][6] | 70f8aa01b803f4dbc0461fd7c986c1ca76d4b1d9 | + +## Spike 实现分析 + +### Trap 统一编码与命名习惯 + +RISC-V Trap 包含中断和异常,其编码在 [自动生成][1] 的编码文件中分别以如下名称开头: + +- IRQ:Interrupt ReQuest,用于表示中断编码 +- CAUSE:cause 是 RISC-V 中用于保存 trap 的具体编码的 CSR,包括 `mcause`, `scause`, `vscause` 等,此处专门用于表示异常编码 + + +### Trap 处理函数调用 + + + +#### 中断处理 + +##### 中断定义 + +```cpp +// riscv/encoding.h: line 266 +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_COP 12 +#define IRQ_LCOF 13 +``` + +Spike 定义了 `sim_t` 类作为作为最外层的模拟管理器,其包含了一个 `processor` vector,Spike 模拟 CPU 的执行即是 `sim` 的各个 `processor` 调用 `step` 函数执行 fetch, decode, execute 等操作。trap 处理操作即在 `step` 函数中实现。 + +具体来说,trap 处理包含了两个步骤: + +##### 判断当前是否需要进行 Interrupt 处理 + +当且仅当 `mie` 和 `mip` 两个 CSR 的值,进行逻辑与不为 0 时,trap 才会被处理: + +```cpp +void take_pending_interrupt() { take_interrupt(state.mip->read() & state.mie->read()); } +``` + +##### 确定 Interrupt 的内容 + +`void take_interrupt(reg_t mask);`:如果 `mask` 为 0 则不执行 trap 处理,否则将判断 trap 具体类型,抛出一个 trap(`trap_t`) + +```cpp +void processor_t::take_interrupt(reg_t pending_interrupts) +{ + // 不执行中断处理 + // Do nothing if no pending interrupts + if (!pending_interrupts) { + return; + } + + // 依照 M, HS, VS 的顺序确定具体要处理哪一个特权级的 trap + // M-ints have higher priority over HS-ints and VS-ints + const reg_t mie = get_field(state.mstatus->read(), MSTATUS_MIE); + const reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie); + reg_t enabled_interrupts = pending_interrupts & ~state.mideleg->read() & -m_enabled; + if (enabled_interrupts == 0) { + // HS-ints have higher priority over VS-ints + const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read(); + const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE); + const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie); + enabled_interrupts = pending_interrupts & deleg_to_hs & -hs_enabled; + if (state.v && enabled_interrupts == 0) { + // VS-ints have least priority and can only be taken with virt enabled + const reg_t deleg_to_vs = state.hideleg->read(); + const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie); + enabled_interrupts = pending_interrupts & deleg_to_vs & -vs_enabled; + } + } + + // 按照 MEI, MSI, MTI; SEI, SSI, STI (HS > VS) 的优先级确定 mcause/scause 最高位之外的内容 + if (!state.debug_mode && enabled_interrupts) { + // nonstandard interrupts have highest priority + if (enabled_interrupts >> (IRQ_M_EXT + 1)) + enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1); + // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI + else if (enabled_interrupts & MIP_MEIP) + enabled_interrupts = MIP_MEIP; + else if (enabled_interrupts & MIP_MSIP) + enabled_interrupts = MIP_MSIP; + else if (enabled_interrupts & MIP_MTIP) + enabled_interrupts = MIP_MTIP; + else if (enabled_interrupts & MIP_SEIP) + enabled_interrupts = MIP_SEIP; + else if (enabled_interrupts & MIP_SSIP) + enabled_interrupts = MIP_SSIP; + else if (enabled_interrupts & MIP_STIP) + enabled_interrupts = MIP_STIP; + else if (enabled_interrupts & MIP_LCOFIP) + enabled_interrupts = MIP_LCOFIP; + else if (enabled_interrupts & MIP_VSEIP) + enabled_interrupts = MIP_VSEIP; + else if (enabled_interrupts & MIP_VSSIP) + enabled_interrupts = MIP_VSSIP; + else if (enabled_interrupts & MIP_VSTIP) + enabled_interrupts = MIP_VSTIP; + else + abort(); + + // 抛出异常编码(最高位为 1) + throw trap_t(((reg_t)1 << (isa->get_max_xlen() - 1)) | ctz(enabled_interrupts)); + } +} +``` + +#### 异常处理 + +##### 异常定义 + +如上节所述,异常的指令集编码即宏定义命名由生成的 `encoding.h` 指定,之后在 `trap.h` 通过宏定义为每一类异常定义一个 `trap_t` 的派生类。 + +RISC-V 指令集规定的所有异常编码及其命名如下代码块所示: + +```cpp +// riscv/encoding.h: line 3147 +#define CAUSE_MISALIGNED_FETCH 0x0 +#define CAUSE_FETCH_ACCESS 0x1 +#define CAUSE_ILLEGAL_INSTRUCTION 0x2 +#define CAUSE_BREAKPOINT 0x3 +#define CAUSE_MISALIGNED_LOAD 0x4 +#define CAUSE_LOAD_ACCESS 0x5 +#define CAUSE_MISALIGNED_STORE 0x6 +#define CAUSE_STORE_ACCESS 0x7 +#define CAUSE_USER_ECALL 0x8 +#define CAUSE_SUPERVISOR_ECALL 0x9 +#define CAUSE_VIRTUAL_SUPERVISOR_ECALL 0xa +#define CAUSE_MACHINE_ECALL 0xb +#define CAUSE_FETCH_PAGE_FAULT 0xc +#define CAUSE_LOAD_PAGE_FAULT 0xd +#define CAUSE_STORE_PAGE_FAULT 0xf +#define CAUSE_FETCH_GUEST_PAGE_FAULT 0x14 +#define CAUSE_LOAD_GUEST_PAGE_FAULT 0x15 +#define CAUSE_VIRTUAL_INSTRUCTION 0x16 +#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17 +``` + +将 exception cause 编码与特定类绑定是通过如下代码实现的,共有三类 exception 类型的 trap: + +- `MEM_TRAP`:访存相关的异常,如地址对齐、page-fault +- `TRAP`:系统调用指令 `ecall` 和 中断指令 `ebreak` +- `INSN_TRAP`:指令相关异常,如非法指令、虚拟指令特权级问题 + +```cpp +// riscv/trap.h: line 91 +#define DECLARE_MEM_TRAP(n, x) class trap_##x : public mem_trap_t { \ + public: \ + trap_##x(bool gva, reg_t tval, reg_t tval2, reg_t tinst) : mem_trap_t(n, gva, tval, tval2, tinst) {} \ + const char* name() { return "trap_"#x; } \ +}; +#define DECLARE_TRAP(n, x) class trap_##x : public trap_t { \ + public: \ + trap_##x() : trap_t(n) {} \ + const char* name() { return "trap_"#x; } \ +}; +#define DECLARE_INST_TRAP(n, x) class trap_##x : public insn_trap_t { \ + public: \ + trap_##x(reg_t tval) : insn_trap_t(n, /* gva */false, tval) {} \ + const char* name() { return "trap_"#x; } \ +}; +// ... + +// riscv/trap.h: line 103 +DECLARE_MEM_TRAP(CAUSE_MISALIGNED_FETCH, instruction_address_misaligned) +DECLARE_TRAP(CAUSE_USER_ECALL, user_ecall) +DECLARE_INST_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) +// ... +``` + +综上,所有 trap(exception,interrupt) 在 Spike 代码中的表示方式如下图所示: + +```mermaid +flowchart BT + +subgraph interrupt +int[interrupt] +end + +subgraph riscv/trap.h: abstract trap +direction LR +t[trap_t]---int +it[insn_trap_t]-->t +mt[mem_trap_t]-->t +end + +subgraph riscv/trap.h: all exceptions + +tmf[trap_instruction_address_misaligned] +tii[trap_illegal_instruction] +tec[trap_user_ecall] +oe[other exceptions, ...] + +tmf-->mt +tii-->it +tec-->t + +oe-.->it +oe-.->mt +oe-.->t +end + +subgraph riscv/encoding.h +mf[CAUSE_MISALIGNED_FETCH] +ii[CAUSE_ILLEGAL_INSTRUCTION] +ec[CAUSE_USER_ECALL] +oc[other causes, ...] +end + +mf-->tmf +ii-->tii +ec-->tec +oc-->oe +``` + +([下载由 Mermaid 生成的 PNG 图片][007]) + +##### 异常抛出 + +整个模拟器在执行过程中,通过 `try, catch` 来捕获并处理每个处理器执行中的异常,这些异常是在各个组件以及指令中写定的。例如访存相关的异常都是在 MMU 的实现中规定的: + +```cpp +// riscv/mmu.h: line 364 +// ITLB lookup +inline tlb_entry_t translate_insn_addr(reg_t addr) { + // ... + return fetch_slow_path(addr); +} +// riscv/mmu.cc: line 80 +tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr) +{ + // ... + if (!mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp)) + throw trap_instruction_access_fault(proc->state.v, vaddr, 0, 0); + result = {(char*)&fetch_temp - vaddr, paddr - vaddr}; + // ... + return result; +} +``` + +单个处理器的执行主题循环如下所示,异常和中断均是在其中的 `try, catch` 中捕获并处理的: + +```cpp +// riscv/execute.cc: line 219 +// fetch/decode/execute loop +void processor_t::step(size_t n) +{ + // ... + + while (n > 0) { + // ... + + try + { + take_pending_interrupt(); + + // 仿真的循环主体:抛出执行过程中的异常,之后进入 catch 中的处理语句 + // Main simulation loop, slow/fast path: throw exceptions if they occur during execution + // ... + } + catch(trap_t& t) + { + take_trap(t, pc); + // ... + } + // Other catch statements, instructions counting. + // ... + } +} + +``` + +#### Trap 处理 + +整个 trap 的处理过程如下图所示: + +虚线表示函数对应的实现,细实线表示函数调用关系,粗实线表示参数的传递关系。 + +```mermaid +flowchart TB + +subgraph sim.h +ss[sim_t] +end + +subgraph execute.c +ps[step] + +subgraph try_catch +try--> +ttpi[take_pending_interrupt] + +try-->mem[mmu]==throw mem_trap_t==>c + +try-->o[others]==throw insn_trap_t==>c +end + +ps-->try_catch +c[catch take_trap] +end + +subgraph processor.cc +s[sim_t::procs->step] +tpi[take_pending_interrupt] +ti[take_interrupt] +tt[take_trap] +end + +subgraph mmu +li[load_insn] +ai[access_icache] +end + +mem-->li +mem-->ai + +ss-->s-.->ps +ttpi-.->tpi-->ti +c-.->tt + +ttpi==throw trap_t==>c +ti==interrupt==>tt +``` + +([下载由 Mermaid 生成的 PNG 图片][008]) + +由图可知,最终的 trap 处理是通过调用 `riscv/process.cc` 的 `take_trap` 函数实现的,其定义及功能分析参见 [此文][2] 的模拟器实现中的 Spike 小节。简单来说,它完成了如下任务: + +- 确定该 trap 的类型(异常或者中断)以及它将在哪个特权级被处理(默认 M,但可以通过 medeleg/mideleg 和 hedeleg/hideleg 委托给 HS 或 VS) +- 写入 CSR(`cause`, `epc`, `tval`)以记录 trap 内容,保存当前执行环境用于处理完成之后的恢复 +- 修改 `status` 寄存器及当前特权级,进入 trap 处理程序运行环境 + +### CLINT + +CLINT(Core-Local INTerrupt)是 RISC-V 核间局部中断控制器,用于管理软件中断和计时器中断的注入和解除。 + +在 Spike 中,CLINT 是 `bus` 上的设备之一 `device`,CPU 对 CLINT 和 `mems` 等的访问通过 `mmio.store/load` 来实现。所有的 mmio 访存均是通过调用 `bus.load/store` 完成的,例如 mmu_t 和 sim_t 的 load, store 操作: + +```mermaid +flowchart + +subgraph riscv/abstract_device.h +ad[abstract_device_t] +end + +subgraph riscv/devices.h +direction TB +cl[clint_t]-->ad +rom[rom_device_t]-->ad +mem[mem_t]-->ad +plic[plic_t]-->ad +ns[ns16550_t]-->ad +plg[mmio_plugin_device_t]-->ad + +subgraph bus_t +d[devices:map] +access[load/store] +end + +cl-.-d +rom-.-d +mem-.-d +plic-.-d +ns-.-d +plg-.-d + +bus_t-->ad +end + +subgraph riscv/sim.cc:sim_t +ldst[mmio_load/store]-->access +end + +subgraph riscv/mmu.cc:mmu_t +direction LR +mldst[mmio_load/store]-->ldst +spi[store_slow_path_intrapage]-->mldst +ssp[store_slow_path]-->spi +store[store, ...]-->ssp +end +``` + +([下载由 Mermaid 生成的 PNG 图片][009]) + +而 CPU 通过 `bus` 实现的访存,是先通过地址所在的范围确定对应的具体设备,进而调用该设备的访存函数来完成的: + +```cpp +bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + // Find the device with the base address closest to but + // less than addr (price-is-right search) + auto it = devices.upper_bound(addr); + if (devices.empty() || it == devices.begin()) { + // Either the bus is empty, or there weren't + // any items with a base address <= addr + return false; + } + // Found at least one item with base address <= addr + // The iterator points to the device after this, so + // go back by one item. + it--; + return it->second->load(addr - it->first, len, bytes); +} + +bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + auto it = devices.upper_bound(addr); + if (devices.empty() || it == devices.begin()) { + return false; + } + it--; + return it->second->store(addr - it->first, len, bytes); +} +``` + +对于 CLINT 来说,其所担负的软件中断和计时器中断的职能,就是通过 `clint_t` 的 `load/store` 方法配合对应 CSR 的访问来实现的,参考 [此文][3]: + +```cpp +// riscv/clint.cc: line 48 +bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + // 若地址在 MSIP 内,表示当前中断为软件中断,将 byte 内的内容写入 addr 作为偏移量所指示的位置,即写入软件中断 + if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) { + std::vector msip(procs.size()); + std::vector mask(procs.size(), 0); + memcpy((uint8_t*)&msip[0] + addr - MSIP_BASE, bytes, len); + memset((uint8_t*)&mask[0] + addr - MSIP_BASE, 0xff, len); + for (size_t i = 0; i < procs.size(); ++i) { + if (!(mask[i] & 0xFF)) continue; + procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, 0); + if (!!(msip[i] & 1)) + procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, MIP_MSIP); + } + } else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) { + // 设置计时器中断:向 mtimecmp 寄存器写入特定值,待到 mtime 达到该值时产生一个中断 + memcpy((uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, bytes, len); + } else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) { + // 设置 mtime 的值 + memcpy((uint8_t*)&mtime + addr - MTIME_BASE, bytes, len); + } else { + return false; + } + increment(0); + return true; +} +``` + +### PLIC/Interrupt Controller/NS16550 + +PLIC(Platform-Level Interrupt Controller)是 RISC-V 外部中断控制器,而 NS16550 则是一个 [UART(Universal Asynchronous Receiver/Transmitter)软核][4],用于 CPU 和外设之间的通信。CLINT,PLIC 以及 NS16550 都要挂载在设备树(fdt, flatten device tree)上。NS16550 在创建时会以 PLIC 指针为中断控制器,用来向 CPU 发送外部中断。 + +与 CLINT 类似,PLIC 等设备在创建之后也都几乎没有用到除计时之外的功能。 + +## QEMU + +### Trap 相关术语及定义 + +中断统一以 IRQ 作为前缀命名: + +```cpp +// target/riscv/cpu_bits.h: line 609 +/* Interrupt causes */ +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_LOCAL_MAX 16 +#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) +``` + +异常定义为枚举变量: + +```cpp +// target/riscv/cpu_bits.h: line 581 +/* Exception causes */ +typedef enum RISCVException { + RISCV_EXCP_NONE = -1, /* sentinel value */ + RISCV_EXCP_INST_ADDR_MIS = 0x0, + RISCV_EXCP_INST_ACCESS_FAULT = 0x1, + RISCV_EXCP_ILLEGAL_INST = 0x2, + RISCV_EXCP_BREAKPOINT = 0x3, + RISCV_EXCP_LOAD_ADDR_MIS = 0x4, + RISCV_EXCP_LOAD_ACCESS_FAULT = 0x5, + RISCV_EXCP_STORE_AMO_ADDR_MIS = 0x6, + RISCV_EXCP_STORE_AMO_ACCESS_FAULT = 0x7, + RISCV_EXCP_U_ECALL = 0x8, + RISCV_EXCP_S_ECALL = 0x9, + RISCV_EXCP_VS_ECALL = 0xa, + RISCV_EXCP_M_ECALL = 0xb, + RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ + RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ + RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ + RISCV_EXCP_SEMIHOST = 0x10, + RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, + RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15, + RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16, + RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT = 0x17, +} RISCVException; +``` + +所有的 CSR 操作函数其返回值均为 `RISCVException`,例如: + +```cpp +// target/riscv/csr.c: line 1468 +static RISCVException read_mtvec(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mtvec; + return RISCV_EXCP_NONE; +} +// target/riscv/csr.c: line 832 +static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & + ~((1ULL << (RISCV_EXCP_S_ECALL)) | + (1ULL << (RISCV_EXCP_VS_ECALL)) | + (1ULL << (RISCV_EXCP_M_ECALL)) | + (1ULL << (RISCV_EXCP_INST_GUEST_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_VIRT_INSTRUCTION_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT))); +``` + +### Trap 处理 + +QEMU 内部通过 `DEFINE_TYPES` 注册 RISC-V CPU 的相关信息,其中就包括中断处理相关的操作。内部中断(Software,Timer)通过将中断处理函数 `riscv_cpu_do_interrupt` 注册到 `tcg_ops` 里面实现来实现,外部中断则通过 CPU 初始化函数中的 GPIO 设备配置来完成。 + +整体结构如下图所示: + +```mermaid +flowchart TB + +subgraph cpu.c + +define[DEFINE_TYPES: riscv_cpu_type_infos]--> +ti[riscv_cpu_type_infos]-->ci[.class_init = riscv_cpu_class_init]-->tcg[riscv_tcg_ops] + +ti-->ii[.instance_init = riscv_cpu_init] +end + +subgraph cpu.c +ii-->igpioi[qdev_init_gpio_in]-->sirq[riscv_cpu_set_irq] +end + +subgraph kvm.c +ksirq[kvm_riscv_set_irq] +end + +ioctl[kvm_vcpu_ioctl: KVM_INTERRUPT] + +sirq-->ksirq-->ioctl + +subgraph cpu_helper.c +eint[riscv_cpu_exec_interrupt] +dint[riscv_cpu_do_interrupt] +end + +tcg-->eint-->dint +tcg-->dint +``` + +([下载由 Mermaid 生成的 PNG 图片][010]) + +用于依据 `type_infos` 进行初始化的函数如下: + +```cpp +// include/qom/object.h: line 849 +/** + * DEFINE_TYPES: + * @type_array: The array containing #TypeInfo structures to register + * + * @type_array should be static constant that exists for the life time + * that the type is registered. + */ +#define DEFINE_TYPES(type_array) \ +static void do_qemu_init_ ## type_array(void) \ +{ \ + type_register_static_array(type_array, ARRAY_SIZE(type_array)); \ +} \ +type_init(do_qemu_init_ ## type_array) + +// include/qemu/module.h: line 56 +#define type_init(function) module_init(function, MODULE_INIT_QOM) +// include/qemu/module.h: line 34 +/* This should not be used directly. Use block_init etc. instead. */ +#define module_init(function, type) \ +static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ +{ \ + register_module_init(function, type); \ +} +#endif +``` + +```cpp +// util/module.c: line 69 +void register_module_init(void (*fn)(void), module_init_type type) +{ + ModuleEntry *e; + ModuleTypeList *l; + + e = g_malloc0(sizeof(*e)); + e->init = fn; + e->type = type; + + l = find_type(type); + + QTAILQ_INSERT_TAIL(l, e, node); +} +``` + +## 总结 + +本文对两个模拟器中 RISC-V 的 Trap 定义和处理过程进行了较为详细地调研,为实际上手进行软硬件实现提供了一定的参考。 + +## 参考资料 + +- [RISC-V CSR 编码生成器][1] +- [RISC-V 架构 H 扩展中的 Trap 处理][2] +- [哪吒 D1 开发板 RISC-V CLINT 编程实践][3] + +[1]: https://github.com/riscv/riscv-opcodes +[2]: 20220905-riscv-kvm-virt-trap.md#spike +[3]: https://cloud.tencent.com/developer/article/1851557 +[4]: https://www.latticesemi.com/zh-CN/Products/DesignSoftwareAndIP/IntellectualProperty/IPCore/DCDCores/D16550 +[5]: https://www.qemu.org/ +[6]: https://github.com/riscv-software-src/riscv-isa-sim +[007]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png +[008]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png +[009]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png +[010]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png -- Gitee From b89ef29c06413c828c08bad7caaca15d71ec32d1 Mon Sep 17 00:00:00 2001 From: XiakaiPan <13212017962@163.com> Date: Fri, 2 Dec 2022 20:04:34 +0800 Subject: [PATCH 2/4] riscv-kvm-int-impl-1.md: commit correct result of tinycorrect-tounix Signed-off-by: XiakaiPan <13212017962@163.com> --- articles/20221120-riscv-kvm-int-impl-1.md | 1334 ++++++++++----------- 1 file changed, 667 insertions(+), 667 deletions(-) diff --git a/articles/20221120-riscv-kvm-int-impl-1.md b/articles/20221120-riscv-kvm-int-impl-1.md index 2ebca55..b25bb44 100644 --- a/articles/20221120-riscv-kvm-int-impl-1.md +++ b/articles/20221120-riscv-kvm-int-impl-1.md @@ -1,667 +1,667 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [pangu]
-> Author: XiakaiPan <13212017962@163.com>
-> Date: 20221201
-> Revisor: Walimis
-> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
-> Proposal: [RISC-V 虚拟化技术调研与分析](https://gitee.com/tinylab/riscv-linux/issues/I5E4VB)
-> Sponsor: PLCT Lab, ISCAS - -# RISC-V 中断处理的实现(一) - -## 前言 - -本文就 Spike 和 QEMU 中与 RISC-V Trap 处理相关的实现细节进行了较为深入的挖掘,包括 PLIC 和 CLINT 等在模拟器中的实现。从模拟器的角度来看,当前它们对于虚拟化扩展的支持,是在其内部中断处理机制的基础上,添加了虚拟化所需的 CSR 以及对应的 Trap 判断与触发逻辑。 - -## 软件版本 - -| Software | Version | -|------------|------------------------------------------| -| [QEMU][5] | 7.1.0 | -| [Spike][6] | 70f8aa01b803f4dbc0461fd7c986c1ca76d4b1d9 | - -## Spike 实现分析 - -### Trap 统一编码与命名习惯 - -RISC-V Trap 包含中断和异常,其编码在 [自动生成][1] 的编码文件中分别以如下名称开头: - -- IRQ:Interrupt ReQuest,用于表示中断编码 -- CAUSE:cause 是 RISC-V 中用于保存 trap 的具体编码的 CSR,包括 `mcause`, `scause`, `vscause` 等,此处专门用于表示异常编码 - - -### Trap 处理函数调用 - - - -#### 中断处理 - -##### 中断定义 - -```cpp -// riscv/encoding.h: line 266 -#define IRQ_U_SOFT 0 -#define IRQ_S_SOFT 1 -#define IRQ_VS_SOFT 2 -#define IRQ_M_SOFT 3 -#define IRQ_U_TIMER 4 -#define IRQ_S_TIMER 5 -#define IRQ_VS_TIMER 6 -#define IRQ_M_TIMER 7 -#define IRQ_U_EXT 8 -#define IRQ_S_EXT 9 -#define IRQ_VS_EXT 10 -#define IRQ_M_EXT 11 -#define IRQ_S_GEXT 12 -#define IRQ_COP 12 -#define IRQ_LCOF 13 -``` - -Spike 定义了 `sim_t` 类作为作为最外层的模拟管理器,其包含了一个 `processor` vector,Spike 模拟 CPU 的执行即是 `sim` 的各个 `processor` 调用 `step` 函数执行 fetch, decode, execute 等操作。trap 处理操作即在 `step` 函数中实现。 - -具体来说,trap 处理包含了两个步骤: - -##### 判断当前是否需要进行 Interrupt 处理 - -当且仅当 `mie` 和 `mip` 两个 CSR 的值,进行逻辑与不为 0 时,trap 才会被处理: - -```cpp -void take_pending_interrupt() { take_interrupt(state.mip->read() & state.mie->read()); } -``` - -##### 确定 Interrupt 的内容 - -`void take_interrupt(reg_t mask);`:如果 `mask` 为 0 则不执行 trap 处理,否则将判断 trap 具体类型,抛出一个 trap(`trap_t`) - -```cpp -void processor_t::take_interrupt(reg_t pending_interrupts) -{ - // 不执行中断处理 - // Do nothing if no pending interrupts - if (!pending_interrupts) { - return; - } - - // 依照 M, HS, VS 的顺序确定具体要处理哪一个特权级的 trap - // M-ints have higher priority over HS-ints and VS-ints - const reg_t mie = get_field(state.mstatus->read(), MSTATUS_MIE); - const reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie); - reg_t enabled_interrupts = pending_interrupts & ~state.mideleg->read() & -m_enabled; - if (enabled_interrupts == 0) { - // HS-ints have higher priority over VS-ints - const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read(); - const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE); - const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie); - enabled_interrupts = pending_interrupts & deleg_to_hs & -hs_enabled; - if (state.v && enabled_interrupts == 0) { - // VS-ints have least priority and can only be taken with virt enabled - const reg_t deleg_to_vs = state.hideleg->read(); - const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie); - enabled_interrupts = pending_interrupts & deleg_to_vs & -vs_enabled; - } - } - - // 按照 MEI, MSI, MTI; SEI, SSI, STI (HS > VS) 的优先级确定 mcause/scause 最高位之外的内容 - if (!state.debug_mode && enabled_interrupts) { - // nonstandard interrupts have highest priority - if (enabled_interrupts >> (IRQ_M_EXT + 1)) - enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1); - // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI - else if (enabled_interrupts & MIP_MEIP) - enabled_interrupts = MIP_MEIP; - else if (enabled_interrupts & MIP_MSIP) - enabled_interrupts = MIP_MSIP; - else if (enabled_interrupts & MIP_MTIP) - enabled_interrupts = MIP_MTIP; - else if (enabled_interrupts & MIP_SEIP) - enabled_interrupts = MIP_SEIP; - else if (enabled_interrupts & MIP_SSIP) - enabled_interrupts = MIP_SSIP; - else if (enabled_interrupts & MIP_STIP) - enabled_interrupts = MIP_STIP; - else if (enabled_interrupts & MIP_LCOFIP) - enabled_interrupts = MIP_LCOFIP; - else if (enabled_interrupts & MIP_VSEIP) - enabled_interrupts = MIP_VSEIP; - else if (enabled_interrupts & MIP_VSSIP) - enabled_interrupts = MIP_VSSIP; - else if (enabled_interrupts & MIP_VSTIP) - enabled_interrupts = MIP_VSTIP; - else - abort(); - - // 抛出异常编码(最高位为 1) - throw trap_t(((reg_t)1 << (isa->get_max_xlen() - 1)) | ctz(enabled_interrupts)); - } -} -``` - -#### 异常处理 - -##### 异常定义 - -如上节所述,异常的指令集编码即宏定义命名由生成的 `encoding.h` 指定,之后在 `trap.h` 通过宏定义为每一类异常定义一个 `trap_t` 的派生类。 - -RISC-V 指令集规定的所有异常编码及其命名如下代码块所示: - -```cpp -// riscv/encoding.h: line 3147 -#define CAUSE_MISALIGNED_FETCH 0x0 -#define CAUSE_FETCH_ACCESS 0x1 -#define CAUSE_ILLEGAL_INSTRUCTION 0x2 -#define CAUSE_BREAKPOINT 0x3 -#define CAUSE_MISALIGNED_LOAD 0x4 -#define CAUSE_LOAD_ACCESS 0x5 -#define CAUSE_MISALIGNED_STORE 0x6 -#define CAUSE_STORE_ACCESS 0x7 -#define CAUSE_USER_ECALL 0x8 -#define CAUSE_SUPERVISOR_ECALL 0x9 -#define CAUSE_VIRTUAL_SUPERVISOR_ECALL 0xa -#define CAUSE_MACHINE_ECALL 0xb -#define CAUSE_FETCH_PAGE_FAULT 0xc -#define CAUSE_LOAD_PAGE_FAULT 0xd -#define CAUSE_STORE_PAGE_FAULT 0xf -#define CAUSE_FETCH_GUEST_PAGE_FAULT 0x14 -#define CAUSE_LOAD_GUEST_PAGE_FAULT 0x15 -#define CAUSE_VIRTUAL_INSTRUCTION 0x16 -#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17 -``` - -将 exception cause 编码与特定类绑定是通过如下代码实现的,共有三类 exception 类型的 trap: - -- `MEM_TRAP`:访存相关的异常,如地址对齐、page-fault -- `TRAP`:系统调用指令 `ecall` 和 中断指令 `ebreak` -- `INSN_TRAP`:指令相关异常,如非法指令、虚拟指令特权级问题 - -```cpp -// riscv/trap.h: line 91 -#define DECLARE_MEM_TRAP(n, x) class trap_##x : public mem_trap_t { \ - public: \ - trap_##x(bool gva, reg_t tval, reg_t tval2, reg_t tinst) : mem_trap_t(n, gva, tval, tval2, tinst) {} \ - const char* name() { return "trap_"#x; } \ -}; -#define DECLARE_TRAP(n, x) class trap_##x : public trap_t { \ - public: \ - trap_##x() : trap_t(n) {} \ - const char* name() { return "trap_"#x; } \ -}; -#define DECLARE_INST_TRAP(n, x) class trap_##x : public insn_trap_t { \ - public: \ - trap_##x(reg_t tval) : insn_trap_t(n, /* gva */false, tval) {} \ - const char* name() { return "trap_"#x; } \ -}; -// ... - -// riscv/trap.h: line 103 -DECLARE_MEM_TRAP(CAUSE_MISALIGNED_FETCH, instruction_address_misaligned) -DECLARE_TRAP(CAUSE_USER_ECALL, user_ecall) -DECLARE_INST_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) -// ... -``` - -综上,所有 trap(exception,interrupt) 在 Spike 代码中的表示方式如下图所示: - -```mermaid -flowchart BT - -subgraph interrupt -int[interrupt] -end - -subgraph riscv/trap.h: abstract trap -direction LR -t[trap_t]---int -it[insn_trap_t]-->t -mt[mem_trap_t]-->t -end - -subgraph riscv/trap.h: all exceptions - -tmf[trap_instruction_address_misaligned] -tii[trap_illegal_instruction] -tec[trap_user_ecall] -oe[other exceptions, ...] - -tmf-->mt -tii-->it -tec-->t - -oe-.->it -oe-.->mt -oe-.->t -end - -subgraph riscv/encoding.h -mf[CAUSE_MISALIGNED_FETCH] -ii[CAUSE_ILLEGAL_INSTRUCTION] -ec[CAUSE_USER_ECALL] -oc[other causes, ...] -end - -mf-->tmf -ii-->tii -ec-->tec -oc-->oe -``` - -([下载由 Mermaid 生成的 PNG 图片][007]) - -##### 异常抛出 - -整个模拟器在执行过程中,通过 `try, catch` 来捕获并处理每个处理器执行中的异常,这些异常是在各个组件以及指令中写定的。例如访存相关的异常都是在 MMU 的实现中规定的: - -```cpp -// riscv/mmu.h: line 364 -// ITLB lookup -inline tlb_entry_t translate_insn_addr(reg_t addr) { - // ... - return fetch_slow_path(addr); -} -// riscv/mmu.cc: line 80 -tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr) -{ - // ... - if (!mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp)) - throw trap_instruction_access_fault(proc->state.v, vaddr, 0, 0); - result = {(char*)&fetch_temp - vaddr, paddr - vaddr}; - // ... - return result; -} -``` - -单个处理器的执行主题循环如下所示,异常和中断均是在其中的 `try, catch` 中捕获并处理的: - -```cpp -// riscv/execute.cc: line 219 -// fetch/decode/execute loop -void processor_t::step(size_t n) -{ - // ... - - while (n > 0) { - // ... - - try - { - take_pending_interrupt(); - - // 仿真的循环主体:抛出执行过程中的异常,之后进入 catch 中的处理语句 - // Main simulation loop, slow/fast path: throw exceptions if they occur during execution - // ... - } - catch(trap_t& t) - { - take_trap(t, pc); - // ... - } - // Other catch statements, instructions counting. - // ... - } -} - -``` - -#### Trap 处理 - -整个 trap 的处理过程如下图所示: - -虚线表示函数对应的实现,细实线表示函数调用关系,粗实线表示参数的传递关系。 - -```mermaid -flowchart TB - -subgraph sim.h -ss[sim_t] -end - -subgraph execute.c -ps[step] - -subgraph try_catch -try--> -ttpi[take_pending_interrupt] - -try-->mem[mmu]==throw mem_trap_t==>c - -try-->o[others]==throw insn_trap_t==>c -end - -ps-->try_catch -c[catch take_trap] -end - -subgraph processor.cc -s[sim_t::procs->step] -tpi[take_pending_interrupt] -ti[take_interrupt] -tt[take_trap] -end - -subgraph mmu -li[load_insn] -ai[access_icache] -end - -mem-->li -mem-->ai - -ss-->s-.->ps -ttpi-.->tpi-->ti -c-.->tt - -ttpi==throw trap_t==>c -ti==interrupt==>tt -``` - -([下载由 Mermaid 生成的 PNG 图片][008]) - -由图可知,最终的 trap 处理是通过调用 `riscv/process.cc` 的 `take_trap` 函数实现的,其定义及功能分析参见 [此文][2] 的模拟器实现中的 Spike 小节。简单来说,它完成了如下任务: - -- 确定该 trap 的类型(异常或者中断)以及它将在哪个特权级被处理(默认 M,但可以通过 medeleg/mideleg 和 hedeleg/hideleg 委托给 HS 或 VS) -- 写入 CSR(`cause`, `epc`, `tval`)以记录 trap 内容,保存当前执行环境用于处理完成之后的恢复 -- 修改 `status` 寄存器及当前特权级,进入 trap 处理程序运行环境 - -### CLINT - -CLINT(Core-Local INTerrupt)是 RISC-V 核间局部中断控制器,用于管理软件中断和计时器中断的注入和解除。 - -在 Spike 中,CLINT 是 `bus` 上的设备之一 `device`,CPU 对 CLINT 和 `mems` 等的访问通过 `mmio.store/load` 来实现。所有的 mmio 访存均是通过调用 `bus.load/store` 完成的,例如 mmu_t 和 sim_t 的 load, store 操作: - -```mermaid -flowchart - -subgraph riscv/abstract_device.h -ad[abstract_device_t] -end - -subgraph riscv/devices.h -direction TB -cl[clint_t]-->ad -rom[rom_device_t]-->ad -mem[mem_t]-->ad -plic[plic_t]-->ad -ns[ns16550_t]-->ad -plg[mmio_plugin_device_t]-->ad - -subgraph bus_t -d[devices:map] -access[load/store] -end - -cl-.-d -rom-.-d -mem-.-d -plic-.-d -ns-.-d -plg-.-d - -bus_t-->ad -end - -subgraph riscv/sim.cc:sim_t -ldst[mmio_load/store]-->access -end - -subgraph riscv/mmu.cc:mmu_t -direction LR -mldst[mmio_load/store]-->ldst -spi[store_slow_path_intrapage]-->mldst -ssp[store_slow_path]-->spi -store[store, ...]-->ssp -end -``` - -([下载由 Mermaid 生成的 PNG 图片][009]) - -而 CPU 通过 `bus` 实现的访存,是先通过地址所在的范围确定对应的具体设备,进而调用该设备的访存函数来完成的: - -```cpp -bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes) -{ - // Find the device with the base address closest to but - // less than addr (price-is-right search) - auto it = devices.upper_bound(addr); - if (devices.empty() || it == devices.begin()) { - // Either the bus is empty, or there weren't - // any items with a base address <= addr - return false; - } - // Found at least one item with base address <= addr - // The iterator points to the device after this, so - // go back by one item. - it--; - return it->second->load(addr - it->first, len, bytes); -} - -bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes) -{ - auto it = devices.upper_bound(addr); - if (devices.empty() || it == devices.begin()) { - return false; - } - it--; - return it->second->store(addr - it->first, len, bytes); -} -``` - -对于 CLINT 来说,其所担负的软件中断和计时器中断的职能,就是通过 `clint_t` 的 `load/store` 方法配合对应 CSR 的访问来实现的,参考 [此文][3]: - -```cpp -// riscv/clint.cc: line 48 -bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes) -{ - // 若地址在 MSIP 内,表示当前中断为软件中断,将 byte 内的内容写入 addr 作为偏移量所指示的位置,即写入软件中断 - if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) { - std::vector msip(procs.size()); - std::vector mask(procs.size(), 0); - memcpy((uint8_t*)&msip[0] + addr - MSIP_BASE, bytes, len); - memset((uint8_t*)&mask[0] + addr - MSIP_BASE, 0xff, len); - for (size_t i = 0; i < procs.size(); ++i) { - if (!(mask[i] & 0xFF)) continue; - procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, 0); - if (!!(msip[i] & 1)) - procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, MIP_MSIP); - } - } else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) { - // 设置计时器中断:向 mtimecmp 寄存器写入特定值,待到 mtime 达到该值时产生一个中断 - memcpy((uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, bytes, len); - } else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) { - // 设置 mtime 的值 - memcpy((uint8_t*)&mtime + addr - MTIME_BASE, bytes, len); - } else { - return false; - } - increment(0); - return true; -} -``` - -### PLIC/Interrupt Controller/NS16550 - -PLIC(Platform-Level Interrupt Controller)是 RISC-V 外部中断控制器,而 NS16550 则是一个 [UART(Universal Asynchronous Receiver/Transmitter)软核][4],用于 CPU 和外设之间的通信。CLINT,PLIC 以及 NS16550 都要挂载在设备树(fdt, flatten device tree)上。NS16550 在创建时会以 PLIC 指针为中断控制器,用来向 CPU 发送外部中断。 - -与 CLINT 类似,PLIC 等设备在创建之后也都几乎没有用到除计时之外的功能。 - -## QEMU - -### Trap 相关术语及定义 - -中断统一以 IRQ 作为前缀命名: - -```cpp -// target/riscv/cpu_bits.h: line 609 -/* Interrupt causes */ -#define IRQ_U_SOFT 0 -#define IRQ_S_SOFT 1 -#define IRQ_VS_SOFT 2 -#define IRQ_M_SOFT 3 -#define IRQ_U_TIMER 4 -#define IRQ_S_TIMER 5 -#define IRQ_VS_TIMER 6 -#define IRQ_M_TIMER 7 -#define IRQ_U_EXT 8 -#define IRQ_S_EXT 9 -#define IRQ_VS_EXT 10 -#define IRQ_M_EXT 11 -#define IRQ_S_GEXT 12 -#define IRQ_LOCAL_MAX 16 -#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) -``` - -异常定义为枚举变量: - -```cpp -// target/riscv/cpu_bits.h: line 581 -/* Exception causes */ -typedef enum RISCVException { - RISCV_EXCP_NONE = -1, /* sentinel value */ - RISCV_EXCP_INST_ADDR_MIS = 0x0, - RISCV_EXCP_INST_ACCESS_FAULT = 0x1, - RISCV_EXCP_ILLEGAL_INST = 0x2, - RISCV_EXCP_BREAKPOINT = 0x3, - RISCV_EXCP_LOAD_ADDR_MIS = 0x4, - RISCV_EXCP_LOAD_ACCESS_FAULT = 0x5, - RISCV_EXCP_STORE_AMO_ADDR_MIS = 0x6, - RISCV_EXCP_STORE_AMO_ACCESS_FAULT = 0x7, - RISCV_EXCP_U_ECALL = 0x8, - RISCV_EXCP_S_ECALL = 0x9, - RISCV_EXCP_VS_ECALL = 0xa, - RISCV_EXCP_M_ECALL = 0xb, - RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ - RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ - RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ - RISCV_EXCP_SEMIHOST = 0x10, - RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, - RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15, - RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16, - RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT = 0x17, -} RISCVException; -``` - -所有的 CSR 操作函数其返回值均为 `RISCVException`,例如: - -```cpp -// target/riscv/csr.c: line 1468 -static RISCVException read_mtvec(CPURISCVState *env, int csrno, - target_ulong *val) -{ - *val = env->mtvec; - return RISCV_EXCP_NONE; -} -// target/riscv/csr.c: line 832 -static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & - ~((1ULL << (RISCV_EXCP_S_ECALL)) | - (1ULL << (RISCV_EXCP_VS_ECALL)) | - (1ULL << (RISCV_EXCP_M_ECALL)) | - (1ULL << (RISCV_EXCP_INST_GUEST_PAGE_FAULT)) | - (1ULL << (RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT)) | - (1ULL << (RISCV_EXCP_VIRT_INSTRUCTION_FAULT)) | - (1ULL << (RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT))); -``` - -### Trap 处理 - -QEMU 内部通过 `DEFINE_TYPES` 注册 RISC-V CPU 的相关信息,其中就包括中断处理相关的操作。内部中断(Software,Timer)通过将中断处理函数 `riscv_cpu_do_interrupt` 注册到 `tcg_ops` 里面实现来实现,外部中断则通过 CPU 初始化函数中的 GPIO 设备配置来完成。 - -整体结构如下图所示: - -```mermaid -flowchart TB - -subgraph cpu.c - -define[DEFINE_TYPES: riscv_cpu_type_infos]--> -ti[riscv_cpu_type_infos]-->ci[.class_init = riscv_cpu_class_init]-->tcg[riscv_tcg_ops] - -ti-->ii[.instance_init = riscv_cpu_init] -end - -subgraph cpu.c -ii-->igpioi[qdev_init_gpio_in]-->sirq[riscv_cpu_set_irq] -end - -subgraph kvm.c -ksirq[kvm_riscv_set_irq] -end - -ioctl[kvm_vcpu_ioctl: KVM_INTERRUPT] - -sirq-->ksirq-->ioctl - -subgraph cpu_helper.c -eint[riscv_cpu_exec_interrupt] -dint[riscv_cpu_do_interrupt] -end - -tcg-->eint-->dint -tcg-->dint -``` - -([下载由 Mermaid 生成的 PNG 图片][010]) - -用于依据 `type_infos` 进行初始化的函数如下: - -```cpp -// include/qom/object.h: line 849 -/** - * DEFINE_TYPES: - * @type_array: The array containing #TypeInfo structures to register - * - * @type_array should be static constant that exists for the life time - * that the type is registered. - */ -#define DEFINE_TYPES(type_array) \ -static void do_qemu_init_ ## type_array(void) \ -{ \ - type_register_static_array(type_array, ARRAY_SIZE(type_array)); \ -} \ -type_init(do_qemu_init_ ## type_array) - -// include/qemu/module.h: line 56 -#define type_init(function) module_init(function, MODULE_INIT_QOM) -// include/qemu/module.h: line 34 -/* This should not be used directly. Use block_init etc. instead. */ -#define module_init(function, type) \ -static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ -{ \ - register_module_init(function, type); \ -} -#endif -``` - -```cpp -// util/module.c: line 69 -void register_module_init(void (*fn)(void), module_init_type type) -{ - ModuleEntry *e; - ModuleTypeList *l; - - e = g_malloc0(sizeof(*e)); - e->init = fn; - e->type = type; - - l = find_type(type); - - QTAILQ_INSERT_TAIL(l, e, node); -} -``` - -## 总结 - -本文对两个模拟器中 RISC-V 的 Trap 定义和处理过程进行了较为详细地调研,为实际上手进行软硬件实现提供了一定的参考。 - -## 参考资料 - -- [RISC-V CSR 编码生成器][1] -- [RISC-V 架构 H 扩展中的 Trap 处理][2] -- [哪吒 D1 开发板 RISC-V CLINT 编程实践][3] - -[1]: https://github.com/riscv/riscv-opcodes -[2]: 20220905-riscv-kvm-virt-trap.md#spike -[3]: https://cloud.tencent.com/developer/article/1851557 -[4]: https://www.latticesemi.com/zh-CN/Products/DesignSoftwareAndIP/IntellectualProperty/IPCore/DCDCores/D16550 -[5]: https://www.qemu.org/ -[6]: https://github.com/riscv-software-src/riscv-isa-sim -[007]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png -[008]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png -[009]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png -[010]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [tounix]
+> Author: XiakaiPan <13212017962@163.com>
+> Date: 20221201
+> Revisor: Walimis
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [RISC-V 虚拟化技术调研与分析](https://gitee.com/tinylab/riscv-linux/issues/I5E4VB)
+> Sponsor: PLCT Lab, ISCAS + +# RISC-V 中断处理的实现(一) + +## 前言 + +本文就 Spike 和 QEMU 中与 RISC-V Trap 处理相关的实现细节进行了较为深入的挖掘,包括 PLIC 和 CLINT 等在模拟器中的实现。从模拟器的角度来看,当前它们对于虚拟化扩展的支持,是在其内部中断处理机制的基础上,添加了虚拟化所需的 CSR 以及对应的 Trap 判断与触发逻辑。 + +## 软件版本 + +| Software | Version | +|------------|------------------------------------------| +| [QEMU][5] | 7.1.0 | +| [Spike][6] | 70f8aa01b803f4dbc0461fd7c986c1ca76d4b1d9 | + +## Spike 实现分析 + +### Trap 统一编码与命名习惯 + +RISC-V Trap 包含中断和异常,其编码在 [自动生成][1] 的编码文件中分别以如下名称开头: + +- IRQ:Interrupt ReQuest,用于表示中断编码 +- CAUSE:cause 是 RISC-V 中用于保存 trap 的具体编码的 CSR,包括 `mcause`, `scause`, `vscause` 等,此处专门用于表示异常编码 + + +### Trap 处理函数调用 + + + +#### 中断处理 + +##### 中断定义 + +```cpp +// riscv/encoding.h: line 266 +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_COP 12 +#define IRQ_LCOF 13 +``` + +Spike 定义了 `sim_t` 类作为作为最外层的模拟管理器,其包含了一个 `processor` vector,Spike 模拟 CPU 的执行即是 `sim` 的各个 `processor` 调用 `step` 函数执行 fetch, decode, execute 等操作。trap 处理操作即在 `step` 函数中实现。 + +具体来说,trap 处理包含了两个步骤: + +##### 判断当前是否需要进行 Interrupt 处理 + +当且仅当 `mie` 和 `mip` 两个 CSR 的值,进行逻辑与不为 0 时,trap 才会被处理: + +```cpp +void take_pending_interrupt() { take_interrupt(state.mip->read() & state.mie->read()); } +``` + +##### 确定 Interrupt 的内容 + +`void take_interrupt(reg_t mask);`:如果 `mask` 为 0 则不执行 trap 处理,否则将判断 trap 具体类型,抛出一个 trap(`trap_t`) + +```cpp +void processor_t::take_interrupt(reg_t pending_interrupts) +{ + // 不执行中断处理 + // Do nothing if no pending interrupts + if (!pending_interrupts) { + return; + } + + // 依照 M, HS, VS 的顺序确定具体要处理哪一个特权级的 trap + // M-ints have higher priority over HS-ints and VS-ints + const reg_t mie = get_field(state.mstatus->read(), MSTATUS_MIE); + const reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie); + reg_t enabled_interrupts = pending_interrupts & ~state.mideleg->read() & -m_enabled; + if (enabled_interrupts == 0) { + // HS-ints have higher priority over VS-ints + const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read(); + const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE); + const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie); + enabled_interrupts = pending_interrupts & deleg_to_hs & -hs_enabled; + if (state.v && enabled_interrupts == 0) { + // VS-ints have least priority and can only be taken with virt enabled + const reg_t deleg_to_vs = state.hideleg->read(); + const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie); + enabled_interrupts = pending_interrupts & deleg_to_vs & -vs_enabled; + } + } + + // 按照 MEI, MSI, MTI; SEI, SSI, STI (HS > VS) 的优先级确定 mcause/scause 最高位之外的内容 + if (!state.debug_mode && enabled_interrupts) { + // nonstandard interrupts have highest priority + if (enabled_interrupts >> (IRQ_M_EXT + 1)) + enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1); + // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI + else if (enabled_interrupts & MIP_MEIP) + enabled_interrupts = MIP_MEIP; + else if (enabled_interrupts & MIP_MSIP) + enabled_interrupts = MIP_MSIP; + else if (enabled_interrupts & MIP_MTIP) + enabled_interrupts = MIP_MTIP; + else if (enabled_interrupts & MIP_SEIP) + enabled_interrupts = MIP_SEIP; + else if (enabled_interrupts & MIP_SSIP) + enabled_interrupts = MIP_SSIP; + else if (enabled_interrupts & MIP_STIP) + enabled_interrupts = MIP_STIP; + else if (enabled_interrupts & MIP_LCOFIP) + enabled_interrupts = MIP_LCOFIP; + else if (enabled_interrupts & MIP_VSEIP) + enabled_interrupts = MIP_VSEIP; + else if (enabled_interrupts & MIP_VSSIP) + enabled_interrupts = MIP_VSSIP; + else if (enabled_interrupts & MIP_VSTIP) + enabled_interrupts = MIP_VSTIP; + else + abort(); + + // 抛出异常编码(最高位为 1) + throw trap_t(((reg_t)1 << (isa->get_max_xlen() - 1)) | ctz(enabled_interrupts)); + } +} +``` + +#### 异常处理 + +##### 异常定义 + +如上节所述,异常的指令集编码即宏定义命名由生成的 `encoding.h` 指定,之后在 `trap.h` 通过宏定义为每一类异常定义一个 `trap_t` 的派生类。 + +RISC-V 指令集规定的所有异常编码及其命名如下代码块所示: + +```cpp +// riscv/encoding.h: line 3147 +#define CAUSE_MISALIGNED_FETCH 0x0 +#define CAUSE_FETCH_ACCESS 0x1 +#define CAUSE_ILLEGAL_INSTRUCTION 0x2 +#define CAUSE_BREAKPOINT 0x3 +#define CAUSE_MISALIGNED_LOAD 0x4 +#define CAUSE_LOAD_ACCESS 0x5 +#define CAUSE_MISALIGNED_STORE 0x6 +#define CAUSE_STORE_ACCESS 0x7 +#define CAUSE_USER_ECALL 0x8 +#define CAUSE_SUPERVISOR_ECALL 0x9 +#define CAUSE_VIRTUAL_SUPERVISOR_ECALL 0xa +#define CAUSE_MACHINE_ECALL 0xb +#define CAUSE_FETCH_PAGE_FAULT 0xc +#define CAUSE_LOAD_PAGE_FAULT 0xd +#define CAUSE_STORE_PAGE_FAULT 0xf +#define CAUSE_FETCH_GUEST_PAGE_FAULT 0x14 +#define CAUSE_LOAD_GUEST_PAGE_FAULT 0x15 +#define CAUSE_VIRTUAL_INSTRUCTION 0x16 +#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17 +``` + +将 exception cause 编码与特定类绑定是通过如下代码实现的,共有三类 exception 类型的 trap: + +- `MEM_TRAP`:访存相关的异常,如地址对齐、page-fault +- `TRAP`:系统调用指令 `ecall` 和 中断指令 `ebreak` +- `INSN_TRAP`:指令相关异常,如非法指令、虚拟指令特权级问题 + +```cpp +// riscv/trap.h: line 91 +#define DECLARE_MEM_TRAP(n, x) class trap_##x : public mem_trap_t { \ + public: \ + trap_##x(bool gva, reg_t tval, reg_t tval2, reg_t tinst) : mem_trap_t(n, gva, tval, tval2, tinst) {} \ + const char* name() { return "trap_"#x; } \ +}; +#define DECLARE_TRAP(n, x) class trap_##x : public trap_t { \ + public: \ + trap_##x() : trap_t(n) {} \ + const char* name() { return "trap_"#x; } \ +}; +#define DECLARE_INST_TRAP(n, x) class trap_##x : public insn_trap_t { \ + public: \ + trap_##x(reg_t tval) : insn_trap_t(n, /* gva */false, tval) {} \ + const char* name() { return "trap_"#x; } \ +}; +// ... + +// riscv/trap.h: line 103 +DECLARE_MEM_TRAP(CAUSE_MISALIGNED_FETCH, instruction_address_misaligned) +DECLARE_TRAP(CAUSE_USER_ECALL, user_ecall) +DECLARE_INST_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) +// ... +``` + +综上,所有 trap(exception,interrupt) 在 Spike 代码中的表示方式如下图所示: + +```mermaid +flowchart BT + +subgraph interrupt +int[interrupt] +end + +subgraph riscv/trap.h: abstract trap +direction LR +t[trap_t]---int +it[insn_trap_t]-->t +mt[mem_trap_t]-->t +end + +subgraph riscv/trap.h: all exceptions + +tmf[trap_instruction_address_misaligned] +tii[trap_illegal_instruction] +tec[trap_user_ecall] +oe[other exceptions, ...] + +tmf-->mt +tii-->it +tec-->t + +oe-.->it +oe-.->mt +oe-.->t +end + +subgraph riscv/encoding.h +mf[CAUSE_MISALIGNED_FETCH] +ii[CAUSE_ILLEGAL_INSTRUCTION] +ec[CAUSE_USER_ECALL] +oc[other causes, ...] +end + +mf-->tmf +ii-->tii +ec-->tec +oc-->oe +``` + +([下载由 Mermaid 生成的 PNG 图片][007]) + +##### 异常抛出 + +整个模拟器在执行过程中,通过 `try, catch` 来捕获并处理每个处理器执行中的异常,这些异常是在各个组件以及指令中写定的。例如访存相关的异常都是在 MMU 的实现中规定的: + +```cpp +// riscv/mmu.h: line 364 +// ITLB lookup +inline tlb_entry_t translate_insn_addr(reg_t addr) { + // ... + return fetch_slow_path(addr); +} +// riscv/mmu.cc: line 80 +tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr) +{ + // ... + if (!mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp)) + throw trap_instruction_access_fault(proc->state.v, vaddr, 0, 0); + result = {(char*)&fetch_temp - vaddr, paddr - vaddr}; + // ... + return result; +} +``` + +单个处理器的执行主题循环如下所示,异常和中断均是在其中的 `try, catch` 中捕获并处理的: + +```cpp +// riscv/execute.cc: line 219 +// fetch/decode/execute loop +void processor_t::step(size_t n) +{ + // ... + + while (n > 0) { + // ... + + try + { + take_pending_interrupt(); + + // 仿真的循环主体:抛出执行过程中的异常,之后进入 catch 中的处理语句 + // Main simulation loop, slow/fast path: throw exceptions if they occur during execution + // ... + } + catch(trap_t& t) + { + take_trap(t, pc); + // ... + } + // Other catch statements, instructions counting. + // ... + } +} + +``` + +#### Trap 处理 + +整个 trap 的处理过程如下图所示: + +虚线表示函数对应的实现,细实线表示函数调用关系,粗实线表示参数的传递关系。 + +```mermaid +flowchart TB + +subgraph sim.h +ss[sim_t] +end + +subgraph execute.c +ps[step] + +subgraph try_catch +try--> +ttpi[take_pending_interrupt] + +try-->mem[mmu]==throw mem_trap_t==>c + +try-->o[others]==throw insn_trap_t==>c +end + +ps-->try_catch +c[catch take_trap] +end + +subgraph processor.cc +s[sim_t::procs->step] +tpi[take_pending_interrupt] +ti[take_interrupt] +tt[take_trap] +end + +subgraph mmu +li[load_insn] +ai[access_icache] +end + +mem-->li +mem-->ai + +ss-->s-.->ps +ttpi-.->tpi-->ti +c-.->tt + +ttpi==throw trap_t==>c +ti==interrupt==>tt +``` + +([下载由 Mermaid 生成的 PNG 图片][008]) + +由图可知,最终的 trap 处理是通过调用 `riscv/process.cc` 的 `take_trap` 函数实现的,其定义及功能分析参见 [此文][2] 的模拟器实现中的 Spike 小节。简单来说,它完成了如下任务: + +- 确定该 trap 的类型(异常或者中断)以及它将在哪个特权级被处理(默认 M,但可以通过 medeleg/mideleg 和 hedeleg/hideleg 委托给 HS 或 VS) +- 写入 CSR(`cause`, `epc`, `tval`)以记录 trap 内容,保存当前执行环境用于处理完成之后的恢复 +- 修改 `status` 寄存器及当前特权级,进入 trap 处理程序运行环境 + +### CLINT + +CLINT(Core-Local INTerrupt)是 RISC-V 核间局部中断控制器,用于管理软件中断和计时器中断的注入和解除。 + +在 Spike 中,CLINT 是 `bus` 上的设备之一 `device`,CPU 对 CLINT 和 `mems` 等的访问通过 `mmio.store/load` 来实现。所有的 mmio 访存均是通过调用 `bus.load/store` 完成的,例如 mmu_t 和 sim_t 的 load, store 操作: + +```mermaid +flowchart + +subgraph riscv/abstract_device.h +ad[abstract_device_t] +end + +subgraph riscv/devices.h +direction TB +cl[clint_t]-->ad +rom[rom_device_t]-->ad +mem[mem_t]-->ad +plic[plic_t]-->ad +ns[ns16550_t]-->ad +plg[mmio_plugin_device_t]-->ad + +subgraph bus_t +d[devices:map] +access[load/store] +end + +cl-.-d +rom-.-d +mem-.-d +plic-.-d +ns-.-d +plg-.-d + +bus_t-->ad +end + +subgraph riscv/sim.cc:sim_t +ldst[mmio_load/store]-->access +end + +subgraph riscv/mmu.cc:mmu_t +direction LR +mldst[mmio_load/store]-->ldst +spi[store_slow_path_intrapage]-->mldst +ssp[store_slow_path]-->spi +store[store, ...]-->ssp +end +``` + +([下载由 Mermaid 生成的 PNG 图片][009]) + +而 CPU 通过 `bus` 实现的访存,是先通过地址所在的范围确定对应的具体设备,进而调用该设备的访存函数来完成的: + +```cpp +bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + // Find the device with the base address closest to but + // less than addr (price-is-right search) + auto it = devices.upper_bound(addr); + if (devices.empty() || it == devices.begin()) { + // Either the bus is empty, or there weren't + // any items with a base address <= addr + return false; + } + // Found at least one item with base address <= addr + // The iterator points to the device after this, so + // go back by one item. + it--; + return it->second->load(addr - it->first, len, bytes); +} + +bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + auto it = devices.upper_bound(addr); + if (devices.empty() || it == devices.begin()) { + return false; + } + it--; + return it->second->store(addr - it->first, len, bytes); +} +``` + +对于 CLINT 来说,其所担负的软件中断和计时器中断的职能,就是通过 `clint_t` 的 `load/store` 方法配合对应 CSR 的访问来实现的,参考 [此文][3]: + +```cpp +// riscv/clint.cc: line 48 +bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + // 若地址在 MSIP 内,表示当前中断为软件中断,将 byte 内的内容写入 addr 作为偏移量所指示的位置,即写入软件中断 + if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) { + std::vector msip(procs.size()); + std::vector mask(procs.size(), 0); + memcpy((uint8_t*)&msip[0] + addr - MSIP_BASE, bytes, len); + memset((uint8_t*)&mask[0] + addr - MSIP_BASE, 0xff, len); + for (size_t i = 0; i < procs.size(); ++i) { + if (!(mask[i] & 0xFF)) continue; + procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, 0); + if (!!(msip[i] & 1)) + procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, MIP_MSIP); + } + } else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) { + // 设置计时器中断:向 mtimecmp 寄存器写入特定值,待到 mtime 达到该值时产生一个中断 + memcpy((uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, bytes, len); + } else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) { + // 设置 mtime 的值 + memcpy((uint8_t*)&mtime + addr - MTIME_BASE, bytes, len); + } else { + return false; + } + increment(0); + return true; +} +``` + +### PLIC/Interrupt Controller/NS16550 + +PLIC(Platform-Level Interrupt Controller)是 RISC-V 外部中断控制器,而 NS16550 则是一个 [UART(Universal Asynchronous Receiver/Transmitter)软核][4],用于 CPU 和外设之间的通信。CLINT,PLIC 以及 NS16550 都要挂载在设备树(fdt, flatten device tree)上。NS16550 在创建时会以 PLIC 指针为中断控制器,用来向 CPU 发送外部中断。 + +与 CLINT 类似,PLIC 等设备在创建之后也都几乎没有用到除计时之外的功能。 + +## QEMU + +### Trap 相关术语及定义 + +中断统一以 IRQ 作为前缀命名: + +```cpp +// target/riscv/cpu_bits.h: line 609 +/* Interrupt causes */ +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_LOCAL_MAX 16 +#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) +``` + +异常定义为枚举变量: + +```cpp +// target/riscv/cpu_bits.h: line 581 +/* Exception causes */ +typedef enum RISCVException { + RISCV_EXCP_NONE = -1, /* sentinel value */ + RISCV_EXCP_INST_ADDR_MIS = 0x0, + RISCV_EXCP_INST_ACCESS_FAULT = 0x1, + RISCV_EXCP_ILLEGAL_INST = 0x2, + RISCV_EXCP_BREAKPOINT = 0x3, + RISCV_EXCP_LOAD_ADDR_MIS = 0x4, + RISCV_EXCP_LOAD_ACCESS_FAULT = 0x5, + RISCV_EXCP_STORE_AMO_ADDR_MIS = 0x6, + RISCV_EXCP_STORE_AMO_ACCESS_FAULT = 0x7, + RISCV_EXCP_U_ECALL = 0x8, + RISCV_EXCP_S_ECALL = 0x9, + RISCV_EXCP_VS_ECALL = 0xa, + RISCV_EXCP_M_ECALL = 0xb, + RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ + RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ + RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ + RISCV_EXCP_SEMIHOST = 0x10, + RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, + RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15, + RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16, + RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT = 0x17, +} RISCVException; +``` + +所有的 CSR 操作函数其返回值均为 `RISCVException`,例如: + +```cpp +// target/riscv/csr.c: line 1468 +static RISCVException read_mtvec(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mtvec; + return RISCV_EXCP_NONE; +} +// target/riscv/csr.c: line 832 +static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & + ~((1ULL << (RISCV_EXCP_S_ECALL)) | + (1ULL << (RISCV_EXCP_VS_ECALL)) | + (1ULL << (RISCV_EXCP_M_ECALL)) | + (1ULL << (RISCV_EXCP_INST_GUEST_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_VIRT_INSTRUCTION_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT))); +``` + +### Trap 处理 + +QEMU 内部通过 `DEFINE_TYPES` 注册 RISC-V CPU 的相关信息,其中就包括中断处理相关的操作。内部中断(Software,Timer)通过将中断处理函数 `riscv_cpu_do_interrupt` 注册到 `tcg_ops` 里面实现来实现,外部中断则通过 CPU 初始化函数中的 GPIO 设备配置来完成。 + +整体结构如下图所示: + +```mermaid +flowchart TB + +subgraph cpu.c + +define[DEFINE_TYPES: riscv_cpu_type_infos]--> +ti[riscv_cpu_type_infos]-->ci[.class_init = riscv_cpu_class_init]-->tcg[riscv_tcg_ops] + +ti-->ii[.instance_init = riscv_cpu_init] +end + +subgraph cpu.c +ii-->igpioi[qdev_init_gpio_in]-->sirq[riscv_cpu_set_irq] +end + +subgraph kvm.c +ksirq[kvm_riscv_set_irq] +end + +ioctl[kvm_vcpu_ioctl: KVM_INTERRUPT] + +sirq-->ksirq-->ioctl + +subgraph cpu_helper.c +eint[riscv_cpu_exec_interrupt] +dint[riscv_cpu_do_interrupt] +end + +tcg-->eint-->dint +tcg-->dint +``` + +([下载由 Mermaid 生成的 PNG 图片][010]) + +用于依据 `type_infos` 进行初始化的函数如下: + +```cpp +// include/qom/object.h: line 849 +/** + * DEFINE_TYPES: + * @type_array: The array containing #TypeInfo structures to register + * + * @type_array should be static constant that exists for the life time + * that the type is registered. + */ +#define DEFINE_TYPES(type_array) \ +static void do_qemu_init_ ## type_array(void) \ +{ \ + type_register_static_array(type_array, ARRAY_SIZE(type_array)); \ +} \ +type_init(do_qemu_init_ ## type_array) + +// include/qemu/module.h: line 56 +#define type_init(function) module_init(function, MODULE_INIT_QOM) +// include/qemu/module.h: line 34 +/* This should not be used directly. Use block_init etc. instead. */ +#define module_init(function, type) \ +static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ +{ \ + register_module_init(function, type); \ +} +#endif +``` + +```cpp +// util/module.c: line 69 +void register_module_init(void (*fn)(void), module_init_type type) +{ + ModuleEntry *e; + ModuleTypeList *l; + + e = g_malloc0(sizeof(*e)); + e->init = fn; + e->type = type; + + l = find_type(type); + + QTAILQ_INSERT_TAIL(l, e, node); +} +``` + +## 总结 + +本文对两个模拟器中 RISC-V 的 Trap 定义和处理过程进行了较为详细地调研,为实际上手进行软硬件实现提供了一定的参考。 + +## 参考资料 + +- [RISC-V CSR 编码生成器][1] +- [RISC-V 架构 H 扩展中的 Trap 处理][2] +- [哪吒 D1 开发板 RISC-V CLINT 编程实践][3] + +[1]: https://github.com/riscv/riscv-opcodes +[2]: 20220905-riscv-kvm-virt-trap.md#spike +[3]: https://cloud.tencent.com/developer/article/1851557 +[4]: https://www.latticesemi.com/zh-CN/Products/DesignSoftwareAndIP/IntellectualProperty/IPCore/DCDCores/D16550 +[5]: https://www.qemu.org/ +[6]: https://github.com/riscv-software-src/riscv-isa-sim +[007]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png +[008]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png +[009]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png +[010]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png -- Gitee From 9863213926562e62a21a73052c11dae7bb4a245c Mon Sep 17 00:00:00 2001 From: XiakaiPan <13212017962@163.com> Date: Fri, 2 Dec 2022 20:05:43 +0800 Subject: [PATCH 3/4] article: trap-impl-1-v1 tico pass without error --- articles/20221120-riscv-kvm-int-impl-1.md | 7 ++----- .../mermaid-riscv-kvm-int-impl-1-1.png | Bin 0 -> 38001 bytes .../mermaid-riscv-kvm-int-impl-1-2.png | Bin 0 -> 34215 bytes .../mermaid-riscv-kvm-int-impl-1-3.png | Bin 0 -> 48393 bytes .../mermaid-riscv-kvm-int-impl-1-4.png | Bin 0 -> 39696 bytes 5 files changed, 2 insertions(+), 5 deletions(-) create mode 100644 articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png create mode 100644 articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png create mode 100644 articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png create mode 100644 articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png diff --git a/articles/20221120-riscv-kvm-int-impl-1.md b/articles/20221120-riscv-kvm-int-impl-1.md index b25bb44..380814b 100644 --- a/articles/20221120-riscv-kvm-int-impl-1.md +++ b/articles/20221120-riscv-kvm-int-impl-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [tounix]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [tounix spaces]
> Author: XiakaiPan <13212017962@163.com>
> Date: 20221201
> Revisor: Walimis
@@ -28,11 +28,8 @@ RISC-V Trap 包含中断和异常,其编码在 [自动生成][1] 的编码文 - IRQ:Interrupt ReQuest,用于表示中断编码 - CAUSE:cause 是 RISC-V 中用于保存 trap 的具体编码的 CSR,包括 `mcause`, `scause`, `vscause` 等,此处专门用于表示异常编码 - ### Trap 处理函数调用 - - #### 中断处理 ##### 中断定义 @@ -198,7 +195,7 @@ DECLARE_INST_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) // ... ``` -综上,所有 trap(exception,interrupt) 在 Spike 代码中的表示方式如下图所示: +综上,所有 trap(exception,interrupt)在 Spike 代码中的表示方式如下图所示: ```mermaid flowchart BT diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png new file mode 100644 index 0000000000000000000000000000000000000000..75fb405deb0c175d6923e5fb4339b2a4bf480a54 GIT binary patch literal 38001 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 D%O&h6 literal 0 HcmV?d00001 diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png new file mode 100644 index 0000000000000000000000000000000000000000..da018e9babb0340e4bbf1dac896d4e0154e70514 GIT binary patch literal 34215 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 z%0D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 literal 0 HcmV?d00001 diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png new file mode 100644 index 0000000000000000000000000000000000000000..28b67d0de8805e581b57d706726c746843b2d031 GIT binary patch literal 48393 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%0DMA)RY&NkgL#B9MSaU0u%vnD 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 literal 0 HcmV?d00001 diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png new file mode 100644 index 0000000000000000000000000000000000000000..c78785fd12cbdd4dc735c0bb149dba81e6386602 GIT binary patch literal 39696 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 NzOEpie(lab{}*-EkUIbX literal 0 HcmV?d00001 -- Gitee From 1be580eca9d5022a9d500496473676a738d3dc5b Mon Sep 17 00:00:00 2001 From: XiakaiPan <13212017962@163.com> Date: Sun, 4 Dec 2022 11:03:10 +0800 Subject: [PATCH 4/4] article: trap-impl-1-v1 words suggestions from wzj --- articles/20221120-riscv-kvm-int-impl-1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/20221120-riscv-kvm-int-impl-1.md b/articles/20221120-riscv-kvm-int-impl-1.md index 380814b..aeef44c 100644 --- a/articles/20221120-riscv-kvm-int-impl-1.md +++ b/articles/20221120-riscv-kvm-int-impl-1.md @@ -53,7 +53,7 @@ RISC-V Trap 包含中断和异常,其编码在 [自动生成][1] 的编码文 #define IRQ_LCOF 13 ``` -Spike 定义了 `sim_t` 类作为作为最外层的模拟管理器,其包含了一个 `processor` vector,Spike 模拟 CPU 的执行即是 `sim` 的各个 `processor` 调用 `step` 函数执行 fetch, decode, execute 等操作。trap 处理操作即在 `step` 函数中实现。 +Spike 定义了 `sim_t` 类作为最外层的模拟管理器,其包含了一个 `processor` vector,Spike 模拟 CPU 的执行即是 `sim` 的各个 `processor` 调用 `step` 函数执行 fetch, decode, execute 等操作。trap 处理操作即在 `step` 函数中实现。 具体来说,trap 处理包含了两个步骤: @@ -557,7 +557,7 @@ static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS & ### Trap 处理 -QEMU 内部通过 `DEFINE_TYPES` 注册 RISC-V CPU 的相关信息,其中就包括中断处理相关的操作。内部中断(Software,Timer)通过将中断处理函数 `riscv_cpu_do_interrupt` 注册到 `tcg_ops` 里面实现来实现,外部中断则通过 CPU 初始化函数中的 GPIO 设备配置来完成。 +QEMU 内部通过 `DEFINE_TYPES` 注册 RISC-V CPU 的相关信息,其中就包括中断处理相关的操作。内部中断(Software,Timer)通过将中断处理函数 `riscv_cpu_do_interrupt` 注册到 `tcg_ops` 里面来实现,外部中断则通过 CPU 初始化函数中的 GPIO 设备配置来完成。 整体结构如下图所示: -- Gitee