diff --git a/articles/20221120-riscv-kvm-int-impl-2.md b/articles/20221120-riscv-kvm-int-impl-2.md new file mode 100644 index 0000000000000000000000000000000000000000..c42e4558b6cc3fa52bb021a56292ea95a46bb3a9 --- /dev/null +++ b/articles/20221120-riscv-kvm-int-impl-2.md @@ -0,0 +1,605 @@ +> 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 中断处理的实现(二) + +## 前言 + +本文对于 kvmtool 以及 KVM 中的中断注入与处理、MMIO 设备注册与使用结合代码进行了分析和解读,主要以流程图的方式呈现上述过程中的代码实现。 + +## 代码版本 + +| Software | Version | +|-------------------|------------------------------------------| +| [Linux Kernel][1] | 6.0-rc6 | +| [kvmtool][6] | e17d182ad3f797f01947fc234d95c96c050c534b | + +## KVM 异常处理 + +### RISC-V Trap 类型、编码及其关系 + +在 RISC-V 中,CSR `mcause` / `scause` / `vscause` 用于记录具体的 Trap 编码,Interrupt 和 Exception 的区分是通过 CSR 的标志位实现的。最高位为 1 时表示当前 Trap 为 Interrupt,为 0 时则是 Exception。 + +RISC-V 中的中断分为三类:软件中断、计时器中断和外部中断,来自不同特权级的各类中断具有各自的编码。Linux 中对这些中断编码如下: + +```cpp +// arch/riscv/include/asm/csr.h: line 66 +/* Exception cause high bit - is an interrupt if set */ +#define CAUSE_IRQ_FLAG (_AC(1, UL) << (__riscv_xlen - 1)) + +/* Interrupt causes (minus the high bit) */ +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_PMU_OVF 13 + +/* Exception causes */ +#define EXC_INST_MISALIGNED 0 +#define EXC_INST_ACCESS 1 +#define EXC_INST_ILLEGAL 2 +#define EXC_BREAKPOINT 3 +#define EXC_LOAD_ACCESS 5 +#define EXC_STORE_ACCESS 7 +#define EXC_SYSCALL 8 +#define EXC_HYPERVISOR_SYSCALL 9 +#define EXC_SUPERVISOR_SYSCALL 10 +#define EXC_INST_PAGE_FAULT 12 +#define EXC_LOAD_PAGE_FAULT 13 +#define EXC_STORE_PAGE_FAULT 15 +#define EXC_INST_GUEST_PAGE_FAULT 20 +#define EXC_LOAD_GUEST_PAGE_FAULT 21 +#define EXC_VIRTUAL_INST_FAULT 22 +#define EXC_STORE_GUEST_PAGE_FAULT 23 +``` + +中断标记为 `IRQ`(Interrupt ReQuest),异常标记为 `EXC`(EXCeption)。 + +### KVM 异常处理 + +KVM 内部处理的是来自于 Hypervisor 以及 Guest 的异常,具体来说包括三类(分别对应于非虚拟化情况下的三类异常): + +- 指令异常:对应的就是虚拟指令异常 +- 内存异常:对应 Guest page-fault +- 系统调用:对应来自于 VS-mode 的 ecall 指令 + +详细代码分析参见 [此文][2]。 + +## KVM 虚拟化相关的中断处理 + +在 `arch/riscv/` 文件夹下的实现包含了 RISC-V 虚拟化扩展所用到的 CSR 以及机制的实现,此节将分析其中有关中断处理的代码实现。据代码可知,KVM 的架构相关的实现中仅包括了 VS-mode 对应的一系列中断的处理,其它中断的处理机制见下一节中断控制器分析。 + +### 全局中断基准 + +如果仅支持 M-Mode,那么默认的中断使能(Interrupt Enable)、Trap 向量、中断请求均以 M-Mode 为基准: + +- CSR 使用 `mstatus`, `mie`, `mtvec`, `mcause` 等 +- 状态寄存器标志以 `mstatus` 的为准:`mstatus.mie`, `mstatus.mpie`, `mstatus.mpp` +- 中断编码均对应 M-Mode:`IRQ_M_SOFT/TIMER/EXT` + +否则,就以 S-Mode 为基准,如下方代码所示。 + +```cpp +// arch/riscv/include/asm/csr.h: line 300 +#ifdef CONFIG_RISCV_M_MODE +/* CSR */ +# define CSR_STATUS CSR_MSTATUS +# define CSR_IE CSR_MIE +# define CSR_TVEC CSR_MTVEC +# define CSR_SCRATCH CSR_MSCRATCH +# define CSR_EPC CSR_MEPC +# define CSR_CAUSE CSR_MCAUSE +# define CSR_TVAL CSR_MTVAL +# define CSR_IP CSR_MIP + +/* Status Register Flags */ +# define SR_IE SR_MIE +# define SR_PIE SR_MPIE +# define SR_PP SR_MPP + +/* Interrupt Cause */ +# define RV_IRQ_SOFT IRQ_M_SOFT +# define RV_IRQ_TIMER IRQ_M_TIMER +# define RV_IRQ_EXT IRQ_M_EXT +#else /* CONFIG_RISCV_M_MODE */ +# define CSR_STATUS CSR_SSTATUS +# define CSR_IE CSR_SIE +# define CSR_TVEC CSR_STVEC +# define CSR_SCRATCH CSR_SSCRATCH +# define CSR_EPC CSR_SEPC +# define CSR_CAUSE CSR_SCAUSE +# define CSR_TVAL CSR_STVAL +# define CSR_IP CSR_SIP + +# define SR_IE SR_SIE +# define SR_PIE SR_SPIE +# define SR_PP SR_SPP + +# define RV_IRQ_SOFT IRQ_S_SOFT +# define RV_IRQ_TIMER IRQ_S_TIMER +# define RV_IRQ_EXT IRQ_S_EXT +# define RV_IRQ_PMU IRQ_PMU_OVF +# define SIP_LCOFIP (_AC(0x1, UL) << IRQ_PMU_OVF) + +#endif /* !CONFIG_RISCV_M_MODE */ + +/* IE/IP (Supervisor/Machine Interrupt Enable/Pending) flags */ +#define IE_SIE (_AC(0x1, UL) << RV_IRQ_SOFT) +#define IE_TIE (_AC(0x1, UL) << RV_IRQ_TIMER) +#define IE_EIE (_AC(0x1, UL) << RV_IRQ_EXT) +``` + +M/S-Mode 的中断做统一处理,Guest 内部的 VS-Mode 中断将由 KVM 单独处理。下面将对三类中断的实现分别进行分析。 + +### VS-Mode 软件中断 + +所谓软件中断也称为 IPI(Inter-Processor Interrupt),即处理器间中断。对于 KVM 虚拟机来说,VS-mode 的软件中断是通过 SBI 进行处理的,如下图所示: + +- 创建 KVM 虚拟机及其 VCPU 时会通过一系列调用将 `IRQ_VS_SOFT` 注册进虚拟机(图右 `kvm_main.c`) +- `kvm_riscv_vcpu_exit` 定义了 vCPU 的退出,Processor 间的中断即 IPI 的行为在此处定义,具体来说是通过调用 SBI(Supervisor Binary Interface)实现的(图左) + +```mermaid +flowchart + +subgraph arch/riscv/include/asm/csr.h +isft[IRQ_VS_SOFT] +end + +subgraph arch/riscv/kvm/main.c +hwen[kvm_arch_hardware_enable] +end + +subgraph virt/kvm/kvm_main.c +startcpu[kvm_starting_cpu]-->hwennl +hwenall[hardware_enable_all]-->hwennl +mdl_init[module_kvm_init]--> +rv_init[riscv_kvm_init]--> +kvm_init[kvm_init]-->ops +kvm_exit[kvm_exit]-->ops +ops[kvm_syscore_ops]--> +resume[kvm_resume]-->hwennl +hwennl[hardware_enable_nolock]-->hwen + +vcpu[kvm_vcpu_ioctl]-->run + +dev_ioctl[kvm_dev_ioctl]--> +dev_create_vm[kvm_dev_ioctl_create_vm]--> +cvm[kvm_create_vm]-->hwenall +kvm_init-->startcpu + +kvm_compat[kvm_vcpu_compat_ioctl]-->vcpu + +exp_exit[EXPORT_SYMBOL_GPL]-->kvm_exit + +vm[kvm_vm_ioctl]--> +cvcpu[kvm_vm_ioctl_create_vcpu]--> +vcpu_fd[create_vcpu_fd]--> +fops[kvm_vcpu_fops]-->kvm_compat +end + +subgraph arch/riscv/kvm/vcpu_sbi_replace.c +ipi[kvm_sbi_ext_ipi_handler] +sbi_ipi[vcpu_sbi_ext_ipi]-->ipi +end + +subgraph arch/riscv/kvm/vcpu_sbi.c +ecall[kvm_riscv_vcpu_sbi_ecall]--> +sbi[sbi_ext]-->sbi_ipi + +sbi-->sbiv01 +end + +subgraph arch/riscv/kvm/vcpu.c +ustint[kvm_riscv_vcpu_unset_interrupt] +stint[kvm_riscv_vcpu_set_interrupt] +syncint[kvm_riscv_vcpu_sync_interrupts]-->isft +run[kvm_arch_vcpu_ioctl_run]-->syncint +end + +subgraph arch/riscv/kvm/vcpu_sbi_v01.c +sbiv01[vcpu_sbi_ext_v01]--> +v01[kvm_sbi_ext_v01_handler]-->stint +v01-->ustint +end + +ipi-->stint +stint-->isft +ustint-->isft +hwen-->isft + +subgraph arch/riscv/kvm/vcpu_exit.c +exit[kvm_riscv_vcpu_exit]-->ecall +end +``` + +([下载由 Mermaid 生成的 PNG 图片][007]) + +### VS-Mode 计时器中断 + +与 VS-mode 软件中断类似,vCPU 的计时器中断处理接口在 `vcpu_timer.c` 中定义,而这些借口则是通过调用 `vcpu.c` 中统一的中断处理函数实现的(`kvm_riscv_vcpu_has/set/unset_interrupts`)。 + +```mermaid +flowchart LR + +subgraph arch/riscv/include/asm/csr.h +itimer[IRQ_VS_TIMER] +end + +subgraph arch/riscv/kvm/vcpu.c +ustint[kvm_riscv_vcpu_unset_interrupt]-->itimer +stint[kvm_riscv_vcpu_set_interrupt]-->itimer +hasint[kvm_riscv_vcpu_has_interrupts]-->itimer +end + +subgraph arch/riscv/kvm/vcpu_timer.c +expired[kvm_riscv_vcpu_hrtimer_expired]-->stint +update[kvm_riscv_vcpu_update_hrtimer]-->ustint +pending[kvm_riscv_vcpu_timer_pending]-->hasint + +init[kvm_riscv_vcpu_timer_init]-->expired +init-->update + +init--> +vstimer_expired[kvm_riscv_vcpu_vstimer_expired] + +init--> +vstimecmp_update[kvm_riscv_vcpu_update_vstimecmp] +end + +subgraph arch/riscv/kvm/vcpu.c +vcpu_create[kvm_arch_vcpu_create]-->init + +vcpu_pending[kvm_cpu_has_pending_timer]-->pending +end + +subgraph virt/kvm/kvm_main.c +check[kvm_vcpu_check_block]-->vcpu_pending +end + +``` + +([下载由 Mermaid 生成的 PNG 图片][008]) + +### VS-Mode 外部中断 + +对应的,在 KVM 内部有关 VS-mode 的外部中断仅在 KVM 兼容模式时才开启,以外部中断的方式配置 Guest。 + +```mermaid +flowchart LR + +subgraph arch/riscv/include/asm/csr.h +ext[IRQ_VS_EXT] +end + +subgraph arch/riscv/kvm/vcpu.c + +async[kvm_arch_vcpu_async_ioctl]--> +int[kvm_riscv_vcpu_set/unset_interrupt]-->ext +end + +subgraph virt/kvm/kvm_main.c +compat[kvm_vcpu_compat_ioctl]--> +vcpu[kvm_vcpu_ioctl]-->async +end + +``` + +([下载由 Mermaid 生成的 PNG 图片][009]) + +```cpp +// arch/riscv/kvm/vcpu.c: line 569 + +long kvm_arch_vcpu_async_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + struct kvm_vcpu *vcpu = filp->private_data; + void __user *argp = (void __user *)arg; + + if (ioctl == KVM_INTERRUPT) { + struct kvm_interrupt irq; + + // 将用户态的由 argp 所指向的中断信息复制到 irq 中 + if (copy_from_user(&irq, argp, sizeof(irq))) + return -EFAULT; + + // 根据 irq 的中断操作类型,对指定的 vcpu 进行中断操作(set, unset) + if (irq.irq == KVM_INTERRUPT_SET) + return kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_EXT); + else + return kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT); + } + + return -ENOIOCTLCMD; +} + +``` + +## RISC-V 中断在 Linux 中的实现 + +### Timer 驱动 + +参考 [此文][3] 对 RISC-V 计时器在 Linux 内核中的实现的分析,Linux Timer 的实现包含两个驱动文件: + +- 无 MMU 的 `drivers/clocksource/timer-riscv.c`:运行于 M-mode 下,可直接读取 `mtime` CSR 获取当前时间、通过 `mtimecmp` CSR 设置中断,考虑到虚拟化对于特权级的需求,该实现并不会在虚拟化系统中被调用。 +- 有 MMU 的 `drivers/clocksource/timer-clint.c`:支持 S-mode (S/HS/VS) 下的时钟访问,但因为权限问题,需要借助于 CSR 读写指令达成。在不支持 SSTC 扩展的情况下,需要通过 SBI 写入 `mtimecmp` 实现计时器中断。 + +在添加了虚拟化扩展之后,VS-mode 的计时器中断操作需要通过 SBI 进入 HS-mode 再进入 M-mode,访问 `htimedelta`,`mtimecmp` 等 CSR,开销较大。后续有望通过添加 [SSTC 扩展][4] 实现对 `vstimecmp` 的直接访问进而简化虚拟情况下的中断开销。 + +### 中断驱动与 PLIC 控制器 + +[这篇文章][5] 基于一个 RTC(Real Time Clock)例程分析了 RISC-V 中断的申请、产生、处理流程。 + +Linux 内核中涉及 RISC-V 中断相关的处理机制如下图所示,从左到右依次为 PLIC、INTC(INTerrupt Controller)和内核中断处理。 + +```mermaid +flowchart + +e[arch/riscv/kernel/entry.S]-->ghai + +subgraph kernel/irq/handle.c +ghai[generic_handle_arch_irq] +shi[set_handle_irq] +end + +subgraph kernel/softirq.c +ghai-->ie[irq_exit] +ghai-.->so[others] +end + +subgraph other +end + +ghai-.->other + +subgraph drivers/irqchip/irq-riscv-intc.c +direction +ii[IRQCHIP_DECLARE:riscv_intc_init]-->shi--> +rii[riscv_intc_irq] +idm[[intc_domain]] +end + +subgraph kernel/irq/irqdesc.c +ghdi[generic_handle_domain_irq] +end + +subgraph include/linux/irqdomain.h +al[irq_domain_add_linear] +end + +ii-->al-.return..->idm + +rii-->ghdi +idm-.arg..->ghdi + +subgraph drivers/irqchip/irq-sifive-plic.c +direction TB +epid[IRQCHIP_DECLARE: plic_edge_init]-->pei[plic_edge_init]-->pi +pid[IRQCHIP_DECLARE: plic_init]--> +tpi[__plic_init]-->pi[plic_init]-->phi[plic_handle_irq] +end +``` + +([下载由 Mermaid 生成的 PNG 图片][010]) + +### 小结 + +结合本节和上一节中有关 Linux 以及 KVM 对 RISC-V 中断的分析可知,KVM 内实现了将虚拟机内部 VS-mode 的中断与外部中断处理控制器的绑定,同时实现了特定于 VS-mode 的中断处理功能,从而完成了对于 RISC-V 虚拟化的支持。 + +## MMIO 虚拟化 + +### KVM + +通过用户态程序(如 kvmtool)创建了 vCPU 之后,vcpu 内部就包含了 MMIO 相关的项,如下图所示。如此,便实现了虚拟机 MMIO 的管理。所以 Guest 的 MMIO 操作都是基于下图所示的数据结构实现的。 + +```mermaid +flowchart BT + +subgraph v[kvm_vcpu] +subgraph va[kvm_vcpu_arch] +md[kvm_mmio_decode] +vao[other arch states, ...] +end + +subgraph r[kvm_run] +m[mmio] +ro[other run states, ...] +end +end +``` + +([下载由 Mermaid 生成的 PNG 图片][011]) + +mmio 在 Host 一端的注册与销毁如下图所示: + +```mermaid +flowchart LR +subgraph kvm_main.c +cv[kvm_create_vm] +cd[kvm_destroy_vm] +... +end + +subgraph coalseced_mmio.c +mi[kvm_coalesced_mmio_init] +mf[kvm_coalesced_mmio_free] +..., +end + +cv-->mi +cv-->mf +cd-->mf +``` + +([下载由 Mermaid 生成的 PNG 图片][012]) + +KVM 中的 MMIO 的访存操作有如下三个对应处理函数: + +```cpp +// arch/riscv/include/asm/kvm_vcpu_insn.h: line 40 +int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long fault_addr, + unsigned long htinst); +int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long fault_addr, + unsigned long htinst); +int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run); + +``` + +下图展示了 MMIO 访存操作的具体实现,可以发现 LAOD/STORE 操作最终是通过调用 IO 设备中注册好的读写函数来实现的: + +```mermaid +flowchart LR +subgraph vi[arch/riscv/kvm/vcpu_insn.c] +l[kvm_riscv_vcpu_mmio_load]-->r +s[kvm_riscv_vcpu_mmio_store]-->r +r[kvm_riscv_vcpu_mmio_return] +end + +subgraph m[virt/kvm/kvm_main.c] +rd[kvm_io_bus_read] +wr[kvm_io_bus_write] +end + +l-->rd +s-->wr + +subgraph dv[include/kvm/iodev.h] +subgraph iodev +subgraph ops +frd[*read] +fwr[*write] +end +end +end + +rd-.->frd +wr-.->fwr +``` + +([下载由 Mermaid 生成的 PNG 图片][013]) + +## kvmtool 中断注入及 MMIO 创建 + +在 kvmtool 中 MMIO 是作为 VIRTIO 设备之一连带着中断处理函数一起被注册的。整个过程可以分为两个部分: + +- PLIC,设备树初始化 +- MMIO/PCI 等设备与 PLIC 以及中断处理函数的绑定 +- Console/Net 等设备与初始化时与 MMIO/PCI 设备的绑定 + +执行完整个 Console 的创建过程就完成了 Guest 的 PLIC、IRQ 与设备的绑定,即实现了虚拟机的中断注入机制与 MMIO 创建。 + +下图中左上的 `virtio_dev_init:virtio_console__init` 表示以 KVM 指定的方式初始化设备完成绑定。 + +右边 RISC-V 模块左下方的 `late_init:setup_fdt` 则表示包含有 PLIC 的设备树的初始化。 + +```mermaid +flowchart LR + +subgraph riscv +subgraph irq.c +il[kvm__irq_line] +it[kvm__irq_trigger] +end + +subgraph plic.c +pit[plic__irq_trig] +pnd[pci__generate_fdt_nodes] +end +il-->pit +it-->pit +subgraph fdt.c +li[late_init:setup_fdt] +end +li-->pnd +end + +subgraph virtio + +subgraph unified_devices +subgraph console.c +cdi[virtio_dev_init:virtio_console__init] +end +subgraph net.c +bdi[virtio_dev_init:virtio_net__init] +end +udo[other unified_devices, ...] +end + +cdi-->vi +bdi-->vi +udo-.->vi + +subgraph pci.c +pvq[virtio_pci__signal_vq]-->it +pvq-->il +pcfg[virtio_pci__signal_config]-->it +po[other functions, ...] +end + +subgraph mmio.c +vq[virtio_mmio_signal_vq]-->it +cfg[virtio_mmio_signal_config]-->it +mo[other functions, ...] +end + +pm[pci-modern.c]-->il +pl[pci-legacy.c]-->il + +subgraph core.c +vi[virtio_init: case VIRTIO_*] +cm[mmio] +cp[pci] +end + +end + +cm-.->mmio.c +cp-.->pci.c + +subgraph hw +i8[i8042.c]-->il +sr[serial.c]-->il +end + +``` + +([下载由 Mermaid 生成的 PNG 图片][014]) + +## 总结 + +RISC-V 中断通过 PLIC,CLINT 等驱动和控制器来实现,KVM 模块对于虚拟化的支持体现在两方面,一方面是 KVM 实现了与 Guest 外部的中断控制相关联的 VS-mode 的中断处理,另一方面则是通过为用户态程序如 kvmtool 提供接口,支持了虚拟机内部的设备与中断处理函数的注册与绑定,也实现了虚拟机与内核态的绑定,这使得 Guest 的 MMIO 访存等操作顺利进行。 + +## 参考资料 + +- [Linux Kernel][1] +- [RISC-V 异常处理在 KVM 中的实现][2] +- [RISC-V timer 在 Linux 中的实现][3] +- [RISC-V SSTC Extension][4] +- [RISC-V 中断子系统分析——PLIC 中断处理][5] +- [kvmtool][6] + +[1]: https://www.kernel.org/ +[2]: 20221021-riscv-kvm-excp-impl.md +[3]: https://tinylab.org/riscv-timer/#kvm-vcpu_timerc +[4]: https://github.com/riscv/riscv-time-compare/releases/download/v0.5.4/Sstc.pdf +[5]: https://gitee.com/tinylab/riscv-linux/blob/master/articles/20220919-riscv-irq-analysis-part2-interrupt-handling-plic.md +[6]: https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git +[007]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-1.png +[008]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-2.png +[009]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-3.png +[010]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-4.png +[011]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-5.png +[012]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-6.png +[013]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-7.png +[014]: images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-8.png diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-1.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2e27df96ff1bbabb100dfec8535d6081b7afa2f0 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-1.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-2.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-2.png new file mode 100644 index 0000000000000000000000000000000000000000..280f1bca55ba9be8d3e240211a7f37e7a787ce58 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-2.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-3.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-3.png new file mode 100644 index 0000000000000000000000000000000000000000..fce9ae7a7c51c99040ee1ec28d588bed4e479c37 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-3.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-4.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-4.png new file mode 100644 index 0000000000000000000000000000000000000000..e7e6c2bb75878e982dbdf87fc9b2c6b24409de19 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-4.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-5.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-5.png new file mode 100644 index 0000000000000000000000000000000000000000..49ab6b415af437cb345cd57172e12af136bba345 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-5.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-6.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-6.png new file mode 100644 index 0000000000000000000000000000000000000000..cdcf0edef2f5886919370577eb59d0fcc5f74873 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-6.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-7.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-7.png new file mode 100644 index 0000000000000000000000000000000000000000..cefe6c3adb5081999510f474ecdb617ff9cb9cef Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-7.png differ diff --git a/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-8.png b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-8.png new file mode 100644 index 0000000000000000000000000000000000000000..2c0783a76b813250fe540f9916c279dd9c51e0c6 Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_2/mermaid-riscv-kvm-int-impl-2-8.png differ