diff --git a/Cargo.lock b/Cargo.lock index b3bad04861f8736ac25ca6480f3ec4615b7dbf27..667dfb1349e074f2c0a3da3bb876af153fa405c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,12 @@ dependencies = [ "cc", ] +[[package]] +name = "fdt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" + [[package]] name = "fdt-rs" version = "0.4.3" @@ -576,6 +582,12 @@ dependencies = [ "embedded-hal 1.0.0-rc.1", ] +[[package]] +name = "riscv-decode" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec7a6dc0b0bb96a4d23271864a45c0d24dcd9dde2a1b630a35f79fa29c588bf" + [[package]] name = "rust_shyper" version = "0.1.0" @@ -585,12 +597,14 @@ dependencies = [ "built", "cfg-if", "chrono", - "fdt", + "fdt 0.1.0", + "fdt 0.1.5", "fdt-rs", "gethostname", "log", "memoffset 0.8.0", "riscv 0.10.1 (git+https://github.com/Zera-Algorithm/riscv)", + "riscv-decode", "rustsbi", "sbi", "smccc", diff --git a/Cargo.toml b/Cargo.toml index a51cf70e0dbacf9ab9689198886da8af88a11a48..0be7fec2f757bcce23a457b2c017713983fdc2e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,8 @@ built = { version = "0.6.1", features = [] } gethostname = "0.4.3" [dependencies] +riscv-decode = "0.2.1" +fdt_print = { package = "fdt", version = "0.1.5", features =["pretty-printing"] } vm-fdt = { git = "https://github.com/OhmR/vm-fdt" } fdt = { git = "https://github.com/Zera-Algorithm/libfdt-rs" } log = { version = "0.4", features = [ @@ -69,6 +71,7 @@ version = "x" default-features = false [features] +default = ["aia", "sbi_legacy"] tx2 = ["ns16550"] qemu = ["pl011"] pi4 = ["pl011"] @@ -85,3 +88,5 @@ unilib = [] memrsv = [] doc = [] sbi_legacy = [] +plic = [] +aia = [] \ No newline at end of file diff --git a/Makefile b/Makefile index 246a9e3a69d0c92a50833496b2bcb27b593e785c..9ee2b0bb0523e0dcab8aa9b0c6d36a5d1f04abd7 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ ifeq ($(ARCH), aarch64) else ifeq ($(ARCH), riscv64) CROSS_COMPILE := riscv64-linux-gnu- TEXT_START := 0x80200000 - VM0_IMAGE_PATH := "./image/Image_5.15.100-riscv-starfive" + VM0_IMAGE_PATH := "./image/Image-6.10-rc1" else $(error bad arch: $(ARCH)) endif @@ -37,6 +37,15 @@ else ifneq ($(GIC_VERSION),2) $(error Bad gic version) endif +IRQ ?= plic +AIA_GUESTS ?= 3 + +ifeq ($(IRQ), plic) +FEATURES += plic +else ifeq ($(IRQ), aia) +FEATURES += aia +endif + TEXT_START ?= 0x83000000 VM0_IMAGE_PATH ?= "./image/L4T" @@ -54,8 +63,13 @@ QEMU_DISK_OPTIONS = -drive file=${DISK},if=none,format=raw,id=x0 -device virtio- MKIMAGE_ARCH = arm64 else ifeq (${ARCH}, riscv64) # -global virtio-mmio.force-legacy=false option for disable legacy,i.e. using the latest virtio version -QEMU_COMMON_OPTIONS = -machine virt\ - -m 8g -smp 4 -display none -bios default \ + +ifeq ($(IRQ), plic) +QEMU_COMMON_OPTIONS = -machine virt +else ifeq ($(IRQ), aia) +QEMU_COMMON_OPTIONS = -machine virt,aia=aplic-imsic,aia-guests=$(AIA_GUESTS) +endif +QEMU_COMMON_OPTIONS += -m 8g -smp 4 -display none -bios default \ -kernel ${TARGET_DIR}/${IMAGE}.bin QEMU_NETWORK_OPTIONS = -netdev user,id=n0,hostfwd=tcp::5555-:22 -device virtio-net-device,netdev=n0 QEMU_DISK_OPTIONS = -drive file=${DISK},if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 @@ -86,7 +100,7 @@ else ifeq ($(ARCH), riscv64) -ffreestanding endif -export BOARD CROSS_COMPILE CFLAGS +export BOARD CROSS_COMPILE CFLAGS AIA_GUESTS = $(AIA_GUESTS) CARGO_ACTION ?= build diff --git a/cli/src/config.rs b/cli/src/config.rs index 87dff40e9879af35994154ab32e70765d8ff463c..0ccc7e0a69004e764f425cd8f0816b2b31d5e143 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -78,6 +78,7 @@ pub enum EmuDeviceType { EMU_DEVICE_T_GICR, EMU_DEVICE_T_META, EMU_DEVICE_T_PLIC, + EMU_DEVICE_T_APLIC, } #[allow(non_camel_case_types)] @@ -88,6 +89,7 @@ pub enum DtbDeviceType { DTB_DEVICE_T_GICC, DTB_DEVICE_T_GICR, DTB_DEVICE_T_PLIC, + DTB_DEVICE_T_APLIC, } // parse hex string to u64, like: "0x8000" -> 32768 diff --git a/doc/RISC-V64_User_Guide.md b/doc/RISC-V64_User_Guide.md index b4f09691ed6a144c15ee1a34083dfa17ebde94b5..91d0ca17bbbbb532cb6810d1a5bac5c30768b68d 100644 --- a/doc/RISC-V64_User_Guide.md +++ b/doc/RISC-V64_User_Guide.md @@ -8,9 +8,11 @@ * Rust * riscv64-linux-gnu-gcc 交叉编译工具链 + ```shell sudo apt install gcc-riscv64-linux-gnu ``` + * cargo-binutils(可选) * qemu-system-riscv64 8.2.0 及以上版本(低版本不支持riscv的虚拟化扩展) @@ -26,7 +28,7 @@ MVM是用于对其他虚拟机进行管理的管理VM,运行Linux,可以通 本项目使用的Linux rootfs是以Ubuntu base image为基础,通过在riscv64平台Linux(如Qemu + riscv64裸Linux)上chroot进入镜像经过一系列安装配置来构建的。如果对这些操作不熟悉的话,可以使用现成的镜像: -* https://ubuntu.com/download/risc-vhttps://bhpan.buaa.edu.cn/link/AAF36D01FF739449A19B7D28CC5639F555 +* * 文件名:vm0_ubuntu_gvm.img * 有效期限:2040-01-01 20:11 * 提取码:2Axz @@ -183,7 +185,7 @@ GVM的启动需要依赖内核模块(`tools/shyper_riscv64.ko`)和CLI程序 在RISC-V64环境下,编译CLI程序,得到 `shyper` 可执行文件。 -> 可参考 https://ubuntu.com/download/risc-v 配置一个只运行Linux的RISC-V虚拟机,然后在这个环境下编译RISC-V的依赖项。 +> 可参考 配置一个只运行Linux的RISC-V虚拟机,然后在这个环境下编译RISC-V的依赖项。 **Step 1**: 安装内核模块 @@ -321,7 +323,7 @@ chmod +x shyper "dtb_device" : { "num": 0, "dtb_device_list": [ - ] + ] } } ``` @@ -367,3 +369,110 @@ MVM绑定的网卡地址为10.0.0.1(eth0,作为GVM的网关)和10.0.2.15(eth1, MVM可以通过10.0.0.0/24网段与GVM通信,并通过10.0.2.2网关与外网通信。具体的网络拓扑图如下图所示: ![alt text](img/network-topo.png) + +## 通过AIA启动Rust-Shyper + +> 本章节主要说明通过AIA启动的方法以及有关的配置(与PLIC不同) + +* 软件依赖:升级qemu版本至9.0.2版本 +* 通过AIA启动的现成rootfs镜像: + * 链接: + * 提取码:skst +* 使用AIA运行Rust-Shyper的运行demo: + +### Rust-Shyper的启动 + +使用如下命令编译使用AIA的RISC-V版本的Rust-Shyper: + +```shell +ARCH=riscv64 IRQ=aia AIA_GUESTS=3 make +``` + +使用如下命令运行: + +```shell +ARCH=riscv64 IRQ=aia AIA_GUESTS=3 make run +``` + +参数解析: + +* IRQ==[plic|aia]:选择中断的方式,当没有输入该参数时,默认是plic + +* AIA_GUESTS=nnn:需要为每个HART模拟的interrupt file的数量,也是将要运行VM的数量,当没有输入该参数时,默认是3 + +### 启动GVM时的配置 + +本目录下(`./vm1_config_riscv_aia.json`)提供了配置文件的示例,与PLIC不同的配置是需要将模拟中断的设备替换为APLIC: + +```json + { + "name": "aplic@d000000", + "base_ipa": "0xd000000", + "length": "0x8000", + "cfg_num": 2, + "cfg_list": [ + 0, + 0 + ], + "type": "EMU_DEVICE_T_APLIC" + }, +``` + +### VM多核启动时,关于IMSIC的地址映射 + +由于在多核启动时,会向不同的hart中写IPI中断号,但由于GuestOS不认为自己处于虚拟化模式下,会访问每个hart的S-mode的interrupt file,所以需要建立地址映射,以便可以找到正确的guest interrupt file。 + +若当前hart的分配情况为: + +* MVM:hart0 +* GVM:hart1、2、3 +* GVM:hart2、3 + +由于MVM是单核启动,所以无需建立地址映射。 + +GVM1应建立以下映射: + +* 由于使用的是hart1、2、3,其正确的guest interrupt file地址应为0x28006000、0x2800a000、0x2800e000,但VM会认为自己的所用的是hart0、1、2,他会访问的地址是0x28000000、0x28004000、0x28008000,所以应建立以下映射关系,VM才会正确访问到对应的interrupt file + +```json + "passthrough_device": { + "passthrough_device_list": [ + { + "base_ipa": "0x28000000", + "base_pa": "0x28006000", + "length": "0x1000" + }, + { + "base_ipa": "0x28004000", + "base_pa": "0x2800a000", + "length": "0x1000" + }, + { + "base_ipa": "0x28008000", + "base_pa": "0x2800e000", + "length": "0x1000" + } + ] + }, +``` + +GVM2: + + ```json + "passthrough_device": { + "passthrough_device_list": [ + { + "base_ipa": "0x28000000", + "base_pa": "0x2800b000", + "length": "0x1000" + }, + { + "base_ipa": "0x28004000", + "base_pa": "0x2800f000", + "length": "0x1000" + }, + ] + }, + ``` + +> 注:以上映射地址会随aia-guests的值变化而变化 diff --git a/image/Image-6.10-rc1 b/image/Image-6.10-rc1 new file mode 100755 index 0000000000000000000000000000000000000000..6cafb3121e995e6939950946af32f1447f776677 Binary files /dev/null and b/image/Image-6.10-rc1 differ diff --git a/src/arch/riscv64/aplic.rs b/src/arch/riscv64/aplic.rs new file mode 100644 index 0000000000000000000000000000000000000000..d8b480130c1353982576e10d501966ceaf7d404e --- /dev/null +++ b/src/arch/riscv64/aplic.rs @@ -0,0 +1,338 @@ +use core::ptr::{read_volatile, write_volatile}; + +// S-mode interrupt delivery controller +// const APLIC_S_IDC: usize = 0xd00_4000; +// domaincfg +pub const APLIC_DOMAINCFG_BASE: usize = 0x0000; +pub const APLIC_DOMAINCFG_TOP: usize = 0x0003; +// sourcecfg +pub const APLIC_SOURCECFG_BASE: usize = 0x0004; +pub const APLIC_SOURCECFG_TOP: usize = 0x0FFF; +//smsiaddrcfg +pub const APLIC_S_MSIADDR_BASE: usize = 0x1BC8; +pub const APLIC_S_MSIADDR_TOP: usize = 0x1BCF; +// setip +pub const APLIC_SET_PENDING_BASE: usize = 0x1C00; +pub const APLIC_SET_PENDING_TOP: usize = 0x1C7F; +// setipnum +pub const APLIC_SET_PENDING_NUM_BASE: usize = 0x1CDC; +pub const APLIC_SET_PENDING_NUM_TOP: usize = 0x1CDF; +// inclrip +pub const APLIC_CLR_PENDING_BASE: usize = 0x1D00; +pub const APLIC_CLR_PENDING_TOP: usize = 0x1D7F; +// clripnum +pub const APLIC_CLR_PENDING_NUM_BASE: usize = 0x1DDC; +pub const APLIC_CLR_PENDING_NUM_TOP: usize = 0x1DDF; +// setie +pub const APLIC_SET_ENABLE_BASE: usize = 0x1E00; +pub const APLIC_SET_ENABLE_TOP: usize = 0x1E7F; +// setienum +pub const APLIC_SET_ENABLE_NUM_BASE: usize = 0x1EDC; +pub const APLIC_SET_ENABLE_NUM_TOP: usize = 0x1EDF; +// clrie +pub const APLIC_CLR_ENABLE_BASE: usize = 0x1F00; +pub const APLIC_CLR_ENABLE_TOP: usize = 0x1F7F; +// clrienum +pub const APLIC_CLR_ENABLE_NUM_BASE: usize = 0x1FDC; +pub const APLIC_CLR_ENABLE_NUM_TOP: usize = 0x1FDF; +// setipnum_le +pub const APLIC_SET_IPNUM_LE_BASE: usize = 0x2000; +pub const APLIC_SET_IPNUM_LE_TOP: usize = 0x2003; +// setipnum_be +pub const APLIC_SET_IPNUM_BE_BASE: usize = 0x2004; +pub const APLIC_SET_IPNUM_BE_TOP: usize = 0x2007; +// genmsi +pub const APLIC_GENMSI_BASE: usize = 0x3000; +pub const APLIC_GENMSI_TOP: usize = 0x3003; +// target +pub const APLIC_TARGET_BASE: usize = 0x3004; +pub const APLIC_TARGET_TOP: usize = 0x3FFF; +// IDC +pub const APLIC_IDC_BASE: usize = 0x4000; + +#[repr(u32)] +#[allow(dead_code)] +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum SourceModes { + Inactive = 0, + Detached = 1, + RisingEdge = 4, + FallingEdge = 5, + LevelHigh = 6, + LevelLow = 7, +} + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum APLICMode { + Machine, + Supervisor, +} + +// offset size register name +// 0x0000 4 bytes domaincfg +// ---------------------------------- +// 0x0004 4 bytes sourcecfg[1] +// 0x0008 4 bytes sourcecfg[2] +// . . . . . . +// 0x0FFC 4 bytes sourcecfg[1023] +// ---------------------------------- +// 0x1BC0 4 bytes mmsiaddrcfg (machine-level interrupt domains only) +// 0x1BC4 4 bytes mmsiaddrcfgh ” +// 0x1BC8 4 bytes smsiaddrcfg ” +// 0x1BCC 4 bytes smsiaddrcfgh ” +// ---------------------------------- +// 0x1C00 4 bytes setip[0] +// 0x1C04 4 bytes setip[1] +// . . . . . . +// 0x1C7C 4 bytes setip[31] +// ---------------------------------- +// 0x1CDC 4 bytes setipnum +// ---------------------------------- +// 0x1D00 4 bytes in clrip[0] +// 0x1D04 4 bytes in clrip[1] +// . . . . . . +// 0x1D7C 4 bytes in clrip[31] +// ---------------------------------- +// 0x1DDC 4 bytes clripnum +// ---------------------------------- +// 0x1E00 4 bytes setie[0] +// 0x1E04 4 bytes setie[1] +// . . . . . . +// 0x1E7C 4 bytes setie[31] +// ---------------------------------- +// 0x1EDC 4 bytes setienum +// ---------------------------------- +// 0x1F00 4 bytes clrie[0] +// 0x1F04 4 bytes clrie[1] +// . . . . . . +// 0x1F7C 4 bytes clrie[31] +// ---------------------------------- +// 0x1FDC 4 bytes clrienum +// ---------------------------------- +// 0x2000 4 bytes setipnum le +// 0x2004 4 bytes setipnum be +// ---------------------------------- +// 0x3000 4 bytes genmsi +// ---------------------------------- +// 0x3004 4 bytes target[1] +// 0x3008 4 bytes target[2] +// . . . . . . +// 0x3FFC 4 bytes target[1023] +// ---------------------------------- + +pub struct APLIC { + base: usize, + size: usize, +} + +pub trait APLICTrait { + fn set_domaincfg(&self, bigendian: bool, msimode: bool, enabled: bool); + fn get_domaincfg(&self) -> u32; + fn get_msimode(&self) -> bool; + fn set_sourcecfg(&self, irq: u32, mode: SourceModes); + fn set_sourcecfg_delegate(&self, irq: u32, child: u32); + fn get_sourcecfg(&self, irq: u32) -> u32; + fn set_msiaddr(&self, address: usize); + fn get_pending(&self, irqidx: usize) -> u32; + fn set_pending(&self, irqidx: usize, value: u32, pending: bool); + fn set_pending_num(&self, value: u32); + fn get_in_clrip(&self, irqidx: usize) -> u32; + fn set_in_clrip(&self, irqidx: usize, value: u32); + fn get_enable(&self, irqidx: usize) -> u32; + fn get_clr_enable(&self, irqidx: usize) -> u32; + fn set_enable(&self, irqidx: usize, value: u32, enabled: bool); + fn set_enable_num(&self, value: u32); + fn clr_enable_num(&self, value: u32); + fn setipnum_le(&self, value: u32); + fn set_target_msi(&self, irq: u32, hart: u32, guest: u32, eiid: u32); + fn set_target_direct(&self, irq: u32, hart: u32, prio: u32); + fn get_target(&self, irq: usize) -> u32; +} + +#[allow(dead_code)] +impl APLIC { + pub const fn new(base: usize, size: usize) -> APLIC { + APLIC { base, size } + } + + fn get_base_addr(&self) -> usize { + self.base + } +} +/// Interrupt Delivery Control is only used in 'direct' mode +#[repr(C)] +struct InterruptDeliveryControl { + pub idelivery: u32, + pub iforce: u32, + pub ithreshold: u32, + pub topi: u32, + pub claimi: u32, +} + +impl APLICTrait for APLIC { + fn set_domaincfg(&self, bigendian: bool, msimode: bool, enabled: bool) { + let enabled = u32::from(enabled); + let msimode = u32::from(msimode); + let bigendian = u32::from(bigendian); + let addr = self.base + APLIC_DOMAINCFG_BASE; + let src = (enabled << 8) | (msimode << 2) | bigendian; + unsafe { + write_volatile(addr as *mut u32, src); + } + } + + fn get_domaincfg(&self) -> u32 { + let addr = self.base + APLIC_DOMAINCFG_BASE; + unsafe { read_volatile(addr as *const u32) } + } + + fn get_msimode(&self) -> bool { + let addr = self.base + APLIC_DOMAINCFG_BASE; + let value = unsafe { read_volatile(addr as *const u32) }; + ((value >> 2) & 0b11) != 0 + } + + fn set_sourcecfg(&self, irq: u32, mode: SourceModes) { + assert!(irq > 0 && irq < 1024); + let addr = self.base + APLIC_SOURCECFG_BASE + (irq as usize - 1) * 4; + let src = mode as u32; + unsafe { + write_volatile(addr as *mut u32, src); + } + } + + fn set_sourcecfg_delegate(&self, irq: u32, child: u32) { + assert!(irq > 0 && irq < 1024); + let addr = self.base + APLIC_SOURCECFG_BASE + (irq as usize - 1) * 4; + let src = 1 << 10 | child & 0x3ff; + unsafe { + write_volatile(addr as *mut u32, src); + } + } + + fn get_sourcecfg(&self, irq: u32) -> u32 { + assert!(irq > 0 && irq < 1024); + let addr = self.base + APLIC_SOURCECFG_BASE + (irq as usize - 1) * 4; + unsafe { read_volatile(addr as *const u32) } + } + + fn set_msiaddr(&self, address: usize) { + let addr = self.base + APLIC_S_MSIADDR_BASE; + let src = (address >> 12) as u32; + unsafe { + write_volatile(addr as *mut u32, src); + write_volatile((addr + 4) as *mut u32, 0); + } + } + + fn get_pending(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_SET_PENDING_BASE + irqidx * 4; + unsafe { read_volatile(addr as *const u32) } + } + + fn set_pending(&self, irqidx: usize, value: u32, pending: bool) { + assert!(irqidx < 32); + let addr = self.base + APLIC_SET_PENDING_BASE + irqidx * 4; + let clr_addr = self.base + APLIC_CLR_PENDING_BASE + irqidx * 4; + if pending { + unsafe { + write_volatile(addr as *mut u32, value); + } + } else { + unsafe { + write_volatile(clr_addr as *mut u32, value); + } + } + } + + fn set_pending_num(&self, value: u32) { + let addr = self.base + APLIC_SET_PENDING_NUM_BASE; + unsafe { + write_volatile(addr as *mut u32, value); + } + } + + fn get_in_clrip(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_CLR_PENDING_BASE + irqidx * 4; + unsafe { read_volatile(addr as *const u32) } + } + + fn set_in_clrip(&self, irqidx: usize, value: u32) { + assert!(irqidx < 32); + let addr = self.base + APLIC_CLR_PENDING_BASE + irqidx * 4; + unsafe { + write_volatile(addr as *mut u32, value); + } + } + + fn get_enable(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_SET_ENABLE_BASE + irqidx * 4; + unsafe { read_volatile(addr as *const u32) } + } + + fn get_clr_enable(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_CLR_ENABLE_BASE + irqidx * 4; + unsafe { read_volatile(addr as *const u32) } + } + + fn set_enable(&self, irqidx: usize, value: u32, enabled: bool) { + assert!(irqidx < 32); + let addr = self.base + APLIC_SET_ENABLE_BASE + irqidx * 4; + let clr_addr = self.base + APLIC_CLR_ENABLE_BASE + irqidx * 4; + if enabled { + unsafe { + write_volatile(addr as *mut u32, value); + } + } else { + unsafe { + write_volatile(clr_addr as *mut u32, value); + } + } + } + + fn set_enable_num(&self, value: u32) { + let addr = self.base + APLIC_SET_ENABLE_NUM_BASE; + unsafe { + write_volatile(addr as *mut u32, value); + } + } + + fn clr_enable_num(&self, value: u32) { + let addr = self.base + APLIC_CLR_ENABLE_NUM_BASE; + unsafe { + write_volatile(addr as *mut u32, value); + } + } + + fn setipnum_le(&self, value: u32) { + let addr = self.base + APLIC_SET_IPNUM_LE_BASE; + unsafe { + write_volatile(addr as *mut u32, value); + } + } + + fn set_target_msi(&self, irq: u32, hart: u32, guest: u32, eiid: u32) { + let addr = self.base + APLIC_TARGET_BASE + (irq as usize - 1) * 4; + let src = (hart << 18) | (guest << 12) | eiid; + unsafe { + write_volatile(addr as *mut u32, src); + } + } + + fn set_target_direct(&self, irq: u32, hart: u32, prio: u32) { + let addr = self.base + APLIC_TARGET_BASE + (irq as usize - 1) * 4; + let src = (hart << 18) | (prio & 0xFF); + unsafe { + write_volatile(addr as *mut u32, src); + } + } + + fn get_target(&self, irq: usize) -> u32 { + let addr = self.base + APLIC_TARGET_BASE + (irq - 1) * 4; + unsafe { read_volatile(addr as *const u32) } + } +} diff --git a/src/arch/riscv64/context_frame.rs b/src/arch/riscv64/context_frame.rs index 790c410f6cceb85516d4ca8ecd71a1a784f23d1c..72ab5d1d089df4075ebb542e13339aee0f91729a 100644 --- a/src/arch/riscv64/context_frame.rs +++ b/src/arch/riscv64/context_frame.rs @@ -159,7 +159,6 @@ impl Default for VmContext { fn default() -> Self { let hstatus_mem: u64; csrr!(hstatus_mem, hstatus); - // Set **initial values** for each privilege level register // of the VM to prevent Linux startup errors Self { diff --git a/src/arch/riscv64/imsic.rs b/src/arch/riscv64/imsic.rs new file mode 100644 index 0000000000000000000000000000000000000000..4cd4aeb1a7a89ccca160ebf4c072e0940ee2cdfa --- /dev/null +++ b/src/arch/riscv64/imsic.rs @@ -0,0 +1,75 @@ +use crate::alloc::string::ToString; +use crate::{csrr, csrw}; +/* AIA Extension */ +pub const CSR_VSISELECT: usize = 0x250; +pub const CSR_VSIREG: usize = 0x251; +pub const CSR_VSTOPI: usize = 0xEB0; +pub const CSR_VSTOPEI: usize = 0x25C; + +pub const IMSIC_VS: usize = 0x2800_0000; +const IMSIC_VS_HART_STRIDE: usize = 0x4000; +const IMSIC_MMIO_PAGE_SIZE: usize = 0x1000; + +const XLEN: usize = usize::BITS as usize; +const XLEN_STRIDE: usize = XLEN / 32; + +const EIP: usize = 0x80; + +pub const fn imsic_vs(hart: usize) -> usize { + IMSIC_VS + IMSIC_VS_HART_STRIDE * hart +} + +pub fn imsic_write(reg: usize, val: usize) { + #[allow(unused_unsafe)] + unsafe { + match reg { + CSR_VSISELECT => csrw!(0x250, val), + CSR_VSIREG => csrw!(0x251, val), + CSR_VSTOPI => csrw!(0xEB0, val), + CSR_VSTOPEI => csrw!(0x25C, val), + _ => panic!("Unknown CSR {}", reg), + } + } +} + +// Read from an IMSIC CSR +fn imsic_read(reg: usize) -> u64 { + let ret: u64; + #[allow(unused_unsafe)] + unsafe { + ret = match reg { + CSR_VSISELECT => csrr!(0x250), + CSR_VSIREG => csrr!(0x251), + CSR_VSTOPI => csrr!(0xEB0), + CSR_VSTOPEI => csrr!(0x25C), + _ => panic!("Unknown CSR {}", reg), + } + } + ret +} + +// Calculate how many bits are needed to represent all interrupt files. +fn imsic_num_bits(count: u32) -> u32 { + let mut ret = 0; + while (1 << ret) < count { + ret += 1; + } + ret +} + +pub fn imsic_trigger(hart: u32, guest: u32, eiid: u32) { + let mut aia_guests: u32 = 3; + // get the value of aia-guests from environment variables + let arg = env!("AIA_GUESTS").to_string(); + match arg.parse::() { + Ok(num) => aia_guests = num, + Err(e) => warn!("Failed to parse: {}", e), + } + let guest_bits = imsic_num_bits(aia_guests + 1); + let hart_stride = (1 << guest_bits) * IMSIC_MMIO_PAGE_SIZE as u32; + let addr_base = IMSIC_VS as u32 + hart * hart_stride; + let addr = addr_base + 0x1000 * guest; + unsafe { + core::ptr::write_volatile(addr as *mut u32, eiid); + } +} diff --git a/src/arch/riscv64/interrupt.rs b/src/arch/riscv64/interrupt.rs index 3272f9ab9276431ef69d4c2733935b295bf52e57..b4f960e809c4a0526af2f75e9dd06eee2409c1a4 100644 --- a/src/arch/riscv64/interrupt.rs +++ b/src/arch/riscv64/interrupt.rs @@ -1,8 +1,14 @@ use sbi::HartMask; use crate::arch::psci_vcpu_on; use crate::arch::InterruptController; +#[cfg(feature = "plic")] use crate::arch::PLIC; +#[cfg(feature = "plic")] use crate::arch::PLICTrait; + +#[cfg(feature = "aia")] +use crate::arch::APLIC; + use crate::kernel::vm; use crate::kernel::IpiInnerMsg; use crate::kernel::IpiMessage; @@ -21,13 +27,19 @@ pub const IRQ_HYPERVISOR_TIMER: usize = 61; // for clint, not plic pub const IRQ_GUEST_TIMER: usize = 62; // not valid pub const PLIC_BASE_ADDR: u64 = 0x0C00_0000; +pub const APLIC_BASE_ADDR: u64 = 0x0D00_0000; +pub const APLIC_SIZE: u64 = 0x8000; const UART0_IRQ: usize = 10; const VIRTIO_IRQ: usize = 1; pub struct IntCtrl; +//test @CHonghao +#[cfg(all(feature = "plic", target_arch = "riscv64"))] pub static GLOBAL_PLIC: Mutex = Mutex::new(PLIC::new(PLIC_BASE_ADDR as usize)); +#[cfg(all(feature = "aia", target_arch = "riscv64"))] +pub static GLOBAL_APLIC: Mutex = Mutex::new(APLIC::new(APLIC_BASE_ADDR as usize, APLIC_SIZE as usize)); // True PLIC impl InterruptController for IntCtrl { @@ -46,11 +58,17 @@ impl InterruptController for IntCtrl { crate::utils::barrier(); // Set interrupt threshold for current cpu - let locked = GLOBAL_PLIC.lock(); - locked.set_threshold(crate::arch::PLICMode::Machine, current_cpu().id, 0); - - // SAFETY: Enable external interrupt - unsafe { riscv::register::sie::set_sext() }; + #[cfg(feature = "plic")] + { + let locked = GLOBAL_PLIC.lock(); + locked.set_threshold(crate::arch::PLICMode::Machine, current_cpu().id, 0); + // SAFETY: Enable external interrupt + unsafe { riscv::register::sie::set_sext() }; + } + #[cfg(feature = "aia")] + { + info!("The platform does not need to init AIA here"); + } } fn enable(int_id: usize, en: bool) { @@ -68,9 +86,11 @@ impl InterruptController for IntCtrl { panic!("enable intr {} not supported", int_id); } } else { + #[cfg(feature = "plic")] GLOBAL_PLIC .lock() .set_enable(int_id, crate::arch::PLICMode::Machine, current_cpu().id); + trace!("set_enable"); } } else if int_id >= CLINT_IRQ_BASE { if int_id == IRQ_HYPERVISOR_TIMER { @@ -83,9 +103,11 @@ impl InterruptController for IntCtrl { panic!("enable intr {} not supported", int_id); } } else { + #[cfg(feature = "plic")] GLOBAL_PLIC .lock() .clear_enable(int_id, crate::arch::PLICMode::Machine, current_cpu().id); + trace!("clear_enable"); } } @@ -97,22 +119,28 @@ impl InterruptController for IntCtrl { fn clear() { // loop until no pending intr loop { - let irq = GLOBAL_PLIC.lock().get_claim(super::PLICMode::Machine, current_cpu().id); - if irq == 0 { - // TODO: not clearing sip,maybe no need? - break; - } else { - GLOBAL_PLIC - .lock() - .set_complete(super::PLICMode::Machine, current_cpu().id, irq); + #[cfg(feature = "plic")] + { + let irq = GLOBAL_PLIC.lock().get_claim(super::PLICMode::Machine, current_cpu().id); + if irq == 0 { + // TODO: not clearing sip,maybe no need? + break; + } else { + GLOBAL_PLIC + .lock() + .set_complete(super::PLICMode::Machine, current_cpu().id, irq); + } } + trace!("clear"); } } fn finish(int_id: usize) { + #[cfg(feature = "plic")] GLOBAL_PLIC .lock() .set_complete(super::PLICMode::Machine, current_cpu().id, int_id); + trace!("finish int_id {:?}", int_id); } #[allow(unused_variables)] @@ -123,12 +151,18 @@ impl InterruptController for IntCtrl { fn vm_inject(vm: &crate::kernel::Vm, vcpu: &crate::kernel::Vcpu, int_id: usize) { // Inject interrupt through virtual plic + #[cfg(feature = "plic")] let vplic = vm.vplic(); + #[cfg(feature = "aia")] + let vaplic = vm.vaplic(); if let Some(cur_vcpu) = current_cpu().active_vcpu.clone() { if cur_vcpu.vm_id() == vcpu.vm_id() { // Note: trigger a timer intr, external intr, or soft intr // if external intr, inject to vplic + #[cfg(feature = "plic")] vplic.inject_intr(int_id); + #[cfg(feature = "aia")] + vaplic.inject_intr(int_id); return; } } @@ -179,14 +213,22 @@ pub fn riscv_get_pending_irqs(int_cause: usize) -> Option { CAUSE_INTR_TIMER => Some(IRQ_HYPERVISOR_TIMER), CAUSE_INTR_EXTERNAL => { // Get intr from PLIC - let irq = GLOBAL_PLIC.lock().get_claim(super::PLICMode::Machine, current_cpu().id); - if irq == 0 { + #[cfg(feature = "plic")] + { + let irq = GLOBAL_PLIC.lock().get_claim(super::PLICMode::Machine, current_cpu().id); + if irq == 0 { + None + } else { + GLOBAL_PLIC + .lock() + .set_complete(super::PLICMode::Machine, current_cpu().id, irq); + Some(irq) + } + } + #[cfg(feature = "aia")] + { + trace!("set_complete"); None - } else { - GLOBAL_PLIC - .lock() - .set_complete(super::PLICMode::Machine, current_cpu().id, irq); - Some(irq) } } _ => { diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index e5c0b8c6d02a52686e8e55a8c601d955826ac20d..efba95a7ba9979ad617e13a5ccafda815cd9e535 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -6,6 +6,12 @@ pub mod interface; pub mod interrupt; mod page_fault; mod page_table; +// test @CHonghao +#[cfg(feature = "aia")] +mod aplic; +#[cfg(feature = "aia")] +mod imsic; +#[cfg(feature = "plic")] mod plic; pub mod power; pub mod regs; @@ -17,8 +23,11 @@ mod smmu; mod start; pub mod timer; pub mod tlb; +#[cfg(feature = "aia")] +mod vaplic; mod vcpu; mod vm; +#[cfg(feature = "plic")] mod vplic; use alloc::sync::Arc; @@ -26,7 +35,12 @@ pub use cache::*; pub use interface::*; pub use interrupt::*; pub use context_frame::*; +#[cfg(feature = "plic")] pub use plic::*; +#[cfg(feature = "aia")] +pub use aplic::*; +#[cfg(feature = "aia")] +pub use imsic::*; pub use regs::*; #[cfg(not(feature = "sbi_legacy"))] pub use sbicall::*; @@ -35,7 +49,10 @@ pub use sbicall_legacy::*; pub use start::*; pub use timer::*; pub use tlb::*; +#[cfg(feature = "plic")] pub use vplic::*; +#[cfg(feature = "aia")] +pub use vaplic::*; pub use page_table::*; pub use power::*; pub use cpu::*; diff --git a/src/arch/riscv64/power.rs b/src/arch/riscv64/power.rs index f2cab8e673cc344743e229500840fd3b35c7f352..33c3225636b1d9290fd801d01ccbc2ff952e3b33 100644 --- a/src/arch/riscv64/power.rs +++ b/src/arch/riscv64/power.rs @@ -1,6 +1,6 @@ use crate::{ arch::get_trapframe_for_hart, - kernel::{current_cpu, CpuState, Scheduler, Vcpu, Vm}, + kernel::{current_cpu, vcpu_set_vgein, CpuState, Scheduler, Vcpu, Vm}, }; use riscv::register::hstatus; use sbi::{ @@ -81,6 +81,7 @@ pub fn psci_vcpu_on(vcpu: &Vcpu, entry: usize, ctx: usize) { current_cpu().ctx_mut().unwrap().sscratch = get_trapframe_for_hart(current_cpu().id); } + vcpu_set_vgein(); info!( "(Secondary vcpu start) vcpu on cpu {} begins running: \nhstatus: {:#x}, entry: {:#x}, use ctx:\n{}", current_cpu().id, diff --git a/src/arch/riscv64/sbicall_legacy.rs b/src/arch/riscv64/sbicall_legacy.rs index 5f84f364ea901464d6aa1b4eb8878c1a881d2436..1ba3fbd24cec557941ab55b9f75703abaa1c2afc 100644 --- a/src/arch/riscv64/sbicall_legacy.rs +++ b/src/arch/riscv64/sbicall_legacy.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; /// For some SBI operations, Hypervisor emulation is required instead of /// directly invoking the M-state SBI software. use rustsbi::{ - init_hsm, init_ipi, init_pmu, init_remote_fence, init_reset, init_timer, + init_hsm, init_ipi, /*init_pmu,*/ init_remote_fence, init_reset, init_timer, spec::{ base::{impl_id::KVM, EID_BASE, GET_MARCHID, GET_MIMPID, GET_MVENDORID}, binary::SbiRet, @@ -355,7 +355,7 @@ pub fn init_ecall_handler() { init_remote_fence(&RFNC); init_hsm(&HSM); init_reset(&SRST); - init_pmu(&PMU); + //init_pmu(&PMU); init_legacy_stdio(&STDIO); } diff --git a/src/arch/riscv64/vaplic.rs b/src/arch/riscv64/vaplic.rs new file mode 100644 index 0000000000000000000000000000000000000000..01efc45908d5ea07db3cc2d2d5ac778cf6878d3e --- /dev/null +++ b/src/arch/riscv64/vaplic.rs @@ -0,0 +1,556 @@ +use crate::config::VmEmulatedDeviceConfig; +use crate::kernel::{active_vm, current_cpu, Vcpu}; +use crate::device::{EmuContext, EmuDev}; +use alloc::sync::Arc; +use spin::Mutex; +use crate::arch::SourceModes; +use crate::arch::riscv64::aplic::APLICTrait; +use crate::arch::riscv64::imsic_trigger; + +use super::{GLOBAL_APLIC, IRQ_GUEST_TIMER, IRQ_IPI}; +use super::aplic::{ + APLIC_DOMAINCFG_BASE, APLIC_DOMAINCFG_TOP, APLIC_SOURCECFG_BASE, APLIC_SOURCECFG_TOP, APLIC_S_MSIADDR_BASE, + APLIC_S_MSIADDR_TOP, APLIC_SET_PENDING_BASE, APLIC_SET_PENDING_TOP, APLIC_SET_PENDING_NUM_BASE, + APLIC_SET_PENDING_NUM_TOP, APLIC_CLR_PENDING_BASE, APLIC_CLR_PENDING_TOP, APLIC_CLR_PENDING_NUM_BASE, + APLIC_CLR_PENDING_NUM_TOP, APLIC_SET_ENABLE_BASE, APLIC_SET_ENABLE_TOP, APLIC_SET_ENABLE_NUM_BASE, + APLIC_SET_ENABLE_NUM_TOP, APLIC_CLR_ENABLE_BASE, APLIC_CLR_ENABLE_TOP, APLIC_CLR_ENABLE_NUM_BASE, + APLIC_CLR_ENABLE_NUM_TOP, APLIC_SET_IPNUM_LE_BASE, APLIC_SET_IPNUM_LE_TOP, APLIC_SET_IPNUM_BE_BASE, + APLIC_SET_IPNUM_BE_TOP, APLIC_GENMSI_BASE, APLIC_GENMSI_TOP, APLIC_TARGET_BASE, APLIC_TARGET_TOP, +}; +use riscv::register::sie; + +const VPLIC_LENGTH: usize = 0x8000; +pub struct VAPlic { + // Note:Here you need to take full advantage of the internal variability, + // so that all functions used by VAPlic do not have mut permissions, so you need to add Mutex to inner + inner: Arc>, +} + +/// Note: Define the structure of the vAPLIC, which includes some information about the VM in addition to the APLIC +pub struct VAPlicInner { + emulated_base: usize, + emulated_size: usize, + domaincfg: u32, + sourcecfg: [u32; 1023], + mmsiaddrcfg: u32, + mmsiaddrcfgh: u32, + smsiaddrcfg: u32, + smsiaddrcfgh: u32, + setip: [u32; 32], + setipnum: u32, + in_clrip: [u32; 32], + clripnum: u32, + setie: [u32; 32], + setienum: u32, + clrie: [u32; 32], + clrienum: u32, + setipnum_le: u32, + setipnum_be: u32, + genmsi: u32, + target: [u32; 1023], +} + +impl VAPlic { + pub fn new(emulated_base: usize) -> VAPlic { + let inner = VAPlicInner { + emulated_base, + emulated_size: VPLIC_LENGTH, + domaincfg: 0, + sourcecfg: [0; 1023], + mmsiaddrcfg: 0, + mmsiaddrcfgh: 0, + smsiaddrcfg: 0, + smsiaddrcfgh: 0, + setip: [0; 32], + setipnum: 0, + in_clrip: [0; 32], + clripnum: 0, + setie: [0; 32], + setienum: 0, + clrie: [0; 32], + clrienum: 0, + setipnum_le: 0, + setipnum_be: 0, + genmsi: 0, + target: [0; 1023], + }; + VAPlic { + inner: Arc::new(Mutex::new(inner)), + } + } + + pub fn get_emulated_base_addr(&self) -> usize { + let inner = self.inner.lock(); + inner.emulated_base + } +} + +impl APLICTrait for VAPlic { + /// # Overview + /// Set the `domaincfg` register. + /// ## Arguments + /// * `bigendian` `true`: the APLIC uses big endian byte order, `false`: the APLIC uses little endian byte order. + /// * `msimode` `true`: the APLIC will send MSIs for interrupts, `false`: the APLIC will only trigger actual wires. + /// * `enabled` `true`: this APLIC is enabled and can receive/send interrupts, `false`: the APLIC domain is disabled. + fn set_domaincfg(&self, bigendian: bool, msimode: bool, enabled: bool) { + // Rust library assures that converting a bool into u32 will use + // 1 for true and 0 for false + GLOBAL_APLIC.lock().set_domaincfg(bigendian, msimode, enabled); + let mut inner = self.inner.lock(); + let enabled = u32::from(enabled); + let msimode = u32::from(msimode); + let bigendian = u32::from(bigendian); + inner.domaincfg = (enabled << 8) | (msimode << 2) | bigendian; + } + + fn get_domaincfg(&self) -> u32 { + // When starting GVM and reading domaincfg, the value is 0, errors may occur. + // let inner = self.inner.lock(); + // let domaincfg = inner.domaincfg; + // Need to re-acquire the domaincfg value from the aplic's register + let domaincfg = GLOBAL_APLIC.lock().get_domaincfg(); + domaincfg + } + + fn get_msimode(&self) -> bool { + // let inner: spin::MutexGuard = self.inner.lock(); + // let domaincfg = inner.domaincfg; + let domaincfg = GLOBAL_APLIC.lock().get_domaincfg(); + ((domaincfg >> 2) & 0b1) != 0 + } + + /// # Overview + /// Setup a source configuration to a particular mode. + /// This does NOT delegate the source to a child. + /// ## Arguments + /// * `irq` the interrupt number to set + /// * `mode` the source mode--how the interrupt is triggered. + fn set_sourcecfg(&self, irq: u32, mode: SourceModes) { + assert!(irq > 0 && irq < 1024); + let vm = active_vm().unwrap(); + if vm.has_interrupt(irq.try_into().unwrap()) { + GLOBAL_APLIC.lock().set_sourcecfg(irq, mode) + } + let mut inner = self.inner.lock(); + inner.sourcecfg[irq as usize - 1] = mode as u32; + } + + /// # Overview + /// Setup a source configuration to delegate an IRQ to a child. + /// ## Arguments + /// * `irq` the interrupt number to delegate + /// * `child` the child to delegate this interrupt to + fn set_sourcecfg_delegate(&self, irq: u32, child: u32) { + assert!(irq > 0 && irq < 1024); + let vm = active_vm().unwrap(); + if vm.has_interrupt(irq.try_into().unwrap()) { + GLOBAL_APLIC.lock().set_sourcecfg_delegate(irq, child) + } + let mut inner = self.inner.lock(); + inner.sourcecfg[irq as usize - 1] = 1 << 10 | child & 0x3ff; + } + + fn get_sourcecfg(&self, irq: u32) -> u32 { + assert!(irq > 0 && irq < 1024); + let inner = self.inner.lock(); + inner.sourcecfg[irq as usize - 1] + } + + /// # Overview + /// Set the MSI target physical address. This only accepts the lower + /// 32-bits of an address. + /// ## Arguments + /// * `mode` the MSI mode (machine or supervisor) + /// * `addr` the physical address for messages. This MUST be page aligned. + fn set_msiaddr(&self, addr: usize) { + let mut inner = self.inner.lock(); + GLOBAL_APLIC.lock().set_msiaddr(addr); + inner.smsiaddrcfg = (addr >> 12) as u32; + inner.smsiaddrcfgh = 0; + } + + fn get_pending(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let inner = self.inner.lock(); + inner.setip[irqidx] + } + + /// # Overview + /// Set the irq pending bit to the given state + /// ## Arguments + /// * `irq` the interrupt number + /// * `pending` true: set the bit to 1, false: clear the bit to 0 + fn set_pending(&self, irqidx: usize, value: u32, pending: bool) { + assert!(irqidx < 32); + GLOBAL_APLIC.lock().set_pending(irqidx, value, pending); + let mut inner = self.inner.lock(); + if pending { + // self.setipnum = irq; + inner.setip[irqidx] = value; + } else { + // self.clripnum = irq; + inner.in_clrip[irqidx] = value; + } + } + + fn set_pending_num(&self, value: u32) { + GLOBAL_APLIC.lock().set_pending_num(value); + let mut inner = self.inner.lock(); + inner.setipnum = value; + } + + fn get_in_clrip(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let inner = self.inner.lock(); + inner.in_clrip[irqidx] + } + + fn set_in_clrip(&self, irqidx: usize, value: u32) { + assert!(irqidx < 32); + GLOBAL_APLIC.lock().set_in_clrip(irqidx, value); + let mut inner = self.inner.lock(); + inner.in_clrip[irqidx] = value; + } + + fn get_enable(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let inner = self.inner.lock(); + inner.setie[irqidx] + } + + fn get_clr_enable(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let inner = self.inner.lock(); + inner.clrie[irqidx] + } + + /// # Overview + /// Set the irq enabled bit to given state + /// ## Arguments + /// * `irq` the interrupt number + /// * `enabled` true: enable interrupt, false: disable interrupt + fn set_enable(&self, irqidx: usize, value: u32, enabled: bool) { + assert!(irqidx < 32); + GLOBAL_APLIC.lock().set_enable(irqidx, value, enabled); + let mut inner = self.inner.lock(); + if enabled { + inner.setie[irqidx] = value; + } else { + inner.clrie[irqidx] = value; + } + } + + fn set_enable_num(&self, value: u32) { + GLOBAL_APLIC.lock().set_enable_num(value); + let mut inner = self.inner.lock(); + inner.setienum = value; + } + + fn clr_enable_num(&self, value: u32) { + GLOBAL_APLIC.lock().clr_enable_num(value); + let mut inner = self.inner.lock(); + inner.clrienum = value; + } + + fn setipnum_le(&self, value: u32) { + GLOBAL_APLIC.lock().setipnum_le(value); + let mut inner = self.inner.lock(); + inner.setipnum_le = value; + } + + /// # Overview + /// Set the target interrupt to a given hart, guest, and identifier + /// ## Arguments + /// * `irq` - the interrupt to set + /// * `hart` - the hart that will receive interrupts from this irq + /// * `guest` - the guest identifier to send these interrupts + /// * `eiid` - the identification number of the irq (usually the same as the irq itself) + fn set_target_msi(&self, irq: u32, hart: u32, guest: u32, eiid: u32) { + assert!(irq > 0 && irq < 1024); + let vm = active_vm().unwrap(); + if vm.has_interrupt(irq.try_into().unwrap()) { + GLOBAL_APLIC.lock().set_target_msi(irq, hart, guest, eiid); + } + let mut inner = self.inner.lock(); + inner.target[irq as usize - 1] = (hart << 18) | (guest << 12) | eiid; + } + + /// # Overview + /// Set the target interrupt to a given hart and priority + /// ## Arguments + /// * `irq` - the interrupt to set + /// * `hart` - the hart that will receive interrupts from this irq + /// * `prio` - the priority of this direct interrupt. + fn set_target_direct(&self, irq: u32, hart: u32, prio: u32) { + assert!(irq > 0 && irq < 1024); + let vm = active_vm().unwrap(); + if vm.has_interrupt(irq.try_into().unwrap()) { + GLOBAL_APLIC.lock().set_target_direct(irq, hart, prio); + } + let mut inner = self.inner.lock(); + inner.target[irq as usize - 1] = (hart << 18) | (prio & 0xFF); + } + + fn get_target(&self, irq: usize) -> u32 { + let inner = self.inner.lock(); + inner.target[irq - 1] + } +} + +impl VAPlic { + pub fn vm_read_register(&self, addr: usize) -> u32 { + // debug!("vm_read_register:0x{:#x}", addr); + let offset = addr - self.get_emulated_base_addr(); + // align to 4B + let offset = offset & !0x3; + if (APLIC_DOMAINCFG_BASE..=APLIC_DOMAINCFG_TOP).contains(&offset) { + // domaincfg + let value = self.get_domaincfg(); + debug!( + "APLIC read domaincfg addr@{:#x} value 0x{:x} bigendian {} msimode {} enabled {}", + addr, + value, + (value & 0b1) != 0, + ((value >> 2) & 0b1) != 0, + ((value >> 8) & 0x1) != 0, + ); + value + // When adapting to other versions of the Linux kernel, if you need to set other registers, you can uncomment the corresponding comments below for addition. + /* } else if (APLIC_SOURCECFG_BASE..=APLIC_SOURCECFG_TOP).contains(&offset) { + // sourcecfg + panic!("sourcecfg Unexpected addr {:#x}", addr); + } else if (APLIC_S_MSIADDR_BASE..=APLIC_S_MSIADDR_TOP).contains(&offset) { + // smsiaddrcfg + panic!("smsiaddrcfg Unexpected addr {:#x}", addr); + } else if (APLIC_SET_PENDING_BASE..=APLIC_SET_PENDING_TOP).contains(&offset) { + // setip + panic!("setip Unexpected addr {:#x}", addr); + } else if (APLIC_SET_PENDING_NUM_BASE..=APLIC_SET_PENDING_NUM_TOP).contains(&offset) { + // setipnum + panic!("setipnum Unexpected addr {:#x}", addr); */ + } else if (APLIC_CLR_PENDING_BASE..=APLIC_CLR_PENDING_TOP).contains(&offset) { + // inclrip + let irqidx = (offset - APLIC_CLR_PENDING_BASE) / 4; + let value = self.get_in_clrip(irqidx); + debug!("APLIC read in clrip addr@{:#x} irqidx {} value {}", addr, irqidx, value); + value + /* } else if (APLIC_CLR_PENDING_NUM_BASE..=APLIC_CLR_PENDING_NUM_TOP).contains(&offset) { + // clripnum + panic!("clripnum Unexpected addr {:#x}", addr); + } else if (APLIC_SET_ENABLE_BASE..=APLIC_SET_ENABLE_TOP).contains(&offset) { + // setie + panic!("setie Unexpected addr {:#x}", addr); + } else if (APLIC_SET_ENABLE_NUM_BASE..=APLIC_SET_ENABLE_NUM_TOP).contains(&offset) { + // setienum + panic!("setienum Unexpected addr {:#x}", addr); + } else if (APLIC_CLR_ENABLE_BASE..=APLIC_CLR_ENABLE_TOP).contains(&offset) { + // clrie + panic!("clrie Unexpected addr {:#x}", addr); + } else if (APLIC_CLR_ENABLE_NUM_BASE..=APLIC_CLR_ENABLE_NUM_TOP).contains(&offset) { + // clrienum + panic!("clrienum Unexpected addr {:#x}", addr); + } else if (APLIC_SET_IPNUM_LE_BASE..=APLIC_SET_IPNUM_LE_TOP).contains(&offset) { + // setipnum_le + panic!("clrienum Unexpected addr {:#x}", addr); + } else if (APLIC_SET_IPNUM_BE_BASE..=APLIC_SET_IPNUM_BE_TOP).contains(&offset) { + // setipnum_be + panic!("clrienum Unexpected addr {:#x}", addr); + } else if (APLIC_GENMSI_BASE..=APLIC_GENMSI_TOP).contains(&offset) { + // genmsi + panic!("genmsi Unexpected addr {:#x}", addr); + } else if (APLIC_TARGET_BASE..=APLIC_TARGET_TOP).contains(&offset) { + // target + panic!("target Unexpected addr {:#x}", addr); */ + } else { + panic!("invalid plic register access: 0x{:x} offset: 0x{:x}", addr, offset); + } + } + + pub fn vm_write_register(&self, addr: usize, value: u32) { + // debug!("vm_write_register:0x{:#x}", addr); + let offset = addr - self.get_emulated_base_addr(); + // align to 4B + let offset = offset & !0x3; + if (APLIC_DOMAINCFG_BASE..=APLIC_DOMAINCFG_TOP).contains(&offset) { + // domaincfg + let enabled = ((value >> 8) & 0x1) != 0; // IE + let msimode = ((value >> 2) & 0b1) != 0; // DM / MSI + let bigendian = (value & 0b1) != 0; // Endianness + let vm = active_vm().unwrap(); + if vm.id() == 0 { + self.set_domaincfg(bigendian, msimode, enabled); + debug!( + "APLIC set domaincfg write addr@{:#x} bigendian {} msimode {} enabled {}", + addr, bigendian, msimode, enabled + ); + } + } else if (APLIC_SOURCECFG_BASE..=APLIC_SOURCECFG_TOP).contains(&offset) { + // sourcecfg + let irq = ((offset - APLIC_SOURCECFG_BASE) / 4) + 1; + if (value >> 10) & 0b1 == 1 { + // delegate + let child = value & 0x3ff; + self.set_sourcecfg_delegate(irq as u32, child); + debug!( + "APLIC set sourcecfg_delegate write addr@{:#x} irq {} child {}", + addr, irq, child + ); + } else { + let mode = match value { + 0 => SourceModes::Inactive, + 1 => SourceModes::Detached, + 4 => SourceModes::RisingEdge, + 5 => SourceModes::FallingEdge, + 6 => SourceModes::LevelHigh, + 7 => SourceModes::LevelLow, + _ => panic!("Unknown sourcecfg mode"), + }; + let vm = active_vm().unwrap(); + if vm.has_interrupt(irq) { + self.set_sourcecfg(irq as u32, mode); + debug!("APLIC set sourcecfg write addr@{:#x} irq {} mode {}", addr, irq, value); + } + } + } else if (APLIC_S_MSIADDR_BASE..=APLIC_S_MSIADDR_TOP).contains(&offset) { + // smsiaddrcfg + let address = (value as usize) << 12; + self.set_msiaddr(address); + debug!("APLIC set msiaddr write addr@{:#x} address {}", addr, address); + } else if (APLIC_SET_PENDING_BASE..=APLIC_SET_PENDING_TOP).contains(&offset) { + // setip + panic!("setip Unexpected addr {:#x}", addr); + } else if (APLIC_SET_PENDING_NUM_BASE..=APLIC_SET_PENDING_NUM_TOP).contains(&offset) { + // setipnum + panic!("setipnum Unexpected addr {:#x}", addr); + } else if (APLIC_CLR_PENDING_BASE..=APLIC_CLR_PENDING_TOP).contains(&offset) { + // inclrip + panic!("clrip Unexpected addr {:#x}", addr); + } else if (APLIC_CLR_PENDING_NUM_BASE..=APLIC_CLR_PENDING_NUM_TOP).contains(&offset) { + // clripnum + panic!("clripnum Unexpected addr {:#x}", addr); + } else if (APLIC_SET_ENABLE_BASE..=APLIC_SET_ENABLE_TOP).contains(&offset) { + // setie + panic!("setie Unexpected addr {:#x}", addr); + } else if (APLIC_SET_ENABLE_NUM_BASE..=APLIC_SET_ENABLE_NUM_TOP).contains(&offset) { + // setienum + self.set_enable_num(value); + debug!("APLIC set enablenum write addr@{:#x} value {}", addr, value); + } else if (APLIC_CLR_ENABLE_BASE..=APLIC_CLR_ENABLE_TOP).contains(&offset) { + // clrie + let irqidx = (offset - APLIC_CLR_ENABLE_BASE) / 4; + let vm = active_vm().unwrap(); + if vm.id() == 0 { + self.set_enable(irqidx, value, false); + debug!( + "APLIC set clr_enable write addr@{:#x} irqidx {} value {}", + addr, irqidx, value + ); + } + } else if (APLIC_CLR_ENABLE_NUM_BASE..=APLIC_CLR_ENABLE_NUM_TOP).contains(&offset) { + // clrienum + self.clr_enable_num(value); + debug!("APLIC set clrienum write addr@{:#x} value{}", offset, value); + } else if (APLIC_SET_IPNUM_LE_BASE..=APLIC_SET_IPNUM_LE_TOP).contains(&offset) { + // setipnum_le + self.setipnum_le(value); + // debug!("APLIC setipnum le write addr@{:#x} value@{:#x}",addr, value); + } else if (APLIC_SET_IPNUM_BE_BASE..=APLIC_SET_IPNUM_BE_TOP).contains(&offset) { + // setipnum_be + warn!("setipnum_be Unexpected addr {:#x}, {:?}", addr, value); + } else if (APLIC_GENMSI_BASE..=APLIC_GENMSI_TOP).contains(&offset) { + // genmsi + panic!("genmsi Unexpected addr {:#x}", addr); + } else if (APLIC_TARGET_BASE..=APLIC_TARGET_TOP).contains(&offset) { + // target + let irq = ((offset - APLIC_TARGET_BASE) / 4) as u32 + 1; + // An error may occur when the CPU ID of a virtual machine is not 0, but it assumes its CPU ID is 0. + let hart = (value >> 18) & 0x3F; + // Retrieve the actual transmitted HART + let vm = active_vm().unwrap(); + let vm_id = vm.id() as u32; + let mut pcpu: usize = 0; + if let Some(vcpu) = vm.vcpu(hart.try_into().unwrap()) { + pcpu = vcpu.phys_id(); + } + // let hart = ((value >> 18) & 0x3F) + (current_cpu.first_cpu) as u32; + if self.get_msimode() { + let guest = ((value >> 12) & 0x3F) + vm_id + 1; + let eiid = value & 0xFFF; + if vm.has_interrupt(irq as usize) { + self.set_target_msi(irq, pcpu.try_into().unwrap(), guest, eiid); + debug!( + "APLIC set msi target write addr@{:#x} irq {} hart {} guest {} eiid {}", + addr, irq, pcpu, guest, eiid + ); + } + } else { + let prio = value & 0xFF; + if vm_id == 0 { + self.set_target_direct(irq, pcpu.try_into().unwrap(), prio); + debug!( + "APLIC set direct target write addr@{:#x} irq {} hart {} prio {}", + addr, irq, pcpu, prio + ); + } + } + } else { + panic!("invalid plic register access: 0x{:x} offset: 0x{:x}", addr, offset); + } + } + pub fn inject_intr(&self, irq: usize) { + if irq == IRQ_GUEST_TIMER { + riscv::register::hvip::trigger_timing_interrupt(); + // SAFETY: Disable timer interrupt + unsafe { sie::clear_stimer() }; + } else if irq == IRQ_IPI { + riscv::register::hvip::trigger_software_interrupt(); + } else { + // Using IMSIC for Interrupt Injection + let target = self.get_target(irq); + let hart = (target >> 18) & 0x3F; + let guest = (target >> 12) & 0x3F; + let eiid = target & 0xFFF; + imsic_trigger(hart, guest, eiid); + } + } +} + +#[allow(unused_variables)] +pub fn emu_intc_init(emu_cfg: &VmEmulatedDeviceConfig, vcpu_list: &[Vcpu]) -> Result, ()> { + // Create and initialize the virtual interrupt controller: + // Create a new VPLIC object and call vm.set_emu_devs to add the emulated device to the Vm + let vaplic = VAPlic::new(emu_cfg.base_ipa); + Ok(Arc::new(vaplic)) +} + +impl EmuDev for VAPlic { + fn emu_type(&self) -> crate::device::EmuDeviceType { + crate::device::EmuDeviceType::EmuDeviceTAPlic + } + + fn address_range(&self) -> core::ops::Range { + let base_addr = self.get_emulated_base_addr(); + base_addr..base_addr + VPLIC_LENGTH + } + + // Emulated aplic's entry + fn handler(&self, emu_ctx: &EmuContext) -> bool { + let vm = active_vm().unwrap(); + let vaplic = vm.vaplic(); + let reg_idx = emu_ctx.reg; + + if emu_ctx.width != 4 { + panic!("emu_plic_mmio_handler: invalid width {}", emu_ctx.width); + } + + // TODO: Research whether there is a requirement to implement other bit widths (other than 32bit) + if emu_ctx.write { + vaplic.vm_write_register(emu_ctx.address, current_cpu().get_gpr(reg_idx) as u32); + } else { + let val = vaplic.vm_read_register(emu_ctx.address); + current_cpu().set_gpr(reg_idx, val as usize); + } + + true + } +} diff --git a/src/config/config.rs b/src/config/config.rs index 5f432847950e2207a902b811480d706629d974d1..01c9dabdfb40835f427a9744284cc7f928bff289 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -40,6 +40,7 @@ pub enum DtbDevType { DevGicc = 2, DevGicr = 3, DevPlic = 4, + DevAPlic = 5, } /// Convert a `usize` value to a `DtbDevType`. @@ -51,6 +52,7 @@ impl From for DtbDevType { 2 => DtbDevType::DevGicc, 3 => DtbDevType::DevGicr, 4 => DtbDevType::DevPlic, + 5 => DtbDevType::DevAPlic, _ => panic!("Unknown DtbDevType value: {}", value), } } diff --git a/src/config/qemu_riscv64_def.rs b/src/config/qemu_riscv64_def.rs index c893b6c96eecd91812453fbb2842c0917ecbbcae..36fc29aad029d0c2c5db804d2d8bd53b9c72d8fb 100644 --- a/src/config/qemu_riscv64_def.rs +++ b/src/config/qemu_riscv64_def.rs @@ -27,32 +27,51 @@ pub fn mvm_config_init() { // vm0 emu let emu_dev_config = vec![ - // Defines the start address and length of the PLIC device - VmEmulatedDeviceConfig { - name: String::from("plic"), - base_ipa: 0xc000000, - length: 0x600000, - irq_id: 0, - cfg_list: Vec::new(), - emu_type: EmuDeviceType::EmuDeviceTPlic, - mediated: false, + #[cfg(feature = "plic")]{ + // Defines the start address and length of the PLIC device + VmEmulatedDeviceConfig { + name: String::from("plic"), + base_ipa: 0xc000000, + length: 0x600000, + irq_id: 0, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTPlic, + mediated: false, + } }, - // hvc2 + #[cfg(feature = "aia")]{ + // Defines the start address and length of the APLIC device + VmEmulatedDeviceConfig { + name: String::from("aplic"), + base_ipa: 0xd000000, + length: 0x8000, + irq_id: 0, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTAPlic, + mediated: false, + } + }, + // The reason for modifying the IRQs of virtio_net and virtio_console devices: + // When using the original IRQ numbers, virtual instruction exceptions were encountered. + // By printing the available range of interrupt numbers obtained within QEMU, + // it was found that the original IRQ numbers were not within this range. + // Therefore, the IRQs were modified (The available IRQ range is: 1-11; 16-18; 32-35; 64-95). + // hvc1 VmEmulatedDeviceConfig { name: String::from("virtio_console@40001000"), base_ipa: 0x4000_1000, length: 0x1000, - irq_id: 46, + irq_id: 66, cfg_list: vec![1, 0x40001000], emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, mediated: false, }, - // hvc1 + // hvc0 VmEmulatedDeviceConfig { name: String::from("virtio_console@40002000"), base_ipa: 0x4000_2000, length: 0x1000, - irq_id: 47, + irq_id: 67, cfg_list: vec![2, 0x4000_2000], // Address of the peer vm and the peer virtio-console emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, mediated: false, @@ -60,9 +79,9 @@ pub fn mvm_config_init() { // virtual eth0 VmEmulatedDeviceConfig { name: String::from("virtio_net@40003000"), - base_ipa: 0x40003000, + base_ipa: 0x4000_3000, length: 0x1000, - irq_id: 48, + irq_id: 68, cfg_list: vec![0x74, 0x56, 0xaa, 0x0f, 0x47, 0xd0], emu_type: EmuDeviceType::EmuDeviceTVirtioNet, mediated: false, diff --git a/src/device/device_tree.rs b/src/device/device_tree.rs index b54f6d23752b80843b16baa20ff171dd8151fceb..28857badbf2c6fe92a28deb3b79cc95c603145bd 100644 --- a/src/device/device_tree.rs +++ b/src/device/device_tree.rs @@ -8,8 +8,12 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(target_arch = "riscv64")] +use alloc::borrow::ToOwned; use alloc::vec::Vec; +use alloc::string::String; +use fdt::myctypes; use vm_fdt::{FdtWriter, FdtWriterResult}; use crate::config::{DtbDevType, VmDtbDevConfig}; @@ -19,12 +23,47 @@ use crate::error::Result; use crate::SYSTEM_FDT; use crate::vmm::CPIO_RAMDISK; +#[allow(unused_imports)] +use fdt_print::Fdt; +extern crate fdt_print; const PI4_DTB_ADDR: usize = 0xf0000000; pub unsafe fn fdt_total_size(dtb: *mut fdt::myctypes::c_void) -> usize { core::ptr::read_unaligned((dtb as usize + 4) as *const u32).to_be() as usize } +pub fn cstr_to_rust_string(s: *mut myctypes::c_char, len: i32) -> String { + let mut ret = String::new(); + let mut s: *mut myctypes::c_char = s; + for _i in 0..len { + let c = unsafe { *s } as char; + if c == 0 as char { + break; + } + ret.push(c); + unsafe { s = s.add(1) }; + } + ret +} + +// Remove ext from riscv isa, return the modified isa +// isa eg: rv64imafdch_zicbom_zicboz_zicntr_zicsr +// We assume: +// ext.len==1: must before first _ +// ext.len!=1: must exist in the form of "_{ext}" +pub fn remove_riscv_ext(isa: String, ext: String) -> String { + let res = isa; + if ext.len() == 1 { + if let Some(pos) = res.find('_') { + String::from(&res[0..4]) + &res[4..pos].replace(ext.as_str(), "") + &res[pos..] + } else { + String::from(&res[0..4]) + &res[4..].replace(ext.as_str(), "") + } + } else { + res.replace(&(String::from("_") + ext.as_str()), "") + } +} + /// Initializes the Device Tree Blob (DTB) for the primary VM (vm0). /// # Safety: /// Dtb is a valid pointer to a device tree blob @@ -178,16 +217,51 @@ pub unsafe fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) -> Result<()> { assert_eq!(fdt_remove_node(dtb, "/flash@20000000\0".as_ptr()), 0); // Delete the previous memory and edit it again later assert_eq!(fdt_remove_node(dtb, "/memory@80000000\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/reserved-memory\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/soc/pci@30000000\0".as_ptr()), 0); + // modify the isa given by fdt + let str_len: i32 = 0; + let pstr: *mut myctypes::c_char = fdt_get_property_string( + dtb, + "/cpus/cpu@0\0".as_ptr(), + "riscv,isa\0".as_ptr(), + &str_len as *const myctypes::c_int as *mut myctypes::c_int, + ); + assert_ne!(pstr as u64, 0); + let isa = cstr_to_rust_string(pstr, str_len); + info!("riscv isa = {}", isa); + let isa = remove_riscv_ext(isa, String::from("h")); + let isa = remove_riscv_ext(isa, String::from("zicbom")); + let isa = remove_riscv_ext(isa, String::from("zicboz")); + let isa = remove_riscv_ext(isa, String::from("sstc")); + info!("modified riscv isa = {}", isa); + let node = fdt_find_node(dtb, "/cpus/cpu@0\0".as_ptr()); + fdt_add_property_string( + dtb, + node, + "riscv,isa\0".as_ptr(), + (isa.as_str().to_owned() + "\0").as_ptr(), + ); + // Delete unused hart of MVM (MVM only owns hart 0) assert_eq!(fdt_remove_node(dtb, "/cpus/cpu@1\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/cpus/cpu@2\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/cpus/cpu@3\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/cpus/cpu-map\0".as_ptr()), 0); + // Delete unused nodes in the AIA Device Tree + #[cfg(feature = "aia")] + { + assert_eq!(fdt_remove_node(dtb, "/soc/aplic@c000000\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/soc/imsics@24000000\0".as_ptr()), 0); + } + let len = fdt_size(dtb) as usize; info!("fdt patched size {}", len); + // Print the device_tree after deleting nodes + // let host_fdt = unsafe { Fdt::from_ptr(dtb as *const u8) }.unwrap(); + // debug!("fdt: {:?}", host_fdt); let slice = core::slice::from_raw_parts(dtb as *const u8, len); SYSTEM_FDT.call_once(|| slice.to_vec()); } @@ -238,6 +312,10 @@ pub fn create_fdt_riscv64(config: &VmConfigEntry) -> FdtWriterResult> { fdt.property_string("compatible", "simple-bus")?; fdt.property_null("ranges")?; + #[cfg(feature = "plic")] + let interrupt_phandle = riscv_plic_phandle(ncpu); + #[cfg(feature = "aia")] + let interrupt_phandle = riscv_aplic_phandle(ncpu); for emu_cfg in config.emulated_device_list() { match emu_cfg.emu_type { EmuDeviceType::EmuDeviceTVirtioBlk @@ -249,7 +327,7 @@ pub fn create_fdt_riscv64(config: &VmConfigEntry) -> FdtWriterResult> { &emu_cfg.name, emu_cfg.irq_id, emu_cfg.base_ipa, - riscv_plic_phandle(ncpu), + interrupt_phandle, )?; } EmuDeviceType::EmuDeviceTShyper => { @@ -266,9 +344,16 @@ pub fn create_fdt_riscv64(config: &VmConfigEntry) -> FdtWriterResult> { info!("plic fdt node init {:x} for {}", emu_cfg.base_ipa, &emu_cfg.name); create_plic_node(&mut fdt, &emu_cfg.name, emu_cfg.base_ipa, emu_cfg.length, ncpu)?; } + EmuDeviceType::EmuDeviceTAPlic => { + info!("aplic fdt node init {:x} for {}", emu_cfg.base_ipa, &emu_cfg.name); + create_aplic_node(&mut fdt, &emu_cfg.name, emu_cfg.base_ipa, emu_cfg.length, ncpu)?; + } _ => {} } } + + #[cfg(feature = "aia")] + create_imsics_node(&mut fdt, "imsics@28000000", 0x28000000, 0x10000, ncpu)?; create_clint_node(&mut fdt, "clint@2000000", 0x2000000, 0x10000, ncpu)?; fdt.end_node(soc)?; @@ -303,6 +388,53 @@ fn create_plic_node(fdt: &mut FdtWriter, name: &str, address: usize, len: usize, Ok(()) } +fn create_aplic_node(fdt: &mut FdtWriter, name: &str, address: usize, len: usize, ncpu: u32) -> FdtWriterResult<()> { + let aplic = fdt.begin_node(name)?; + let aplic_phandle = riscv_aplic_phandle(ncpu); + let msi_parent = riscv_imsic_phandle(ncpu); + + fdt.property_u32("phandle", aplic_phandle)?; + fdt.property_u32("riscv,num-sources", 0x60)?; + fdt.property_array_u64("reg", &[address as u64, len as u64])?; + + fdt.property_u32("msi-parent", msi_parent)?; + fdt.property_null("interrupt-controller")?; + fdt.property_u32("#interrupt-cells", 0x2)?; + fdt.property_string("compatible", "riscv,aplic")?; + + fdt.end_node(aplic)?; + + Ok(()) +} + +fn create_imsics_node(fdt: &mut FdtWriter, name: &str, address: usize, len: usize, ncpu: u32) -> FdtWriterResult<()> { + let imsic = fdt.begin_node(name)?; + let imsic_phandle = riscv_imsic_phandle(ncpu); + // This value is related to the 'aia-guests' startup parameter + let aia_guests = 2; + + fdt.property_u32("phandle", imsic_phandle)?; + fdt.property_u32("riscv,guest-index-bits", aia_guests)?; + fdt.property_u32("riscv,num-ids", 0xff)?; + fdt.property_array_u64("reg", &[address as u64, len as u64])?; + + let mut interrupts: Vec = Vec::new(); + for i in 0..ncpu { + let cpu_phandle = riscv_cpu_intc_phandle(i, ncpu); + interrupts.push(cpu_phandle); + interrupts.push(0x09); + } + + fdt.property_array_u32("interrupts-extended", interrupts.as_slice())?; + fdt.property_null("msi-controller")?; + fdt.property_null("interrupt-controller")?; + fdt.property_u32("#interrupt-cells", 0x00)?; + fdt.property_string("compatible", "riscv,imsics")?; + fdt.end_node(imsic)?; + + Ok(()) +} + fn create_clint_node(fdt: &mut FdtWriter, name: &str, address: usize, len: usize, ncpu: u32) -> FdtWriterResult<()> { let clint = fdt.begin_node(name)?; @@ -501,6 +633,16 @@ pub fn riscv_plic_phandle(ncpu: u32) -> u32 { ncpu * 2 + 1 } +// acquire the aplic phandle id +pub fn riscv_aplic_phandle(ncpu: u32) -> u32 { + ncpu * 2 + 4 +} + +// acquire the imsic phandle id +pub fn riscv_imsic_phandle(ncpu: u32) -> u32 { + ncpu * 2 + 2 +} + /// Creates the CPU node in the Device Tree for the VM based on the provided configuration. fn create_cpu_node_riscv(fdt: &mut FdtWriter, config: &VmConfigEntry) -> FdtWriterResult<()> { let cpus = fdt.begin_node("cpus")?; @@ -519,9 +661,19 @@ fn create_cpu_node_riscv(fdt: &mut FdtWriter, config: &VmConfigEntry) -> FdtWrit fdt.property_string("status", "okay")?; fdt.property_string("compatible", "riscv")?; // keep qemu host's all extensions except H-extension - fdt.property_string("riscv,isa", "rv64imafdc_zicbom_zicboz_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_sstc_svadu")?; + + // delete sstc, zicboz + #[cfg(feature = "plic")] + fdt.property_string( + "riscv,isa", + "rv64imafdc_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_svadu", + )?; + #[cfg(feature = "aia")] + fdt.property_string( + "riscv,isa", + "rv64imafdc_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_smaia_ssaia_svadu", + )?; fdt.property_string("mmu-type", "riscv,sv57")?; - fdt.property_string("compatible", "riscv")?; let intc = fdt.begin_node("interrupt-controller")?; fdt.property_u32("#interrupt-cells", 0x01)?; @@ -627,7 +779,10 @@ fn create_virtio_node_riscv64( plic_phandle: u32, ) -> FdtWriterResult<()> { let virtio = fdt.begin_node(name)?; + #[cfg(feature = "plic")] fdt.property_array_u32("interrupts", &[irq as u32])?; + #[cfg(feature = "aia")] + fdt.property_array_u32("interrupts", &[irq as u32, 0x04])?; fdt.property_u32("interrupt-parent", plic_phandle)?; fdt.property_array_u64("reg", &[address as u64, 0x1000])?; fdt.property_string("compatible", "virtio,mmio")?; diff --git a/src/device/emu.rs b/src/device/emu.rs index 36cb0419884d804e98f5fa0e8f50c87c5fba6660..24123489fe7c64de38cda87d1811db267f8e1e1b 100644 --- a/src/device/emu.rs +++ b/src/device/emu.rs @@ -97,6 +97,7 @@ pub enum EmuDeviceType { EmuDeviceTGICR = 11, EmuDeviceTMeta = 12, EmuDeviceTPlic = 13, + EmuDeviceTAPlic = 14, } impl Display for EmuDeviceType { @@ -115,6 +116,7 @@ impl Display for EmuDeviceType { EmuDeviceType::EmuDeviceTGICR => write!(f, "interrupt controller gicr"), EmuDeviceType::EmuDeviceTMeta => write!(f, "meta device"), EmuDeviceType::EmuDeviceTPlic => write!(f, "platform level interrupt controller"), + EmuDeviceType::EmuDeviceTAPlic => write!(f, "Advanced Platform-Level Interrupt Controller"), } } } @@ -150,6 +152,7 @@ impl EmuDeviceType { 11 => EmuDeviceType::EmuDeviceTGICR, 12 => EmuDeviceType::EmuDeviceTMeta, 13 => EmuDeviceType::EmuDeviceTPlic, + 14 => EmuDeviceType::EmuDeviceTAPlic, _ => panic!("Unknown EmuDeviceType value: {}", value), } } diff --git a/src/kernel/hvc.rs b/src/kernel/hvc.rs index eba3f6b90a7b693ab7ed467e6c3d1b71cf747699..5127397370fc783ebc1d62ac3c4aac3a57f5cee8 100644 --- a/src/kernel/hvc.rs +++ b/src/kernel/hvc.rs @@ -131,8 +131,13 @@ pub const HVC_IRQ: usize = 32 + 0x20; pub const HVC_IRQ: usize = 32 + 0x10; #[cfg(all(feature = "qemu", target_arch = "aarch64"))] pub const HVC_IRQ: usize = 32 + 0x20; +// The reason for modifying the IRQs of virtio_net and virtio_console devices: +// When using the original IRQ numbers, virtual instruction exceptions were encountered. +// By printing the available range of interrupt numbers obtained within QEMU, +// it was found that the original IRQ numbers were not within this range. +// Therefore, the IRQs were modified (The available IRQ range is: 1-11; 16-18; 32-35; 64-95). #[cfg(all(feature = "qemu", target_arch = "riscv64"))] -pub const HVC_IRQ: usize = 51; +pub const HVC_IRQ: usize = 69; #[cfg(feature = "rk3588")] pub const HVC_IRQ: usize = 32 + 0x10; diff --git a/src/kernel/vcpu.rs b/src/kernel/vcpu.rs index df473319e2c73b840ea76b98caa569e2aaa04d0f..b9576348c407a86bc301a40553f2f50470701fb1 100644 --- a/src/kernel/vcpu.rs +++ b/src/kernel/vcpu.rs @@ -392,6 +392,7 @@ pub fn vcpu_run(announce: bool) -> ! { trace!("Prepare to enter context vm entry..."); let sepc = current_cpu().ctx_mut().unwrap().sepc; let sscratch = current_cpu().ctx_mut().unwrap().sscratch; + vcpu_set_vgein(); trace!( "sepc: {:#x}, sscratch: {:#x}, hgatp: {:#x}, hstatus: {:#x}, sstatus: {:#x}, sie: {:#x}", sepc, @@ -418,3 +419,11 @@ pub fn vcpu_run(announce: bool) -> ! { context_vm_entry(current_cpu().ctx as usize); } } + +#[cfg(target_arch = "riscv64")] +pub fn vcpu_set_vgein() { + let vm_id = active_vm_id(); + let vgein = (vm_id + 1) << 12; + let hstatusval = hstatus::read(); + hstatus::write(hstatusval & 0xFFFFC0FFF | vgein); +} diff --git a/src/kernel/vm.rs b/src/kernel/vm.rs index e7bf44116ad06d512c0d002efeb675795edc8aa0..ffb2a169a017a96731e6036b897e046760bee6fc 100644 --- a/src/kernel/vm.rs +++ b/src/kernel/vm.rs @@ -14,8 +14,10 @@ use spin::{Mutex, Once}; #[cfg(target_arch = "aarch64")] use crate::arch::Vgic; -#[cfg(target_arch = "riscv64")] +#[cfg(all(feature = "plic", target_arch = "riscv64"))] use crate::arch::VPlic; +#[cfg(all(feature = "aia", target_arch = "riscv64"))] +use crate::arch::VAPlic; use crate::arch::{PAGE_SIZE, emu_intc_init, PageTable}; use crate::config::VmConfigEntry; use crate::device::{EmuDev, emu_virtio_mmio_init}; @@ -183,8 +185,10 @@ struct VmInnerConst { int_bitmap: BitAlloc4K, #[cfg(target_arch = "aarch64")] arch_intc_dev: Option>, - #[cfg(target_arch = "riscv64")] + #[cfg(all(feature = "plic", target_arch = "riscv64"))] arch_intc_dev: Option>, + #[cfg(all(feature = "aia", target_arch = "riscv64"))] + arch_intc_dev: Option>, // Emul devs config emu_devs: Vec>, } @@ -237,7 +241,7 @@ impl VmInnerConst { vgic }) } - #[cfg(target_arch = "riscv64")] + #[cfg(all(feature = "plic", target_arch = "riscv64"))] EmuDeviceTPlic => { self.intc_type = IntCtrlType::Emulated; emu_intc_init(emu_cfg, &self.vcpu_list).map(|vplic| { @@ -245,6 +249,14 @@ impl VmInnerConst { vplic }) } + #[cfg(all(feature = "aia", target_arch = "riscv64"))] + EmuDeviceTAPlic => { + self.intc_type = IntCtrlType::Emulated; + emu_intc_init(emu_cfg, &self.vcpu_list).map(|vaplic| { + self.arch_intc_dev = vaplic.clone().into_any_arc().downcast::().ok(); + vaplic + }) + } #[cfg(feature = "gicv3")] EmuDeviceTGICR => { if let Some(vgic) = &self.arch_intc_dev { @@ -515,7 +527,7 @@ impl Vm { self.inner_const.arch_intc_dev.is_some() } - #[cfg(target_arch = "riscv64")] + #[cfg(all(feature = "plic", target_arch = "riscv64"))] pub fn vplic(&self) -> &VPlic { if let Some(vplic) = self.inner_const.arch_intc_dev.as_ref() { return vplic; @@ -523,11 +535,24 @@ impl Vm { panic!("vm{} cannot find vgic", self.id()); } - #[cfg(target_arch = "riscv64")] + #[cfg(all(feature = "plic", target_arch = "riscv64"))] pub fn has_vplic(&self) -> bool { self.inner_const.arch_intc_dev.is_some() } + #[cfg(all(feature = "aia", target_arch = "riscv64"))] + pub fn vaplic(&self) -> &VAPlic { + if let Some(vaplic) = self.inner_const.arch_intc_dev.as_ref() { + return vaplic; + } + panic!("vm{} cannot find vaplic", self.id()); + } + + #[cfg(all(feature = "aia", target_arch = "riscv64"))] + pub fn has_vaplic(&self) -> bool { + self.inner_const.arch_intc_dev.is_some() + } + pub fn ncpu(&self) -> usize { self.inner_const.config.cpu_allocated_bitmap() as usize } diff --git a/src/vmm/init.rs b/src/vmm/init.rs index 50576df64c59e3aa657d4ae119763adea4cc4e22..4387d43a763fce6f7ac7b6eb0067a20629629e8e 100644 --- a/src/vmm/init.rs +++ b/src/vmm/init.rs @@ -382,19 +382,42 @@ unsafe fn fdt_add_virtio_riscv64( panic!("fdt_create_node failed {}", node); } - let ret = fdt_add_property_u32(dtb, node, "interrupts\0".as_ptr(), irq_id); - if ret < 0 { - panic!("fdt_add_property_u32 failed {}", ret); + #[cfg(feature = "plic")] + { + let int_phandle_id = 9_u32; + let ret = fdt_add_property_u32(dtb, node, "interrupts\0".as_ptr(), irq_id); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } + + let ret = fdt_add_property_u32( + dtb, + node, + "interrupt-parent\0".as_ptr(), + int_phandle_id, // phandle id + ); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } } + #[cfg(feature = "aia")] + { + let int_phandle_id = 12_u32; + let mut interrupts = [irq_id, 0x04]; + let ret = fdt_add_property_u32_array(dtb, node, "interrupts\0".as_ptr(), interrupts.as_mut_ptr(), 2); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } - let ret = fdt_add_property_u32( - dtb, - node, - "interrupt-parent\0".as_ptr(), - 9_u32, // plic phandle id - ); - if ret < 0 { - panic!("fdt_add_property_u32 failed {}", ret); + let ret = fdt_add_property_u32( + dtb, + node, + "interrupt-parent\0".as_ptr(), + int_phandle_id, // phandle id + ); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } } let mut regs = [base_ipa, length]; @@ -423,9 +446,42 @@ unsafe fn fdt_add_vm_service_riscv64(dtb: *mut fdt::myctypes::c_void, irq_id: u3 panic!("fdt_add_property_string failed {}", ret); } - let ret = fdt_add_property_u32(dtb, node, "interrupts\0".as_ptr(), irq_id); - if ret < 0 { - panic!("fdt_add_property_u32 failed {}", ret); + #[cfg(feature = "plic")] + { + let int_phandle_id = 9_u32; + let ret = fdt_add_property_u32(dtb, node, "interrupts\0".as_ptr(), irq_id); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } + + let ret = fdt_add_property_u32( + dtb, + node, + "interrupt-parent\0".as_ptr(), + int_phandle_id, // phandle id + ); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } + } + #[cfg(feature = "aia")] + { + let int_phandle_id = 12_u32; + let mut interrupts = [irq_id, 0x04]; + let ret = fdt_add_property_u32_array(dtb, node, "interrupts\0".as_ptr(), interrupts.as_mut_ptr(), 2); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } + + let ret = fdt_add_property_u32( + dtb, + node, + "interrupt-parent\0".as_ptr(), + int_phandle_id, // phandle id + ); + if ret < 0 { + panic!("fdt_add_property_u32 failed {}", ret); + } } let mut regs = [base_ipa, length]; @@ -433,16 +489,6 @@ unsafe fn fdt_add_vm_service_riscv64(dtb: *mut fdt::myctypes::c_void, irq_id: u3 if ret < 0 { panic!("fdt_add_property_u64_array failed {}", ret); } - - let ret = fdt_add_property_u32( - dtb, - node, - "interrupt-parent\0".as_ptr(), - 9_u32, // plic phandle id - ); - if ret < 0 { - panic!("fdt_add_property_u32 failed {}", ret); - } } // Here is used to write vm0 edit fdt function, mainly used to add virtual fdt item @@ -545,6 +591,9 @@ pub unsafe fn vmm_setup_fdt(config: &VmConfigEntry, dtb: *mut fdt::myctypes::c_v } } debug!("after dtb size {}", fdt_size(dtb)); + // Print the device_tree after adding new nodes + let host_fdt = unsafe { fdt_print::Fdt::from_ptr(dtb as *const u8) }.unwrap(); + debug!("after add fdt: \n{:?}", host_fdt); } /* Setup VM Configuration before boot.