diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..b0c91346668bb9bc0a0864b543dc73c68360f812 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,2 @@ +symbol-file target/riscv64/release/rust_shyper +target remote localhost:1234 diff --git a/.gitignore b/.gitignore index 98a81ec7c995a15209ccc3db7ec9eabb3aec0d92..1de089a2ffd7278ca405b2c9fdc3116c59fe900c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ build/ /vm1* /image/Image* *.patch -target +*.bin +*.elf +*.bak diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c371970d7a092253315592d6eff64e008f43b0b5..276d514495a89ff8f04c22b91fd13082f94eb30f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,6 +34,8 @@ build: FEATURES: - "" - rk3588-noeth + - BOARD: qemu + ARCH: riscv64 script: - make clippy - make doc diff --git a/Cargo.lock b/Cargo.lock index e214f709208e8c2d54daae1ca85ab0e824d6698a..07415692b65a1c28d47f15f8c94fb395ac35ed91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,12 @@ dependencies = [ "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "2.4.1" @@ -161,6 +167,12 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "cstr_core" version = "0.2.6" @@ -183,6 +195,22 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2894bc2f0457b8ca3d6b8ab8aad64d9337583672494013457f86c5a9146c0e22" + [[package]] name = "endian-type-rs" version = "0.1.4" @@ -214,7 +242,7 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fdt" version = "0.1.0" -source = "git+https://github.com/karin0/libfdt-rs#4c8cfe54f37e80d2023f758ed976906cb9542248" +source = "git+https://github.com/Zera-Algorithm/libfdt-rs?rev=61ec00#61ec003305148d00903b137a846b2bf36139f880" dependencies = [ "bindgen", "cc", @@ -408,6 +436,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + [[package]] name = "nom" version = "7.1.3" @@ -513,6 +556,26 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "riscv" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa3145d2fae3778b1e31ec2e827b228bdc6abd9b74bb5705ba46dcb82069bc4f" +dependencies = [ + "bit_field", + "critical-section", + "embedded-hal 0.2.7", +] + +[[package]] +name = "riscv" +version = "0.10.1" +source = "git+https://github.com/Zera-Algorithm/riscv#e0e9ca8d3647de8a0c9ec7980337587c584a6052" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0-rc.1", +] + [[package]] name = "rust_shyper" version = "0.1.0" @@ -527,6 +590,9 @@ dependencies = [ "gethostname", "log", "memoffset 0.8.0", + "riscv 0.10.1 (git+https://github.com/Zera-Algorithm/riscv)", + "rustsbi", + "sbi", "smccc", "spin 0.9.8", "tock-registers", @@ -561,6 +627,31 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustsbi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c74c6df5cecd1ddc81f953d100cee1111acd981535526d3e89d2854015cf93b" +dependencies = [ + "riscv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sbi-spec", +] + +[[package]] +name = "sbi" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cb0870400aca7e4487e8ec1e93f9d4288da763cb1da2cedc5102e62b6522ad" + +[[package]] +name = "sbi-spec" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a535b1f16d5517a020539569f7e37e61fad35ed0d5352f9fd7073304e2f2dc72" +dependencies = [ + "static_assertions", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -776,6 +867,12 @@ dependencies = [ "cstr_core", ] +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wasm-bindgen" version = "0.2.88" diff --git a/Cargo.toml b/Cargo.toml index 8bac4354de6580de34ec4f46e7f890b867c4bb41..d4651d67f9084a70163a0e086b8e7cf5b67aa583 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,13 +35,12 @@ gethostname = "0.4.3" [dependencies] vm-fdt = { git = "https://github.com/OhmR/vm-fdt" } -fdt = { git = "https://github.com/karin0/libfdt-rs" } +fdt = { git = "https://github.com/Zera-Algorithm/libfdt-rs", rev = "61ec00" } log = { version = "0.4", features = [ "max_level_trace", "release_max_level_info", ] } spin = { version = "0.9.4", features = ["use_ticket_mutex"] } -buddy_system_allocator = "0.8.0" memoffset = { version = "0.8", default-features = false, features = [ "unstable_const", ] } @@ -51,6 +50,15 @@ cfg-if = "1.0.0" cortex-a = { package = "aarch64-cpu", version = "9.3.1" } smccc = "0.1.1" +[target.'cfg(target_arch = "riscv64")'.dependencies] +rustsbi = { version = "0.3.2", default-features = false, features = [ "legacy", "singleton" ] } +sbi = "0.2.0" +riscv = { git = "https://github.com/Zera-Algorithm/riscv" } + +[dependencies.buddy_system_allocator] +version = "0.8.0" +features = ["const_fn"] + [dependencies.tock-registers] version = "0.8.0" default-features = false @@ -74,4 +82,6 @@ rk3588-noeth = ["rk3588"] ns16550 = [] pl011 = [] unilib = [] +memrsv = [] doc = [] +sbi_legacy = [] diff --git a/Makefile b/Makefile index 69cc07c54f85278d79c1e551e20cd7da72242606..246a9e3a69d0c92a50833496b2bcb27b593e785c 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,13 @@ FEATURES ?= # Toolchain ifeq ($(ARCH), aarch64) - CROSS_COMPILE ?= aarch64-none-elf- + CROSS_COMPILE := aarch64-none-elf- + TEXT_START := 0x40080000 + VM0_IMAGE_PATH := "./image/Image_vanilla" else ifeq ($(ARCH), riscv64) - CROSS_COMPILE ?= riscv64-linux-gnu- + CROSS_COMPILE := riscv64-linux-gnu- + TEXT_START := 0x80200000 + VM0_IMAGE_PATH := "./image/Image_5.15.100-riscv-starfive" else $(error bad arch: $(ARCH)) endif @@ -41,6 +45,26 @@ IMAGE=rust_shyper TARGET_DIR=target/${ARCH}/${PROFILE} +ifeq (${ARCH}, aarch64) +QEMU_COMMON_OPTIONS = -machine virt,virtualization=on,gic-version=$(GIC_VERSION)\ + -m 8g -cpu cortex-a57 -smp 4 -display none -global virtio-mmio.force-legacy=false\ + -kernel ${TARGET_DIR}/${IMAGE}.bin +QEMU_NETWORK_OPTIONS = -netdev user,id=n0,hostfwd=tcp::5555-:22 -device virtio-net-device,bus=virtio-mmio-bus.24,netdev=n0 +QEMU_DISK_OPTIONS = -drive file=${DISK},if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.25 +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 \ + -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 +MKIMAGE_ARCH = riscv +FEATURES += ,sbi_legacy, +else +$(error bad qemu arch: $(ARCH)) +endif + # Use target_cfg depending on ARCH TARGET_CFG := $(CURDIR)/cfg/${ARCH}.json @@ -83,12 +107,16 @@ cargo: # TODO: fix the mkimage ARCH because it only accept "arm64" and "AArch64" for aarch64 upload: build - @mkimage -n ${IMAGE} -A arm64 -O linux -T kernel -C none -a $(TEXT_START) -e $(TEXT_START) -d ${TARGET_DIR}/${IMAGE}.bin ${TARGET_DIR}/${UBOOT_IMAGE} + @mkimage -n ${IMAGE} -A ${MKIMAGE_ARCH} -O linux -T kernel -C none -a $(TEXT_START) -e $(TEXT_START) -d ${TARGET_DIR}/${IMAGE}.bin ${TARGET_DIR}/${UBOOT_IMAGE} @echo "*** Upload Image ${UBOOT_IMAGE} ***" @scp ${TARGET_DIR}/${UBOOT_IMAGE} ${TFTP_SERVER}/${UBOOT_IMAGE} qemu: - $(MAKE) build BOARD=qemu TEXT_START=0x40080000 VM0_IMAGE_PATH="./image/Image_vanilla" + $(MAKE) build BOARD=qemu TEXT_START=${TEXT_START} VM0_IMAGE_PATH=${VM0_IMAGE_PATH} + ${OBJCOPY} ${TARGET_DIR}/${IMAGE} -O binary ${TARGET_DIR}/${IMAGE}.bin + +qemu_uboot: + $(MAKE) upload BOARD=qemu TEXT_START=0x80200000 VM0_IMAGE_PATH="./image/Image_vanilla" rk3588: $(MAKE) upload BOARD=rk3588 TEXT_START=0x00480000 VM0_IMAGE_PATH="./image/Image-5.10.160" @@ -102,17 +130,6 @@ tx2_ramdisk: pi4: $(MAKE) upload BOARD=pi4 TEXT_START=0xf0080000 VM0_IMAGE_PATH="./image/Image_pi4_5.4.83_tlb" -ifeq (${ARCH}, aarch64) -QEMU_COMMON_OPTIONS = -machine virt,virtualization=on,gic-version=$(GIC_VERSION)\ - -m 8g -cpu cortex-a57 -smp 4 -display none -global virtio-mmio.force-legacy=false\ - -kernel ${TARGET_DIR}/${IMAGE}.bin -else ifeq (${ARCH}, riscv64) -QEMU_COMMON_OPTIONS = -machine virt,virtualization=on\ - -m 8g -cpu rv64 -smp 4 -display none -global virtio-mmio.force-legacy=false\ - -kernel ${TARGET_DIR}/${IMAGE}.bin -else -$(error bad qemu arch: $(ARCH)) -endif QEMU_SERIAL_OPTIONS = -serial mon:stdio #\ -serial telnet:localhost:12345,server @@ -120,12 +137,9 @@ QEMU_SERIAL_OPTIONS = -serial mon:stdio #\ # QEMU_NETWORK_OPTIONS = -netdev tap,id=tap0,ifname=tap0,script=no,downscript=no -device virtio-net-device,bus=virtio-mmio-bus.24,netdev=tap0 #/home/cwm/c-hyper/syberx-hypervisor/build/shyper_qemuv3.bin -QEMU_NETWORK_OPTIONS = -netdev user,id=n0,hostfwd=tcp::5555-:22 -device virtio-net-device,bus=virtio-mmio-bus.24,netdev=n0 - -QEMU_DISK_OPTIONS = -drive file=${DISK},if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.25 run: qemu - ${QEMU} ${QEMU_COMMON_OPTIONS} ${QEMU_SERIAL_OPTIONS} ${QEMU_NETWORK_OPTIONS} ${QEMU_DISK_OPTIONS} \ + ${QEMU} ${QEMU_COMMON_OPTIONS} ${QEMU_SERIAL_OPTIONS} ${QEMU_NETWORK_OPTIONS} ${QEMU_DISK_OPTIONS} debug: qemu ${QEMU} ${QEMU_COMMON_OPTIONS} ${QEMU_SERIAL_OPTIONS} ${QEMU_NETWORK_OPTIONS} ${QEMU_DISK_OPTIONS} \ @@ -134,6 +148,13 @@ debug: qemu gdb: ${GDB} -x gdb/$(ARCH).gdb +# target remote localhost:1234 +gdb-connect: + echo "symbol-file ${TARGET_DIR}/${IMAGE}" > .gdbinit + echo "target remote localhost:1234" >> .gdbinit + gdb-multiarch -command=.gdbinit + + clean: cargo clean @@ -141,4 +162,4 @@ clippy: CARGO_ACTION = clippy clippy: cargo doc: - $(MAKE) cargo CARGO_ACTION=doc FEATURES=doc,$(FEATURES) + $(MAKE) cargo CARGO_ACTION=doc FEATURES="doc,$(FEATURES)" diff --git a/README.ch.md b/README.ch.md index 2ca72c78d4122608acdf3b1c8e779befac6e5ccf..9d52eb3cd07adc16386f62680affeff271ef5d6b 100644 --- a/README.ch.md +++ b/README.ch.md @@ -19,6 +19,7 @@ Rust-Shyper是由北航计算机学院操作系统研究团队,在华为技术 - [x] Raspberry Pi 4 Model B - [x] QEMU (note that VM migration and Hypervisor Live-update is not supported on QEMU) - [x] Firefly ROC-RK3588S-PC (note that VM migration and Hypervisor Live-update is not supported on ROC-RK3588S-PC) +- [x] QEMU (for RISCV64) ## 如何编译 @@ -39,6 +40,10 @@ make 主要注意的是,请在编译前,根据需求编辑管理虚拟机(MVM)的配置文件。该文件的路径是 src/config/\_def.rs. +**RISCV64平台的支持** + +目前Rust-Shyper已经支持QEMU上的RISCV64平台,并提供了完整的用户使用手册和相应的附件,具体可以参考[面向 RISCV64 的 Rust-Shyper 使用文档](./doc/RISC-V64_User_Guide.md) + **RK3588的支持** 目前已经支持Firefly ROC-RK3588S-PC平台,并提供了完整的用户使用手册和相应的附件,具体可以参考[Firefly ROC-RK3588S-PC平台的使用](https://bhpan.buaa.edu.cn/link/AA90DE4F5ED6F447E1A9DE59F7B1A8FF72)。 @@ -67,7 +72,7 @@ insmod tools/shyper.ko 注:shyper-cli是Rust-Shyper配套的一个简单的命令行工具,以二进制的形式提供在tools目录下,其编译的目标平台为aarch64。 -> `cli`目录下是Rust版本的cli工具源码,您可以用cli目录下的源码在您所在的平台上编译,得到shyper-cli可执行文件,然后用其代替 `tools/shyper-cli` 进行后续的虚拟机管理操作。 +> `cli`目录下是Rust版本的 cli 工具源码,您可以用`cli`目录下的源码在您所在的平台上编译,得到`shyper-cli`可执行文件,然后用其代替 `tools/shyper-cli` 进行后续的虚拟机管理操作。 ```bash sudo tools/shyper system daemon [mediated-cfg.json] & @@ -214,6 +219,11 @@ sudo tools/shyper vm boot 然后就可以和客户虚拟机进行交互了 +## Riscv仓库说明 + +* `image/Image_vanilla` 所指向的镜像其实是 `riscv` 架构下的 `Linux 5.12.1` 版本内核。 +* `vm0.img` 其实是装载了 `busybox` 的 `rootfs` 文件系统。 + ## 发表文献 1. C. Mo, L. Wang, S. Li, K. Hu, B. Jiang. Rust-Shyper: A reliable embedded hypervisor supporting VM migration and hypervisor live-update, Journal of Systems Architecture (2023), doi: https://doi.org/10.1016/j.sysarc.2023.102948. diff --git a/README.md b/README.md index a7b726703256a21bf537aa26e4e95298cdabb2b6..56ffa5cf0a89eb5a881b1f6a2331f1604cea992d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ The list of supported (and work in progress) platforms is presented below: - [x] Raspberry Pi 4 Model B - [x] QEMU (note that VM migration and Hypervisor Live-update is not supported on QEMU) - [x] Firefly ROC-RK3588S-PC (note that VM migration and Hypervisor Live-update is not supported on ROC-RK3588S-PC) +- [x] QEMU (for RISCV64) ## How to Build @@ -39,6 +40,9 @@ For example, `make tx2` is to build Rust-Shyper for TX2. Note that please edit the MVM profile in src/config/\_def.rs according to your requirements. +**RISCV64 Platform support** + +At present, Rust-Shyper already supports the RISCV64 platform on QEMU, and provides a complete user manual and corresponding accessories, please refer to [Rust-Shyper for RISCV64 Documentation](./doc/RISC-V64_User_Guide.md) **RK3588 support** @@ -54,8 +58,6 @@ The kernel module on NVIDIA L4T 32.6.1 (for Jestion TX2), Linux 4.9.140/5.10.160 When starting Rust-Shyper, the MVM will boot automatically. Logging on to the MVM (a Linux priviledged VM), take the QEMU configuration as an example, then can we configure and start the Guest VMs. -> The `cli` directory contains the Rust version of the cli tool source code, which you can use to compile on your platform, obtain the shyper-cli executable file, and then use it in place of the `tools/shyper-cli` for subsequent virtual machine management operations. - **Step 1**: Install the kernel module ```bash @@ -68,6 +70,8 @@ insmod tools/shyper.ko sudo tools/shyper system daemon [mediated-cfg.json] & ``` +> The `cli` directory contains the Rust version of the cli tool source code, which you can use to compile on your platform, obtain the shyper-cli executable file, and then use it in place of the `tools/shyper-cli` for subsequent virtual machine management operations. + mediated-cfg.json is used for guest VM as virtio block. If it is a physical platform, you can connect external disk devices, such as /dev/sda2, /dev/nvme0n1p2. For example: ``` diff --git a/cfg/aarch64.json b/cfg/aarch64.json index ac7c5b456d9710c0803901375a1b459b053bdc6e..a0c5a75a4100c606c03ff14ac07805a05144a9d7 100644 --- a/cfg/aarch64.json +++ b/cfg/aarch64.json @@ -8,7 +8,7 @@ "linker": "rust-lld", "linker-flavor": "ld.lld", "linker-is-gnu": true, - "llvm-target": "aarch64-unknown-none-softfloat", + "llvm-target": "aarch64-unknown-none", "max-atomic-width": 128, "os": "none", "panic-strategy": "abort", diff --git a/cli/readme.md b/cli/readme.md index e2a8c7cdb798ede3231ecdfe7dd80da31e8f2c27..d07252491dbf2e661a6191db2b3b08fdfc0ec6ee 100644 --- a/cli/readme.md +++ b/cli/readme.md @@ -1,3 +1,13 @@ -# How to compile +# How to compile and use cli tool -Put this in aarch64/riscv environment, then run `make`. +Before you compile the cli tool source, you should **move cli directory to a location outside the root directory of rust-shyper**. Because cli and rust-shyper are two independent rust crates, and will have conflicts when compiling. + +First, create a Linux distribution environment for one architecture (AArch64 or RISCV64). + +Second, apt install the following dependency under this environment: + +* libfdisk-dev + +Third, locate the directory to the cli, then run `make`. + +Finally, you can use the compiled executable as a command-line tool to manage rust-shyper. diff --git a/doc/RISC-V64_User_Guide.md b/doc/RISC-V64_User_Guide.md new file mode 100644 index 0000000000000000000000000000000000000000..b4f09691ed6a144c15ee1a34083dfa17ebde94b5 --- /dev/null +++ b/doc/RISC-V64_User_Guide.md @@ -0,0 +1,369 @@ +# 面向 RISCV64 的 Rust-Shyper 使用文档 + +本文档主要用来说明如何在QEMU平台上启动riscv64版本的Rust-Shyper,并在其上运行多个VM。 + +## 软件依赖 + +在运行Rust-Shyper之前,需要安装如下的软件依赖: + +* Rust +* riscv64-linux-gnu-gcc 交叉编译工具链 + ```shell + sudo apt install gcc-riscv64-linux-gnu + ``` +* cargo-binutils(可选) +* qemu-system-riscv64 8.2.0 及以上版本(低版本不支持riscv的虚拟化扩展) + +## MVM的配置 + +### 基本介绍 + +MVM是用于对其他虚拟机进行管理的管理VM,运行Linux,可以通过内核模块和用户态命令行工具(CLI)与Rust-Shyper通信,以此完成VM管理的任务。 + +对于MVM,本项目使用的Linux镜像在 `image/Image_5.15.100-riscv-starfive`。此镜像是基于Ubuntu 22.04 Starfive发行版Preinstalled镜像提取的内核镜像,具有较为完整的功能(开启了大部分常用的内核选项)和兼容性。 + +> 要了解更多关于Ubuntu对RISC-V的支持,参见[https://ubuntu.com/download/risc-v](https://ubuntu.com/download/risc-v) + +本项目使用的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 + +此镜像的用户名和密码均为root。 + +> Ubuntu base image是一个很小的Linux rootfs。与自行构建的rootfs有所不同,Ubuntu base image支持apt安装程序,并自带基本的gnu命令行工具,可供用户从零构建包含完整软件包rootfs。可参考[Ubuntu Base官方](https://www.cdimage.ubuntu.com/ubuntu-base/releases/jammy/release/) + +### MVM的配置信息 + +对MVM的配置主要看 `src/config/qemu_riscv64_def.rs`. + +```rs +pub fn mvm_config_init() { + // Set the configuration name for VM0 + vm_cfg_set_config_name("qemu-default"); + + // 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, + }, + // hvc2 + VmEmulatedDeviceConfig { + name: String::from("virtio_console@40001000"), + base_ipa: 0x4000_1000, + length: 0x1000, + irq_id: 46, + cfg_list: vec![1, 0x40001000], + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }, + // hvc1 + VmEmulatedDeviceConfig { + name: String::from("virtio_console@40002000"), + base_ipa: 0x4000_2000, + length: 0x1000, + irq_id: 47, + cfg_list: vec![2, 0x4000_2000], // Address of the peer vm and the peer virtio-console + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }, + // virtual eth0 + VmEmulatedDeviceConfig { + name: String::from("virtio_net@40003000"), + base_ipa: 0x40003000, + length: 0x1000, + irq_id: 48, + cfg_list: vec![0x74, 0x56, 0xaa, 0x0f, 0x47, 0xd0], + emu_type: EmuDeviceType::EmuDeviceTVirtioNet, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("shyper"), + base_ipa: 0, + length: 0, + irq_id: HVC_IRQ, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTShyper, + mediated: false, + } + ]; + + // vm0 passthrough + let pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig { + regions: vec![ + // pass-through virtio blk + PassthroughRegion { ipa: 0x10001000, pa: 0x10001000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10002000, pa: 0x10002000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10003000, pa: 0x10003000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10004000, pa: 0x10004000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10005000, pa: 0x10005000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10006000, pa: 0x10006000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10007000, pa: 0x10007000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10008000, pa: 0x10008000, length: 0x1000, dev_property: true }, + // Serial Device + PassthroughRegion { ipa: 0x10000000, pa: 0x10000000, length: 0x1000, dev_property: true }, + // RTC + PassthroughRegion { ipa: 0x101000, pa: 0x101000, length: 0x1000, dev_property: true }, + ], + irqs: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,], + streams_ids: vec![] + }; + + // vm0 vm_region + let vm_region = vec![ + VmRegion { + ipa_start: 0x90000000, + length: 0x100000000, + } + ]; + + // vm0 config + let mvm_config_entry = VmConfigEntry { + id: 0, + name: String::from("supervisor"), + os_type: VmType::VmTOs, + cmdline: String::from("earlycon=sbi console=ttyS0 root=/dev/vda rw audit=0 default_hugepagesz=2M hugepagesz=2M hugepages=10\0"), + image: VmImageConfig { + kernel_img_name: Some("Image"), + kernel_load_ipa: 0x90000000, + kernel_entry_point: 0x90000000, + device_tree_load_ipa: 0x180000000, + ramdisk_load_ipa: 0, + mediated_block_index: None, + }, + cpu: VmCpuConfig { + num: 1, + allocate_bitmap: 0b0001, + master: Some(0), + }, + memory: VmMemoryConfig { + region: vm_region, + }, + vm_emu_dev_confg: VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config }, + vm_pt_dev_confg: pt_dev_config, + vm_dtb_devs: VMDtbDevConfigList::default(), + ..Default::default() + }; + let _ = vm_cfg_add_vm_entry(mvm_config_entry); +} +``` + +该文件配置了MVM的模拟设备、直通设备、中间物理内存(IPA)起始地址与大小、内核启动参数、内核加载地址、cpu数目等等。这部分并非完全固定,可以根据自己的需求在一定范围内做动态的更改。 + +## Rust-Shyper的启动 + +使用如下命令编译RISC-V版本的Rust-Shyper: + +```shell +ARCH=riscv64 make +``` + +使用如下命令运行: + +```shell +ARCH=riscv64 make run +``` + +## GVM的启动与配置 + +GVM的启动需要依赖内核模块(`tools/shyper_riscv64.ko`)和CLI程序(由`cli/`编译得到)。 + +登录到MVM中,按照如下步骤,就可以配置并启动客户虚拟机。 + +**Step 0**: + +在RISC-V64环境下,编译CLI程序,得到 `shyper` 可执行文件。 + +> 可参考 https://ubuntu.com/download/risc-v 配置一个只运行Linux的RISC-V虚拟机,然后在这个环境下编译RISC-V的依赖项。 + +**Step 1**: 安装内核模块 + +```shell +insmod shyper_riscv64.ko +``` + +**Step 2**: 启动shyper-cli守护进程 + +```shell +chmod +x shyper +./shyper system daemon [mediated-cfg.json] & +``` + +`mediated-cfg.json`(本目录下的 `shypercli-config.json` 就是一个参考)用于配置其他 guest VM 的 virtio 中介磁盘,示例如下: + +```json +{ + "mediated": [ + "/root/vm0_ubuntu.img", // 此处配置第1个GVM的中介磁盘 + "/root/vm0_ubuntu_2.img", // 此处配置第2个VM的中介磁盘 + "/root/vm0_busybox.img" // 此处配置第3个VM的中介磁盘 + ] +} +``` + +其中,列表每一项既可以写分区名(如`/dev/sda1`),又可以写磁盘镜像名。 + +**Step 3**: 通过配置文件来配置GVM: + +```shell +./shyper vm config +``` + +本目录下(`./vm1_config_riscv.json`)提供了配置文件的示例: + +```json +{ + "name": "guest-os-1", + "type": "VM_T_LINUX", + // 配置内核启动参数 + "cmdline": "earlycon=sbi console=hvc1 root=/dev/vda rw audit=0", + "image": { + // 配置内核镜像的位置 + "kernel_filename": "Image", + "kernel_load_ipa": "0x80200000", + "kernel_entry_point": "0x80200000", + "device_tree_filename": "", + "device_tree_load_ipa": "0xa0000000", + "ramdisk_filename": "initrd.gz", + "ramdisk_load_ipa": "0" + }, + "memory": { + // 内存区域配置 + "num": 1, + "region": [ + { + "ipa_start": "0x80000000", + "length": "0x40000000" + } + ] + }, + "cpu": { + // 配置CPU分配。allocate_bitmap表示给GVM分配哪些物理核心 + // MVM独占0核,其他核允许GVM之间共享。如GVM1和GVM2可以共享1、2、3核 + "num": 3, + "allocate_bitmap": "0b1110", + // 主核心号,是上面的allocate_bitmap中的其中一个 + "master" : 1 + }, + // 模拟设备配置,不用管,已经配置好了 + "emulated_device" : { + "num": 4, + "emulated_device_list": [ + { + "name": "plic@c000000", + "base_ipa": "0xc000000", + "length": "0x600000", + "irq_id": 1, + "cfg_num": 2, + "cfg_list": [ + 0, + 0 + ], + "type": "EMU_DEVICE_T_PLIC" + }, + { + "name": "virtio_blk@10001000", + "base_ipa": "0x10001000", + "length": "0x1000", + "irq_id": 19, + "cfg_num": 2, + "cfg_list": [ + 0, + 8192000 + ], + "type": "EMU_DEVICE_T_VIRTIO_BLK_MEDIATED" + }, + { + "name": "virtio_net@50000000", + "base_ipa": "0x50000000", + "length": "0x1000", + "irq_id": 20, + "cfg_num": 6, + "cfg_list": [ + "0x74", + "0x56", + "0xaa", + "0x0f", + "0x47", + "0xd1" + ], + "type": "EMU_DEVICE_T_VIRTIO_NET" + }, + { + "name": "virtio_console@40001000", + "base_ipa": "0x40001000", + "length": "0x1000", + "irq_id": 21, + "cfg_num": 2, + "cfg_list": [ + "0", + "0x40001000" + ], + "type": "EMU_DEVICE_T_VIRTIO_CONSOLE" + } + ] + }, + // 直通设备配置,不用管,已经配置好了 + "passthrough_device": { + "passthrough_device_list": [ + ] + }, + // 直通设备的设备树配置,不用管,已经配置好了 + "dtb_device" : { + "num": 0, + "dtb_device_list": [ + ] + } +} +``` + +**Step 4**: 启动客户虚拟机 + +```shell +./shyper vm boot +``` + +按照启动的顺序排序。MVM的VMID为0,第一个启动的GVM的VMID为1。可以通过`./shyper vm list` 查看当前配置的各个VM的信息。 + +**Step 5**: 与客户虚拟机交互 + +此时先从外部ssh连接到MVM: + +```shell +ssh root@localhost -p 5555 +``` + +然后通过minicom连接hvc1,监控GVM的串口输出: + +```shell +minicom -D /dev/hvc1 -b 115200 +``` + +这样就可以与GVM交互了: + +![与GVM交互图](img/gvm.png) + +> 本项目镜像中带有的GVM镜像 `vm0_ubuntu.img`,其用户名为root,密码为vm1 + +## 网络通信 + +MVM上默认绑定了两块网卡:eth0和eth1,均为virtio网卡。其中第一块是虚拟网卡,第二块是来自qemu的直通物理网卡。 + +![MVM网卡](img/iface.png) + +MVM绑定的网卡地址为10.0.0.1(eth0,作为GVM的网关)和10.0.2.15(eth1, 网关为10.0.2.2,通过该网关访问外部网络)。 + +对于GVM,VM1的网卡地址为10.0.0.2,其他VM IP地址最后一位数依次递增。 + +MVM可以通过10.0.0.0/24网段与GVM通信,并通过10.0.2.2网关与外网通信。具体的网络拓扑图如下图所示: + +![alt text](img/network-topo.png) diff --git a/doc/img/gvm.png b/doc/img/gvm.png new file mode 100644 index 0000000000000000000000000000000000000000..6e2c211c07136bc3a2a8865a3333e230360400c6 Binary files /dev/null and b/doc/img/gvm.png differ diff --git a/doc/img/iface.png b/doc/img/iface.png new file mode 100644 index 0000000000000000000000000000000000000000..78a797cc30f4b2174ae0210006b2c153be3ffdc6 Binary files /dev/null and b/doc/img/iface.png differ diff --git a/doc/img/network-topo.png b/doc/img/network-topo.png new file mode 100644 index 0000000000000000000000000000000000000000..278c1c0059726e43fd4058d4e6e80f3eb58c07ca Binary files /dev/null and b/doc/img/network-topo.png differ diff --git a/doc/shypercli-config.json b/doc/shypercli-config.json new file mode 100644 index 0000000000000000000000000000000000000000..918352b6c3c9ed7606e19e34519abaaa51ea8a63 --- /dev/null +++ b/doc/shypercli-config.json @@ -0,0 +1,7 @@ +{ + "mediated": [ + "/root/vm0_ubuntu.img", + "/root/vm0_ubuntu_2.img", + "/root/vm0_busybox.img" + ] +} \ No newline at end of file diff --git a/doc/vm1_config_riscv.json b/doc/vm1_config_riscv.json new file mode 100644 index 0000000000000000000000000000000000000000..52d13964282bf19099419120651f2023b787c0ba --- /dev/null +++ b/doc/vm1_config_riscv.json @@ -0,0 +1,94 @@ +{ + "name": "guest-os-1", + "type": "VM_T_LINUX", + "cmdline": "earlycon=sbi console=hvc1 root=/dev/vda rw audit=0", + "image": { + "kernel_filename": "Image", + "kernel_load_ipa": "0x80200000", + "kernel_entry_point": "0x80200000", + "device_tree_filename": "", + "device_tree_load_ipa": "0xa0000000", + "ramdisk_filename": "initrd.gz", + "ramdisk_load_ipa": "0" + }, + "memory": { + "num": 1, + "region": [ + { + "ipa_start": "0x80000000", + "length": "0x40000000" + } + ] + }, + "cpu": { + "num": 3, + "allocate_bitmap": "0b1110", + "master" : 1 + }, + "emulated_device" : { + "num": 4, + "emulated_device_list": [ + { + "name": "plic@c000000", + "base_ipa": "0xc000000", + "length": "0x600000", + "irq_id": 1, + "cfg_num": 2, + "cfg_list": [ + 0, + 0 + ], + "type": "EMU_DEVICE_T_PLIC" + }, + { + "name": "virtio_blk@10001000", + "base_ipa": "0x10001000", + "length": "0x1000", + "irq_id": 19, + "cfg_num": 2, + "cfg_list": [ + 0, + 8192000 + ], + "type": "EMU_DEVICE_T_VIRTIO_BLK_MEDIATED" + }, + { + "name": "virtio_net@50000000", + "base_ipa": "0x50000000", + "length": "0x1000", + "irq_id": 20, + "cfg_num": 6, + "cfg_list": [ + "0x74", + "0x56", + "0xaa", + "0x0f", + "0x47", + "0xd1" + ], + "type": "EMU_DEVICE_T_VIRTIO_NET" + }, + { + "name": "virtio_console@40001000", + "base_ipa": "0x40001000", + "length": "0x1000", + "irq_id": 21, + "cfg_num": 2, + "cfg_list": [ + "0", + "0x40001000" + ], + "type": "EMU_DEVICE_T_VIRTIO_CONSOLE" + } + ] + }, + "passthrough_device": { + "passthrough_device_list": [ + ] + }, + "dtb_device" : { + "num": 0, + "dtb_device_list": [ + ] + } +} diff --git a/image/Image_5.15.100-riscv-starfive b/image/Image_5.15.100-riscv-starfive new file mode 100755 index 0000000000000000000000000000000000000000..3ce224c601dcb3a090e1fb841e9d8bb7e13ea4da Binary files /dev/null and b/image/Image_5.15.100-riscv-starfive differ diff --git a/linkers/riscv64.ld b/linkers/riscv64.ld index becd72c894a265acc8891d2bf202f595834711ef..edffca70139ebd54d4fab8cf2ac3e472ab8ac2e7 100644 --- a/linkers/riscv64.ld +++ b/linkers/riscv64.ld @@ -1,4 +1,7 @@ ENTRY(_start) +TARGET(binary) +INPUT("vm0img") +OUTPUT_FORMAT(default) SECTIONS { . = TEXT_START; @@ -21,15 +24,19 @@ SECTIONS .data : { *(.data*) + vm0img(.vm0image) } - . = ALIGN(4096); - _bss_begin = .; .bss (NOLOAD) : ALIGN(4096) { + *(.bss.stack) + + . = ALIGN(4096); + _bss_begin = .; + *(.sbss*) *(.bss*) + . = ALIGN(4096); + _bss_end = .; } - . = ALIGN(4096); - _bss_end = .; _image_end = ABSOLUTE(.); } diff --git a/src/arch/aarch64/context_frame.rs b/src/arch/aarch64/context_frame.rs index def56e2390d8992bd109979d5e28382de729fa9b..dfa282db2cdd1d064b60c56b9ba18f633b870e9d 100644 --- a/src/arch/aarch64/context_frame.rs +++ b/src/arch/aarch64/context_frame.rs @@ -11,6 +11,8 @@ use core::arch::global_asm; use crate::arch::traits::InterruptContextTrait; use core::fmt; +use crate::arch::timer_arch_get_counter; +use crate::arch::VmContextTrait; use cortex_a::registers::*; @@ -66,11 +68,11 @@ impl crate::arch::ContextFrameTrait for Aarch64ContextFrame { fn new(pc: usize, sp: usize, arg: usize) -> Self { let mut r = Aarch64ContextFrame { gpr: [0; 31], - spsr: (SPSR_EL1::M::EL1h - + SPSR_EL1::I::Masked - + SPSR_EL1::F::Masked - + SPSR_EL1::A::Masked - + SPSR_EL1::D::Masked) + spsr: (SPSR_EL2::M::EL1h + + SPSR_EL2::I::Masked + + SPSR_EL2::F::Masked + + SPSR_EL2::A::Masked + + SPSR_EL2::D::Masked) .value, elr: pc as u64, sp: sp as u64, @@ -112,11 +114,11 @@ impl Aarch64ContextFrame { pub fn default() -> Aarch64ContextFrame { Aarch64ContextFrame { gpr: [0; 31], - spsr: (SPSR_EL1::M::EL1h - + SPSR_EL1::I::Masked - + SPSR_EL1::F::Masked - + SPSR_EL1::A::Masked - + SPSR_EL1::D::Masked) + spsr: (SPSR_EL2::M::EL1h + + SPSR_EL2::I::Masked + + SPSR_EL2::F::Masked + + SPSR_EL2::A::Masked + + SPSR_EL2::D::Masked) .value, elr: 0, sp: 0, @@ -325,8 +327,8 @@ impl Default for VmContext { } } -impl VmContext { - pub fn reset(&mut self) { +impl VmContextTrait for VmContext { + fn reset(&mut self) { self.cntvoff_el2 = 0; self.cntp_cval_el0 = 0; self.cntv_cval_el0 = 0; @@ -365,7 +367,7 @@ impl VmContext { self.fpsimd.reset(); } - pub fn ext_regs_store(&mut self) { + fn ext_regs_store(&mut self) { self.cntvoff_el2 = CNTVOFF_EL2::read(); self.cntv_cval_el0 = CNTV_CVAL_EL0::read(); self.cntkctl_el1 = CNTKCTL_EL1::read() as u32; @@ -400,7 +402,7 @@ impl VmContext { self.actlr_el1 = ACTLR_EL1::read(); } - pub fn ext_regs_restore(&self) { + fn ext_regs_restore(&self) { // SAFETY: // 1. The registers has defined as valid register // 2. The value is read from the register or @@ -436,7 +438,7 @@ impl VmContext { } } - pub fn fpsimd_save_context(&mut self) { + fn fpsimd_save_context(&mut self) { // SAFETY: // We use the address of fpsimd to save the all floating point register // eg. Q0-Q31, FPSR, FPCR @@ -447,7 +449,7 @@ impl VmContext { } } - pub fn fpsimd_restore_context(&self) { + fn fpsimd_restore_context(&self) { // SAFETY: // We use the address of fpsimd to restore the all floating point register // eg. Q0-Q31, FPSR, FPCR @@ -457,11 +459,27 @@ impl VmContext { } } - pub fn gic_save_state(&mut self) { + fn gic_save_state(&mut self) { self.gic_state.save_state(); } - pub fn gic_restore_state(&self) { + fn gic_restore_state(&self) { self.gic_state.restore_state(); } + + fn gic_ctx_reset(&mut self) { + use crate::arch::gich_lrs_num; + for i in 0..gich_lrs_num() { + self.gic_state.lr[i] = 0; + } + self.gic_state.hcr |= 1 << 2; // init hcr + } + + // Reset the el2 offset register + // so that the virtual time of the virtual machine is consistent with the physical time + fn reset_vtimer_offset(&mut self) { + self.cntvoff_el2 = 0; + let curpct = timer_arch_get_counter() as u64; + self.cntvoff_el2 = curpct - self.cntvct_el0; + } } diff --git a/src/arch/aarch64/exception.rs b/src/arch/aarch64/exception.rs index 2070977ae6472c86f5dc7a383653bde37d782114..50e638c7d80b78ca46407a9e3b72c3820a475ef3 100644 --- a/src/arch/aarch64/exception.rs +++ b/src/arch/aarch64/exception.rs @@ -218,12 +218,13 @@ extern "C" fn lower_aarch64_synchronous(ctx: &mut ContextFrame) { (*ctx).gpr(29) ); panic!( - "core {} vm {}: handler not presents for EC_{:b} @ipa 0x{:x}, @pc 0x{:x}", + "core {} vm {}: handler not presents for EC_{:b} @ipa 0x{:x}, @pc 0x{:x}, @esr:0x{:x}", current_cpu().id, active_vm_id(), exception_class(), exception_fault_addr(), - (*ctx).exception_pc() + (*ctx).exception_pc(), + exception_esr() ); } } @@ -232,6 +233,7 @@ extern "C" fn lower_aarch64_synchronous(ctx: &mut ContextFrame) { #[no_mangle] extern "C" fn lower_aarch64_irq(ctx: &mut ContextFrame) { + // SAFETY: ctx is a valid pointer unsafe { current_cpu().set_ctx(ctx); } diff --git a/src/arch/aarch64/gicv3.rs b/src/arch/aarch64/gicv3.rs index 477df0c59b794f27a35f18404919712d98127910..3dbb74f65d9dd3ecba272d4fa9bd12a2f51271ba 100644 --- a/src/arch/aarch64/gicv3.rs +++ b/src/arch/aarch64/gicv3.rs @@ -26,7 +26,7 @@ use crate::arch::{ ICH_AP0R1_EL2, ICH_AP0R0_EL2, ICH_AP1R0_EL2, ICH_AP1R1_EL2, ICH_AP1R2_EL2, ICC_PMR_EL1, ICC_BPR1_EL1, ICC_CTLR_EL1, ICH_ELRSR_EL2, ICC_IGRPEN1_EL1, ICC_DIR_EL1, ICC_EOIR1_EL1, ICH_LR0_EL2, ICH_LR1_EL2, ICH_LR2_EL2, ICH_LR3_EL2, ICH_LR4_EL2, ICH_LR5_EL2, ICH_LR6_EL2, ICH_LR7_EL2, ICH_LR8_EL2, ICH_LR9_EL2, ICH_LR10_EL2, ICH_LR11_EL2, - ICH_LR12_EL2, ICH_LR13_EL2, ICH_LR14_EL2, ICH_LR15_EL2, ICH_EISR_EL2, ICH_MISR_EL2, + ICH_LR12_EL2, ICH_LR13_EL2, ICH_LR14_EL2, ICH_LR15_EL2, ICH_EISR_EL2, ICH_MISR_EL2, sysreg_enc_addr, }; use crate::board::{Platform, PlatOperation}; use crate::utils::bit_extract; @@ -150,6 +150,10 @@ pub const GICD_TYPER_CPUNUM_MSK: usize = ((1 << GICD_TYPER_CPUNUM_LEN) - 1) << ( const GICD_TYPER_ITLINESNUM_LEN: usize = 0b11111; pub const ICC_CTLR_EOIMODE_BIT: usize = 0x1 << 1; +//sysreg +pub const ICC_SRE_ADDR: usize = sysreg_enc_addr(3, 0, 12, 12, 5); +pub const ICC_SGIR_ADDR: usize = sysreg_enc_addr(3, 0, 12, 11, 5); + pub static GIC_LRS_NUM: AtomicUsize = AtomicUsize::new(0); static GICD_LOCK: Mutex<()> = Mutex::new(()); diff --git a/src/arch/aarch64/interface.rs b/src/arch/aarch64/interface.rs index dc79962fa1d2c30faa683309c69e9c5ec6df0488..d715256db00034e485a834cf2d6056971b2d0e75 100644 --- a/src/arch/aarch64/interface.rs +++ b/src/arch/aarch64/interface.rs @@ -8,6 +8,8 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::arch::ArchTrait; + pub const PAGE_SIZE: usize = 4096; pub const PAGE_SHIFT: usize = 12; @@ -18,3 +20,25 @@ pub type ContextFrame = super::context_frame::Aarch64ContextFrame; pub const WORD_SIZE: usize = 8; pub const PTE_PER_PAGE: usize = PAGE_SIZE / WORD_SIZE; + +pub type Arch = Aarch64Arch; + +pub struct Aarch64Arch; + +impl ArchTrait for Aarch64Arch { + fn wait_for_interrupt() { + // SAFETY: Wait for interrupt + crate::arch::wfi(); + } + + // Restore the MMU context of a VM Stage2 (typically set vmid) + fn install_vm_page_table(base: usize, vmid: usize) { + // restore vm's Stage2 MMU context + let vttbr = (vmid << 48) | base; + // SAFETY: 'vttbr' is saved in the vcpu struct when last scheduled + unsafe { + core::arch::asm!("msr VTTBR_EL2, {0}", in(reg) vttbr); + crate::arch::isb(); + } + } +} diff --git a/src/arch/aarch64/interrupt.rs b/src/arch/aarch64/interrupt.rs index 701995e94ffcaa299258db7f21c99f0920532dbc..3ba085ae621cd77be6b501d87dc208b691ac0448 100644 --- a/src/arch/aarch64/interrupt.rs +++ b/src/arch/aarch64/interrupt.rs @@ -97,10 +97,10 @@ impl InterruptController for IntCtrl { } } - fn vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize) { + fn vm_inject(vm: &Vm, vcpu: &Vcpu, int_id: usize) { let vgic = vm.vgic(); - if let Some(cur_vcpu) = current_cpu().active_vcpu.clone() { - if cur_vcpu.vm_id() == vcpu.vm_id() { + if let Some(cur_vcpu) = current_cpu().active_vcpu.as_ref() { + if cur_vcpu == vcpu { vgic.inject(vcpu, int_id); return; } @@ -109,7 +109,7 @@ impl InterruptController for IntCtrl { vcpu.push_int(int_id); } - fn vm_register(vm: Vm, int_id: usize) { + fn vm_register(vm: &Vm, int_id: usize) { super::vgic_set_hw_int(vm, int_id); } diff --git a/src/arch/aarch64/mmu.rs b/src/arch/aarch64/mmu.rs index eb476fb7d0734d508c3435c9d08efd10101add67..7d5043a367f067d15d99a15c193bfb93626b9c88 100644 --- a/src/arch/aarch64/mmu.rs +++ b/src/arch/aarch64/mmu.rs @@ -155,9 +155,6 @@ pub extern "C" fn pt_populate(lvl1_pt: &mut PageTables, lvl2_pt: &mut PageTables BlockDescriptor::invalid() } } - // for i in PLATFORM_PHYSICAL_LIMIT_GB..ENTRY_PER_PAGE { - // pt.entry[i] = BlockDescriptor::invalid(); - // } lvl1_pt.entry[0] = BlockDescriptor::table(lvl2_base); // 0x200000 ~ 2MB @@ -281,10 +278,6 @@ pub extern "C" fn mmu_init(pt: &PageTables) { + TCR_EL2::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL2::T0SZ.val(64 - 39), ); - - // barrier::isb(barrier::SY); - // SCTLR_EL2.modify(SCTLR_EL2::M::Enable + SCTLR_EL2::C::Cacheable + SCTLR_EL2::I::Cacheable); - // barrier::isb(barrier::SY); } const PAR_EL1_OFF: usize = 12; diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index 02ec57e314b14179d4bb25bdc7bdd41d986ca70e..ec4bf4d94cb53fd17ec52af2b0b3b5b038f746db 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -23,17 +23,18 @@ pub use self::smc::*; pub use self::smmu::*; pub use self::sync::*; pub use self::timer::*; -pub use self::vcpu::*; #[cfg(not(feature = "gicv3"))] pub use self::vgic::*; #[cfg(feature = "gicv3")] pub use self::vgicv3::*; pub use self::start::*; pub use self::regs::*; +pub use self::cache::*; +pub use self::tlb::*; #[macro_use] mod regs; -mod cache; +pub mod cache; mod context_frame; mod cpu; mod exception; @@ -51,12 +52,13 @@ mod smmu; mod start; mod sync; mod timer; -mod tlb; +pub mod tlb; mod vcpu; #[cfg(not(feature = "gicv3"))] mod vgic; #[cfg(feature = "gicv3")] mod vgicv3; +mod vm; #[repr(C)] pub struct ArchDesc { diff --git a/src/arch/aarch64/psci.rs b/src/arch/aarch64/psci.rs index 400f8812ca132f4a143b40fc2c810489abc391ee..8b735a7e149850d46cdd8217de1d7ebcd2ab6393 100644 --- a/src/arch/aarch64/psci.rs +++ b/src/arch/aarch64/psci.rs @@ -8,7 +8,7 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::{gic_cpu_init, gicc_clear_current_irq, vcpu_arch_init}; +use crate::arch::{gic_cpu_init, gicc_clear_current_irq}; use crate::board::{Platform, PlatOperation}; use crate::kernel::{cpu_idle, current_cpu, ipi_intra_broadcast_msg, Scheduler, timer_enable, Vcpu, VcpuState, Vm}; use crate::kernel::{active_vm, ipi_send_msg, IpiInnerMsg, IpiPowerMessage, IpiType, PowerEvent}; @@ -21,6 +21,9 @@ use smccc::{self, Smc}; use super::smc::smc_call; +// RKNPU requires this feature to be reported by the PSCI_FEATURES call. +const SMCCC_VERSION: usize = 0x80000000; + pub const PSCI_VERSION: usize = 0x84000000; pub const PSCI_CPU_SUSPEND_32: usize = 0x84000001; pub const PSCI_CPU_SUSPEND_64: usize = 0xC4000001; @@ -63,7 +66,7 @@ const TEGRA_SIP_GET_ACTMON_CLK_COUNTERS: usize = 0xC2FFFE02; pub const PSCI_TOS_NOT_PRESENT_MP: usize = 2; -pub fn power_arch_vm_shutdown_secondary_cores(vm: Vm) { +pub fn power_arch_vm_shutdown_secondary_cores(vm: &Vm) { let m = IpiPowerMessage { src: vm.id(), vcpuid: 0, @@ -189,17 +192,12 @@ pub fn smc_guest_handler(fid: usize, x1: usize, x2: usize, x3: usize) -> bool { #[cfg(feature = "tx2")] TEGRA_SIP_GET_ACTMON_CLK_COUNTERS => { let result = unsafe { smc_call(fid, x1, x2, x3) }; - // println!("x1 0x{:x}, x2 0x{:x}, x3 0x{:x}", x1, x2, x3); - // println!( - // "result.0 0x{:x}, result.1 0x{:x}, result.2 0x{:x}", - // result.0, result.1, result.2 - // ); current_cpu().set_gpr(1, result.1); current_cpu().set_gpr(2, result.2); result.0 } PSCI_FEATURES => match x1 { - PSCI_VERSION | PSCI_CPU_ON_64 | PSCI_FEATURES => PSCI_E_SUCCESS, + PSCI_VERSION | PSCI_CPU_ON_64 | PSCI_FEATURES | SMCCC_VERSION => PSCI_E_SUCCESS, _ => PSCI_E_NOT_SUPPORTED, }, PSCI_CPU_FREEZE => match smccc::psci::cpu_freeze::() { @@ -243,7 +241,7 @@ pub fn smc_guest_handler(fid: usize, x1: usize, x2: usize, x3: usize) -> bool { true } -fn psci_vcpu_on(vcpu: Vcpu, entry: usize, ctx: usize) { +fn psci_vcpu_on(vcpu: &Vcpu, entry: usize, ctx: usize) { if vcpu.phys_id() != current_cpu().id { panic!( "cannot psci on vcpu on cpu {} by cpu {}", @@ -258,7 +256,7 @@ fn psci_vcpu_on(vcpu: Vcpu, entry: usize, ctx: usize) { // Just wake up the vcpu and // invoke current_cpu().sched.schedule() // let the scheduler enable or disable timer - current_cpu().scheduler().wakeup(vcpu); + current_cpu().scheduler().wakeup(vcpu.clone()); current_cpu().scheduler().do_schedule(); if cfg!(feature = "secondary_start") { @@ -272,7 +270,7 @@ fn psci_vcpu_on(vcpu: Vcpu, entry: usize, ctx: usize) { } // Todo: need to support more vcpu in one Core -pub fn psci_ipi_handler(msg: &IpiMessage) { +pub fn psci_ipi_handler(msg: IpiMessage) { match msg.ipi_message { IpiInnerMsg::Power(power_msg) => { if let PowerEvent::PsciIpiVcpuAssignAndCpuOn = power_msg.event { @@ -311,13 +309,11 @@ pub fn psci_ipi_handler(msg: &IpiMessage) { psci_vcpu_on(trgt_vcpu, power_msg.entry, power_msg.context); } PowerEvent::PsciIpiCpuOff => { - // TODO: 为什么ipi cpu off是当前vcpu shutdown,而vcpu shutdown 最后是把平台的物理核心shutdown - // 没有用到。不用管 - // current_cpu().active_vcpu.clone().unwrap().shutdown(); unimplemented!("PowerEvent::PsciIpiCpuOff") } PowerEvent::PsciIpiCpuReset => { - vcpu_arch_init(active_vm().unwrap(), current_cpu().active_vcpu.clone().unwrap()); + let vcpu = current_cpu().active_vcpu.as_ref().unwrap(); + vcpu.init_boot_info(active_vm().unwrap().config()); } _ => {} } diff --git a/src/arch/aarch64/smmu.rs b/src/arch/aarch64/smmu.rs index 88265308de936a0277d6a1b66da6d91a23c113ab..9011ae44acf821b7cbcf1aa7d0fbdda33f12b8ea 100644 --- a/src/arch/aarch64/smmu.rs +++ b/src/arch/aarch64/smmu.rs @@ -10,6 +10,8 @@ use alloc::vec::Vec; use core::mem::size_of; +use core::ops::Range; +use alloc::sync::Arc; use core::ptr; use spin::Mutex; @@ -18,7 +20,8 @@ use tock_registers::interfaces::*; use tock_registers::registers::*; use crate::board::PLAT_DESC; -use crate::device::EmuContext; +use crate::config::VmEmulatedDeviceConfig; +use crate::device::{EmuContext, EmuDeviceType, EmuDev}; use crate::kernel::VM_NUM_MAX; use crate::kernel::Vm; use crate::kernel::{active_vm, active_vm_id, current_cpu}; @@ -571,7 +574,7 @@ pub fn smmu_init() { SMMU_V2.call_once(|| Mutex::new(SmmuV2::new())); } -pub fn smmu_vm_init(vm: Vm) -> bool { +pub fn smmu_vm_init(vm: &Vm) -> bool { let mut smmu_v2 = SMMU_V2.get().unwrap().lock(); match smmu_v2.alloc_ctxbnk() { Some(context_id) => { @@ -583,6 +586,34 @@ pub fn smmu_vm_init(vm: Vm) -> bool { } } +pub struct EmuSmmu { + address_range: Range, +} + +impl EmuDev for EmuSmmu { + fn emu_type(&self) -> crate::device::EmuDeviceType { + EmuDeviceType::EmuDeviceTIOMMU + } + + fn address_range(&self) -> Range { + self.address_range.clone() + } + + fn handler(&self, emu_ctx: &EmuContext) -> bool { + emu_smmu_handler(0, emu_ctx) + } +} + +pub fn emu_smmu_init(emu_cfg: &VmEmulatedDeviceConfig) -> Result, ()> { + if emu_cfg.emu_type == EmuDeviceType::EmuDeviceTIOMMU { + Ok(Arc::new(EmuSmmu { + address_range: emu_cfg.base_ipa..emu_cfg.base_ipa + emu_cfg.length, + })) + } else { + Err(()) + } +} + pub fn smmu_add_device(context_id: usize, stream_id: usize) -> bool { let mut smmu_v2 = SMMU_V2.get().unwrap().lock(); let prep_id = (stream_id & bit_mask!(SMMU_SMR_ID_OFF, SMMU_SMR_ID_LEN)) as u16; diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs index 42ff29a61402cfb5b024cd6926ab02ea647632f5..97cb09c43d6216c7ba8e135f04bba740a374583a 100644 --- a/src/arch/aarch64/start.rs +++ b/src/arch/aarch64/start.rs @@ -228,3 +228,11 @@ unsafe extern "C" fn cache_invalidate(cache_level: usize) { options(nostack) ); } + +pub fn is_boot_core(cpu_id: usize) -> bool { + cpu_id == 0 +} + +pub fn boot_core() -> usize { + 0 +} diff --git a/src/arch/aarch64/vcpu.rs b/src/arch/aarch64/vcpu.rs index 23180fc97b652015aff597166a5d365f7d559ae8..5a65a45fc7830785eb0c688fab3f71f57b12b42e 100644 --- a/src/arch/aarch64/vcpu.rs +++ b/src/arch/aarch64/vcpu.rs @@ -10,26 +10,41 @@ use cortex_a::registers::*; +use crate::kernel::Vcpu; use crate::arch::traits::ContextFrameTrait; -use crate::kernel::{Vcpu, Vm}; +use crate::config::VmConfigEntry; use crate::kernel::VmType; /// vcpu init function for specific architecture -pub fn vcpu_arch_init(vm: Vm, vcpu: Vcpu) { - let config = vm.config(); - let mut vcpu_inner = vcpu.inner.inner_mut.lock(); - match config.os_type { - VmType::VmTOs => { - vcpu_inner.vcpu_ctx.set_argument(config.device_tree_load_ipa()); - } - _ => { - let arg = &config.memory_region()[0]; - vcpu_inner.vcpu_ctx.set_argument(arg.ipa_start + arg.length); - } +impl Vcpu { + /// init boot info, including argument and exception pc + pub fn init_boot_info(&self, config: &VmConfigEntry) { + let arg = match config.os_type { + VmType::VmTOs => config.device_tree_load_ipa(), + _ => { + let arg = &config.memory_region()[0]; + arg.ipa_start + arg.length + } + }; + let mut inner = self.inner.inner_mut.lock(); + inner.vcpu_ctx.set_argument(arg); + inner.vcpu_ctx.set_exception_pc(config.kernel_entry_point()); } - vcpu_inner.vcpu_ctx.set_exception_pc(config.kernel_entry_point()); - vcpu_inner.vcpu_ctx.spsr = - (SPSR_EL1::M::EL1h + SPSR_EL1::I::Masked + SPSR_EL1::F::Masked + SPSR_EL1::A::Masked + SPSR_EL1::D::Masked) - .value; + pub fn set_gich_ctlr(&self, ctlr: u32) { + let mut inner = self.inner.inner_mut.lock(); + inner.vm_ctx.gic_state.ctlr = ctlr; + } + + pub fn set_hcr(&self, hcr: u64) { + let mut inner = self.inner.inner_mut.lock(); + inner.vm_ctx.hcr_el2 = hcr; + } + + pub fn init_spsr(&self) { + let mut vcpu_inner = self.inner.inner_mut.lock(); + vcpu_inner.vcpu_ctx.spsr = + (SPSR_EL1::M::EL1h + SPSR_EL1::I::Masked + SPSR_EL1::F::Masked + SPSR_EL1::A::Masked + SPSR_EL1::D::Masked) + .value; + } } diff --git a/src/arch/aarch64/vgic.rs b/src/arch/aarch64/vgic.rs index 46d03edf726b23b18c3e55c393864ce30a27a0c0..7ef61304c7beee1c93cd8f67babda2e23cccac7c 100644 --- a/src/arch/aarch64/vgic.rs +++ b/src/arch/aarch64/vgic.rs @@ -7,17 +7,21 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. - +use core::ptr::NonNull; +use core::cell::{Cell, RefCell}; use alloc::collections::VecDeque; use alloc::sync::Arc; use alloc::vec::Vec; +use core::sync::atomic::{AtomicU32, Ordering}; +use core::ops::Range; use spin::Mutex; use crate::{arch::GICH, kernel::IpiInitcMessage}; +use crate::config::VmEmulatedDeviceConfig; use crate::board::{Platform, PlatOperation}; use crate::device::EmuContext; -use crate::device::EmuDevs; +use crate::device::{EmuDev, EmuDeviceType}; use crate::kernel::{active_vcpu_id, current_cpu, restore_vcpu_gic, save_vcpu_gic}; use crate::kernel::{active_vm, active_vm_id, active_vm_ncpu}; use crate::kernel::{ipi_intra_broadcast_msg, ipi_send_msg, IpiInnerMsg, IpiMessage, IpiType}; @@ -25,24 +29,42 @@ use crate::kernel::{InitcEvent, Vcpu, Vm}; use crate::utils::{bit_extract, bit_get, bit_set, bitmap_find_nth}; use super::gic::*; -#[derive(Clone)] + +// GICv2 interrupt structure struct VgicInt { - inner: Arc>, - pub lock: Arc>, + inner_const: VgicIntInnerConst, + inner: Mutex, + pub lock: Mutex<()>, +} + +struct VgicIntInnerConst { + id: u16, + hw: Cell, } +// SAFETY: VgicIntInnerConst hw is only set when initializing +unsafe impl Sync for VgicIntInnerConst {} + impl VgicInt { - fn new(id: usize) -> VgicInt { - VgicInt { - inner: Arc::new(Mutex::new(VgicIntInner::new(id))), - lock: Arc::new(Mutex::new(())), + fn new(id: usize) -> Self { + Self { + inner_const: VgicIntInnerConst { + id: (id + GIC_PRIVINT_NUM) as u16, + hw: Cell::new(false), + }, + inner: Mutex::new(VgicIntInnerMut::new()), + lock: Mutex::new(()), } } - fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool) -> VgicInt { - VgicInt { - inner: Arc::new(Mutex::new(VgicIntInner::priv_new(id, owner, targets, enabled))), - lock: Arc::new(Mutex::new(())), + fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool) -> Self { + Self { + inner_const: VgicIntInnerConst { + id: id as u16, + hw: Cell::new(false), + }, + inner: Mutex::new(VgicIntInnerMut::priv_new(owner, targets, enabled)), + lock: Mutex::new(()), } } @@ -78,9 +100,6 @@ impl VgicInt { fn set_targets(&self, targets: u8) { let mut vgic_int = self.inner.lock(); - // if vgic_int.targets == 1 && vgic_int.id == 48 { - // panic!("set targets to {}", targets); - // } vgic_int.targets = targets; } @@ -111,8 +130,7 @@ impl VgicInt { } fn set_hw(&self, hw: bool) { - let mut vgic_int = self.inner.lock(); - vgic_int.hw = hw; + self.inner_const.hw.set(hw); } fn set_cfg(&self, cfg: u8) { @@ -130,9 +148,9 @@ impl VgicInt { vgic_int.in_lr } + #[inline] fn id(&self) -> u16 { - let vgic_int = self.inner.lock(); - vgic_int.id + self.inner_const.id } fn enabled(&self) -> bool { @@ -150,9 +168,9 @@ impl VgicInt { vgic_int.targets } + #[inline] fn hw(&self) -> bool { - let vgic_int = self.inner.lock(); - vgic_int.hw + self.inner_const.hw.get() } pub fn state(&self) -> IrqState { @@ -191,16 +209,21 @@ impl VgicInt { vgic_int.owner.as_ref().map(|owner| owner.vm_id()) } - fn owner_vm(&self) -> Vm { + fn owner_vm(&self) -> Arc { let vgic_int = self.inner.lock(); vgic_int.owner_vm() } + + fn locked_helper(&self, f: F) + where + F: FnOnce(&mut VgicIntInnerMut), + { + f(&mut self.inner.lock()); + } } -struct VgicIntInner { +struct VgicIntInnerMut { owner: Option, - id: u16, - hw: bool, in_lr: bool, lr: u16, enabled: bool, @@ -213,12 +236,10 @@ struct VgicIntInner { in_act: bool, } -impl VgicIntInner { - fn new(id: usize) -> VgicIntInner { - VgicIntInner { +impl VgicIntInnerMut { + fn new() -> Self { + Self { owner: None, - id: (id + GIC_PRIVINT_NUM) as u16, - hw: false, in_lr: false, lr: 0, enabled: false, @@ -231,11 +252,9 @@ impl VgicIntInner { } } - fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool) -> VgicIntInner { - VgicIntInner { + fn priv_new(owner: Vcpu, targets: usize, enabled: bool) -> Self { + Self { owner: Some(owner), - id: id as u16, - hw: false, in_lr: false, lr: 0, enabled, @@ -248,233 +267,218 @@ impl VgicIntInner { } } - fn owner_vm(&self) -> Vm { + fn owner_vm(&self) -> Arc { let owner = self.owner.as_ref().unwrap(); owner.vm().unwrap() } } struct Vgicd { - ctlr: u32, + // ctlr will be written among different cores, so we use AtomicU32 to guarantee thread safety + ctlr: AtomicU32, + // Others will be read only and only be written when initializings typer: u32, iidr: u32, interrupts: Vec, } impl Vgicd { - fn default() -> Vgicd { + fn new(cpu_num: usize) -> Vgicd { Vgicd { - ctlr: 0, - typer: 0, - iidr: 0, + ctlr: AtomicU32::new(0b10), + typer: (GICD.typer() & GICD_TYPER_CPUNUM_MSK as u32) + | (((cpu_num - 1) << GICD_TYPER_CPUNUM_OFF) & GICD_TYPER_CPUNUM_MSK) as u32, + iidr: GICD.iidr(), interrupts: Vec::new(), } } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct Sgis { pub pend: u8, pub act: u8, } -impl Sgis { - fn default() -> Sgis { - Sgis { pend: 0, act: 0 } - } +struct VgicCpuPriv { + interrupts: Vec, + inner_mut: RefCell, } -struct VgicCpuPriv { - // gich: GicHypervisorInterfaceBlock, +struct VgicCpuPrivMut { curr_lrs: [u16; GIC_LIST_REGS_NUM], sgis: [Sgis; GIC_SGIS_NUM], - interrupts: Vec, - pend_list: VecDeque, - act_list: VecDeque, + pend_list: VecDeque>, + act_list: VecDeque>, +} + +impl VgicCpuPrivMut { + fn queue_remove(list: &mut VecDeque>, interrupt: &VgicInt) { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + list.iter() + .position(|x| unsafe { x.as_ref().id() } == interrupt.id()) + .map(|i| list.remove(i)); + } + + fn pend_list_push(&mut self, interrupt: &VgicInt) { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + self.pend_list + .push_back(unsafe { NonNull::new_unchecked(interrupt as *const _ as *mut _) }); + } + + fn pend_list_remove(&mut self, interrupt: &VgicInt) { + Self::queue_remove(&mut self.pend_list, interrupt); + } + + fn act_list_push(&mut self, interrupt: &VgicInt) { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + self.act_list + .push_back(unsafe { NonNull::new_unchecked(interrupt as *const _ as *mut _) }); + } + + fn act_list_remove(&mut self, interrupt: &VgicInt) { + Self::queue_remove(&mut self.act_list, interrupt); + } } +// SAFETY: VgicCpuPriv is only accessed on one core +unsafe impl Send for VgicCpuPriv {} +unsafe impl Sync for VgicCpuPriv {} + impl VgicCpuPriv { fn default() -> VgicCpuPriv { VgicCpuPriv { - curr_lrs: [0; GIC_LIST_REGS_NUM], - sgis: [Sgis::default(); GIC_SGIS_NUM], interrupts: Vec::new(), - pend_list: VecDeque::new(), - act_list: VecDeque::new(), + inner_mut: RefCell::new(VgicCpuPrivMut { + curr_lrs: [0; GIC_LIST_REGS_NUM], + sgis: [Sgis::default(); GIC_SGIS_NUM], + pend_list: VecDeque::new(), + act_list: VecDeque::new(), + }), } } } pub struct Vgic { - vgicd: Mutex, - cpu_priv: Mutex>, + address_range: Range, + vgicd: Vgicd, + cpu_priv: Vec, } impl Vgic { - pub fn default() -> Vgic { + pub fn new(base: usize, length: usize, cpu_num: usize) -> Vgic { Vgic { - vgicd: Mutex::new(Vgicd::default()), - cpu_priv: Mutex::new(Vec::new()), - } - } - - fn remove_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { - let mut cpu_priv = self.cpu_priv.lock(); - let vcpu_id = vcpu.id(); - let int_id = interrupt.id(); - if is_pend { - if !interrupt.in_pend() { - // println!("why int {} in pend is false but in pend list", int_id); - return; - } - for i in 0..cpu_priv[vcpu_id].pend_list.len() { - if cpu_priv[vcpu_id].pend_list[i].id() == int_id { - // if int_id == 297 { - // println!("remove int {} in pend list[{}]", int_id, i); - // } - cpu_priv[vcpu_id].pend_list.remove(i); - break; - } - } - interrupt.set_in_pend_state(false); - } else { - if !interrupt.in_act() { - return; - } - for i in 0..cpu_priv[vcpu_id].act_list.len() { - if cpu_priv[vcpu_id].act_list[i].id() == int_id { - cpu_priv[vcpu_id].act_list.remove(i); - break; - } - } - interrupt.set_in_act_state(false); - }; - } - - fn add_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { - let mut cpu_priv = self.cpu_priv.lock(); - let vcpu_id = vcpu.id(); - if is_pend { - interrupt.set_in_pend_state(true); - cpu_priv[vcpu_id].pend_list.push_back(interrupt); - } else { - interrupt.set_in_act_state(true); - cpu_priv[vcpu_id].act_list.push_back(interrupt); + address_range: base..base + length, + vgicd: Vgicd::new(cpu_num), + cpu_priv: Vec::new(), } } - fn update_int_list(&self, vcpu: Vcpu, interrupt: VgicInt) { - let state = interrupt.state().to_num(); + fn update_int_list(&self, vcpu: &Vcpu, interrupt: &VgicInt) { + // Every vcpu has its own cpu_priv, so we can use vcpu.id() to index cpu_priv + let mut cpu_priv = self.cpu_priv[vcpu.id()].inner_mut.borrow_mut(); - if state & 1 != 0 && !interrupt.in_pend() { - self.add_int_list(vcpu.clone(), interrupt.clone(), true); - } else if state & 1 == 0 { - self.remove_int_list(vcpu.clone(), interrupt.clone(), true); - } + interrupt.locked_helper(|int| { + let state = int.state.to_num(); - if state & 2 != 0 && !interrupt.in_act() { - self.add_int_list(vcpu.clone(), interrupt.clone(), false); - } else if state & 2 == 0 { - self.remove_int_list(vcpu.clone(), interrupt.clone(), false); - } + if state & IrqState::IrqSPend.to_num() != 0 && !int.in_pend { + cpu_priv.pend_list_push(interrupt); + int.in_pend = true; + } else if state & IrqState::IrqSPend.to_num() == 0 { + cpu_priv.pend_list_remove(interrupt); + int.in_pend = false; + } - if interrupt.id() < GIC_SGIS_NUM as u16 - && self.cpu_priv_sgis_pend(vcpu.id(), interrupt.id() as usize) != 0 - && !interrupt.in_pend() - { - self.add_int_list(vcpu, interrupt, true); - } + if state & IrqState::IrqSActive.to_num() != 0 && !int.in_act { + cpu_priv.act_list_push(interrupt); + int.in_act = true; + } else if state & IrqState::IrqSActive.to_num() == 0 { + cpu_priv.act_list_remove(interrupt); + int.in_act = false; + } + }); } - fn int_list_head(&self, vcpu: Vcpu, is_pend: bool) -> Option { - let cpu_priv = self.cpu_priv.lock(); + fn int_list_head(&self, vcpu: &Vcpu, is_pend: bool) -> Option<&VgicInt> { let vcpu_id = vcpu.id(); + let cpu_priv = self.cpu_priv[vcpu_id].inner_mut.borrow(); if is_pend { - if cpu_priv[vcpu_id].pend_list.is_empty() { - None - } else { - Some(cpu_priv[vcpu_id].pend_list[0].clone()) - } - } else if cpu_priv[vcpu_id].act_list.is_empty() { - None + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + cpu_priv.pend_list.front().cloned().map(|x| unsafe { x.as_ref() }) } else { - Some(cpu_priv[vcpu_id].act_list[0].clone()) + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + cpu_priv.act_list.front().cloned().map(|x| unsafe { x.as_ref() }) } } fn set_vgicd_ctlr(&self, ctlr: u32) { - let mut vgicd = self.vgicd.lock(); - vgicd.ctlr = ctlr; + self.vgicd.ctlr.store(ctlr, Ordering::Relaxed); } pub fn vgicd_ctlr(&self) -> u32 { - let vgicd = self.vgicd.lock(); - vgicd.ctlr + self.vgicd.ctlr.load(Ordering::Relaxed) } pub fn vgicd_typer(&self) -> u32 { - let vgicd = self.vgicd.lock(); - vgicd.typer + self.vgicd.typer } pub fn vgicd_iidr(&self) -> u32 { - let vgicd = self.vgicd.lock(); - vgicd.iidr + self.vgicd.iidr } - fn cpu_priv_interrupt(&self, cpu_id: usize, idx: usize) -> VgicInt { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].interrupts[idx].clone() + fn cpu_priv_interrupt(&self, cpu_id: usize, idx: usize) -> Option<&VgicInt> { + self.cpu_priv[cpu_id].interrupts.get(idx) } fn cpu_priv_curr_lrs(&self, cpu_id: usize, idx: usize) -> u16 { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].curr_lrs[idx] + let cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow(); + cpu_priv.curr_lrs[idx] } fn cpu_priv_sgis_pend(&self, cpu_id: usize, idx: usize) -> u8 { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].pend + let cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow(); + cpu_priv.sgis[idx].pend } fn cpu_priv_sgis_act(&self, cpu_id: usize, idx: usize) -> u8 { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].act + let cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow(); + cpu_priv.sgis[idx].act } fn set_cpu_priv_curr_lrs(&self, cpu_id: usize, idx: usize, val: u16) { - let mut cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].curr_lrs[idx] = val; + let mut cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow_mut(); + cpu_priv.curr_lrs[idx] = val; } fn set_cpu_priv_sgis_pend(&self, cpu_id: usize, idx: usize, pend: u8) { - let mut cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].pend = pend; + let mut cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow_mut(); + cpu_priv.sgis[idx].pend = pend; } fn set_cpu_priv_sgis_act(&self, cpu_id: usize, idx: usize, act: u8) { - let mut cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].act = act; + let mut cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow_mut(); + cpu_priv.sgis[idx].act = act; } - fn vgicd_interrupt(&self, idx: usize) -> VgicInt { - let vgicd = self.vgicd.lock(); - vgicd.interrupts[idx].clone() + fn vgicd_interrupt(&self, idx: usize) -> Option<&VgicInt> { + self.vgicd.interrupts.get(idx) } - fn get_int(&self, vcpu: Vcpu, int_id: usize) -> Option { + fn get_int(&self, vcpu: &Vcpu, int_id: usize) -> Option<&VgicInt> { if int_id < GIC_PRIVINT_NUM { let vcpu_id = vcpu.id(); - return Some(self.cpu_priv_interrupt(vcpu_id, int_id)); + self.cpu_priv_interrupt(vcpu_id, int_id) } else if (GIC_PRIVINT_NUM..GIC_INTS_MAX).contains(&int_id) { - return Some(self.vgicd_interrupt(int_id - GIC_PRIVINT_NUM)); + self.vgicd_interrupt(int_id - GIC_PRIVINT_NUM) + } else { + None } - None } - fn remove_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { - if !vgic_owns(vcpu.clone(), interrupt.clone()) { + fn remove_lr(&self, vcpu: &Vcpu, interrupt: &VgicInt) -> bool { + if !vgic_owns(vcpu, interrupt) { return false; } let int_lr = interrupt.lr(); @@ -486,7 +490,7 @@ impl Vgic { } let mut lr_val = 0; - if let Some(lr) = gich_get_lr(interrupt.clone()) { + if let Some(lr) = gich_get_lr(interrupt) { GICH.set_lr(int_lr as usize, 0); lr_val = lr; } @@ -505,10 +509,9 @@ impl Vgic { } } - self.update_int_list(vcpu, interrupt.clone()); + self.update_int_list(vcpu, interrupt); if (interrupt.state().to_num() & 1 != 0) && interrupt.enabled() { - // println!("remove_lr: interrupt_state {}", interrupt.state().to_num()); let hcr = GICH.hcr(); /* NPIE, bit [3] @@ -530,7 +533,7 @@ impl Vgic { false } - fn add_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { + fn add_lr(&self, vcpu: &Vcpu, interrupt: &VgicInt) -> bool { if !interrupt.enabled() || interrupt.in_lr() { return false; } @@ -580,17 +583,15 @@ impl Vgic { } if let Some(idx) = lr_ind { - let spilled_int = self - .get_int(vcpu.clone(), GICH.lr(idx) as usize & 0b1111111111) - .unwrap(); + let spilled_int = self.get_int(vcpu, GICH.lr(idx) as usize & 0b1111111111).unwrap(); if spilled_int.id() != interrupt.id() { let spilled_int_lock = spilled_int.lock.lock(); - self.remove_lr(vcpu.clone(), spilled_int.clone()); - vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + self.remove_lr(vcpu, spilled_int); + vgic_int_yield_owner(vcpu, spilled_int); drop(spilled_int_lock); } else { - self.remove_lr(vcpu.clone(), spilled_int.clone()); - vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + self.remove_lr(vcpu, spilled_int); + vgic_int_yield_owner(vcpu, spilled_int); } } } @@ -612,39 +613,30 @@ impl Vgic { false } - fn write_lr(&self, vcpu: Vcpu, interrupt: VgicInt, lr_ind: usize) { + fn write_lr(&self, vcpu: &Vcpu, interrupt: &VgicInt, lr_ind: usize) { let vcpu_id = vcpu.id(); let int_id = interrupt.id() as usize; let int_prio = interrupt.prio(); let prev_int_id = self.cpu_priv_curr_lrs(vcpu_id, lr_ind) as usize; if prev_int_id != int_id { - let prev_interrupt_option = self.get_int(vcpu.clone(), prev_int_id); - if let Some(prev_interrupt) = prev_interrupt_option { + if let Some(prev_interrupt) = self.get_int(vcpu, prev_int_id) { let prev_interrupt_lock = prev_interrupt.lock.lock(); - // println!( - // "write_lr: Core {} get int {} lock", - // current_cpu().id, - // prev_interrupt.id() - // ); - if vgic_owns(vcpu.clone(), prev_interrupt.clone()) - && prev_interrupt.in_lr() - && prev_interrupt.lr() == lr_ind as u16 - { + if vgic_owns(vcpu, prev_interrupt) && prev_interrupt.in_lr() && prev_interrupt.lr() == lr_ind as u16 { prev_interrupt.set_in_lr(false); let prev_id = prev_interrupt.id() as usize; if !gic_is_priv(prev_id) { - vgic_int_yield_owner(vcpu.clone(), prev_interrupt.clone()); + vgic_int_yield_owner(vcpu, prev_interrupt); } } drop(prev_interrupt_lock); } } - let state = vgic_get_state(interrupt.clone()); + let state = vgic_get_state(interrupt); let mut lr = (int_id & 0b1111111111) | (((int_prio as usize >> 3) & 0b11111) << 23); - if vgic_int_is_hw(interrupt.clone()) { + if vgic_int_is_hw(interrupt) { lr |= 1 << 31; lr |= (0b1111111111 & int_id) << 10; if state == 3 { @@ -678,23 +670,25 @@ impl Vgic { lr |= 1 << 19; } } else { - if !gic_is_priv(int_id) && !vgic_int_is_hw(interrupt.clone()) { + if !gic_is_priv(int_id) && !vgic_int_is_hw(interrupt) { lr |= 1 << 19; } lr |= (state & 0b11) << 28; } - interrupt.set_state(IrqState::IrqSInactive); - interrupt.set_in_lr(true); - interrupt.set_lr(lr_ind as u16); + interrupt.locked_helper(|int| { + int.state = IrqState::IrqSInactive; + int.in_lr = true; + int.lr = lr_ind as u16; + }); self.set_cpu_priv_curr_lrs(vcpu_id, lr_ind, int_id as u16); GICH.set_lr(lr_ind, lr as u32); self.update_int_list(vcpu, interrupt); } - fn route(&self, vcpu: Vcpu, interrupt: VgicInt) { + fn route(&self, vcpu: &Vcpu, interrupt: &VgicInt) { let cpu_id = current_cpu().id; if let IrqState::IrqSInactive = interrupt.state() { return; @@ -706,7 +700,7 @@ impl Vgic { let int_targets = interrupt.targets(); if (int_targets & (1 << cpu_id)) != 0 { - self.add_lr(vcpu.clone(), interrupt.clone()); + self.add_lr(vcpu, interrupt); } if !interrupt.in_lr() && (int_targets & !(1 << cpu_id)) != 0 { @@ -719,30 +713,30 @@ impl Vgic { val: 0, }; vgic_int_yield_owner(vcpu, interrupt); - ipi_intra_broadcast_msg(active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(ipi_msg)); + ipi_intra_broadcast_msg(&active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(ipi_msg)); } } - fn set_enable(&self, vcpu: Vcpu, int_id: usize, en: bool) { + fn set_enable(&self, vcpu: &Vcpu, int_id: usize, en: bool) { if int_id < GIC_SGIS_NUM { return; } - match self.get_int(vcpu.clone(), int_id) { + match self.get_int(vcpu, int_id) { Some(interrupt) => { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { if interrupt.enabled() ^ en { interrupt.set_enabled(en); if !interrupt.enabled() { - self.remove_lr(vcpu.clone(), interrupt.clone()); + self.remove_lr(vcpu, interrupt); } else { - self.route(vcpu.clone(), interrupt.clone()); + self.route(vcpu, interrupt); } if interrupt.hw() { GICD.set_enable(interrupt.id() as usize, en); } } - vgic_int_yield_owner(vcpu, interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } else { let int_phys_id = interrupt.owner_phys_id().unwrap(); let vcpu_vm_id = vcpu.vm_id(); @@ -767,23 +761,21 @@ impl Vgic { } } - fn get_enable(&self, vcpu: Vcpu, int_id: usize) -> bool { + fn get_enable(&self, vcpu: &Vcpu, int_id: usize) -> bool { self.get_int(vcpu, int_id).unwrap().enabled() } - fn set_pend(&self, vcpu: Vcpu, int_id: usize, pend: bool) { + fn set_pend(&self, vcpu: &Vcpu, int_id: usize, pend: bool) { // TODO: sgi_get_pend ? if bit_extract(int_id, 0, 10) < GIC_SGIS_NUM { self.sgi_set_pend(vcpu, int_id, pend); return; } - let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); - - if let Some(interrupt) = interrupt_option { + if let Some(interrupt) = self.get_int(vcpu, bit_extract(int_id, 0, 10)) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { - self.remove_lr(vcpu.clone(), interrupt.clone()); + if vgic_int_get_owner(vcpu, interrupt) { + self.remove_lr(vcpu, interrupt); let state = interrupt.state().to_num(); if pend && ((state & 1) == 0) { @@ -791,15 +783,15 @@ impl Vgic { } else if !pend && (state & 1) != 0 { interrupt.set_state(IrqState::num_to_state(state & !1)); } - self.update_int_list(vcpu.clone(), interrupt.clone()); + self.update_int_list(vcpu, interrupt); let state = interrupt.state().to_num(); if interrupt.hw() { let vgic_int_id = interrupt.id() as usize; GICD.set_state(vgic_int_id, if state == 1 { 2 } else { state }) } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu, interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); drop(interrupt_lock); } else { let vm_id = vcpu.vm_id(); @@ -834,27 +826,26 @@ impl Vgic { } } - fn set_active(&self, vcpu: Vcpu, int_id: usize, act: bool) { - let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); - if let Some(interrupt) = interrupt_option { + fn set_active(&self, vcpu: &Vcpu, int_id: usize, act: bool) { + if let Some(interrupt) = self.get_int(vcpu, bit_extract(int_id, 0, 10)) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { - self.remove_lr(vcpu.clone(), interrupt.clone()); + if vgic_int_get_owner(vcpu, interrupt) { + self.remove_lr(vcpu, interrupt); let state = interrupt.state().to_num(); if act && ((state & 2) == 0) { interrupt.set_state(IrqState::num_to_state(state | 2)); } else if !act && (state & 2) != 0 { interrupt.set_state(IrqState::num_to_state(state & !2)); } - self.update_int_list(vcpu.clone(), interrupt.clone()); + self.update_int_list(vcpu, interrupt); let state = interrupt.state().to_num(); if interrupt.hw() { let vgic_int_id = interrupt.id() as usize; GICD.set_state(vgic_int_id, if state == 1 { 2 } else { state }) } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu, interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); } else { let vm_id = vcpu.vm_id(); @@ -876,16 +867,15 @@ impl Vgic { } } - fn set_icfgr(&self, vcpu: Vcpu, int_id: usize, cfg: u8) { - let interrupt_option = self.get_int(vcpu.clone(), int_id); - if let Some(interrupt) = interrupt_option { + fn set_icfgr(&self, vcpu: &Vcpu, int_id: usize, cfg: u8) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { interrupt.set_cfg(cfg); if interrupt.hw() { GICD.set_icfgr(interrupt.id() as usize, cfg); } - vgic_int_yield_owner(vcpu, interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } else { let m = IpiInitcMessage { event: InitcEvent::VgicdSetCfg, @@ -911,26 +901,24 @@ impl Vgic { } } - fn get_icfgr(&self, vcpu: Vcpu, int_id: usize) -> u8 { - let interrupt_option = self.get_int(vcpu, int_id); - if let Some(interrupt) = interrupt_option { + fn get_icfgr(&self, vcpu: &Vcpu, int_id: usize) -> u8 { + if let Some(interrupt) = self.get_int(vcpu, int_id) { interrupt.cfg() } else { unimplemented!(); } } - fn sgi_set_pend(&self, vcpu: Vcpu, int_id: usize, pend: bool) { + fn sgi_set_pend(&self, vcpu: &Vcpu, int_id: usize, pend: bool) { if bit_extract(int_id, 0, 10) > GIC_SGIS_NUM { return; } - let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); let source = bit_extract(int_id, 10, 5); - if let Some(interrupt) = interrupt_option { + if let Some(interrupt) = self.get_int(vcpu, bit_extract(int_id, 0, 10)) { let interrupt_lock = interrupt.lock.lock(); - self.remove_lr(vcpu.clone(), interrupt.clone()); + self.remove_lr(vcpu, interrupt); let vcpu_id = vcpu.id(); let vgic_int_id = interrupt.id() as usize; @@ -950,15 +938,14 @@ impl Vgic { interrupt.set_state(IrqState::num_to_state(state & !1)); } - self.update_int_list(vcpu.clone(), interrupt.clone()); + self.update_int_list(vcpu, interrupt); - // println!("state {}", interrupt.state().to_num()); match interrupt.state() { IrqState::IrqSInactive => { debug!("inactive"); } _ => { - self.add_lr(vcpu, interrupt.clone()); + self.add_lr(vcpu, interrupt); } } } @@ -968,25 +955,24 @@ impl Vgic { } } - fn set_prio(&self, vcpu: Vcpu, int_id: usize, mut prio: u8) { - let interrupt_option = self.get_int(vcpu.clone(), int_id); - prio &= 0xf0; // gic-400 only allows 4 priority bits in non-secure state + fn set_prio(&self, vcpu: &Vcpu, int_id: usize, mut prio: u8) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { + prio &= 0xf0; // gic-400 only allows 4 priority bits in non-secure state - if let Some(interrupt) = interrupt_option { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { if interrupt.prio() != prio { - self.remove_lr(vcpu.clone(), interrupt.clone()); + self.remove_lr(vcpu, interrupt); let prev_prio = interrupt.prio(); interrupt.set_prio(prio); if prio <= prev_prio { - self.route(vcpu.clone(), interrupt.clone()); + self.route(vcpu, interrupt); } if interrupt.hw() { GICD.set_prio(interrupt.id() as usize, prio); } } - vgic_int_yield_owner(vcpu, interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } else { let vm_id = vcpu.vm_id(); @@ -1012,16 +998,14 @@ impl Vgic { } } - fn get_prio(&self, vcpu: Vcpu, int_id: usize) -> u8 { - let interrupt_option = self.get_int(vcpu, int_id); - interrupt_option.unwrap().prio() + fn get_prio(&self, vcpu: &Vcpu, int_id: usize) -> u8 { + self.get_int(vcpu, int_id).unwrap().prio() } - fn set_trgt(&self, vcpu: Vcpu, int_id: usize, trgt: u8) { - let interrupt_option = self.get_int(vcpu.clone(), int_id); - if let Some(interrupt) = interrupt_option { + fn set_trgt(&self, vcpu: &Vcpu, int_id: usize, trgt: u8) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { if interrupt.targets() != trgt { interrupt.set_targets(trgt); let mut ptrgt = 0; @@ -1033,11 +1017,11 @@ impl Vgic { if interrupt.hw() { GICD.set_trgt(interrupt.id() as usize, ptrgt as u8); } - if vgic_get_state(interrupt.clone()) != 0 { - self.route(vcpu.clone(), interrupt.clone()); + if vgic_get_state(interrupt) != 0 { + self.route(vcpu, interrupt); } } - vgic_int_yield_owner(vcpu, interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } else { let vm_id = vcpu.vm_id(); let m = IpiInitcMessage { @@ -1062,22 +1046,21 @@ impl Vgic { } } - fn get_trgt(&self, vcpu: Vcpu, int_id: usize) -> u8 { - let interrupt_option = self.get_int(vcpu, int_id); - interrupt_option.unwrap().targets() + fn get_trgt(&self, vcpu: &Vcpu, int_id: usize) -> u8 { + self.get_int(vcpu, int_id).unwrap().targets() } - pub fn inject(&self, vcpu: Vcpu, int_id: usize) { - // println!("Core {} inject int {} to vm{}", current_cpu().id, int_id, vcpu.vm_id()); - let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); - if let Some(interrupt) = interrupt_option { + pub fn inject(&self, vcpu: &Vcpu, int_id: usize) { + if let Some(interrupt) = self.get_int(vcpu, bit_extract(int_id, 0, 10)) { if interrupt.hw() { let interrupt_lock = interrupt.lock.lock(); - interrupt.set_owner(vcpu.clone()); - interrupt.set_state(IrqState::IrqSPend); - self.update_int_list(vcpu.clone(), interrupt.clone()); - interrupt.set_in_lr(false); - self.route(vcpu, interrupt.clone()); + interrupt.locked_helper(|interrupt| { + interrupt.owner = Some(vcpu.clone()); + interrupt.state = IrqState::IrqSPend; + interrupt.in_lr = false; + }); + self.update_int_list(vcpu, interrupt); + self.route(vcpu, interrupt); drop(interrupt_lock); } else { self.set_pend(vcpu, int_id, true); @@ -1105,7 +1088,7 @@ impl Vgic { int_id: 0, val: enable as u8, }; - ipi_intra_broadcast_msg(active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(m)); + ipi_intra_broadcast_msg(&active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(m)); } } else { let idx = emu_ctx.reg; @@ -1166,12 +1149,12 @@ impl Vgic { if emu_ctx.write { for i in 0..32 { if bit_get(val, i) != 0 { - self.set_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i, true); + self.set_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, true); } } } else { for i in 0..32 { - if self.get_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + if self.get_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { val |= 1 << i; } } @@ -1209,14 +1192,14 @@ impl Vgic { if emu_ctx.write { for i in 0..32 { if bit_get(val, i) != 0 { - self.set_pend(current_cpu().active_vcpu.clone().unwrap(), first_int + i, set); + self.set_pend(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, set); } } } else { for i in 0..32 { - match self.get_int(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + match self.get_int(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { Some(interrupt) => { - if vgic_get_state(interrupt.clone()) & 1 != 0 { + if vgic_get_state(interrupt) & 1 != 0 { val |= 1 << i; } } @@ -1266,14 +1249,14 @@ impl Vgic { if emu_ctx.write { for i in 0..32 { if bit_get(val, i) != 0 { - self.set_active(current_cpu().active_vcpu.clone().unwrap(), first_int + i, set); + self.set_active(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, set); } } } else { for i in 0..32 { - match self.get_int(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + match self.get_int(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { Some(interrupt) => { - if vgic_get_state(interrupt.clone()) & 2 != 0 { + if vgic_get_state(interrupt) & 2 != 0 { val |= 1 << i; } } @@ -1324,12 +1307,12 @@ impl Vgic { if emu_ctx.write { for i in 0..32 { if bit_get(val, i) != 0 { - self.set_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i, false); + self.set_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, false); } } } else { for i in 0..32 { - if self.get_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + if self.get_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { val |= 1 << i; } } @@ -1377,7 +1360,7 @@ impl Vgic { let mut bit = 0; while bit < emu_ctx.width * 8 { self.set_icfgr( - current_cpu().active_vcpu.clone().unwrap(), + current_cpu().active_vcpu.as_ref().unwrap(), irq, bit_extract(cfg as usize, bit, 2) as u8, ); @@ -1389,7 +1372,7 @@ impl Vgic { let mut irq = first_int; let mut bit = 0; while bit < emu_ctx.width * 8 { - cfg |= (self.get_icfgr(current_cpu().active_vcpu.clone().unwrap(), irq) as usize) << bit; + cfg |= (self.get_icfgr(current_cpu().active_vcpu.as_ref().unwrap(), irq) as usize) << bit; bit += 2; irq += 1; } @@ -1416,7 +1399,7 @@ impl Vgic { // println!("addr {:x}, sgir trglst flt {}, vtrgt {}", emu_ctx.address, sgir_trglstflt, bit_extract(val, 16, 8)); match sgir_trglstflt { 0 => { - trgtlist = vgic_target_translate(vm, bit_extract(val, 16, 8) as u32, true) as usize; + trgtlist = vgic_target_translate(&vm, bit_extract(val, 16, 8) as u32, true) as usize; } 1 => { trgtlist = active_vm_ncpu() & !(1 << current_cpu().id); @@ -1485,14 +1468,14 @@ impl Vgic { if emu_ctx.write { for i in 0..emu_ctx.width { self.set_prio( - current_cpu().active_vcpu.clone().unwrap(), + current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, bit_extract(val, GIC_PRIO_BITS * i, GIC_PRIO_BITS) as u8, ); } } else { for i in 0..emu_ctx.width { - val |= (self.get_prio(current_cpu().active_vcpu.clone().unwrap(), first_int + i) as usize) + val |= (self.get_prio(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) as usize) << (GIC_PRIO_BITS * i); } let idx = emu_ctx.reg; @@ -1506,30 +1489,26 @@ impl Vgic { let first_int = (8 / GIC_TARGET_BITS) * bit_extract(emu_ctx.address, 0, 9); if emu_ctx.write { - // println!("write"); - val = vgic_target_translate(active_vm().unwrap(), val as u32, true) as usize; + val = vgic_target_translate(&active_vm().unwrap(), val as u32, true) as usize; for i in 0..emu_ctx.width { self.set_trgt( - current_cpu().active_vcpu.clone().unwrap(), + current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, bit_extract(val, GIC_TARGET_BITS * i, GIC_TARGET_BITS) as u8, ); } } else { - // println!("read, first_int {}, width {}", first_int, emu_ctx.width); for i in 0..emu_ctx.width { - // println!("{}", self.get_trgt(active_vcpu().unwrap(), first_int + i)); - val |= (self.get_trgt(current_cpu().active_vcpu.clone().unwrap(), first_int + i) as usize) + val |= (self.get_trgt(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) as usize) << (GIC_TARGET_BITS * i); } - // println!("after read val {}", val); - val = vgic_target_translate(active_vm().unwrap(), val as u32, false) as usize; + val = vgic_target_translate(&active_vm().unwrap(), val as u32, false) as usize; let idx = emu_ctx.reg; current_cpu().set_gpr(idx, val); } } - fn handle_trapped_eoir(&self, vcpu: Vcpu) { + fn handle_trapped_eoir(&self, vcpu: &Vcpu) { let gic_lrs = gic_lrs(); let mut lr_idx_opt = bitmap_find_nth( GICH.eisr(0) as usize | ((GICH.eisr(1) as usize) << 32), @@ -1544,14 +1523,14 @@ impl Vgic { let lr_val = GICH.lr(lr_idx) as usize; GICH.set_lr(lr_idx, 0); - match self.get_int(vcpu.clone(), bit_extract(lr_val, 0, 10)) { + match self.get_int(vcpu, bit_extract(lr_val, 0, 10)) { Some(interrupt) => { let interrupt_lock = interrupt.lock.lock(); interrupt.set_in_lr(false); if (interrupt.id() as usize) < GIC_SGIS_NUM { - self.add_lr(vcpu.clone(), interrupt.clone()); + self.add_lr(vcpu, interrupt); } else { - vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } drop(interrupt_lock); } @@ -1569,7 +1548,7 @@ impl Vgic { } } - fn refill_lrs(&self, vcpu: Vcpu) { + fn refill_lrs(&self, vcpu: &Vcpu) { let gic_lrs = gic_lrs(); let mut has_pending = false; @@ -1589,21 +1568,21 @@ impl Vgic { ); while lr_idx_opt.is_some() { - let mut interrupt_opt: Option = None; + let mut interrupt_opt: Option<&VgicInt> = None; let mut prev_pend = false; - let act_head = self.int_list_head(vcpu.clone(), false); - let pend_head = self.int_list_head(vcpu.clone(), true); + let act_head = self.int_list_head(vcpu, false); + let pend_head = self.int_list_head(vcpu, true); if has_pending { if let Some(act_int) = act_head { if !act_int.in_lr() { - interrupt_opt = Some(act_int.clone()); + interrupt_opt = Some(act_int); } } } if interrupt_opt.is_none() { if let Some(pend_int) = pend_head { if !pend_int.in_lr() { - interrupt_opt = Some(pend_int.clone()); + interrupt_opt = Some(pend_int); prev_pend = true; } } @@ -1612,8 +1591,8 @@ impl Vgic { match interrupt_opt { Some(interrupt) => { // println!("refill int {}", interrupt.id()); - vgic_int_get_owner(vcpu.clone(), interrupt.clone()); - self.write_lr(vcpu.clone(), interrupt.clone(), lr_idx_opt.unwrap()); + vgic_int_get_owner(vcpu, interrupt); + self.write_lr(vcpu, interrupt, lr_idx_opt.unwrap()); has_pending = has_pending || prev_pend; } None => { @@ -1634,17 +1613,16 @@ impl Vgic { } } - fn eoir_highest_spilled_active(&self, vcpu: Vcpu) { - let interrupt = self.int_list_head(vcpu.clone(), false); - if let Some(int) = interrupt { + fn eoir_highest_spilled_active(&self, vcpu: &Vcpu) { + if let Some(int) = self.int_list_head(vcpu, false) { int.lock.lock(); - vgic_int_get_owner(vcpu.clone(), int.clone()); + vgic_int_get_owner(vcpu, int); let state = int.state().to_num(); int.set_state(IrqState::num_to_state(state & !2)); - self.update_int_list(vcpu.clone(), int.clone()); + self.update_int_list(vcpu, int); - if vgic_int_is_hw(int.clone()) { + if vgic_int_is_hw(int) { GICD.set_act(int.id() as usize, false); } else if int.state().to_num() & 1 != 0 { self.add_lr(vcpu, int); @@ -1653,7 +1631,7 @@ impl Vgic { } } -fn vgic_target_translate(vm: Vm, trgt: u32, v2p: bool) -> u32 { +fn vgic_target_translate(vm: &Vm, trgt: u32, v2p: bool) -> u32 { let from = trgt.to_le_bytes(); let mut result = 0; @@ -1676,13 +1654,10 @@ fn vgic_target_translate(vm: Vm, trgt: u32, v2p: bool) -> u32 { result } -fn vgic_owns(vcpu: Vcpu, interrupt: VgicInt) -> bool { +fn vgic_owns(vcpu: &Vcpu, interrupt: &VgicInt) -> bool { if gic_is_priv(interrupt.id() as usize) { return true; } - // if interrupt.owner().is_none() { - // return false; - // } let vcpu_id = vcpu.id(); let pcpu_id = vcpu.phys_id(); @@ -1690,33 +1665,17 @@ fn vgic_owns(vcpu: Vcpu, interrupt: VgicInt) -> bool { Some(owner) => { let owner_vcpu_id = owner.id(); let owner_pcpu_id = owner.phys_id(); - // println!( - // "return {}, arc same {}", - // owner_vcpu_id == vcpu_id && owner_pcpu_id == pcpu_id, - // result - // ); owner_vcpu_id == vcpu_id && owner_pcpu_id == pcpu_id } None => false, } - - // let tmp = interrupt.owner().unwrap(); - // let owner_vcpu_id = interrupt.owner_id(); - // let owner_pcpu_id = interrupt.owner_phys_id(); - // let owner_vm_id = interrupt.owner_vm_id(); - // println!("3: owner_vm_id {}", owner_vm_id); - - // let vcpu_vm_id = vcpu.vm_id(); - - // println!("return vgic_owns: vcpu_vm_id {}", vcpu_vm_id); - // return (owner_vcpu_id == vcpu_id && owner_vm_id == vcpu_vm_id); } -fn vgic_get_state(interrupt: VgicInt) -> usize { +fn vgic_get_state(interrupt: &VgicInt) -> usize { let mut state = interrupt.state().to_num(); if interrupt.in_lr() && interrupt.owner_phys_id().unwrap() == current_cpu().id { - let lr_option = gich_get_lr(interrupt.clone()); + let lr_option = gich_get_lr(interrupt); if let Some(lr_val) = lr_option { state = lr_val as usize; } @@ -1740,24 +1699,21 @@ fn vgic_get_state(interrupt: VgicInt) -> usize { state } -fn vgic_int_yield_owner(vcpu: Vcpu, interrupt: VgicInt) { - if !vgic_owns(vcpu, interrupt.clone()) { - return; - } - if gic_is_priv(interrupt.id() as usize) || interrupt.in_lr() { +fn vgic_int_yield_owner(vcpu: &Vcpu, interrupt: &VgicInt) { + if !vgic_owns(vcpu, interrupt) || interrupt.in_lr() || gic_is_priv(interrupt.id() as usize) { return; } - if vgic_get_state(interrupt.clone()) & 2 == 0 { + if vgic_get_state(interrupt) & 2 == 0 { interrupt.clear_owner(); } } -fn vgic_int_is_hw(interrupt: VgicInt) -> bool { +fn vgic_int_is_hw(interrupt: &VgicInt) -> bool { interrupt.id() as usize >= GIC_SGIS_NUM && interrupt.hw() } -fn gich_get_lr(interrupt: VgicInt) -> Option { +fn gich_get_lr(interrupt: &VgicInt) -> Option { let cpu_id = current_cpu().id; let phys_id = interrupt.owner_phys_id().unwrap(); @@ -1772,7 +1728,7 @@ fn gich_get_lr(interrupt: VgicInt) -> Option { None } -fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { +fn vgic_int_get_owner(vcpu: &Vcpu, interrupt: &VgicInt) -> bool { let vcpu_id = vcpu.id(); let vcpu_vm_id = vcpu.vm_id(); @@ -1784,15 +1740,10 @@ fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { owner_vm_id == vcpu_vm_id && owner_vcpu_id == vcpu_id } None => { - interrupt.set_owner(vcpu); + interrupt.set_owner(vcpu.clone()); true } } - - // let owner_vcpu_id = interrupt.owner_id().unwrap(); - // let owner_vm_id = interrupt.owner_vm_id().unwrap(); - - // return false; } pub fn gic_maintenance_handler() { @@ -1806,17 +1757,17 @@ pub fn gic_maintenance_handler() { let vgic = vm.vgic(); if misr & 1 != 0 { - vgic.handle_trapped_eoir(current_cpu().active_vcpu.clone().unwrap()); + vgic.handle_trapped_eoir(current_cpu().active_vcpu.as_ref().unwrap()); } if misr & (1 << 3) != 0 { - vgic.refill_lrs(current_cpu().active_vcpu.clone().unwrap()); + vgic.refill_lrs(current_cpu().active_vcpu.as_ref().unwrap()); } if misr & (1 << 2) != 0 { let mut hcr = GICH.hcr(); while hcr & (0b11111 << 27) != 0 { - vgic.eoir_highest_spilled_active(current_cpu().active_vcpu.clone().unwrap()); + vgic.eoir_highest_spilled_active(current_cpu().active_vcpu.as_ref().unwrap()); hcr -= 1 << 27; GICH.set_hcr(hcr); hcr = GICH.hcr(); @@ -1835,82 +1786,6 @@ const VGICD_REG_OFFSET_PREFIX_ICACTIVER: usize = 0x7; const VGICD_REG_OFFSET_PREFIX_ICFGR: usize = 0x18; const VGICD_REG_OFFSET_PREFIX_SGIR: usize = 0x1e; -pub fn emu_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - let offset = emu_ctx.address & 0xfff; - if emu_ctx.width > 4 { - return false; - } - - let vm = match crate::kernel::active_vm() { - None => { - panic!("emu_intc_handler: vm is None"); - } - Some(x) => x, - }; - let vgic = vm.vgic(); - let vgicd_offset_prefix = (offset & 0xf80) >> 7; - if !vgicd_emu_access_is_vaild(emu_ctx) { - return false; - } - - match vgicd_offset_prefix { - VGICD_REG_OFFSET_PREFIX_ISENABLER => { - vgic.emu_isenabler_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ISPENDR => { - vgic.emu_ispendr_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ISACTIVER => { - vgic.emu_isactiver_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICENABLER => { - vgic.emu_icenabler_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICPENDR => { - vgic.emu_icpendr_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICACTIVER => { - vgic.emu_icativer_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICFGR => { - vgic.emu_icfgr_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_SGIR => { - vgic.emu_sgiregs_access(emu_ctx); - } - _ => { - match offset { - // VGICD_REG_OFFSET(CTLR) - 0 => { - vgic.emu_ctrl_access(emu_ctx); - } - // VGICD_REG_OFFSET(TYPER) - 0x004 => { - vgic.emu_typer_access(emu_ctx); - } - // VGICD_REG_OFFSET(IIDR) - 0x008 => { - vgic.emu_iidr_access(emu_ctx); - } - _ => { - if !emu_ctx.write { - let idx = emu_ctx.reg; - let val = 0; - current_cpu().set_gpr(idx, val); - } - } - } - if (0x400..0x800).contains(&offset) { - vgic.emu_ipriorityr_access(emu_ctx); - } else if (0x800..0xc00).contains(&offset) { - vgic.emu_itargetr_access(emu_ctx); - } - } - } - // println!("finish emu_intc_handler"); - true -} - pub fn vgicd_emu_access_is_vaild(emu_ctx: &EmuContext) -> bool { let offset = emu_ctx.address & 0xfff; let offset_prefix = (offset & 0xf80) >> 7; @@ -1946,60 +1821,32 @@ pub fn vgicd_emu_access_is_vaild(emu_ctx: &EmuContext) -> bool { true } -pub fn partial_passthrough_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - if !vgicd_emu_access_is_vaild(emu_ctx) { - return false; - } - if emu_ctx.write { - // SAFETY: Emu_ctx.address is writeable in EL2 - unsafe { - emu_ctx.write(current_cpu().get_gpr(emu_ctx.reg)); - } - } else { - // SAFETY: Emu_ctx.address is readable in EL2 - current_cpu().set_gpr(emu_ctx.reg, unsafe { emu_ctx.read() }); - } +pub fn vgic_ipi_handler(msg: IpiMessage) { + if let IpiInnerMsg::Initc(intc) = msg.ipi_message { + let vm_id = intc.vm_id; + let int_id = intc.int_id; + let val = intc.val; + let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { + None => { + error!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); + return; + } + Some(vcpu) => vcpu, + }; + restore_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu.clone()); - true -} + let vm = match trgt_vcpu.vm() { + None => { + panic!("vgic_ipi_handler: vm is None"); + } + Some(x) => x, + }; + let vgic = vm.vgic(); -pub fn vgic_ipi_handler(msg: &IpiMessage) { - let vm_id; - let int_id; - let val; - match &msg.ipi_message { - IpiInnerMsg::Initc(intc) => { - vm_id = intc.vm_id; - int_id = intc.int_id; - val = intc.val; - } - _ => { - error!("vgic_ipi_handler: illegal ipi"); - return; - } - } - let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { - None => { - error!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); + if vm_id != vm.id() { + error!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); return; } - Some(vcpu) => vcpu, - }; - restore_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu.clone()); - - let vm = match trgt_vcpu.vm() { - None => { - panic!("vgic_ipi_handler: vm is None"); - } - Some(x) => x, - }; - let vgic = vm.vgic(); - - if vm_id != vm.id() { - error!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); - return; - } - if let IpiInnerMsg::Initc(intc) = &msg.ipi_message { match intc.event { InitcEvent::VgicdGichEn => { let hcr = GICH.hcr(); @@ -2010,26 +1857,25 @@ pub fn vgic_ipi_handler(msg: &IpiMessage) { } } InitcEvent::VgicdSetEn => { - vgic.set_enable(trgt_vcpu.clone(), int_id as usize, val != 0); + vgic.set_enable(trgt_vcpu, int_id as usize, val != 0); } InitcEvent::VgicdSetPend => { - vgic.set_pend(trgt_vcpu.clone(), int_id as usize, val != 0); + vgic.set_pend(trgt_vcpu, int_id as usize, val != 0); } InitcEvent::VgicdSetPrio => { - vgic.set_prio(trgt_vcpu.clone(), int_id as usize, val); + vgic.set_prio(trgt_vcpu, int_id as usize, val); } InitcEvent::VgicdSetTrgt => { - vgic.set_trgt(trgt_vcpu.clone(), int_id as usize, val); + vgic.set_trgt(trgt_vcpu, int_id as usize, val); } InitcEvent::VgicdRoute => { - let interrupt_option = vgic.get_int(trgt_vcpu.clone(), bit_extract(int_id as usize, 0, 10)); - if let Some(interrupt) = interrupt_option { + if let Some(interrupt) = vgic.get_int(trgt_vcpu, bit_extract(int_id as usize, 0, 10)) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(trgt_vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(trgt_vcpu, interrupt) { if (interrupt.targets() & (1 << current_cpu().id)) != 0 { - vgic.add_lr(trgt_vcpu.clone(), interrupt.clone()); + vgic.add_lr(trgt_vcpu, interrupt); } - vgic_int_yield_owner(trgt_vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(trgt_vcpu, interrupt); } drop(interrupt_lock); } @@ -2038,30 +1884,28 @@ pub fn vgic_ipi_handler(msg: &IpiMessage) { error!("vgic_ipi_handler: core {} received unknown event", current_cpu().id) } } + save_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu); + } else { + error!("vgic_ipi_handler: illegal ipi"); } - save_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu); } -pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { - let vgic_cpu_num = vm.config().cpu_num(); - vm.init_intc_mode(true); - - let vgic = Arc::new(Vgic::default()); +pub fn emu_intc_init(emu_cfg: &VmEmulatedDeviceConfig, vcpu_list: &[Vcpu]) -> Result, ()> { + if emu_cfg.emu_type != EmuDeviceType::EmuDeviceTGicd { + error!("emu_intc_init: emu_type is not EmuDeviceTGicd"); + return Err(()); + } - let mut vgicd = vgic.vgicd.lock(); - vgicd.typer = (GICD.typer() & GICD_TYPER_CPUNUM_MSK as u32) - | (((vm.cpu_num() - 1) << GICD_TYPER_CPUNUM_OFF) & GICD_TYPER_CPUNUM_MSK) as u32; - vgicd.iidr = GICD.iidr(); + let vcpu_num = vcpu_list.len(); + let mut vgic = Vgic::new(emu_cfg.base_ipa, emu_cfg.length, vcpu_num); for i in 0..GIC_SPI_MAX { - vgicd.interrupts.push(VgicInt::new(i)); + vgic.vgicd.interrupts.push(VgicInt::new(i)); } - drop(vgicd); - for i in 0..vgic_cpu_num { + for vcpu in vcpu_list { let mut cpu_priv = VgicCpuPriv::default(); for int_idx in 0..GIC_PRIVINT_NUM { - let vcpu = vm.vcpu(i).unwrap(); let phys_id = vcpu.phys_id(); cpu_priv.interrupts.push(VgicInt::priv_new( @@ -2072,18 +1916,13 @@ pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { )); } - let mut vgic_cpu_priv = vgic.cpu_priv.lock(); - vgic_cpu_priv.push(cpu_priv); + vgic.cpu_priv.push(cpu_priv); } - vm.set_emu_devs(emu_dev_id, EmuDevs::Vgic(vgic.clone())); -} - -pub fn partial_passthrough_intc_init(vm: Vm) { - vm.init_intc_mode(false); + Ok(Arc::new(vgic)) } -pub fn vgic_set_hw_int(vm: Vm, int_id: usize) { +pub fn vgic_set_hw_int(vm: &Vm, int_id: usize) { if int_id < GIC_SGIS_NUM { return; } @@ -2095,19 +1934,138 @@ pub fn vgic_set_hw_int(vm: Vm, int_id: usize) { if int_id < GIC_PRIVINT_NUM { for i in 0..vm.cpu_num() { - let interrupt_option = vgic.get_int(vm.vcpu(i).unwrap(), int_id); - if let Some(interrupt) = interrupt_option { + if let Some(interrupt) = vgic.get_int(vm.vcpu(i).unwrap(), int_id) { let interrupt_lock = interrupt.lock.lock(); interrupt.set_hw(true); drop(interrupt_lock); } } - } else { - let interrupt_option = vgic.get_int(vm.vcpu(0).unwrap(), int_id); - if let Some(interrupt) = interrupt_option { - let interrupt_lock = interrupt.lock.lock(); - interrupt.set_hw(true); - drop(interrupt_lock); + } else if let Some(interrupt) = vgic.get_int(vm.vcpu(0).unwrap(), int_id) { + let interrupt_lock = interrupt.lock.lock(); + interrupt.set_hw(true); + drop(interrupt_lock); + } +} + +impl EmuDev for Vgic { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTGicd + } + + fn address_range(&self) -> Range { + self.address_range.clone() + } + + fn handler(&self, emu_ctx: &EmuContext) -> bool { + let offset = emu_ctx.address & 0xfff; + + let vgicd_offset_prefix = offset >> 7; + if !vgicd_emu_access_is_vaild(emu_ctx) { + return false; } + + trace!( + "current_cpu:{} emu_intc_handler offset:{:#x} is write:{},val:{:#x}", + current_cpu().id, + emu_ctx.address, + emu_ctx.write, + current_cpu().get_gpr(emu_ctx.reg) + ); + match vgicd_offset_prefix { + VGICD_REG_OFFSET_PREFIX_ISENABLER => { + self.emu_isenabler_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ISPENDR => { + self.emu_ispendr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ISACTIVER => { + self.emu_isactiver_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICENABLER => { + self.emu_icenabler_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICPENDR => { + self.emu_icpendr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICACTIVER => { + self.emu_icativer_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICFGR => { + self.emu_icfgr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_SGIR => { + self.emu_sgiregs_access(emu_ctx); + } + _ => { + match offset { + // VGICD_REG_OFFSET(CTLR) + 0 => { + self.emu_ctrl_access(emu_ctx); + } + // VGICD_REG_OFFSET(TYPER) + 0x004 => { + self.emu_typer_access(emu_ctx); + } + // VGICD_REG_OFFSET(IIDR) + 0x008 => { + self.emu_iidr_access(emu_ctx); + } + _ => { + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, 0); + } + } + } + if (0x400..0x800).contains(&offset) { + self.emu_ipriorityr_access(emu_ctx); + } else if (0x800..0xc00).contains(&offset) { + self.emu_itargetr_access(emu_ctx); + } + } + } + true + } +} + +pub struct PartialPassthroughIntc { + address_range: Range, +} + +impl EmuDev for PartialPassthroughIntc { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTGPPT + } + + fn address_range(&self) -> Range { + self.address_range.clone() + } + + fn handler(&self, emu_ctx: &EmuContext) -> bool { + if !vgicd_emu_access_is_vaild(emu_ctx) { + return false; + } + + if emu_ctx.write { + // SAFETY: Emu_ctx.address is writeable in EL2 + unsafe { + emu_ctx.write(current_cpu().get_gpr(emu_ctx.reg)); + } + } else { + // SAFETY: Emu_ctx.address is readable in EL2 + current_cpu().set_gpr(emu_ctx.reg, unsafe { emu_ctx.read() }); + } + + true + } +} + +pub fn partial_passthrough_intc_init(emu_cfg: &VmEmulatedDeviceConfig) -> Result, ()> { + if emu_cfg.emu_type == EmuDeviceType::EmuDeviceTGPPT { + let intc = PartialPassthroughIntc { + address_range: emu_cfg.base_ipa..emu_cfg.base_ipa + emu_cfg.length, + }; + Ok(Arc::new(intc)) + } else { + Err(()) } } diff --git a/src/arch/aarch64/vgicv3.rs b/src/arch/aarch64/vgicv3.rs index 0539c26125e3b194b64e2c832d82d04fca760330..290cfee9498c088ac180cf5c4fecd504d0120582 100644 --- a/src/arch/aarch64/vgicv3.rs +++ b/src/arch/aarch64/vgicv3.rs @@ -7,8 +7,11 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. - +use core::ptr::NonNull; +use core::cell::{Cell, RefCell}; use core::mem::size_of; +use core::ops::Range; +use core::sync::atomic::{AtomicU32, Ordering}; use alloc::collections::VecDeque; use alloc::sync::Arc; @@ -19,8 +22,9 @@ use spin::Mutex; use crate::arch::{GICH, MPIDR_EL1}; use crate::arch::aarch64::regs::ReadableReg; use crate::board::PLAT_DESC; +use crate::config::VmEmulatedDeviceConfig; use crate::device::EmuContext; -use crate::device::EmuDevs; +use crate::device::{EmuDev, EmuDeviceType, EmuRegType, emu_register_reg}; use crate::kernel::{current_cpu, restore_vcpu_gic, save_vcpu_gic, cpuid2mpidr, IpiInitcMessage}; use crate::kernel::{active_vm, active_vm_id}; use crate::kernel::{ipi_intra_broadcast_msg, ipi_send_msg, IpiInnerMsg, IpiMessage, IpiType}; @@ -29,27 +33,41 @@ use crate::utils::{bit_extract, bit_get, bitmap_find_nth}; use super::gicv3::*; -#[derive(Clone)] /// GICv3 interrupt struct struct VgicInt { - inner: Arc>, - pub lock: Arc>, + inner_const: VgicIntInnerConst, + inner: Mutex, + pub lock: Mutex<()>, +} + +struct VgicIntInnerConst { + id: u16, + hw: Cell, } +// SAFETY: VgicIntInnerConst hw is only set when initializing +unsafe impl Sync for VgicIntInnerConst {} + impl VgicInt { - fn new(id: usize) -> VgicInt { - VgicInt { - inner: Arc::new(Mutex::new(VgicIntInner::new(id))), - lock: Arc::new(Mutex::new(())), + fn new(id: usize) -> Self { + Self { + inner_const: VgicIntInnerConst { + id: (id + GIC_PRIVINT_NUM) as u16, + hw: Cell::new(false), + }, + inner: Mutex::new(VgicIntInnerMut::new()), + lock: Mutex::new(()), } } - fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool, redist: usize, cfg: usize) -> VgicInt { - VgicInt { - inner: Arc::new(Mutex::new(VgicIntInner::priv_new( - id, owner, targets, enabled, redist, cfg, - ))), - lock: Arc::new(Mutex::new(())), + fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool, redist: usize, cfg: usize) -> Self { + Self { + inner_const: VgicIntInnerConst { + id: id as u16, + hw: Cell::new(false), + }, + inner: Mutex::new(VgicIntInnerMut::priv_new(owner, targets, enabled, redist, cfg)), + lock: Mutex::new(()), } } @@ -114,8 +132,7 @@ impl VgicInt { } fn set_hw(&self, hw: bool) { - let mut vgic_int = self.inner.lock(); - vgic_int.hw = hw; + self.inner_const.hw.set(hw); } fn set_cfg(&self, cfg: u8) { @@ -173,9 +190,9 @@ impl VgicInt { vgic_int.route = route as u64; } + #[inline] fn id(&self) -> u16 { - let vgic_int = self.inner.lock(); - vgic_int.id + self.inner_const.id } fn enabled(&self) -> bool { @@ -193,9 +210,9 @@ impl VgicInt { vgic_int.targets } + #[inline] fn hw(&self) -> bool { - let vgic_int = self.inner.lock(); - vgic_int.hw + self.inner_const.hw.get() } pub fn state(&self) -> IrqState { @@ -234,10 +251,17 @@ impl VgicInt { vgic_int.owner.as_ref().map(|owner| owner.vm_id()) } - fn owner_vm(&self) -> Vm { + fn owner_vm(&self) -> Arc { let vgic_int = self.inner.lock(); vgic_int.owner_vm() } + + fn locked_helper(&self, f: F) + where + F: FnOnce(&mut VgicIntInnerMut), + { + f(&mut self.inner.lock()); + } } #[derive(Clone)] @@ -246,12 +270,10 @@ enum VgicIntPhys { Route(u64), } -struct VgicIntInner { +struct VgicIntInnerMut { owner: Option, route: u64, phys: VgicIntPhys, - id: u16, - hw: bool, in_lr: bool, lr: u16, enabled: bool, @@ -264,14 +286,12 @@ struct VgicIntInner { in_act: bool, } -impl VgicIntInner { - fn new(id: usize) -> VgicIntInner { - VgicIntInner { +impl VgicIntInnerMut { + fn new() -> Self { + Self { owner: None, route: GICD_IROUTER_INV as u64, phys: VgicIntPhys::Route(GICD_IROUTER_INV as u64), - id: (id + GIC_PRIVINT_NUM) as u16, - hw: false, in_lr: false, lr: 0, enabled: false, @@ -284,13 +304,11 @@ impl VgicIntInner { } } - fn priv_new(id: usize, owner: Vcpu, targets: usize, enabled: bool, redist: usize, cfg: usize) -> VgicIntInner { - VgicIntInner { + fn priv_new(owner: Vcpu, targets: usize, enabled: bool, redist: usize, cfg: usize) -> Self { + Self { owner: Some(owner), route: GICD_IROUTER_INV as u64, phys: VgicIntPhys::Redist(redist as u64), - id: id as u16, - hw: false, in_lr: false, lr: 0, enabled, @@ -303,7 +321,7 @@ impl VgicIntInner { } } - fn owner_vm(&self) -> Vm { + fn owner_vm(&self) -> Arc { let owner = self.owner.as_ref().unwrap(); owner.vm().unwrap() } @@ -311,52 +329,46 @@ impl VgicIntInner { /// VGIC Distributor struct Vgicd { - ctlr: u32, + // ctlr will be written among different cores, so we use AtomicU32 to guarantee thread safety + ctlr: AtomicU32, + // Others will be read only and only be written when initializing typer: u32, iidr: u32, interrupts: Vec, } impl Vgicd { - fn default() -> Vgicd { + fn new(cpu_num: usize) -> Vgicd { Vgicd { - ctlr: 0, - typer: 0, - iidr: 0, + ctlr: AtomicU32::new(0b10), + typer: (GICD.typer() & !(GICD_TYPER_CPUNUM_MSK | GICD_TYPER_LPIS) as u32) + | ((((cpu_num - 1) << GICD_TYPER_CPUNUM_OFF) & GICD_TYPER_CPUNUM_MSK) as u32), + iidr: GICD.iidr(), interrupts: Vec::new(), } } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct Sgis { pub pend: u8, pub act: u8, } -impl Sgis { - fn default() -> Sgis { - Sgis { pend: 0, act: 0 } - } -} - /// VGIC Redistributor struct Vgicr { - inner: Arc>, - pub lock: Arc>, + inner: Mutex, } impl Vgicr { fn default() -> Vgicr { Vgicr { - inner: Arc::new(Mutex::new(VgicrInner::default())), - lock: Arc::new(Mutex::new(())), + inner: Mutex::new(VgicrInner::default()), } } fn new(typer: usize, cltr: usize, iidr: usize) -> Vgicr { Vgicr { - inner: Arc::new(Mutex::new(VgicrInner::new(typer, cltr, iidr))), - lock: Arc::new(Mutex::new(())), + inner: Mutex::new(VgicrInner::new(typer, cltr, iidr)), } } @@ -364,13 +376,9 @@ impl Vgicr { let vgicr = self.inner.lock(); vgicr.typer } - - pub fn set_typer(&self, typer: usize) { - let mut vgicr = self.inner.lock(); - vgicr.typer = typer as u64; - } } +#[derive(Default)] struct VgicrInner { typer: u64, cltr: u32, @@ -378,14 +386,6 @@ struct VgicrInner { } impl VgicrInner { - fn default() -> VgicrInner { - VgicrInner { - typer: 0, - cltr: 0, - iidr: 0, - } - } - fn new(typer: usize, cltr: usize, iidr: usize) -> VgicrInner { VgicrInner { typer: typer as u64, @@ -398,163 +398,161 @@ impl VgicrInner { /// VGIC CPU Private data struct VgicCpuPriv { vigcr: Vgicr, - // gich: GicHypervisorInterfaceBlock, + interrupts: Vec, + inner_mut: RefCell, +} + +struct VgicCpuPrivMut { curr_lrs: [u16; GIC_LIST_REGS_NUM], sgis: [Sgis; GIC_SGIS_NUM], - interrupts: Vec, - pend_list: VecDeque, - act_list: VecDeque, + pend_list: VecDeque>, + act_list: VecDeque>, +} + +impl VgicCpuPrivMut { + fn queue_remove(list: &mut VecDeque>, interrupt: &VgicInt) { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + list.iter() + .position(|x| unsafe { x.as_ref().id() } == interrupt.id()) + .map(|i| list.remove(i)); + } + + fn pend_list_push(&mut self, interrupt: &VgicInt) { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + self.pend_list + .push_back(unsafe { NonNull::new_unchecked(interrupt as *const _ as *mut _) }); + } + + fn pend_list_remove(&mut self, interrupt: &VgicInt) { + Self::queue_remove(&mut self.pend_list, interrupt); + } + + fn act_list_push(&mut self, interrupt: &VgicInt) { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + self.act_list + .push_back(unsafe { NonNull::new_unchecked(interrupt as *const _ as *mut _) }); + } + + fn act_list_remove(&mut self, interrupt: &VgicInt) { + Self::queue_remove(&mut self.act_list, interrupt); + } } +// SAFETY: VgicCpuPriv is only accessed on one core +unsafe impl Send for VgicCpuPriv {} +unsafe impl Sync for VgicCpuPriv {} + impl VgicCpuPriv { fn default() -> VgicCpuPriv { VgicCpuPriv { vigcr: Vgicr::default(), - curr_lrs: [0; GIC_LIST_REGS_NUM], - sgis: [Sgis::default(); GIC_SGIS_NUM], interrupts: Vec::new(), - pend_list: VecDeque::new(), - act_list: VecDeque::new(), + inner_mut: RefCell::new(VgicCpuPrivMut { + curr_lrs: [0; GIC_LIST_REGS_NUM], + sgis: [Sgis::default(); GIC_SGIS_NUM], + pend_list: VecDeque::new(), + act_list: VecDeque::new(), + }), } } fn new(typer: usize, cltr: usize, iidr: usize) -> VgicCpuPriv { VgicCpuPriv { vigcr: Vgicr::new(typer, cltr, iidr), - curr_lrs: [0; GIC_LIST_REGS_NUM], - sgis: [Sgis::default(); GIC_SGIS_NUM], interrupts: Vec::new(), - pend_list: VecDeque::new(), - act_list: VecDeque::new(), + inner_mut: RefCell::new(VgicCpuPrivMut { + curr_lrs: [0; GIC_LIST_REGS_NUM], + sgis: [Sgis::default(); GIC_SGIS_NUM], + pend_list: VecDeque::new(), + act_list: VecDeque::new(), + }), } } } /// VGIC general struct pub struct Vgic { - vgicd: Mutex, - cpu_priv: Mutex>, + address_range: Range, + vgicd: Vgicd, + cpu_priv: Vec, } impl Vgic { - pub fn default() -> Vgic { + pub fn new(base: usize, length: usize, cpu_num: usize) -> Vgic { Vgic { - vgicd: Mutex::new(Vgicd::default()), - cpu_priv: Mutex::new(Vec::new()), + address_range: base..base + length, + vgicd: Vgicd::new(cpu_num), + cpu_priv: Vec::new(), } } - fn remove_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { - let mut cpu_priv = self.cpu_priv.lock(); - let vcpu_id = vcpu.id(); - let int_id = interrupt.id(); - if interrupt.in_lr() { - if is_pend { - if !interrupt.in_pend() { - return; - } - for i in 0..cpu_priv[vcpu_id].pend_list.len() { - if cpu_priv[vcpu_id].pend_list[i].id() == int_id { - cpu_priv[vcpu_id].pend_list.remove(i); - break; - } - } - interrupt.set_in_pend_state(false); - } else { - if !interrupt.in_act() { - return; - } - for i in 0..cpu_priv[vcpu_id].act_list.len() { - if cpu_priv[vcpu_id].act_list[i].id() == int_id { - cpu_priv[vcpu_id].act_list.remove(i); - break; - } - } - interrupt.set_in_act_state(false); - }; - } - } - - fn add_int_list(&self, vcpu: Vcpu, interrupt: VgicInt, is_pend: bool) { - let mut cpu_priv = self.cpu_priv.lock(); - let vcpu_id = vcpu.id(); - if !interrupt.in_lr() { - if is_pend { - interrupt.set_in_pend_state(true); - cpu_priv[vcpu_id].pend_list.push_back(interrupt); - } else { - interrupt.set_in_act_state(true); - cpu_priv[vcpu_id].act_list.push_back(interrupt); - } - } - } + fn update_int_list(&self, vcpu: &Vcpu, interrupt: &VgicInt) { + // Every vcpu has its own cpu_priv, so we can use vcpu.id() to index cpu_priv + let mut cpu_priv = self.cpu_priv[vcpu.id()].inner_mut.borrow_mut(); - fn update_int_list(&self, vcpu: Vcpu, interrupt: VgicInt) { - let state = interrupt.state().to_num(); + interrupt.locked_helper(|int| { + let state = int.state.to_num(); - if state & IrqState::IrqSPend.to_num() != 0 && !interrupt.in_pend() { - self.add_int_list(vcpu.clone(), interrupt.clone(), true); - } else if state & IrqState::IrqSPend.to_num() == 0 { - self.remove_int_list(vcpu.clone(), interrupt.clone(), true); - } + if state & IrqState::IrqSPend.to_num() != 0 && !int.in_pend { + cpu_priv.pend_list_push(interrupt); + int.in_pend = true; + } else if state & IrqState::IrqSPend.to_num() == 0 { + cpu_priv.pend_list_remove(interrupt); + int.in_pend = false; + } - if state & IrqState::IrqSActive.to_num() != 0 && !interrupt.in_act() { - self.add_int_list(vcpu.clone(), interrupt.clone(), false); - } else if state & IrqState::IrqSActive.to_num() == 0 { - self.remove_int_list(vcpu.clone(), interrupt.clone(), false); - } + if state & IrqState::IrqSActive.to_num() != 0 && !int.in_act { + cpu_priv.act_list_push(interrupt); + int.in_act = true; + } else if state & IrqState::IrqSActive.to_num() == 0 { + cpu_priv.act_list_remove(interrupt); + int.in_act = false; + } + }); } - fn int_list_head(&self, vcpu: Vcpu, is_pend: bool) -> Option { - let cpu_priv = self.cpu_priv.lock(); + fn int_list_head(&self, vcpu: &Vcpu, is_pend: bool) -> Option<&VgicInt> { let vcpu_id = vcpu.id(); + let cpu_priv = self.cpu_priv[vcpu_id].inner_mut.borrow(); if is_pend { - if cpu_priv[vcpu_id].pend_list.is_empty() { - None - } else { - Some(cpu_priv[vcpu_id].pend_list[0].clone()) - } - } else if cpu_priv[vcpu_id].act_list.is_empty() { - None + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + cpu_priv.pend_list.front().cloned().map(|x| unsafe { x.as_ref() }) } else { - Some(cpu_priv[vcpu_id].act_list[0].clone()) + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + cpu_priv.act_list.front().cloned().map(|x| unsafe { x.as_ref() }) } } fn set_vgicd_ctlr(&self, ctlr: u32) { - let mut vgicd = self.vgicd.lock(); - vgicd.ctlr = ctlr; + self.vgicd.ctlr.store(ctlr, Ordering::Relaxed); } pub fn vgicd_ctlr(&self) -> u32 { - let vgicd = self.vgicd.lock(); - vgicd.ctlr + self.vgicd.ctlr.load(Ordering::Relaxed) } pub fn vgicd_typer(&self) -> u32 { - let vgicd = self.vgicd.lock(); - vgicd.typer + self.vgicd.typer } pub fn vgicd_iidr(&self) -> u32 { - let vgicd = self.vgicd.lock(); - vgicd.iidr + self.vgicd.iidr } fn vgicr_emul_typer_access(&self, emu_ctx: &EmuContext, vgicr_id: usize) { - let cpu_priv = self.cpu_priv.lock(); if !emu_ctx.write { - current_cpu().set_gpr(emu_ctx.reg, cpu_priv[vgicr_id].vigcr.get_typer() as usize); + // Read only and only be written when initializing + current_cpu().set_gpr(emu_ctx.reg, self.cpu_priv[vgicr_id].vigcr.get_typer() as usize); } } - fn vgicd_set_irouter(&self, vcpu: Vcpu, int_id: usize, val: usize) { - if let Some(interrupt) = self.get_int(vcpu.clone(), int_id) { + fn vgicd_set_irouter(&self, vcpu: &Vcpu, int_id: usize, val: usize) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { - self.remove_lr(vcpu.clone(), interrupt.clone()); + if vgic_int_get_owner(vcpu, interrupt) { + self.remove_lr(vcpu, interrupt); let phys_route = if (val & GICD_IROUTER_IRM_BIT) != 0 { cpuid2mpidr(vcpu.phys_id()) @@ -569,8 +567,8 @@ impl Vgic { if interrupt.hw() { GICD.set_route(int_id, phys_route); } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); } else { let m = IpiInitcMessage { event: InitcEvent::VgicdRoute, @@ -594,58 +592,57 @@ impl Vgic { } } - fn cpu_priv_interrupt(&self, cpu_id: usize, idx: usize) -> VgicInt { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].interrupts[idx].clone() + fn cpu_priv_interrupt(&self, cpu_id: usize, idx: usize) -> Option<&VgicInt> { + self.cpu_priv[cpu_id].interrupts.get(idx) } fn cpu_priv_curr_lrs(&self, cpu_id: usize, idx: usize) -> u16 { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].curr_lrs[idx] + let cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow(); + cpu_priv.curr_lrs[idx] } fn cpu_priv_sgis_pend(&self, cpu_id: usize, idx: usize) -> u8 { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].pend + let cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow(); + cpu_priv.sgis[idx].pend } fn cpu_priv_sgis_act(&self, cpu_id: usize, idx: usize) -> u8 { - let cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].act + let cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow(); + cpu_priv.sgis[idx].act } fn set_cpu_priv_curr_lrs(&self, cpu_id: usize, idx: usize, val: u16) { - let mut cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].curr_lrs[idx] = val; + let mut cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow_mut(); + cpu_priv.curr_lrs[idx] = val; } fn set_cpu_priv_sgis_pend(&self, cpu_id: usize, idx: usize, pend: u8) { - let mut cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].pend = pend; + let mut cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow_mut(); + cpu_priv.sgis[idx].pend = pend; } fn set_cpu_priv_sgis_act(&self, cpu_id: usize, idx: usize, act: u8) { - let mut cpu_priv = self.cpu_priv.lock(); - cpu_priv[cpu_id].sgis[idx].act = act; + let mut cpu_priv = self.cpu_priv[cpu_id].inner_mut.borrow_mut(); + cpu_priv.sgis[idx].act = act; } - fn vgicd_interrupt(&self, idx: usize) -> VgicInt { - let vgicd = self.vgicd.lock(); - vgicd.interrupts[idx].clone() + fn vgicd_interrupt(&self, idx: usize) -> Option<&VgicInt> { + self.vgicd.interrupts.get(idx) } - fn get_int(&self, vcpu: Vcpu, int_id: usize) -> Option { + fn get_int(&self, vcpu: &Vcpu, int_id: usize) -> Option<&VgicInt> { if int_id < GIC_PRIVINT_NUM { let vcpu_id = vcpu.id(); - return Some(self.cpu_priv_interrupt(vcpu_id, int_id)); + self.cpu_priv_interrupt(vcpu_id, int_id) } else if (GIC_PRIVINT_NUM..GIC_INTS_MAX).contains(&int_id) { - return Some(self.vgicd_interrupt(int_id - GIC_PRIVINT_NUM)); + self.vgicd_interrupt(int_id - GIC_PRIVINT_NUM) + } else { + None } - None } - fn remove_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { - if !vgic_owns(vcpu.clone(), interrupt.clone()) { + fn remove_lr(&self, vcpu: &Vcpu, interrupt: &VgicInt) -> bool { + if !vgic_owns(vcpu, interrupt) { return false; } let int_lr = interrupt.lr(); @@ -655,7 +652,7 @@ impl Vgic { } let mut lr_val = 0; - if let Some(lr) = gich_get_lr(interrupt.clone()) { + if let Some(lr) = gich_get_lr(interrupt) { GICH.set_lr(int_lr as usize, 0); lr_val = lr; } @@ -666,7 +663,7 @@ impl Vgic { if lr_state != IrqState::IrqSInactive.to_num() { interrupt.set_state(IrqState::num_to_state(lr_state)); - self.update_int_list(vcpu.clone(), interrupt.clone()); + self.update_int_list(vcpu, interrupt); if (interrupt.state().to_num() & IrqState::IrqSPend.to_num() != 0) && interrupt.enabled() { let hcr = GICH.hcr(); @@ -677,7 +674,7 @@ impl Vgic { false } - fn add_lr(&self, vcpu: Vcpu, interrupt: VgicInt) -> bool { + fn add_lr(&self, vcpu: &Vcpu, interrupt: &VgicInt) -> bool { if !interrupt.enabled() || interrupt.in_lr() { return false; } @@ -737,18 +734,17 @@ impl Vgic { } if let Some(idx) = lr_ind { - if let Some(spilled_int) = &self.get_int( - vcpu.clone(), - bit_extract(GICH.lr(idx), GICH_LR_VID_OFF, GICH_LR_VID_LEN), - ) { + if let Some(spilled_int) = + self.get_int(vcpu, bit_extract(GICH.lr(idx), GICH_LR_VID_OFF, GICH_LR_VID_LEN)) + { if spilled_int.id() != interrupt.id() { let spilled_int_lock = spilled_int.lock.lock(); - self.remove_lr(vcpu.clone(), spilled_int.clone()); - vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + self.remove_lr(vcpu, spilled_int); + vgic_int_yield_owner(vcpu, spilled_int); drop(spilled_int_lock); } else { - self.remove_lr(vcpu.clone(), spilled_int.clone()); - vgic_int_yield_owner(vcpu.clone(), spilled_int.clone()); + self.remove_lr(vcpu, spilled_int); + vgic_int_yield_owner(vcpu, spilled_int); } } } @@ -773,32 +769,29 @@ impl Vgic { false } - fn write_lr(&self, vcpu: Vcpu, interrupt: VgicInt, lr_ind: usize) { + fn write_lr(&self, vcpu: &Vcpu, interrupt: &VgicInt, lr_ind: usize) { let vcpu_id = vcpu.id(); let int_id = interrupt.id() as usize; let int_prio = interrupt.prio(); let prev_int_id = self.cpu_priv_curr_lrs(vcpu_id, lr_ind) as usize; if prev_int_id != int_id && !gic_is_priv(prev_int_id) { - if let Some(prev_interrupt) = self.get_int(vcpu.clone(), prev_int_id) { + if let Some(prev_interrupt) = self.get_int(vcpu, prev_int_id) { let prev_interrupt_lock = prev_interrupt.lock.lock(); - if vgic_owns(vcpu.clone(), prev_interrupt.clone()) - && prev_interrupt.in_lr() - && (prev_interrupt.lr() == lr_ind as u16) - { + if vgic_owns(vcpu, prev_interrupt) && prev_interrupt.in_lr() && (prev_interrupt.lr() == lr_ind as u16) { prev_interrupt.set_in_lr(false); - vgic_int_yield_owner(vcpu.clone(), prev_interrupt.clone()); + vgic_int_yield_owner(vcpu, prev_interrupt); } drop(prev_interrupt_lock); } } - let state = vgic_get_state(interrupt.clone()); + let state = vgic_get_state(interrupt); let mut lr = (int_id << GICH_LR_VID_OFF) & GICH_LR_VID_MASK; lr |= ((int_prio as usize) << GICH_LR_PRIO_OFF) & GICH_LR_PRIO_MSK; - if vgic_int_is_hw(interrupt.clone()) { + if vgic_int_is_hw(interrupt) { lr |= GICH_LR_HW_BIT; lr |= (int_id << GICH_LR_PID_OFF) & GICH_LR_PID_MSK; if state == IrqState::IrqSPendActive.to_num() { @@ -807,7 +800,7 @@ impl Vgic { lr |= (state << GICH_LR_STATE_OFF) & GICH_LR_STATE_MSK; } } else { - if !gic_is_priv(int_id) && !vgic_int_is_hw(interrupt.clone()) { + if !gic_is_priv(int_id) && !vgic_int_is_hw(interrupt) { lr |= GICH_LR_EOI_BIT; } @@ -820,16 +813,18 @@ impl Vgic { */ lr |= GICH_LR_GRP_BIT; - interrupt.set_state(IrqState::IrqSInactive); - interrupt.set_in_lr(true); - interrupt.set_lr(lr_ind as u16); + interrupt.locked_helper(|int| { + int.state = IrqState::IrqSInactive; + int.in_lr = true; + int.lr = lr_ind as u16; + }); self.set_cpu_priv_curr_lrs(vcpu_id, lr_ind, int_id as u16); GICH.set_lr(lr_ind, lr); self.update_int_list(vcpu, interrupt); } - fn route(&self, vcpu: Vcpu, interrupt: VgicInt) { + fn route(&self, vcpu: &Vcpu, interrupt: &VgicInt) { if let IrqState::IrqSInactive = interrupt.state() { return; } @@ -838,11 +833,11 @@ impl Vgic { return; } - if vgic_int_vcpu_is_target(&vcpu, &interrupt) { - self.add_lr(vcpu.clone(), interrupt.clone()); + if vgic_int_vcpu_is_target(vcpu, interrupt) { + self.add_lr(vcpu, interrupt); } - if !interrupt.in_lr() && vgic_int_has_other_target(interrupt.clone()) { + if !interrupt.in_lr() && vgic_int_has_other_target(interrupt) { let vcpu_vm_id = vcpu.vm_id(); let ipi_msg = IpiInitcMessage { event: InitcEvent::VgicdRoute, @@ -850,7 +845,7 @@ impl Vgic { int_id: interrupt.id(), val: 0, }; - vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); let trglist = vgic_int_ptarget_mask(interrupt) & !(1 << vcpu.phys_id()); for i in 0..PLAT_DESC.cpu_desc.num { if trglist & (1 << i) != 0 { @@ -860,14 +855,14 @@ impl Vgic { } } - fn set_enable(&self, vcpu: Vcpu, int_id: usize, en: bool) { - match self.get_int(vcpu.clone(), int_id) { + fn set_enable(&self, vcpu: &Vcpu, int_id: usize, en: bool) { + match self.get_int(vcpu, int_id) { Some(interrupt) => { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { if interrupt.enabled() ^ en { interrupt.set_enabled(en); - self.remove_lr(vcpu.clone(), interrupt.clone()); + self.remove_lr(vcpu, interrupt); if interrupt.hw() { if gic_is_priv(interrupt.id() as usize) { GICR.set_enable(interrupt.id() as usize, en, interrupt.phys_redist() as u32); @@ -876,8 +871,8 @@ impl Vgic { } } } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu, interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); } else { let int_phys_id = interrupt.owner_phys_id().unwrap(); let vcpu_vm_id = vcpu.vm_id(); @@ -902,15 +897,15 @@ impl Vgic { } } - fn get_enable(&self, vcpu: Vcpu, int_id: usize) -> bool { + fn get_enable(&self, vcpu: &Vcpu, int_id: usize) -> bool { self.get_int(vcpu, int_id).unwrap().enabled() } - fn set_pend(&self, vcpu: Vcpu, int_id: usize, pend: bool) { - if let Some(interrupt) = self.get_int(vcpu.clone(), int_id) { + fn set_pend(&self, vcpu: &Vcpu, int_id: usize, pend: bool) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { - self.remove_lr(vcpu.clone(), interrupt.clone()); + if vgic_int_get_owner(vcpu, interrupt) { + self.remove_lr(vcpu, interrupt); let state = interrupt.state().to_num(); if pend && ((state & 1) == 0) { @@ -928,8 +923,8 @@ impl Vgic { gic_set_state(interrupt.id() as usize, state, 0); } } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu, interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); } else { let vm_id = vcpu.vm_id(); @@ -963,12 +958,11 @@ impl Vgic { } } - fn set_active(&self, vcpu: Vcpu, int_id: usize, act: bool) { - let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); - if let Some(interrupt) = interrupt_option { + fn set_active(&self, vcpu: &Vcpu, int_id: usize, act: bool) { + if let Some(interrupt) = self.get_int(vcpu, bit_extract(int_id, 0, 10)) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { - self.remove_lr(vcpu.clone(), interrupt.clone()); + if vgic_int_get_owner(vcpu, interrupt) { + self.remove_lr(vcpu, interrupt); let state = interrupt.state().to_num(); if act && ((state & IrqState::IrqSActive.to_num()) == 0) { interrupt.set_state(IrqState::num_to_state(state | IrqState::IrqSActive.to_num())); @@ -988,8 +982,8 @@ impl Vgic { gic_set_state(vgic_int_id, if state == 1 { 2 } else { state }, 0); } } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu, interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); } else { let vm_id = vcpu.vm_id(); @@ -1011,10 +1005,10 @@ impl Vgic { } } - fn set_icfgr(&self, vcpu: Vcpu, int_id: usize, cfg: u8) { - if let Some(interrupt) = self.get_int(vcpu.clone(), int_id) { + fn set_icfgr(&self, vcpu: &Vcpu, int_id: usize, cfg: u8) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { interrupt.set_cfg(cfg); if interrupt.hw() { if gic_is_priv(interrupt.id() as usize) { @@ -1023,8 +1017,8 @@ impl Vgic { GICD.set_icfgr(interrupt.id() as usize, cfg); } } - self.route(vcpu.clone(), interrupt.clone()); - vgic_int_yield_owner(vcpu, interrupt.clone()); + self.route(vcpu, interrupt); + vgic_int_yield_owner(vcpu, interrupt); } else { let m = IpiInitcMessage { event: InitcEvent::VgicdSetCfg, @@ -1050,28 +1044,26 @@ impl Vgic { } } - fn get_icfgr(&self, vcpu: Vcpu, int_id: usize) -> u8 { - let interrupt_option = self.get_int(vcpu, int_id); - if let Some(interrupt) = interrupt_option { + fn get_icfgr(&self, vcpu: &Vcpu, int_id: usize) -> u8 { + if let Some(interrupt) = self.get_int(vcpu, int_id) { interrupt.cfg() } else { unimplemented!(); } } - fn set_prio(&self, vcpu: Vcpu, int_id: usize, mut prio: u8) { - let interrupt_option = self.get_int(vcpu.clone(), int_id); - prio &= 0xf0; // gicv3 allows 8 priority bits in non-secure state + fn set_prio(&self, vcpu: &Vcpu, int_id: usize, mut prio: u8) { + if let Some(interrupt) = self.get_int(vcpu, int_id) { + prio &= 0xf0; // gicv3 allows 8 priority bits in non-secure state - if let Some(interrupt) = interrupt_option { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(vcpu.clone(), interrupt.clone()) { + if vgic_int_get_owner(vcpu, interrupt) { if interrupt.prio() != prio { - self.remove_lr(vcpu.clone(), interrupt.clone()); + self.remove_lr(vcpu, interrupt); let prev_prio = interrupt.prio(); interrupt.set_prio(prio); if prio <= prev_prio { - self.route(vcpu.clone(), interrupt.clone()); + self.route(vcpu, interrupt); } if interrupt.hw() { if gic_is_priv(interrupt.id() as usize) { @@ -1081,7 +1073,7 @@ impl Vgic { } } } - vgic_int_yield_owner(vcpu, interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } else { let vm_id = vcpu.vm_id(); @@ -1107,21 +1099,21 @@ impl Vgic { } } - fn get_prio(&self, vcpu: Vcpu, int_id: usize) -> u8 { - let interrupt_option = self.get_int(vcpu, int_id); - interrupt_option.unwrap().prio() + fn get_prio(&self, vcpu: &Vcpu, int_id: usize) -> u8 { + self.get_int(vcpu, int_id).unwrap().prio() } - pub fn inject(&self, vcpu: Vcpu, int_id: usize) { - let interrupt_option = self.get_int(vcpu.clone(), bit_extract(int_id, 0, 10)); - if let Some(interrupt) = interrupt_option { + pub fn inject(&self, vcpu: &Vcpu, int_id: usize) { + if let Some(interrupt) = self.get_int(vcpu, bit_extract(int_id, 0, 10)) { if interrupt.hw() { let interrupt_lock = interrupt.lock.lock(); - interrupt.set_owner(vcpu.clone()); - interrupt.set_state(IrqState::IrqSPend); - self.update_int_list(vcpu.clone(), interrupt.clone()); - interrupt.set_in_lr(false); - self.route(vcpu, interrupt.clone()); + interrupt.locked_helper(|interrupt| { + interrupt.owner = Some(vcpu.clone()); + interrupt.state = IrqState::IrqSPend; + interrupt.in_lr = false; + }); + self.update_int_list(vcpu, interrupt); + self.route(vcpu, interrupt); drop(interrupt_lock); } else { self.set_pend(vcpu, int_id, true); @@ -1141,11 +1133,11 @@ impl Vgic { let mut val = if emu_ctx.write { current_cpu().get_gpr(idx) } else { 0 }; if emu_ctx.write { - self.vgicd_set_irouter(current_cpu().active_vcpu.clone().unwrap(), first_int, val); + self.vgicd_set_irouter(current_cpu().active_vcpu.as_ref().unwrap(), first_int, val); } else { if !gic_is_priv(first_int) { val = self - .get_int(current_cpu().active_vcpu.clone().unwrap(), first_int) + .get_int(current_cpu().active_vcpu.as_ref().unwrap(), first_int) .unwrap() .route() as usize; } @@ -1173,7 +1165,7 @@ impl Vgic { int_id: 0, val: enable as u8, }; - ipi_intra_broadcast_msg(active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(m)); + ipi_intra_broadcast_msg(&active_vm().unwrap(), IpiType::IpiTIntc, IpiInnerMsg::Initc(m)); } } else { let idx = emu_ctx.reg; @@ -1233,12 +1225,12 @@ impl Vgic { if emu_ctx.write { for i in 0..(emu_ctx.width * 8) { if bit_get(val, i) != 0 { - self.set_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i, true); + self.set_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, true); } } } else { for i in 0..(emu_ctx.width * 8) { - if self.get_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + if self.get_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { val |= 1 << i; } } @@ -1262,16 +1254,14 @@ impl Vgic { if emu_ctx.write { for i in 0..(emu_ctx.width * 8) { if bit_get(val, i) != 0 { - self.set_pend(current_cpu().active_vcpu.clone().unwrap(), first_int + i, set); + self.set_pend(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, set); } } } else { for i in 0..32 { - match self.get_int(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + match self.get_int(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { Some(interrupt) => { - if vgic_get_state(interrupt.clone()) & IrqState::IrqSPend.to_num() - != IrqState::IrqSInactive.to_num() - { + if vgic_get_state(interrupt) & IrqState::IrqSPend.to_num() != IrqState::IrqSInactive.to_num() { val |= 1 << i; } } @@ -1332,14 +1322,14 @@ impl Vgic { if emu_ctx.write { for i in 0..(emu_ctx.width * 8) { if bit_get(val, i) != 0 { - self.set_active(current_cpu().active_vcpu.clone().unwrap(), first_int + i, set); + self.set_active(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, set); } } } else { for i in 0..(emu_ctx.width * 8) { - match self.get_int(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + match self.get_int(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { Some(interrupt) => { - if vgic_get_state(interrupt.clone()) & IrqState::IrqSActive.to_num() != 0 { + if vgic_get_state(interrupt) & IrqState::IrqSActive.to_num() != 0 { val |= 1 << i; } } @@ -1390,12 +1380,12 @@ impl Vgic { if emu_ctx.write { for i in 0..(emu_ctx.width * 8) { if bit_get(val, i) != 0 { - self.set_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i, false); + self.set_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, false); } } } else { for i in 0..(emu_ctx.width * 8) { - if self.get_enable(current_cpu().active_vcpu.clone().unwrap(), first_int + i) { + if self.get_enable(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) { val |= 1 << i; } } @@ -1460,7 +1450,7 @@ impl Vgic { let mut bit = 0; while bit < (emu_ctx.width * 8) { self.set_icfgr( - current_cpu().active_vcpu.clone().unwrap(), + current_cpu().active_vcpu.as_ref().unwrap(), irq, bit_extract(cfg as usize, bit, GIC_CONFIG_BITS) as u8, ); @@ -1472,7 +1462,7 @@ impl Vgic { let mut irq = first_int; let mut bit = 0; while bit < (emu_ctx.width * 8) { - cfg |= (self.get_icfgr(current_cpu().active_vcpu.clone().unwrap(), irq) as usize) << bit; + cfg |= (self.get_icfgr(current_cpu().active_vcpu.as_ref().unwrap(), irq) as usize) << bit; bit += 2; irq += 1; } @@ -1514,14 +1504,14 @@ impl Vgic { if emu_ctx.write { for i in 0..emu_ctx.width { self.set_prio( - current_cpu().active_vcpu.clone().unwrap(), + current_cpu().active_vcpu.as_ref().unwrap(), first_int + i, bit_extract(val, GIC_PRIO_BITS * i, GIC_PRIO_BITS) as u8, ); } } else { for i in 0..emu_ctx.width { - val |= (self.get_prio(current_cpu().active_vcpu.clone().unwrap(), first_int + i) as usize) + val |= (self.get_prio(current_cpu().active_vcpu.as_ref().unwrap(), first_int + i) as usize) << (GIC_PRIO_BITS * i); } let idx = emu_ctx.reg; @@ -1529,7 +1519,7 @@ impl Vgic { } } - fn handle_trapped_eoir(&self, vcpu: Vcpu) { + fn handle_trapped_eoir(&self, vcpu: &Vcpu) { let gic_lrs = gic_lrs(); // eisr():Interrupt Controller End of Interrupt Status Register let mut lr_idx_opt = bitmap_find_nth(GICH.eisr() as usize, 0, gic_lrs, 1, true); @@ -1539,14 +1529,14 @@ impl Vgic { let lr_val = GICH.lr(lr_idx) as usize; GICH.set_lr(lr_idx, 0); - match self.get_int(vcpu.clone(), bit_extract(lr_val, GICH_LR_VID_OFF, GICH_LR_VID_LEN)) { + match self.get_int(vcpu, bit_extract(lr_val, GICH_LR_VID_OFF, GICH_LR_VID_LEN)) { Some(interrupt) => { let interrupt_lock = interrupt.lock.lock(); interrupt.set_in_lr(false); if (interrupt.id() as usize) < GIC_SGIS_NUM { - self.add_lr(vcpu.clone(), interrupt.clone()); + self.add_lr(vcpu, interrupt); } else { - vgic_int_yield_owner(vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(vcpu, interrupt); } drop(interrupt_lock); } @@ -1558,34 +1548,35 @@ impl Vgic { } } - fn vgic_highest_proi_spilled(&self, vcpu: &Vcpu, flag: bool) -> Option { - let cpu_priv = &self.cpu_priv.lock()[vcpu.id()]; - // info!("binding list_len:{}", binding.len()); + fn vgic_highest_proi_spilled(&self, vcpu: &Vcpu, flag: bool) -> Option> { + let cpu_priv = self.cpu_priv[vcpu.id()].inner_mut.borrow(); + let array = [ Some(cpu_priv.pend_list.iter()), if flag { None } else { Some(cpu_priv.act_list.iter()) }, ]; let binding = array.into_iter().flatten().flatten(); + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull binding - .min_by_key(|x| (((x.prio() as u32) << 10) | x.id() as u32)) + .min_by_key(|x| (((unsafe { x.as_ref().prio() } as u32) << 10) | unsafe { x.as_ref().id() } as u32)) .cloned() } - fn refill_lrs(&self, vcpu: Vcpu, flag: bool) { + fn refill_lrs(&self, vcpu: &Vcpu, flag: bool) { let gic_lrs = gic_lrs(); // ICH_ELRSR_EL2:locate a usable List register when the hypervisor is delivering an interrupt to a Guest OS. let mut lr_idx_opt = bitmap_find_nth(GICH.elrsr(), 0, gic_lrs, 1, true); // flag indicates that is no pending or not true:no pending flase:have pending,the we will look up active and pend let mut new_flags = flag; while lr_idx_opt.is_some() { - let interrupt_opt: Option = self.vgic_highest_proi_spilled(&vcpu, new_flags); - - match interrupt_opt { - Some(interrupt) => { + match self.vgic_highest_proi_spilled(vcpu, new_flags) { + Some(interrupt_ptr) => { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + let interrupt = unsafe { interrupt_ptr.as_ref() }; let interrupt_lock = interrupt.lock.lock(); - let got_ownership = vgic_int_get_owner(vcpu.clone(), interrupt.clone()); + let got_ownership = vgic_int_get_owner(vcpu, interrupt); if got_ownership { - self.write_lr(vcpu.clone(), interrupt.clone(), lr_idx_opt.unwrap()); + self.write_lr(vcpu, interrupt, lr_idx_opt.unwrap()); } drop(interrupt_lock); if !got_ownership { @@ -1604,33 +1595,36 @@ impl Vgic { } } - fn eoir_highest_spilled_active(&self, vcpu: Vcpu) { - let cpu_priv = self.cpu_priv.lock(); - let binding = &cpu_priv[vcpu.id()].act_list; + fn eoir_highest_spilled_active(&self, vcpu: &Vcpu) { + let cpu_priv = self.cpu_priv[vcpu.id()].inner_mut.borrow(); + let binding = &cpu_priv.act_list; + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull let interrupt = binding .iter() - .min_by_key(|x| (((x.prio() as u32) << 10) | x.id() as u32)) + .min_by_key(|x| (((unsafe { x.as_ref().prio() } as u32) << 10) | unsafe { x.as_ref().id() } as u32)) .cloned(); drop(cpu_priv); - if let Some(int) = interrupt { + if let Some(int_ptr) = interrupt { + /// SAFETY: All VgicInt are allocated when initializing, so it's safe to convert them to NonNull + let int = unsafe { int_ptr.as_ref() }; int.lock.lock(); - vgic_int_get_owner(vcpu.clone(), int.clone()); + vgic_int_get_owner(vcpu, int); let state = int.state().to_num(); int.set_state(IrqState::num_to_state(state & !2)); - self.update_int_list(vcpu.clone(), int.clone()); + self.update_int_list(vcpu, int); - if vgic_int_is_hw(int.clone()) { + if vgic_int_is_hw(int) { gic_set_act(int.id() as usize, false, current_cpu().id as u32); } else if int.state().to_num() & 1 != 0 { - self.add_lr(vcpu, int.clone()); + self.add_lr(vcpu, int); } } } } #[inline(always)] -fn vgic_broadcast(interrupt: VgicInt) -> bool { +fn vgic_broadcast(interrupt: &VgicInt) -> bool { (interrupt.route() as usize & GICD_IROUTER_IRM_BIT) != 0 } @@ -1669,12 +1663,12 @@ fn vgic_int_vcpu_is_target(vcpu: &Vcpu, interrupt: &VgicInt) -> bool { let pri = gic_is_priv(interrupt.id() as usize); let local = pri && (interrupt.phys_redist() as usize == vcpu.phys_id()); let routed_here = !pri && (interrupt.phys_route() as usize ^ (MPIDR_EL1::read() as usize & MPIDR_AFF_MSK)) == 0; - let any = !pri && vgic_broadcast(interrupt.clone()); + let any = !pri && vgic_broadcast(interrupt); local || routed_here || any } -fn vgic_int_has_other_target(interrupt: VgicInt) -> bool { +fn vgic_int_has_other_target(interrupt: &VgicInt) -> bool { let pri = gic_is_priv(interrupt.id() as usize); if pri { return false; @@ -1682,13 +1676,13 @@ fn vgic_int_has_other_target(interrupt: VgicInt) -> bool { let routed_here = !pri && (interrupt.phys_route() as usize ^ (MPIDR_EL1::read() as usize & MPIDR_AFF_MSK)) == 0; let route_valid = interrupt.phys_route() as usize != GICD_IROUTER_INV; - let any = !pri && vgic_broadcast(interrupt.clone()); + let any = !pri && vgic_broadcast(interrupt); any || (!routed_here && route_valid) } -fn vgic_int_ptarget_mask(interrupt: VgicInt) -> usize { - if vgic_broadcast(interrupt.clone()) { +fn vgic_int_ptarget_mask(interrupt: &VgicInt) -> usize { + if vgic_broadcast(interrupt) { current_cpu().active_vcpu.clone().unwrap().vm().clone().unwrap().ncpu() & !(1 << current_cpu().id) } else if cfg!(feature = "rk3588") { (1 << (interrupt.phys_route() >> 8)) as usize @@ -1697,7 +1691,7 @@ fn vgic_int_ptarget_mask(interrupt: VgicInt) -> usize { } } -fn vgic_target_translate(vm: Vm, trgt: u32, v2p: bool) -> u32 { +fn vgic_target_translate(vm: &Vm, trgt: u32, v2p: bool) -> u32 { let from = trgt.to_le_bytes(); let mut result = 0; @@ -1720,7 +1714,7 @@ fn vgic_target_translate(vm: Vm, trgt: u32, v2p: bool) -> u32 { result } -fn vgic_owns(vcpu: Vcpu, interrupt: VgicInt) -> bool { +fn vgic_owns(vcpu: &Vcpu, interrupt: &VgicInt) -> bool { if gic_is_priv(interrupt.id() as usize) { return true; } @@ -1737,11 +1731,11 @@ fn vgic_owns(vcpu: Vcpu, interrupt: VgicInt) -> bool { } } -fn vgic_get_state(interrupt: VgicInt) -> usize { +fn vgic_get_state(interrupt: &VgicInt) -> usize { let mut state = interrupt.state().to_num(); if interrupt.in_lr() && interrupt.owner_phys_id().unwrap() == current_cpu().id { - let lr_option = gich_get_lr(interrupt.clone()); + let lr_option = gich_get_lr(interrupt); if let Some(lr_val) = lr_option { state = bit_extract(lr_val, GICH_LR_STATE_OFF, GICH_LR_STATE_LEN); } @@ -1750,10 +1744,10 @@ fn vgic_get_state(interrupt: VgicInt) -> usize { state } -fn vgic_int_yield_owner(vcpu: Vcpu, interrupt: VgicInt) { - if !vgic_owns(vcpu, interrupt.clone()) +fn vgic_int_yield_owner(vcpu: &Vcpu, interrupt: &VgicInt) { + if !vgic_owns(vcpu, interrupt) || interrupt.in_lr() - || (vgic_get_state(interrupt.clone()) & IrqState::IrqSActive.to_num() != 0) + || (vgic_get_state(interrupt) & IrqState::IrqSActive.to_num() != 0) { return; } @@ -1762,11 +1756,11 @@ fn vgic_int_yield_owner(vcpu: Vcpu, interrupt: VgicInt) { } #[inline(always)] -fn vgic_int_is_hw(interrupt: VgicInt) -> bool { +fn vgic_int_is_hw(interrupt: &VgicInt) -> bool { interrupt.id() as usize >= GIC_SGIS_NUM && interrupt.hw() } -fn gich_get_lr(interrupt: VgicInt) -> Option { +fn gich_get_lr(interrupt: &VgicInt) -> Option { let cpu_id = current_cpu().id; let phys_id = interrupt.owner_phys_id().unwrap(); @@ -1783,7 +1777,7 @@ fn gich_get_lr(interrupt: VgicInt) -> Option { None } -fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { +fn vgic_int_get_owner(vcpu: &Vcpu, interrupt: &VgicInt) -> bool { let vcpu_id = vcpu.id(); let vcpu_vm_id = vcpu.vm_id(); @@ -1795,7 +1789,7 @@ fn vgic_int_get_owner(vcpu: Vcpu, interrupt: VgicInt) -> bool { owner_vm_id == vcpu_vm_id && owner_vcpu_id == vcpu_id } None => { - interrupt.set_owner(vcpu); + interrupt.set_owner(vcpu.clone()); true } } @@ -1812,14 +1806,14 @@ pub fn gic_maintenance_handler() { let vgic = vm.vgic(); if misr & (GICH_MISR_EOI as u32) != 0 { - vgic.handle_trapped_eoir(current_cpu().active_vcpu.clone().unwrap()); + vgic.handle_trapped_eoir(current_cpu().active_vcpu.as_ref().unwrap()); } // NP:List Register Entry Not Present. // U: underflow Zero or one of the List register entries are marked as a valid interrupt, that is, if the corresponding ICH_LR_EL2.State bits do not equal 0x0. if misr & (GICH_MISR_NP as u32 | GICH_MISR_U as u32) != 0 { vgic.refill_lrs( - current_cpu().active_vcpu.clone().unwrap(), + current_cpu().active_vcpu.as_ref().unwrap(), (misr & GICH_MISR_NP as u32) != 0, ); } @@ -1827,7 +1821,7 @@ pub fn gic_maintenance_handler() { if misr & (GICH_MISR_LRPEN as u32) != 0 { let mut hcr = GICH.hcr(); while hcr & GICH_HCR_EOIC_MSK != 0 { - vgic.eoir_highest_spilled_active(current_cpu().active_vcpu.clone().unwrap()); + vgic.eoir_highest_spilled_active(current_cpu().active_vcpu.as_ref().unwrap()); hcr -= 1 << GICH_HCR_EOIC_OFF; GICH.set_hcr(hcr); hcr = GICH.hcr(); @@ -1846,84 +1840,132 @@ const VGICD_REG_OFFSET_PREFIX_ICACTIVER: usize = 0x7; const VGICD_REG_OFFSET_PREFIX_ICFGR: usize = 0x18; const VGICD_REG_OFFSET_PREFIX_SGIR: usize = 0x1e; -/// emulated interrupt controller handler -pub fn emu_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - let offset = emu_ctx.address & 0xffff; +impl EmuDev for Vgic { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTGicd + } - let vm = match crate::kernel::active_vm() { - None => { - panic!("emu_intc_handler: vm is None"); - } - Some(x) => x, - }; + fn address_range(&self) -> Range { + self.address_range.clone() + } - let vgic = vm.vgic(); - let vgicd_offset_prefix = offset >> 7; - trace!( - "current_cpu:{} emu_intc_handler offset:{:#x} is write:{},val:{:#x}", - current_cpu().id, - emu_ctx.address, - emu_ctx.write, - current_cpu().get_gpr(emu_ctx.reg) - ); + fn handler(&self, emu_ctx: &EmuContext) -> bool { + let offset = emu_ctx.address & 0xffff; - match vgicd_offset_prefix { - VGICD_REG_OFFSET_PREFIX_ISENABLER => { - vgic.emu_isenabler_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ISPENDR => { - vgic.emu_ispendr_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ISACTIVER => { - vgic.emu_isactiver_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICENABLER => { - vgic.emu_icenabler_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICPENDR => { - vgic.emu_icpendr_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICACTIVER => { - vgic.emu_icativer_access(emu_ctx); - } - VGICD_REG_OFFSET_PREFIX_ICFGR => { - vgic.emu_icfgr_access(emu_ctx); + let vgicd_offset_prefix = offset >> 7; + if !vgicd_emu_access_is_vaild(emu_ctx) { + return false; } - _ => { - match offset { - // VGICD_REG_OFFSET(CTLR) - 0 => { - vgic.emu_ctrl_access(emu_ctx); - } - // VGICD_REG_OFFSET(TYPER) - 0x004 => { - vgic.emu_typer_access(emu_ctx); - } - // VGICD_REG_OFFSET(IIDR) - 0x008 => { - vgic.emu_iidr_access(emu_ctx); - } - 0xf00 => { - vgic.emu_razwi(emu_ctx); - } - _ => { - if (0x400..0x800).contains(&offset) { - vgic.emu_ipriorityr_access(emu_ctx); - } else if (0x800..0xc00).contains(&offset) { - vgic.emu_razwi(emu_ctx); - } else if (0x6000..0x8000).contains(&offset) { - vgic.emu_irouter_access(emu_ctx); - } else if (0xffd0..0x10000).contains(&offset) { - //ffe8 is GICD_PIDR2, Peripheral ID2 Register - vgic.emu_pidr_access(emu_ctx); - } else { - vgic.emu_razwi(emu_ctx); + + trace!( + "current_cpu:{} emu_intc_handler offset:{:#x} is write:{},val:{:#x}", + current_cpu().id, + emu_ctx.address, + emu_ctx.write, + current_cpu().get_gpr(emu_ctx.reg) + ); + + match vgicd_offset_prefix { + VGICD_REG_OFFSET_PREFIX_ISENABLER => { + self.emu_isenabler_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ISPENDR => { + self.emu_ispendr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ISACTIVER => { + self.emu_isactiver_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICENABLER => { + self.emu_icenabler_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICPENDR => { + self.emu_icpendr_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICACTIVER => { + self.emu_icativer_access(emu_ctx); + } + VGICD_REG_OFFSET_PREFIX_ICFGR => { + self.emu_icfgr_access(emu_ctx); + } + _ => { + match offset { + // VGICD_REG_OFFSET(CTLR) + 0 => { + self.emu_ctrl_access(emu_ctx); + } + // VGICD_REG_OFFSET(TYPER) + 0x004 => { + self.emu_typer_access(emu_ctx); + } + // VGICD_REG_OFFSET(IIDR) + 0x008 => { + self.emu_iidr_access(emu_ctx); + } + 0xf00 => { + self.emu_razwi(emu_ctx); + } + _ => { + if (0x400..0x800).contains(&offset) { + self.emu_ipriorityr_access(emu_ctx); + } else if (0x800..0xc00).contains(&offset) { + self.emu_razwi(emu_ctx); + } else if (0x6000..0x8000).contains(&offset) { + self.emu_irouter_access(emu_ctx); + } else if (0xffd0..0x10000).contains(&offset) { + //ffe8 is GICD_PIDR2, Peripheral ID2 Register + self.emu_pidr_access(emu_ctx); + } else { + self.emu_razwi(emu_ctx); + } } } } } + true + } +} +pub struct PartialPassthroughIntc { + address_range: Range, +} + +impl EmuDev for PartialPassthroughIntc { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTGPPT + } + + fn address_range(&self) -> Range { + self.address_range.clone() + } + + /// partial passthrough interrupt controller handler + fn handler(&self, emu_ctx: &EmuContext) -> bool { + if !vgicd_emu_access_is_vaild(emu_ctx) { + return false; + } + + if emu_ctx.write { + // SAFETY: Emu_ctx.address is writeable in EL2 + unsafe { + emu_ctx.write(current_cpu().get_gpr(emu_ctx.reg)); + } + } else { + // SAFETY: Emu_ctx.address is readable in EL2 + current_cpu().set_gpr(emu_ctx.reg, unsafe { emu_ctx.read() }); + } + + true + } +} + +pub fn partial_passthrough_intc_init(emu_cfg: &VmEmulatedDeviceConfig) -> Result, ()> { + if emu_cfg.emu_type == EmuDeviceType::EmuDeviceTGPPT { + let intc = PartialPassthroughIntc { + address_range: emu_cfg.base_ipa..emu_cfg.base_ipa + emu_cfg.length, + }; + Ok(Arc::new(intc)) + } else { + Err(()) } - true } pub fn vgicd_emu_access_is_vaild(emu_ctx: &EmuContext) -> bool { @@ -1961,61 +2003,33 @@ pub fn vgicd_emu_access_is_vaild(emu_ctx: &EmuContext) -> bool { true } -/// partial passthrough interrupt controller handler -pub fn partial_passthrough_intc_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - if !vgicd_emu_access_is_vaild(emu_ctx) { - return false; - } - if emu_ctx.write { - // SAFETY: Emu_ctx.address is writeable in EL2 - unsafe { - emu_ctx.write(current_cpu().get_gpr(emu_ctx.reg)); - } - } else { - // SAFETY: Emu_ctx.address is readable in EL2 - current_cpu().set_gpr(emu_ctx.reg, unsafe { emu_ctx.read() }); - } +pub fn vgic_ipi_handler(msg: IpiMessage) { + if let IpiInnerMsg::Initc(intc) = msg.ipi_message { + let vm_id = intc.vm_id; + let int_id = intc.int_id; + let val = intc.val; - true -} + let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { + None => { + error!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); + return; + } + Some(vcpu) => vcpu, + }; + restore_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu.clone()); -pub fn vgic_ipi_handler(msg: &IpiMessage) { - let vm_id; - let int_id; - let val; - match &msg.ipi_message { - IpiInnerMsg::Initc(intc) => { - vm_id = intc.vm_id; - int_id = intc.int_id; - val = intc.val; - } - _ => { - error!("vgic_ipi_handler: illegal ipi"); - return; - } - } - let trgt_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { - None => { - error!("Core {} received vgic msg from unknown VM {}", current_cpu().id, vm_id); - return; - } - Some(vcpu) => vcpu, - }; - restore_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu.clone()); + let vm = match trgt_vcpu.vm() { + None => { + panic!("vgic_ipi_handler: vm is None"); + } + Some(x) => x, + }; + let vgic = vm.vgic(); - let vm = match trgt_vcpu.vm() { - None => { - panic!("vgic_ipi_handler: vm is None"); + if vm_id != vm.id() { + error!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); + return; } - Some(x) => x, - }; - let vgic = vm.vgic(); - - if vm_id != vm.id() { - error!("VM {} received vgic msg from another vm {}", vm.id(), vm_id); - return; - } - if let IpiInnerMsg::Initc(intc) = &msg.ipi_message { match intc.event { InitcEvent::VgicdGichEn => { let hcr = GICH.hcr(); @@ -2026,64 +2040,64 @@ pub fn vgic_ipi_handler(msg: &IpiMessage) { } } InitcEvent::VgicdSetEn => { - vgic.set_enable(trgt_vcpu.clone(), int_id as usize, val != 0); + vgic.set_enable(trgt_vcpu, int_id as usize, val != 0); } InitcEvent::VgicdSetPend => { - vgic.set_pend(trgt_vcpu.clone(), int_id as usize, val != 0); + vgic.set_pend(trgt_vcpu, int_id as usize, val != 0); } InitcEvent::VgicdSetPrio => { - vgic.set_prio(trgt_vcpu.clone(), int_id as usize, val); + vgic.set_prio(trgt_vcpu, int_id as usize, val); } InitcEvent::VgicdSetCfg => { - vgic.set_icfgr(trgt_vcpu.clone(), int_id as usize, val); + vgic.set_icfgr(trgt_vcpu, int_id as usize, val); } InitcEvent::VgicdRoute => { - let interrupt_option = vgic.get_int(trgt_vcpu.clone(), bit_extract(int_id as usize, 0, 10)); - if let Some(interrupt) = interrupt_option { + if let Some(interrupt) = vgic.get_int(trgt_vcpu, bit_extract(int_id as usize, 0, 10)) { let interrupt_lock = interrupt.lock.lock(); - if vgic_int_get_owner(trgt_vcpu.clone(), interrupt.clone()) { - if vgic_int_vcpu_is_target(&trgt_vcpu, &interrupt) { - vgic.add_lr(trgt_vcpu.clone(), interrupt.clone()); + if vgic_int_get_owner(trgt_vcpu, interrupt) { + if vgic_int_vcpu_is_target(trgt_vcpu, interrupt) { + vgic.add_lr(trgt_vcpu, interrupt); } - vgic_int_yield_owner(trgt_vcpu.clone(), interrupt.clone()); + vgic_int_yield_owner(trgt_vcpu, interrupt); } drop(interrupt_lock); } } InitcEvent::Vgicdinject => { - crate::kernel::interrupt_vm_inject(trgt_vcpu.vm().unwrap(), trgt_vcpu.clone(), int_id as usize); + crate::kernel::interrupt_vm_inject(trgt_vcpu.vm().as_ref().unwrap(), trgt_vcpu, int_id as usize); } _ => { error!("vgic_ipi_handler: core {} received unknown event", current_cpu().id) } } + save_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu); + } else { + error!("vgic_ipi_handler: illegal ipi"); } - save_vcpu_gic(current_cpu().active_vcpu.clone(), trgt_vcpu); } /// emulated interrupt controller initialize -pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { - let vgic_cpu_num = vm.config().cpu_num(); - vm.init_intc_mode(true); +pub fn emu_intc_init(emu_cfg: &VmEmulatedDeviceConfig, vcpu_list: &[Vcpu]) -> Result, ()> { + if emu_cfg.emu_type != EmuDeviceType::EmuDeviceTGicd { + error!("emu_intc_init: emu_type is not EmuDeviceTGicd"); + return Err(()); + } - let vgic = Arc::new(Vgic::default()); + let vcpu_num = vcpu_list.len(); + let mut vgic = Vgic::new(emu_cfg.base_ipa, emu_cfg.length, vcpu_num); - let mut vgicd = vgic.vgicd.lock(); - vgicd.typer = (GICD.typer() & !(GICD_TYPER_CPUNUM_MSK | GICD_TYPER_LPIS) as u32) - | ((((vm.cpu_num() - 1) << GICD_TYPER_CPUNUM_OFF) & GICD_TYPER_CPUNUM_MSK) as u32); - vgicd.iidr = GICD.iidr(); - vgicd.ctlr = 0b10; + emu_register_reg(EmuRegType::SysReg, ICC_SRE_ADDR, vgic_icc_sre_handler); + emu_register_reg(EmuRegType::SysReg, ICC_SGIR_ADDR, vgic_icc_sgir_handler); for i in 0..GIC_SPI_MAX { - vgicd.interrupts.push(VgicInt::new(i)); + vgic.vgicd.interrupts.push(VgicInt::new(i)); } - drop(vgicd); - for i in 0..vgic_cpu_num { - let mut typer = i << GICR_TYPER_PRCNUM_OFF; - let vmpidr = vm.vcpu(i).unwrap().get_vmpidr(); + for vcpu in vcpu_list { + let mut typer = vcpu.id() << GICR_TYPER_PRCNUM_OFF; + let vmpidr = vcpu.get_vmpidr(); typer |= (vmpidr & MPIDR_AFF_MSK) << GICR_TYPER_AFFVAL_OFF; - typer |= !!((i == (vm.cpu_num() - 1)) as usize) << GICR_TYPER_LAST_OFF; + typer |= !!((vcpu.id() == (vcpu_num - 1)) as usize) << GICR_TYPER_LAST_OFF; //need the low 6 bits for LPI/ITS init //DPGS, bit [5]:Sets support for GICR_CTLR.DPG* bits //DirectLPI, bit [3]: Indicates whether this Redistributor supports direct injection of LPIs. @@ -2094,12 +2108,11 @@ pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { let mut cpu_priv = VgicCpuPriv::new( typer, - GICR.get_ctrl(vm.vcpu(i).unwrap().phys_id() as u32) as usize, - GICR.get_iidr(vm.vcpu(i).unwrap().phys_id()) as usize, + GICR.get_ctrl(vcpu.phys_id() as u32) as usize, + GICR.get_iidr(vcpu.phys_id()) as usize, ); for int_idx in 0..GIC_SGIS_NUM { - let vcpu = vm.vcpu(i).unwrap(); let phys_id = vcpu.phys_id(); cpu_priv.interrupts.push(VgicInt::priv_new( @@ -2113,7 +2126,6 @@ pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { } for int_idx in GIC_SGIS_NUM..GIC_PRIVINT_NUM { - let vcpu = vm.vcpu(i).unwrap(); let phys_id = vcpu.phys_id(); cpu_priv.interrupts.push(VgicInt::priv_new( @@ -2126,17 +2138,115 @@ pub fn emu_intc_init(vm: Vm, emu_dev_id: usize) { )); } - let mut vgic_cpu_priv = vgic.cpu_priv.lock(); - vgic_cpu_priv.push(cpu_priv); - drop(vgic_cpu_priv); + vgic.cpu_priv.push(cpu_priv); + } + + Ok(Arc::new(vgic)) +} + +pub struct VgicRedis { + address_range: Range, + vgic: Arc, +} + +impl VgicRedis { + pub fn new(base_ipa: usize, length: usize, vgic: Arc) -> Self { + Self { + address_range: base_ipa..base_ipa + length, + vgic, + } + } + + fn vgicr_emul_ctrl_access(&self, emu_ctx: &EmuContext) { + if !emu_ctx.write { + current_cpu().set_gpr(emu_ctx.reg, GICR.get_ctrl(current_cpu().id as u32) as usize); + } else { + GICR.set_ctrlr(current_cpu().id, current_cpu().get_gpr(emu_ctx.reg)); + } + } +} + +impl EmuDev for VgicRedis { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTGICR } - vm.set_emu_devs(emu_dev_id, EmuDevs::Vgic(vgic.clone())); + fn address_range(&self) -> Range { + self.address_range.clone() + } + + fn handler(&self, emu_ctx: &EmuContext) -> bool { + let vgic = &self.vgic; + let vgicr_id = vgicr_get_id(emu_ctx); + let offset = emu_ctx.address & 0x1ffff; + + trace!( + "current_cpu:{}emul_vgicr_handler addr:{:#x} reg {:?} offset {:#x} is write:{}, val:{:#x}", + current_cpu().id, + emu_ctx.address, + GicrRegs::from(offset), + offset, + emu_ctx.write, + current_cpu().get_gpr(emu_ctx.reg) + ); + + match offset { + VGICR_REG_OFFSET_CLTR => { + self.vgicr_emul_ctrl_access(emu_ctx); + } + VGICR_REG_OFFSET_TYPER => { + vgic.vgicr_emul_typer_access(emu_ctx, vgicr_id as usize); + } + VGICR_REG_OFFSET_ISENABLER0 => { + vgic.emu_isenabler_access(emu_ctx); + } + VGICR_REG_OFFSET_ISPENDR0 => { + vgic.emu_ispendr_access(emu_ctx); + } + VGICR_REG_OFFSET_ISACTIVER0 => { + vgic.emu_isactiver_access(emu_ctx); + } + VGICR_REG_OFFSET_ICENABLER0 => { + vgic.emu_icenabler_access(emu_ctx); + } + VGICR_REG_OFFSET_ICPENDR0 => { + vgic.emu_icpendr_access(emu_ctx); + } + VGICR_REG_OFFSET_ICACTIVER0 => { + vgic.emu_icativer_access(emu_ctx); + } + VGICR_REG_OFFSET_ICFGR0 | VGICR_REG_OFFSET_ICFGR1 => { + vgic.emu_icfgr_access(emu_ctx); + } + VGICR_REG_OFFSET_PROPBASER => { + vgic.emu_probaser_access(emu_ctx); + } + VGICR_REG_OFFSET_PENDBASER => { + vgic.emu_pendbaser_access(emu_ctx); + } + _ => { + if (0x10400..0x10420).contains(&offset) { + vgic.emu_ipriorityr_access(emu_ctx); + } else if (0xffd0..0x10000).contains(&offset) { + vgicr_emul_pidr_access(emu_ctx, vgicr_id as usize); + } else { + vgic.emu_razwi(emu_ctx); + } + } + } + true + } } -pub fn emu_vgicr_init(vm: Vm, emu_dev_id: usize) { - let vigc = vm.emu_dev(vm.intc_dev_id()).clone(); - vm.set_emu_devs(emu_dev_id, vigc); +pub fn emu_vgicr_init(emu_cfg: &VmEmulatedDeviceConfig, vgic: Arc) -> Result, ()> { + if emu_cfg.emu_type != EmuDeviceType::EmuDeviceTGICR { + error!("emu_vgicr_init: emu_type is not EmuDeviceTGICR"); + return Err(()); + } + + let vgicr = Arc::new(VgicRedis::new(emu_cfg.base_ipa, emu_cfg.length, vgic)); + + Ok(vgicr) } const VGICR_REG_OFFSET_CLTR: usize = 0x0; @@ -2185,73 +2295,7 @@ impl From for GicrRegs { } } -pub fn emul_vgicr_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - let vgic = current_cpu().active_vcpu.clone().unwrap().vm().unwrap().vgic(); - let vgicr_id = vgicr_get_id(emu_ctx); - let offset = emu_ctx.address & 0x1ffff; - - trace!( - "current_cpu:{}emul_vgicr_handler addr:{:#x} reg {:?} offset {:#x} is write:{}, val:{:#x}", - current_cpu().id, - emu_ctx.address, - GicrRegs::from(offset), - offset, - emu_ctx.write, - current_cpu().get_gpr(emu_ctx.reg) - ); - - match offset { - VGICR_REG_OFFSET_CLTR => { - vgicr_emul_ctrl_access(emu_ctx); - } - VGICR_REG_OFFSET_TYPER => { - vgic.vgicr_emul_typer_access(emu_ctx, vgicr_id as usize); - } - VGICR_REG_OFFSET_ISENABLER0 => { - vgic.emu_isenabler_access(emu_ctx); - } - VGICR_REG_OFFSET_ISPENDR0 => { - vgic.emu_ispendr_access(emu_ctx); - } - VGICR_REG_OFFSET_ISACTIVER0 => { - vgic.emu_isactiver_access(emu_ctx); - } - VGICR_REG_OFFSET_ICENABLER0 => { - vgic.emu_icenabler_access(emu_ctx); - } - VGICR_REG_OFFSET_ICPENDR0 => { - vgic.emu_icpendr_access(emu_ctx); - } - VGICR_REG_OFFSET_ICACTIVER0 => { - vgic.emu_icativer_access(emu_ctx); - } - VGICR_REG_OFFSET_ICFGR0 | VGICR_REG_OFFSET_ICFGR1 => { - vgic.emu_icfgr_access(emu_ctx); - } - VGICR_REG_OFFSET_PROPBASER => { - vgic.emu_probaser_access(emu_ctx); - } - VGICR_REG_OFFSET_PENDBASER => { - vgic.emu_pendbaser_access(emu_ctx); - } - _ => { - if (0x10400..0x10420).contains(&offset) { - vgic.emu_ipriorityr_access(emu_ctx); - } else if (0xffd0..0x10000).contains(&offset) { - vgicr_emul_pidr_access(emu_ctx, vgicr_id as usize); - } else { - vgic.emu_razwi(emu_ctx); - } - } - } - true -} - -pub fn partial_passthrough_intc_init(vm: Vm) { - vm.init_intc_mode(false); -} - -pub fn vgic_set_hw_int(vm: Vm, int_id: usize) { +pub fn vgic_set_hw_int(vm: &Vm, int_id: usize) { if int_id < GIC_SGIS_NUM { return; } @@ -2316,7 +2360,7 @@ pub fn vgic_icc_sgir_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { //for rk3588 the aff1 vtarget <<= (sgir & 0xf0000) >> 16; } - vgic_target_translate(vm, vtarget as u32, true) as usize + vgic_target_translate(&vm, vtarget as u32, true) as usize }; vgic_send_sgi_msg(current_cpu().active_vcpu.clone().unwrap(), targtlist, int_id); } diff --git a/src/arch/aarch64/vm.rs b/src/arch/aarch64/vm.rs new file mode 100644 index 0000000000000000000000000000000000000000..58579a8de7f21b93662ff36ed2a7fa3c16d41c2d --- /dev/null +++ b/src/arch/aarch64/vm.rs @@ -0,0 +1,31 @@ +use crate::kernel::{IntCtrlType, Vm}; + +impl Vm { + /// Init the VM's interrupt controller mode. + pub fn init_intc_mode(&self, intc_type: IntCtrlType) { + use super::{GICC_CTLR_EN_BIT, GICC_CTLR_EOIMODENS_BIT}; + use cortex_a::registers::HCR_EL2; + + let (gich_ctlr, hcr) = match intc_type { + IntCtrlType::Emulated => ( + (GICC_CTLR_EN_BIT | GICC_CTLR_EOIMODENS_BIT) as u32, + (HCR_EL2::VM::Enable + + HCR_EL2::IMO::EnableVirtualIRQ + + HCR_EL2::FMO::EnableVirtualFIQ + + HCR_EL2::TSC::EnableTrapEl1SmcToEl2 + + HCR_EL2::RW::EL1IsAarch64) + .value, + ), + IntCtrlType::Passthrough => ( + (GICC_CTLR_EN_BIT) as u32, + (HCR_EL2::VM::Enable + HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::TSC::EnableTrapEl1SmcToEl2).value, + ), + }; + + for vcpu in self.vcpu_list() { + debug!("vm {} vcpu {} set {:?} hcr", self.id(), vcpu.id(), intc_type); + vcpu.set_gich_ctlr(gich_ctlr); + vcpu.set_hcr(hcr); + } + } +} diff --git a/src/arch/riscv64/cache.rs b/src/arch/riscv64/cache.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6555a29cf4b7f065ccab461248283e4944dc863 --- /dev/null +++ b/src/arch/riscv64/cache.rs @@ -0,0 +1,23 @@ +use riscv; + +/// TODO: Current RISCV ISA doesn't support this feature. +/// Invalidate the data cache for the given address range. +/// # Safety: +/// The 'start' and 'len' must be valid address and length. +pub unsafe fn cache_invalidate_d(_start: usize, _len: usize) {} + +/// TODO: Current RISCV ISA doesn't support this feature. +pub fn cache_clean_invalidate_d(_start: usize, _len: usize) {} + +pub fn isb() { + // SAFETY: + // Fence_I only flushes the instruction cache, which doesn't have effect on data. + unsafe { riscv::asm::fence_i() }; +} + +pub fn fence() { + // SAFETY: + // Fence allows all the previous load/store instructions to complete + // before any subsequent load/store instructions are executed. + unsafe { riscv::asm::fence() }; +} diff --git a/src/arch/riscv64/context_frame.rs b/src/arch/riscv64/context_frame.rs new file mode 100644 index 0000000000000000000000000000000000000000..790c410f6cceb85516d4ca8ecd71a1a784f23d1c --- /dev/null +++ b/src/arch/riscv64/context_frame.rs @@ -0,0 +1,264 @@ +use core::fmt; +use crate::{arch::VmContextTrait, csrr, csrw}; +use super::{regs::RISCV_REG_NAME, A0_NUM, SP_NUM, SSTATUS_FS, SSTATUS_SD, SSTATUS_VS}; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct Riscv64ContextFrame { + pub gpr: [u64; 32], // including sp + pub sepc: u64, + pub scause: u64, + pub stval: u64, + pub sstatus: u64, + pub sscratch: u64, +} + +pub fn print_vs_regs() { + let mut ctx = VmContext::default(); + + csrr!(ctx.vsstatus, vsstatus); + csrr!(ctx.vsip, vsip); + csrr!(ctx.vsie, vsie); + csrr!(ctx.vstvec, vstvec); + csrr!(ctx.vsscratch, vsscratch); + csrr!(ctx.vsepc, vsepc); + csrr!(ctx.vscause, vscause); + csrr!(ctx.vstval, vstval); + csrr!(ctx.vsatp, vsatp); + + info!("vsstatus: {:016x}", ctx.vsstatus); + info!("vsip: {:016x}", ctx.vsip); + info!("vsie: {:016x}", ctx.vsie); + info!("vstvec: {:016x}", ctx.vstvec); + info!("vsscratch:{:016x}", ctx.vsscratch); + info!("vsepc: {:016x}", ctx.vsepc); + info!("vscause: {:016x}", ctx.vscause); + info!("vstval: {:016x}", ctx.vstval); + info!("vsatp: {:016x}", ctx.vsatp); +} + +impl fmt::Display for Riscv64ContextFrame { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + for i in 0..31 { + write!(f, "${:03}: {:016x} ", RISCV_REG_NAME[i + 1], self.gpr[i + 1])?; + if (i + 1) % 2 == 0 { + writeln!(f)?; + } + } + writeln!(f, "sepc: {:016x}", self.sepc)?; + write!(f, "scause: {:016x}", self.scause)?; + writeln!(f, " stval: {:016x}", self.stval)?; + writeln!(f, "sstatus: {:016x}", self.sstatus)?; + writeln!(f, "sscratch: {:016x}", self.sscratch)?; + Ok(()) + } +} + +impl crate::arch::ContextFrameTrait for Riscv64ContextFrame { + #[allow(unused_variables)] + fn new(pc: usize, sp: usize, arg: usize) -> Self { + let mut r = Riscv64ContextFrame { + gpr: [0; 32], + sepc: pc as u64, + scause: 0, + stval: 0, + sstatus: 0, + sscratch: 0, + }; + r.set_argument(arg); + r + } + + fn exception_pc(&self) -> usize { + self.sepc as usize + } + + fn set_exception_pc(&mut self, pc: usize) { + self.sepc = pc as u64; + } + + fn stack_pointer(&self) -> usize { + self.gpr[SP_NUM] as usize + } + + fn set_stack_pointer(&mut self, sp: usize) { + self.gpr[SP_NUM] = sp as u64; + } + + fn set_argument(&mut self, arg: usize) { + self.gpr[A0_NUM] = arg as u64 + } + + fn set_gpr(&mut self, index: usize, val: usize) { + self.gpr[index] = val as u64; + } + + fn gpr(&self, index: usize) -> usize { + self.gpr[index] as usize + } +} + +impl Riscv64ContextFrame { + pub fn default() -> Riscv64ContextFrame { + Riscv64ContextFrame { + gpr: [0; 32], + sepc: 0, + scause: 0, + stval: 0, + sstatus: 0, + sscratch: 0, + } + } + + pub fn print_scause() {} +} + +// represent as C struct format +#[repr(C)] +#[repr(align(16))] +#[derive(Copy, Clone, Debug, Default)] +pub struct VmCtxFpsimd { + fp: [u64; 32], + // TODO: save simd regs like vector +} + +impl VmCtxFpsimd { + pub fn reset(&mut self) { + self.fp.iter_mut().for_each(|x| *x = 0); + } +} + +pub struct VmContext { + fpsimd: VmCtxFpsimd, + + // hvip needs to be placed in the VmContext because interrupt injection is different for different VMs + pub hvip: u64, + // TODO: hstatus also needs to be placed here because the configuration of VTSR, VTW, SPV, etc. varies on different VMs + pub hstatus: u64, + + // VS-mode registers + pub vsstatus: u64, + pub vsip: u64, + pub vsie: u64, + pub vstvec: u64, + pub vsscratch: u64, + pub vsepc: u64, + pub vscause: u64, + pub vstval: u64, + pub vsatp: u64, + + // vcpu's cpuid + pub cpuid: u64, + + pub next_timer_intr: u64, +} + +const HSTATUS_SPV: u64 = 1 << 7; + +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 { + fpsimd: VmCtxFpsimd::default(), + hvip: 0, + hstatus: hstatus_mem | HSTATUS_SPV, + vsstatus: SSTATUS_FS | SSTATUS_VS | SSTATUS_SD, + vsip: 0, + vsie: 0, + vstvec: 0, + vsscratch: 0, + vsepc: 0, + vscause: 0, + vstval: 0, + vsatp: 0, + cpuid: 0, + next_timer_intr: 0xffffffffffff, + } + } +} + +impl VmContextTrait for VmContext { + // Initialize all registers of the VmContext to 0 + fn reset(&mut self) { + let hstatus_mem: u64; + csrr!(hstatus_mem, hstatus); + + self.fpsimd.reset(); + + self.hvip = 0; + self.hstatus = hstatus_mem | HSTATUS_SPV; + + // clear all privilege regs + self.vsstatus = SSTATUS_FS | SSTATUS_VS | SSTATUS_SD; + self.vsip = 0; + self.vsie = 0; + self.vstvec = 0; + self.vsscratch = 0; + self.vsepc = 0; + self.vscause = 0; + self.vstval = 0; + self.vsatp = 0; + + self.cpuid = 0; + + // Set it to a large value so that it does not trigger a timer interrupt + self.next_timer_intr = 0xffffffffffff; + } + + // Save some VS-mode privilege regs + fn ext_regs_store(&mut self) { + csrr!(self.vsstatus, vsstatus); + csrr!(self.vsip, vsip); + csrr!(self.vsie, vsie); + csrr!(self.vstvec, vstvec); + csrr!(self.vsscratch, vsscratch); + csrr!(self.vsepc, vsepc); + csrr!(self.vscause, vscause); + csrr!(self.vstval, vstval); + csrr!(self.vsatp, vsatp); + + csrr!(self.hvip, hvip); + csrr!(self.hstatus, hstatus); + } + + // Restore some VS-mode privilege level registers + fn ext_regs_restore(&self) { + csrw!(vsstatus, self.vsstatus); + csrw!(vsip, self.vsip); + csrw!(vsie, self.vsie); + csrw!(vstvec, self.vstvec); + csrw!(vsscratch, self.vsscratch); + csrw!(vsepc, self.vsepc); + csrw!(vscause, self.vscause); + csrw!(vstval, self.vstval); + csrw!(vsatp, self.vsatp); + + csrw!(hvip, self.hvip); + csrw!(hstatus, self.hstatus); + } + + // Save fp, simd regs + fn fpsimd_save_context(&mut self) { + // todo!() + } + + // Restore fp, simd regs + fn fpsimd_restore_context(&self) { + // TODO: + // todo!() + } + + fn gic_save_state(&mut self) {} + + fn gic_restore_state(&self) {} + + fn gic_ctx_reset(&mut self) {} + + fn reset_vtimer_offset(&mut self) { + todo!() + } +} diff --git a/src/arch/riscv64/cpu.rs b/src/arch/riscv64/cpu.rs new file mode 100644 index 0000000000000000000000000000000000000000..2d75d462b3609ecf8660dcb5263f69d105d11010 --- /dev/null +++ b/src/arch/riscv64/cpu.rs @@ -0,0 +1,44 @@ +use crate::kernel::current_cpu; + +/// Mask (disable) interrupt from perspective of CPU +#[inline(always)] +pub fn cpu_interrupt_mask() { + // SAFETY: Disable the interrupt for current hart. + unsafe { + riscv::register::sstatus::clear_sie(); + } +} + +/// Unmask (enable) interrupt from perspective of CPU +#[inline(always)] +pub fn cpu_interrupt_unmask() { + // SAFETY: Enable the interrupt for current hart. + unsafe { + riscv::register::sstatus::set_sie(); + } +} + +#[inline(always)] +pub fn current_cpu_arch() -> u64 { + let addr: u64; + // SAFETY: The 'tp' register is used to store the current CPU pointer. + unsafe { + core::arch::asm!("mv {}, tp", + out(reg) addr); + } + addr +} + +pub fn get_current_cpuid() -> usize { + current_cpu().id as usize +} + +/// Set the current CPU pointer. +/// # Safety: +/// 1. The 'cpu_addr' must be a valid address. +/// 2. The memory pointed by 'cpu_addr' must have enough space to store the `Cpu` struct. +/// 3. The 'cpu_addr' must be aligned. +pub unsafe fn set_current_cpu(cpu_addr: u64) { + core::arch::asm!("mv tp, {}", + in(reg) cpu_addr); +} diff --git a/src/arch/riscv64/exception.S b/src/arch/riscv64/exception.S new file mode 100644 index 0000000000000000000000000000000000000000..2158e95d87d6a6a3788259005f7e7c368087f336 --- /dev/null +++ b/src/arch/riscv64/exception.S @@ -0,0 +1,221 @@ +.equ RA, 8 +.equ SP, 16 +.equ GP, 24 +.equ TP, 32 +.equ T0, 40 +.equ T1, 48 +.equ T2, 56 +.equ S0, 64 +.equ S1, 72 +.equ A0, 80 +.equ A1, 88 +.equ A2, 96 +.equ A3, 104 +.equ A4, 112 +.equ A5, 120 +.equ A6, 128 +.equ A7, 136 +.equ S2, 144 +.equ S3, 152 +.equ S4, 160 +.equ S5, 168 +.equ S6, 176 +.equ S7, 184 +.equ S8, 192 +.equ S9, 200 +.equ S10, 208 +.equ S11, 216 +.equ T3, 224 +.equ T4, 232 +.equ T5, 240 +.equ T6, 248 +.equ SEPC, 256 +.equ SCAUSE, 264 +.equ STVAL, 272 +.equ SSTATUS, 280 +.equ SSCRATCH, 288 +.equ HART_INFO, 296 +.equ HYPER_SP, 304 + +.equ FRAME_SIZE, (HYPER_SP + 8) + + +// Save all registers except sp and privileged registers to the data structure formed by sp +.macro SAVE_REGS + sd ra, RA(sp) + sd gp, GP(sp) + sd tp, TP(sp) + sd t0, T0(sp) + sd t1, T1(sp) + sd t2, T2(sp) + sd s0, S0(sp) + sd s1, S1(sp) + sd a0, A0(sp) + sd a1, A1(sp) + sd a2, A2(sp) + sd a3, A3(sp) + sd a4, A4(sp) + sd a5, A5(sp) + sd a6, A6(sp) + sd a7, A7(sp) + sd s2, S2(sp) + sd s3, S3(sp) + sd s4, S4(sp) + sd s5, S5(sp) + sd s6, S6(sp) + sd s7, S7(sp) + sd s8, S8(sp) + sd s9, S9(sp) + sd s10, S10(sp) + sd s11, S11(sp) + sd t3, T3(sp) + sd t4, T4(sp) + sd t5, T5(sp) + sd t6, T6(sp) +.endm + +// Recover all registers except sp and privileged registers from the data structure formed by sp +.macro RESTORE_REGS + ld ra, RA(sp) + ld gp, GP(sp) + ld tp, TP(sp) + ld t0, T0(sp) + ld t1, T1(sp) + ld t2, T2(sp) + ld s0, S0(sp) + ld s1, S1(sp) + ld a0, A0(sp) + ld a1, A1(sp) + ld a2, A2(sp) + ld a3, A3(sp) + ld a4, A4(sp) + ld a5, A5(sp) + ld a6, A6(sp) + ld a7, A7(sp) + ld s2, S2(sp) + ld s3, S3(sp) + ld s4, S4(sp) + ld s5, S5(sp) + ld s6, S6(sp) + ld s7, S7(sp) + ld s8, S8(sp) + ld s9, S9(sp) + ld s10, S10(sp) + ld s11, S11(sp) + ld t3, T3(sp) + ld t4, T4(sp) + ld t5, T5(sp) + ld t6, T6(sp) +.endm + +.macro VECTOR handler + // Like FarmOS(xv6),save hypervisor trapframe's address in sscratch reg + // including hypervisor_sp, tp(Pointer to the cpu information structure corresponding to this cpu) and so on + + // swap sscratch and sp + csrrw sp, sscratch, sp + bne sp, zero, virt_entry + +// from HS-mode +hs_mode_entry: + csrrw sp, sscratch, sp + // sp: hypervisor's sp, sscratch: 0 + addi sp, sp, -FRAME_SIZE + + SAVE_REGS + + csrr s0, sepc + csrr s1, scause + csrr s2, stval + csrr s3, sstatus + sd s0, SEPC(sp) + sd s1, SCAUSE(sp) + sd s2, STVAL(sp) + sd s3, SSTATUS(sp) + + csrr s0, sscratch + + // pass ctx + mv a0, sp + + call \handler + j context_pop + +virt_entry: + // store general regs + SAVE_REGS + + // save VM's sp to Trapframe + csrr t0, sscratch + sd t0, SP(sp) + + // clear sscratch,indicating that we're trapped into kernel mode + csrw sscratch, zero + + // save Trapframe(sscratch) + sd sp, SSCRATCH(sp) + + csrr s0, sepc + csrr s1, scause + csrr s2, stval + csrr s3, sstatus + sd s0, SEPC(sp) + sd s1, SCAUSE(sp) + sd s2, STVAL(sp) + sd s3, SSTATUS(sp) + + # Load the tp saved on the stack + ld tp, HART_INFO(sp) + + // pass ctx + mv a0, sp + mv s0, sp + + ld sp, HYPER_SP(sp) + + call \handler + j context_pop +.endm + +.global context_vm_entry +context_vm_entry: + // TODO: Before entry,we need to write sstatus,sscratch,sepc into ctx pointed by a0 + mv sp, a0 + j return_to_vm + +context_pop: + // after 5 + bne s0, zero, return_to_vm_pre + +return_to_hypervisor: + ld s1, SEPC(sp) + csrw sepc, s1 // set jumping destination + + RESTORE_REGS + + addi sp, sp, FRAME_SIZE + sret + + +return_to_vm_pre: + mv sp, s0 // s0 = old sscratch + +return_to_vm: + ld s1, SEPC(sp) + ld s2, SSTATUS(sp) + ld s3, SSCRATCH(sp) + csrw sepc, s1 // set jumping destination + csrw sstatus, s2 // Set sstatus,configure the next phase to jump to the S-mode + csrw sscratch, s3 + + // restore general regs + RESTORE_REGS + + ld sp, SP(sp) + // Currently, sp is vm's sp,pointing to vm's address space + sret + +.global exception_entry +exception_entry: + VECTOR exception_rust_handler + diff --git a/src/arch/riscv64/exception.rs b/src/arch/riscv64/exception.rs new file mode 100644 index 0000000000000000000000000000000000000000..7982454d41cb2c456e391b7adeb5a0cf22a1503e --- /dev/null +++ b/src/arch/riscv64/exception.rs @@ -0,0 +1,210 @@ +use core::sync::atomic::AtomicUsize; +use alloc::string::String; +use riscv::register::hstatus::{self, VirtualizationMode}; +use rustsbi::spec::hsm::{EID_HSM, HART_STOP}; +use spin::Once; + +use crate::arch::{ + hypervisor_handle_ecall, ldst_guest_page_fault_handler, A0_NUM, A1_NUM, A2_NUM, A3_NUM, A4_NUM, A5_NUM, A6_NUM, + A7_NUM, +}; +use crate::kernel::{current_cpu, hvc_guest_handler, interrupt_handler}; +use super::interface::ContextFrame; +use super::{init_ecall_handler, riscv_get_pending_irqs}; +use riscv::register::{sstatus, vsstatus}; + +#[cfg(not(feature = "sbi_legacy"))] +use super::VmHart; + +pub const INTR_CAUSE: [&str; 16] = [ + "Reserved", + "Supervisor software interrupt", + "Virtual Supervisor software interrupt", + "Machine software interrupt", + "Reserved", + "Supervisor timer interrupt", + "Virtual Supervisor timer interrupt", + "Machine timer interrupt", + "Reserved", + "Supervisor external interrupt", + "Virtual Supervisor external interrupt", + "Machine external interrupt", + "Supervisor guest external interrupt", + "Reserved", + "Reserved", + "Reserved", +]; + +pub const EXCEPTION_CAUSE: [&str; 24] = [ + "Instruction address misaligned", + "Instruction access fault", + "Illegal instruction", + "Breakpoint", + "Load address misaligned", + "Load access fault", + "Store/AMO address misaligned", + "Store/AMO access fault", + "Environment call from U-mode or VU-mode", + "Environment call from HS-mode", + "Environment call from VS-mode", + "Environment call from M-mode", + "Instruction page fault", + "Load page fault", + "Reserved", + "Store/AMO page fault", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Instruction guest-page fault", + "Load guest-page fault", + "Virtual instruction", + "Store/AMO guest-page fault", +]; + +const INSTR_PAGE_FAULT: usize = 12; +const LOAD_PAGE_FAULT: usize = 13; +const STORE_PAGE_FAULT: usize = 15; + +const ECALL_FROM_VS: usize = 10; +// spin::Once> = spin::Once::new(); +#[cfg(feature = "sbi_legacy")] +static SBI_VM_HART: Once = Once::new(); +#[cfg(not(feature = "sbi_legacy"))] +static SBI_VM_HART: Once = Once::new(); + +fn ecall_handler(ctx: &mut ContextFrame) { + let eid = ctx.gpr[A7_NUM]; + let fid = ctx.gpr[A6_NUM]; + + let x0 = ctx.gpr[A0_NUM] as usize; + let x1 = ctx.gpr[A1_NUM] as usize; + let x2 = ctx.gpr[A2_NUM] as usize; + let x3 = ctx.gpr[A3_NUM] as usize; + let x4 = ctx.gpr[A4_NUM] as usize; + let x5 = ctx.gpr[A5_NUM] as usize; + + let ret; + + // SBI spec defined this space as firmware specific extension space + // we use this space for hvc call + if (0x0A000000..=0x0AFFFFFF).contains(&eid) { + let hvc_type = ((eid >> 8) & 0xff) as usize; + let event = (eid & 0xff) as usize; + match hvc_guest_handler(hvc_type, event, x0, x1, x2, x3, x4, x5, fid as usize) { + Ok(val) => { + current_cpu().set_gpr(A0_NUM, val); + } + Err(_) => { + warn!("Failed to handle hvc request type 0x{:x} event 0x{:x}", hvc_type, event); + current_cpu().set_gpr(A0_NUM, usize::MAX); + } + } + return; + } + + #[cfg(not(feature = "sbi_legacy"))] + { + ret = + SBI_VM_HART + .call_once(|| VmHart::new()) + .handle_ecall(eid as usize, fid as usize, [x0, x1, x2, x3, x4, x5]); + } + #[cfg(feature = "sbi_legacy")] + { + SBI_VM_HART.call_once(init_ecall_handler); + ret = hypervisor_handle_ecall(eid as usize, fid as usize, [x0, x1, x2, x3, x4, x5]); + } + + if eid == EID_HSM as u64 && fid == HART_STOP as u64 { + // hart_stop,no need to move elr + return; + } + + // Set return value + current_cpu().set_gpr(A0_NUM, ret.error); + current_cpu().set_gpr(A1_NUM, ret.value); +} + +fn get_previous_mode() -> String { + let spv = hstatus::read_spv(); + let spp = sstatus::read().spp(); + if spv as usize == VirtualizationMode::Guest as usize { + if spp == sstatus::SPP::User { + String::from("VU") + } else { + String::from("VS") + } + } else if spp == sstatus::SPP::User { + String::from("U") + } else { + String::from("HS") + } +} + +static TIMER_IRQ_COUNT: AtomicUsize = AtomicUsize::new(0); + +#[no_mangle] +pub fn exception_rust_handler(ctx: &mut ContextFrame) { + /// SAFETY: 'ctx' is a valid pointer to a ContextFrame, which was assigned by the assembly code. + unsafe { + current_cpu().set_ctx(ctx) + } + + // The destination of the jump back is determined by the state of the previous CPU. + // If you enter VS state from VS state, the return address is the address of VS state + // If you enter VS state from HS state, the return address is the address of HS state. + // There is no need to manually set the spv bit of hstatus + let scause = ctx.scause; + let sepc = ctx.sepc; + + let is_intr = ((scause >> 63) & 1) == 1; + let cause = scause & 0xfff; + + if is_intr { + if let Some(id) = riscv_get_pending_irqs(cause as usize) { + interrupt_handler(id); + // Clearing the PLIC interrupt may cause the VM to miss the interrupt signal, resulting in a freeze, so it is not recommended to clear the interrupt + // but wait until the next time you return to VS, because the peripheral interrupt that was not handled before falls back into the Hypervisor + } + } else { + match cause as usize { + ECALL_FROM_VS => { + ecall_handler(ctx); + // Skip the ecall instruction that has already been executed (the instruction length is 4B) + current_cpu().set_elr(current_cpu().get_elr() + 4); + } + 21 | 23 => { + // Load / Store guest-page fault + ldst_guest_page_fault_handler(ctx); + } + 2 => { + // Note:Floating-point instructions can be executed only if both S and VS have the FS switch on the Status register + info!( + "sstatus: {}, vsstatus: {:#x}, previous_mode = {}\n{}", + sstatus::read().fs() as usize, + vsstatus::read(), + get_previous_mode(), + ctx + ); + panic!("illegal instruction: 0x{:08x}", sepc); + } + _ => { + panic!( + "unhandled exception: id = {}, {}\ncpu_id = {}\n{}Previous mode = {}", + cause, + EXCEPTION_CAUSE[cause as usize], + current_cpu().id, + ctx, + get_previous_mode() + ); + } + } + } + + // Note:Do not set the spp and spie bits of sstatus arbitrarily; + // otherwise, when GuestOS VU-mode is trapped in Hypervisor, sret will crash. + // A deep lesson!! Keep the original value + + current_cpu().clear_ctx(); +} diff --git a/src/arch/riscv64/interface.rs b/src/arch/riscv64/interface.rs index c2fbd074fe62905a970185e6641021b55cc47245..0fce2855fb31ac263f2f7155a882aad5278c8956 100644 --- a/src/arch/riscv64/interface.rs +++ b/src/arch/riscv64/interface.rs @@ -8,11 +8,43 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::arch::ArchTrait; +use riscv::register::hgatp::Setting; + pub const PAGE_SIZE: usize = 4096; pub const PAGE_SHIFT: usize = 12; pub const ENTRY_PER_PAGE: usize = PAGE_SIZE / 8; -pub type ContextFrame = super::context_frame::Aarch64ContextFrame; +pub type ContextFrame = super::context_frame::Riscv64ContextFrame; +pub const NUM_CORE: usize = crate::board::PLAT_DESC.cpu_desc.num; pub const WORD_SIZE: usize = 8; pub const PTE_PER_PAGE: usize = PAGE_SIZE / WORD_SIZE; + +pub type Arch = Riscv64Arch; + +pub struct Riscv64Arch; + +impl ArchTrait for Riscv64Arch { + fn wait_for_interrupt() { + // SAFETY: Wait for interrupt + unsafe { riscv::asm::wfi() }; + } + + fn install_vm_page_table(base: usize, vmid: usize) { + // TODO: Too many VMs may result in insufficient bits of vm id + use riscv::register::hgatp::Mode; + let setting = Setting::new(Mode::Sv39x4, vmid as u16, base >> 12); + riscv::register::hgatp::set(&setting); + + // SAFETY: Flush gTLB + unsafe { core::arch::riscv64::hfence_gvma_all() }; + + // SAFETY: Flush I-Cache + unsafe { riscv::asm::fence_i() }; + + // Flush D-Cache + // TODO: Is the D-Cache refresh here necessary? + // If it is VIPT, you do not need to refresh the D-Cache + } +} diff --git a/src/arch/riscv64/interrupt.rs b/src/arch/riscv64/interrupt.rs new file mode 100644 index 0000000000000000000000000000000000000000..3272f9ab9276431ef69d4c2733935b295bf52e57 --- /dev/null +++ b/src/arch/riscv64/interrupt.rs @@ -0,0 +1,267 @@ +use sbi::HartMask; +use crate::arch::psci_vcpu_on; +use crate::arch::InterruptController; +use crate::arch::PLIC; +use crate::arch::PLICTrait; +use crate::kernel::vm; +use crate::kernel::IpiInnerMsg; +use crate::kernel::IpiMessage; +use crate::kernel::current_cpu; +use crate::kernel::PowerEvent; +use crate::kernel::VcpuState; +use spin::Mutex; + +// reference: fu740-c000-manual +const INTERRUPT_NUM_MAX: usize = 70; +const PRIORITY_NUM_MAX: usize = 7; + +pub const CLINT_IRQ_BASE: usize = 60; +pub const IRQ_IPI: usize = 60; // for clint, not plic +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; + +const UART0_IRQ: usize = 10; +const VIRTIO_IRQ: usize = 1; + +pub struct IntCtrl; + +pub static GLOBAL_PLIC: Mutex = Mutex::new(PLIC::new(PLIC_BASE_ADDR as usize)); + +// True PLIC +impl InterruptController for IntCtrl { + const NUM_MAX: usize = INTERRUPT_NUM_MAX; + + const PRI_NUN_MAX: usize = PRIORITY_NUM_MAX; + + const IRQ_IPI: usize = IRQ_IPI; + + const IRQ_HYPERVISOR_TIMER: usize = IRQ_HYPERVISOR_TIMER; + + const IRQ_GUEST_TIMER: usize = IRQ_GUEST_TIMER; + + fn init() { + #[cfg(not(feature = "secondary_start"))] + 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() }; + } + + fn enable(int_id: usize, en: bool) { + if en { + // Note: for timer intr, ipi intr and other reserved intr + // give it a specified fake intr id + if int_id >= CLINT_IRQ_BASE { + if int_id == IRQ_HYPERVISOR_TIMER { + // SAFETY: Enable timer interrupt + unsafe { riscv::register::sie::set_stimer() } + } else if int_id == IRQ_IPI { + // SAFETY: Enable software interrupt(IPI) + unsafe { riscv::register::sie::set_ssoft() } + } else { + panic!("enable intr {} not supported", int_id); + } + } else { + GLOBAL_PLIC + .lock() + .set_enable(int_id, crate::arch::PLICMode::Machine, current_cpu().id); + } + } else if int_id >= CLINT_IRQ_BASE { + if int_id == IRQ_HYPERVISOR_TIMER { + // SAFETY: Disable timer interrupt + unsafe { riscv::register::sie::clear_stimer() } + } else if int_id == IRQ_IPI { + // SAFETY: Disable software interrupt(IPI) + unsafe { riscv::register::sie::clear_ssoft() } + } else { + panic!("enable intr {} not supported", int_id); + } + } else { + GLOBAL_PLIC + .lock() + .clear_enable(int_id, crate::arch::PLICMode::Machine, current_cpu().id); + } + } + + fn fetch() -> Option { + // Invalid function + todo!() + } + + 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); + } + } + } + + fn finish(int_id: usize) { + GLOBAL_PLIC + .lock() + .set_complete(super::PLICMode::Machine, current_cpu().id, int_id); + } + + #[allow(unused_variables)] + fn ipi_send(cpu_id: usize, ipi_id: usize) { + // TODO: can't specify ipi_id + let _ = sbi::ipi::send_ipi(HartMask::from(cpu_id)); + } + + fn vm_inject(vm: &crate::kernel::Vm, vcpu: &crate::kernel::Vcpu, int_id: usize) { + // Inject interrupt through virtual plic + let vplic = vm.vplic(); + 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 + vplic.inject_intr(int_id); + return; + } + } + + vcpu.push_int(int_id); + } + + #[allow(unused_variables)] + fn vm_register(vm: &crate::kernel::Vm, int_id: usize) { + // register interrupts with the virtual plic, that is, bind the real interrupts to the virtual plic. + // The PLIC operation should be written here, but the int id is added to the vm bitmap by interrupt vm register. + // Therefore, PLIC does not need to do any operation at this time + } + + #[allow(unused_variables)] + fn clear_current_irq(for_hypervisor: bool) { + // Don't do anything for a while + // TODO: Maybe you need to take an interrupt and deal with it + current_cpu().current_irq = 0; + } +} + +const CAUSE_INTR_SOFT: usize = 1; +const CAUSE_INTR_TIMER: usize = 5; +const CAUSE_INTR_EXTERNAL: usize = 9; + +const SIP_SSIP: usize = 1 << 1; + +pub fn deactivate_soft_intr() { + // sip register mapping error for riscv crate is sie (master changed, but not released) + + // SAFETY: + // Clearing the soft interrupt bit of reg sip means deactivate software interrupt, + // which is called just in the IPI handler, so it is safe. + unsafe { + use core::arch::asm; + // Remove the soft interrupt bit of reg sip + // This operation must be before ipi handler,since some handler may return to VM + asm!("csrc sip, {}", in(reg) SIP_SSIP); + } +} + +pub fn riscv_get_pending_irqs(int_cause: usize) -> Option { + // Get interrupt information from CLINT first, if ext hangs in sip, then access PLIC + // If you read the claim register directly, you may read an unknown value + match int_cause { + CAUSE_INTR_SOFT => Some(IRQ_IPI), + 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 { + None + } else { + GLOBAL_PLIC + .lock() + .set_complete(super::PLICMode::Machine, current_cpu().id, irq); + Some(irq) + } + } + _ => { + panic!("unhandled interrupt cause: {}", int_cause); + } + } +} + +pub fn psci_ipi_handler(msg: IpiMessage) { + info!("psci_ipi_handler: cpu{} receive psci ipi", current_cpu().id); + match msg.ipi_message { + IpiInnerMsg::Power(power_msg) => { + // True power event + + // AssignAndOn events,indicates that the vcpu is allocated to a cpu and needs to run + if let PowerEvent::PsciIpiVcpuAssignAndCpuOn = power_msg.event { + trace!("receive PsciIpiVcpuAssignAndCpuOn msg"); + let vm = vm(power_msg.src).unwrap(); + let vcpu = vm.vcpuid_to_vcpu(power_msg.vcpuid).unwrap(); + current_cpu().vcpu_array.append_vcpu(vcpu); + } + + let target_vcpu = match current_cpu().vcpu_array.pop_vcpu_through_vmid(power_msg.src) { + Some(vcpu) => vcpu, + None => { + warn!( + "Core {} failed to find target vcpu, source vmid {}", + current_cpu().id, + power_msg.src + ); + return; + } + }; + + match power_msg.event { + PowerEvent::PsciIpiVcpuAssignAndCpuOn => {} + PowerEvent::PsciIpiCpuOn => { + if target_vcpu.state() as usize != VcpuState::Invalid as usize { + warn!( + "psci_ipi_handler: target VCPU {} in VM {} is already running", + target_vcpu.id(), + target_vcpu.vm().unwrap().id() + ); + return; + } + info!( + "Core {} (vm {}, vcpu {}) is woke up", + current_cpu().id, + target_vcpu.vm().unwrap().id(), + target_vcpu.id() + ); + psci_vcpu_on(target_vcpu, power_msg.entry, power_msg.context); + } + PowerEvent::PsciIpiCpuOff => { + unimplemented!("PsciIpiCpuOff"); + } + _ => { + panic!( + "unimplemented power event: {} in psci_ipi_handler", + power_msg.event as usize + ); + } + } + } + _ => { + panic!( + "psci_ipi_handler: cpu{} receive illegal psci ipi type {}", + current_cpu().id, + msg.ipi_type as usize + ); + } + } +} + +#[allow(unused_variables)] +pub fn vgic_ipi_handler(msg: IpiMessage) { + todo!() +} diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index 7f0acf34bfc28c9112670dc5b81b5ffc769a9b4d..e5c0b8c6d02a52686e8e55a8c601d955826ac20d 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -1 +1,83 @@ +pub mod cache; +mod context_frame; +mod cpu; +mod exception; +pub mod interface; +pub mod interrupt; +mod page_fault; +mod page_table; +mod plic; +pub mod power; +pub mod regs; +#[cfg(not(feature = "sbi_legacy"))] +mod sbicall; +#[cfg(feature = "sbi_legacy")] +mod sbicall_legacy; +mod smmu; mod start; +pub mod timer; +pub mod tlb; +mod vcpu; +mod vm; +mod vplic; + +use alloc::sync::Arc; +pub use cache::*; +pub use interface::*; +pub use interrupt::*; +pub use context_frame::*; +pub use plic::*; +pub use regs::*; +#[cfg(not(feature = "sbi_legacy"))] +pub use sbicall::*; +#[cfg(feature = "sbi_legacy")] +pub use sbicall_legacy::*; +pub use start::*; +pub use timer::*; +pub use tlb::*; +pub use vplic::*; +pub use page_table::*; +pub use power::*; +pub use cpu::*; +pub use smmu::*; +pub use page_fault::*; + +/// TODO: fake implementations +pub struct GicDesc { + pub gicd_addr: usize, + pub gicc_addr: usize, + pub gich_addr: usize, + pub gicv_addr: usize, + pub maintenance_int_id: usize, +} + +pub struct SmmuDesc { + pub base: usize, + pub interrupt_id: usize, + pub global_mask: u16, +} + +#[repr(C)] +pub struct ArchDesc { + pub gic_desc: GicDesc, + pub smmu_desc: SmmuDesc, +} + +pub const GIC_SGIS_NUM: usize = 16; + +#[derive(Default)] +pub struct GicContext; + +#[allow(unused_variables)] +pub fn gicc_clear_current_irq(for_hypervisor: bool) { + IntCtrl::clear(); +} + +use crate::{config::VmEmulatedDeviceConfig, device::EmuDev}; + +use super::InterruptController; + +#[allow(unused_variables)] +pub fn emu_smmu_init(emu_cfg: &VmEmulatedDeviceConfig) -> Result, ()> { + todo!() +} diff --git a/src/arch/riscv64/page_fault.rs b/src/arch/riscv64/page_fault.rs new file mode 100644 index 0000000000000000000000000000000000000000..7cd1733d9c0db2e946be525a905e7987532b0ed2 --- /dev/null +++ b/src/arch/riscv64/page_fault.rs @@ -0,0 +1,196 @@ +use core::{arch::riscv64::hlvx_hu, panic}; + +use crate::{ + arch::print_vs_regs, + device::{emu_handler, EmuContext}, + kernel::{active_vm, current_cpu}, +}; + +use super::ContextFrame; + +// load guest page fault, need to parse instructions, access the address, access width and other information + +#[inline(always)] +fn inst_width(funct3: u32) -> usize { + match funct3 & 3 { + 0 => 1, + 1 => 2, + 2 => 4, + _ => 8, + } +} + +pub fn read_inst_from_guest_mem(addr: u64) -> u32 { + if addr & 0x1 == 1 { + panic!("read_inst_from_guest_mem: unaligned access"); + } + + // SAFETY: + // 1. Read instruction memory + // 2. The memory is executable but not nessarily readable + // 3. The address is sepc, which is a valid instruction address + // 4. The address is 16bit aligned + let mut inst: u32 = unsafe { hlvx_hu(addr as *const u16) } as u32; + // if inst is ending with 0b11, it means the inst is not a compressed inst + if (inst & 0b11) == 0b11 { + // SAFETY: + // Read the second half of the compressed instruction, which is 16bit aligned + let inst2: u16 = unsafe { hlvx_hu((addr + 2) as *const u16) }; + inst |= (inst2 as u32) << 16; + } + inst +} + +#[inline(always)] +fn get_ins_size(inst: u32) -> usize { + if (inst & 0b11) == 0b11 { + 4 + } else { + // compressed + 2 + } +} + +#[inline(always)] +fn is_compressed(inst: u32) -> bool { + (inst & 0b11) != 0b11 +} + +const TINST_PSEUDO_STORE: u32 = 0x3020; +const TINST_PSEUDO_LOAD: u32 = 0x3000; + +#[inline(always)] +fn is_pseudo_ins(inst: u32) -> bool { + // memory page fault in implicitly memory access of VS-stage memory translation + inst == TINST_PSEUDO_LOAD || inst == TINST_PSEUDO_STORE +} + +#[inline(always)] +fn transformed_inst_size(inst: u32) -> usize { + if ((inst) & 0x2) == 0 { + 2 + } else { + 4 + } +} + +#[inline(always)] +fn is_compressed_ldst(inst: u32) -> bool { + // load, store + (inst & 0xe003) == 0x4000 || (inst & 0xe003) == 0xc000 +} + +#[inline(always)] +fn is_compressed_st(inst: u32) -> bool { + (inst & 0xe003) == 0xc000 +} + +#[inline(always)] +fn is_normal_ldst(inst: u32) -> bool { + // load, store + (inst & 0x7f) == 0x03 || (inst & 0x7f) == 0x23 +} + +#[inline(always)] +fn is_normal_st(inst: u32) -> bool { + (inst & 0x7f) == 0x23 +} + +#[inline(always)] +fn ins_compressed_rd_rs2(inst: u32) -> u32 { + (inst >> 2) & 0x7 +} + +const MATCH_LOAD: u32 = 0x03; +const MATCH_STORE: u32 = 0x23; + +// decode load and store instruction, and return the EmuContext +// if the instruction is not a load or store instruction, return None +#[inline(always)] +fn inst_ldst_decode(inst: u32) -> Option { + // decode load and store instruction in load(store)_guest_page_fault_handler + if is_compressed(inst) { + if !is_compressed_ldst(inst) { + None + } else { + Some(EmuContext { + address: 0, + width: 4, + write: is_compressed_st(inst), + sign_ext: true, + reg: (ins_compressed_rd_rs2(inst) + 8) as usize, + reg_width: 8, + }) + } + } else if !is_normal_ldst(inst) { + None + } else { + let func3 = (inst >> 12) & 0x7; + let reg = if is_normal_st(inst) { + (inst >> 20) & 0x1f // rs2 + } else { + (inst >> 7) & 0x1f // rd + }; + Some(EmuContext { + address: 0, + width: inst_width(func3), + write: is_normal_st(inst), + sign_ext: (func3 & 0x4) == 0, + reg: reg as usize, + reg_width: 8, + }) + } +} + +pub fn ldst_guest_page_fault_handler(ctx: &mut ContextFrame) { + // Faulting address's least-significant two bits are usually stval's least-significant two bits + let addr = (riscv::register::htval::read() << 2) + (riscv::register::stval::read() & 0x3); + let mut inst: u32 = riscv::register::htinst::read() as u32; + let inst_size; + + if inst == 0 { + // if inst does not provide info about the trap, we must read the instruction from the guest memory + // and decode it + let ins_addr = ctx.sepc; + inst = read_inst_from_guest_mem(ins_addr); + inst_size = get_ins_size(inst); + } else if is_pseudo_ins(inst) { + // TODO: we should reinject this in the guest as a fault access + panic!("memory access fault on 1st stage(VS-stage) page table walk"); + } else { + inst_size = transformed_inst_size(inst); + inst |= 0b10; + } + + // decode the instruction + let decoded_emu_ctx = inst_ldst_decode(inst); + + if decoded_emu_ctx.is_none() { + panic!("ldst_guest_page_fault_handler: unknown instruction\nctx: {}", ctx); + } + + let mut emu_ctx = decoded_emu_ctx.unwrap(); + emu_ctx.address = addr; + + // find a handler to handle this mmio access + if !emu_handler(&emu_ctx) { + active_vm().unwrap().show_pagetable(emu_ctx.address); + debug!( + "write {}, width {}, reg width {}, addr {:x}, reg idx {}, reg val 0x{:x}", + emu_ctx.write, + emu_ctx.width, + emu_ctx.reg_width, + emu_ctx.address, + emu_ctx.reg, + current_cpu().get_gpr(emu_ctx.reg), + ); + print_vs_regs(); + panic!( + "data_abort_handler: Failed to handler emul device request, ipa 0x{:x}, sepc 0x{:x}\n{}", + emu_ctx.address, ctx.sepc, ctx + ); + } + + let val = ctx.sepc + inst_size as u64; + current_cpu().set_elr(val as usize); +} diff --git a/src/arch/riscv64/page_table.rs b/src/arch/riscv64/page_table.rs new file mode 100644 index 0000000000000000000000000000000000000000..95d8a359d96bca2372a96d4c74eed01d83fdba99 --- /dev/null +++ b/src/arch/riscv64/page_table.rs @@ -0,0 +1,292 @@ +use super::interface::PAGE_SIZE; +use crate::arch::ArchPageTableEntryTrait; +use crate::mm::PageFrame; +use super::interface::WORD_SIZE; +use super::interface::PTE_PER_PAGE; +use crate::kernel::mem_page_alloc; +use crate::utils::round_up; +use alloc::sync::Arc; +use spin::Mutex; +use alloc::vec::Vec; + +// TODO: +// Consider using Svnapot to use larger page entries, merging consecutive page entries +// Svnapot: supports 64K large pages + +#[repr(transparent)] +#[derive(Copy, Clone, Debug)] +pub struct Riscv64PTEntry(usize); + +// page_table const +pub const LVL0_SHIFT: usize = 30; +pub const LVL1_SHIFT: usize = 21; +pub const LVL2_SHIFT: usize = 12; + +/// PageTable Entry bits +pub const PTE_V: usize = 1 << 0; +pub const PTE_R: usize = 1 << 1; +pub const PTE_W: usize = 1 << 2; +pub const PTE_X: usize = 1 << 3; +pub const PTE_U: usize = 1 << 4; +pub const PTE_G: usize = 1 << 5; +pub const PTE_A: usize = 1 << 6; +pub const PTE_D: usize = 1 << 7; +pub const PTE_RSW: usize = 0b11 << 8; // 2 bits + +// Note: riscv doesn't support marking device memory +// Basic Stage-2 Page Table Entry +pub const PTE_S2_NORMAL: usize = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_DEVICE: usize = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_NORMALNOCACHE: usize = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_RO: usize = PTE_V | PTE_R | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_FIELD_AP_RO: usize = PTE_R; + +/// page_table functions +/// L0 level pagetable's size is 16KB +pub fn pt_lvl0_idx(va: usize) -> usize { + (va >> LVL0_SHIFT) & (PTE_PER_PAGE * 4 - 1) +} + +pub fn pt_lvl1_idx(va: usize) -> usize { + (va >> LVL1_SHIFT) & (PTE_PER_PAGE - 1) +} + +pub fn pt_lvl2_idx(va: usize) -> usize { + (va >> LVL2_SHIFT) & (PTE_PER_PAGE - 1) +} + +/// Page Table Entry Implementation +/// Currently, We use Sv39x4 PageTable +/// Similar to supervisor mode page table entry +// Defines specifications for RISCV page entries: RISCV page entries are pa shifted 2 bits to the right, with perm fields occupying the lower 10 bits +impl ArchPageTableEntryTrait for Riscv64PTEntry { + // Pass a page table entry,with permission bit + fn from_pte(value: usize) -> Self { + Riscv64PTEntry(value) + } + + fn from_pa(pa: usize) -> Self { + // 56 bit PA + Riscv64PTEntry((pa & 0x003F_FFFF_FFFF_F000) >> 2) + } + + fn to_pte(&self) -> usize { + self.0 + } + + fn to_pa(&self) -> usize { + (self.0 & 0xFFFF_FFFF_FFFF_FC00) << 2 + } + + fn valid(&self) -> bool { + (self.0 & PTE_V) != 0 + } + + /// read an item in the page table + fn entry(&self, index: usize) -> Self { + let addr = self.to_pa() + index * WORD_SIZE; + // SAFETY: 'addr' is a valid address of page table item + unsafe { Riscv64PTEntry((addr as *const usize).read_volatile()) } + } + + /// Write the entry in the page table. + /// # Safety: + /// 1. The 'index' must be a valid index, within the range of the page table, usually 0..512 + /// 2. The 'value' must be a valid page table entry, with legal permission bits + unsafe fn set_entry(&self, index: usize, value: Self) { + let addr = self.to_pa() + index * WORD_SIZE; + (addr as *mut usize).write_volatile(value.to_pte()); + } + + // frame_pa is 4KB aligned + fn make_table(frame_pa: usize) -> Self { + Riscv64PTEntry::from_pte((frame_pa >> 2) | PTE_V) + } +} + +#[derive(Clone)] +pub struct PageTable { + pub directory: Arc, + pub pages: Arc>>, +} + +/// RISCV PageTable Features: +/// No PTE_BLOCK, all page are 4KB +/// All leaf PTE are in the 3-depth position +impl PageTable { + pub fn new(directory: PageFrame) -> PageTable { + PageTable { + directory: Arc::new(directory), + pages: Arc::new(Mutex::new(Vec::new())), + } + } + + pub fn base_pa(&self) -> usize { + self.directory.pa() + } + + /// TODO: change a range of va's access permission + /// only change access_permission, not others(PTE_V, PTE_U, PTE_A, PTE_D) + #[allow(unused_variables)] + pub fn access_permission(&self, start_ipa: usize, len: usize, ap: usize) -> (usize, usize) { + todo!() + } + + /// map ipa to pa + /// pa and ipa should be 4KB aligned + /// pte should be pte entry bits + pub fn map(&self, ipa: usize, pa: usize, pte: usize) { + let directory = Riscv64PTEntry::from_pa(self.directory.pa()); + let mut l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + if let Ok(frame) = mem_page_alloc() { + l0e = Riscv64PTEntry::make_table(frame.pa()); + // Set directory's entry + // SAFETY: idx is an index within the permitted range, and l0e is a valid entry + unsafe { directory.set_entry(pt_lvl0_idx(ipa), l0e) } + let mut pages = self.pages.lock(); + pages.push(frame); + } else { + panic!("map lv0 page failed"); + } + } + + let mut l1e = l0e.entry(pt_lvl1_idx(ipa)); + if !l1e.valid() { + if let Ok(frame) = mem_page_alloc() { + l1e = Riscv64PTEntry::make_table(frame.pa()); + // Note: Set the entry for the level 1 page table + // SAFETY: idx is an index within the permitted range, and l1e is a valid entry + unsafe { l0e.set_entry(pt_lvl1_idx(ipa), l1e) } + let mut pages = self.pages.lock(); + pages.push(frame); + } else { + error!("map lv1 page failed"); + return; + } + } + + let l2e = l1e.entry(pt_lvl2_idx(ipa)); + if l2e.valid() { + debug!("map lvl 2 already mapped with 0x{:x}", l2e.to_pte()); + } else { + // SAFETY: + // idx is an index within the permitted range, and value is a valid entry + // (containing a valid PA and permission bits) + unsafe { l1e.set_entry(pt_lvl2_idx(ipa), Riscv64PTEntry::from_pte((pa >> 2) | pte)) }; + } + } + + pub fn unmap(&self, ipa: usize) { + let directory = Riscv64PTEntry::from_pa(self.directory.pa()); + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + return; + } + + let l1e = l0e.entry(pt_lvl1_idx(ipa)); + if !l1e.valid() { + return; + } + + let l2e = l1e.entry(pt_lvl2_idx(ipa)); + if !l2e.valid() { + return; + } + + // check and release l1 page table + // SAFETY: idx is an index within the permitted range, and 0 is an allowed entry value + unsafe { l1e.set_entry(pt_lvl1_idx(ipa), Riscv64PTEntry(0)) }; + if !empty_page(l1e.to_pa()) { + return; + } + let l1e_pa = l1e.to_pa(); + let mut pages = self.pages.lock(); + pages.retain(|pf| pf.pa != l1e_pa); // remove l1 table pageframe from pages + + // Check and release l0 page table + // SAFETY: idx is an index within the permitted range, and 0 is an allowed entry value + unsafe { l0e.set_entry(pt_lvl0_idx(ipa), Riscv64PTEntry(0)) }; + if !empty_page(l0e.to_pa()) { + return; + } + let l0e_pa = l0e.to_pa(); + pages.retain(|pf| pf.pa != l0e_pa); + + // SAFETY: idx is an index within the permitted range, and 0 is an allowed entry value + unsafe { directory.set_entry(pt_lvl0_idx(ipa), Riscv64PTEntry(0)) }; + } + + pub fn map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize) { + let page_num = round_up(len, PAGE_SIZE) / PAGE_SIZE; + trace!( + "map range ipa {:x} len {:x}, to pa {:x} pte {:x}. page_num = {}", + ipa, + len, + pa, + pte, + page_num + ); + for i in 0..page_num { + self.map(ipa + i * PAGE_SIZE, pa + i * PAGE_SIZE, pte); + if i % 0x20000 == 0 { + debug!("map ipa {:x} to pa {:x}", ipa + i * PAGE_SIZE, pa + i * PAGE_SIZE); + } + } + trace!("finish map_range ipa {:x}!", ipa); + } + + pub fn unmap_range(&self, ipa: usize, len: usize) { + let page_num = round_up(len, PAGE_SIZE) / PAGE_SIZE; + for i in 0..page_num { + self.unmap(ipa + i * PAGE_SIZE); + } + } + + pub fn show_pt(&self, ipa: usize) { + let directory = Riscv64PTEntry::from_pa(self.directory.pa()); + debug!("root {:x}", directory.to_pte()); + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + error!("invalid ipa {:x} to l0 pte {:x}", ipa, l0e.to_pte()); + return; + } else { + debug!("l0e {:x}", l0e.to_pte()); + } + + let l1e = l0e.entry(pt_lvl1_idx(ipa)); + if !l1e.valid() { + error!("invalid ipa {:x} to l1 pte {:x}", ipa, l1e.to_pte()); + return; + } else { + debug!("l1e {:x}", l1e.to_pte()); + } + + let l2e = l1e.entry(pt_lvl2_idx(ipa)); + debug!("l1 ipa {:x} to pa {:x}", ipa, l2e.to_pte()); + } + + #[allow(unused_variables)] + pub fn pt_map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize, map_block: bool) { + self.map_range(ipa, len, pa, pte) + } + + #[allow(unused_variables)] + pub fn pt_unmap_range(&self, ipa: usize, len: usize, map_block: bool) { + self.unmap_range(ipa, len); + } +} + +/// TODO: suggest to delete and use reference counter to manage pages +/// judge whether a page is all 0 +pub fn empty_page(addr: usize) -> bool { + for i in 0..(PAGE_SIZE / 8) { + // SAFETY: + // 1. addr is a page aligned address + // 2. The memory is readable + if unsafe { ((addr + i * 8) as *const usize).read_volatile() != 0 } { + return false; + } + } + true +} diff --git a/src/arch/riscv64/page_table_sv57.rs b/src/arch/riscv64/page_table_sv57.rs new file mode 100644 index 0000000000000000000000000000000000000000..9348b67cf657e61b7fff9cc39d0443b907b281a8 --- /dev/null +++ b/src/arch/riscv64/page_table_sv57.rs @@ -0,0 +1,279 @@ +use super::interface::PAGE_SIZE; +use crate::arch::ArchPageTableEntryTrait; +use crate::mm::PageFrame; +use super::interface::PAGE_SHIFT; +use super::interface::WORD_SIZE; +use super::interface::PTE_PER_PAGE; +use crate::kernel::mem_page_alloc; +use crate::utils::round_up; +use alloc::sync::Arc; +use spin::Mutex; +use alloc::vec::Vec; +use crate::kernel::Cpu; + + +// TODO: +// Consider using Svnapot to use larger page entries, merging consecutive page entries +// Svnapot: supports 64K large pages + +#[repr(transparent)] +#[derive(Copy, Clone, Debug)] +pub struct Riscv64PTEntry(usize); + + +// page_table const +pub const LVL0_SHIFT: usize = 30; +pub const LVL1_SHIFT: usize = 21; +pub const LVL2_SHIFT: usize = 12; + + +/// PageTable Entry bits +pub const PTE_V: usize = 1 << 0; +pub const PTE_R: usize = 1 << 1; +pub const PTE_W: usize = 1 << 2; +pub const PTE_X: usize = 1 << 3; +pub const PTE_U: usize = 1 << 4; +pub const PTE_G: usize = 1 << 5; +pub const PTE_A: usize = 1 << 6; +pub const PTE_D: usize = 1 << 7; +pub const PTE_RSW: usize = 0b11 << 8; // 2 bits + +// Note: riscv doesn't support marking device memory +// Basic Stage-2 Page Table Entry +pub const PTE_S2_NORMAL: usize = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_DEVICE: usize = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_NORMALNOCACHE: usize = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_RO: usize = PTE_V | PTE_R | PTE_U | PTE_A | PTE_D; +pub const PTE_S2_FIELD_AP_RO: usize = PTE_R; + +/// page_table functions +pub fn pt_lvl0_idx(va: usize) -> usize { + (va >> LVL0_SHIFT) & (PTE_PER_PAGE - 1) +} + +pub fn pt_lvl1_idx(va: usize) -> usize { + (va >> LVL1_SHIFT) & (PTE_PER_PAGE - 1) +} + +pub fn pt_lvl2_idx(va: usize) -> usize { + (va >> LVL2_SHIFT) & (PTE_PER_PAGE - 1) +} + + +/// Page Table Entry Implementation +/// Currently, We use Sv39x4 PageTable +/// Similar to supervisor mode page table entry +// Defines specifications for RISCV page entries: RISCV page entries are pa shifted 2 bits to the right, with perm fields occupying the lower 10 bits +impl ArchPageTableEntryTrait for Riscv64PTEntry { + // Pass a page table entry,with permission bit + fn from_pte(value: usize) -> Self { + Riscv64PTEntry(value) + } + + fn from_pa(pa: usize) -> Self { + // 56 bit PA + Riscv64PTEntry((pa & 0x003F_FFFF_FFFF_F000) >> 2) + } + + fn to_pte(&self) -> usize { + self.0 + } + + fn to_pa(&self) -> usize { + (self.0 & 0xFFFF_FFFF_FFFF_FC00) << 2 + } + + fn valid(&self) -> bool { + (self.0 & PTE_V) != 0 + } + + /// read an item in the page table + fn entry(&self, index: usize) -> Self { + let addr = self.to_pa() + index * WORD_SIZE; + unsafe { + Riscv64PTEntry((addr as *const usize).read_volatile()) + } + } + + /// Write the entry in the page table. + /// # Safety: + /// 1. The 'index' must be a valid index, within the range of the page table, usually 0..512 + /// 2. The 'value' must be a valid page table entry, with legal permission bits + unsafe fn set_entry(&self, index: usize, value: Self) { + let addr = self.to_pa() + index * WORD_SIZE; + (addr as *mut usize).write_volatile(value.to_pte()); + } + + // frame_pa is 4KB aligned + fn make_table(frame_pa: usize) -> Self { + Riscv64PTEntry::from_pte((frame_pa >> 2) | PTE_V) + } +} + +#[derive(Clone)] +pub struct PageTable { + pub directory: Arc, + pub pages: Arc>>, +} + +/// RISCV PageTable Features: +/// No PTE_BLOCK, all page are 4KB +/// All leaf PTE are in the 3-depth position +impl PageTable { + pub fn new(directory: PageFrame) -> PageTable { + PageTable { + directory: Arc::new(directory), + pages: Arc::new(Mutex::new(Vec::new())) + } + } + + pub fn base_pa(&self) -> usize { + self.directory.pa() + } + + /// TODO: change a range of va's access permission + /// only change access_permission, not others(PTE_V, PTE_U, PTE_A, PTE_D) + pub fn access_permission(&self, start_ipa: usize, len: usize, ap: usize) -> (usize, usize) { + todo!() + } + + /// map ipa to pa + /// pa and ipa should be 4KB aligned + /// pte should be pte entry bits + pub fn map(&self, ipa: usize, pa: usize, pte: usize) { + let directory = Riscv64PTEntry::from_pa(self.directory.pa()); + let mut l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + if let Ok(frame) = mem_page_alloc() { + l0e = Riscv64PTEntry::make_table(frame.pa()); + // Note: Set directory's entry + unsafe { directory.set_entry(pt_lvl0_idx(ipa), l0e) } + let mut pages = self.pages.lock(); + pages.push(frame); + } else { + panic!("map lv0 page failed"); + } + } + + let mut l1e = l0e.entry(pt_lvl1_idx(ipa)); + if !l1e.valid() { + if let Ok(frame) = mem_page_alloc() { + l1e = Riscv64PTEntry::make_table(frame.pa()); + // Note: Set the entry for the level 1 page table + unsafe { l0e.set_entry(pt_lvl1_idx(ipa), l1e) } + let mut pages = self.pages.lock(); + pages.push(frame); + } else { + error!("map lv1 page failed"); + return; + } + } + + let l2e = l1e.entry(pt_lvl2_idx(ipa)); + if l2e.valid() { + debug!("map lvl 2 already mapped with 0x{:x}", l2e.to_pte()); + } else { + unsafe { l1e.set_entry(pt_lvl2_idx(ipa), + Riscv64PTEntry::from_pte((pa >> 2) | pte)) }; + } + } + + pub fn unmap(&self, ipa: usize) { + let directory = Riscv64PTEntry::from_pa(self.directory.pa()); + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + return; + } + + let l1e = l0e.entry(pt_lvl1_idx(ipa)); + if !l1e.valid() { + return; + } + + let l2e = l1e.entry(pt_lvl2_idx(ipa)); + if !l2e.valid() { + return; + } + + // check and release l1 page table + unsafe { l1e.set_entry(pt_lvl1_idx(ipa), Riscv64PTEntry(0)) }; + if !empty_page(l1e.to_pa()) { + return; + } + let l1e_pa = l1e.to_pa(); + let mut pages = self.pages.lock(); + pages.retain(|pf| pf.pa != l1e_pa); // remove l1 table pageframe from pages + + // check and release l0 page table + unsafe { l0e.set_entry(pt_lvl0_idx(ipa), Riscv64PTEntry(0)) }; + if !empty_page(l0e.to_pa()) { + return; + } + let l0e_pa = l0e.to_pa(); + pages.retain(|pf| pf.pa != l0e_pa); + + unsafe { directory.set_entry(pt_lvl0_idx(ipa), Riscv64PTEntry(0)) }; + } + + pub fn map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize) { + let page_num = round_up(len, PAGE_SIZE) / PAGE_SIZE; + info!("map range ipa {:x} len {:x}, to pa {:x} pte {:x}. page_num = {}", ipa, len, pa, pte, page_num); + for i in 0..page_num { + self.map(ipa + i * PAGE_SIZE, pa + i * PAGE_SIZE, pte); + if i % 0x10000 == 0 { + info!("map ipa {:x} to pa {:x}", ipa + i * PAGE_SIZE, pa + i * PAGE_SIZE); + } + } + info!("finish map_range!"); + } + + pub fn unmap_range(&self, ipa: usize, len: usize) { + let page_num = round_up(len, PAGE_SIZE) / PAGE_SIZE; + for i in 0..page_num { + self.unmap(ipa + i * PAGE_SIZE); + } + } + + pub fn show_pt(&self, ipa: usize) { + let directory = Riscv64PTEntry::from_pa(self.directory.pa()); + debug!("root {:x}", directory.to_pte()); + let l0e = directory.entry(pt_lvl0_idx(ipa)); + if !l0e.valid() { + error!("invalid ipa {:x} to l0 pte {:x}", ipa, l0e.to_pte()); + return; + } else { + debug!("l0e {:x}", l0e.to_pte()); + } + + let l1e = l0e.entry(pt_lvl1_idx(ipa)); + if !l1e.valid() { + error!("invalid ipa {:x} to l1 pte {:x}", ipa, l1e.to_pte()); + return; + } else { + debug!("l1e {:x}", l1e.to_pte()); + } + + let l2e = l1e.entry(pt_lvl2_idx(ipa)); + debug!("l1 ipa {:x} to pa {:x}", ipa, l2e.to_pte()); + } + + pub fn pt_map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize, map_block: bool) { + self.map_range(ipa, len, pa, pte) + } + + pub fn pt_unmap_range(&self, ipa: usize, len: usize, map_block: bool) { + self.unmap_range(ipa, len); + } + +} + +/// TODO: suggest to delete and use reference counter to manage pages +/// judge whether a page is all 0 +pub fn empty_page(addr: usize) -> bool { + for i in 0..(PAGE_SIZE / 8) { + if unsafe { ((addr + i * 8) as *const usize).read_volatile() != 0 } { + return false; + } + } + true +} diff --git a/src/arch/riscv64/plic.rs b/src/arch/riscv64/plic.rs new file mode 100644 index 0000000000000000000000000000000000000000..33f52b5efe71069b3d075bf5ef4c3804c5819eca --- /dev/null +++ b/src/arch/riscv64/plic.rs @@ -0,0 +1,250 @@ +use core::ptr::{read_volatile, write_volatile}; +use alloc::vec::Vec; + +pub struct PLIC { + base_addr: usize, +} + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum PLICMode { + Machine, + Supervisor, +} + +pub const PLIC_MAX_IRQ: usize = 511; +pub const MAX_HARTS: usize = 8; + +pub const PLIC_PRIO_BEGIN: usize = 0x0000; +pub const PLIC_PRIO_END: usize = 0x0FFF; + +pub const PLIC_PENDING_BEGIN: usize = 0x1000; +pub const PLIC_PENDING_END: usize = 0x1FFF; + +pub const PLIC_ENABLE_BEGIN: usize = 0x2000; +pub const PLIC_ENABLE_END: usize = 0x1f_ffff; + +pub const PLIC_THRESHOLD_CLAIM_BEGIN: usize = 0x20_0000; +pub const PLIC_THRESHOLD_CLAIM_END: usize = 0x3f_ffff; + +/// PLIC features list: +/// * Get the current Pending interrupt list +/// * claim an interrupt +/// * complete an interrupt +/// * Get enable information +/// * Get priority information +/// * Set enable information +/// * Set priority information +/// * Set threshold information + +pub trait PLICTrait { + fn get_priority(&self, irq: usize) -> usize; + fn set_priority(&self, irq: usize, priority: usize); + fn get_pending(&self, irq: usize) -> bool; + fn get_enable(&self, irq: usize, mode: PLICMode, hart: usize) -> bool; + fn set_enable(&self, irq: usize, mode: PLICMode, hart: usize); + fn clear_enable(&self, irq: usize, mode: PLICMode, hart: usize); + fn get_threshold(&self, mode: PLICMode, hart: usize) -> usize; + fn set_threshold(&self, mode: PLICMode, hart: usize, threshold: usize); + fn get_claim(&self, mode: PLICMode, hart: usize) -> usize; + fn set_complete(&self, mode: PLICMode, hart: usize, irq: usize); + + fn get_pending_list(&self) -> Vec { + let mut pending_list = Vec::new(); + for irq in 1..=PLIC_MAX_IRQ { + if self.get_pending(irq) { + pending_list.push(irq); + } + } + pending_list + } +} + +impl PLIC { + pub const fn new(base_addr: usize) -> PLIC { + PLIC { base_addr } + } + + fn get_base_addr(&self) -> usize { + self.base_addr + } + + #[inline(always)] + fn get_enable_array_addr(&self, irq: usize, mode: PLICMode, hart: usize) -> Option { + // TODO: assert hart < hart count of platform + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return None; + } + + // hart 0 don't support Supervisor (in sifive U740) + #[cfg(feature = "board_u740")] + if mode == PLICMode::Supervisor && hart == 0 { + return None; + } + + // divided into u740 and virt + #[cfg(feature = "board_u740")] + let res = match hart { + 0 => self.base_addr + PLIC_ENABLE_BEGIN + 0x4 * (irq / 32), + _ => match mode { + PLICMode::Machine => self.base_addr + PLIC_ENABLE_BEGIN + 0x80 + 0x100 * (hart - 1) + 0x4 * (irq / 32), + PLICMode::Supervisor => { + self.base_addr + PLIC_ENABLE_BEGIN + 0x100 + 0x100 * (hart - 1) + 0x4 * (irq / 32) + } + }, + }; + #[cfg(not(feature = "board_u740"))] + let res = match mode { + PLICMode::Machine => self.base_addr + PLIC_ENABLE_BEGIN + 0x80 + 0x100 * hart + 0x4 * (irq / 32), + PLICMode::Supervisor => self.base_addr + PLIC_ENABLE_BEGIN + 0x100 + 0x100 * hart + 0x4 * (irq / 32), + }; + Some(res) + } + + #[inline(always)] + fn get_prio_claim_baseaddr(&self, mode: PLICMode, hart: usize) -> Option { + // TODO: assert hart < hart count of platform + // hart 0 don't support Supervisor (in sifive U740) + + #[cfg(feature = "board_u740")] + if mode == PLICMode::Supervisor && hart == 0 { + return None; + } + + // divided into u740 and virt + #[cfg(feature = "board_u740")] + let res = match hart { + 0 => self.base_addr + PLIC_THRESHOLD_CLAIM_BEGIN, + _ => match mode { + PLICMode::Machine => self.base_addr + PLIC_THRESHOLD_CLAIM_BEGIN + 0x1000 + 0x2000 * (hart - 1), + PLICMode::Supervisor => self.base_addr + PLIC_THRESHOLD_CLAIM_BEGIN + 0x2000 + 0x2000 * (hart - 1), + }, + }; + #[cfg(not(feature = "board_u740"))] + let res = match mode { + PLICMode::Machine => self.base_addr + PLIC_THRESHOLD_CLAIM_BEGIN + 0x1000 + 0x2000 * hart, + PLICMode::Supervisor => self.base_addr + PLIC_THRESHOLD_CLAIM_BEGIN + 0x2000 + 0x2000 * hart, + }; + Some(res) + } +} + +impl PLICTrait for PLIC { + fn get_priority(&self, irq: usize) -> usize { + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return 0; + } + + let addr = self.base_addr + 0x4 * irq; + // SAFETY: + // addr is a valid PLIC device address. + unsafe { read_volatile((addr) as *const u32) as usize } + } + + fn set_priority(&self, irq: usize, priority: usize) { + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return; + } + + let addr = self.base_addr + 0x4 * irq; + // SAFETY: + // addr is a valid PLIC device address that allows to write. + unsafe { + write_volatile((addr) as *mut u32, priority as u32); + } + } + + // pending bits are read only + fn get_pending(&self, irq: usize) -> bool { + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return false; + } + + let addr = self.base_addr + PLIC_PENDING_BEGIN + 0x4 * (irq / 32); + // SAFETY: + // addr is a valid PLIC device address that allows to read. + unsafe { (read_volatile((addr) as *const u32) & (1 << (irq % 32))) != 0 } + } + + fn get_enable(&self, irq: usize, mode: PLICMode, hart: usize) -> bool { + let addr = self.get_enable_array_addr(irq, mode, hart); + match addr { + None => false, + // SAFETY: + // addr is a valid PLIC device address. + Some(addr) => unsafe { (read_volatile((addr) as *const u32) & (1 << (irq % 32))) != 0 }, + } + } + + fn set_enable(&self, irq: usize, mode: PLICMode, hart: usize) { + let addr = self.get_enable_array_addr(irq, mode, hart); + if let Some(addr) = addr { + // SAFETY: + // addr is a valid PLIC device address. + unsafe { + write_volatile( + addr as *mut u32, + read_volatile(addr as *mut u32) | (1 << (irq % 32)) as u32, + ); + } + } + } + + fn clear_enable(&self, irq: usize, mode: PLICMode, hart: usize) { + let addr = self.get_enable_array_addr(irq, mode, hart); + if let Some(addr) = addr { + // SAFETY: + // addr is a valid PLIC device address. + unsafe { + write_volatile( + (addr) as *mut u32, + read_volatile((addr) as *mut u32) & !((1 << (irq % 32)) as u32), + ); + } + } + } + + fn get_threshold(&self, mode: PLICMode, hart: usize) -> usize { + let addr = self.get_prio_claim_baseaddr(mode, hart); + match addr { + None => 0, + // SAFETY: + // addr is a valid PLIC device address. + Some(addr) => unsafe { read_volatile((addr) as *const u32) as usize }, + } + } + + fn set_threshold(&self, mode: PLICMode, hart: usize, threshold: usize) { + let addr = self.get_prio_claim_baseaddr(mode, hart); + if let Some(addr) = addr { + // SAFETY: + // addr is a valid PLIC device address. + unsafe { + write_volatile(addr as *mut u32, threshold as u32); + } + } + } + + fn get_claim(&self, mode: PLICMode, hart: usize) -> usize { + let addr = self.get_prio_claim_baseaddr(mode, hart); + if let Some(addr) = addr { + let addr = addr + 0x4; + // SAFETY: + // addr is a valid PLIC device address. + unsafe { read_volatile(addr as *mut u32) as usize } + } else { + 0 + } + } + + fn set_complete(&self, mode: PLICMode, hart: usize, irq: usize) { + let addr = self.get_prio_claim_baseaddr(mode, hart); + if let Some(addr) = addr { + let addr = addr + 0x4; + // SAFETY: + // addr is a valid PLIC device address. + unsafe { + write_volatile((addr) as *mut u32, irq as u32); + } + } + } +} diff --git a/src/arch/riscv64/power.rs b/src/arch/riscv64/power.rs new file mode 100644 index 0000000000000000000000000000000000000000..f2cab8e673cc344743e229500840fd3b35c7f352 --- /dev/null +++ b/src/arch/riscv64/power.rs @@ -0,0 +1,114 @@ +use crate::{ + arch::get_trapframe_for_hart, + kernel::{current_cpu, CpuState, Scheduler, Vcpu, Vm}, +}; +use riscv::register::hstatus; +use sbi::{ + system_reset::{ResetType, ResetReason}, + hart_state_management::{hart_stop, hart_start}, +}; + +use super::{A0_NUM, A1_NUM}; + +/// Start the given hart, letting it to jump to the 'entry'. +/// # Safety: +/// 1. 'hart_id' must be a valid hart id, within the range of 0..=max_hart_id. +/// 2. 'entry' must be a valid address for cpu to jump. +pub unsafe fn power_arch_cpu_on(hart_id: usize, entry: usize, ctx: usize) -> usize { + // a1 = ctx + if let Err(e) = hart_start(hart_id, entry, ctx) { + warn!("hart {} start failed! {}", hart_id, e); + // use non-0 number when error happens + 1 + } else { + 0 + } +} + +pub fn power_arch_cpu_shutdown() { + // TODO: Maybe just leave the cpu idle? + match hart_stop() { + Ok(_) => {} + Err(e) => { + panic!("hart_stop failed! {}", e); + } + } +} + +/// Reset the system. +/// # Safety: +/// The platform must support reset operation. +pub unsafe fn power_arch_sys_reset() { + sbi::system_reset::system_reset(ResetType::ColdReboot, ResetReason::NoReason).unwrap(); +} + +pub fn power_arch_sys_shutdown() { + let _ = sbi::system_reset::system_reset(ResetType::Shutdown, ResetReason::NoReason); +} + +#[allow(unused_variables)] +pub fn psci_vm_maincpu_on(vmpidr: usize, entry: usize, ctx: usize, vm_id: usize) -> usize { + todo!() +} + +// Reference: During secondary startup, the vcpu executes the corresponding vm context vm entry. +// Carried from the implementation of aarch64 +pub fn psci_vcpu_on(vcpu: &Vcpu, entry: usize, ctx: usize) { + if vcpu.phys_id() != current_cpu().id { + panic!( + "cannot psci on vcpu on cpu {} by cpu {}", + vcpu.phys_id(), + current_cpu().id + ); + } + current_cpu().cpu_state = CpuState::CpuRun; + vcpu.reset_context(); + vcpu.set_gpr(A0_NUM, vcpu.id()); + vcpu.set_gpr(A1_NUM, ctx); + vcpu.set_elr(entry); + + // Just wake up the vcpu and + // invoke current_cpu().sched.schedule() + // let the scheduler enable or disable timer + current_cpu().scheduler().wakeup(vcpu.clone()); + current_cpu().scheduler().do_schedule(); + + // Note: There is no need to turn on the clock interrupt here, because it is already turned on with interrupt init + + #[cfg(target_arch = "riscv64")] + { + // Set sscratch for saving VM's TrapFrame + current_cpu().ctx_mut().unwrap().sscratch = get_trapframe_for_hart(current_cpu().id); + } + + info!( + "(Secondary vcpu start) vcpu on cpu {} begins running: \nhstatus: {:#x}, entry: {:#x}, use ctx:\n{}", + current_cpu().id, + hstatus::read(), + entry, + current_cpu().ctx_mut().unwrap() + ); + + // VM's vcpu's first booting must be entering context_vm_entry + // TODO: The vcpu cannot be started after hart is shut down + extern "C" { + fn context_vm_entry(ctx: usize) -> !; + } + // SAFETY: current_cpu().ctx_ptr() is a valid pointer to a ContextFrame, which was inited by previous code. + unsafe { + context_vm_entry(current_cpu().ctx_ptr().unwrap() as usize); + } +} + +#[allow(unused_variables)] +pub fn power_arch_vm_shutdown_secondary_cores(vm: &Vm) { + todo!() +} + +#[cfg(not(feature = "secondary_start"))] +pub fn guest_cpu_on(_mpidr: usize) {} + +#[cfg(feature = "secondary_start")] +pub fn guest_cpu_on(_mpidr: usize) { + todo!() +} diff --git a/src/arch/riscv64/regs.rs b/src/arch/riscv64/regs.rs new file mode 100644 index 0000000000000000000000000000000000000000..8447dd5f10b984079c879d3aaa759681ca691542 --- /dev/null +++ b/src/arch/riscv64/regs.rs @@ -0,0 +1,76 @@ +#[macro_export] +// Read register's value to memory +macro_rules! csrr { + ($reg: expr) => { + { + let r: u64; + unsafe { + core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) r, options(nomem, nostack)); + } + r + } + }; + ($val: expr, $reg: expr, $asm_width:tt) => { + unsafe { + core::arch::asm!(concat!("csrr {0:", $asm_width, "}, ", stringify!($reg)), out(reg) $val, options(nomem, nostack)); + } + }; + ($val: expr, $reg: expr) => { + unsafe { + core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) $val, options(nomem, nostack)); + } + }; +} + +#[macro_export] +macro_rules! csrw { + ($reg: expr, $val: expr, $asm_width:tt) => { + unsafe { + core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0:", $asm_width, "}"), in(reg) $val, options(nomem, nostack)); + } + }; + ($reg: expr, $val: expr) => { + unsafe { + core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0}"), in(reg) $val, options(nomem, nostack)); + } + }; +} + +pub const RISCV_REG_NAME: [&str; 32] = [ + "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", + "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", +]; + +pub const A0_NUM: usize = 10; +pub const A1_NUM: usize = 11; +pub const A2_NUM: usize = 12; +pub const A3_NUM: usize = 13; +pub const A4_NUM: usize = 14; +pub const A5_NUM: usize = 15; +pub const A6_NUM: usize = 16; +pub const A7_NUM: usize = 17; + +pub const SP_NUM: usize = 2; +pub const TP_NUM: usize = 4; + +#[inline(always)] +pub fn get_index_by_regname(reg_name: &str) -> usize { + for i in 0..RISCV_REG_NAME.len() { + if RISCV_REG_NAME[i] == reg_name { + return i; + } + } + panic!("invalid reg name: {}", reg_name); +} + +pub const SSTATUS_SUM: u64 = 1 << 18; +pub const SSTATUS_FS: u64 = 0x00006000; +// TODO: Introduces dynamic storage of floating point and vector registers, using FS and VS flags to decide whether to save these registers +// Initially turn off the floating point function, and then turn on the VM after it uses the floating point function +pub const SSTATUS_VS: u64 = 3 << 9; + +pub const SSTATUS_SD: u64 = 1 << 63; + +pub const SSTATUS_SPP: u64 = 1 << 8; +pub const SSTATUS_SPIE: u64 = 1 << 5; +pub const SSTATUS_SIE: u64 = 1 << 1; diff --git a/src/arch/riscv64/sbicall.rs b/src/arch/riscv64/sbicall.rs new file mode 100644 index 0000000000000000000000000000000000000000..4a7f8a029c41aabdfcd97b5f82d3be5835f39378 --- /dev/null +++ b/src/arch/riscv64/sbicall.rs @@ -0,0 +1,379 @@ +/// This file is temporarily abandoned and is currently in use sbicall_legacy.rs +use core::{arch::asm, panic}; + +use alloc::vec::Vec; +/// This file provides the interface that the VM accesses to the upper-layer SBI. +/// For some SBI operations, Hypervisor emulation is required instead of +/// directly invoking the M-state SBI software. +use rustsbi::{ + spec::{ + binary::SbiRet, + hsm::{HART_STATE_STARTED, HART_STATE_STOPPED}, + }, + Hsm, Ipi, MachineInfo, Pmu, Reset, RustSBI, Timer, +}; +use sbi::HartMask; +use spin::Mutex; +use timer::timer_arch_get_counter; + +use crate::{ + arch::{ + power_arch_cpu_on, + riscv64::{cpu, vcpu}, + timer, + }, + kernel::{ + active_vm, current_cpu, ipi_send_msg, CpuState, IpiInnerMsg, IpiIntInjectMsg, IpiMessage, IpiPowerMessage, + IpiType, PowerEvent, StartReason, VcpuState, Vm, CPU_IF_LIST, + }, +}; +use crate::kernel::IpiType::IpiTIntInject; +use crate::kernel::IpiInnerMsg::IntInjectMsg; + +use super::{IRQ_IPI, NUM_CORE}; +use crate::kernel::Scheduler; + +pub struct VmHart { + pub env: Mutex>, +} + +#[derive(Default)] +struct VTimer {} + +impl Timer for VTimer { + fn set_timer(&self, stime_value: u64) { + info!("set_timer: {}, current_time: {}", stime_value, timer_arch_get_counter()); + + // Clear the current hart clock interrupt (triggered by setting the next timer) + riscv::register::hvip::clear_timing_interrupt(); + + // SAFETY: Enable timer interrupt + unsafe { + riscv::register::sie::set_stimer(); + } + + // Set the time for the next vcpu clock interruption at the VCPU level + current_cpu() + .active_vcpu + .as_ref() + .unwrap() + .inner + .inner_mut + .lock() + .vm_ctx + .next_timer_intr = stime_value; + } +} + +#[derive(Default)] +struct VIpi {} + +impl Ipi for VIpi { + fn send_ipi(&self, hart_mask: rustsbi::HartMask) -> rustsbi::spec::binary::SbiRet { + info!("sbi_send_ipi: {:?}", hart_mask); + let vm = current_cpu().active_vcpu.as_ref().unwrap().vm().unwrap(); + let vm_id = vm.id(); + let (pcpu_ids, valid) = get_pcpu_ids(vm, hart_mask); + + if !valid { + SbiRet::invalid_param() + } else { + for pcpu in pcpu_ids { + // Send remote IPI request for injection interrupt + let ok = ipi_send_msg( + pcpu, + IpiTIntInject, + IntInjectMsg(IpiIntInjectMsg { vm_id, int_id: IRQ_IPI }), + ); + if !ok { + warn!("send ipi failed!"); + return SbiRet::failed(); + } + } + SbiRet::success(0) + } + } +} + +#[inline(always)] +fn get_pcpu_ids(vm: Vm, hart_mask: rustsbi::HartMask) -> (Vec, bool) { + let mut ret: Vec = Vec::new(); + let nvcpu = vm.inner().lock().vcpu_list.len(); + let mut valid = true; + + for i in 0..64 { + if hart_mask.has_bit(i) { + // If the number of cpus selected by Hart Mask exceeds the number of vcpus, an error is returned + if i >= nvcpu { + valid = false; + break; + } + + let pcpu = vm.vcpu(i).unwrap().phys_id(); + ret.push(pcpu); + } + } + + (ret, valid) +} + +#[derive(Default)] +struct VRfnc {} + +#[inline(always)] +fn rustsbi_hart_mask_to_sbi(hart_mask: rustsbi::HartMask) -> sbi::HartMask { + let mut base: usize = 0; + for i in 0..64 { + if hart_mask.has_bit(i) { + base = i; + break; + } + } + + let mask = sbi::HartMask::new(base); + for i in 0..64 { + if hart_mask.has_bit(i) { + mask.with(i); + } + } + + mask +} + +impl rustsbi::Fence for VRfnc { + fn remote_fence_i(&self, hart_mask: rustsbi::HartMask) -> rustsbi::spec::binary::SbiRet { + sbi::rfence::remote_fence_i(rustsbi_hart_mask_to_sbi(hart_mask)).map_or_else( + |err| SbiRet { + error: (-(err as isize)) as usize, + value: 0, + }, + |x| SbiRet { error: 0, value: 0 }, + ) + } + + fn remote_sfence_vma( + &self, + hart_mask: rustsbi::HartMask, + start_addr: usize, + size: usize, + ) -> rustsbi::spec::binary::SbiRet { + let sbi_mask = rustsbi_hart_mask_to_sbi(hart_mask); + // On harts specified by hart_mask,execute hfence.vvma(vmid, start_addr, size) (vmid is from current cpu's hgatp) + sbi::rfence::remote_hfence_vvma(sbi_mask, start_addr, size).map_or_else( + |err| SbiRet { + error: (-(err as isize)) as usize, + value: 0, + }, + |x| SbiRet { error: 0, value: 0 }, + ) + } + + fn remote_sfence_vma_asid( + &self, + hart_mask: rustsbi::HartMask, + start_addr: usize, + size: usize, + asid: usize, + ) -> rustsbi::spec::binary::SbiRet { + let sbi_mask = rustsbi_hart_mask_to_sbi(hart_mask); + sbi::rfence::remote_hfence_vvma_asid(sbi_mask, start_addr, size, asid).map_or_else( + |err| SbiRet { + error: (-(err as isize)) as usize, + value: 0, + }, + |x| SbiRet { error: 0, value: 0 }, + ) + } +} + +#[derive(Default)] +struct VHsm {} + +impl Hsm for VHsm { + // TODO: needs to handle the cpu hart stop restart. This is not a real stop, + // but into the sleep state, need to call PsciIpiCpuOn + // similar to psci guest cpu on this function (in fact, it is also copied from the aarch64 function) + fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> rustsbi::spec::binary::SbiRet { + let vm = active_vm().unwrap(); + let physical_linear_id = vm.vcpuid_to_pcpuid(hartid); + + if hartid > vm.cpu_num() || physical_linear_id.is_err() { + warn!("hart_start: invalid hartid {}", hartid); + return SbiRet::invalid_param(); + } + + let cpu_idx = physical_linear_id.unwrap(); + + debug!("[hart_start] {}, {}", hartid, start_addr); + + // Get physical cpu's current status + let state = CPU_IF_LIST.lock().get(cpu_idx).unwrap().state_for_start; + + let mut r = 0; + if state == CpuState::CpuInv { + // If a pcpu is in the closed state, schedule a vcpu to start the pcpu + let mut cpu_if_list = CPU_IF_LIST.lock(); + if let Some(cpu_if) = cpu_if_list.get_mut(cpu_idx) { + cpu_if.ctx = opaque as u64; + cpu_if.entry = start_addr as u64; + cpu_if.vm_id = vm.id(); + cpu_if.state_for_start = CpuState::CpuIdle; + cpu_if.vcpuid = hartid; + cpu_if.start_reason = StartReason::SecondaryCore; + } + drop(cpu_if_list); + + let entry_point = crate::arch::_secondary_start as usize; + + // SAFETY: + // It attempts to power on the CPU specified by the cpu_idx parameter. + // The entry is the address of function _secondary_start. + // The ctx here is the cpu_idx. + unsafe { + r = power_arch_cpu_on(hartid, entry_point, cpu_idx); + } + debug!( + "start to power_arch_cpu_on! hartid={:X}, entry_point={:X}", + hartid, entry_point + ); + } else { + // Pass a message so that the corresponding core is started + // Predetermined assumption: The corresponding core has been started (non-secondary start) + let m = IpiPowerMessage { + src: vm.id(), + vcpuid: hartid, + event: PowerEvent::PsciIpiVcpuAssignAndCpuOn, + entry: start_addr, + context: opaque, + }; + + if !ipi_send_msg(physical_linear_id.unwrap(), IpiType::IpiTPower, IpiInnerMsg::Power(m)) { + warn!("psci_guest_cpu_on: fail to send msg"); + return SbiRet::failed(); + } + } + + if r == 0 { + SbiRet::success(0) + } else { + SbiRet::failed() + } + } + + fn hart_stop(&self) -> rustsbi::spec::binary::SbiRet { + // Note: copy from aarch64 code + // save the vcpu context for resume + current_cpu().active_vcpu.clone().unwrap().reset_context(); + + // There is no need to explicitly call do schedule + // because sleep automatically schedules the next vcpu + // when it detects that sleep has an active vcpu + current_cpu() + .scheduler() + .sleep(current_cpu().active_vcpu.clone().unwrap()); + + SbiRet::success(0) + } + + fn hart_get_status(&self, hartid: usize) -> rustsbi::spec::binary::SbiRet { + let vm = active_vm().unwrap(); + let vcpu_ = vm.vcpu(hartid); + if let Some(vcpu) = vcpu_ { + let state = vcpu.state(); + match state { + // Both the running state and the ready state are HART_STATE_STARTED + VcpuState::Running => SbiRet::success(HART_STATE_STARTED), + VcpuState::Ready => SbiRet::success(HART_STATE_STARTED), + // The inactive state and the sleep state (which is no longer functioning) are both HART_STATE_STOPPED + VcpuState::Invalid => SbiRet::success(HART_STATE_STOPPED), + VcpuState::Sleep => SbiRet::success(HART_STATE_STOPPED), + } + } else { + SbiRet::invalid_param() + } + } +} + +#[derive(Default)] +struct VSrst {} + +impl Reset for VSrst { + fn system_reset(&self, reset_type: u32, reset_reason: u32) -> rustsbi::spec::binary::SbiRet { + todo!() + } +} + +#[derive(Default)] +struct VPmu {} + +impl Pmu for VPmu { + fn num_counters(&self) -> usize { + todo!() + } + + fn counter_get_info(&self, counter_idx: usize) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + fn counter_config_matching( + &self, + counter_idx_base: usize, + counter_idx_mask: usize, + config_flags: usize, + event_idx: usize, + event_data: u64, + ) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + fn counter_start( + &self, + counter_idx_base: usize, + counter_idx_mask: usize, + start_flags: usize, + initial_value: u64, + ) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + fn counter_stop( + &self, + counter_idx_base: usize, + counter_idx_mask: usize, + stop_flags: usize, + ) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + fn counter_fw_read(&self, counter_idx: usize) -> rustsbi::spec::binary::SbiRet { + todo!() + } +} + +// The mutual exclusion of SBICALL is maintained by the **underlying function**, and the Hypervisor does not hold the lock +impl VmHart { + pub fn new() -> Self { + let info = MachineInfo { + mvendorid: 0, + marchid: 0, + mimpid: 0, + }; + VmHart { + env: Mutex::new(RustSBI::with_machine_info( + VTimer::default(), + VIpi::default(), + VRfnc::default(), + VHsm::default(), + VSrst::default(), + VPmu::default(), + info, + )), + } + } + + /// ecall dispatch function + #[inline(always)] + pub fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> SbiRet { + self.env.lock().handle_ecall(extension, function, param) + } +} diff --git a/src/arch/riscv64/sbicall_legacy.rs b/src/arch/riscv64/sbicall_legacy.rs new file mode 100644 index 0000000000000000000000000000000000000000..5f84f364ea901464d6aa1b4eb8878c1a881d2436 --- /dev/null +++ b/src/arch/riscv64/sbicall_legacy.rs @@ -0,0 +1,379 @@ +use alloc::vec::Vec; +/// This file provides the interface that the VM accesses to the upper-layer SBI. +/// 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, + spec::{ + base::{impl_id::KVM, EID_BASE, GET_MARCHID, GET_MIMPID, GET_MVENDORID}, + binary::SbiRet, + hsm::{HART_STATE_STARTED, HART_STATE_STOPPED}, + }, + Hsm, Ipi, Pmu, Reset, Timer, +}; +use rustsbi::legacy_stdio::LegacyStdio; +use rustsbi::legacy_stdio::init_legacy_stdio; + +use crate::kernel::{ + active_vm, current_cpu, ipi_send_msg, IpiInnerMsg, IpiIntInjectMsg, IpiPowerMessage, IpiType, PowerEvent, + VcpuState, Vm, +}; +use crate::kernel::IpiType::IpiTIntInject; +use crate::kernel::IpiInnerMsg::IntInjectMsg; + +use super::IRQ_IPI; +use crate::kernel::Scheduler; + +#[derive(Default)] +struct VTimer {} + +impl Timer for VTimer { + fn set_timer(&self, stime_value: u64) { + // Clear the current hart clock interrupt (triggered by setting the next timer) + riscv::register::hvip::clear_timing_interrupt(); + + // SAFETY: Enable timer interrupt + unsafe { riscv::register::sie::set_stimer() }; + + // Set the time for the next vcpu clock interruption at the VCPU level + current_cpu() + .active_vcpu + .as_ref() + .unwrap() + .inner + .inner_mut + .lock() + .vm_ctx + .next_timer_intr = stime_value; + } +} + +#[derive(Default)] +struct VIpi {} + +impl Ipi for VIpi { + fn send_ipi(&self, hart_mask: rustsbi::HartMask) -> rustsbi::spec::binary::SbiRet { + let vm = current_cpu().active_vcpu.as_ref().unwrap().vm().unwrap(); + let vm_id = vm.id(); + let (pcpu_ids, valid) = get_pcpu_ids(&vm, hart_mask); + + if !valid { + SbiRet::invalid_param() + } else { + for pcpu in pcpu_ids { + // Send remote IPI request for injection interrupt + let ok = ipi_send_msg( + pcpu, + IpiTIntInject, + IntInjectMsg(IpiIntInjectMsg { vm_id, int_id: IRQ_IPI }), + ); + if !ok { + warn!("send ipi failed!"); + return SbiRet::failed(); + } + } + SbiRet::success(0) + } + } +} + +#[inline(always)] +fn get_pcpu_ids(vm: &Vm, hart_mask: rustsbi::HartMask) -> (Vec, bool) { + let mut ret: Vec = Vec::new(); + let nvcpu = vm.cpu_num(); + let mut valid = true; + + for i in 0..64 { + if hart_mask.has_bit(i) { + // If the number of cpus selected by Hart Mask exceeds the number of vcpus, an error is returned + if i >= nvcpu { + valid = false; + break; + } + + let pcpu = vm.vcpu(i).unwrap().phys_id(); + ret.push(pcpu); + } + } + + (ret, valid) +} + +#[derive(Default)] +struct VRfnc {} + +// Convert the hart mask in rustsbi format to the hart mask in sbi format +#[inline(always)] +fn rustsbi_hart_mask_to_sbi(hart_mask: rustsbi::HartMask) -> sbi::HartMask { + let mut base: usize = 0; + for i in 0..64 { + if hart_mask.has_bit(i) { + base = i; + break; + } + } + + let mask = sbi::HartMask::new(base); + for i in 0..64 { + if hart_mask.has_bit(i) { + let _ = mask.with(i); + } + } + + mask +} + +// Converts the vCPU-based hart mask to the hart mask of the pcpu corresponding to the vcpu of the current vm +// If an error occurs (for example, the vcpu core number exceeds the number of Vcpus on the vm), return an empty HartMask +#[inline(always)] +fn vcpu_hart_mask_to_pcpu_mask(hart_mask: rustsbi::HartMask) -> sbi::HartMask { + let (pcpus, valid) = get_pcpu_ids(&active_vm().unwrap(), hart_mask.clone()); + if valid { + let mask = sbi::HartMask::new(0); + for pcpu in pcpus { + let _ = mask.with(pcpu); + } + mask + } else { + // no core selected + warn!( + "vcpu_hart_mask_to_pcpu_mask: no core selected since invalid hart_mask: {:?}!", + hart_mask + ); + sbi::HartMask::new(0) + } +} + +// hart mask is parsed by vcpu, not by pcpu +impl rustsbi::Fence for VRfnc { + fn remote_fence_i(&self, hart_mask: rustsbi::HartMask) -> rustsbi::spec::binary::SbiRet { + sbi::rfence::remote_fence_i(vcpu_hart_mask_to_pcpu_mask(hart_mask)).map_or_else( + |err| SbiRet { + error: (-(err as isize)) as usize, + value: 0, + }, + |_x| SbiRet { error: 0, value: 0 }, + ) + } + + fn remote_sfence_vma( + &self, + hart_mask: rustsbi::HartMask, + start_addr: usize, + size: usize, + ) -> rustsbi::spec::binary::SbiRet { + let sbi_mask = vcpu_hart_mask_to_pcpu_mask(hart_mask); + // On harts specified by hart_mask,execute hfence.vvma(vmid, start_addr, size) (vmid is from current cpu's hgatp) + sbi::rfence::remote_hfence_vvma(sbi_mask, start_addr, size).map_or_else( + |err| SbiRet { + error: (-(err as isize)) as usize, + value: 0, + }, + |_x| SbiRet { error: 0, value: 0 }, + ) + } + + fn remote_sfence_vma_asid( + &self, + hart_mask: rustsbi::HartMask, + start_addr: usize, + size: usize, + asid: usize, + ) -> rustsbi::spec::binary::SbiRet { + let sbi_mask = vcpu_hart_mask_to_pcpu_mask(hart_mask); + sbi::rfence::remote_hfence_vvma_asid(sbi_mask, start_addr, size, asid).map_or_else( + |err| SbiRet { + error: (-(err as isize)) as usize, + value: 0, + }, + |_x| SbiRet { error: 0, value: 0 }, + ) + } +} + +#[derive(Default)] +struct VHsm {} + +impl Hsm for VHsm { + // TODO: needs to handle the cpu hart stop restart. This is not a real stop, + // but into the sleep state, need to call PsciIpiCpuOn + // similar to psci guest cpu on this function (in fact, it is also copied from the aarch64 function) + fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> rustsbi::spec::binary::SbiRet { + info!("hart_start: {}, {:08x}, {}", hartid, start_addr, opaque); + + let vcpu_id = hartid; + let vm = active_vm().unwrap(); + let physical_linear_id = vm.vcpuid_to_pcpuid(vcpu_id); + + if vcpu_id >= vm.cpu_num() || physical_linear_id.is_err() { + warn!("hart_start: target vcpu {} not exist", vcpu_id); + return SbiRet::invalid_param(); + } + + let m = IpiPowerMessage { + src: vm.id(), + vcpuid: 0, + event: PowerEvent::PsciIpiCpuOn, + entry: start_addr, + context: opaque, + }; + + // Receiver and handler are in psci_ipi_handler function of `interrupt.rs` + if !ipi_send_msg(physical_linear_id.unwrap(), IpiType::IpiTPower, IpiInnerMsg::Power(m)) { + warn!("psci_guest_cpu_on: fail to send msg"); + return SbiRet::failed(); + } + + SbiRet::success(0) + } + + fn hart_stop(&self) -> rustsbi::spec::binary::SbiRet { + // Note: copy from aarch64 code + // save the vcpu context for resume + current_cpu().active_vcpu.clone().unwrap().reset_context(); + + // There is no need to explicitly call do schedule + // because sleep automatically schedules the next vcpu + // when it detects that sleep has an active vcpu + current_cpu() + .scheduler() + .sleep(current_cpu().active_vcpu.clone().unwrap()); + + SbiRet::success(0) + } + + fn hart_get_status(&self, hartid: usize) -> rustsbi::spec::binary::SbiRet { + let vm = active_vm().unwrap(); + let vcpu_ = vm.vcpu(hartid); + if let Some(vcpu) = vcpu_ { + let state = vcpu.state(); + match state { + // Both the running state and the ready state are HART_STATE_STARTED + VcpuState::Running => SbiRet::success(HART_STATE_STARTED), + VcpuState::Ready => SbiRet::success(HART_STATE_STARTED), + // The inactive state and the sleep state (which is no longer functioning) are both HART_STATE_STOPPED + VcpuState::Invalid => SbiRet::success(HART_STATE_STOPPED), + VcpuState::Sleep => SbiRet::success(HART_STATE_STOPPED), + } + } else { + SbiRet::invalid_param() + } + } +} + +#[derive(Default)] +struct VSrst {} + +impl Reset for VSrst { + #[allow(unused_variables)] + fn system_reset(&self, reset_type: u32, reset_reason: u32) -> rustsbi::spec::binary::SbiRet { + todo!() + } +} + +#[derive(Default)] +struct VPmu {} + +impl Pmu for VPmu { + fn num_counters(&self) -> usize { + todo!() + } + + #[allow(unused_variables)] + fn counter_get_info(&self, counter_idx: usize) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + #[allow(unused_variables)] + fn counter_config_matching( + &self, + counter_idx_base: usize, + counter_idx_mask: usize, + config_flags: usize, + event_idx: usize, + event_data: u64, + ) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + #[allow(unused_variables)] + fn counter_start( + &self, + counter_idx_base: usize, + counter_idx_mask: usize, + start_flags: usize, + initial_value: u64, + ) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + #[allow(unused_variables)] + fn counter_stop( + &self, + counter_idx_base: usize, + counter_idx_mask: usize, + stop_flags: usize, + ) -> rustsbi::spec::binary::SbiRet { + todo!() + } + + #[allow(unused_variables)] + fn counter_fw_read(&self, counter_idx: usize) -> rustsbi::spec::binary::SbiRet { + todo!() + } +} + +#[derive(Default)] +struct VLegacyStdio {} + +const GETC_EMPTY: u8 = 255; + +impl LegacyStdio for VLegacyStdio { + fn getchar(&self) -> u8 { + match sbi::legacy::console_getchar() { + Some(c) => c, + None => GETC_EMPTY, + } + } + + fn putchar(&self, ch: u8) { + sbi::legacy::console_putchar(ch); + } +} +static TIMER: VTimer = VTimer {}; +static IPI: VIpi = VIpi {}; +static RFNC: VRfnc = VRfnc {}; +static HSM: VHsm = VHsm {}; +static SRST: VSrst = VSrst {}; +static PMU: VPmu = VPmu {}; +static STDIO: VLegacyStdio = VLegacyStdio {}; + +// The mutual exclusion of SBICALL is maintained by the **underlying function**, and the Hypervisor does not hold the lock +pub fn init_ecall_handler() { + init_timer(&TIMER); + init_ipi(&IPI); + init_remote_fence(&RFNC); + init_hsm(&HSM); + init_reset(&SRST); + init_pmu(&PMU); + init_legacy_stdio(&STDIO); +} + +/// ecall dispatch function +#[inline(always)] +pub fn hypervisor_handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> SbiRet { + // patch + if extension == EID_BASE { + match function { + GET_MARCHID => { + let marchid = 0; + SbiRet::success(marchid) + } + GET_MIMPID => SbiRet::success(KVM), + GET_MVENDORID => SbiRet::success(0), + _ => rustsbi::ecall(extension, function, param), + } + } else { + rustsbi::ecall(extension, function, param) + } +} diff --git a/src/arch/riscv64/smmu.rs b/src/arch/riscv64/smmu.rs new file mode 100644 index 0000000000000000000000000000000000000000..ca17643da498e80f11773284342dc2ada33a6324 --- /dev/null +++ b/src/arch/riscv64/smmu.rs @@ -0,0 +1,22 @@ +use crate::kernel::Vm; +use crate::device::EmuContext; + +pub fn smmu_init() { + // TODO: implement smmu's initialization + todo!() +} + +#[allow(unused_variables)] +pub fn smmu_add_device(context_id: usize, stream_id: usize) -> bool { + todo!() +} + +#[allow(unused_variables)] +pub fn smmu_vm_init(vm: &Vm) -> bool { + todo!() +} + +#[allow(unused_variables)] +pub fn emu_smmu_handler(_emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { + todo!() +} diff --git a/src/arch/riscv64/start.S b/src/arch/riscv64/start.S deleted file mode 100644 index 1c6442418c5a523cf109a8a5276b9fc1ffd220da..0000000000000000000000000000000000000000 --- a/src/arch/riscv64/start.S +++ /dev/null @@ -1,9 +0,0 @@ -.option norvc - -// insert this file to boot section -.section .text.boot - -.global _start -_start: - j _start - diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs index 51122bd4ffa12c1765b4dc199307f0bd3d9f6215..ec7a6804de052f91d6c4f471b20844de168e0fe3 100644 --- a/src/arch/riscv64/start.rs +++ b/src/arch/riscv64/start.rs @@ -1,3 +1,335 @@ use core::arch::global_asm; +use core::ptr::write_volatile; +use core::sync::atomic::{AtomicBool, AtomicU32}; +use super::interface; +use riscv::asm::fence; +use core::arch::asm; +use super::interface::*; +use crate::arch::current_cpu_arch; +use crate::init; +use riscv::register::hideleg::{VSEIP, VSSIP, VSTIP}; +use riscv::register::{hcounteren, hedeleg}; +use crate::kernel::{cpu_map_self, CpuState, CPU_IF_LIST, CPU_STACK_OFFSET, CPU_STACK_SIZE}; +use core::sync::atomic::Ordering::SeqCst; +use crate::secondary_init; +use crate::arch::regs::{SSTATUS_FS, SSTATUS_VS}; -global_asm!(include_str!("start.S")); +const MAX_CPU: usize = NUM_CORE; + +static BOOTED_CORES: AtomicU32 = AtomicU32::new(0); +pub static ALLOW_BOOT: AtomicBool = AtomicBool::new(false); +const BOOT_STACK_SIZE: usize = PAGE_SIZE * 2; + +// Get the address of the trapframe for each physical core, usually stored in sscratch +pub fn get_trapframe_for_hart(cpu_id: usize) -> u64 { + // SAFETY: + // 1. BOOT_STACK is a valid static variable + // 2. BOOT_STACK_SIZE is a valid constant + // 3. cpu_id is a valid usize, and it is less than MAX_CPU + // 4. So, the addr is a valid address which not exceed the BOOT_STACK's range + unsafe { + let addr = BOOT_STACK.0.as_ptr().add(cpu_id * BOOT_STACK_SIZE); + addr as u64 + } +} + +// every cpu uses a 2-page stack +#[repr(align(8), C)] +pub struct BootStack([u8; BOOT_STACK_SIZE * MAX_CPU]); + +// Put BOOT_STACK in the .bss.stack section,in case it is cleared by clear_bss +#[link_section = ".bss.stack"] +pub static mut BOOT_STACK: BootStack = BootStack([0; BOOT_STACK_SIZE * MAX_CPU]); + +// Note: According to the code in section aarch64, the BOOT_STACK is used as the stack only during the startup phase, +// and the given larger Hypervisor stack will be used thereafter + +global_asm!(include_str!("exception.S")); + +extern "C" { + fn _bss_begin(); + fn _bss_end(); + + pub fn exception_entry(); +} + +#[naked] +#[no_mangle] +#[link_section = ".text.boot"] +pub unsafe extern "C" fn _start() -> ! { + asm!( + r#" + // Mask all interrupts + csrw sie, zero + + // Enable FPU + li t0, {SSTATUS} + csrs sstatus, t0 + + // setup stack sp per core + la t0, {boot_stack} + li t1, (4096 * 2) + mul t2, a0, t1 + add t0, t0, t2 + add sp, t0, t1 + + // set core_id to tp + mv s3, a0 + mv s2, a1 + + jal {clear_bss} + + // pass cpu_id(a0) and dtb(a1) to next func + mv a0, s3 + mv a1, s2 + j {boot_core_init} + "#, + SSTATUS = const (SSTATUS_FS | SSTATUS_VS), + boot_stack = sym BOOT_STACK, + clear_bss = sym clear_bss, + boot_core_init = sym boot_core_init, + options(noreturn) + ); +} + +fn init_sysregs() { + // 3. delegate exceptions and interrupts + hedeleg::write( + hedeleg::INST_ADDR_MISALIGN + | hedeleg::BREAKPOINT + | hedeleg::ENV_CALL_FROM_U_MODE_OR_VU_MODE + | hedeleg::INST_PAGE_FAULT + | hedeleg::LOAD_PAGE_FAULT + | hedeleg::STORE_AMO_PAGE_FAULT, + ); + + // SAFETY: Set the cycle, time, instret registers to be accessible to VS + // TODO: These registers can be emulated later to virtualize the clock in the Hypervisor + unsafe { + hcounteren::set_cycle(); + hcounteren::set_time(); + hcounteren::set_instret(); + } + + // Note:You need to delegate a virtual Timer Interrupt, otherwise the Hypervisor cannot inject a clock interrupt + riscv::register::hideleg::write(VSEIP | VSSIP | VSTIP); + + // 4. clear hvip for emulated intr + riscv::register::hvip::write(0); + + // disable paging + riscv::register::satp::write(0); + + // 5. set stvec handler + // SAFETY: exception_entry is a valid assembly function for handling exceptions + unsafe { + riscv::register::stvec::write(exception_entry as usize, riscv::register::stvec::TrapMode::Direct); + } +} + +#[no_mangle] +fn boot_core_init(cpu_id: usize, dtb: &mut fdt::myctypes::c_void) { + riscv_init(true, cpu_id, dtb); +} + +#[no_mangle] +fn secondary_core_init(cpu_id: usize, dtb: &mut fdt::myctypes::c_void) { + riscv_init(false, cpu_id, dtb); +} + +// Do some common initialization +#[no_mangle] +pub fn riscv_init(is_init_core: bool, cpu_id: usize, dtb: &mut fdt::myctypes::c_void) { + init_sysregs(); + + if is_init_core { + println!("boot from core {}", cpu_id); + } + + // SAFETY: + // clear .bss segment + // clear_bss It may affect the value of some local variables, such as the stack allocated for each core. + // So you need to call rust function after clear_bss + unsafe { fence() }; + + // mark booted cores + BOOTED_CORES.fetch_or(1 << cpu_id, SeqCst); + cpu_map_self(cpu_id); + + // The value obtained for current_cpu_arch is valid only after cpu_map_self + let new_stack = current_cpu_arch() + (CPU_STACK_OFFSET + CPU_STACK_SIZE) as u64 + - core::mem::size_of::() as u64; + + // SAFETY: + // 1. BOOT_STACK is a valid memory space for each core, which is used to save the context of each core + // 2. Every cpu's stack is 2 pages, so the size of the stack is 4096 * 2, greater than 304+4 + // 3. cpu_id is a valid usize, and it is less than MAX_CPU + unsafe { + let addr = BOOT_STACK.0.as_ptr().add(cpu_id * BOOT_STACK_SIZE); + // Later, we will write scratch to a predefined storage location (i.e. the unused BootStack), + // Enables the context to be stored directly here when an interruption occurs + riscv::register::sscratch::write(0); + + // Trapframe saves data in the form of the Riscv64ContextFrame struct in context_frame.rs + // However, there are some other data after Riscv64ContextFrame, including: + // tp: Core number, constant (8byte) + // hypervisor_sp: pointer to the hypervisor stack (8byte) + // because the hypervisor shares a stack pointer register with the kernel and user + write_volatile(addr.add(296) as *mut u64, current_cpu_arch()); + write_volatile(addr.add(304) as *mut u64, new_stack); + } + + // SAFETY: + // sync BOOT_CORE variable to all cores + unsafe { fence() }; + + if is_init_core { + arch_boot_secondary_cores(dtb); + } + + // Wait for other harts to start + let target_boot_map: u32 = (1 << NUM_CORE) - 1; + loop { + // SAFETY: + // Sync the value of BOOTED_CORES, and if it is equal to target_boot_map, + // it means that all cores have been booted + unsafe { fence() }; + if BOOTED_CORES.load(SeqCst) == target_boot_map { + break; + } + } + + if cpu_id == 0 { + // Setup the kernel stack pointer, and jump to init function + // SAFETY: + // 1. tp points to Cpu struct, containing a large stack at offset CPU_STACK_OFFSET + // 2. dtb_addr is a valid pointer to the device tree blob + unsafe { + asm!( + r#" + // set real sp pointer(not boot stack, but real large stack) + mv t0, tp + add t0, t0, {STACK_TOP} + sub sp, t0, {CONTEXT_SIZE} + + mv a0, {DTB_ADDR} + jal {init} + "#, + STACK_TOP = in(reg) (CPU_STACK_OFFSET + CPU_STACK_SIZE), + CONTEXT_SIZE = in(reg) core::mem::size_of::(), + DTB_ADDR = in(reg) dtb, + init = sym init, + options(noreturn) + ); + } + } else { + loop { + // SAFETY: Wait for the activation signal from core 0 + unsafe { fence() }; + if ALLOW_BOOT.load(SeqCst) { + break; + } + } + + // SAFETY: + // tp points to Cpu struct, containing a large stack at offset CPU_STACK_OFFSET + unsafe { + asm!( + r#" + // set real sp pointer(not boot stack, but real large stack) + mv t0, tp + add t0, t0, {STACK_TOP} + sub sp, t0, {CONTEXT_SIZE} + + mv a0, {CPU_ID} + jal {secondary_init} + "#, + STACK_TOP = in(reg) (CPU_STACK_OFFSET + CPU_STACK_SIZE), + CONTEXT_SIZE = in(reg) core::mem::size_of::(), + CPU_ID = in(reg) cpu_id, + secondary_init = sym secondary_init, + options(noreturn) + ); + } + } +} + +pub fn arch_boot_secondary_cores(dtb: &mut fdt::myctypes::c_void) { + // boot other cores + for i in 0..interface::NUM_CORE { + if (BOOTED_CORES.load(SeqCst) & (1 << i)) != 0 { + continue; + } + let result = sbi::hsm::hart_start(i, _secondary_start as usize, dtb as *mut _ as usize); + if let Err(err) = result { + println!("An error happens when booting core {}: {:?}", i, err); + } + } +} + +// Send a start signal to non-zero hart +pub fn arch_boot_other_cores() { + ALLOW_BOOT.store(true, SeqCst); + + // SAFETY: Sync the value of ALLOW_BOOT before running the following code + unsafe { fence() }; + + // Set the state of the other harts to Idle + let mut cpu_if_list = CPU_IF_LIST.lock(); + for cpu_idx in 1..NUM_CORE { + if let Some(cpu_if) = cpu_if_list.get_mut(cpu_idx) { + cpu_if.state_for_start = CpuState::CpuIdle; + } + } +} + +#[naked] +#[no_mangle] +pub unsafe extern "C" fn _secondary_start() -> ! { + asm!( + r#" + // Mask all interrupts + csrw sie, zero + + // Enable FPU + li t0, {SSTATUS} + csrs sstatus, t0 + + // setup stack sp per core + la t0, {boot_stack} + li t1, (4096 * 2) + mul t2, a0, t1 + add t0, t0, t2 + add sp, t0, t1 + + // set core_id to tp + mv s3, a0 + mv s2, a1 + + // pass cpu_id(a0) to next func + mv a0, s3 + mv a1, s2 + j {secondary_core_init} + "#, + SSTATUS = const (SSTATUS_FS | SSTATUS_VS), + boot_stack = sym BOOT_STACK, + secondary_core_init = sym secondary_core_init, + options(noreturn) + ); +} + +unsafe extern "C" fn clear_bss() { + println!( + "bss_begin: {:016x}, bss_end: {:016x}", + _bss_begin as usize, _bss_end as usize + ); + core::slice::from_raw_parts_mut(_bss_begin as *mut u8, _bss_end as usize - _bss_begin as usize).fill(0); +} + +pub fn boot_core() -> usize { + 0 +} + +pub fn is_boot_core(cpu_id: usize) -> bool { + cpu_id == 0 +} diff --git a/src/arch/riscv64/timer.rs b/src/arch/riscv64/timer.rs new file mode 100644 index 0000000000000000000000000000000000000000..f09ce6f6a4791a80304b6b9e883bc72a3be1d15c --- /dev/null +++ b/src/arch/riscv64/timer.rs @@ -0,0 +1,55 @@ +use sbi; +use riscv; +use spin::Mutex; + +use crate::kernel::TIMER_INTERVAL; + +pub static TIMER_FREQ: Mutex = Mutex::new(0); +// cycles per ms +pub static TIMER_SLICE: Mutex = Mutex::new(0); + +pub fn timer_arch_disable_irq() { + // SAFETY: Disable stie bit in sie register, to disable timer interrupt + unsafe { + riscv::register::sie::clear_stimer(); + }; +} + +pub fn timer_arch_enable_irq() { + // SAFETY: Enable stie bit in sie register, to enable timer interrupt + unsafe { + riscv::register::sie::set_stimer(); + }; +} + +#[inline] +pub fn timer_arch_get_counter() -> usize { + riscv::register::time::read() +} + +/// riscv timer freq depends on board +pub fn timer_arch_get_frequency() -> usize { + // TODO: get frequency by device tree + 1000_0000 +} + +/// set next timer's time, i.e. after num ms, trigger time intr +pub fn timer_arch_set(num_ms: usize) { + let slice_lock = TIMER_SLICE.lock(); + let val = *slice_lock * num_ms; + drop(slice_lock); + let _ = sbi::timer::set_timer((timer_arch_get_counter() + val) as u64); +} + +pub fn timer_arch_init() { + timer_arch_enable_irq(); + let mut freq_lock = TIMER_FREQ.lock(); + let mut slice_lock = TIMER_SLICE.lock(); + *freq_lock = timer_arch_get_frequency(); + *slice_lock = (*freq_lock) / 1000; + + drop(freq_lock); + drop(slice_lock); + + timer_arch_set(TIMER_INTERVAL); +} diff --git a/src/arch/riscv64/tlb.rs b/src/arch/riscv64/tlb.rs index bfb822a55c68f5dab5515e208ae53519c9629f13..1c347e22fae058d6fe0d46eb14103db856da4000 100644 --- a/src/arch/riscv64/tlb.rs +++ b/src/arch/riscv64/tlb.rs @@ -11,8 +11,9 @@ use core::arch::asm; pub fn tlb_invalidate_guest_all() { + // SAFETY: + // Invalidate all guest tlb entries. unsafe { asm!("hfence.vvma", "hfence.gvma"); } } - diff --git a/src/arch/riscv64/vcpu.rs b/src/arch/riscv64/vcpu.rs new file mode 100644 index 0000000000000000000000000000000000000000..d79bfd33cfaf9ffa17af5b1d68afdea9c8e8a5ab --- /dev/null +++ b/src/arch/riscv64/vcpu.rs @@ -0,0 +1,33 @@ +use crate::{ + arch::{ContextFrameTrait, SSTATUS_FS, SSTATUS_VS}, + config::VmConfigEntry, + kernel::{current_cpu, Vcpu, VmType}, +}; + +use super::{A0_NUM, A1_NUM, SSTATUS_SPIE, SSTATUS_SPP}; + +impl Vcpu { + /// init boot info, including argument and exception pc + pub fn init_boot_info(&self, config: &VmConfigEntry) { + // Since this function is not necessarily executed on the core corresponding to the vcpu, + // the privilege level register should not be set directly, but the context variable should be set + + info!("init boot info for vcpu {}", self.id()); + + let mut vcpu_inner = self.inner.inner_mut.lock(); + match config.os_type { + VmType::VmTOs => { + // a0 = hartid, a1 = dtb + vcpu_inner.vcpu_ctx.set_gpr(A0_NUM, current_cpu().id); // a0 = hart_id + vcpu_inner.vcpu_ctx.set_gpr(A1_NUM, config.device_tree_load_ipa()); + // a1 = dtb + } + _ => { + let arg = &config.memory_region()[0]; + vcpu_inner.vcpu_ctx.set_argument(arg.ipa_start + arg.length); + } + } + vcpu_inner.vcpu_ctx.set_exception_pc(config.kernel_entry_point()); + vcpu_inner.vcpu_ctx.sstatus = SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_VS; + } +} diff --git a/src/arch/riscv64/vm.rs b/src/arch/riscv64/vm.rs new file mode 100644 index 0000000000000000000000000000000000000000..b021bb7887a1e087be23c9848f55dcd86081fa81 --- /dev/null +++ b/src/arch/riscv64/vm.rs @@ -0,0 +1,8 @@ +use crate::kernel::{IntCtrlType, Vm}; + +impl Vm { + pub fn init_intc_mode(&self, intc_type: IntCtrlType) { + // Note: do nothing here + info!("arch_init_intc_mode: {:?}", intc_type); + } +} diff --git a/src/arch/riscv64/vplic.rs b/src/arch/riscv64/vplic.rs new file mode 100644 index 0000000000000000000000000000000000000000..5d259d466d53a6060ee4adb412bfc4301249f1fb --- /dev/null +++ b/src/arch/riscv64/vplic.rs @@ -0,0 +1,463 @@ +use core::arch::asm; + +use crate::config::VmEmulatedDeviceConfig; +use crate::kernel::{active_vm, current_cpu, Vcpu}; +use crate::device::{EmuContext, EmuDev}; +use super::{GLOBAL_PLIC, IRQ_GUEST_TIMER, IRQ_IPI}; +use super::plic::PLICMode; +use alloc::sync::Arc; +use riscv::register::sie; +use spin::Mutex; +use super::plic::{ + PLIC_PRIO_BEGIN, PLIC_PRIO_END, PLIC_PENDING_BEGIN, PLIC_PENDING_END, PLIC_ENABLE_BEGIN, PLIC_ENABLE_END, + PLIC_THRESHOLD_CLAIM_BEGIN, PLIC_THRESHOLD_CLAIM_END, MAX_HARTS, PLIC_MAX_IRQ, +}; +use crate::arch::riscv64::plic::PLICTrait; + +pub struct VPlic { + // Note:Here you need to take full advantage of the internal variability, + // so that all functions used by VPlic do not have mut permissions, so you need to add Mutex to inner + inner: Arc>, +} + +/// Note: Define the structure of the vPLIC, which includes some information about the VM in addition to the PLIC +pub struct VPlicInner { + emulated_base_addr: usize, + priority: [u32; PLIC_MAX_IRQ + 1], + pending_cnt: [usize; PLIC_MAX_IRQ + 1], + m_enables: [[bool; PLIC_MAX_IRQ + 1]; MAX_HARTS], + s_enables: [[bool; PLIC_MAX_IRQ + 1]; MAX_HARTS], // no hart 0 + m_thresholds: [u32; MAX_HARTS], + s_thresholds: [u32; MAX_HARTS], // no hart 0 + m_claim: [u32; MAX_HARTS], + s_claim: [u32; MAX_HARTS], // no hart 0 +} + +impl VPlic { + pub fn new(emulated_base_addr: usize) -> VPlic { + let inner = VPlicInner { + emulated_base_addr, + priority: [0; PLIC_MAX_IRQ + 1], + pending_cnt: [0; PLIC_MAX_IRQ + 1], + m_enables: [[false; PLIC_MAX_IRQ + 1]; MAX_HARTS], + s_enables: [[false; PLIC_MAX_IRQ + 1]; MAX_HARTS], + m_thresholds: [0; MAX_HARTS], + s_thresholds: [0; MAX_HARTS], + m_claim: [0; MAX_HARTS], + s_claim: [0; MAX_HARTS], + }; + VPlic { + inner: Arc::new(Mutex::new(inner)), + } + } + + pub fn get_emulated_base_addr(&self) -> usize { + let inner = self.inner.lock(); + inner.emulated_base_addr + } + + // Function: Gets the currently pending interrupts + // If there are no pending interrupts, return 0 + fn get_pending_irq(&self, mode: PLICMode, hart: usize) -> usize { + // 1. get prio threshold + let thres = self.get_threshold(mode, hart); + + // 2. get pending list + for i in 1..=PLIC_MAX_IRQ { + if self.get_pending(i) && self.get_enable(i, mode, hart) && self.get_priority(i) >= thres { + return i; + } + } + + 0 + } +} + +impl PLICTrait for VPlic { + #[inline(always)] + fn get_priority(&self, irq: usize) -> usize { + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return 0; + } + + let inner = self.inner.lock(); + inner.priority[irq] as usize + } + + #[inline(always)] + fn set_priority(&self, irq: usize, priority: usize) { + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return; + } + + // Regardless of the current vplic set priority, the PLIC set priority is 1 + let vm = active_vm().unwrap(); + if vm.has_interrupt(irq) { + GLOBAL_PLIC.lock().set_priority(irq, 1); + } + + let mut inner = self.inner.lock(); + inner.priority[irq] = priority as u32; + } + + #[inline(always)] + fn get_pending(&self, irq: usize) -> bool { + if !(irq <= PLIC_MAX_IRQ && irq > 0) { + return false; + } + + let inner = self.inner.lock(); + inner.pending_cnt[irq] > 0 + } + + #[inline(always)] + fn get_enable(&self, irq: usize, mode: PLICMode, hart: usize) -> bool { + if !(irq <= PLIC_MAX_IRQ && irq > 0) || hart >= MAX_HARTS || (mode == PLICMode::Supervisor && hart == 0) { + return false; + } + + let inner = self.inner.lock(); + match mode { + PLICMode::Machine => inner.m_enables[hart][irq], + PLICMode::Supervisor => inner.s_enables[hart][irq], + } + } + + #[inline(always)] + fn set_enable(&self, irq: usize, mode: PLICMode, hart: usize) { + if !(irq <= PLIC_MAX_IRQ && irq > 0) || hart >= MAX_HARTS || (mode == PLICMode::Supervisor && hart == 0) { + return; + } + + let mut inner = self.inner.lock(); + match mode { + PLICMode::Machine => inner.m_enables[hart][irq] = true, + PLICMode::Supervisor => inner.s_enables[hart][irq] = true, + } + + // If this interrupt irq is a real pass-through interrupt number, + // then convey the enable operation to the real plic + let vm = active_vm().unwrap(); + if let Some(pcpu) = vm.vcpu(hart) { + let pcpu = pcpu.phys_id(); + if vm.has_interrupt(irq) { + GLOBAL_PLIC.lock().set_enable(irq, mode, pcpu); + } + } + } + + #[inline(always)] + fn clear_enable(&self, irq: usize, mode: PLICMode, hart: usize) { + if !(irq <= PLIC_MAX_IRQ && irq > 0) || hart >= MAX_HARTS || (mode == PLICMode::Supervisor && hart == 0) { + return; + } + + let mut inner = self.inner.lock(); + match mode { + PLICMode::Machine => inner.m_enables[hart][irq] = false, + PLICMode::Supervisor => inner.s_enables[hart][irq] = false, + } + + let vm = active_vm().unwrap(); + if let Some(pcpu) = vm.vcpu(hart) { + let pcpu = pcpu.phys_id(); + if vm.has_interrupt(irq) { + GLOBAL_PLIC.lock().clear_enable(irq, mode, pcpu); + } + } + } + + #[inline(always)] + fn get_threshold(&self, mode: PLICMode, hart: usize) -> usize { + if hart >= MAX_HARTS || (mode == PLICMode::Supervisor && hart == 0) { + return 0; + } + let inner = self.inner.lock(); + match mode { + PLICMode::Machine => inner.m_thresholds[hart] as usize, + PLICMode::Supervisor => inner.s_thresholds[hart] as usize, + } + } + + #[inline(always)] + fn set_threshold(&self, mode: PLICMode, hart: usize, threshold: usize) { + if hart >= MAX_HARTS || (mode == PLICMode::Supervisor && hart == 0) { + return; + } + + let mut inner = self.inner.lock(); + match mode { + PLICMode::Machine => inner.m_thresholds[hart] = threshold as u32, + PLICMode::Supervisor => inner.s_thresholds[hart] = threshold as u32, + } + } + + fn get_claim(&self, mode: PLICMode, hart: usize) -> usize { + let irq = self.get_pending_irq(mode, hart); + if irq != 0 { + // disable pending + let mut inner = self.inner.lock(); + inner.pending_cnt[irq] -= 1; + } + + // SAFETY: Clear the hvip external interrupt + unsafe { + asm!("csrc hvip, {}", in(reg) (1 << 10)); + } + + irq + } + + #[allow(unused_variables)] + fn set_complete(&self, mode: PLICMode, hart: usize, irq: usize) { + // no action + } +} + +/// Some features of the impl VPlicInner include +/// (Since the VPlicInner is an analog interrupt controller, So we can add a read/write interface to this controller) +/// * Read the value of a register (VM interface) +/// * Write the value of a register (VM interface) +/// * Inject an interrupt: The behavior is to set in Pending, then check Priority and Enable, Threshold, and then trigger Claim and the corresponding interrupt +/// inner function +/// use the related functions of the PLIC structure to perform read and write operations on the related attributes +impl VPlic { + fn get_pending_reg(&self, index: usize) -> u32 { + let mut res: u32 = 0; + let inner = self.inner.lock(); + for i in 0..32 { + if inner.pending_cnt[index * 32 + i] > 0 { + res |= (1 << i) as u32; + } + } + res + } + + fn get_enable_reg(&self, index: usize, mode: PLICMode, hart: usize) -> u32 { + let mut res: u32 = 0; + for i in 0..32 { + if self.get_enable(index * 32 + i, mode, hart) { + res |= (1 << i) as u32; + } + } + res + } + + fn edit_enable_reg(&self, index: usize, mode: PLICMode, hart: usize, val: u32) { + for i in 0..32 { + if (val & (1 << i)) != 0 { + self.set_enable(index * 32 + i, mode, hart); + } else { + self.clear_enable(index * 32 + i, mode, hart); + } + } + } + + // VM Called + // Handle register boundaries, beyond the boundary always return 0 + // 4B aligned read, read size u32 + pub fn vm_read_register(&self, addr: usize) -> u32 { + let offset = addr - self.get_emulated_base_addr(); + // align to 4B + let offset = offset & !0x3; + + if (PLIC_PRIO_BEGIN..=PLIC_PRIO_END).contains(&offset) { + self.get_priority((offset - PLIC_PRIO_BEGIN) / 0x4) as u32 + } else if (PLIC_PENDING_BEGIN..=PLIC_PENDING_END).contains(&offset) { + self.get_pending_reg((offset - PLIC_PENDING_BEGIN) / 0x4) + } else if (PLIC_ENABLE_BEGIN..=PLIC_ENABLE_END).contains(&offset) { + let index = (offset & (0x80 - 1)) / 0x4; + + #[cfg(feature = "board_u740")] + { + let mode = if ((offset & 0x80) != 0) && ((offset & !0x7F) == PLIC_ENABLE_BEGIN) { + PLICMode::Machine + } else { + PLICMode::Supervisor + }; + let hart = (offset - 0x1f80) / 0x100; + self.get_enable_reg(index, mode, hart) + } + #[cfg(not(feature = "board_u740"))] + { + let mode = if (offset & 0x80) != 0 { + PLICMode::Machine + } else { + PLICMode::Supervisor + }; + let hart = (offset - 0x1f80) / 0x100 - 1; + self.get_enable_reg(index, mode, hart) + } + } else if (PLIC_THRESHOLD_CLAIM_BEGIN..=PLIC_THRESHOLD_CLAIM_END).contains(&offset) { + let reg = offset & 0xfff; + let mode: PLICMode; + let hart: usize; + + #[cfg(feature = "board_u740")] + { + mode = if ((offset & 0x2000) != 0) && ((offset & !0xfff) != PLIC_THRESHOLD_CLAIM_BEGIN) { + PLICMode::Supervisor + } else { + PLICMode::Machine + }; + hart = (offset - (PLIC_THRESHOLD_CLAIM_BEGIN - 0x1000)) / 0x2000; + } + #[cfg(not(feature = "board_u740"))] + { + mode = if (offset & 0x2000) != 0 { + PLICMode::Supervisor + } else { + PLICMode::Machine + }; + hart = (offset - PLIC_THRESHOLD_CLAIM_BEGIN) / 0x2000; + } + + if reg == 0x0 { + self.get_threshold(mode, hart) as u32 + } else if reg == 0x4 { + self.get_claim(mode, hart) as u32 + } else { + 0 + } + } else { + panic!("invalid plic register access: 0x{:x} offset: 0x{:x}", addr, offset); + } + } + + pub fn vm_write_register(&self, addr: usize, val: u32) { + let offset = addr - self.get_emulated_base_addr(); + // align to 4B + let offset = offset & !0x3; + + // Pending regs is Read Only + if (PLIC_PRIO_BEGIN..=PLIC_PRIO_END).contains(&offset) { + self.set_priority((offset - PLIC_PRIO_BEGIN) / 0x4, val as usize); + } else if (PLIC_ENABLE_BEGIN..=PLIC_ENABLE_END).contains(&offset) { + let index = (offset & (0x80 - 1)) / 0x4; + + #[cfg(feature = "board_u740")] + { + let mode = if ((offset & 0x80) != 0) && ((offset & !0x7F) == PLIC_ENABLE_BEGIN) { + PLICMode::Machine + } else { + PLICMode::Supervisor + }; + let hart = (offset - 0x1f80) / 0x100; + self.edit_enable_reg(index, mode, hart, val); + } + #[cfg(not(feature = "board_u740"))] + { + let mode = if (offset & 0x80) != 0 { + PLICMode::Machine + } else { + PLICMode::Supervisor + }; + let hart = (offset - 0x1f80) / 0x100 - 1; + self.edit_enable_reg(index, mode, hart, val); + } + } else if (PLIC_THRESHOLD_CLAIM_BEGIN..=PLIC_THRESHOLD_CLAIM_END).contains(&offset) { + let reg = offset & 0xfff; + let mode: PLICMode; + let hart: usize; + + #[cfg(feature = "board_u740")] + { + mode = if ((offset & 0x2000) != 0) && ((offset & !0xfff) != PLIC_THRESHOLD_CLAIM_BEGIN) { + PLICMode::Supervisor + } else { + PLICMode::Machine + }; + hart = (offset - (PLIC_THRESHOLD_CLAIM_BEGIN - 0x1000)) / 0x2000; + } + #[cfg(not(feature = "board_u740"))] + { + mode = if (offset & 0x2000) != 0 { + PLICMode::Supervisor + } else { + PLICMode::Machine + }; + hart = (offset - PLIC_THRESHOLD_CLAIM_BEGIN) / 0x2000; + } + + if reg == 0x0 { + self.set_threshold(mode, hart, val as usize); + } else if reg == 0x4 { + self.set_complete(mode, hart, val as usize); + } + } else { + panic!("invalid plic register access: 0x{:x}, offset: 0x{:x}", addr, offset); + } + // Does not conform to the specification of the visit, do nothing + } + + 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 { + self.inject_external_intr(irq) + } + } + + // Check and inject a PLIC interrupt to VM + fn inject_external_intr(&self, irq: usize) { + let mut inner = self.inner.lock(); + inner.pending_cnt[irq] += 1; + drop(inner); + + // In general, the Hypervisor should enable Machine-level interrupts to obtain Machine-level pending + let mode = PLICMode::Machine; + let vcpu = current_cpu().active_vcpu.as_ref().unwrap().id(); + let fetched_irq = self.get_pending_irq(mode, vcpu as usize); + if fetched_irq != 0 { + // Note: mode and hart are dynamicly fetched + // inject external intr + riscv::register::hvip::trigger_external_interrupt(); + } + } +} + +#[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 vplic = VPlic::new(emu_cfg.base_ipa); + Ok(Arc::new(vplic)) +} + +const VPLIC_LENGTH: usize = 0x600000; + +impl EmuDev for VPlic { + fn emu_type(&self) -> crate::device::EmuDeviceType { + crate::device::EmuDeviceType::EmuDeviceTPlic + } + + fn address_range(&self) -> core::ops::Range { + let base_addr = self.get_emulated_base_addr(); + base_addr..base_addr + VPLIC_LENGTH + } + + // Emulated plic's entry + fn handler(&self, emu_ctx: &EmuContext) -> bool { + let vm = active_vm().unwrap(); + let vplic = vm.vplic(); + 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 { + vplic.vm_write_register(emu_ctx.address, current_cpu().get_gpr(reg_idx) as u32); + } else { + let val = vplic.vm_read_register(emu_ctx.address); + current_cpu().set_gpr(reg_idx, val as usize); + } + + true + } +} diff --git a/src/arch/traits.rs b/src/arch/traits.rs index 16538e90bf3434a48a57e909a0bfc3a43d4021d4..cb53daa33f1f2b4641c2f614c741d2a844843b40 100644 --- a/src/arch/traits.rs +++ b/src/arch/traits.rs @@ -23,6 +23,11 @@ pub trait ContextFrameTrait { fn gpr(&self, index: usize) -> usize; } +pub trait ArchTrait { + fn wait_for_interrupt(); + fn install_vm_page_table(base: usize, vmid: usize); +} + /// Architecture-independent PageTableEntry trait. pub trait ArchPageTableEntryTrait { fn from_pte(value: usize) -> Self; @@ -55,7 +60,19 @@ pub trait InterruptController { fn clear(); fn finish(int_id: usize); fn ipi_send(cpu_id: usize, ipi_id: usize); - fn vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize); - fn vm_register(vm: Vm, int_id: usize); + fn vm_inject(vm: &Vm, vcpu: &Vcpu, int_id: usize); + fn vm_register(vm: &Vm, int_id: usize); fn clear_current_irq(for_hypervisor: bool); } + +pub trait VmContextTrait { + fn reset(&mut self); + fn ext_regs_store(&mut self); + fn ext_regs_restore(&self); + fn fpsimd_save_context(&mut self); + fn fpsimd_restore_context(&self); + fn gic_save_state(&mut self); + fn gic_restore_state(&self); + fn gic_ctx_reset(&mut self); + fn reset_vtimer_offset(&mut self); +} diff --git a/src/board/mod.rs b/src/board/mod.rs index d5b578473f3348c9dc3bb121bd64b53e9bc5350b..583649d60f9b33ae6236e414b4e40537e9377029 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -14,8 +14,10 @@ pub use self::platform_common::*; #[cfg(feature = "pi4")] pub use self::pi4::{Pi4Platform as Platform, PLAT_DESC}; -#[cfg(feature = "qemu")] +#[cfg(all(feature = "qemu", target_arch = "aarch64"))] pub use self::qemu::{QemuPlatform as Platform, PLAT_DESC}; +#[cfg(all(feature = "qemu", target_arch = "riscv64"))] +pub use self::qemu_riscv64::{QemuPlatform as Platform, PLAT_DESC}; #[cfg(feature = "tx2")] pub use self::tx2::{Tx2Platform as Platform, PLAT_DESC}; #[cfg(feature = "rk3588")] @@ -24,8 +26,10 @@ pub use self::rk3588::{Rk3588Platform as Platform, PLAT_DESC}; #[cfg(feature = "pi4")] mod pi4; mod platform_common; -#[cfg(feature = "qemu")] +#[cfg(all(feature = "qemu", target_arch = "aarch64"))] mod qemu; +#[cfg(all(feature = "qemu", target_arch = "riscv64"))] +mod qemu_riscv64; #[cfg(feature = "rk3588")] mod rk3588; #[cfg(feature = "tx2")] diff --git a/src/board/platform_common.rs b/src/board/platform_common.rs index 060d5200b1b0450d9cb7bec8552f9f22aa8f73bf..90298c68dd50deaa83fb6624d8952efccc391254 100644 --- a/src/board/platform_common.rs +++ b/src/board/platform_common.rs @@ -10,6 +10,9 @@ use crate::arch::ArchDesc; +#[cfg(target_arch = "riscv64")] +use crate::arch::arch_boot_other_cores; + /// Maximum number of CPUs supported by the platform pub const PLATFORM_CPU_NUM_MAX: usize = 8; @@ -81,10 +84,6 @@ pub trait PlatOperation { const GICV_BASE: usize; #[cfg(feature = "gicv3")] const GICR_BASE: usize; - #[cfg(feature = "gicv3")] - const ICC_SRE_ADDR: usize; - #[cfg(feature = "gicv3")] - const ICC_SGIR_ADDR: usize; const DISK_PARTITION_0_START: usize = usize::MAX; const DISK_PARTITION_1_START: usize = usize::MAX; @@ -114,19 +113,26 @@ pub trait PlatOperation { /// Powers on secondary cores of the CPU fn power_on_secondary_cores() { - use super::PLAT_DESC; extern "C" { fn _secondary_start(); } - for i in 1..PLAT_DESC.cpu_desc.num { - // SAFETY: - // We iterate all the cores except the primary core so the arch_core_id must be valid. - // Entry is a valid address with executable permission. - // The 'i' is a valid cpu_idx for ctx. - unsafe { - Self::cpu_on(PLAT_DESC.cpu_desc.core_list[i].mpidr, _secondary_start as usize, i); + #[cfg(target_arch = "aarch64")] + { + use super::PLAT_DESC; + for i in 1..PLAT_DESC.cpu_desc.num { + // SAFETY: + // We iterate all the cores except the primary core so the arch_core_id must be valid. + // Entry is a valid address with executable permission. + // The 'i' is a valid cpu_idx for ctx. + unsafe { + Self::cpu_on(PLAT_DESC.cpu_desc.core_list[i].mpidr, _secondary_start as usize, i); + } } } + #[cfg(target_arch = "riscv64")] + { + arch_boot_other_cores(); + } } /// Reboots the system diff --git a/src/board/qemu/platform.rs b/src/board/qemu/platform.rs index c0d6c93a30dad0dce4fb9ea97322a0fc24a21944..0bac511e53de33e6b30983d782aac081fe65a34e 100644 --- a/src/board/qemu/platform.rs +++ b/src/board/qemu/platform.rs @@ -14,8 +14,6 @@ extern crate fdt_rs; // TODO: move these core name to device /// Crate imports for architecture and board configurations use crate::arch::ArchDesc; -#[cfg(feature = "gicv3")] -use crate::arch::sysreg_enc_addr; use crate::board::{ PlatOperation, Platform, PlatCpuCoreConfig, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, PlatMemRegion, ClusterDesc, @@ -47,10 +45,6 @@ impl PlatOperation for QemuPlatform { const GICV_BASE: usize = 0x08040000; #[cfg(feature = "gicv3")] const GICR_BASE: usize = 0x080a0000; - #[cfg(feature = "gicv3")] - const ICC_SRE_ADDR: usize = sysreg_enc_addr(3, 0, 12, 12, 5); - #[cfg(feature = "gicv3")] - const ICC_SGIR_ADDR: usize = sysreg_enc_addr(3, 0, 12, 11, 5); const DISK_PARTITION_0_START: usize = 0; const DISK_PARTITION_1_START: usize = 2097152; diff --git a/src/board/qemu_riscv64/mod.rs b/src/board/qemu_riscv64/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..e79869a290f3854ae3a3e70668f5375678dfecb4 --- /dev/null +++ b/src/board/qemu_riscv64/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub use self::platform::*; + +mod platform; diff --git a/src/board/qemu_riscv64/platform.rs b/src/board/qemu_riscv64/platform.rs new file mode 100644 index 0000000000000000000000000000000000000000..b811fab9d0a64397e87d23e4c0d5bf01505f641c --- /dev/null +++ b/src/board/qemu_riscv64/platform.rs @@ -0,0 +1,147 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// external fdt crate +extern crate fdt_rs; + +// TODO: move these core name to device +/// Crate imports for architecture and board configurations +use crate::arch::ArchDesc; +#[cfg(feature = "gicv3")] +use crate::arch::sysreg_enc_addr; +use crate::board::{ + PlatOperation, Platform, PlatCpuCoreConfig, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, PlatMemRegion, + ClusterDesc, +}; +use crate::board::SchedRule::RoundRobin; + +/// Represents the platform configuration for QEMU virtual machines +pub struct QemuPlatform; + +/// Implementation of platform operations for QEMU virtual machines +impl PlatOperation for QemuPlatform { + /// UART base addresses + const UART_0_ADDR: usize = 0x9000000; + const UART_1_ADDR: usize = 0x9100000; + const UART_2_ADDR: usize = 0x9110000; + + /// UART interrupt numbers + const UART_0_INT: usize = 32 + 0x70; + const UART_1_INT: usize = 32 + 0x72; + + /// Hypervisor UART base address + const HYPERVISOR_UART_BASE: usize = Self::UART_0_ADDR; + + // Invalid fields, just for PlatOperation Trait + const GICD_BASE: usize = 0; + const GICC_BASE: usize = 0; + const GICH_BASE: usize = 0; + const GICV_BASE: usize = 0; + #[cfg(feature = "gicv3")] + const GICR_BASE: usize = 0; + #[cfg(feature = "gicv3")] + const ICC_SRE_ADDR: usize = 0; + #[cfg(feature = "gicv3")] + const ICC_SGIR_ADDR: usize = 0; + + const DISK_PARTITION_0_START: usize = 0; + const DISK_PARTITION_1_START: usize = 0; + const DISK_PARTITION_2_START: usize = 0; + + const DISK_PARTITION_TOTAL_SIZE: usize = 0; + const DISK_PARTITION_0_SIZE: usize = 0; + const DISK_PARTITION_1_SIZE: usize = 0; + const DISK_PARTITION_2_SIZE: usize = 0; + + /// Maps CPU ID to CPU interface number for QEMU + fn cpuid_to_cpuif(cpuid: usize) -> usize { + // PLAT_DESC.cpu_desc.core_list[cpuid].mpidr + cpuid + } + /// Maps CPU interface number to CPU ID for QEMU + fn cpuif_to_cpuid(cpuif: usize) -> usize { + cpuif + } + + // should not add any println!() + /// Converts MPIDR to CPU ID for QEMU, ensuring it is in the valid range + /// In riscv, mpidr is equal to id. + fn mpidr2cpuid(mpidr: usize) -> usize { + mpidr + } +} + +/// Static configuration for the QEMU platform +pub const PLAT_DESC: PlatformConfig = PlatformConfig { + /// CPU configuration details for QEMU + cpu_desc: PlatCpuConfig { + num: 4, + core_list: &[ + /// Configuration for each CPU core in QEMU + PlatCpuCoreConfig { + name: 0, + mpidr: 0, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + name: 0, + mpidr: 1, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + name: 0, + mpidr: 2, + sched: RoundRobin, + }, + PlatCpuCoreConfig { + name: 0, + mpidr: 3, + sched: RoundRobin, + }, + ], + /// Cluster description for the CPU cores + cluster_desc: ClusterDesc { num: 1, core_num: &[4] }, + }, + // Memory configuration details for QEMU + mem_desc: PlatMemoryConfig { + regions: &[ + // reserve 0x80200000 ~ 0x88200000 for Shyper + PlatMemRegion { + base: 0x80200000, + size: 0x08000000, + }, + // General memory region for QEMU + PlatMemRegion { + base: 0x90000000, + size: 0x1f0000000, + }, + ], + base: 0x80000000, + }, + // Architecture-specific configuration for QEMU + arch_desc: ArchDesc { + // GIC (Generic Interrupt Controller) configuration for QEMU + gic_desc: crate::arch::GicDesc { + gicd_addr: Platform::GICD_BASE, + gicc_addr: Platform::GICC_BASE, + gich_addr: Platform::GICH_BASE, + gicv_addr: Platform::GICV_BASE, + #[cfg(feature = "gicv3")] + gicr_addr: Platform::GICR_BASE, + maintenance_int_id: 0, + }, + // SMMU (System Memory Management Unit) configuration for QEMU + smmu_desc: crate::arch::SmmuDesc { + base: 0, + interrupt_id: 0, + global_mask: 0, + }, + }, +}; diff --git a/src/board/rk3588/platform.rs b/src/board/rk3588/platform.rs index d9eaa293e33b9d75042a0c452dcbd30453e972b8..51932da140148d8d71b9cf67a65980555ada3b94 100644 --- a/src/board/rk3588/platform.rs +++ b/src/board/rk3588/platform.rs @@ -10,7 +10,6 @@ /// Crate imports for architecture and board configurations use crate::arch::ArchDesc; -use crate::arch::sysreg_enc_addr; use crate::board::{ PlatOperation, Platform, PlatCpuCoreConfig, ClusterDesc, PlatCpuConfig, PlatformConfig, PlatMemoryConfig, PlatMemRegion, @@ -55,10 +54,6 @@ impl PlatOperation for Rk3588Platform { const DISK_PARTITION_1_SIZE: usize = 8192; const DISK_PARTITION_2_SIZE: usize = 524288; - //sysreg - const ICC_SRE_ADDR: usize = sysreg_enc_addr(3, 0, 12, 12, 5); - const ICC_SGIR_ADDR: usize = sysreg_enc_addr(3, 0, 12, 11, 5); - /// Converts MPIDR to CPU ID for RK3588 fn mpidr2cpuid(mpidr: usize) -> usize { (mpidr >> 8) & 0xff diff --git a/src/config/config.rs b/src/config/config.rs index 06e099aef710de691cb9490d519864fdc69fcedd..5f432847950e2207a902b811480d706629d974d1 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -14,7 +14,6 @@ /// functions to manipulate VM configurations, such as adding memory regions, /// setting CPU configurations, and adding emulated or passthrough devices. use alloc::string::{String, ToString}; -use alloc::sync::Arc; use alloc::vec::Vec; use core::ffi::CStr; @@ -40,23 +39,25 @@ pub enum DtbDevType { DevGicd = 1, DevGicc = 2, DevGicr = 3, + DevPlic = 4, } -impl DtbDevType { - /// Convert a `usize` value to a `DtbDevType`. - pub fn from_usize(value: usize) -> DtbDevType { +/// Convert a `usize` value to a `DtbDevType`. +impl From for DtbDevType { + fn from(value: usize) -> Self { match value { 0 => DtbDevType::DevSerial, 1 => DtbDevType::DevGicd, 2 => DtbDevType::DevGicc, 3 => DtbDevType::DevGicr, + 4 => DtbDevType::DevPlic, _ => panic!("Unknown DtbDevType value: {}", value), } } } //! Represents the configuration of an emulated device for a virtual machine. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VmEmulatedDeviceConfig { /// The name of the emulated device. pub name: String, @@ -75,20 +76,12 @@ pub struct VmEmulatedDeviceConfig { } /// Represents a list of emulated device configurations for a virtual machine. +#[derive(Clone, Default)] pub struct VmEmulatedDeviceConfigList { /// List of emulated device configurations. pub emu_dev_list: Vec, } -impl VmEmulatedDeviceConfigList { - /// Creates a new, empty list of emulated device configurations. - pub const fn default() -> VmEmulatedDeviceConfigList { - VmEmulatedDeviceConfigList { - emu_dev_list: Vec::new(), - } - } -} - /// Represents the configuration of a passthrough region. #[derive(Default, Clone, Copy, Debug, Eq)] pub struct PassthroughRegion { @@ -119,19 +112,8 @@ pub struct VmPassthroughDeviceConfig { pub streams_ids: Vec, } -impl VmPassthroughDeviceConfig { - /// Creates a new, default configuration for passthrough devices. - pub const fn default() -> VmPassthroughDeviceConfig { - VmPassthroughDeviceConfig { - regions: Vec::new(), - irqs: Vec::new(), - streams_ids: Vec::new(), - } - } -} - /// Represents a memory region configuration for a virtual machine. -#[derive(Clone, Copy, Debug, Eq)] +#[derive(Clone, Debug)] pub struct VmRegion { /// The starting IPA (Intermediate Physical Address) of the memory region. pub ipa_start: usize, @@ -139,46 +121,19 @@ pub struct VmRegion { pub length: usize, } -impl VmRegion { - /// Creates a new memory region configuration. - pub const fn default() -> VmRegion { - VmRegion { - ipa_start: 0, - length: 0, - } - } -} - -/// Implementation of the PartialEq trait for VmRegion, enabling equality comparisons between VmRegion instances. -impl PartialEq for VmRegion { - fn eq(&self, other: &Self) -> bool { - self.ipa_start == other.ipa_start && self.length == other.length - } -} - /// Clone implementation for VmMemoryConfig struct. -#[derive(Clone)] +#[derive(Default, Clone)] pub struct VmMemoryConfig { pub region: Vec, } -impl VmMemoryConfig { - /// Default constructor for VmMemoryConfig. - pub const fn default() -> VmMemoryConfig { - VmMemoryConfig { region: vec![] } - } -} - /// Clone, Copy, and Default implementations for VmImageConfig struct. #[derive(Clone, Copy, Default)] pub struct VmImageConfig { pub kernel_img_name: Option<&'static str>, pub kernel_load_ipa: usize, - pub kernel_load_pa: usize, pub kernel_entry_point: usize, - // pub device_tree_filename: Option<&'static str>, pub device_tree_load_ipa: usize, - // pub ramdisk_filename: Option<&'static str>, pub ramdisk_load_ipa: usize, pub mediated_block_index: Option, } @@ -189,11 +144,8 @@ impl VmImageConfig { VmImageConfig { kernel_img_name: None, kernel_load_ipa, - kernel_load_pa: 0, kernel_entry_point: kernel_load_ipa, - // device_tree_filename: None, device_tree_load_ipa, - // ramdisk_filename: None, ramdisk_load_ipa, mediated_block_index: None, } @@ -201,23 +153,14 @@ impl VmImageConfig { } /// Configuration for VmCpu (Virtual Machine CPU). -#[derive(Clone, Copy)] +#[derive(Clone, Default)] pub struct VmCpuConfig { pub num: usize, pub allocate_bitmap: u32, - pub master: i32, + pub master: Option, } impl VmCpuConfig { - /// Default constructor for VmCpuConfig. - pub const fn default() -> VmCpuConfig { - VmCpuConfig { - num: 0, - allocate_bitmap: 0, - master: 0, - } - } - /// Constructor for VmCpuConfig with specified parameters. fn new(num: usize, allocate_bitmap: usize, master: usize) -> Self { /// Adjust num and allocate_bitmap based on the given values. @@ -238,7 +181,12 @@ impl VmCpuConfig { } allocate_bitmap & (index - 1) } as u32; - let master = master as i32; + // make sure `master` is in `allocate_bitmap`, So the master can't be -1 + let master = if allocate_bitmap & (1 << master) != 0 { + Some(master) + } else { + None + }; Self { num, allocate_bitmap, @@ -247,39 +195,23 @@ impl VmCpuConfig { } } -/// Structure representing address regions. -#[derive(Clone, Copy)] -pub struct AddrRegions { - pub ipa: usize, - pub length: usize, -} - /// Configuration for VmDtbDev (Device Tree Device in Virtual Machine). #[derive(Clone)] pub struct VmDtbDevConfig { pub name: String, pub dev_type: DtbDevType, pub irqs: Vec, - pub addr_region: AddrRegions, + pub addr_region: VmRegion, } /// Configuration for VMDtbDevConfigList (List of Device Tree Devices in Virtual Machine). -#[derive(Clone)] +#[derive(Clone, Default)] pub struct VMDtbDevConfigList { pub dtb_device_list: Vec, } -impl VMDtbDevConfigList { - // Default constructor for VMDtbDevConfigList. - pub const fn default() -> VMDtbDevConfigList { - VMDtbDevConfigList { - dtb_device_list: Vec::new(), - } - } -} - /// Configuration for VmConfigEntry (Virtual Machine Configuration Entry). -#[derive(Clone)] +#[derive(Clone, Default)] pub struct VmConfigEntry { // VM id, generate inside hypervisor. pub id: usize, @@ -287,33 +219,14 @@ pub struct VmConfigEntry { pub name: String, pub os_type: VmType, pub cmdline: String, + pub image: VmImageConfig, // Following config can be modified during configuration. - pub image: Arc>, - pub memory: Arc>, - pub cpu: Arc>, - pub vm_emu_dev_confg: Arc>, - pub vm_pt_dev_confg: Arc>, - pub vm_dtb_devs: Arc>, - pub fdt_overlay: Arc>>, -} - -/// Default implementation for VmConfigEntry. -impl Default for VmConfigEntry { - fn default() -> VmConfigEntry { - VmConfigEntry { - id: 0, - name: String::from("unknown"), - os_type: VmType::VmTBma, - cmdline: String::from("root=/dev/vda rw audit=0"), - image: Arc::new(Mutex::new(VmImageConfig::default())), - memory: Arc::new(Mutex::new(VmMemoryConfig::default())), - cpu: Arc::new(Mutex::new(VmCpuConfig::default())), - vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList::default())), - vm_pt_dev_confg: Arc::new(Mutex::new(VmPassthroughDeviceConfig::default())), - vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), - fdt_overlay: Arc::new(Mutex::new(Vec::new())), - } - } + pub memory: VmMemoryConfig, + pub cpu: VmCpuConfig, + pub vm_emu_dev_confg: VmEmulatedDeviceConfigList, + pub vm_pt_dev_confg: VmPassthroughDeviceConfig, + pub vm_dtb_devs: VMDtbDevConfigList, + pub fdt_overlay: Vec, } /// Additional methods for VmConfigEntry. @@ -329,13 +242,9 @@ impl VmConfigEntry { ) -> VmConfigEntry { VmConfigEntry { name, - os_type: VmType::from_usize(vm_type), + os_type: VmType::from(vm_type), cmdline, - image: Arc::new(Mutex::new(VmImageConfig::new( - kernel_load_ipa, - device_tree_load_ipa, - ramdisk_load_ipa, - ))), + image: VmImageConfig::new(kernel_load_ipa, device_tree_load_ipa, ramdisk_load_ipa), ..Default::default() } } @@ -357,168 +266,129 @@ impl VmConfigEntry { /// Returns the index of the mediated block, if any. pub fn mediated_block_index(&self) -> Option { - let img_cfg = self.image.lock(); - img_cfg.mediated_block_index + self.image.mediated_block_index } /// Sets the mediated block index. pub fn set_mediated_block_index(&mut self, med_blk_id: usize) { - let mut img_cfg = self.image.lock(); - // println!("set_mediated_block_index {}",med_blk_id); - img_cfg.mediated_block_index = Some(med_blk_id); - // println!("set_mediated_block_index {} self.med_blk_idx {:?}",med_blk_id, img_cfg.mediated_block_index); + self.image.mediated_block_index = Some(med_blk_id); } /// Returns the name of the kernel image, if any. pub fn kernel_img_name(&self) -> Option<&'static str> { - let img_cfg = self.image.lock(); - img_cfg.kernel_img_name + self.image.kernel_img_name } /// Returns the IPA (Physical Address) of the kernel load address. pub fn kernel_load_ipa(&self) -> usize { - let img_cfg = self.image.lock(); - img_cfg.kernel_load_ipa - } - - /// Sets the physical address of the kernel load address. - pub fn set_kernel_load_pa(&mut self, kernel_load_pa: usize) { - let mut img_cfg = self.image.lock(); - img_cfg.kernel_load_pa = kernel_load_pa - } - - /// Returns the physical address of the kernel load address. - pub fn kernel_load_pa(&self) -> usize { - let img_cfg = self.image.lock(); - img_cfg.kernel_load_pa + self.image.kernel_load_ipa } /// Returns the entry point of the kernel. pub fn kernel_entry_point(&self) -> usize { - let img_cfg = self.image.lock(); - img_cfg.kernel_entry_point + self.image.kernel_entry_point } /// Returns the IPA (Physical Address) of the device tree load address. pub fn device_tree_load_ipa(&self) -> usize { - let img_cfg = self.image.lock(); - img_cfg.device_tree_load_ipa + self.image.device_tree_load_ipa } /// Returns the IPA (Physical Address) of the ramdisk load address. pub fn ramdisk_load_ipa(&self) -> usize { - let img_cfg = self.image.lock(); - img_cfg.ramdisk_load_ipa + self.image.ramdisk_load_ipa } /// Returns the memory regions configured for the virtual machine. - pub fn memory_region(&self) -> Vec { - let mem_cfg = self.memory.lock(); - mem_cfg.region.clone() + pub fn memory_region(&self) -> &[VmRegion] { + &self.memory.region } /// Adds a memory configuration with the specified IPA start and length. - pub fn add_memory_cfg(&self, ipa_start: usize, length: usize) { - let mut mem_cfg = self.memory.lock(); - mem_cfg.region.push(VmRegion { ipa_start, length }); + pub fn add_memory_cfg(&mut self, ipa_start: usize, length: usize) { + self.memory.region.push(VmRegion { ipa_start, length }); } /// Returns the number of CPUs configured for the virtual machine. pub fn cpu_num(&self) -> usize { - let cpu_cfg = self.cpu.lock(); - cpu_cfg.num + self.cpu.num } /// Returns the CPU allocate bitmap for the virtual machine. pub fn cpu_allocated_bitmap(&self) -> u32 { - let cpu_cfg = self.cpu.lock(); - cpu_cfg.allocate_bitmap + self.cpu.allocate_bitmap } /// Returns the master CPU ID for the virtual machine. - pub fn cpu_master(&self) -> usize { - let cpu_cfg = self.cpu.lock(); - cpu_cfg.master as usize + pub fn cpu_master(&self) -> Option { + self.cpu.master } /// Sets the CPU configuration with the specified number, allocate bitmap, and master CPU ID. - pub fn set_cpu_cfg(&self, num: usize, allocate_bitmap: usize, master: usize) { - let mut cpu_cfg = self.cpu.lock(); - *cpu_cfg = VmCpuConfig::new(num, allocate_bitmap, master); + pub fn set_cpu_cfg(&mut self, num: usize, allocate_bitmap: usize, master: usize) { + self.cpu = VmCpuConfig::new(num, allocate_bitmap, master); } /// Returns the list of emulated devices configured for the virtual machine. - pub fn emulated_device_list(&self) -> Vec { - let emu_dev_cfg = self.vm_emu_dev_confg.lock(); - emu_dev_cfg.emu_dev_list.clone() + pub fn emulated_device_list(&self) -> &[VmEmulatedDeviceConfig] { + &self.vm_emu_dev_confg.emu_dev_list } /// Adds an emulated device configuration to the virtual machine. - pub fn add_emulated_device_cfg(&self, cfg: VmEmulatedDeviceConfig) { - let mut emu_dev_cfgs = self.vm_emu_dev_confg.lock(); - emu_dev_cfgs.emu_dev_list.push(cfg); + pub fn add_emulated_device_cfg(&mut self, cfg: VmEmulatedDeviceConfig) { + self.vm_emu_dev_confg.emu_dev_list.push(cfg); } /// Returns the list of passthrough device regions configured for the virtual machine. - pub fn passthrough_device_regions(&self) -> Vec { - let pt_dev_cfg = self.vm_pt_dev_confg.lock(); - pt_dev_cfg.regions.clone() + pub fn passthrough_device_regions(&self) -> &[PassthroughRegion] { + &self.vm_pt_dev_confg.regions } /// Returns the list of passthrough device IRQs configured for the virtual machine. - pub fn passthrough_device_irqs(&self) -> Vec { - let pt_dev_cfg = self.vm_pt_dev_confg.lock(); - pt_dev_cfg.irqs.clone() + pub fn passthrough_device_irqs(&self) -> &[usize] { + &self.vm_pt_dev_confg.irqs } /// Returns the list of passthrough device stream IDs configured for the virtual machine. - pub fn passthrough_device_stread_ids(&self) -> Vec { - let pt_dev_cfg = self.vm_pt_dev_confg.lock(); - pt_dev_cfg.streams_ids.clone() + pub fn passthrough_device_stread_ids(&self) -> &[usize] { + &self.vm_pt_dev_confg.streams_ids } /// Adds a passthrough device region with the specified IPA start, PA start, and length. - pub fn add_passthrough_device_region(&self, base_ipa: usize, base_pa: usize, length: usize) { - let mut pt_dev_cfg = self.vm_pt_dev_confg.lock(); - let pt_region_cfg = PassthroughRegion { + pub fn add_passthrough_device_region(&mut self, base_ipa: usize, base_pa: usize, length: usize) { + self.vm_pt_dev_confg.regions.push(PassthroughRegion { ipa: base_ipa, pa: base_pa, length, dev_property: true, - }; - pt_dev_cfg.regions.push(pt_region_cfg) + }) } /// Adds passthrough device IRQs to the virtual machine configuration. - pub fn add_passthrough_device_irqs(&self, irqs: &mut Vec) { - let mut pt_dev_cfg = self.vm_pt_dev_confg.lock(); - pt_dev_cfg.irqs.append(irqs); + pub fn add_passthrough_device_irqs(&mut self, irqs: &mut Vec) { + self.vm_pt_dev_confg.irqs.append(irqs); } /// Adds passthrough device stream IDs to the virtual machine configuration. - pub fn add_passthrough_device_streams_ids(&self, streams_ids: &mut Vec) { - let mut pt_dev_cfg = self.vm_pt_dev_confg.lock(); - pt_dev_cfg.streams_ids.append(streams_ids); + pub fn add_passthrough_device_streams_ids(&mut self, streams_ids: &mut Vec) { + self.vm_pt_dev_confg.streams_ids.append(streams_ids); } /// Returns the list of DTB (Device Tree Blob) devices configured for the virtual machine. - pub fn dtb_device_list(&self) -> Vec { - let dtb_dev_cfg = self.vm_dtb_devs.lock(); - dtb_dev_cfg.dtb_device_list.clone() + pub fn dtb_device_list(&self) -> &[VmDtbDevConfig] { + &self.vm_dtb_devs.dtb_device_list } /// Adds a DTB device configuration to the virtual machine. - pub fn add_dtb_device(&self, cfg: VmDtbDevConfig) { - let mut dtb_dev_cfg = self.vm_dtb_devs.lock(); - dtb_dev_cfg.dtb_device_list.push(cfg); + pub fn add_dtb_device(&mut self, cfg: VmDtbDevConfig) { + self.vm_dtb_devs.dtb_device_list.push(cfg); } /// Returns the IPA of the GICC (Generic Interrupt Controller - CPU Interface) device. pub fn gicc_addr(&self) -> usize { - let dtb_devs = self.vm_dtb_devs.lock(); - for dev in &dtb_devs.dtb_device_list { + for dev in &self.vm_dtb_devs.dtb_device_list { if let DtbDevType::DevGicc = dev.dev_type { - return dev.addr_region.ipa; + return dev.addr_region.ipa_start; } } 0 @@ -526,10 +396,9 @@ impl VmConfigEntry { /// Returns the IPA of the GICD (Generic Interrupt Controller Distributor) device. pub fn gicd_addr(&self) -> usize { - let dtb_devs = self.vm_dtb_devs.lock(); - for dev in &dtb_devs.dtb_device_list { + for dev in &self.vm_dtb_devs.dtb_device_list { if let DtbDevType::DevGicd = dev.dev_type { - return dev.addr_region.ipa; + return dev.addr_region.ipa_start; } } 0 @@ -537,10 +406,9 @@ impl VmConfigEntry { /// Returns the IPA of the GICR (Generic Interrupt Controller Redistributor) device. pub fn gicr_addr(&self) -> usize { - let dtb_devs = self.vm_dtb_devs.lock(); - for dev in &dtb_devs.dtb_device_list { + for dev in &self.vm_dtb_devs.dtb_device_list { if let DtbDevType::DevGicr = dev.dev_type { - return dev.addr_region.ipa; + return dev.addr_region.ipa_start; } } 0 @@ -637,8 +505,21 @@ pub fn vm_cfg_entry(vmid: usize) -> Option { None } +fn vm_cfg_editor(vmid: usize, editor: F) -> Result +where + F: FnOnce(&mut VmConfigEntry) -> Result, +{ + let mut vm_config = DEF_VM_CONFIG_TABLE.lock(); + for vm_cfg_entry in vm_config.entries.iter_mut() { + if vm_cfg_entry.id == vmid { + return editor(vm_cfg_entry); + } + } + error!("failed to find VM[{}] in vm cfg entry list", vmid); + Err(()) +} + /// Adds a virtual machine configuration entry to DEF_VM_CONFIG_TABLE. -/* Add VM config entry to DEF_VM_CONFIG_TABLE */ pub fn vm_cfg_add_vm_entry(mut vm_cfg_entry: VmConfigEntry) -> Result { let mut vm_config = DEF_VM_CONFIG_TABLE.lock(); match vm_config.generate_vm_id() { @@ -688,9 +569,8 @@ pub fn vm_cfg_remove_vm_entry(vm_id: usize) { } /// Generates a new VM Config Entry and sets basic values. -/* Generate a new VM Config Entry, set basic value */ pub fn vm_cfg_add_vm(config_ipa: usize) -> Result { - let config_pa = vm_ipa2pa(active_vm().unwrap(), config_ipa); + let config_pa = vm_ipa2pa(&active_vm().unwrap(), config_ipa); // SAFETY: config_pa is from user space, it is checked by shyper.ko firstly. // And in the function, vm_ipa2pa, it is checked whether the config_pa is in the memory region of the VM. let [vm_name_ipa, _vm_name_length, vm_type, cmdline_ipa, _cmdline_length, kernel_load_ipa, device_tree_load_ipa, ramdisk_load_ipa] = @@ -698,7 +578,7 @@ pub fn vm_cfg_add_vm(config_ipa: usize) -> Result { info!("\n\nStart to prepare configuration for new VM"); // Copy VM name from user ipa. - let vm_name_pa = vm_ipa2pa(active_vm().unwrap(), vm_name_ipa); + let vm_name_pa = vm_ipa2pa(&active_vm().unwrap(), vm_name_ipa); if vm_name_pa == 0 { error!("illegal vm_name_ipa {:x}", vm_name_ipa); return Err(()); @@ -709,7 +589,7 @@ pub fn vm_cfg_add_vm(config_ipa: usize) -> Result { .to_string(); // Copy VM cmdline from user ipa. - let cmdline_pa = vm_ipa2pa(active_vm().unwrap(), cmdline_ipa); + let cmdline_pa = vm_ipa2pa(&active_vm().unwrap(), cmdline_ipa); if cmdline_pa == 0 { error!("illegal cmdline_ipa {:x}", cmdline_ipa); return Err(()); @@ -736,7 +616,6 @@ pub fn vm_cfg_add_vm(config_ipa: usize) -> Result { } /// Deletes a VM config entry. -/* Delete a VM config entry */ pub fn vm_cfg_del_vm(vmid: usize) -> Result { info!("VM[{}] delete config entry", vmid); vm_cfg_remove_vm_entry(vmid); @@ -744,43 +623,35 @@ pub fn vm_cfg_del_vm(vmid: usize) -> Result { } /// Add VM memory region according to VM id. -/* Add VM memory region according to VM id */ pub fn vm_cfg_add_mem_region(vmid: usize, ipa_start: usize, length: usize) -> Result { - let vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - vm_cfg.add_memory_cfg(ipa_start, length); - info!( - "\nVM[{}] vm_cfg_add_mem_region: add region start_ipa {:x} length {:x}", - vmid, ipa_start, length - ); - Ok(0) + vm_cfg_editor(vmid, |vm_cfg| { + vm_cfg.add_memory_cfg(ipa_start, length); + info!( + "\nVM[{}] vm_cfg_add_mem_region: add region start_ipa {:x} length {:x}", + vmid, ipa_start, length + ); + Ok(0) + }) } /// Set VM CPU config according to VM id. -/* Set VM cpu config according to VM id */ pub fn vm_cfg_set_cpu(vmid: usize, num: usize, allocate_bitmap: usize, master: usize) -> Result { - let vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - - vm_cfg.set_cpu_cfg(num, allocate_bitmap, master); + vm_cfg_editor(vmid, |vm_cfg| { + vm_cfg.set_cpu_cfg(num, allocate_bitmap, master); - info!( - "\nVM[{}] vm_cfg_set_cpu: num {} allocate_bitmap {} master {}", - vmid, - vm_cfg.cpu_num(), - vm_cfg.cpu_allocated_bitmap(), - vm_cfg.cpu_master() - ); + info!( + "\nVM[{}] vm_cfg_set_cpu: num {} allocate_bitmap {} master {:?}", + vmid, + vm_cfg.cpu_num(), + vm_cfg.cpu_allocated_bitmap(), + vm_cfg.cpu_master() + ); - Ok(0) + Ok(0) + }) } /// Add emulated device config for VM. -/* Add emulated device config for VM */ pub fn vm_cfg_add_emu_dev( vmid: usize, name_ipa: usize, @@ -790,93 +661,90 @@ pub fn vm_cfg_add_emu_dev( cfg_list_ipa: usize, emu_type: usize, ) -> Result { - let mut vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - let emu_cfg_list = vm_cfg.emulated_device_list(); + vm_cfg_editor(vmid, |vm_cfg| { + let emu_cfg_list = vm_cfg.emulated_device_list(); - // Copy emu device name from user ipa. - let name_pa = vm_ipa2pa(active_vm().unwrap(), name_ipa); - if name_pa == 0 { - error!("illegal emulated device name_ipa {:x}", name_ipa); - return Err(()); - } - // SAFETY: - // We have checked the name_pa is in the memory region of the VM by vm_ipa2pa. - let name_str = unsafe { CStr::from_ptr(name_pa as *const _) } - .to_string_lossy() - .to_string(); - // Copy emu device cfg list from user ipa. - let cfg_list_pa = vm_ipa2pa(active_vm().unwrap(), cfg_list_ipa); - if cfg_list_pa == 0 { - error!("illegal emulated device cfg_list_ipa {:x}", cfg_list_ipa); - return Err(()); - } - let cfg_list = vec![0_usize; CFG_MAX_NUM]; - // SAFETY: - // We have both read and write access to the src and dst memory regions. - // The copied size will not exceed the memory region. - unsafe { - memcpy( - &cfg_list[0] as *const _ as *const u8, - cfg_list_pa as *mut u8, - CFG_MAX_NUM * 8, // sizeof(usize) / sizeof(u8) - ); - } + // Copy emu device name from user ipa. + let name_pa = vm_ipa2pa(&active_vm().unwrap(), name_ipa); + if name_pa == 0 { + error!("illegal emulated device name_ipa {:x}", name_ipa); + return Err(()); + } + // SAFETY: + // We have checked the name_pa is in the memory region of the VM by vm_ipa2pa. + let name_str = unsafe { CStr::from_ptr(name_pa as *const _) } + .to_string_lossy() + .to_string(); + // Copy emu device cfg list from user ipa. + let cfg_list_pa = vm_ipa2pa(&active_vm().unwrap(), cfg_list_ipa); + if cfg_list_pa == 0 { + error!("illegal emulated device cfg_list_ipa {:x}", cfg_list_ipa); + return Err(()); + } + let cfg_list = vec![0_usize; CFG_MAX_NUM]; + // SAFETY: + // We have both read and write access to the src and dst memory regions. + // The copied size will not exceed the memory region. + unsafe { + memcpy( + &cfg_list[0] as *const _ as *const u8, + cfg_list_pa as *mut u8, + CFG_MAX_NUM * 8, // sizeof(usize) / sizeof(u8) + ); + } - info!( - concat!( - "\nVM[{}] vm_cfg_add_emu_dev: ori emu dev num {}\n", - " name {:?}\n", - " cfg_list {:?}\n", - " base ipa {:x} length {:x} irq_id {} emu_type {}" - ), - vmid, - emu_cfg_list.len(), - name_str, - cfg_list, - base_ipa, - length, - irq_id, - emu_type - ); + info!( + concat!( + "\nVM[{}] vm_cfg_add_emu_dev: ori emu dev num {}\n", + " name {:?}\n", + " cfg_list {:?}\n", + " base ipa {:x} length {:x} irq_id {} emu_type {}" + ), + vmid, + emu_cfg_list.len(), + name_str, + cfg_list, + base_ipa, + length, + irq_id, + emu_type + ); - let emu_dev_type = EmuDeviceType::from_usize(emu_type); - let emu_dev_cfg = VmEmulatedDeviceConfig { - name: name_str, - base_ipa, - length, - irq_id, - cfg_list, - emu_type: match emu_dev_type { - EmuDeviceType::EmuDeviceTVirtioBlkMediated => EmuDeviceType::EmuDeviceTVirtioBlk, - _ => emu_dev_type, - }, - mediated: matches!( - EmuDeviceType::from_usize(emu_type), - EmuDeviceType::EmuDeviceTVirtioBlkMediated - ), - }; - vm_cfg.add_emulated_device_cfg(emu_dev_cfg); - - // Set GVM Mediated Blk Index Here. - if emu_dev_type == EmuDeviceType::EmuDeviceTVirtioBlkMediated { - let med_blk_index = match mediated_blk_request() { - Ok(idx) => idx, - Err(_) => { - error!("no more medaited blk for vm {}", vmid); - return Err(()); - } + let emu_dev_type = EmuDeviceType::from_usize(emu_type); + let emu_dev_cfg = VmEmulatedDeviceConfig { + name: name_str, + base_ipa, + length, + irq_id, + cfg_list, + emu_type: match emu_dev_type { + EmuDeviceType::EmuDeviceTVirtioBlkMediated => EmuDeviceType::EmuDeviceTVirtioBlk, + _ => emu_dev_type, + }, + mediated: matches!( + EmuDeviceType::from_usize(emu_type), + EmuDeviceType::EmuDeviceTVirtioBlkMediated + ), }; - vm_cfg.set_mediated_block_index(med_blk_index); - } + vm_cfg.add_emulated_device_cfg(emu_dev_cfg); + + // Set GVM Mediated Blk Index Here. + if emu_dev_type == EmuDeviceType::EmuDeviceTVirtioBlkMediated { + let med_blk_index = match mediated_blk_request() { + Ok(idx) => idx, + Err(_) => { + error!("no more medaited blk for vm {}", vmid); + return Err(()); + } + }; + vm_cfg.set_mediated_block_index(med_blk_index); + } - Ok(0) + Ok(0) + }) } /// Add passthrough device config region for VM -/* Add passthrough device config region for VM */ pub fn vm_cfg_add_passthrough_device_region( vmid: usize, base_ipa: usize, @@ -884,31 +752,21 @@ pub fn vm_cfg_add_passthrough_device_region( length: usize, ) -> Result { // Get VM config entry. - let vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - // Get passthrough device config list. - let pt_dev_regions = vm_cfg.passthrough_device_regions(); - - info!( - concat!( - "\nVM[{}] vm_cfg_add_pt_dev: ori pt dev regions num {}\n", - " base_ipa {:x} base_pa {:x} length {:x}" - ), - vmid, - pt_dev_regions.len(), - base_ipa, - base_pa, - length - ); + vm_cfg_editor(vmid, |vm_cfg| { + info!( + concat!( + "\nVM[{}] vm_cfg_add_pt_dev: \n", + " base_ipa {:x} base_pa {:x} length {:x}" + ), + vmid, base_ipa, base_pa, length + ); - vm_cfg.add_passthrough_device_region(base_ipa, base_pa, length); - Ok(0) + vm_cfg.add_passthrough_device_region(base_ipa, base_pa, length); + Ok(0) + }) } /// Add passthrough device config irqs for VM. -/* Add passthrough device config irqs for VM */ pub fn vm_cfg_add_passthrough_device_irqs(vmid: usize, irqs_base_ipa: usize, irqs_length: usize) -> Result { info!( "\nVM[{}] vm_cfg_add_pt_dev irqs:\n base_ipa {:x} length {:x}", @@ -916,7 +774,7 @@ pub fn vm_cfg_add_passthrough_device_irqs(vmid: usize, irqs_base_ipa: usize, irq ); // Copy passthrough device irqs from user ipa. - let irqs_base_pa = vm_ipa2pa(active_vm().unwrap(), irqs_base_ipa); + let irqs_base_pa = vm_ipa2pa(&active_vm().unwrap(), irqs_base_ipa); if irqs_base_pa == 0 { error!("illegal irqs_base_ipa {:x}", irqs_base_ipa); return Err(()); @@ -936,16 +794,13 @@ pub fn vm_cfg_add_passthrough_device_irqs(vmid: usize, irqs_base_ipa: usize, irq } debug!(" irqs {:?}", irqs); - let vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - vm_cfg.add_passthrough_device_irqs(&mut irqs); - Ok(0) + vm_cfg_editor(vmid, |vm_cfg| { + vm_cfg.add_passthrough_device_irqs(&mut irqs); + Ok(0) + }) } /// Add passthrough device config streams ids for VM -/* Add passthrough device config streams ids for VM */ pub fn vm_cfg_add_passthrough_device_streams_ids( vmid: usize, streams_ids_base_ipa: usize, @@ -957,7 +812,7 @@ pub fn vm_cfg_add_passthrough_device_streams_ids( ); // Copy passthrough device streams ids from user ipa. - let streams_ids_base_pa = vm_ipa2pa(active_vm().unwrap(), streams_ids_base_ipa); + let streams_ids_base_pa = vm_ipa2pa(&active_vm().unwrap(), streams_ids_base_ipa); if streams_ids_base_pa == 0 { error!("illegal streams_ids_base_ipa {:x}", streams_ids_base_ipa); return Err(()); @@ -977,16 +832,13 @@ pub fn vm_cfg_add_passthrough_device_streams_ids( } debug!(" get streams_ids {:?}", streams_ids); - let vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - vm_cfg.add_passthrough_device_streams_ids(&mut streams_ids); - Ok(0) + vm_cfg_editor(vmid, |vm_cfg| { + vm_cfg.add_passthrough_device_streams_ids(&mut streams_ids); + Ok(0) + }) } /// Add device tree device config for VM -/* Add device tree device config for VM */ pub fn vm_cfg_add_dtb_dev( vmid: usize, name_ipa: usize, @@ -1002,7 +854,7 @@ pub fn vm_cfg_add_dtb_dev( ); // Copy DTB device name from user ipa. - let name_pa = vm_ipa2pa(active_vm().unwrap(), name_ipa); + let name_pa = vm_ipa2pa(&active_vm().unwrap(), name_ipa); if name_pa == 0 { error!("illegal dtb_dev name ipa {:x}", name_ipa); return Err(()); @@ -1014,7 +866,7 @@ pub fn vm_cfg_add_dtb_dev( debug!(" get dtb dev name {:?}", dtb_dev_name_str); // Copy DTB device irq list from user ipa. - let irq_list_pa = vm_ipa2pa(active_vm().unwrap(), irq_list_ipa); + let irq_list_pa = vm_ipa2pa(&active_vm().unwrap(), irq_list_ipa); if irq_list_pa == 0 { error!("illegal dtb_dev irq list ipa {:x}", irq_list_ipa); return Err(()); @@ -1039,76 +891,43 @@ pub fn vm_cfg_add_dtb_dev( } debug!(" get dtb dev dtb_irq_list {:?}", dtb_irq_list); - // Get VM config entry. - let vm_cfg = match vm_cfg_entry(vmid) { - Some(vm_cfg) => vm_cfg, - None => return Err(()), - }; - // Get DTB device config list. let vm_dtb_dev = VmDtbDevConfig { name: dtb_dev_name_str, - dev_type: DtbDevType::from_usize(dev_type), + dev_type: DtbDevType::from(dev_type), irqs: dtb_irq_list, - addr_region: AddrRegions { - ipa: addr_region_ipa, + addr_region: VmRegion { + ipa_start: addr_region_ipa, length: addr_region_length, }, }; - vm_cfg.add_dtb_device(vm_dtb_dev); + vm_cfg_editor(vmid, |vm_cfg| { + // Get DTB device config list. + vm_cfg.add_dtb_device(vm_dtb_dev); - Ok(0) + Ok(0) + }) } /// Final step for GVM configuration. /// Set up GVM configuration and VM kernel image load region. -/** - * Final Step for GVM configuration. - * Set up GVM configuration; - * Set VM kernel image load region; - */ -fn vm_cfg_finish_configuration(vmid: usize, img_size: usize) -> Vm { +fn vm_cfg_finish_configuration(vmid: usize, _img_size: usize) -> alloc::sync::Arc { // Set up GVM configuration. vmm_init_gvm(vmid); // Get VM structure. - let vm = match vm(vmid) { + match vm(vmid) { None => { panic!("vm_cfg_upload_kernel_image:failed to init VM[{}]", vmid); } Some(vm) => vm, - }; - - let mut config = vm.config(); - let load_ipa = config.kernel_load_ipa(); - - // Find actual physical memory region according to kernel image ipa. - for (idx, region) in config.memory_region().iter().enumerate() { - if load_ipa < region.ipa_start || load_ipa + img_size > region.ipa_start + region.length { - continue; - } - let offset = load_ipa - region.ipa_start; - info!( - "VM [{}] {} kernel image region: ipa=<0x{:x}>, pa=<0x{:x}>, img_size=<{}KB>", - vm.id(), - config.vm_name(), - load_ipa, - vm.pa_start(idx) + offset, - img_size / 1024 - ); - config.set_kernel_load_pa(vm.pa_start(idx) + offset); } - vm } /// Uploads the kernel image file from MVM user space. /// /// This function is the last step in GVM configuration. It sets up the GVM and loads the kernel /// image into the specified VM. -/** - * Load kernel image file from MVM user space. - * It's the last step in GVM configuration. - */ pub fn vm_cfg_upload_kernel_image( vmid: usize, img_size: usize, @@ -1135,28 +954,20 @@ pub fn vm_cfg_upload_kernel_image( vmid, cache_ipa, load_offset, load_size ); // Get cache pa. - let cache_pa = vm_ipa2pa(active_vm().unwrap(), cache_ipa); + // TODO: In the Hypervisor, we shouldn't use the cache_pa directly. + // Instead, we should translate the IPA to the HVA and use the HVA to access the PA. + // But currently, we don't have the HVA in the Hypervisor. + let cache_pa = vm_ipa2pa(&active_vm().unwrap(), cache_ipa); + let dest_pa = vm.ipa2pa(config.kernel_load_ipa() + load_offset); + if cache_pa == 0 { error!("illegal cache ipa {:x}", cache_ipa); return Err(()); } // SAFETY: We have checked the cache_pa is in the memory region of the VM by vm_ipa2pa. - let src = unsafe { core::slice::from_raw_parts_mut((cache_pa) as *mut u8, load_size) }; - - // Get kernel image load pa. - let load_pa = config.kernel_load_pa(); - if load_pa == 0 { - error!( - "vm_cfg_upload_kernel_image: failed to get kernel image load pa of VM[{}]", - vmid - ); - return Err(()); - } - // Copy from user space. - // SAFETY: - // We have checked the load_pa is in the memory region of the VM by vm_cfg_finish_configuration. - // And the load_offset and load_size will not exceed the kernel image size. - let dst = unsafe { core::slice::from_raw_parts_mut((load_pa + load_offset) as *mut u8, load_size) }; + let src = unsafe { core::slice::from_raw_parts_mut(cache_pa as *mut u8, load_size) }; + + let dst = unsafe { core::slice::from_raw_parts_mut(dest_pa as *mut u8, load_size) }; dst.copy_from_slice(src); Ok(0) } @@ -1169,20 +980,12 @@ pub fn vm_cfg_upload_device_tree( load_offset: usize, load_size: usize, ) -> Result { - let cfg = match vm_cfg_entry(vmid) { - None => { - error!("vm_cfg_upload_device_tree: vm {} not found", vmid); - return Err(()); - } - Some(cfg) => cfg, - }; - info!( "vm_cfg_upload_device_tree: VM[{}] upload device tree. cache_ipa: {:x} load_offset: {:x} load_size: {}", vmid, cache_ipa, load_offset, load_size, ); - let cache_pa = vm_ipa2pa(active_vm().unwrap(), cache_ipa); + let cache_pa = vm_ipa2pa(&active_vm().unwrap(), cache_ipa); if cache_pa == 0 { error!("illegal cache ipa {:x}", cache_ipa); return Err(()); @@ -1190,8 +993,10 @@ pub fn vm_cfg_upload_device_tree( // SAFETY: We have checked the cache_pa is in the memory region of the VM by vm_ipa2pa. let src = unsafe { core::slice::from_raw_parts(cache_pa as *mut u8, load_size) }; - let mut dst = cfg.fdt_overlay.lock(); - dst.extend_from_slice(src); - Ok(0) + vm_cfg_editor(vmid, |cfg| { + cfg.fdt_overlay.extend_from_slice(src); + + Ok(0) + }) } diff --git a/src/config/mod.rs b/src/config/mod.rs index 4337a9abafca1027eced5a7c62045f2ee761caa6..ccb6d68dc72e34c270b17ad11277b86bf0128930 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -15,8 +15,10 @@ pub use self::config::*; pub use self::vm_def::*; #[cfg(feature = "pi4")] pub use self::pi4_def::*; -#[cfg(feature = "qemu")] +#[cfg(all(feature = "qemu", target_arch = "aarch64"))] pub use self::qemu_def::*; +#[cfg(all(feature = "qemu", target_arch = "riscv64"))] +pub use self::qemu_riscv64_def::*; #[cfg(feature = "tx2")] pub use self::tx2_def::*; #[cfg(feature = "rk3588")] @@ -25,8 +27,10 @@ pub use self::rk3588_def::*; mod config; #[cfg(feature = "pi4")] mod pi4_def; -#[cfg(feature = "qemu")] +#[cfg(all(feature = "qemu", target_arch = "aarch64"))] mod qemu_def; +#[cfg(all(feature = "qemu", target_arch = "riscv64"))] +mod qemu_riscv64_def; #[cfg(feature = "rk3588")] mod rk3588_def; #[cfg(feature = "tx2")] diff --git a/src/config/pi4_def.rs b/src/config/pi4_def.rs index 9acd11610177ca39694486c2aa4a6d77c2dc789b..bcf796b0fa33d0b9e0b2c1125d3920b2a6db0ab6 100644 --- a/src/config/pi4_def.rs +++ b/src/config/pi4_def.rs @@ -9,11 +9,8 @@ // See the Mulan PSL v2 for more details. use alloc::string::String; -use alloc::sync::Arc; use alloc::vec::Vec; -use spin::Mutex; - use crate::board::*; use crate::config::vm_cfg_add_vm_entry; use crate::device::EmuDeviceType; @@ -83,8 +80,8 @@ pub fn mvm_config_init() { ]; // vm0 passthrough - let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); - pt_dev_config.regions = vec![ + let pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig { + regions: vec![ // all PassthroughRegion { ipa: 0xFC000000, pa: 0xFC000000, length: 0x04000000, dev_property: true }, // pcie@7d500000 @@ -93,9 +90,9 @@ pub fn mvm_config_init() { PassthroughRegion { ipa: 0x3e000000, pa: 0x3e000000, length: 0x40000000 - 0x3e000000, dev_property: false }, // gicv PassthroughRegion { ipa: Platform::GICC_BASE + 0xF_0000_0000, pa: Platform::GICV_BASE, length: 0x2000, dev_property: true }, - ]; + ], // 146 is UART_INT - pt_dev_config.irqs = vec![ + irqs: vec![ 27, // timer 32 + 0x21, // mailbox@7e00b880 32 + 0x28, // usb@7e980000 @@ -154,8 +151,9 @@ pub fn mvm_config_init() { 32 + 0x5c, // dma@7e007b00 32 + 0xb0, // xhci@7e9c0000 32 + 0x62, // rpivid-local-intc@7eb10000 - ]; - pt_dev_config.streams_ids = vec![]; + ], + streams_ids: vec![] +}; // vm0 vm_region let vm_region = vec![ @@ -180,25 +178,24 @@ pub fn mvm_config_init() { // String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/nvme0n1p2 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), String::from("coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1 console=ttyAMA0,115200n8 root=/dev/sda1 rootfstype=ext4 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), - image: Arc::new(Mutex::new(VmImageConfig { + image: VmImageConfig { kernel_img_name: Some("Raspi4"), kernel_load_ipa: 0x280000, - kernel_load_pa: 0, kernel_entry_point: 0x280000, device_tree_load_ipa: 0x10000000, ramdisk_load_ipa: 0, mediated_block_index: None, - })), - memory: Arc::new(Mutex::new(VmMemoryConfig { + }, + memory: VmMemoryConfig { region: vm_region, - })), - cpu: Arc::new(Mutex::new(VmCpuConfig { + }, + cpu: VmCpuConfig { num: 1, allocate_bitmap: 0b0001, - master: 0, - })), - vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), - vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), + master: None, + }, + vm_emu_dev_confg: VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config }, + vm_pt_dev_confg: pt_dev_config, ..Default::default() }; // Add VM0 entry to the configuration diff --git a/src/config/qemu_def.rs b/src/config/qemu_def.rs index 2d4dece9e88046e6589c9725355aff80174f1bdd..8c93608df6759773cd8f6f156cdc442f1beb40b5 100644 --- a/src/config/qemu_def.rs +++ b/src/config/qemu_def.rs @@ -9,11 +9,8 @@ // See the Mulan PSL v2 for more details. use alloc::string::String; -use alloc::sync::Arc; use alloc::vec::Vec; -use spin::Mutex; - use crate::board::*; use crate::device::EmuDeviceType; use crate::kernel::{HVC_IRQ, VmType}; @@ -21,7 +18,6 @@ use crate::kernel::{HVC_IRQ, VmType}; use super::{ VmConfigEntry, VmCpuConfig, VmEmulatedDeviceConfig, VmImageConfig, VmMemoryConfig, VmPassthroughDeviceConfig, VmRegion, vm_cfg_set_config_name, PassthroughRegion, vm_cfg_add_vm_entry, VmEmulatedDeviceConfigList, - VMDtbDevConfigList, }; /// Initializes the configuration for the manager VM (VM0). @@ -54,35 +50,6 @@ pub fn mvm_config_init() { emu_type: EmuDeviceType::EmuDeviceTGICR, mediated: false, }, - #[cfg(feature="gicv3")] - VmEmulatedDeviceConfig { - name: String::from("icc_sre"), - base_ipa: Platform::ICC_SRE_ADDR, - length: 2, - irq_id: 0, - cfg_list: Vec::new(), - emu_type: EmuDeviceType::EmuDeviceTICCSRE, - mediated: false, - }, - #[cfg(feature="gicv3")] - VmEmulatedDeviceConfig { - name: String::from("icc_sgir"), - base_ipa: Platform::ICC_SGIR_ADDR, - length: 2, - irq_id: 0, - cfg_list: Vec::new(), - emu_type: EmuDeviceType::EmuDeviceTSGIR, - mediated: false, - }, - // VmEmulatedDeviceConfig { - // name: String::from("virtio-blk0"), - // base_ipa: 0xa000000, - // length: 0x1000, - // irq_id: 32 + 0x10, - // cfg_list: vec![DISK_PARTITION_1_START, DISK_PARTITION_1_SIZE], - // emu_type: EmuDeviceType::EmuDeviceTVirtioBlk, - // mediated: false, - // }, VmEmulatedDeviceConfig { name: String::from("virtio-nic0"), base_ipa: 0xa001000, @@ -113,8 +80,8 @@ pub fn mvm_config_init() { ]; // vm0 passthrough - let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); - pt_dev_config.regions = vec![ + let pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig{ + regions: vec![ PassthroughRegion { ipa: Platform::UART_0_ADDR, pa: Platform::UART_0_ADDR, length: 0x1000, dev_property: true }, #[cfg(not(feature = "gicv3"))] PassthroughRegion { ipa: Platform::GICC_BASE, pa: Platform::GICV_BASE, length: 0x2000, dev_property: true }, @@ -122,32 +89,10 @@ pub fn mvm_config_init() { PassthroughRegion { ipa: 0x8080000, pa: 0x8080000, length: 0x20000, dev_property: true }, //pass-through gicv3-its // pass-througn virtio blk/net PassthroughRegion { ipa: 0x0a003000, pa: 0x0a003000, length: 0x1000, dev_property: true }, - ]; - pt_dev_config.irqs = vec![33,27, 72, 73,74,75,76,77,78,79]; - pt_dev_config.streams_ids = vec![]; - // pt_dev_config.push(VmPassthroughDeviceConfig { - // name: String::from("serial0"), - // base_pa: UART_1_ADDR, - // base_ipa: 0x9000000, - // length: 0x1000, - // // dma: false, - // irq_list: vec![UART_1_INT, 27], - // }); - // pt_dev_config.push(VmPassthroughDeviceConfig { - // name: String::from("gicc"), - // base_pa: PLATFORM_GICV_BASE, - // base_ipa: 0x8010000, - // length: 0x2000, - // // dma: false, - // irq_list: Vec::new(), - // }); - // pt_dev_config.push(VmPassthroughDeviceConfig { - // name: String::from("nic"), - // base_pa: 0x0a003000, - // base_ipa: 0x0a003000, - // length: 0x1000, - // irq_list: vec![32 + 0x2e], - // }); + ], + irqs: vec![33,27, 72, 73,74,75,76,77,78,79], + streams_ids: vec![] + }; // vm0 vm_region let vm_region = vec![ @@ -163,180 +108,25 @@ pub fn mvm_config_init() { name: String::from("supervisor"), os_type: VmType::VmTOs, cmdline: String::from("earlycon console=ttyAMA0 root=/dev/vda rw audit=0 default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), - image: Arc::new(Mutex::new(VmImageConfig { + image: VmImageConfig { kernel_img_name: Some("Image"), kernel_load_ipa: 0x80080000, - kernel_load_pa: 0, kernel_entry_point: 0x80080000, - // device_tree_filename: Some("qemu1.bin"), device_tree_load_ipa: 0x80000000, - // ramdisk_filename: Some("initrd.gz"), - // ramdisk_load_ipa: 0x53000000, ramdisk_load_ipa: 0, mediated_block_index: None, - })), - cpu: Arc::new(Mutex::new(VmCpuConfig { + }, + cpu: VmCpuConfig { num: 1, allocate_bitmap: 0b0001, - master: 0, - })), - memory: Arc::new(Mutex::new(VmMemoryConfig { + master: None, + }, + memory: VmMemoryConfig { region: vm_region, - })), - vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), - vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), - vm_dtb_devs: Arc::new(Mutex::new(VMDtbDevConfigList::default())), + }, + vm_emu_dev_confg: VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config }, + vm_pt_dev_confg: pt_dev_config, ..Default::default() }; let _ = vm_cfg_add_vm_entry(mvm_config_entry); } - -// pub fn config_init() { -// let mut vm_config = DEF_VM_CONFIG_TABLE.lock(); -// // vm1 emu -// let mut emu_dev_config: Vec = Vec::new(); -// emu_dev_config.push(VmEmulatedDeviceConfig { -// name: String::from("vgicd"), -// base_ipa: 0x8000000, -// length: 0x1000, -// irq_id: 0, -// cfg_list: Vec::new(), -// emu_type: EmuDeviceType::EmuDeviceTGicd, -// mediated: false, -// }); -// emu_dev_config.push(VmEmulatedDeviceConfig { -// name: String::from("virtio-blk1"), -// base_ipa: 0xa000000, -// length: 0x1000, -// irq_id: 32 + 0x10, -// cfg_list: vec![DISK_PARTITION_2_START, DISK_PARTITION_2_SIZE], -// emu_type: EmuDeviceType::EmuDeviceTVirtioBlk, -// mediated: false, -// }); - -// // vm1 passthrough -// let mut pt_dev_config: Vec = Vec::new(); -// // pt_dev_config.push(VmPassthroughDeviceConfig { -// // name: String::from("serial1"), -// // base_pa: UART_2_ADDR, -// // base_ipa: 0x9000000, -// // length: 0x1000, -// // // dma: false, -// // irq_list: vec![UART_2_INT, 27], -// // }); -// // pt_dev_config.push(VmPassthroughDeviceConfig { -// // name: String::from("gicc"), -// // base_pa: PLATFORM_GICV_BASE, -// // base_ipa: 0x8010000, -// // length: 0x2000, -// // // dma: false, -// // irq_list: Vec::new(), -// // }); -// // vm1 vm_region -// let mut vm_region: Vec = Vec::new(); -// vm_region.push(VmRegion { -// ipa_start: 0x80000000, -// length: 0x80000000, -// }); - -// // vm1 config -// vm_config.entries.push(Arc::new(VmConfigEntry { -// id: 1, -// name: String::from("guest-os-0"), -// os_type: VmType::VmTOs, -// memory: VmMemoryConfig { -// num: 1, -// region: Some(vm_region), -// }, -// image: VmImageConfig { -// kernel_name: Some("Image"), -// kernel_load_ipa: 0x88080000, -// kernel_entry_point: 0x88080000, -// device_tree_filename: Some("qemu2.bin"), -// device_tree_load_ipa: 0x82000000, -// ramdisk_filename: Some("initrd.gz"), -// ramdisk_load_ipa: 0x83000000, -// }, -// cpu: VmCpuConfig { -// num: 1, -// allocate_bitmap: 0b0010, -// master: -1, -// }, -// vm_emu_dev_confg: Some(emu_dev_config), -// vm_pt_dev_confg: Some(pt_dev_config), -// })); - -// // vm2 BMA emu -// let mut emu_dev_config: Vec = Vec::new(); -// emu_dev_config.push(VmEmulatedDeviceConfig { -// name: String::from("vgicd"), -// base_ipa: 0x8000000, -// length: 0x1000, -// irq_id: 0, -// cfg_list: Vec::new(), -// emu_type: EmuDeviceType::EmuDeviceTGicd, -// mediated: false, -// }); -// emu_dev_config.push(VmEmulatedDeviceConfig { -// name: String::from("virtio-blk0"), -// base_ipa: 0xa000000, -// length: 0x1000, -// irq_id: 32 + 0x10, -// cfg_list: vec![DISK_PARTITION_1_START, DISK_PARTITION_1_SIZE], -// emu_type: EmuDeviceType::EmuDeviceTVirtioBlk, -// mediated: false, -// }); - -// // vm2 BMA passthrough -// let mut pt_dev_config: Vec = Vec::new(); -// pt_dev_config.push(VmPassthroughDeviceConfig { -// name: String::from("serial1"), -// base_pa: UART_2_ADDR, -// base_ipa: 0x9000000, -// length: 0x1000, -// // dma: false, -// irq_list: vec![27], -// }); -// pt_dev_config.push(VmPassthroughDeviceConfig { -// name: String::from("gicc"), -// base_pa: PLATFORM_GICV_BASE, -// base_ipa: 0x8010000, -// length: 0x2000, -// // dma: false, -// irq_list: Vec::new(), -// }); - -// // vm2 BMA vm_region -// let mut vm_region: Vec = Vec::new(); -// vm_region.push(VmRegion { -// ipa_start: 0x40000000, -// length: 0x1000000, -// }); - -// // vm2 BMA config -// vm_config.entries.push(Arc::new(VmConfigEntry { -// id: 2, -// name: String::from("guest-bma-0"), -// os_type: VmType::VmTBma, -// memory: VmMemoryConfig { -// num: 1, -// region: Some(vm_region), -// }, -// image: VmImageConfig { -// kernel_name: Some("sbma1.bin"), -// kernel_load_ipa: 0x40080000, -// kernel_entry_point: 0x40080000, -// device_tree_filename: None, -// device_tree_load_ipa: 0, -// ramdisk_filename: None, -// ramdisk_load_ipa: 0, -// }, -// cpu: VmCpuConfig { -// num: 1, -// allocate_bitmap: 0b0100, -// master: -1, -// }, -// vm_emu_dev_confg: Some(emu_dev_config), -// vm_pt_dev_confg: Some(pt_dev_config), -// })); -// } diff --git a/src/config/qemu_riscv64_def.rs b/src/config/qemu_riscv64_def.rs new file mode 100644 index 0000000000000000000000000000000000000000..c893b6c96eecd91812453fbb2842c0917ecbbcae --- /dev/null +++ b/src/config/qemu_riscv64_def.rs @@ -0,0 +1,143 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use alloc::string::String; +use alloc::vec::Vec; +use crate::device::EmuDeviceType; +use crate::kernel::{HVC_IRQ, VmType}; + +use super::{ + VmConfigEntry, VmCpuConfig, VmEmulatedDeviceConfig, VmImageConfig, VmMemoryConfig, VmPassthroughDeviceConfig, + VmRegion, vm_cfg_set_config_name, PassthroughRegion, vm_cfg_add_vm_entry, VmEmulatedDeviceConfigList, + VMDtbDevConfigList, +}; + +/// Initializes the configuration for the manager VM (VM0). +#[rustfmt::skip] +pub fn mvm_config_init() { + // Set the configuration name for VM0 + vm_cfg_set_config_name("qemu-default"); + + // 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, + }, + // hvc2 + VmEmulatedDeviceConfig { + name: String::from("virtio_console@40001000"), + base_ipa: 0x4000_1000, + length: 0x1000, + irq_id: 46, + cfg_list: vec![1, 0x40001000], + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }, + // hvc1 + VmEmulatedDeviceConfig { + name: String::from("virtio_console@40002000"), + base_ipa: 0x4000_2000, + length: 0x1000, + irq_id: 47, + cfg_list: vec![2, 0x4000_2000], // Address of the peer vm and the peer virtio-console + emu_type: EmuDeviceType::EmuDeviceTVirtioConsole, + mediated: false, + }, + // virtual eth0 + VmEmulatedDeviceConfig { + name: String::from("virtio_net@40003000"), + base_ipa: 0x40003000, + length: 0x1000, + irq_id: 48, + cfg_list: vec![0x74, 0x56, 0xaa, 0x0f, 0x47, 0xd0], + emu_type: EmuDeviceType::EmuDeviceTVirtioNet, + mediated: false, + }, + VmEmulatedDeviceConfig { + name: String::from("shyper"), + base_ipa: 0, + length: 0, + irq_id: HVC_IRQ, + cfg_list: Vec::new(), + emu_type: EmuDeviceType::EmuDeviceTShyper, + mediated: false, + } + ]; + + // vm0 passthrough + let pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig { + regions: vec![ + // pass-through virtio blk + PassthroughRegion { ipa: 0x10001000, pa: 0x10001000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10002000, pa: 0x10002000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10003000, pa: 0x10003000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10004000, pa: 0x10004000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10005000, pa: 0x10005000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10006000, pa: 0x10006000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10007000, pa: 0x10007000, length: 0x1000, dev_property: true }, + PassthroughRegion { ipa: 0x10008000, pa: 0x10008000, length: 0x1000, dev_property: true }, + // Serial Device + PassthroughRegion { ipa: 0x10000000, pa: 0x10000000, length: 0x1000, dev_property: true }, + // RTC + PassthroughRegion { ipa: 0x101000, pa: 0x101000, length: 0x1000, dev_property: true }, + ], + irqs: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,], + streams_ids: vec![] + }; + + // vm0 vm_region + let vm_region = vec![ + VmRegion { + ipa_start: 0x90000000, + length: 0x100000000, + } + ]; + + // vm0 config + let mvm_config_entry = VmConfigEntry { + id: 0, + name: String::from("supervisor"), + os_type: VmType::VmTOs, + cmdline: String::from("earlycon=sbi console=ttyS0 root=/dev/vda rw audit=0 default_hugepagesz=2M hugepagesz=2M hugepages=10\0"), + image: VmImageConfig { + kernel_img_name: Some("Image"), + kernel_load_ipa: 0x90000000, + kernel_entry_point: 0x90000000, + // device_tree_filename: Some("qemu1.bin"), + // Note: In Linux, Device Tree should be placed above linux kernel, otherwise it will be ignored + // Linux Print: OF: fdt: Ignoring memory range 0x90000000 - 0x90200000 + device_tree_load_ipa: 0x180000000, + // ramdisk_filename: Some("initrd.gz"), + // ramdisk_load_ipa: 0x53000000, + ramdisk_load_ipa: 0, + mediated_block_index: None, + }, + cpu: VmCpuConfig { + num: 1, + allocate_bitmap: 0b0001, + master: Some(0), + }, + memory: VmMemoryConfig { + region: vm_region, + }, + vm_emu_dev_confg: VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config }, + vm_pt_dev_confg: pt_dev_config, + vm_dtb_devs: VMDtbDevConfigList::default(), + ..Default::default() + }; + let _ = vm_cfg_add_vm_entry(mvm_config_entry); +} diff --git a/src/config/rk3588_def.rs b/src/config/rk3588_def.rs index 7017be5fa2f85f5447ea5abdbcdcc4eb4656fb64..0e110c8dda77f7f67f95d09919cc51307d7d58bd 100644 --- a/src/config/rk3588_def.rs +++ b/src/config/rk3588_def.rs @@ -9,11 +9,9 @@ // See the Mulan PSL v2 for more details. use alloc::string::String; -use alloc::sync::Arc; use alloc::vec::Vec; use fdt::binding::FdtBuf; -use spin::Mutex; use crate::arch::traits::InterruptController; use crate::board::{Platform, PlatOperation}; @@ -105,24 +103,6 @@ pub fn mvm_config_init() { emu_type: EmuDeviceType::EmuDeviceTGICR, mediated: false, }, - VmEmulatedDeviceConfig { - name: String::from("ICC_SRE_ADDR"), - base_ipa: Platform::ICC_SRE_ADDR, - length: 0, - irq_id: 25, - cfg_list: Vec::new(), - emu_type: EmuDeviceType::EmuDeviceTICCSRE, - mediated: false, - }, - VmEmulatedDeviceConfig { - name: String::from("ICC_SGIR_ADDR"), - base_ipa: Platform::ICC_SGIR_ADDR, - length: 0 , - irq_id: 25, - cfg_list: Vec::new(), - emu_type: EmuDeviceType::EmuDeviceTSGIR, - mediated: false, - }, // VmEmulatedDeviceConfig { // name: String::from("virtio-blk@feb60000"), // base_ipa: 0xfeb60000, @@ -182,20 +162,25 @@ pub fn mvm_config_init() { let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); let mut pt = crate::utils::interval::IntervalExcluder::new(); - // all, exclude gic - pt.add_range(0xfb000000, 0xfe600000); + // all peripherals + pt.add_range(0xfb000000, 0x1_0000_0000); // exclude ethernet@0xfe1c0000 for 'noeth' #[cfg(feature = "rk3588-noeth")] pt.exclude_len(0xfe1c0000, 0x10000); + // interrupt-controller (without msi-controller) + pt.exclude_len(0xfe600000, 0x10000); + pt.exclude_len(0xfe680000, 0x100000); + + // serial1 to serial9 + pt.exclude_range(0xfeb40000, 0xfebc0000); + pt_dev_config.regions = pt.into_iter().map(|t| { PassthroughRegion { ipa: t.left, pa: t.left, length: t.len(), dev_property: true } }).collect(); pt_dev_config.regions.extend(&[ - PassthroughRegion { ipa: 0xfe680000 + 0x100000, pa: 0xfe680000 + 0x100000, length: 0xfeb40000 - (0xfe680000 + 0x100000), dev_property: true }, - PassthroughRegion { ipa: 0xfebc0000, pa: 0xfebc0000, length: 0x0001_0000_0000 - 0xfebc0000, dev_property: true }, // serial@feb5000——ttyFIQ PassthroughRegion { ipa: 0xfeb50000, pa: 0xfeb50000, length: 0x100, dev_property: true }, // serial@feba000——ttyS7 @@ -209,9 +194,6 @@ pub fn mvm_config_init() { // device for PCI bus to be mapped in. PassthroughRegion { ipa: 0xf4000000, pa: 0xf4000000, length: 0x1000000, dev_property: true }, PassthroughRegion { ipa: 0xf3000000, pa: 0xf3000000, length: 0x1000000, dev_property: true }, - // device for its - PassthroughRegion { ipa: 0xfe640000, pa: 0xfe640000, length: 0x20000, dev_property: true }, - PassthroughRegion { ipa: 0xfe660000, pa: 0xfe660000, length: 0x20000, dev_property: true }, ]); pt_dev_config.irqs = vec![ @@ -339,25 +321,24 @@ pub fn mvm_config_init() { // String::from("emmc androidboot.storagemedia=emmc androidboot.mode=normal dsi-0=2 storagenode=/mmc@fe2e0000 earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 root=/dev/nfs nfsroot=192.168.106.153:/tftp/rootfs,proto=tcp rw ip=192.168.106.143:192.168.106.153:192.168.106.1:255.255.255.0::eth0:off default_hugepagesz=32M hugepagesz=32M hugepages=4"), // String::from("earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 rw rootwait overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0\0"), // String::from("storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal dsi-0=2 storagenode=/mmc@fe2e0000 androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0\0"), - image: Arc::new(Mutex::new(VmImageConfig { + image: VmImageConfig { kernel_img_name: Some("Linux-5.10"), kernel_load_ipa: 0x10080000, - kernel_load_pa: 0, kernel_entry_point: 0x10080000, device_tree_load_ipa: 0x10000000, ramdisk_load_ipa: 0, mediated_block_index: None, - })), - memory: Arc::new(Mutex::new(VmMemoryConfig { + }, + memory: VmMemoryConfig { region: vm_region, - })), - cpu: Arc::new(Mutex::new(VmCpuConfig { + }, + cpu: VmCpuConfig { num: 1, allocate_bitmap: 0b1, - master: 0, - })), - vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), - vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), + master: None, + }, + vm_emu_dev_confg: VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config }, + vm_pt_dev_confg: pt_dev_config, ..Default::default() }; let _ = vm_cfg_add_vm_entry(mvm_config_entry); diff --git a/src/config/tx2_def.rs b/src/config/tx2_def.rs index d9f6e44c76b3a22915ac05813152dd6ef637f3d7..b9c6618e79855de33f4d8fb676d4714e2fdd7ac6 100644 --- a/src/config/tx2_def.rs +++ b/src/config/tx2_def.rs @@ -9,11 +9,8 @@ // See the Mulan PSL v2 for more details. use alloc::string::String; -use alloc::sync::Arc; use alloc::vec::Vec; -use spin::Mutex; - use crate::arch::traits::InterruptController; use crate::board::{Platform, PlatOperation}; use crate::config::vm_cfg_add_vm_entry; @@ -91,8 +88,8 @@ pub fn mvm_config_init() { ]; // vm0 passthrough - let mut pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig::default(); - pt_dev_config.regions = vec![ + let pt_dev_config: VmPassthroughDeviceConfig = VmPassthroughDeviceConfig { + regions: vec![ PassthroughRegion { ipa: 0x100000, pa: 0x100000, length: 0x10000, dev_property: true }, PassthroughRegion { ipa: 0x02100000, pa: 0x02100000, length: 0x1000, dev_property: true }, PassthroughRegion { ipa: 0x02110000, pa: 0x02110000, length: 0x1000, dev_property: true }, @@ -201,9 +198,9 @@ pub fn mvm_config_init() { PassthroughRegion { ipa: 0x17000000, pa: 0x17000000, length: 0x2000000, dev_property: true }, PassthroughRegion { ipa: 0x30000000, pa: 0x30000000, length: 0x10000000, dev_property: true }, PassthroughRegion { ipa: 0x40000000, pa: 0x40000000, length: 0x40000000, dev_property: true }, - ]; + ], // 146 is UART_INT - pt_dev_config.irqs = vec![ + irqs: vec![ crate::arch::IntCtrl::IRQ_GUEST_TIMER, 32, 33, 34, 35, 36, 37, 38, 39, 40, 48, 49, 56, 57, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 74, 76, 79, 82, 85, 88, 91, 92, 94, 95, 96, 97, 102, 103, 104, 105, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, @@ -212,12 +209,12 @@ pub fn mvm_config_init() { 185, 186, 187, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 208, 212, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 229, 230, 233, 234, 235, 237, 238, 242, 255, 256, 295, 297, 315, 322, 328, 329, 330, 331, 352, 353, 366, - ]; - pt_dev_config.streams_ids = vec![ + ], + streams_ids: vec![ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 25, 26, 27, 28, 29, 30, 31, 32, 42, 45, 50, 51, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - ]; + ]}; // vm0 vm_region let vm_region = vec![ @@ -226,10 +223,6 @@ pub fn mvm_config_init() { length: 0x60000000, } ]; - // vm_region.push(VmRegion { - // ipa_start: 0xf0200000, - // length: 0xc0000000, - // }); // vm0 config let mvm_config_entry = VmConfigEntry { @@ -240,25 +233,24 @@ pub fn mvm_config_init() { // String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/nvme0n1p1 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=4\0"), String::from("earlycon=uart8250,mmio32,0x3100000 console=ttyS0,115200n8 root=/dev/sda1 rw audit=0 rootwait default_hugepagesz=32M hugepagesz=32M hugepages=5\0"), - image: Arc::new(Mutex::new(VmImageConfig { + image: VmImageConfig { kernel_img_name: Some("L4T"), kernel_load_ipa: 0xa0080000, - kernel_load_pa: 0, kernel_entry_point: 0xa0080000, device_tree_load_ipa: 0xa0000000, ramdisk_load_ipa: 0, mediated_block_index: None, - })), - memory: Arc::new(Mutex::new(VmMemoryConfig { + }, + memory: VmMemoryConfig { region: vm_region, - })), - cpu: Arc::new(Mutex::new(VmCpuConfig { + }, + cpu: VmCpuConfig { num: 1, allocate_bitmap: 0b0001, - master: 0, - })), - vm_emu_dev_confg: Arc::new(Mutex::new(VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config })), - vm_pt_dev_confg: Arc::new(Mutex::new(pt_dev_config)), + master: None, + }, + vm_emu_dev_confg: VmEmulatedDeviceConfigList { emu_dev_list: emu_dev_config }, + vm_pt_dev_confg: pt_dev_config, ..Default::default() }; let _ = vm_cfg_add_vm_entry(mvm_config_entry); diff --git a/src/device/device_tree.rs b/src/device/device_tree.rs index ffb194db5704a49413574001d80fb90dd5bebc9a..b54f6d23752b80843b16baa20ff171dd8151fceb 100644 --- a/src/device/device_tree.rs +++ b/src/device/device_tree.rs @@ -21,6 +21,10 @@ use crate::vmm::CPIO_RAMDISK; 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 +} + /// Initializes the Device Tree Blob (DTB) for the primary VM (vm0). /// # Safety: /// Dtb is a valid pointer to a device tree blob @@ -94,12 +98,13 @@ pub unsafe fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) -> Result<()> { let slice = core::slice::from_raw_parts(pi_fdt as *const u8, len as usize); SYSTEM_FDT.call_once(|| slice.to_vec()); } - #[cfg(feature = "qemu")] + #[cfg(all(feature = "qemu", target_arch = "aarch64"))] { fdt_pack(dtb); fdt_enlarge(dtb); fdt_clear_initrd(dtb); // assert_eq!(fdt_disable_node(dtb, "/platform@c000000\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/flash@0\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/fw-cfg@9020000\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/memory@40000000\0".as_ptr()), 0); assert_eq!(fdt_remove_node(dtb, "/virtio_mmio@a000000\0".as_ptr()), 0); @@ -152,6 +157,40 @@ pub unsafe fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) -> Result<()> { let slice = core::slice::from_raw_parts(dtb as *const u8, len); SYSTEM_FDT.call_once(|| slice.to_vec()); } + // #[cfg(all(feature = "qemu", target_arch = "riscv64"))] + #[cfg(target_arch = "riscv64")] + { + fdt_pack(dtb); + fdt_enlarge(dtb); + fdt_clear_initrd(dtb); + // assert_eq!(fdt_remove_node(dtb, "/soc/plic@c000000\0".as_ptr()), 0); + // Note: OpenSBI protects the CLINT owning area with PMP, allowing only M-mode read and write, + // but not S-mode or U-mode read and write + // 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: () + + // TODO: Emulate CLINT + assert_eq!(fdt_remove_node(dtb, "/soc/clint@2000000\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/poweroff\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/reboot\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/platform-bus@4000000\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/pmu\0".as_ptr()), 0); + assert_eq!(fdt_remove_node(dtb, "/fw-cfg@10100000\0".as_ptr()), 0); + 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, "/soc/pci@30000000\0".as_ptr()), 0); + + // 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); + + let len = fdt_size(dtb) as usize; + info!("fdt patched size {}", len); + let slice = core::slice::from_raw_parts(dtb as *const u8, len); + SYSTEM_FDT.call_once(|| slice.to_vec()); + } #[cfg(feature = "rk3588")] { use fdt::binding::Fdt; @@ -165,7 +204,126 @@ pub unsafe fn init_vm0_dtb(dtb: *mut fdt::myctypes::c_void) -> Result<()> { /// Creates the Device Tree Blob (DTB) for the secondary VM (vm1) based on the provided configuration. // create vm1 fdt demo -pub fn create_fdt(config: VmConfigEntry) -> FdtWriterResult> { +pub fn create_fdt(config: &VmConfigEntry) -> FdtWriterResult> { + #[cfg(target_arch = "riscv64")] + { + create_fdt_riscv64(config) + } + #[cfg(target_arch = "aarch64")] + { + create_fdt_aarch64(config) + } +} + +pub fn create_fdt_riscv64(config: &VmConfigEntry) -> FdtWriterResult> { + let mut fdt = FdtWriter::new()?; + let ncpu = config.cpu_allocated_bitmap().count_ones(); + + let root_node = fdt.begin_node("root")?; + fdt.property_u32("#address-cells", 0x2)?; + fdt.property_u32("#size-cells", 0x2)?; + fdt.property_string("compatible", "riscv-virtio")?; + fdt.property_string("model", "riscv-virtio,qemu")?; + + create_memory_node(&mut fdt, config.clone())?; + + // todo: fix create_chosen_node size + create_chosen_node(&mut fdt, &config.cmdline, config.ramdisk_load_ipa(), CPIO_RAMDISK.len())?; + + create_cpu_node_riscv(&mut fdt, config)?; + + let soc = fdt.begin_node("soc")?; + fdt.property_u32("#address-cells", 0x2)?; + fdt.property_u32("#size-cells", 0x2)?; + fdt.property_string("compatible", "simple-bus")?; + fdt.property_null("ranges")?; + + for emu_cfg in config.emulated_device_list() { + match emu_cfg.emu_type { + EmuDeviceType::EmuDeviceTVirtioBlk + | EmuDeviceType::EmuDeviceTVirtioNet + | EmuDeviceType::EmuDeviceTVirtioConsole => { + info!("virtio fdt node init {} {:x}", emu_cfg.name, emu_cfg.base_ipa); + create_virtio_node_riscv64( + &mut fdt, + &emu_cfg.name, + emu_cfg.irq_id, + emu_cfg.base_ipa, + riscv_plic_phandle(ncpu), + )?; + } + EmuDeviceType::EmuDeviceTShyper => { + info!("shyper fdt node init {:x}", emu_cfg.base_ipa); + create_shyper_node( + &mut fdt, + &emu_cfg.name, + emu_cfg.irq_id, + emu_cfg.base_ipa, + emu_cfg.length, + )?; + } + EmuDeviceType::EmuDeviceTPlic => { + 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)?; + } + _ => {} + } + } + create_clint_node(&mut fdt, "clint@2000000", 0x2000000, 0x10000, ncpu)?; + fdt.end_node(soc)?; + + fdt.end_node(root_node)?; + fdt.finish() +} + +fn create_plic_node(fdt: &mut FdtWriter, name: &str, address: usize, len: usize, ncpu: u32) -> FdtWriterResult<()> { + let plic = fdt.begin_node(name)?; + let plic_phandle = riscv_plic_phandle(ncpu); + + fdt.property_u32("phandle", plic_phandle)?; + fdt.property_u32("riscv,ndev", 63)?; + 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(0x0b); + interrupts.push(cpu_phandle); + interrupts.push(0x09); + } + + fdt.property_array_u32("interrupts-extended", interrupts.as_slice())?; + fdt.property_null("interrupt-controller")?; + fdt.property_string("compatible", "sifive,plic-1.0.0")?; + fdt.property_u32("#address-cells", 0x00)?; + fdt.property_u32("#interrupt-cells", 0x01)?; + fdt.end_node(plic)?; + + Ok(()) +} + +fn create_clint_node(fdt: &mut FdtWriter, name: &str, address: usize, len: usize, ncpu: u32) -> FdtWriterResult<()> { + let clint = fdt.begin_node(name)?; + + 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(0x03); + interrupts.push(cpu_phandle); + interrupts.push(0x07); + } + + fdt.property_array_u32("interrupts-extended", interrupts.as_slice())?; + fdt.property_array_u64("reg", &[address as u64, len as u64])?; + fdt.property_string("compatible", "sifive,clint0")?; + fdt.end_node(clint)?; + + Ok(()) +} + +pub fn create_fdt_aarch64(config: &VmConfigEntry) -> FdtWriterResult> { let mut fdt = FdtWriter::new()?; let root_node = fdt.begin_node("root")?; @@ -189,9 +347,9 @@ pub fn create_fdt(config: VmConfigEntry) -> FdtWriterResult> { } // todo: fix create_chosen_node size create_chosen_node(&mut fdt, &config.cmdline, config.ramdisk_load_ipa(), CPIO_RAMDISK.len())?; - create_cpu_node(&mut fdt, config.clone())?; + create_cpu_node(&mut fdt, config)?; if !config.dtb_device_list().is_empty() { - create_serial_node(&mut fdt, &config.dtb_device_list())?; + create_serial_node(&mut fdt, config.dtb_device_list())?; } // match &config.vm_dtb_devs { // Some(vm_dtb_devs) => { @@ -203,6 +361,7 @@ pub fn create_fdt(config: VmConfigEntry) -> FdtWriterResult> { create_gicv3_node(&mut fdt, config.gicr_addr(), config.gicd_addr())?; #[cfg(not(feature = "gicv3"))] create_gic_node(&mut fdt, config.gicc_addr(), config.gicd_addr())?; + // TODO: create_plic_node for emu_cfg in config.emulated_device_list() { match emu_cfg.emu_type { @@ -296,7 +455,7 @@ fn create_timer_node(fdt: &mut FdtWriter, trigger_lvl: u32) -> FdtWriterResult<( } /// Creates the CPU node in the Device Tree for the VM based on the provided configuration. -fn create_cpu_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterResult<()> { +fn create_cpu_node(fdt: &mut FdtWriter, config: &VmConfigEntry) -> FdtWriterResult<()> { let cpus = fdt.begin_node("cpus")?; fdt.property_u32("#size-cells", 0)?; fdt.property_u32("#address-cells", 0x2)?; @@ -327,11 +486,63 @@ fn create_cpu_node(fdt: &mut FdtWriter, config: VmConfigEntry) -> FdtWriterResul Ok(()) } +// acquire the cpu phandle id +pub fn riscv_cpu_phandle(cpu_id: u32, ncpu: u32) -> u32 { + ncpu * 2 - 1 - cpu_id * 2 +} + +// acquire the cpu interrupt controller phandle id +pub fn riscv_cpu_intc_phandle(cpu_id: u32, ncpu: u32) -> u32 { + ncpu * 2 - cpu_id * 2 +} + +// acquire the plic phandle id +pub fn riscv_plic_phandle(ncpu: u32) -> u32 { + ncpu * 2 + 1 +} + +/// 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")?; + fdt.property_u32("#address-cells", 0x1)?; + fdt.property_u32("#size-cells", 0)?; + fdt.property_u32("timebase-frequency", 10000000)?; + + let cpu_num = config.cpu_allocated_bitmap().count_ones(); + for cpu_id in 0..cpu_num { + let cpu_name = format!("cpu@{:x}", cpu_id); + let cpu_node = fdt.begin_node(&cpu_name)?; + + fdt.property_u32("phandle", riscv_cpu_phandle(cpu_id, cpu_num))?; + fdt.property_string("device_type", "cpu")?; + fdt.property_u32("reg", cpu_id)?; + 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")?; + 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)?; + fdt.property_null("interrupt-controller")?; + fdt.property_string("compatible", "riscv,cpu-intc")?; + fdt.property_u32("phandle", riscv_cpu_intc_phandle(cpu_id, cpu_num))?; // intc phandle + fdt.end_node(intc)?; + + fdt.end_node(cpu_node)?; + } + + fdt.end_node(cpus)?; + + Ok(()) +} + /// Creates the serial node in the Device Tree for the VM based on the provided configuration. fn create_serial_node(fdt: &mut FdtWriter, devs_config: &[VmDtbDevConfig]) -> FdtWriterResult<()> { for dev in devs_config { if dev.dev_type == DtbDevType::DevSerial { - let serial_name = format!("serial@{:x}", dev.addr_region.ipa); + let serial_name = format!("serial@{:x}", dev.addr_region.ipa_start); let serial = fdt.begin_node(&serial_name)?; if cfg!(feature = "rk3588") { fdt.property_string("compatible", "snps,dw-apb-uart")?; @@ -339,7 +550,7 @@ fn create_serial_node(fdt: &mut FdtWriter, devs_config: &[VmDtbDevConfig]) -> Fd fdt.property_string("compatible", "ns16550")?; } fdt.property_u32("clock-frequency", 408000000)?; - fdt.property_array_u64("reg", &[dev.addr_region.ipa as u64, 0x1000])?; + fdt.property_array_u64("reg", &[dev.addr_region.ipa_start as u64, 0x1000])?; fdt.property_u32("reg-shift", 0x2)?; fdt.property_array_u32("interrupts", &[0x0, (dev.irqs[0] - 32) as u32, 0x4])?; fdt.property_string("status", "okay")?; @@ -408,6 +619,23 @@ fn create_virtio_node(fdt: &mut FdtWriter, name: &str, irq: usize, address: usiz Ok(()) } +fn create_virtio_node_riscv64( + fdt: &mut FdtWriter, + name: &str, + irq: usize, + address: usize, + plic_phandle: u32, +) -> FdtWriterResult<()> { + let virtio = fdt.begin_node(name)?; + fdt.property_array_u32("interrupts", &[irq as u32])?; + fdt.property_u32("interrupt-parent", plic_phandle)?; + fdt.property_array_u64("reg", &[address as u64, 0x1000])?; + fdt.property_string("compatible", "virtio,mmio")?; + fdt.end_node(virtio)?; + + Ok(()) +} + /// Creates a Shyper (Sample Hypervisor) node in the Device Tree for the VM based on the provided configuration. fn create_shyper_node(fdt: &mut FdtWriter, name: &str, irq: usize, address: usize, len: usize) -> FdtWriterResult<()> { let shyper = fdt.begin_node(name)?; diff --git a/src/device/emu.rs b/src/device/emu.rs index 41b464c04a0a55ba2e816f4b2217b2b065932292..36cb0419884d804e98f5fa0e8f50c87c5fba6660 100644 --- a/src/device/emu.rs +++ b/src/device/emu.rs @@ -8,30 +8,25 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::sync::Arc; use alloc::vec::Vec; use core::fmt::{Display, Formatter}; +use core::ops::Range; use core::ptr; -use spin::Mutex; use spin::RwLock; -use crate::arch::Vgic; -use crate::device::VirtioMmio; -use crate::kernel::current_cpu; -use crate::utils::in_range; +use crate::kernel::{active_vm, current_cpu}; +use crate::utils::downcast::DowncastSync; -pub static EMU_DEVS_LIST: Mutex> = Mutex::new(Vec::new()); - -/// Enumeration representing various emulator devices. -#[derive(Clone)] -pub enum EmuDevs { - Vgic(Arc), - VirtioBlk(VirtioMmio), - VirtioNet(VirtioMmio), - VirtioConsole(VirtioMmio), - None, +pub trait EmuDev: DowncastSync { + /// emulated device type + fn emu_type(&self) -> EmuDeviceType; + /// emulated device address range + fn address_range(&self) -> Range; + /// emulated device handler + fn handler(&self, emu_ctx: &EmuContext) -> bool; } + #[derive(Debug, Clone, Copy)] pub struct EmuContext { pub address: usize, @@ -99,10 +94,9 @@ pub enum EmuDeviceType { EmuDeviceTShyper = 6, EmuDeviceTVirtioBlkMediated = 7, EmuDeviceTIOMMU = 8, - EmuDeviceTICCSRE = 9, - EmuDeviceTSGIR = 10, EmuDeviceTGICR = 11, EmuDeviceTMeta = 12, + EmuDeviceTPlic = 13, } impl Display for EmuDeviceType { @@ -118,10 +112,9 @@ impl Display for EmuDeviceType { EmuDeviceType::EmuDeviceTShyper => write!(f, "device shyper"), EmuDeviceType::EmuDeviceTVirtioBlkMediated => write!(f, "medaited virtio block"), EmuDeviceType::EmuDeviceTIOMMU => write!(f, "IOMMU"), - EmuDeviceType::EmuDeviceTICCSRE => write!(f, "interrupt ICC SRE"), - EmuDeviceType::EmuDeviceTSGIR => write!(f, "interrupt ICC SGIR"), EmuDeviceType::EmuDeviceTGICR => write!(f, "interrupt controller gicr"), EmuDeviceType::EmuDeviceTMeta => write!(f, "meta device"), + EmuDeviceType::EmuDeviceTPlic => write!(f, "platform level interrupt controller"), } } } @@ -133,8 +126,6 @@ impl EmuDeviceType { matches!( *self, EmuDeviceType::EmuDeviceTGicd - | EmuDeviceType::EmuDeviceTSGIR - | EmuDeviceType::EmuDeviceTICCSRE | EmuDeviceType::EmuDeviceTGPPT | EmuDeviceType::EmuDeviceTVirtioBlk | EmuDeviceType::EmuDeviceTVirtioNet @@ -156,10 +147,9 @@ impl EmuDeviceType { 6 => EmuDeviceType::EmuDeviceTShyper, 7 => EmuDeviceType::EmuDeviceTVirtioBlkMediated, 8 => EmuDeviceType::EmuDeviceTIOMMU, - 9 => EmuDeviceType::EmuDeviceTICCSRE, - 10 => EmuDeviceType::EmuDeviceTSGIR, 11 => EmuDeviceType::EmuDeviceTGICR, 12 => EmuDeviceType::EmuDeviceTMeta, + 13 => EmuDeviceType::EmuDeviceTPlic, _ => panic!("Unknown EmuDeviceType value: {}", value), } } @@ -171,73 +161,20 @@ pub type EmuDevHandler = fn(usize, &EmuContext) -> bool; // TO CHECK pub fn emu_handler(emu_ctx: &EmuContext) -> bool { let ipa = emu_ctx.address; - let emu_devs_list = EMU_DEVS_LIST.lock(); - for emu_dev in &*emu_devs_list { - let active_vcpu = current_cpu().active_vcpu.clone().unwrap(); - if active_vcpu.vm_id() == emu_dev.vm_id && in_range(ipa, emu_dev.ipa, emu_dev.size - 1) { - // if current_cpu().id == 1 { - // println!("emu dev {:#?} handler", emu_dev.emu_type); - // } - let handler = emu_dev.handler; - let id = emu_dev.id; - drop(emu_devs_list); - return handler(id, emu_ctx); - } + if let Some(emu_dev) = active_vm().unwrap().find_emu_dev(ipa) { + return emu_dev.handler(emu_ctx); } + error!( - "emu_handler: no emul handler for Core {} data abort ipa 0x{:x}", + "emu_handler: no emul handler for Core {} data abort ipa 0x{:x}\nctx: {:?}", current_cpu().id, - ipa + ipa, + emu_ctx, ); false } -/// Function to register an emulator device. -pub fn emu_register_dev( - emu_type: EmuDeviceType, - vm_id: usize, - dev_id: usize, - address: usize, - size: usize, - handler: EmuDevHandler, -) { - let mut emu_devs_list = EMU_DEVS_LIST.lock(); - - for emu_dev in &*emu_devs_list { - if vm_id != emu_dev.vm_id { - continue; - } - if in_range(address, emu_dev.ipa, emu_dev.size - 1) || in_range(emu_dev.ipa, address, size - 1) { - panic!("emu_register_dev: duplicated emul address region: prev address 0x{:x} size 0x{:x}, next address 0x{:x} size 0x{:x}", emu_dev.ipa, emu_dev.size, address, size); - } - } - - emu_devs_list.push(EmuDevEntry { - emu_type, - vm_id, - id: dev_id, - ipa: address, - size, - handler, - }); -} - -/// Function to remove an emulator device. -pub fn emu_remove_dev(vm_id: usize, dev_id: usize, address: usize, size: usize) { - let mut emu_devs_list = EMU_DEVS_LIST.lock(); - for (idx, emu_dev) in emu_devs_list.iter().enumerate() { - if vm_id == emu_dev.vm_id && emu_dev.ipa == address && emu_dev.id == dev_id && emu_dev.size == size { - emu_devs_list.remove(idx); - return; - } - } - panic!( - "emu_remove_dev: emu dev not exist address 0x{:x} size 0x{:x}", - address, size - ); -} - /// Static RwLock containing a vector of EmuRegEntry instances, representing emulator registers. static EMU_REGS_LIST: RwLock> = RwLock::new(Vec::new()); diff --git a/src/device/memrsv.rs b/src/device/memrsv.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e567a9a43a633d6e62092e86a882acfc1f394ea --- /dev/null +++ b/src/device/memrsv.rs @@ -0,0 +1,209 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +// The following is the structure of the flattened device tree (FDT) +// --------------------------------- +// | Ftd_header | +// --------------------------------- +// | (free space) | +// --------------------------------- +// | Memory reservation block | +// --------------------------------- +// | (free space) | +// --------------------------------- +// | Structure Block | +// --------------------------------- +// | (free space) | +// --------------------------------- +// | Strings Block | +// --------------------------------- +// | (free space) | +// --------------------------------- + +use core::cmp::Ordering; +use core::fmt::{Display, Error, Formatter}; + +// Flatteened Device Tree header offsets +mod offset { + // Offset of the magic number + pub const MAGIC: usize = 0x0; + // Offset of total size of the device tree + pub const TOTALSIZE: usize = 0x4; + // Offset of the structure block + pub const OFF_DT_STRUCT: usize = 0x8; + // Offset of the strings block + pub const OFF_DT_STRINGS: usize = 0xc; + // Offset of the memory reservation block + pub const OFF_MEM_RSVS: usize = 0x10; + // Offset of the version + pub const VERSION: usize = 0x14; + // Offset of the last compatible version + pub const LAST_COMP_VERSION: usize = 0x18; + // Offset of the boot cpuid + pub const BOOT_CPUID_PHYS: usize = 0x1c; + // Offset of the size of the strings block + pub const SIZE_DT_STRINGS: usize = 0x20; + // Offset of the size of the structure block + pub const SIZE_DT_STRUCT: usize = 0x24; +} + +// Get the beginning of the u32 structure block +fn get_be32(buf: &[u8], offset: usize) -> Result { + let bytes = buf.get(offset..offset + 4).ok_or(())?; + Ok(u32::from_be_bytes(bytes.try_into().unwrap())) +} + +// Set the beginning of the u32 structure block +fn set_be32(buf: &mut [u8], offset: usize, val: u32) -> Result<(), ()> { + let bytes = buf.get_mut(offset..offset + 4).ok_or(())?; + + bytes.copy_from_slice(&u32::to_be_bytes(val)); + Ok(()) +} + +// Get the beginning of the u64 structure block +fn get_be64(buf: &[u8], offset: usize) -> Result { + let bytes = buf.get(offset..offset + 8).ok_or(())?; + Ok(u64::from_be_bytes(bytes.try_into().unwrap())) +} + +// Set the beginning of the u64 structure block +fn set_be64(buf: &mut [u8], offset: usize, val: u64) -> Result<(), ()> { + let bytes = buf.get_mut(offset..offset + 8).ok_or(())?; + + bytes.copy_from_slice(&u64::to_be_bytes(val)); + Ok(()) +} + +trait Section { + // Offset of the section reference above + const OFF_OFFSET: usize; + + // Get the size of the section + fn get_size(dts: &[u8]) -> Option; + + // Do shift the section by the given "shift" + fn shift(dts: &mut [u8], shift: isize) -> Result<(), ()> { + let total_size = dts.len(); + let size = Self::get_size(dts).ok_or(())?; + let src = get_be32(dts, Self::OFF_OFFSET)?; // Get the offset of the section + let dst: u32 = match (src as usize).overflowing_add_signed(shift) { + // Calculate the new offset of the section + (dst, false) => dst.try_into().map_err(|_| ())?, + (_, true) => return Err(()), + }; + let src_end = src.checked_add(size).ok_or(())? as usize; // Calculate the end of the section + let dst_end = dst.checked_add(size).ok_or(())? as usize; // Calculate the new end of the section + + if src_end > total_size || dst_end > total_size { + return Err(()); // Check if the section is out of the device tree + } + + set_be32(dts, Self::OFF_OFFSET, dst)?; // Set the new offset of the section + dts.copy_within(src as usize..src_end, dst as usize); // Copy the section to the new offset + Ok(()) + } +} + +trait SizeSection: Section { + // The offset of the section + const OFF_OFFSET: usize; + + // The offset of the section's size + const SIZE_OFFSET: usize; +} + +impl Section for T { + const OFF_OFFSET: usize = ::OFF_OFFSET; + + fn get_size(dts: &[u8]) -> Option { + let bytes = dts.get(T::SIZE_OFFSET..T::SIZE_OFFSET + 4)?; + Some(u32::from_be_bytes(bytes.try_into().unwrap())) + } +} + +struct StructSection; + +impl SizeSection for StructSection { + const OFF_OFFSET: usize = offset::OFF_DT_STRUCT; + const SIZE_OFFSET: usize = offset::SIZE_DT_STRUCT; +} + +struct StringSection; + +impl SizeSection for StringSection { + const OFF_OFFSET: usize = offset::OFF_DT_STRINGS; + const SIZE_OFFSET: usize = offset::SIZE_DT_STRINGS; +} + +/// Error type for memory reservation block +pub struct MemRsvError(); + +impl Display for MemRsvError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "Memory reservation block error") + } +} + +/// Result type for memory reservation block +pub type MemRsvResult = Result<(), MemRsvError>; + +/// Resize the memory reservation block +fn mem_rsvmap_resize(dts: &mut [u8], adj: isize) -> Result<(), ()> { + // Add/Delete a fdt_reserve_entry need resize 2 u64 of the value of "address" and "size" + let byteadj = adj * 2 * 8; + + match byteadj.cmp(&0) { + // Geater than 0 means add a fdt_reserve_entry + // StringSection is after StructSection, so the StringSection should be shifted first + Ordering::Greater => { + StringSection::shift(dts, byteadj)?; + StructSection::shift(dts, byteadj)?; + } + // Less than 0 means delete a fdt_reserve_entry + // StringSection is after StructSection, so the StructSection should be shifted first + Ordering::Less => { + StructSection::shift(dts, byteadj)?; + StringSection::shift(dts, byteadj)?; + } + Ordering::Equal => (), + } + + Ok(()) +} + +/// Append a memory reservation block to the device tree +/// +/// This function appends given memory ranges, as pairs of (start, len) to stands for the memory reservation block. +pub fn mem_reserve(dts: &mut [u8], ranges: &[(u64, u64)]) -> MemRsvResult { + // Resize the strcut and string section first + mem_rsvmap_resize(dts, ranges.len() as isize).map_err(|()| MemRsvError())?; + + // Get the offset of the memory reservation block + let offset = get_be32(dts, offset::OFF_MEM_RSVS).map_err(|()| MemRsvError())?; + // Delete the data fronted the memory reservation block + let rsvmap = &mut dts[offset as usize..]; + let mut it = rsvmap.chunks_exact_mut(16). // The size of a fdt_reserve_entry is 16 bytes + skip_while(|chunk| get_be64(chunk, 0).unwrap() != 0 || get_be64(chunk, 8).unwrap() != 0); // Skip the empty fdt_reserve_entry + + for (base, size) in ranges { + let chunk = it.next().ok_or(MemRsvError())?; + + set_be64(chunk, 0, *base).map_err(|()| MemRsvError())?; + set_be64(chunk, 8, *size).map_err(|()| MemRsvError())?; + } + + // Don't forget to set the end of the memory reservation block to 0 + let chunk = it.next().ok_or(MemRsvError())?; + + set_be64(chunk, 0, 0).map_err(|()| MemRsvError())?; + set_be64(chunk, 8, 0).map_err(|()| MemRsvError())?; + Ok(()) +} diff --git a/src/device/meta/meta.rs b/src/device/meta/meta.rs index c96473621bc7199728b4cd96c3d06808d12d1eef..4c9d5831182bb84bfcaa0ac0a8be7c8d15eb07df 100644 --- a/src/device/meta/meta.rs +++ b/src/device/meta/meta.rs @@ -1,13 +1,16 @@ -use crate::config::VmEmulatedDeviceConfig; -use crate::device::EmuContext; -use crate::device::meta::dispatch; -use crate::kernel::Vm; -use crate::error::{ErrorKind, Result}; +use core::ops::Range; use alloc::collections::BTreeMap; use alloc::boxed::Box; +use alloc::sync::Arc; use spin::RwLock; +use crate::config::VmEmulatedDeviceConfig; +use crate::device::{EmuContext, EmuDev, EmuDeviceType, meta}; +use crate::device::meta::dispatch; +use crate::kernel::Vm; +use crate::error::{ErrorKind, Result}; + /// Metadata context containing device ID and emulation context. #[derive(Debug, Clone, Copy)] pub struct MetaContext { @@ -56,3 +59,41 @@ pub fn unregister(dev_id: usize) { let mut devs = META_DEVICES.write(); devs.remove(&dev_id).unwrap(); } + +pub struct EmuMetaDev { + address_range: Range, + dev_id: usize, +} + +impl EmuDev for EmuMetaDev { + fn emu_type(&self) -> crate::device::EmuDeviceType { + crate::device::EmuDeviceType::EmuDeviceTMeta + } + + fn address_range(&self) -> Range { + self.address_range.clone() + } + + fn handler(&self, emu_ctx: &EmuContext) -> bool { + emu_meta_handler(self.dev_id, emu_ctx) + } +} + +pub fn emu_meta_init( + dev_id: usize, + vm: &Vm, + cfg: &VmEmulatedDeviceConfig, +) -> core::result::Result, ()> { + if cfg.emu_type == EmuDeviceType::EmuDeviceTMeta { + if meta::register(dev_id, vm, cfg).is_ok() { + Ok(Arc::new(EmuMetaDev { + address_range: cfg.base_ipa..cfg.base_ipa + cfg.length, + dev_id, + })) + } else { + Err(()) + } + } else { + Err(()) + } +} diff --git a/src/device/mod.rs b/src/device/mod.rs index 4aa0c224270258ea0496af2f453b56ff6e28cef9..a7151d27902b8abfa99b422abaff9e27e88aa1cf 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -10,13 +10,18 @@ //! Device module, including device tree, device model, and device emulation. +#[allow(unused_imports)] pub use self::device::*; pub use self::device_tree::*; pub use self::emu::*; pub use self::virtio::*; +#[cfg(feature = "memrsv")] +pub use self::memrsv::*; mod device; mod device_tree; mod emu; +#[cfg(feature = "memrsv")] +mod memrsv; pub mod meta; mod virtio; diff --git a/src/device/virtio/blk.rs b/src/device/virtio/blk.rs index a9cc519e5823f7ca033261758cefc90a94e41cc0..8b41eec23a658695bbb322cab5ae996117f5a93d 100644 --- a/src/device/virtio/blk.rs +++ b/src/device/virtio/blk.rs @@ -10,16 +10,18 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use spin::Mutex; use crate::arch::PAGE_SIZE; use crate::device::{mediated_blk_list_get, VirtioMmio, Virtq}; use crate::kernel::{ active_vm_id, add_async_task, async_blk_id_req, async_blk_io_req, async_ipi_req, AsyncTask, AsyncTaskData, - AsyncTaskState, IoAsyncMsg, IoIdAsyncMsg, IpiMediatedMsg, push_used_info, Vm, vm_ipa2pa, + AsyncTaskState, IoAsyncMsg, IoIdAsyncMsg, IpiMediatedMsg, push_used_info, Vm, }; use crate::utils::{memcpy, trace}; +use super::mmio::VIRTIO_F_VERSION_1; + +/* VIRTIO_QUEUE_MAX_SIZE */ pub const VIRTQUEUE_BLK_MAX_SIZE: usize = 256; pub const VIRTQUEUE_NET_MAX_SIZE: usize = 256; @@ -43,29 +45,22 @@ pub const VIRTIO_BLK_S_OK: usize = 0; // pub const VIRTIO_BLK_S_IOERR: usize = 1; pub const VIRTIO_BLK_S_UNSUPP: usize = 2; +pub fn blk_features() -> usize { + VIRTIO_F_VERSION_1 | VIRTIO_BLK_F_SIZE_MAX | VIRTIO_BLK_F_SEG_MAX +} + /// Represents the geometry information of a block device. #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] struct BlkGeometry { cylinders: u16, heads: u8, sectors: u8, } -impl BlkGeometry { - /// Creates a default `BlkGeometry` instance. - fn default() -> BlkGeometry { - BlkGeometry { - cylinders: 0, - heads: 0, - sectors: 0, - } - } -} - /// Represents the topology information of a block device. #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] struct BlkTopology { // # of logical blocks per physical block (log2) physical_block_exp: u8, @@ -77,42 +72,26 @@ struct BlkTopology { opt_io_size: u32, } -impl BlkTopology { - /// Creates a default `BlkTopology` instance. - fn default() -> BlkTopology { - BlkTopology { - physical_block_exp: 0, - alignment_offset: 0, - min_io_size: 0, - opt_io_size: 0, - } - } -} - /// Represents a block descriptor. -#[derive(Clone)] pub struct BlkDesc { - inner: Arc>, + inner: BlkDescInner, } impl BlkDesc { /// Creates a default `BlkDesc` instance. - pub fn default() -> BlkDesc { - BlkDesc { - inner: Arc::new(Mutex::new(BlkDescInner::default())), - } - } - - /// Initializes the block descriptor configuration. - pub fn cfg_init(&self, bsize: usize) { - let mut inner = self.inner.lock(); - inner.cfg_init(bsize); + pub fn new(bsize: usize) -> BlkDesc { + let desc = BlkDescInner { + capacity: bsize, + size_max: BLOCKIF_SIZE_MAX as u32, + seg_max: BLOCKIF_IOV_MAX as u32, + ..Default::default() + }; + BlkDesc { inner: desc } } /// Gets the start address of the block descriptor. pub fn start_addr(&self) -> usize { - let inner = self.inner.lock(); - &inner.capacity as *const _ as usize + &self.inner.capacity as *const _ as usize } /// # Safety: @@ -132,7 +111,7 @@ impl BlkDesc { /// Represents the inner data structure of a block descriptor. #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct BlkDescInner { capacity: usize, size_max: u32, @@ -151,36 +130,6 @@ pub struct BlkDescInner { unused1: [u8; 3], } -impl BlkDescInner { - /// Creates a default `BlkDescInner` instance. - pub fn default() -> BlkDescInner { - BlkDescInner { - capacity: 0, - size_max: 0, - seg_max: 0, - geometry: BlkGeometry::default(), - blk_size: 0, - topology: BlkTopology::default(), - writeback: 0, - unused0: [0; 3], - max_discard_sectors: 0, - max_discard_seg: 0, - discard_sector_alignment: 0, - max_write_zeroes_sectors: 0, - max_write_zeroes_seg: 0, - write_zeroes_may_unmap: 0, - unused1: [0; 3], - } - } - - /// Initializes the block descriptor configuration. - pub fn cfg_init(&mut self, bsize: usize) { - self.capacity = bsize; - self.size_max = BLOCKIF_SIZE_MAX as u32; - self.seg_max = BLOCKIF_IOV_MAX as u32; - } -} - /// Represents a block I/O vector. #[repr(C)] #[derive(Clone)] @@ -197,97 +146,49 @@ pub struct BlkReqRegion { } /// Represents a VirtioBlk request. -#[derive(Clone)] +#[repr(C)] pub struct VirtioBlkReq { - inner: Arc>, - req_list: Arc>>, + region: BlkReqRegion, + mediated: bool, } impl VirtioBlkReq { /// Creates a default `VirtioBlkReq` instance. pub fn default() -> VirtioBlkReq { VirtioBlkReq { - inner: Arc::new(Mutex::new(VirtioBlkReqInner::default())), - req_list: Arc::new(Mutex::new(Vec::new())), + region: BlkReqRegion { start: 0, size: 0 }, + mediated: false, } } - pub fn add_req_node(&self, node: VirtioBlkReqNode, _vm: Vm) { - let mut list = self.req_list.lock(); - // let mediated_blk = mediated_blk_list_get(vm.med_blk_id()); - // push_used_info(node.desc_chain_head_idx, node.iov_total as u32, vm.id()); - list.push(node); - - // match list.last_mut() { - // None => { - // list.push(node); - // } - // Some(prev) => { - // if prev.req_type == node.req_type - // && (prev.sector + prev.iov_sum_up / SECTOR_BSIZE) == node.sector - // && (prev.iov_sum_up + node.iov_sum_up) / SECTOR_BSIZE < mediated_blk.dma_block_max() - // { - // prev.iov_sum_up += node.iov_sum_up; - // prev.iov.append(&mut node.iov); - // } else { - // list.push(node); - // } - // } - // } - } - - /// Gets the number of requests in the request list. - pub fn req_num(&self) -> usize { - let list = self.req_list.lock(); - list.len() - } - - /// Gets a request node at a specified index from the request list. - pub fn req_node(&self, idx: usize) -> VirtioBlkReqNode { - let list = self.req_list.lock(); - list[idx].clone() - } - - /// Clears the request list. - pub fn clear_node(&self) { - let mut list = self.req_list.lock(); - list.clear(); - } - /// Sets the start address of the block request region. - pub fn set_start(&self, start: usize) { - let mut inner = self.inner.lock(); - inner.set_start(start); + pub fn set_start(&mut self, start: usize) { + self.region.start = start; } /// Sets the size of the block request region. - pub fn set_size(&self, size: usize) { - let mut inner = self.inner.lock(); - inner.set_size(size); + pub fn set_size(&mut self, size: usize) { + self.region.size = size; } /// Sets whether the request is mediated. - pub fn set_mediated(&self, mediated: bool) { - let mut inner = self.inner.lock(); - inner.mediated = mediated; + pub fn set_mediated(&mut self, mediated: bool) { + self.mediated = mediated; } /// Checks if the request is mediated. pub fn mediated(&self) -> bool { - let inner = self.inner.lock(); - inner.mediated + self.mediated } /// Gets the start address of the block request region. pub fn region_start(&self) -> usize { - let inner = self.inner.lock(); - inner.region.start + self.region.start } /// Gets the size of the block request region. pub fn region_size(&self) -> usize { - let inner = self.inner.lock(); - inner.region.size + self.region.size } } @@ -321,42 +222,19 @@ impl VirtioBlkReqNode { } } -/// Represents the inner data structure of a VirtioBlkReq. -#[repr(C)] -struct VirtioBlkReqInner { - region: BlkReqRegion, - mediated: bool, - process_list: Vec, -} - -impl VirtioBlkReqInner { - /// Creates a default `VirtioBlkReqInner` instance. - pub fn default() -> VirtioBlkReqInner { - VirtioBlkReqInner { - region: BlkReqRegion { start: 0, size: 0 }, - mediated: false, - process_list: Vec::new(), - } - } - - /// Sets the start address of the block request region. - pub fn set_start(&mut self, start: usize) { - self.region.start = start; - } - - /// Sets the size of the block request region. - pub fn set_size(&mut self, size: usize) { - self.region.size = size; - } -} - /// Generates a block request using the provided parameters. -pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: usize, vm: Vm) { +pub fn generate_blk_req( + req: &VirtioBlkReq, + vq: Arc, + dev: Arc, + cache: usize, + vm: Arc, + req_node_list: Vec, +) { let region_start = req.region_start(); let region_size = req.region_size(); let mut cache_ptr = cache; - for idx in 0..req.req_num() { - let req_node = req.req_node(idx); + for req_node in req_node_list { let sector = req_node.sector; if sector + req_node.iov_sum_up / SECTOR_BSIZE > region_start + region_size { warn!( @@ -492,29 +370,20 @@ pub fn generate_blk_req(req: VirtioBlkReq, vq: Virtq, dev: VirtioMmio, cache: us // update used ring if !req.mediated() { todo!("reset num to vq size"); - // if !vq.update_used_ring(req_node.iov_total as u32, req_node.desc_chain_head_idx as u32) { - // println!("blk_req_handler: fail to update used ring"); - // } } else { push_used_info(req_node.desc_chain_head_idx, req_node.iov_total as u32, vm.id()); } } - - req.clear_node(); } /// Handles the notification for a mediated block request on the specified Virtqueue (`vq`) and Virtio block device (`blk`) /// associated with the virtual machine (`vm`). This function creates an asynchronous IPI task to process the mediated /// block request. -pub fn virtio_mediated_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { - // add_task_count(); +pub fn virtio_mediated_blk_notify_handler(vq: Arc, blk: Arc, vm: Arc) -> bool { + let src_vmid = vm.id(); let task = AsyncTask::new( - AsyncTaskData::AsyncIpiTask(IpiMediatedMsg { - src_id: vm.id(), - vq, - blk, - }), - vm.id(), + AsyncTaskData::AsyncIpiTask(IpiMediatedMsg { src_vm: vm, vq, blk }), + src_vmid, async_ipi_req(), ); add_async_task(task, true); @@ -524,32 +393,29 @@ pub fn virtio_mediated_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> /// Handles the notification for a Virtio block request on the specified Virtqueue (`vq`) and Virtio block device (`blk`) /// associated with the virtual machine (`vm`). This function processes the available descriptors in the Virtqueue and /// generates block requests accordingly. The function returns `true` upon successful handling. -pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { +pub fn virtio_blk_notify_handler(vq: Arc, blk: Arc, vm: Arc) -> bool { if vm.id() == 0 && active_vm_id() == 0 { panic!("src vm should not be 0"); } let avail_idx = vq.avail_idx(); - // let begin = time_current_us(); if vq.ready() == 0 { error!("blk virt_queue is not ready!"); return false; } - // let mediated = blk.mediated(); let dev = blk.dev(); let req = match dev.req() { - super::DevReq::BlkReq(blk_req) => blk_req, + Some(blk_req) => blk_req, _ => { - panic!("virtio_blk_notify_handler: illegal req"); + panic!("virtio_blk_notify_handler: illegal req") } }; - // let vq_size = vq.num(); let mut next_desc_idx_opt = vq.pop_avail_desc_idx(avail_idx); let mut process_count: i32 = 0; - // let mut desc_chain_head_idx; + let mut req_node_list: Vec = vec![]; // let time0 = time_current_us(); @@ -561,18 +427,9 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { } let mut head = true; - // desc_chain_head_idx = next_desc_idx; - - // vq.show_desc_info(4, vm.clone()); let mut req_node = VirtioBlkReqNode::default(); req_node.desc_chain_head_idx = next_desc_idx as u32; - // println!( - // "avail idx {} desc_chain_head {} avail flag {}", - // vq.last_avail_idx() - 1, - // req_node.desc_chain_head_idx, - // vq.avail_flags() - // ); loop { if vq.desc_has_next(next_desc_idx) { @@ -583,12 +440,11 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { next_desc_idx, vq.desc_flags(next_desc_idx) ); - blk.notify(vm); - // vq.notify(dev.int_id(), vm.clone()); + blk.notify(); return false; } head = false; - let vreq_addr = vm_ipa2pa(vm.clone(), vq.desc_addr(next_desc_idx)); + let vreq_addr = vm.ipa2pa(vq.desc_addr(next_desc_idx)); if vreq_addr == 0 { error!("virtio_blk_notify_handler: failed to get vreq"); return false; @@ -606,11 +462,10 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { req_node.req_type, vq.desc_flags(next_desc_idx) ); - blk.notify(vm); - // vq.notify(dev.int_id(), vm.clone()); + blk.notify(); return false; } - let data_bg = vm_ipa2pa(vm.clone(), vq.desc_addr(next_desc_idx)); + let data_bg = vm.ipa2pa(vq.desc_addr(next_desc_idx)); if data_bg == 0 { error!("virtio_blk_notify_handler: failed to get iov data begin"); return false; @@ -627,11 +482,10 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { /*state handler*/ if !vq.desc_is_writable(next_desc_idx) { error!("Failed to get virt blk queue desc status, idx = {}", next_desc_idx); - blk.notify(vm); - // vq.notify(dev.int_id(), vm.clone()); + blk.notify(); return false; } - let vstatus_addr = vm_ipa2pa(vm.clone(), vq.desc_addr(next_desc_idx)); + let vstatus_addr = vm.ipa2pa(vq.desc_addr(next_desc_idx)); if vstatus_addr == 0 { error!("virtio_blk_notify_handler: vm[{}] failed to vstatus", vm.id()); return false; @@ -648,35 +502,26 @@ pub fn virtio_blk_notify_handler(vq: Virtq, blk: VirtioMmio, vm: Vm) -> bool { next_desc_idx = vq.desc_next(next_desc_idx) as usize; } req_node.iov_total = req_node.iov_sum_up; - req.add_req_node(req_node, vm.clone()); + req_node_list.push(req_node); process_count += 1; next_desc_idx_opt = vq.pop_avail_desc_idx(avail_idx); } if !req.mediated() { - generate_blk_req(req.clone(), vq.clone(), blk.clone(), dev.cache(), vm.clone()); + unimplemented!("!!req.mediated()"); } else { let mediated_blk = mediated_blk_list_get(vm.med_blk_id()); let cache = mediated_blk.cache_pa(); - generate_blk_req(req.clone(), vq.clone(), blk.clone(), cache, vm.clone()); + generate_blk_req(req, vq.clone(), blk.clone(), cache, vm.clone(), req_node_list); }; // let time1 = time_current_us(); if vq.avail_flags() == 0 && process_count > 0 && !req.mediated() { trace!("virtio blk notify"); - blk.notify(vm); - // vq.notify(dev.int_id(), vm.clone()); + blk.notify(); } - // if req.mediated() { - // finish_task(true); - // finish_async_task(true); - // async_task_exe(); - // } - - // let end = time_current_us(); - // println!("init time {}us, while handle desc ring time {}us, finish task {}us", time0 - begin, time1 - time0, end - time1); true } diff --git a/src/device/virtio/console.rs b/src/device/virtio/console.rs index 74f0bdb74ea6b17782e099080a8738dba91fb2b1..703ba9166ca020a1395eba57f1a8837087d4fcc1 100644 --- a/src/device/virtio/console.rs +++ b/src/device/virtio/console.rs @@ -15,9 +15,7 @@ use spin::Mutex; use crate::arch::PAGE_SIZE; use crate::device::{VirtioMmio, Virtq}; use crate::device::DevDesc; -use crate::device::EmuDevs; use crate::device::VirtioIov; -use crate::kernel::{active_vm, vm_if_set_mem_map_bit, vm_ipa2pa}; use crate::kernel::vm; use crate::kernel::Vm; use crate::utils::round_down; @@ -42,9 +40,8 @@ const VIRTIO_CONSOLE_PORT_OPEN: usize = 6; const VIRTIO_CONSOLE_PORT_NAME: usize = 7; /// A wrapper structure for `ConsoleDescInner` providing a convenient and thread-safe interface. -#[derive(Clone)] pub struct ConsoleDesc { - inner: Arc>, + inner: Mutex, } /// Holds data related to console configuration. @@ -59,21 +56,18 @@ pub struct ConsoleDescData { } impl ConsoleDesc { - /// Creates a new `ConsoleDesc` with default inner values. - pub fn default() -> ConsoleDesc { + /// Creates a new `ConsoleDesc` with passed value. + pub fn new(oppo_end_vmid: u16, oppo_end_ipa: u64) -> ConsoleDesc { + let mut desc = ConsoleDescInner::default(); + desc.oppo_end_vmid = oppo_end_vmid; + desc.oppo_end_ipa = oppo_end_ipa; + desc.cols = 80; + desc.rows = 25; ConsoleDesc { - inner: Arc::new(Mutex::new(ConsoleDescInner::default())), + inner: Mutex::new(desc), } } - pub fn cfg_init(&self, oppo_end_vmid: u16, oppo_end_ipa: u64) { - let mut inner = self.inner.lock(); - inner.oppo_end_vmid = oppo_end_vmid; - inner.oppo_end_ipa = oppo_end_ipa; - inner.cols = 80; - inner.rows = 25; - } - /// Returns the starting address of the console configuration. pub fn start_addr(&self) -> usize { let inner = self.inner.lock(); @@ -121,9 +115,8 @@ pub fn console_features() -> usize { /// Handles notification for a Virtio console request on the specified Virtqueue (`vq`), Virtio console device (`console`), and virtual machine (`vm`). /// This function processes the available descriptors in the Virtqueue and performs necessary operations. /// Returns `true` upon successful handling. -pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> bool { +pub fn virtio_console_notify_handler(vq: Arc, console: Arc, vm: Arc) -> bool { if vq.vq_indx() % 4 != 1 { - // println!("console rx queue notified!"); return true; } @@ -132,7 +125,6 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> return false; } - let tx_iov = VirtioIov::default(); let dev = console.dev(); let (trgt_vmid, trgt_console_ipa) = match dev.desc() { @@ -143,21 +135,19 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> } }; - // let buf = dev.cache(); let mut next_desc_idx_opt = vq.pop_avail_desc_idx(vq.avail_idx()); while next_desc_idx_opt.is_some() { let mut idx = next_desc_idx_opt.unwrap() as usize; let mut len = 0; - tx_iov.clear(); + let mut tx_iov = VirtioIov::default(); loop { - let addr = vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)); + let addr = vm.ipa2pa(vq.desc_addr(idx)); if addr == 0 { error!("virtio_console_notify_handler: failed to desc addr"); return false; } - // println!("virtio_consle:addr:{:#x}", addr); tx_iov.push_data(addr, vq.desc_len(idx) as usize); len += vq.desc_len(idx) as usize; @@ -166,29 +156,9 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> } idx = vq.desc_next(idx) as usize; } - // unsafe { - // let slice = core::slice::from_raw_parts( - // vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)) as *const u8, - // len as usize, - // ); - // use alloc::string::String; - // let s = String::from_utf8_lossy(slice); - // println!("send data:{}", s); - // } - - if !virtio_console_recv(trgt_vmid, trgt_console_ipa, tx_iov.clone(), len) { + + if !virtio_console_recv(trgt_vmid, trgt_console_ipa, tx_iov, len) { error!("virtio_console_notify_handler: failed send"); - // return false; - } - if vm.id() != 0 { - let used_addr = vm_ipa2pa(vm.clone(), vq.used_addr()); - // println!( - // "virtio_console_notify_handler: used addr {:x}, size {}", - // used_addr, - // size_of::() - // ); - vm_if_set_mem_map_bit(vm.clone(), used_addr); - vm_if_set_mem_map_bit(vm.clone(), used_addr + PAGE_SIZE); } if !vq.update_used_ring(len as u32, next_desc_idx_opt.unwrap() as u32) { return false; @@ -202,8 +172,7 @@ pub fn virtio_console_notify_handler(vq: Virtq, console: VirtioMmio, vm: Vm) -> return false; } - console.notify(vm); - // vq.notify(dev.int_id(), vm.clone()); + console.notify(); true } @@ -219,15 +188,18 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, Some(vm) => vm, }; - let console = match trgt_vm.emu_console_dev(trgt_console_ipa as usize) { - EmuDevs::VirtioConsole(x) => x, - _ => { + let console = match trgt_vm + .find_emu_dev(trgt_console_ipa as usize) + .and_then(|dev| dev.into_any_arc().downcast::().ok()) + { + None => { error!( "virtio_console_recv: trgt_vm[{}] failed to get virtio console dev", trgt_vmid ); return true; } + Some(vm) => vm, }; if !console.dev().activated() { @@ -254,16 +226,15 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, error!("virtio_console_recv: receive invalid avail desc idx"); return false; } else if desc_header_idx_opt.is_none() { - // println!("virtio_console_recv: desc_header_idx_opt.is_none()"); return true; } let desc_idx_header = desc_header_idx_opt.unwrap(); let mut desc_idx = desc_header_idx_opt.unwrap() as usize; - let rx_iov = VirtioIov::default(); + let mut rx_iov = VirtioIov::default(); let mut rx_len = 0; loop { - let dst = vm_ipa2pa(trgt_vm.clone(), rx_vq.desc_addr(desc_idx)); + let dst = trgt_vm.ipa2pa(rx_vq.desc_addr(desc_idx)); if dst == 0 { error!( "virtio_console_recv: failed to get dst, desc_idx {}, avail idx {}", @@ -272,13 +243,11 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, ); return false; } - // println!("virtio_consle recv:addr:{:#x}", dst); let desc_len = rx_vq.desc_len(desc_idx) as usize; // dirty pages if trgt_vmid != 0 { let mut addr = round_down(dst, PAGE_SIZE); while addr <= round_down(dst + desc_len, PAGE_SIZE) { - vm_if_set_mem_map_bit(trgt_vm.clone(), addr); addr += PAGE_SIZE; } } @@ -299,7 +268,7 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, return false; } - if tx_iov.write_through_iov(rx_iov.clone(), len) > 0 { + if tx_iov.write_through_iov(&rx_iov, len) > 0 { error!( "virtio_console_recv: write through iov failed, rx_iov_num {} tx_iov_num {} rx_len {} tx_len {}", rx_iov.num(), @@ -310,11 +279,6 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, return false; } - if trgt_vmid != 0 { - let used_addr = vm_ipa2pa(trgt_vm.clone(), rx_vq.used_addr()); - vm_if_set_mem_map_bit(trgt_vm.clone(), used_addr); - vm_if_set_mem_map_bit(trgt_vm.clone(), used_addr + PAGE_SIZE); - } if !rx_vq.update_used_ring(len as u32, desc_idx_header as u32) { error!( "virtio_console_recv: update used ring failed len {} rx_vq num {}", @@ -324,7 +288,6 @@ fn virtio_console_recv(trgt_vmid: u16, trgt_console_ipa: u64, tx_iov: VirtioIov, return false; } - console.notify(trgt_vm); - // rx_vq.notify(console.dev().int_id(), trgt_vm.clone()); + console.notify(); true } diff --git a/src/device/virtio/dev.rs b/src/device/virtio/dev.rs index 414ac15df9366a7abc60eee68b774c827fc9b2de..06671c2a444e2e08378371c516715e2e8ac0b8e8 100644 --- a/src/device/virtio/dev.rs +++ b/src/device/virtio/dev.rs @@ -8,19 +8,13 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::sync::Arc; - use spin::Mutex; use crate::config::VmEmulatedDeviceConfig; // use crate::device::add_mediated_dev; -use crate::device::{net_features, NetDesc, NetDescData}; -use crate::device::{console_features, ConsoleDesc, ConsoleDescData}; -use crate::device::{BlkDesc, BLOCKIF_IOV_MAX, VirtioBlkReq}; -use crate::device::{VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_F_VERSION_1}; -use crate::device::{BlkStat, NicStat}; -use crate::kernel::mem_pages_alloc; -use crate::mm::PageFrame; +use crate::device::{net_features, blk_features, NetDesc}; +use crate::device::{console_features, ConsoleDesc}; +use crate::device::{BlkDesc, VirtioBlkReq}; /// Represents the type of a Virtio device. #[derive(Copy, Clone, Debug)] @@ -31,43 +25,6 @@ pub enum VirtioDeviceType { Console = 3, } -/// Placeholder struct for block device configuration data. -pub struct BlkDescData {} - -/// Represents different types of device descriptions. -pub enum DevDescData { - /// Reserved block device description. - BlkDesc(BlkDescData), - /// Network device description. - NetDesc(NetDescData), - /// Console device description. - ConsoleDesc(ConsoleDescData), - /// No device description. - None, -} - -/// Represents various data associated with a Virtio device. -pub struct VirtDevData { - pub activated: bool, - pub dev_type: VirtioDeviceType, - pub features: usize, - pub generation: usize, - pub int_id: usize, - pub desc: DevDescData, - // req: reserve; we used nfs, no need to mig blk req data - // cache: reserve - // stat: reserve -} - -/// Represents the status of a device. -#[derive(Clone)] -pub enum DevStat { - BlkStat(BlkStat), - NicStat(NicStat), - None, -} - -#[derive(Clone)] pub enum DevDesc { BlkDesc(BlkDesc), NetDesc(NetDesc), @@ -75,35 +32,55 @@ pub enum DevDesc { None, } -#[derive(Clone)] -pub enum DevReq { - BlkReq(VirtioBlkReq), - None, -} - -#[derive(Clone)] pub struct VirtDev { - inner: Arc>, + dev_type: VirtioDeviceType, + int_id: usize, + desc: DevDesc, + features: usize, + req: Option, + inner: Mutex, } impl VirtDev { /// Creates a new `VirtDev` with default inner values. - pub fn default() -> VirtDev { - VirtDev { - inner: Arc::new(Mutex::new(VirtDevInner::default())), + pub fn new(dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig) -> Self { + let (desc, features, req) = match dev_type { + VirtioDeviceType::Block => { + let desc = DevDesc::BlkDesc(BlkDesc::new(config.cfg_list[1])); + let features = blk_features(); + let mut blk_req = VirtioBlkReq::default(); + blk_req.set_start(config.cfg_list[0]); + blk_req.set_mediated(config.mediated); + blk_req.set_size(config.cfg_list[1]); + (desc, features, Some(blk_req)) + } + VirtioDeviceType::Net => { + let desc = DevDesc::NetDesc(NetDesc::new(&config.cfg_list)); + let features = net_features(); + (desc, features, None) + } + VirtioDeviceType::Console => { + let desc = DevDesc::ConsoleDesc(ConsoleDesc::new(config.cfg_list[0] as u16, config.cfg_list[1] as u64)); + let features = console_features(); + (desc, features, None) + } + _ => { + panic!("ERROR: Wrong virtio device type"); + } + }; + Self { + dev_type, + int_id: config.irq_id, + desc, + features, + req, + inner: Mutex::new(VirtDevInner::default()), } } - /// Initializes the Virtio device with the specified parameters. - pub fn init(&self, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig, mediated: bool) { - let mut inner = self.inner.lock(); - inner.init(dev_type, config, mediated); - } - /// Retrieves the features supported by the Virtio device. pub fn features(&self) -> usize { - let inner = self.inner.lock(); - inner.features + self.features } /// Retrieves the generation of the Virtio device. @@ -113,33 +90,18 @@ impl VirtDev { } /// Retrieves the device description associated with the Virtio device. - pub fn desc(&self) -> DevDesc { - let inner = self.inner.lock(); - inner.desc.clone() + pub fn desc(&self) -> &DevDesc { + &self.desc } /// Retrieves the device request associated with the Virtio device. - pub fn req(&self) -> DevReq { - let inner = self.inner.lock(); - inner.req.clone() + pub fn req(&self) -> &Option { + &self.req } /// Retrieves the interrupt ID associated with the Virtio device. pub fn int_id(&self) -> usize { - let inner = self.inner.lock(); - inner.int_id - } - - /// Retrieves the cache associated with the Virtio device. - pub fn cache(&self) -> usize { - let inner = self.inner.lock(); - return inner.cache.as_ref().unwrap().pa(); - } - - /// Retrieves the status of the Virtio device. - pub fn stat(&self) -> DevStat { - let inner = self.inner.lock(); - inner.stat.clone() + self.int_id } /// Checks if the Virtio device is activated. @@ -156,22 +118,17 @@ impl VirtDev { /// Checks if the Virtio device is mediated. pub fn mediated(&self) -> bool { - let inner = self.inner.lock(); - inner.mediated() + match self.req() { + Some(req) => req.mediated(), + None => false, + } } } /// Represents the inner data structure for `VirtDev`. pub struct VirtDevInner { activated: bool, - dev_type: VirtioDeviceType, - features: usize, generation: usize, - int_id: usize, - desc: DevDesc, - req: DevReq, - cache: Option, - stat: DevStat, } impl VirtDevInner { @@ -179,101 +136,7 @@ impl VirtDevInner { pub fn default() -> VirtDevInner { VirtDevInner { activated: false, - dev_type: VirtioDeviceType::None, - features: 0, generation: 0, - int_id: 0, - desc: DevDesc::None, - req: DevReq::None, - cache: None, - stat: DevStat::None, - } - } - - /// Checks if the Virtio device is mediated. - pub fn mediated(&self) -> bool { - match &self.req { - DevReq::BlkReq(req) => req.mediated(), - DevReq::None => false, - } - } - - /// Initializes the Virtio device with the specified parameters. - // virtio_dev_init - pub fn init(&mut self, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig, mediated: bool) { - self.dev_type = dev_type; - self.int_id = config.irq_id; - - match self.dev_type { - VirtioDeviceType::Block => { - let blk_desc = BlkDesc::default(); - blk_desc.cfg_init(config.cfg_list[1]); - self.desc = DevDesc::BlkDesc(blk_desc); - - // TODO: blk_features_init & cache init - self.features |= VIRTIO_BLK_F_SIZE_MAX | VIRTIO_BLK_F_SEG_MAX | VIRTIO_F_VERSION_1; - - let blk_req = VirtioBlkReq::default(); - blk_req.set_start(config.cfg_list[0]); - blk_req.set_mediated(mediated); - blk_req.set_size(config.cfg_list[1]); - self.req = DevReq::BlkReq(blk_req); - - match mem_pages_alloc(BLOCKIF_IOV_MAX) { - Ok(page_frame) => { - // println!("PageFrame pa {:x}", page_frame.pa()); - self.cache = Some(page_frame); - // if mediated { - // // todo: change to iov ring - // let cache_size = BLOCKIF_IOV_MAX * PAGE_SIZE; - // add_mediated_dev(0, page_frame.pa(), cache_size); - // } - } - Err(_) => { - error!("VirtDevInner::init(): mem_pages_alloc failed"); - } - } - - self.stat = DevStat::BlkStat(BlkStat::default()); - } - VirtioDeviceType::Net => { - let net_desc = NetDesc::default(); - net_desc.cfg_init(&config.cfg_list); - self.desc = DevDesc::NetDesc(net_desc); - - self.features |= net_features(); - - match mem_pages_alloc(1) { - Ok(page_frame) => { - // println!("PageFrame pa {:x}", page_frame.pa()); - self.cache = Some(page_frame); - } - Err(_) => { - error!("VirtDevInner::init(): mem_pages_alloc failed"); - } - } - - self.stat = DevStat::NicStat(NicStat::default()); - } - VirtioDeviceType::Console => { - let console_desc = ConsoleDesc::default(); - console_desc.cfg_init(config.cfg_list[0] as u16, config.cfg_list[1] as u64); - self.desc = DevDesc::ConsoleDesc(console_desc); - self.features |= console_features(); - - match mem_pages_alloc(1) { - Ok(page_frame) => { - // println!("PageFrame pa {:x}", page_frame.pa()); - self.cache = Some(page_frame); - } - Err(_) => { - error!("VirtDevInner::init(): mem_pages_alloc failed"); - } - } - } - _ => { - panic!("ERROR: Wrong virtio device type"); - } } } } diff --git a/src/device/virtio/iov.rs b/src/device/virtio/iov.rs index 5daf7a905e92d27ae8d4a77118c935f659796f51..6168e529c4b2fbe15a1e0d87283e34040d7612b3 100644 --- a/src/device/virtio/iov.rs +++ b/src/device/virtio/iov.rs @@ -7,52 +7,44 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. - -use alloc::sync::Arc; use alloc::vec::Vec; use core::slice::from_raw_parts; -use spin::Mutex; - use crate::utils::{memcpy, trace}; /// Represents a Virtio I/O vector. -#[derive(Clone)] pub struct VirtioIov { - inner: Arc>, + vector: Vec, +} + +impl core::ops::Deref for VirtioIov { + type Target = [VirtioIovData]; + + fn deref(&self) -> &Self::Target { + &self.vector + } } impl VirtioIov { /// Creates a new `VirtioIov` with default inner values. pub fn default() -> VirtioIov { - VirtioIov { - inner: Arc::new(Mutex::new(VirtioIovInner::default())), - } - } - - /// Clears the I/O vector by removing all data. - pub fn clear(&self) { - let mut inner = self.inner.lock(); - inner.vector.clear(); + VirtioIov { vector: Vec::new() } } /// Adds a data segment to the I/O vector. - pub fn push_data(&self, buf: usize, len: usize) { - let mut inner = self.inner.lock(); - inner.vector.push(VirtioIovData { buf, len }); + pub fn push_data(&mut self, buf: usize, len: usize) { + self.vector.push(VirtioIovData { buf, len }); } /// Retrieves the buffer address at the specified index. pub fn get_buf(&self, idx: usize) -> usize { - let inner = self.inner.lock(); - inner.vector[idx].buf + self.vector[idx].buf } /// Copies data from the I/O vector to the specified buffer. pub fn to_buf(&self, addr: usize, len: usize) { let mut size = len; - let inner = self.inner.lock(); - for iov_data in &inner.vector { + for iov_data in &self.vector { let offset = len - size; let dst = addr + offset; if iov_data.len >= size { @@ -76,10 +68,9 @@ impl VirtioIov { } /// Copies data from the specified buffer to the I/O vector. - pub fn from_buf(&self, addr: usize, len: usize) { + pub fn from_buf(&mut self, addr: usize, len: usize) { let mut size = len; - let inner = self.inner.lock(); - for iov_data in &inner.vector { + for iov_data in &self.vector { let offset = len - size; let src = addr + offset; if iov_data.len >= size { @@ -104,23 +95,19 @@ impl VirtioIov { /// Retrieves the number of data segments in the I/O vector. pub fn num(&self) -> usize { - let inner = self.inner.lock(); - inner.vector.len() + self.vector.len() } /// Retrieves the length of the data segment at the specified index. pub fn get_len(&self, idx: usize) -> usize { - let inner = self.inner.lock(); - inner.vector[idx].len + self.vector[idx].len } /// Retrieves a pointer to the data in the I/O vector. pub fn get_ptr(&self, size: usize) -> &'static [u8] { - let inner = self.inner.lock(); - // let mut iov_idx = 0; let mut idx = size; - for iov_data in &inner.vector { + for iov_data in &self.vector { if iov_data.len > idx { if trace() && iov_data.buf + idx < 0x1000 { panic!("illegal addr {:x}", iov_data.buf + idx); @@ -134,25 +121,23 @@ impl VirtioIov { } debug!("iov get_ptr failed"); - debug!("get_ptr iov {:#?}", inner.vector); + debug!("get_ptr iov {:#?}", self.vector); debug!("size {}, idx {}", size, idx); &[0] } /// Writes data from the I/O vector to another I/O vector. - pub fn write_through_iov(&self, dst: VirtioIov, remain: usize) -> usize { - let inner = self.inner.lock(); - + pub fn write_through_iov(&self, dst: &VirtioIov, remain: usize) -> usize { let mut dst_iov_idx = 0; let mut src_iov_idx = 0; let mut dst_ptr = dst.get_buf(0); - let mut src_ptr = inner.vector[0].buf; + let mut src_ptr = self.vector[0].buf; let mut dst_vlen_remain = dst.get_len(0); - let mut src_vlen_remain = inner.vector[0].len; + let mut src_vlen_remain = self.vector[0].len; let mut remain = remain; while remain > 0 { - if dst_iov_idx == dst.num() || src_iov_idx == inner.vector.len() { + if dst_iov_idx == dst.num() || src_iov_idx == self.vector.len() { break; } @@ -169,9 +154,9 @@ impl VirtioIov { memcpy(dst_ptr as *const u8, src_ptr as *const u8, written); } src_iov_idx += 1; - if src_iov_idx < inner.vector.len() { - src_ptr = inner.vector[src_iov_idx].buf; - src_vlen_remain = inner.vector[src_iov_idx].len; + if src_iov_idx < self.vector.len() { + src_ptr = self.vector[src_iov_idx].buf; + src_vlen_remain = self.vector[src_iov_idx].len; dst_ptr += written; dst_vlen_remain -= written; } @@ -193,11 +178,11 @@ impl VirtioIov { src_ptr += written; src_vlen_remain -= written; } - if inner.vector[src_iov_idx].len == 0 { + if self.vector[src_iov_idx].len == 0 { src_iov_idx += 1; - if src_iov_idx < inner.vector.len() { - src_ptr = inner.vector[src_iov_idx].buf; - src_vlen_remain = inner.vector[src_iov_idx].len; + if src_iov_idx < self.vector.len() { + src_ptr = self.vector[src_iov_idx].buf; + src_vlen_remain = self.vector[src_iov_idx].len; } } } @@ -210,20 +195,7 @@ impl VirtioIov { /// Represents a data segment in the Virtio I/O vector. #[derive(Debug)] -struct VirtioIovData { +pub struct VirtioIovData { buf: usize, len: usize, } - -/// Represents the inner data structure for `VirtioIov`. -#[derive(Debug)] -struct VirtioIovInner { - vector: Vec, -} - -impl VirtioIovInner { - /// Creates a new `VirtioIovInner` with an empty vector. - pub fn default() -> VirtioIovInner { - VirtioIovInner { vector: Vec::new() } - } -} diff --git a/src/device/virtio/mac.rs b/src/device/virtio/mac.rs new file mode 100644 index 0000000000000000000000000000000000000000..05e1e1594184f26a39bf7847dc01bad8b0f03975 --- /dev/null +++ b/src/device/virtio/mac.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2023 Beihang University, Huawei Technologies Co.,Ltd. All rights reserved. +// Rust-Shyper is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +use alloc::collections::BTreeMap; +use alloc::sync::Arc; + +use spin::Mutex; + +use super::VirtioMmio; + +static MAC2NIC_INFO: Mutex>> = Mutex::new(BTreeMap::new()); + +#[derive(PartialEq, Eq, PartialOrd, Ord)] +struct MacAddress([u8; 6]); + +impl MacAddress { + fn new(mac: &[u8]) -> Self { + let mut this = Self([0; 6]); + this.0.clone_from_slice(&mac[0..6]); + this + } +} + +pub fn set_mac_info(mac: &[u8], nic: Arc) { + MAC2NIC_INFO.lock().insert(MacAddress::new(mac), nic); +} + +pub fn mac_to_nic(mac: &[u8]) -> Option> { + MAC2NIC_INFO.lock().get(&MacAddress::new(mac)).cloned() +} + +#[inline] +pub fn virtio_nic_list_walker(mut f: F) +where + F: FnMut(&Arc), +{ + for nic in MAC2NIC_INFO.lock().values() { + f(nic); + } +} + +pub fn remove_virtio_nic(vmid: usize) { + MAC2NIC_INFO.lock().retain(|_mac, nic| { + if let Some(vm) = nic.upper_vm() { + vm.id() != vmid + } else { + false // if the vm is gone, the nic should be removed + } + }); +} diff --git a/src/device/virtio/mediated.rs b/src/device/virtio/mediated.rs index c8d71b8ccb8bd94eea46f1ea228035132b8c93ad..aa499b4c8bd14e930b377fef5cee312c5b484ef4 100644 --- a/src/device/virtio/mediated.rs +++ b/src/device/virtio/mediated.rs @@ -15,7 +15,7 @@ use spin::Mutex; use crate::device::{virtio_blk_notify_handler, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT}; use crate::kernel::{ active_vm, async_task_exe, AsyncTaskState, finish_async_task, hvc_send_msg_to_vm, HvcDefaultMsg, HvcGuestMsg, - IpiInnerMsg, set_front_io_task_state, vm, vm_ipa2pa, VM_LIST, + IpiInnerMsg, set_front_io_task_state, vm_ipa2pa, vm_list_walker, }; use crate::kernel::IpiMessage; @@ -25,7 +25,7 @@ pub static MEDIATED_BLK_LIST: Mutex> = Mutex::new(Vec::new()); /// Adds a mediated block to the list and assigns it to a VM if needed. pub fn mediated_blk_list_push(mut blk: MediatedBlk) { let mut list = MEDIATED_BLK_LIST.lock(); - for vm in VM_LIST.lock().iter() { + vm_list_walker(|vm| { if let Some(id) = vm.config().mediated_block_index() { if id == list.len() { info!("Assign blk[{}] to VM {}", list.len(), vm.id()); @@ -36,10 +36,9 @@ pub fn mediated_blk_list_push(mut blk: MediatedBlk) { use crate::vmm::vmm_boot_vm; vmm_boot_vm(vm.id()); } - break; } } - } + }); list.push(blk); } @@ -187,13 +186,13 @@ pub struct MediatedBlkReq { // only run in vm0 pub fn mediated_dev_append(_class_id: usize, mmio_ipa: usize) -> Result { let vm = active_vm().unwrap(); - let blk_pa = vm_ipa2pa(vm.clone(), mmio_ipa); + let blk_pa = vm_ipa2pa(&vm, mmio_ipa); // TODO: check weather the blk_pa is valid // SAFETY:'blk_pa' is valid MMIO address of virtio-blk config let mediated_blk = unsafe { MediatedBlk::from_addr(blk_pa) }; mediated_blk.set_nreq(0); - let cache_pa = vm_ipa2pa(vm, mediated_blk.cache_ipa()); + let cache_pa = vm_ipa2pa(&vm, mediated_blk.cache_ipa()); info!( "mediated_dev_append: dev_ipa_reg 0x{:x}, cache ipa 0x{:x}, cache_pa 0x{:x}, dma_block_max 0x{:x}", mmio_ipa, @@ -209,7 +208,7 @@ pub fn mediated_dev_append(_class_id: usize, mmio_ipa: usize) -> Result Result { - let dev_pa_reg = vm_ipa2pa(active_vm().unwrap(), dev_ipa_reg); + let dev_pa_reg = vm_ipa2pa(&active_vm().unwrap(), dev_ipa_reg); // check weather src vm is still alive let mediated_blk = match mediated_blk_list_get_from_pa(dev_pa_reg) { @@ -231,13 +230,11 @@ pub fn mediated_blk_notify_handler(dev_ipa_reg: usize) -> Result { } // call by normal VMs ipi request (generated by mediated virtio blk) -pub fn mediated_ipi_handler(msg: &IpiMessage) { +pub fn mediated_ipi_handler(msg: IpiMessage) { // println!("core {} mediated_ipi_handler", current_cpu().id); - if let IpiInnerMsg::MediatedMsg(mediated_msg) = &msg.ipi_message { - let src_id = mediated_msg.src_id; - let vm = vm(src_id).unwrap(); + if let IpiInnerMsg::MediatedMsg(mediated_msg) = msg.ipi_message { // generate IO request in `virtio_blk_notify_handler` - virtio_blk_notify_handler(mediated_msg.vq.clone(), mediated_msg.blk.clone(), vm); + virtio_blk_notify_handler(mediated_msg.vq, mediated_msg.blk, mediated_msg.src_vm); // mark the ipi task as finish (pop it from the ipi queue) finish_async_task(true); // invoke the executor to do IO request diff --git a/src/device/virtio/mmio.rs b/src/device/virtio/mmio.rs index 25ac2544a4d697d56abb9ba7692fe17d735e6d14..a81158cf26cc5785a91ed574dbdd01fb1f11851b 100644 --- a/src/device/virtio/mmio.rs +++ b/src/device/virtio/mmio.rs @@ -7,20 +7,19 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. - -use alloc::sync::Arc; +use alloc::sync::{Arc, Weak}; use alloc::vec::Vec; use spin::Mutex; use crate::config::VmEmulatedDeviceConfig; use crate::device::{ EmuContext, virtio_blk_notify_handler, virtio_console_notify_handler, virtio_mediated_blk_notify_handler, - virtio_net_handle_ctrl, virtio_net_notify_handler, + virtio_net_handle_ctrl, virtio_net_notify_handler, EmuDev, EmuDeviceType, }; -use crate::device::{EmuDevs, VirtioDeviceType}; -use crate::device::{VirtioQueue, Virtq, VirtqData}; +use crate::device::VirtioDeviceType; +use crate::device::Virtq; use crate::device::{VIRTQUEUE_BLK_MAX_SIZE, VIRTQUEUE_CONSOLE_MAX_SIZE, VIRTQUEUE_NET_MAX_SIZE}; -use crate::device::{VirtDev, VirtDevData}; +use crate::device::VirtDev; use crate::device::VIRTQ_READY; use crate::kernel::{current_cpu, ipi_send_msg, IpiInnerMsg, IpiIntInjectMsg, IpiType, vm_ipa2pa}; use crate::kernel::{active_vm, active_vm_id}; @@ -75,17 +74,6 @@ pub struct VirtMmioRegs { dev_stat: u32, } -/// Represents the data structure for Virtio MMIO device. -pub struct VirtioMmioData { - pub id: usize, - pub driver_features: usize, - pub driver_status: usize, - pub regs: VirtMmioRegs, - pub dev: VirtDevData, - pub oppo_dev: VirtDevData, - pub vq: [VirtqData; 4], // TODO: 4 is hard code for vq max len -} - impl VirtMmioRegs { /// Creates a default instance of VirtMmioRegs. pub fn default() -> VirtMmioRegs { @@ -118,132 +106,147 @@ impl VirtMmioRegs { } } +/// Represents a Virtio MMIO device const value part which will be unmuttable after initialaztion. +struct VirtioInnerConst { + base: usize, + length: usize, + emu_type: EmuDeviceType, + vq: Vec>, + dev: VirtDev, + vm: Weak, +} + /// Represents a Virtio MMIO device. -#[derive(Clone)] pub struct VirtioMmio { - inner: Arc>, + inner_const: VirtioInnerConst, + inner: Mutex, } -impl VirtioQueue for VirtioMmio { - /// Initializes the Virtio queue based on the specified VirtioDeviceType. - fn virtio_queue_init(&self, dev_type: VirtioDeviceType) { +impl VirtioMmio { + /// Creates a new instance of VirtioMmio with the specified ID. + pub fn new(vm: Weak, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig) -> Self { + Self { + inner_const: VirtioInnerConst { + base: config.base_ipa, + length: config.length, + emu_type: config.emu_type, + vq: vec![], + dev: VirtDev::new(dev_type, config), + vm, + }, + inner: Mutex::new(VirtioMmioInnerMut::new()), + } + } + + // Initializes the Virtio queue based on the specified VirtioDeviceType. + fn init(&self, dev_type: VirtioDeviceType) { + let mut inner = self.inner.lock(); + inner.regs.init(dev_type); + } + + /// Sets the maximum queue number (Q_NUM_MAX) for the Virtio MMIO device. + pub fn set_q_num_max(&self, q_num_max: u32) { + let mut inner = self.inner.lock(); + inner.regs.q_num_max = q_num_max; + } + + fn virtio_queue_init(&mut self, weak: &Weak, dev_type: VirtioDeviceType) { match dev_type { VirtioDeviceType::Block => { self.set_q_num_max(VIRTQUEUE_BLK_MAX_SIZE as u32); - let mut inner = self.inner.lock(); - let queue = Virtq::default(); - queue.reset(0); - if inner.dev.mediated() { - queue.set_notify_handler(virtio_mediated_blk_notify_handler); + let queue = if self.inner_const.dev.mediated() { + Virtq::new(0, weak.clone(), virtio_mediated_blk_notify_handler) } else { - queue.set_notify_handler(virtio_blk_notify_handler); - } - inner.vq.push(queue); + Virtq::new(0, weak.clone(), virtio_blk_notify_handler) + }; + self.inner_const.vq.push(queue); } VirtioDeviceType::Net => { self.set_q_num_max(VIRTQUEUE_NET_MAX_SIZE as u32); - let mut inner = self.inner.lock(); - // Not support feature VIRTIO_NET_F_CTRL_VQ (no control queue) + // Create two queues for the VirtioNet data device. for i in 0..2 { - let queue = Virtq::default(); - queue.reset(i); - queue.set_notify_handler(virtio_net_notify_handler); - inner.vq.push(queue); + let queue = Virtq::new(i, weak.clone(), virtio_net_notify_handler); + self.inner_const.vq.push(queue); } - inner.vq.push(Virtq::default()); - inner.vq[2].reset(2); - inner.vq[2].set_notify_handler(virtio_net_handle_ctrl); + // Create a queue for the VirtioNet control device. + let queue = Virtq::new(2, weak.clone(), virtio_net_handle_ctrl); + self.inner_const.vq.push(queue); } VirtioDeviceType::Console => { self.set_q_num_max(VIRTQUEUE_CONSOLE_MAX_SIZE as u32); - let mut inner = self.inner.lock(); for i in 0..4 { - let queue = Virtq::default(); - queue.reset(i); - queue.set_notify_handler(virtio_console_notify_handler); - inner.vq.push(queue); + let queue = Virtq::new(i, weak.clone(), virtio_console_notify_handler); + self.inner_const.vq.push(queue); } } - VirtioDeviceType::None => { - panic!("virtio_queue_init: unknown emulated device type"); + _ => { + panic!("virtio_queue_init: unknown dev_type"); } } } - /// Resets the Virtio queue at the specified index. - fn virtio_queue_reset(&self, index: usize) { - let inner = self.inner.lock(); - inner.vq[index].reset(index); - } -} - -impl VirtioMmio { - /// Creates a new instance of VirtioMmio with the specified ID. - pub fn new(id: usize) -> VirtioMmio { - VirtioMmio { - inner: Arc::new(Mutex::new(VirtioMmioInner::new(id))), - } + pub fn upper_vm(&self) -> Option> { + self.inner_const.vm.upgrade() } /// Notifies the specified VM about the configuration changes. - pub fn notify_config(&self, vm: Vm) { + pub fn notify_config(&self) { let mut inner = self.inner.lock(); inner.regs.irt_stat |= VIRTIO_MMIO_INT_CONFIG; - let int_id = inner.dev.int_id(); - let trgt_id = vm.vcpu(0).unwrap().phys_id(); drop(inner); + let vm = self.upper_vm().unwrap(); + let int_id = self.inner_const.dev.int_id(); + let target_vcpu = vm.vcpu(0).unwrap(); use crate::kernel::interrupt_vm_inject; - if trgt_id == current_cpu().id { - interrupt_vm_inject(vm.clone(), vm.vcpu(0).unwrap(), int_id); + if target_vcpu.phys_id() == current_cpu().id { + interrupt_vm_inject(&vm, target_vcpu, int_id); } else { let m = IpiIntInjectMsg { vm_id: vm.id(), int_id }; - if !ipi_send_msg(trgt_id, IpiType::IpiTIntInject, IpiInnerMsg::IntInjectMsg(m)) { - error!("notify_config: failed to send ipi to Core {}", trgt_id); + if !ipi_send_msg( + target_vcpu.phys_id(), + IpiType::IpiTIntInject, + IpiInnerMsg::IntInjectMsg(m), + ) { + error!("notify_config: failed to send ipi to Core {}", target_vcpu.phys_id()); } } } /// Notifies the specified VM. - pub fn notify(&self, vm: Vm) { + pub fn notify(&self) { let mut inner = self.inner.lock(); inner.regs.irt_stat |= VIRTIO_MMIO_INT_VRING; - let int_id = inner.dev.int_id(); - let trgt_id = vm.vcpu(0).unwrap().phys_id(); drop(inner); + let vm = self.upper_vm().unwrap(); + let int_id = self.inner_const.dev.int_id(); + let target_vcpu = vm.vcpu(0).unwrap(); use crate::kernel::interrupt_vm_inject; - if trgt_id == current_cpu().id { - interrupt_vm_inject(vm.clone(), vm.vcpu(0).unwrap(), int_id); + if target_vcpu.phys_id() == current_cpu().id { + interrupt_vm_inject(&vm, target_vcpu, int_id); } else { let m = IpiIntInjectMsg { vm_id: vm.id(), int_id }; - if !ipi_send_msg(trgt_id, IpiType::IpiTIntInject, IpiInnerMsg::IntInjectMsg(m)) { - error!("notify_config: failed to send ipi to Core {}", trgt_id); + if !ipi_send_msg( + target_vcpu.phys_id(), + IpiType::IpiTIntInject, + IpiInnerMsg::IntInjectMsg(m), + ) { + error!("notify_config: failed to send ipi to Core {}", target_vcpu.phys_id()); } } } - /// Initializes the MMIO registers based on the specified VirtioDeviceType. - pub fn mmio_reg_init(&self, dev_type: VirtioDeviceType) { - let mut inner = self.inner.lock(); - inner.reg_init(dev_type); - } - - /// Initializes the Virtio device based on the specified parameters. - pub fn dev_init(&self, dev_type: VirtioDeviceType, config: &VmEmulatedDeviceConfig, mediated: bool) { - let inner = self.inner.lock(); - inner.dev.init(dev_type, config, mediated) - } - - // virtio_dev_reset + /// virtio_dev_reset pub fn dev_reset(&self) { let mut inner = self.inner.lock(); inner.regs.dev_stat = 0; inner.regs.irt_stat = 0; let idx = inner.regs.q_sel as usize; - inner.vq[idx].set_ready(0); - for (idx, virtq) in inner.vq.iter().enumerate() { - virtq.reset(idx); + let vq = &self.inner_const.vq; + vq[idx].set_ready(0); + for virtq in vq.iter() { + virtq.reset(); } - inner.dev.set_activated(false); + self.dev().set_activated(false); } /// Sets the Interrupt Status (IRT_STAT) for the Virtio MMIO device. @@ -270,12 +273,6 @@ impl VirtioMmio { inner.regs.dev_stat = dev_stat; } - /// Sets the maximum queue number (Q_NUM_MAX) for the Virtio MMIO device. - pub fn set_q_num_max(&self, q_num_max: u32) { - let mut inner = self.inner.lock(); - inner.regs.q_num_max = q_num_max; - } - /// Sets the Device Features (DEV_FEATURE) for the Virtio MMIO device. pub fn set_dev_feature(&self, dev_feature: u32) { let mut inner = self.inner.lock(); @@ -306,10 +303,9 @@ impl VirtioMmio { inner.driver_features |= driver_features; } - /// Retrieves a clone of the VirtioDev associated with the Virtio MMIO device. - pub fn dev(&self) -> VirtDev { - let inner = self.inner.lock(); - inner.dev.clone() + /// Retrieves a reference of the VirtioDev associated with the Virtio MMIO device. + pub fn dev(&self) -> &VirtDev { + &self.inner_const.dev } /// Retrieves the selected queue index (Q_SEL) for the Virtio MMIO device. @@ -373,96 +369,60 @@ impl VirtioMmio { /// Retrieves a clone of the Virtq associated with the specified index, wrapped in a Result. /// Returns an Err variant if the index is out of bounds. - pub fn vq(&self, idx: usize) -> Result { - let inner = self.inner.lock(); - if idx >= inner.vq.len() { - return Err(()); + pub fn vq(&self, idx: usize) -> Result<&Virtq, ()> { + match self.inner_const.vq.get(idx) { + Some(vq) => Ok(vq), + None => Err(()), } - Ok(inner.vq[idx].clone()) } - /// Retrieves the ID of the Virtio MMIO device. - pub fn id(&self) -> usize { - let inner = self.inner.lock(); - inner.id - } - - /// Calls the notify handler for the Virtq associated with the specified index. - /// Returns true if the operation is successful, otherwise false. - pub fn notify_handler(&self, idx: usize) -> bool { - let inner = self.inner.lock(); - if idx >= inner.vq.len() { - return false; - } - let vq = inner.vq[idx].clone(); - drop(inner); - vq.call_notify_handler(self.clone()) + #[inline] + /// Retrieves the Base of the Virtio MMIO device. + pub fn base(&self) -> usize { + self.inner_const.base } } -/// Represents the inner data structure of VirtioMmio. -struct VirtioMmioInner { - id: usize, +/// Represents the inner mutable data structure of VirtioMmio. +struct VirtioMmioInnerMut { driver_features: usize, driver_status: usize, regs: VirtMmioRegs, - dev: VirtDev, - - vq: Vec, } -impl VirtioMmioInner { - /// Creates a new `VirtioMmioInner` instance with the given ID. - fn new(id: usize) -> VirtioMmioInner { - VirtioMmioInner { - id, +impl VirtioMmioInnerMut { + fn new() -> VirtioMmioInnerMut { + VirtioMmioInnerMut { driver_features: 0, driver_status: 0, regs: VirtMmioRegs::default(), - dev: VirtDev::default(), - vq: Vec::new(), } } - - /// Initializes the registers of the `VirtioMmioInner` with the specified device type. - fn reg_init(&mut self, dev_type: VirtioDeviceType) { - self.regs.init(dev_type); - } } /// Handles prologue access to Virtio MMIO registers. -fn virtio_mmio_prologue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { +fn virtio_mmio_prologue_access(mmio: &VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { if !write { - let value; - match offset { - VIRTIO_MMIO_MAGIC_VALUE => { - value = mmio.magic(); - } - VIRTIO_MMIO_VERSION => { - value = mmio.version(); - } - VIRTIO_MMIO_DEVICE_ID => { - value = mmio.device_id(); - } - VIRTIO_MMIO_VENDOR_ID => { - value = mmio.vendor_id(); - } + let value = match offset { + VIRTIO_MMIO_MAGIC_VALUE => mmio.magic(), + VIRTIO_MMIO_VERSION => mmio.version(), + VIRTIO_MMIO_DEVICE_ID => mmio.device_id(), + VIRTIO_MMIO_VENDOR_ID => mmio.vendor_id(), VIRTIO_MMIO_HOST_FEATURES => { - if mmio.dev_feature_sel() != 0 { - value = (mmio.dev().features() >> 32) as u32; + let value = if mmio.dev_feature_sel() != 0 { + (mmio.dev().features() >> 32) as u32 } else { - value = mmio.dev().features() as u32; - } + mmio.dev().features() as u32 + }; mmio.set_dev_feature(value); + value } - VIRTIO_MMIO_STATUS => { - value = mmio.dev_stat(); - } + VIRTIO_MMIO_STATUS => mmio.dev_stat(), _ => { error!("virtio_be_init_handler wrong reg_read, address=0x{:x}", emu_ctx.address); return; } - } + }; let idx = emu_ctx.reg; let val = value as usize; current_cpu().set_gpr(idx, val); @@ -488,10 +448,10 @@ fn virtio_mmio_prologue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: u mmio.set_dev_stat(value); if mmio.dev_stat() == 0 { mmio.dev_reset(); - info!("VM {} virtio device {} is reset", active_vm_id(), mmio.id()); + info!("VM {} virtio device {:#x} is reset", active_vm_id(), mmio.base()); } else if mmio.dev_stat() == 0xf { mmio.dev().set_activated(true); - info!("VM {} virtio device {} init ok", active_vm_id(), mmio.id()); + info!("VM {} virtio device {:#x} init ok", active_vm_id(), mmio.base()); } } _ => { @@ -502,7 +462,7 @@ fn virtio_mmio_prologue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: u } /// Handles queue access to Virtio MMIO registers. -fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { +fn virtio_mmio_queue_access(mmio: &VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { if !write { let value; match offset { @@ -558,16 +518,16 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz virtq.set_ready(value); if value == VIRTQ_READY { info!( - "VM {} virtio device {} queue {} ready", + "VM {} virtio device {:#x} queue {:#x} ready", active_vm_id(), - mmio.id(), + mmio.base(), q_sel ); } else { warn!( - "VM {} virtio device {} queue {} init failed", + "VM {} virtio device {:#x} queue {:#x} init failed", active_vm_id(), - mmio.id(), + mmio.base(), q_sel ); } @@ -593,7 +553,7 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz VIRTIO_MMIO_QUEUE_DESC_HIGH => match mmio.vq(q_sel) { Ok(virtq) => { virtq.or_desc_table_addr(value << 32); - let desc_table_addr = vm_ipa2pa(active_vm().unwrap(), virtq.desc_table_addr()); + let desc_table_addr = vm_ipa2pa(&active_vm().unwrap(), virtq.desc_table_addr()); if desc_table_addr == 0 { error!("virtio_mmio_queue_access: invalid desc_table_addr"); return; @@ -626,7 +586,7 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz VIRTIO_MMIO_QUEUE_AVAIL_HIGH => match mmio.vq(q_sel) { Ok(virtq) => { virtq.or_avail_addr(value << 32); - let avail_addr = vm_ipa2pa(active_vm().unwrap(), virtq.avail_addr()); + let avail_addr = vm_ipa2pa(&active_vm().unwrap(), virtq.avail_addr()); if avail_addr == 0 { error!("virtio_mmio_queue_access: invalid avail_addr"); return; @@ -659,7 +619,7 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz VIRTIO_MMIO_QUEUE_USED_HIGH => match mmio.vq(q_sel) { Ok(virtq) => { virtq.or_used_addr(value << 32); - let used_addr = vm_ipa2pa(active_vm().unwrap(), virtq.used_addr()); + let used_addr = vm_ipa2pa(&active_vm().unwrap(), virtq.used_addr()); if used_addr == 0 { error!("virtio_mmio_queue_access: invalid used_addr"); return; @@ -686,22 +646,19 @@ fn virtio_mmio_queue_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usiz } /// Handles config space access to Virtio MMIO registers. -fn virtio_mmio_cfg_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { +fn virtio_mmio_cfg_access(mmio: &VirtioMmio, emu_ctx: &EmuContext, offset: usize, write: bool) { if !write { - let value; let width = emu_ctx.width; - match offset { - VIRTIO_MMIO_CONFIG_GENERATION => { - value = mmio.dev().generation(); - } + let value = match offset { + VIRTIO_MMIO_CONFIG_GENERATION => mmio.dev().generation(), VIRTIO_MMIO_CONFIG..=0x1ff => match mmio.dev().desc() { super::DevDesc::BlkDesc(blk_desc) => { // SAFETY: Offset is between VIRTIO_MMIO_CONFIG..VIRTIO_MMIO_REGS_END ,so is valid - value = unsafe { blk_desc.offset_data(offset - VIRTIO_MMIO_CONFIG, width) }; + unsafe { blk_desc.offset_data(offset - VIRTIO_MMIO_CONFIG, width) } } super::DevDesc::NetDesc(net_desc) => { // SAFETY: Offset is between VIRTIO_MMIO_CONFIG..VIRTIO_MMIO_REGS_END ,so is valid - value = unsafe { net_desc.offset_data(offset - VIRTIO_MMIO_CONFIG, width) }; + unsafe { net_desc.offset_data(offset - VIRTIO_MMIO_CONFIG, width) } } _ => { panic!("unknow desc type"); @@ -711,7 +668,7 @@ fn virtio_mmio_cfg_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, error!("virtio_mmio_cfg_access: wrong reg write 0x{:x}", emu_ctx.address); return; } - } + }; let idx = emu_ctx.reg; current_cpu().set_gpr(idx, value); } else { @@ -720,93 +677,80 @@ fn virtio_mmio_cfg_access(mmio: VirtioMmio, emu_ctx: &EmuContext, offset: usize, } /// Initializes the Virtio MMIO device for a virtual machine. -pub fn emu_virtio_mmio_init(vm: Vm, emu_dev_id: usize, mediated: bool) -> bool { - let virt_dev_type: VirtioDeviceType; - let vm_cfg = vm.config(); - let mmio = VirtioMmio::new(emu_dev_id); - match vm_cfg.emulated_device_list()[emu_dev_id].emu_type { - crate::device::EmuDeviceType::EmuDeviceTVirtioBlk => { - virt_dev_type = VirtioDeviceType::Block; - vm.set_emu_devs(emu_dev_id, EmuDevs::VirtioBlk(mmio.clone())); - } - crate::device::EmuDeviceType::EmuDeviceTVirtioNet => { - virt_dev_type = VirtioDeviceType::Net; - vm.set_emu_devs(emu_dev_id, EmuDevs::VirtioNet(mmio.clone())); - } - crate::device::EmuDeviceType::EmuDeviceTVirtioConsole => { - virt_dev_type = VirtioDeviceType::Console; - vm.set_emu_devs(emu_dev_id, EmuDevs::VirtioConsole(mmio.clone())); - } +pub fn emu_virtio_mmio_init(vm: Weak, emu_cfg: &VmEmulatedDeviceConfig) -> Result, ()> { + let virt_dev_type = match emu_cfg.emu_type { + EmuDeviceType::EmuDeviceTVirtioBlk => VirtioDeviceType::Block, + EmuDeviceType::EmuDeviceTVirtioNet => VirtioDeviceType::Net, + EmuDeviceType::EmuDeviceTVirtioConsole => VirtioDeviceType::Console, _ => { error!("emu_virtio_mmio_init: unknown emulated device type"); - return false; + return Err(()); } - } + }; - mmio.mmio_reg_init(virt_dev_type); - mmio.dev_init(virt_dev_type, &vm_cfg.emulated_device_list()[emu_dev_id], mediated); - // no need to set vm_if_list - mmio.virtio_queue_init(virt_dev_type); + let mmio = Arc::new_cyclic(|weak| { + let mut mmio = VirtioMmio::new(vm, virt_dev_type, emu_cfg); + mmio.init(virt_dev_type); + mmio.virtio_queue_init(weak, virt_dev_type); + mmio + }); - true + if emu_cfg.emu_type == EmuDeviceType::EmuDeviceTVirtioNet { + let nic = mmio.clone(); + let mac = emu_cfg.cfg_list.iter().take(6).map(|&x| x as u8).collect::>(); + super::mac::set_mac_info(&mac, nic); + } + Ok(mmio) } -/// Handles Virtio MMIO events for the specified emulated device. -pub fn emu_virtio_mmio_handler(emu_dev_id: usize, emu_ctx: &EmuContext) -> bool { - trace!("emu_virtio_mmio_handler active_vcpu addr {:x}", unsafe { - *(¤t_cpu().active_vcpu as *const _ as *const usize) - }); - let vm = match active_vm() { - Some(vm) => vm, - None => { - panic!("emu_virtio_mmio_handler: current vcpu.vm is none"); - } - }; +impl EmuDev for VirtioMmio { + fn emu_type(&self) -> EmuDeviceType { + self.inner_const.emu_type + } - let mmio = match vm.emu_dev(emu_dev_id) { - EmuDevs::VirtioBlk(blk) => blk, - EmuDevs::VirtioNet(net) => net, - EmuDevs::VirtioConsole(console) => console, - _ => { - panic!("emu_virtio_mmio_handler: illegal mmio dev type") - } - }; + fn address_range(&self) -> core::ops::Range { + self.inner_const.base..(self.inner_const.base + self.inner_const.length) + } - let addr = emu_ctx.address; - let offset = addr - vm.config().emulated_device_list()[emu_dev_id].base_ipa; - let write = emu_ctx.write; + /// Handles Virtio MMIO events for the specified emulated device. + fn handler(&self, emu_ctx: &EmuContext) -> bool { + let addr = emu_ctx.address; + let offset = addr - self.base(); + let write = emu_ctx.write; - if offset == VIRTIO_MMIO_QUEUE_NOTIFY && write { - mmio.set_irt_stat(VIRTIO_MMIO_INT_VRING); + if offset == VIRTIO_MMIO_QUEUE_NOTIFY && write { + self.set_irt_stat(VIRTIO_MMIO_INT_VRING); - if !mmio.notify_handler(current_cpu().get_gpr(emu_ctx.reg)) { - error!("Failed to handle virtio mmio request!"); + let idx = current_cpu().get_gpr(emu_ctx.reg); + if !self.inner_const.vq[idx].call_notify_handler() { + error!("Failed to handle virtio mmio request!"); + } + } else if offset == VIRTIO_MMIO_INTERRUPT_STATUS && !write { + let idx = emu_ctx.reg; + let val = self.irt_stat() as usize; + current_cpu().set_gpr(idx, val); + } else if offset == VIRTIO_MMIO_INTERRUPT_ACK && write { + let idx = emu_ctx.reg; + let val = self.irt_stat(); + self.set_irt_stat(val & !(current_cpu().get_gpr(idx) as u32)); + self.set_irt_ack(current_cpu().get_gpr(idx) as u32); + } else if (VIRTIO_MMIO_MAGIC_VALUE..=VIRTIO_MMIO_GUEST_FEATURES_SEL).contains(&offset) + || offset == VIRTIO_MMIO_STATUS + { + virtio_mmio_prologue_access(self, emu_ctx, offset, write); + } else if (VIRTIO_MMIO_QUEUE_SEL..=VIRTIO_MMIO_QUEUE_USED_HIGH).contains(&offset) { + virtio_mmio_queue_access(self, emu_ctx, offset, write); + } else if (VIRTIO_MMIO_CONFIG_GENERATION..=VIRTIO_MMIO_REGS_END).contains(&offset) { + virtio_mmio_cfg_access(self, emu_ctx, offset, write); + } else { + error!( + "emu_virtio_mmio_handler: regs wrong {}, address 0x{:x}, offset 0x{:x}", + if write { "write" } else { "read" }, + addr, + offset + ); + return false; } - } else if offset == VIRTIO_MMIO_INTERRUPT_STATUS && !write { - let idx = emu_ctx.reg; - let val = mmio.irt_stat() as usize; - current_cpu().set_gpr(idx, val); - } else if offset == VIRTIO_MMIO_INTERRUPT_ACK && write { - let idx = emu_ctx.reg; - let val = mmio.irt_stat(); - mmio.set_irt_stat(val & !(current_cpu().get_gpr(idx) as u32)); - mmio.set_irt_ack(current_cpu().get_gpr(idx) as u32); - } else if (VIRTIO_MMIO_MAGIC_VALUE..=VIRTIO_MMIO_GUEST_FEATURES_SEL).contains(&offset) - || offset == VIRTIO_MMIO_STATUS - { - virtio_mmio_prologue_access(mmio, emu_ctx, offset, write); - } else if (VIRTIO_MMIO_QUEUE_SEL..=VIRTIO_MMIO_QUEUE_USED_HIGH).contains(&offset) { - virtio_mmio_queue_access(mmio, emu_ctx, offset, write); - } else if (VIRTIO_MMIO_CONFIG_GENERATION..=VIRTIO_MMIO_REGS_END).contains(&offset) { - virtio_mmio_cfg_access(mmio, emu_ctx, offset, write); - } else { - error!( - "emu_virtio_mmio_handler: regs wrong {}, address 0x{:x}, offset 0x{:x}", - if write { "write" } else { "read" }, - addr, - offset - ); - return false; - } - true + true + } } diff --git a/src/device/virtio/mod.rs b/src/device/virtio/mod.rs index bf870fb0b921a3add461e9a1b5f7d2b94a812fdd..70d948b93fdbf90e42e9f5eba674630fcb619d7e 100644 --- a/src/device/virtio/mod.rs +++ b/src/device/virtio/mod.rs @@ -13,6 +13,7 @@ pub use self::blk::*; pub use self::dev::*; +pub use self::mac::*; pub use self::iov::*; pub use self::mediated::*; pub use self::mmio::*; @@ -24,6 +25,7 @@ mod blk; mod console; mod dev; mod iov; +mod mac; mod mediated; mod mmio; mod net; diff --git a/src/device/virtio/net.rs b/src/device/virtio/net.rs index db0af6a755c9dec274428a0b47b6a504586ed40f..9d6ef745367a224de69e98aaaa5f55c4df40c348 100644 --- a/src/device/virtio/net.rs +++ b/src/device/virtio/net.rs @@ -9,24 +9,18 @@ // See the Mulan PSL v2 for more details. use alloc::sync::Arc; +use alloc::vec::Vec; use core::mem::size_of; use spin::Mutex; -use crate::arch::PAGE_SIZE; -use crate::config::{vm_num, vm_type}; use crate::device::{DevDesc, VirtioMmio, Virtq, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; -use crate::device::EmuDevs; use crate::device::VirtioIov; -use crate::kernel::{ - active_vm, active_vm_id, current_cpu, vm_if_cmp_mac, vm_if_get_cpu_id, vm_if_set_mem_map_bit, vm_ipa2pa, VM_LIST, - VM_STATE_FLAG, -}; +use crate::kernel::{current_cpu, vm_if_get_cpu_id}; use crate::kernel::{ipi_send_msg, IpiEthernetMsg, IpiInnerMsg, IpiType}; use crate::kernel::IpiMessage; -use crate::kernel::vm; use crate::kernel::Vm; -use crate::utils::{round_down, trace}; +use crate::utils::trace; const VIRTIO_NET_OK: u8 = 0; const VIRTIO_NET_ERR: u8 = 1; @@ -82,9 +76,8 @@ struct VirtioNetHdr { } /// A cloneable wrapper for the `NetDescInner` structure. -#[derive(Clone)] pub struct NetDesc { - inner: Arc>, + inner: Mutex, } /// Holds data related to the network device. @@ -94,13 +87,18 @@ pub struct NetDescData { } impl NetDesc { - /// Creates a new `NetDesc` instance with default values. - pub fn default() -> NetDesc { + /// Creates a new `NetDesc` instance with mac values. + pub fn new(mac: &[usize]) -> NetDesc { + let mut desc = NetDescInner::default(); + for (i, item) in mac.iter().enumerate().take(6) { + desc.mac[i] = *item as u8; + } NetDesc { - inner: Arc::new(Mutex::new(NetDescInner::default())), + inner: Mutex::new(desc), } } + /// Set the status of the network device. pub fn set_status(&self, status: u16) { let mut inner = self.inner.lock(); inner.status = status; @@ -112,17 +110,6 @@ impl NetDesc { inner.status } - /// Initializes the configuration of the network device with the provided MAC address. - pub fn cfg_init(&self, mac: &[usize]) { - let mut inner = self.inner.lock(); - inner.mac[0] = mac[0] as u8; - inner.mac[1] = mac[1] as u8; - inner.mac[2] = mac[2] as u8; - inner.mac[3] = mac[3] as u8; - inner.mac[4] = mac[4] as u8; - inner.mac[5] = mac[5] as u8; - } - /// Computes the offset data within the `NetDesc` structure. /// # SAFETY: /// Caller must ensure offset is valid @@ -130,6 +117,7 @@ impl NetDesc { pub unsafe fn offset_data(&self, offset: usize, width: usize) -> usize { let inner = self.inner.lock(); let start_addr = &inner.mac[0] as *const _ as usize; + // TODO: Some out-of-bounds accesses beyond offset 6 May return incorrect values match width { 1 => unsafe { *((start_addr + offset) as *const u8) as usize }, 2 => unsafe { *((start_addr + offset) as *const u16) as usize }, @@ -195,23 +183,20 @@ const VIRTIO_NET_CTRL_ANNOUNCE: u8 = 3; const VIRTIO_NET_CTRL_ANNOUNCE_ACK: u8 = 0; /// Handles VirtioNet control operations. -pub fn virtio_net_handle_ctrl(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { +pub fn virtio_net_handle_ctrl(vq: Arc, nic: Arc, vm: Arc) -> bool { if vq.ready() == 0 { error!("virtio net control queue is not ready!"); return false; } - let out_iov = VirtioIov::default(); - let in_iov = VirtioIov::default(); - let mut next_desc_idx_opt = vq.pop_avail_desc_idx(vq.avail_idx()); - while next_desc_idx_opt.is_some() { - let mut idx = next_desc_idx_opt.unwrap() as usize; + while let Some(next_desc_idx) = vq.pop_avail_desc_idx(vq.avail_idx()) { + let mut idx = next_desc_idx as usize; let mut len = 0; - out_iov.clear(); - in_iov.clear(); + let mut out_iov = VirtioIov::default(); + let mut in_iov = VirtioIov::default(); loop { - let addr = vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)); + let addr = vm.ipa2pa(vq.desc_addr(idx)); if addr == 0 { error!("virtio_net_handle_ctrl: failed to desc addr"); return false; @@ -252,51 +237,35 @@ pub fn virtio_net_handle_ctrl(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { } // update ctrl queue used ring - if vm.id() != 0 { - let used_addr = vm_ipa2pa(vm.clone(), vq.used_addr()); - if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { - debug!("vm1 virtio net ctrl write memory in 0x{:x}", used_addr); - } - vm_if_set_mem_map_bit(vm.clone(), used_addr); - - for idx in 0..in_iov.num() { - vm_if_set_mem_map_bit(vm.clone(), in_iov.get_buf(idx)); - } - } - if !vq.update_used_ring(len as u32, next_desc_idx_opt.unwrap() as u32) { + if !vq.update_used_ring(len as u32, next_desc_idx as u32) { return false; } - next_desc_idx_opt = vq.pop_avail_desc_idx(vq.avail_idx()); } - nic.notify(vm); + nic.notify(); true } /// Handles the notification from the VirtioNet device to the specified virtual queue (`vq`) in a virtual machine (`vm`). /// Returns `true` if the notification is successfully processed; otherwise, returns `false`. -pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { +pub fn virtio_net_notify_handler(vq: Arc, nic: Arc, vm: Arc) -> bool { if vq.ready() == 0 { error!("net virt_queue is not ready!"); return false; } if vq.vq_indx() != 1 { - // println!("net rx queue notified!"); return true; } - let tx_iov = VirtioIov::default(); - let mut vms_to_notify = 0; + let mut nics_to_notify = vec![]; - let mut next_desc_idx_opt = vq.pop_avail_desc_idx(vq.avail_idx()); - - while next_desc_idx_opt.is_some() { - let mut idx = next_desc_idx_opt.unwrap() as usize; + while let Some(head_idx) = vq.pop_avail_desc_idx(vq.avail_idx()) { + let mut idx = head_idx as usize; let mut len = 0; - tx_iov.clear(); + let mut tx_iov = VirtioIov::default(); loop { - let addr = vm_ipa2pa(active_vm().unwrap(), vq.desc_addr(idx)); + let addr = vm.ipa2pa(vq.desc_addr(idx)); if addr == 0 { error!("virtio_net_notify_handler: failed to desc addr"); return false; @@ -310,27 +279,13 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { idx = vq.desc_next(idx) as usize; } - let trgt_vmid_map = ethernet_transmit(tx_iov.clone(), len).1; - if trgt_vmid_map != 0 { - vms_to_notify |= trgt_vmid_map; + if let Some(list) = ethernet_transmit(tx_iov, len, &vm) { + nics_to_notify.extend(list); } - if vm.id() != 0 { - let used_addr = vm_ipa2pa(vm.clone(), vq.used_addr()); - if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { - debug!("vm1 virtio net write memory in 0x{:x}", used_addr); - } - vm_if_set_mem_map_bit(vm.clone(), used_addr); - vm_if_set_mem_map_bit(vm.clone(), used_addr + PAGE_SIZE); - } - if !vq.update_used_ring( - (len - size_of::()) as u32, - next_desc_idx_opt.unwrap() as u32, - ) { + if !vq.update_used_ring((len - size_of::()) as u32, head_idx as u32) { return false; } - - next_desc_idx_opt = vq.pop_avail_desc_idx(vq.avail_idx()); } if !vq.avail_is_avail() { @@ -338,90 +293,51 @@ pub fn virtio_net_notify_handler(vq: Virtq, nic: VirtioMmio, vm: Vm) -> bool { return false; } - nic.notify(vm); - // vq.notify(dev.int_id(), vm.clone()); - let mut trgt_vmid = 0; - while vms_to_notify > 0 { - if vms_to_notify & 1 != 0 { - let vm = match crate::kernel::vm(trgt_vmid) { - None => { + nic.notify(); + for nic in nics_to_notify { + let trgt_vm = nic.upper_vm().unwrap(); + let vcpu = trgt_vm.vcpu(0).unwrap(); + // When the target VM is not running on the current CPU, send an IPI message to the target CPU. + if vcpu.phys_id() == current_cpu().id { + let rx_vq = match nic.vq(0) { + Ok(x) => x, + Err(_) => { error!( - "virtio_net_notify_handler: target vm [{}] is not ready or not exist", - trgt_vmid + "virtio_net_notify_handler: vm[{}] failed to get virtio net rx virt queue", + vm.id() ); - return true; + return false; } - Some(_vm) => _vm, }; - let vcpu = vm.vcpu(0).unwrap(); - if vcpu.phys_id() == current_cpu().id { - let nic = match vm.emu_net_dev(0) { - EmuDevs::VirtioNet(x) => x, - _ => { - error!("virtio_net_notify_handler: failed to get virtio net dev"); - return false; - } - }; - let rx_vq = match nic.vq(0) { - Ok(x) => x, - Err(_) => { - error!( - "virtio_net_notify_handler: vm[{}] failed to get virtio net rx virt queue", - vm.id() - ); - return false; - } - }; - if rx_vq.ready() != 0 && rx_vq.avail_flags() == 0 { - nic.notify(vm.clone()); - // rx_vq.notify(nic.dev().int_id(), vm.clone()); - } - } else { - let msg = IpiEthernetMsg { - src_vmid: active_vm_id(), - trgt_vmid, - }; - let cpu_trgt = vm_if_get_cpu_id(trgt_vmid); - if !ipi_send_msg(cpu_trgt, IpiType::IpiTEthernetMsg, IpiInnerMsg::EnternetMsg(msg)) { - error!( - "virtio_net_notify_handler: failed to send ipi message, target {}", - cpu_trgt - ); - } + if rx_vq.ready() != 0 && rx_vq.avail_flags() == 0 { + nic.notify(); } + } else if let Some(cpu_trgt) = vm_if_get_cpu_id(trgt_vm.id()) { + let msg = IpiEthernetMsg { trgt_nic: nic }; + if !ipi_send_msg(cpu_trgt, IpiType::IpiTEthernetMsg, IpiInnerMsg::EnternetMsg(msg)) { + error!( + "virtio_net_notify_handler: failed to send IPI message to CPU {}", + cpu_trgt + ); + return false; + } + } else { + error!( + "virtio_net_notify_handler: failed to get cpu id for vm {}", + trgt_vm.id() + ); + return false; } - - trgt_vmid += 1; - vms_to_notify >>= 1; } true } /// Handles the IPI (Inter-Processor Interrupt) message related to Ethernet. -pub fn ethernet_ipi_rev_handler(msg: &IpiMessage) { +pub fn ethernet_ipi_rev_handler(msg: IpiMessage) { match msg.ipi_message { IpiInnerMsg::EnternetMsg(ethernet_msg) => { - let trgt_vmid = ethernet_msg.trgt_vmid; - let vm = match vm(trgt_vmid) { - None => { - error!( - "ethernet_ipi_rev_handler: target vm [{}] is not ready or not exist", - trgt_vmid - ); - return; - } - Some(_vm) => _vm, - }; - let nic = match vm.emu_net_dev(0) { - EmuDevs::VirtioNet(x) => x, - _ => { - // println!( - // "ethernet_ipi_rev_handler: vm[{}] failed to get virtio net dev", - // vm.id() - // ); - return; - } - }; + let nic = ethernet_msg.trgt_nic; + let vm = nic.upper_vm().unwrap(); let rx_vq = match nic.vq(0) { Ok(x) => x, Err(_) => { @@ -434,8 +350,7 @@ pub fn ethernet_ipi_rev_handler(msg: &IpiMessage) { }; if rx_vq.ready() != 0 && rx_vq.avail_flags() == 0 { - nic.notify(vm); - // rx_vq.notify(nic.dev().int_id(), vm); + nic.notify(); } } _ => { @@ -447,7 +362,7 @@ pub fn ethernet_ipi_rev_handler(msg: &IpiMessage) { /// Transmits Ethernet frames using VirtioNet. /// Returns a tuple with the first element indicating success (`true` if successful) and the second element /// representing the target virtual machine's bitmask. -fn ethernet_transmit(tx_iov: VirtioIov, len: usize) -> (bool, usize) { +fn ethernet_transmit(tx_iov: VirtioIov, len: usize, vm: &Vm) -> Option>> { // [ destination MAC - 6 ][ source MAC - 6 ][ EtherType - 2 ][ Payload ] if len < size_of::() || len - size_of::() < 6 + 6 + 2 { warn!( @@ -455,80 +370,58 @@ fn ethernet_transmit(tx_iov: VirtioIov, len: usize) -> (bool, usize) { len, size_of::() ); - return (false, 0); + return None; } let frame: &[u8] = tx_iov.get_ptr(size_of::()); - // need to check mac - // vm_if_list_cmp_mac(active_vm_id(), frame + 6); - - if frame[0] == 0xff - && frame[1] == 0xff - && frame[2] == 0xff - && frame[3] == 0xff - && frame[4] == 0xff - && frame[5] == 0xff - { - if !ethernet_is_arp(frame) { - return (false, 0); + + if frame[0..6] == [0xff, 0xff, 0xff, 0xff, 0xff, 0xff] { + if ethernet_is_arp(frame) { + return ethernet_broadcast(&tx_iov, len, vm); } - return ethernet_broadcast(tx_iov.clone(), len); + return None; } if frame[0] == 0x33 && frame[1] == 0x33 { if !(frame[12] == 0x86 && frame[13] == 0xdd) { // Only IPV6 multicast packet is allowed to be broadcast - return (false, 0); + return None; } - return ethernet_broadcast(tx_iov.clone(), len); + return ethernet_broadcast(&tx_iov, len, vm); } match ethernet_mac_to_vm_id(frame) { - Ok(vm_id) => (ethernet_send_to(vm_id, tx_iov.clone(), len), 1 << vm_id), - Err(_) => (false, 0), + Ok(nic) => { + let vm = nic.upper_vm().unwrap(); + if ethernet_send_to(&vm, &nic, &tx_iov, len) { + Some(vec![nic]) + } else { + None + } + } + Err(_) => None, } } /// Broadcasts an Ethernet frame to all virtual machines, excluding the current one. -fn ethernet_broadcast(tx_iov: VirtioIov, len: usize) -> (bool, usize) { - let vm_num = vm_num(); - let cur_vm_id = active_vm_id(); - let mut trgt_vmid_map = 0; - for vm_id in 0..vm_num { - if vm_id == cur_vm_id { - continue; - } - if vm_type(vm_id) as usize != 0 { - continue; - } - if !ethernet_send_to(vm_id, tx_iov.clone(), len) { - continue; +fn ethernet_broadcast(tx_iov: &VirtioIov, len: usize, cur_vm: &Vm) -> Option>> { + let mut nic_list = vec![]; + super::mac::virtio_nic_list_walker(|nic| { + let vm = nic.upper_vm().unwrap(); + if vm.id() != cur_vm.id() && ethernet_send_to(&vm, nic, tx_iov, len) { + nic_list.push(nic.clone()); } - trgt_vmid_map |= 1 << vm_id; + }); + if nic_list.is_empty() { + None + } else { + Some(nic_list) } - (trgt_vmid_map != 0, trgt_vmid_map) } /// Sends an Ethernet frame to the specified virtual machine (`vmid`). -fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { - // println!("ethernet send to vm{}", vmid); - let vm = match vm(vmid) { - None => { - // println!("ethernet_send_to: target vm [{}] is not ready or not exist", vmid); - return true; - } - Some(vm) => vm, - }; - let nic = match vm.emu_net_dev(0) { - EmuDevs::VirtioNet(x) => x, - _ => { - // println!("ethernet_send_to: vm[{}] failed to get virtio net dev", vmid); - return true; - } - }; - +fn ethernet_send_to(vm: &Vm, nic: &VirtioMmio, tx_iov: &VirtioIov, len: usize) -> bool { if !nic.dev().activated() { - // println!("ethernet_send_to: vm[{}] nic dev is not activate", vmid); return false; } @@ -548,17 +441,16 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { error!("ethernet_send_to: receive invalid avail desc idx"); return false; } else if desc_header_idx_opt.is_none() { - // println!("ethernet_send_to: desc_header_idx_opt is none"); return false; } let desc_idx_header = desc_header_idx_opt.unwrap(); let mut desc_idx = desc_header_idx_opt.unwrap() as usize; - let rx_iov = VirtioIov::default(); + let mut rx_iov = VirtioIov::default(); let mut rx_len = 0; loop { - let dst = vm_ipa2pa(vm.clone(), rx_vq.desc_addr(desc_idx)); + let dst = vm.ipa2pa(rx_vq.desc_addr(desc_idx)); if dst == 0 { debug!( "rx_vq desc base table addr 0x{:x}, idx {}, avail table addr 0x{:x}, avail last idx {}", @@ -567,21 +459,11 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { rx_vq.avail_addr(), rx_vq.avail_idx() ); - error!("ethernet_send_to: failed to get dst {}", vmid); + error!("ethernet_send_to: failed to get dst {}", vm.id()); return false; } let desc_len = rx_vq.desc_len(desc_idx) as usize; - if vmid != 0 { - let mut addr = round_down(dst, PAGE_SIZE); - if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { - debug!("A: vm0 virtio net write vm1 memory in 0x{:x}", addr); - } - while addr <= round_down(dst + desc_len, PAGE_SIZE) { - vm_if_set_mem_map_bit(vm.clone(), addr); - addr += PAGE_SIZE; - } - } rx_iov.push_data(dst, desc_len); rx_len += desc_len; if rx_len >= len { @@ -604,7 +486,7 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { let header = unsafe { &mut *(tx_iov.get_buf(0) as *mut VirtioNetHdr) }; header.num_buffers = 1; - if tx_iov.write_through_iov(rx_iov.clone(), len) > 0 { + if tx_iov.write_through_iov(&rx_iov, len) > 0 { error!( "ethernet_send_to: write through iov failed, rx_iov_num {} tx_iov_num {} rx_len {} tx_len {}", rx_iov.num(), @@ -615,14 +497,6 @@ fn ethernet_send_to(vmid: usize, tx_iov: VirtioIov, len: usize) -> bool { return false; } - if vmid != 0 { - let used_addr = vm_ipa2pa(vm.clone(), rx_vq.used_addr()); - if VM_STATE_FLAG.load(core::sync::atomic::Ordering::Relaxed) == 1 { - debug!("B: vm0 virtio net write vm1 memory in 0x{:x}", used_addr); - } - vm_if_set_mem_map_bit(vm.clone(), used_addr); - vm_if_set_mem_map_bit(vm, used_addr + PAGE_SIZE); - } if !rx_vq.update_used_ring(len as u32, desc_idx_header as u32) { return false; } @@ -636,23 +510,18 @@ fn ethernet_is_arp(frame: &[u8]) -> bool { } /// Maps the MAC address in the Ethernet frame to the corresponding virtual machine ID. -fn ethernet_mac_to_vm_id(frame: &[u8]) -> Result { - for vm in VM_LIST.lock().iter() { - let vm_id = vm.id(); - if vm_if_cmp_mac(vm_id, frame) { - return Ok(vm_id); - } - } - Err(()) +fn ethernet_mac_to_vm_id(frame: &[u8]) -> Result, ()> { + let frame_mac = &frame[0..6]; + super::mac::mac_to_nic(frame_mac).ok_or(()) } /// Handles the VirtioNet announcement in a virtual machine (`vm`). -pub fn virtio_net_announce(vm: Vm) { - if let EmuDevs::VirtioNet(nic) = vm.emu_net_dev(0) { - if let DevDesc::NetDesc(desc) = nic.dev().desc() { - let status = desc.status(); - desc.set_status(status | VIRTIO_NET_S_ANNOUNCE); - nic.notify_config(vm); +pub fn virtio_net_announce(vm: Arc) { + super::mac::virtio_nic_list_walker(|nic| { + if let Some(nic_vm) = nic.upper_vm() { + if Arc::ptr_eq(&nic_vm, &vm) { + nic.notify_config(); + } } - } + }) } diff --git a/src/device/virtio/queue.rs b/src/device/virtio/queue.rs index ff00db0ec653ee0c8b445d62c1109c8257a583ac..4e16f02934ffeb87ba56696da08cfe0da849eb19 100644 --- a/src/device/virtio/queue.rs +++ b/src/device/virtio/queue.rs @@ -8,13 +8,12 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::sync::Arc; +use alloc::sync::{Arc, Weak}; use core::slice; use spin::Mutex; -use crate::device::VirtioDeviceType; use crate::device::VirtioMmio; use crate::kernel::{active_vm, Vm, vm_ipa2pa}; @@ -99,32 +98,35 @@ pub struct VringUsed { ring: [VringUsedElem; 512], } -/// Represents a VirtIO queue trait with initialization and reset methods. -pub trait VirtioQueue { - /// Initializes the VirtIO queue with the given device type. - fn virtio_queue_init(&self, dev_type: VirtioDeviceType); - /// Resets the VirtIO queue at the specified index. - fn virtio_queue_reset(&self, index: usize); -} - /// A wrapper struct representing a VirtIO queue. -#[derive(Clone)] pub struct Virtq { - inner: Arc>>, + /// The index of the VirtIO queue. + vq_index: usize, + /// Optional function to handle VirtIO queue notifications. + notify_handler: fn(Arc, Arc, Arc) -> bool, + mmio: Weak, + inner: Mutex>, } impl Virtq { - /// Creates a new default Virtq instance. - pub fn default() -> Virtq { - Virtq { - inner: Arc::new(Mutex::new(VirtqInner::default())), - } + /// Creates a new Virtq instance. + pub fn new( + vq_index: usize, + mmio: Weak, + notify_handler: fn(Arc, Arc, Arc) -> bool, + ) -> Arc { + Arc::new(Self { + vq_index, + notify_handler, + mmio, + inner: Mutex::new(VirtqInner::default()), + }) } /// Resets the VirtIO queue at the specified index. - pub fn reset(&self, index: usize) { + pub fn reset(&self) { let mut inner = self.inner.lock(); - inner.reset(index); + inner.reset(); } /// Pops the next available descriptor index from the available ring. @@ -224,34 +226,22 @@ impl Virtq { } } - /// Sets the notify handler function for the VirtIO queue. - pub fn set_notify_handler(&self, handler: fn(Virtq, VirtioMmio, Vm) -> bool) { - let mut inner = self.inner.lock(); - inner.notify_handler = Some(handler); - } - /// Calls the registered notify handler function. - pub fn call_notify_handler(&self, mmio: VirtioMmio) -> bool { - let inner = self.inner.lock(); - match inner.notify_handler { - Some(handler) => { - drop(inner); - handler(self.clone(), mmio, active_vm().unwrap()) - } - None => { - error!("call_notify_handler: virtq notify handler is None"); - false - } + pub fn call_notify_handler(self: &Arc) -> bool { + if let Some(mmio) = self.mmio.upgrade() { + (self.notify_handler)(self.clone(), mmio, active_vm().unwrap()) + } else { + false } } /// Displays information about the descriptors in the VirtIO queue. - pub fn show_desc_info(&self, size: usize, vm: Vm) { + pub fn show_desc_info(&self, size: usize, vm: Arc) { let inner = self.inner.lock(); let desc = inner.desc_table.as_ref().unwrap(); info!("[*desc_ring*]"); for i in 0..size { - let desc_addr = vm_ipa2pa(vm.clone(), desc[i].addr); + let desc_addr = vm_ipa2pa(&vm, desc[i].addr); info!( "index {} desc_addr_ipa 0x{:x} desc_addr_pa 0x{:x} len 0x{:x} flags {} next {}", i, desc[i].addr, desc_addr, desc[i].len, desc[i].flags, desc[i].next @@ -293,7 +283,7 @@ impl Virtq { /// Sets the last used index for the used ring. pub fn set_last_used_idx(&self, last_used_idx: u16) { - let mut inner = self.inner.lock(); + let mut inner: spin::MutexGuard<'_, VirtqInner<'_>> = self.inner.lock(); inner.last_used_idx = last_used_idx; } @@ -413,8 +403,7 @@ impl Virtq { /// Returns the VirtIO queue index. pub fn vq_indx(&self) -> usize { - let inner = self.inner.lock(); - inner.vq_index + self.vq_index } /// Returns the number of descriptors in the VirtIO queue. @@ -483,8 +472,6 @@ impl Virtq { pub struct VirtqInner<'a> { /// The ready state of the VirtIO queue. ready: usize, - /// The index of the VirtIO queue. - vq_index: usize, /// The number of descriptors in the VirtIO queue. num: usize, /// Optional reference to the descriptor table. @@ -506,9 +493,6 @@ pub struct VirtqInner<'a> { avail_addr: usize, /// Guest-physical address of the used ring. used_addr: usize, - - /// Optional function to handle VirtIO queue notifications. - notify_handler: Option bool>, } impl VirtqInner<'_> { @@ -516,7 +500,6 @@ impl VirtqInner<'_> { pub fn default() -> Self { VirtqInner { ready: 0, - vq_index: 0, num: 0, desc_table: None, avail: None, @@ -528,15 +511,12 @@ impl VirtqInner<'_> { desc_table_addr: 0, avail_addr: 0, used_addr: 0, - - notify_handler: None, } } /// Resets the VirtIO queue to its initial state. - pub fn reset(&mut self, index: usize) { + pub fn reset(&mut self) { self.ready = 0; - self.vq_index = index; self.num = 0; self.last_avail_idx = 0; self.last_used_idx = 0; diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 697ae97a1caba743f6347e2ee8005b0ec9127dd7..501f6a43604562f93d24b1684272665d564c7303 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -10,6 +10,7 @@ //! Driver module, including device drivers and board drivers. +#[cfg(target_arch = "aarch64")] pub use uart::putc; #[cfg(feature = "pi4")] diff --git a/src/kernel/async_task.rs b/src/kernel/async_task.rs index 09a2213e865175a247984d6f839852cef14b4f9c..d6c7ef967eee6d28c58c11bc5ad0fa548c205144 100644 --- a/src/kernel/async_task.rs +++ b/src/kernel/async_task.rs @@ -23,7 +23,7 @@ use crate::device::{ BlkIov, mediated_blk_read, mediated_blk_write, virtio_blk_notify_handler, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VirtioMmio, Virtq, }; -use crate::kernel::{active_vm_id, ipi_send_msg, IpiInnerMsg, IpiMediatedMsg, IpiType, vm}; +use crate::kernel::{active_vm_id, ipi_send_msg, IpiInnerMsg, IpiMediatedMsg, IpiType}; use crate::utils::{memcpy, sleep, trace}; #[derive(Clone, Copy, Debug)] @@ -41,8 +41,8 @@ pub struct UsedInfo { #[derive(Clone)] pub struct IoAsyncMsg { pub src_vmid: usize, - pub vq: Virtq, - pub dev: VirtioMmio, + pub vq: Arc, + pub dev: Arc, pub io_type: usize, pub blk_id: usize, pub sector: usize, @@ -53,8 +53,8 @@ pub struct IoAsyncMsg { #[derive(Clone)] pub struct IoIdAsyncMsg { - pub vq: Virtq, - pub dev: VirtioMmio, + pub vq: Arc, + pub dev: Arc, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -261,10 +261,10 @@ pub async fn async_ipi_req() { drop(ipi_list); if let AsyncTaskData::AsyncIpiTask(msg) = task.task_data { if active_vm_id() == 0 { - virtio_blk_notify_handler(msg.vq.clone(), msg.blk.clone(), vm(msg.src_id).unwrap()); + // Here, if the active vm is the service VM, handle the task directly + // Possible cond: share time slice with MVM + virtio_blk_notify_handler(msg.vq, msg.blk, msg.src_vm); } else { - // add_task_ipi_count(); - // send IPI to target cpu, and the target will invoke `mediated_ipi_handler` ipi_send_msg(0, IpiType::IpiTMediatedDev, IpiInnerMsg::MediatedMsg(msg)); } } @@ -449,15 +449,13 @@ pub fn finish_async_task(ipi: bool) { } } - update_used_info(args.vq.clone(), task.src_vmid); - let src_vm = vm(task.src_vmid).unwrap(); - args.dev.notify(src_vm); + update_used_info(args.vq, task.src_vmid); + args.dev.notify(); } AsyncTaskData::AsyncIpiTask(_) => {} AsyncTaskData::AsyncNoneTask(args) => { - update_used_info(args.vq.clone(), task.src_vmid); - let src_vm = vm(task.src_vmid).unwrap(); - args.dev.notify(src_vm); + update_used_info(args.vq, task.src_vmid); + args.dev.notify(); } } } @@ -477,16 +475,12 @@ pub fn push_used_info(desc_chain_head_idx: u32, used_len: u32, src_vmid: usize) } } -fn update_used_info(vq: Virtq, src_vmid: usize) { +fn update_used_info(vq: Arc, src_vmid: usize) { let mut used_info_list = ASYNC_USED_INFO_LIST.lock(); match used_info_list.get_mut(&src_vmid) { Some(info_list) => { - // for info in info_list.iter() { - // vq.update_used_ring(info.used_len, info.desc_chain_head_idx, vq_size); let info = info_list.pop_front().unwrap(); vq.update_used_ring(info.used_len, info.desc_chain_head_idx); - // } - // info_list.clear(); } None => { error!("async_push_used_info: src_vmid {} not existed", src_vmid); diff --git a/src/kernel/cpu.rs b/src/kernel/cpu.rs index 99f4c9ae6baebd645533f13989f20fa6be0ea0ac..510a723b4ee5c25479fa01544433a427f650e8bb 100644 --- a/src/kernel/cpu.rs +++ b/src/kernel/cpu.rs @@ -13,10 +13,8 @@ use core::ptr; use spin::Mutex; -use crate::arch::{PAGE_SIZE, set_current_cpu}; - +use crate::arch::{is_boot_core, set_current_cpu, Arch, ArchTrait, PAGE_SIZE}; use crate::arch::ContextFrame; -use crate::arch::{wfi, isb}; use crate::arch::ContextFrameTrait; // use core::ops::{Deref, DerefMut}; use crate::arch::{cpu_interrupt_unmask, current_cpu_arch}; @@ -27,7 +25,11 @@ use crate::utils::trace; pub const CPU_MASTER: usize = 0; pub const CPU_STACK_SIZE: usize = PAGE_SIZE * 128; +#[cfg(target_arch = "aarch64")] pub const CONTEXT_GPR_NUM: usize = 31; +#[cfg(target_arch = "riscv64")] +// Including x0-x31,totally 32 registers +pub const CONTEXT_GPR_NUM: usize = 32; pub const CPU_STACK_OFFSET: usize = offset_of!(Cpu, stack); #[derive(Copy, Clone, Debug, Eq)] @@ -55,6 +57,7 @@ pub enum StartReason { pub struct CpuIf { pub msg_queue: Vec, pub entry: u64, + // a1 stored value, also known as opache pub ctx: u64, pub vm_id: usize, pub state_for_start: CpuState, @@ -180,10 +183,6 @@ impl Cpu { self.ctx().unwrap().exception_pc() } - pub fn get_spsr(&self) -> usize { - self.ctx().unwrap().spsr as usize - } - pub fn set_elr(&self, val: usize) { self.ctx_mut().unwrap().set_exception_pc(val) } @@ -202,32 +201,21 @@ impl Cpu { /// schedule a vcpu to run on this physical cpu pub fn schedule_to(&mut self, next_vcpu: Vcpu) { if let Some(prev_vcpu) = &self.active_vcpu { - if prev_vcpu.vm_id() != next_vcpu.vm_id() { - // println!( - // "next vm{} vcpu {}, prev vm{} vcpu {}", - // next_vcpu.vm_id(), - // next_vcpu.id(), - // prev_vcpu.vm_id(), - // prev_vcpu.id() - // ); - prev_vcpu.set_state(VcpuState::Ready); - prev_vcpu.context_vm_store(); - } + // On RISC-V, only one VM goes into this func also, since risc-v + // depends on traping to hypervisor to inject timer interrupt + // TODO: Use Sstc Extension to allow VS to receive its own timer interrupt without traping into hypervisor + + // This way, even when vm doesn't change, we should save prev_cpu's state + prev_vcpu.set_state(VcpuState::Ready); + prev_vcpu.context_vm_store(); } // NOTE: Must set active first and then restore context!!! // because context restore while inject pending interrupt for VM // and will judge if current active vcpu self.set_active_vcpu(Some(next_vcpu.clone())); next_vcpu.context_vm_restore(); - // restore vm's Stage2 MMU context - let vttbr = (next_vcpu.vm_id() << 48) | next_vcpu.vm_pt_dir(); - // println!("vttbr {:#x}", vttbr); - // TODO: replace the arch related expr - // SAFETY: 'vttbr' is saved in the vcpu struct when last scheduled - unsafe { - core::arch::asm!("msr VTTBR_EL2, {0}", in(reg) vttbr); - isb(); - } + + Arch::install_vm_page_table(next_vcpu.vm_pt_dir(), next_vcpu.vm_id()); } /// get this cpu's scheduler @@ -265,8 +253,8 @@ pub fn active_vm_id() -> usize { vm.id() } -pub fn active_vm() -> Option { - match current_cpu().active_vcpu.clone() { +pub fn active_vm() -> Option> { + match current_cpu().active_vcpu.as_ref() { None => None, Some(active_vcpu) => active_vcpu.vm(), } @@ -282,7 +270,7 @@ pub fn active_vm_ncpu() -> usize { /// initialize the CPU pub fn cpu_init() { let cpu_id = current_cpu().id; - if cpu_id == 0 { + if is_boot_core(cpu_id) { cpu_if_init(); if cfg!(not(feature = "secondary_start")) { Platform::power_on_secondary_cores(); @@ -295,15 +283,15 @@ pub fn cpu_init() { let size = core::mem::size_of::(); // SAFETY: Sp is valid when boot_stage setting unsafe { + // The space of the ContextFrame size at the top of the CPU stack is used to store the current cpu context current_cpu().set_ctx((sp - size) as *mut _); } - info!("Core {} init ok", cpu_id); if cfg!(not(feature = "secondary_start")) { crate::utils::barrier(); // println!("after barrier cpu init"); use crate::board::PLAT_DESC; - if cpu_id == 0 { + if is_boot_core(cpu_id) { info!("Bring up {} cores", PLAT_DESC.cpu_desc.num); info!("Cpu init ok"); } @@ -316,7 +304,8 @@ pub fn cpu_idle() -> ! { current_cpu().cpu_state = state; cpu_interrupt_unmask(); loop { - wfi(); + info!("[idle] prepare to idle..."); + crate::arch::Arch::wait_for_interrupt(); } } @@ -336,3 +325,8 @@ pub extern "C" fn cpu_map_self(mpidr: usize) { set_current_cpu(cpu as *const _ as u64); } } + +pub fn get_cpu_info_addr(cpu_id: usize) -> u64 { + let cpu = unsafe { &CPU_LIST[cpu_id] }; + cpu as *const _ as u64 +} diff --git a/src/kernel/hvc.rs b/src/kernel/hvc.rs index de60b946df8b807a7c865848bc39de8ad9434263..eba3f6b90a7b693ab7ed467e6c3d1b71cf747699 100644 --- a/src/kernel/hvc.rs +++ b/src/kernel/hvc.rs @@ -85,10 +85,11 @@ pub const HVC_IVC_ACK: usize = 5; pub const HVC_IVC_GET_TIME: usize = 6; pub const HVC_IVC_SHARE_MEM: usize = 7; pub const HVC_IVC_SEND_SHAREMEM: usize = 0x10; -//共享内存通信 +// Share memory communication +// Used for the VM to obtain the shared memory IPA pub const HVC_IVC_GET_SHARED_MEM_IPA: usize = 0x11; -//用于VM获取共享内存IPA -pub const HVC_IVC_SEND_SHAREMEM_TEST_SPEED: usize = 0x12; //共享内存通信速度测试 +// Shared memory communication speed test +pub const HVC_IVC_SEND_SHAREMEM_TEST_SPEED: usize = 0x12; // hvc_mediated_event pub const HVC_MEDIATED_DEV_APPEND: usize = 0x30; @@ -128,8 +129,10 @@ pub const HVC_CONFIG_UPLOAD_DEVICE_TREE: usize = 11; pub const HVC_IRQ: usize = 32 + 0x20; #[cfg(feature = "pi4")] pub const HVC_IRQ: usize = 32 + 0x10; -#[cfg(feature = "qemu")] +#[cfg(all(feature = "qemu", target_arch = "aarch64"))] pub const HVC_IRQ: usize = 32 + 0x20; +#[cfg(all(feature = "qemu", target_arch = "riscv64"))] +pub const HVC_IRQ: usize = 51; #[cfg(feature = "rk3588")] pub const HVC_IRQ: usize = 32 + 0x10; @@ -207,6 +210,10 @@ pub fn hvc_guest_handler( x5: usize, x6: usize, ) -> Result { + // info!("hvc_guest_handler: hvc_type {} event {} x0: \n{}", + // hvc_type, event, + // current_cpu().ctx().unwrap() + // ); match hvc_type { HVC_SYS => hvc_sys_handler(event, x0), HVC_VMM => hvc_vmm_handler(event, x0, x1), @@ -431,23 +438,27 @@ pub fn hvc_send_msg_to_vm(vm_id: usize, guest_msg: &HvcGuestMsg) -> bool { } }; - let cpu_trgt = vm_if_get_cpu_id(vm_id); - if cpu_trgt != current_cpu().id { - let ipi_msg = IpiHvcMsg { - src_vmid: 0, - trgt_vmid: vm_id, - fid, - event, - }; - if !ipi_send_msg(cpu_trgt, IpiType::IpiTHvc, IpiInnerMsg::HvcMsg(ipi_msg)) { - error!( - "hvc_send_msg_to_vm: Failed to send ipi message, target {} type {:#?}", - cpu_trgt, - IpiType::IpiTHvc - ); + if let Some(cpu_trgt) = vm_if_get_cpu_id(vm_id) { + if cpu_trgt != current_cpu().id { + let ipi_msg = IpiHvcMsg { + src_vmid: 0, + trgt_vmid: vm_id, + fid, + event, + }; + if !ipi_send_msg(cpu_trgt, IpiType::IpiTHvc, IpiInnerMsg::HvcMsg(ipi_msg)) { + error!( + "hvc_send_msg_to_vm: Failed to send ipi message, target {} type {:#?}", + cpu_trgt, + IpiType::IpiTHvc + ); + } + } else { + hvc_guest_notify(vm_id); } } else { - hvc_guest_notify(vm_id); + error!("hvc_send_msg_to_vm: Failed to get cpu id of VM {}", vm_id); + return false; } true @@ -466,13 +477,13 @@ pub fn hvc_guest_notify(vm_id: usize) { } Some(vcpu) => { // println!("hvc_guest_notify here"); - interrupt_vm_inject(vm, vcpu, HVC_IRQ); + interrupt_vm_inject(&vm, vcpu, HVC_IRQ); } }; } -pub fn hvc_ipi_handler(msg: &IpiMessage) { - match &msg.ipi_message { +pub fn hvc_ipi_handler(msg: IpiMessage) { + match msg.ipi_message { IpiInnerMsg::HvcMsg(msg) => { if current_cpu().vcpu_array.pop_vcpu_through_vmid(msg.trgt_vmid).is_none() { error!( diff --git a/src/kernel/interrupt.rs b/src/kernel/interrupt.rs index 18c771b7e2f93aa0cdc7265c1dff158c9b1cc19a..36e24dbde90dc89238f362731c60816ebbf58d89 100644 --- a/src/kernel/interrupt.rs +++ b/src/kernel/interrupt.rs @@ -13,7 +13,7 @@ use alloc::collections::BTreeMap; use spin::Mutex; use crate::arch::traits::InterruptController; -use crate::arch::IntCtrl; +use crate::arch::{IntCtrl, is_boot_core}; use crate::kernel::{current_cpu, ipi_irq_handler, IpiInnerMsg, IpiMessage, Vcpu, VcpuState}; use crate::kernel::Vm; use crate::utils::{BitAlloc, BitAlloc256, BitAlloc4K, BitMap}; @@ -47,7 +47,7 @@ pub fn interrupt_init() { IntCtrl::init(); let cpu_id = current_cpu().id; - if cpu_id == 0 { + if is_boot_core(cpu_id) { interrupt_reserve_int(IntCtrl::IRQ_IPI, ipi_irq_handler); info!("Interrupt init ok"); @@ -56,7 +56,7 @@ pub fn interrupt_init() { } /// register a new interrupt for specific vm -pub fn interrupt_vm_register(vm: Vm, id: usize) -> bool { +pub fn interrupt_vm_register(vm: &Vm, id: usize) -> bool { // println!("VM {} register interrupt {}", vm.id(), id); let mut glb_bitmap_lock = INTERRUPT_GLB_BITMAP.lock(); if glb_bitmap_lock.get(id) != 0 && id >= IntCtrl::PRI_NUN_MAX { @@ -64,14 +64,13 @@ pub fn interrupt_vm_register(vm: Vm, id: usize) -> bool { return false; } - IntCtrl::vm_register(vm.clone(), id); - vm.set_int_bit_map(id); + IntCtrl::vm_register(vm, id); glb_bitmap_lock.set(id); true } /// remove interrupt for specific vm -pub fn interrupt_vm_remove(_vm: Vm, id: usize) { +pub fn interrupt_vm_remove(_vm: &Vm, id: usize) { let mut glb_bitmap_lock = INTERRUPT_GLB_BITMAP.lock(); // vgic and vm will be removed with struct vm glb_bitmap_lock.clear(id); @@ -81,7 +80,7 @@ pub fn interrupt_vm_remove(_vm: Vm, id: usize) { } } -pub fn interrupt_vm_inject(vm: Vm, vcpu: Vcpu, int_id: usize) { +pub fn interrupt_vm_inject(vm: &Vm, vcpu: &Vcpu, int_id: usize) { if vcpu.phys_id() != current_cpu().id { error!( "interrupt_vm_inject: Core {} failed to find target (VCPU {} VM {})", @@ -108,7 +107,7 @@ pub fn interrupt_handler(int_id: usize) -> bool { if let Some(vcpu) = ¤t_cpu().active_vcpu { if let Some(active_vm) = vcpu.vm() { if active_vm.has_interrupt(int_id) { - interrupt_vm_inject(active_vm, vcpu.clone(), int_id); + interrupt_vm_inject(&active_vm, vcpu, int_id); return false; } else { return true; @@ -126,7 +125,7 @@ pub fn interrupt_handler(int_id: usize) -> bool { return true; } - interrupt_vm_inject(vm, vcpu.clone(), int_id); + interrupt_vm_inject(&vm, vcpu, int_id); return false; } } @@ -141,8 +140,8 @@ pub fn interrupt_handler(int_id: usize) -> bool { } /// ipi interrupt handler entry -pub fn interrupt_inject_ipi_handler(msg: &IpiMessage) { - match &msg.ipi_message { +pub fn interrupt_inject_ipi_handler(msg: IpiMessage) { + match msg.ipi_message { IpiInnerMsg::IntInjectMsg(int_msg) => { let vm_id = int_msg.vm_id; let int_id = int_msg.int_id; @@ -151,7 +150,7 @@ pub fn interrupt_inject_ipi_handler(msg: &IpiMessage) { panic!("inject int {} to illegal cpu {}", int_id, current_cpu().id); } Some(vcpu) => { - interrupt_vm_inject(vcpu.vm().unwrap(), vcpu, int_id); + interrupt_vm_inject(&vcpu.vm().unwrap(), vcpu, int_id); } } } diff --git a/src/kernel/iommu.rs b/src/kernel/iommu.rs index be0eb1c10927eec1b351b77f866a17b08e1b4d4e..cd07c54ffec220f138da7faa2b74f86af7e90e95 100644 --- a/src/kernel/iommu.rs +++ b/src/kernel/iommu.rs @@ -7,8 +7,11 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use alloc::sync::Arc; use crate::arch::{smmu_add_device, smmu_vm_init}; +use crate::device::EmuDev; +use crate::config::VmEmulatedDeviceConfig; use crate::kernel::Vm; /// init iommu @@ -22,7 +25,7 @@ pub fn iommu_init() { } /// init iommu for vm -pub fn iommmu_vm_init(vm: Vm) -> bool { +pub fn iommmu_vm_init(vm: &Vm) -> bool { if cfg!(feature = "tx2") { smmu_vm_init(vm) } else { @@ -32,7 +35,7 @@ pub fn iommmu_vm_init(vm: Vm) -> bool { } /// add device to iommu -pub fn iommu_add_device(vm: Vm, stream_id: usize) -> bool { +pub fn iommu_add_device(vm: &Vm, stream_id: usize) -> bool { if cfg!(feature = "tx2") { smmu_add_device(vm.iommu_ctx_id(), stream_id) } else { @@ -40,3 +43,12 @@ pub fn iommu_add_device(vm: Vm, stream_id: usize) -> bool { false } } + +/// init emu_iommu for vm +pub fn emu_iommu_init(emu_cfg: &VmEmulatedDeviceConfig) -> Result, ()> { + if cfg!(feature = "tx2") { + crate::arch::emu_smmu_init(emu_cfg) + } else { + Err(()) + } +} diff --git a/src/kernel/ipi.rs b/src/kernel/ipi.rs index d50827089f9ea7dbd931a53737be89c0c8c065f3..6ea2e1020e6ddce09411c8781929e785767974a6 100644 --- a/src/kernel/ipi.rs +++ b/src/kernel/ipi.rs @@ -7,12 +7,13 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use alloc::sync::Arc; use crate::arch::traits::InterruptController; use crate::board::PLAT_DESC; use crate::device::{VirtioMmio, Virtq}; use crate::kernel::{CPU_IF_LIST, current_cpu, interrupt_cpu_ipi_send}; -use crate::vmm::VmmEvent; +use crate::vmm::{VmmEvent, VmmPercoreEvent}; use super::Vm; @@ -67,11 +68,10 @@ pub struct IpiPowerMessage { // pub succeed: bool, // } -#[derive(Copy, Clone)] +#[derive(Clone)] /// Ethernet Message Struct transfered by IPI pub struct IpiEthernetMsg { - pub src_vmid: usize, - pub trgt_vmid: usize, + pub trgt_nic: Arc, } #[derive(Copy, Clone)] @@ -81,22 +81,20 @@ pub struct IpiVmmMsg { pub event: VmmEvent, } -#[derive(Copy, Clone)] +#[derive(Clone)] /// VCPU Message Struct transfered by IPI -pub struct IpiVcpuMsg { - pub vmid: usize, - pub vcpuid: usize, - pub event: VmmEvent, +pub struct IpiVmmPercoreMsg { + pub vm: Arc, + pub event: VmmPercoreEvent, } // only support for mediated blk #[derive(Clone)] /// Mediated Device Message Struct transfered by IPI pub struct IpiMediatedMsg { - pub src_id: usize, - pub vq: Virtq, - pub blk: VirtioMmio, - // pub avail_idx: u16, + pub src_vm: Arc, + pub vq: Arc, + pub blk: Arc, } #[derive(Clone, Copy)] @@ -124,7 +122,7 @@ pub struct IpiIntInjectMsg { declare_enum_with_handler! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(usize)] - pub enum IpiType [pub IPI_HANDLER_LIST => IpiHandlerFunc] { + pub enum IpiType [pub IPI_HANDLER_LIST => fn(IpiMessage)] { IpiTIntc => crate::arch::vgic_ipi_handler, IpiTPower => crate::arch::psci_ipi_handler, IpiTEthernetMsg => crate::device::ethernet_ipi_rev_handler, @@ -142,7 +140,7 @@ pub enum IpiInnerMsg { Power(IpiPowerMessage), EnternetMsg(IpiEthernetMsg), VmmMsg(IpiVmmMsg), - VcpuMsg(IpiVcpuMsg), + VmmPercoreMsg(IpiVmmPercoreMsg), MediatedMsg(IpiMediatedMsg), MediatedNotifyMsg(IpiMediatedNotifyMsg), HvcMsg(IpiHvcMsg), @@ -159,34 +157,22 @@ pub struct IpiMessage { const IPI_HANDLER_MAX: usize = 16; -pub type IpiHandlerFunc = fn(&IpiMessage); - -/// IPI Handler Struct -pub struct IpiHandler { - pub handler: IpiHandlerFunc, - pub ipi_type: IpiType, -} - -impl IpiHandler { - fn new(handler: IpiHandlerFunc, ipi_type: IpiType) -> IpiHandler { - IpiHandler { handler, ipi_type } - } -} - /// ipi handler entry, scanning the received ipi list and call the coresponding handler pub fn ipi_irq_handler() { - // println!("ipi handler"); let cpu_id = current_cpu().id; let mut cpu_if_list = CPU_IF_LIST.lock(); let mut msg: Option = cpu_if_list[cpu_id].pop(); drop(cpu_if_list); + #[cfg(target_arch = "riscv64")] + crate::arch::interrupt::deactivate_soft_intr(); + while msg.is_some() { let ipi_msg = msg.unwrap(); let ipi_type = ipi_msg.ipi_type as usize; if let Some(handler) = IPI_HANDLER_LIST.get(ipi_type) { - handler(&ipi_msg); + handler(ipi_msg); } else { error!("illegal ipi type {}", ipi_type) } @@ -204,7 +190,12 @@ fn ipi_send(target_id: usize, msg: IpiMessage) -> bool { let mut cpu_if_list = CPU_IF_LIST.lock(); cpu_if_list[target_id].msg_queue.push(msg); drop(cpu_if_list); + + #[cfg(target_arch = "aarch64")] crate::arch::dsb::ishst(); + #[cfg(target_arch = "riscv64")] + crate::arch::fence(); + interrupt_cpu_ipi_send(target_id, crate::arch::IntCtrl::IRQ_IPI); true @@ -216,7 +207,7 @@ pub fn ipi_send_msg(target_id: usize, ipi_type: IpiType, ipi_message: IpiInnerMs ipi_send(target_id, msg) } -pub fn ipi_intra_broadcast_msg(vm: Vm, ipi_type: IpiType, msg: IpiInnerMsg) -> bool { +pub fn ipi_intra_broadcast_msg(vm: &Vm, ipi_type: IpiType, msg: IpiInnerMsg) -> bool { let mut i = 0; let mut n = 0; while n < (vm.cpu_num() - 1) { diff --git a/src/kernel/ivc.rs b/src/kernel/ivc.rs index 0cd6d294f7399e9c797cf211126d075eec0c3e6b..f38886db6e1d7a3b6dca5063d424ca9a2b036914 100644 --- a/src/kernel/ivc.rs +++ b/src/kernel/ivc.rs @@ -9,6 +9,8 @@ // See the Mulan PSL v2 for more details. use spin::Mutex; +use alloc::sync::{Weak, Arc}; +use core::ops::Range; use crate::arch::PAGE_SIZE; use crate::arch::PTE_S2_NORMAL; @@ -16,6 +18,7 @@ use crate::kernel::{ active_vm, current_cpu, mem_pages_alloc, Vm, vm_if_set_ivc_arg, vm_if_set_ivc_arg_ptr, vm_ipa2pa, VM_NUM_MAX, }; use crate::mm::PageFrame; +use crate::device::{EmuDev, EmuContext}; // todo: need to rewrite for more vm pub static SHARED_MEM: Mutex> = Mutex::new(None); @@ -25,8 +28,8 @@ pub const SHARED_MEM_SIZE_MAX: usize = 0x200000; pub fn ivc_update_mq(receive_ipa: usize, cfg_ipa: usize) -> bool { let vm = active_vm().unwrap(); let vm_id = vm.id(); - let receive_pa = vm_ipa2pa(vm.clone(), receive_ipa); - let cfg_pa = vm_ipa2pa(vm, cfg_ipa); + let receive_pa = vm_ipa2pa(&vm, receive_ipa); + let cfg_pa = vm_ipa2pa(&vm, cfg_ipa); if receive_pa == 0 { error!("ivc_update_mq: invalid receive_pa"); @@ -38,6 +41,7 @@ pub fn ivc_update_mq(receive_ipa: usize, cfg_ipa: usize) -> bool { let idx = 0; let val = vm_id; + // TODO: The return value is set to vm_id, but is actually useless current_cpu().set_gpr(idx, val); // println!("VM {} update message", vm_id); true @@ -53,21 +57,44 @@ pub fn mem_shared_mem_init() { } } -pub fn shyper_init(vm: Vm, base_ipa: usize, len: usize) -> bool { +pub fn shyper_init(vm: Weak, base_ipa: usize, len: usize) -> Result, ()> { if base_ipa == 0 || len == 0 { - info!("vm{} shyper base ipa {:x}, len {:x}", vm.id(), base_ipa, len); - return true; + info!("vm shyper base ipa {:x}, len {:x}", base_ipa, len); + return Ok(Arc::new(EmuShyper { base_ipa, len })); } let shared_mem = SHARED_MEM.lock(); match &*shared_mem { Some(page_frame) => { - vm.pt_map_range(base_ipa, len, page_frame.pa(), PTE_S2_NORMAL, true); - true + vm.upgrade() + .unwrap() + .pt_map_range(base_ipa, len, page_frame.pa(), PTE_S2_NORMAL, true); + Ok(Arc::new(EmuShyper { base_ipa, len })) } None => { error!("shyper_init: shared mem should not be None"); - false + Err(()) } } } + +struct EmuShyper { + base_ipa: usize, + len: usize, +} + +impl EmuDev for EmuShyper { + fn emu_type(&self) -> crate::device::EmuDeviceType { + crate::device::EmuDeviceType::EmuDeviceTShyper + } + + fn address_range(&self) -> Range { + 0..0 + } + + fn handler(&self, emu_ctx: &EmuContext) -> bool { + info!("emu_shyper_handler: ipa {:x}", emu_ctx.address); + info!("DO NOTHING"); + true + } +} diff --git a/src/kernel/logger.rs b/src/kernel/logger.rs index e3ccd5aa8301259b1ea43d0c914a8e4bd96d4880..5623c6648b131597f8512d7ef0c2d4b10790a1a6 100755 --- a/src/kernel/logger.rs +++ b/src/kernel/logger.rs @@ -13,6 +13,22 @@ use log::{LevelFilter, SetLoggerError}; struct SimpleLogger; +fn level2color(level: Level) -> u8 { + match level { + Level::Error => 31, // 31 Red + Level::Warn => 93, // 93 BrightYellow + Level::Info => 34, // 34 Blue + Level::Debug => 32, // 32 Green + Level::Trace => 90, // 90 BrightBlack + } +} + +macro_rules! with_color { + ($color: expr, $($arg:tt)*) => { + format_args!("\u{1B}[{}m{}\u{1B}[0m", $color as u8, format_args!($($arg)*)) + }; +} + impl log::Log for SimpleLogger { fn enabled(&self, _metadata: &Metadata) -> bool { true @@ -20,11 +36,6 @@ impl log::Log for SimpleLogger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { - // let ms = crate::lib::timer::current_ms(); - // let s = ms / 1000; - // let ms = ms % 1000; - // print!("[{:04}.{:03}]", s, ms); - let level = match record.level() { Level::Error => "[E]", Level::Warn => "[W]", @@ -33,11 +44,17 @@ impl log::Log for SimpleLogger { Level::Trace => "[T]", }; println!( - "{}>{}[{}] {}", - level, - crate::kernel::current_cpu().id, - record.target(), - record.args() + "{}", + with_color!( + level2color(record.level()), + "{}>[core {}, {}, {}:{}] {}", + level, + crate::kernel::current_cpu().id, + record.target(), + record.file().unwrap_or("Unknown File"), + record.line().unwrap_or(0), + record.args() + ) ); } } diff --git a/src/kernel/mem.rs b/src/kernel/mem.rs index 592a635e628113aa37d384782dacee6b5bc34829..fd7a3ffe5ddb13931ae1485fa26353e585cf968b 100644 --- a/src/kernel/mem.rs +++ b/src/kernel/mem.rs @@ -11,91 +11,18 @@ use crate::arch::PAGE_SIZE; use crate::board::*; use crate::kernel::mem_shared_mem_init; -use crate::utils::{memset, round_up}; use crate::mm::PageFrame; use super::mem_region::*; -use self::AllocError::*; -use core::slice::from_raw_parts_mut; pub const VM_MEM_REGION_MAX: usize = 4; pub fn mem_init() { - mem_heap_region_init(); mem_vm_region_init(); mem_shared_mem_init(); info!("Mem init ok"); } -/// Clear BSS section -pub unsafe fn clear_bss() { - extern "C" { - fn _bss_begin(); - fn _bss_end(); - } - println!("clear bss : from {:x} to {:x}", _bss_begin as usize, _bss_end as usize); - from_raw_parts_mut(_bss_begin as usize as *mut u8, _bss_end as usize - _bss_begin as usize).fill(0); -} - -/// init heap memory region -pub fn mem_heap_region_init() { - extern "C" { - // Note: link-time label, see aarch64.lds - fn _image_end(); - } - - if PLAT_DESC.mem_desc.regions.is_empty() { - warn!("Platform has no memory region!"); - } - - let base = round_up(_image_end as usize, PAGE_SIZE); - let size = round_up( - PLAT_DESC.mem_desc.regions[0].size - (base - PLAT_DESC.mem_desc.base), - PAGE_SIZE, - ) / PAGE_SIZE; - - info!("init memory, please waiting..."); - // SAFETY: - // The region is writable for the Hypervisor in EL2. - // The 'c' is a valid value of type u8 without overflow. - unsafe { - memset(base as *mut u8, 0, size as usize * PAGE_SIZE); - } - // core::intrinsics::volatile_set_memory(ptr, 0, size as usize * PAGE_SIZE); - - let mut heap_lock = HEAP_REGION.lock(); - (*heap_lock).region_init(base, size, size, 0); - - drop(heap_lock); - - info!( - "Memory Heap: base 0x{:x}, size {} MB / {} pages", - base, - size * PAGE_SIZE / (1024 * 1024), - size - ); - info!("Memory Heap init ok"); -} - -/// Reserve Heap Memory from base_addr to base_addr + size -/// #Example -/// ``` -/// mem_heap_region_reserve(0x8a000000, 0x8000000); -/// ``` -pub fn mem_heap_region_reserve(base_addr: usize, size: usize) { - let mut heap = HEAP_REGION.lock(); - // //TODO: a compromise way for live_update - if base_addr < heap.region.base { - return; - } - heap.reserve_pages(base_addr, round_up(size, PAGE_SIZE) / PAGE_SIZE); - info!( - "Reserve Heap Region 0x{:x} ~ 0x{:x}", - base_addr, - base_addr + round_up(size, PAGE_SIZE) - ); -} - fn mem_vm_region_init() { if PLAT_DESC.mem_desc.regions.is_empty() { panic!("Platform Vm Memory Regions Overrun!"); @@ -137,44 +64,18 @@ pub enum AllocError { OutOfFrame, } -fn mem_heap_reset() { - let heap = HEAP_REGION.lock(); - // SAFETY: - // The 'heap_region' is writable for the Hypervisor in EL2. - // The 'c' is a valid value of type u8 without overflow. - unsafe { - memset(heap.region.base as *mut u8, 0, heap.region.size * PAGE_SIZE); - } -} - -/// alloc some page from heap region -pub fn mem_heap_alloc(page_num: usize, _aligned: bool) -> Result { - if page_num == 0 { - return Err(AllocZeroPage); - } - - let mut heap = HEAP_REGION.lock(); - if page_num > heap.region.free { - return Err(OutOfFrame); - } - - heap.alloc_pages(page_num) -} - -/// free some page from heap region -pub fn mem_heap_free(addr: usize, page_num: usize) -> bool { - let mut heap = HEAP_REGION.lock(); - heap.free_pages(addr, page_num) -} - /// alloc one page pub fn mem_page_alloc() -> Result { - PageFrame::alloc_pages(1) + PageFrame::alloc_pages(1, 1) } /// alloc some continuous pages pub fn mem_pages_alloc(page_num: usize) -> Result { - PageFrame::alloc_pages(page_num) + PageFrame::alloc_pages(page_num, 1) +} + +pub fn mem_pages_alloc_align(page_num: usize, align_page: usize) -> Result { + PageFrame::alloc_pages(page_num, align_page) } /// alloc some space and create a new vm region diff --git a/src/kernel/mem_region.rs b/src/kernel/mem_region.rs index d291cc09464e731a9af8615f53e0184eca6b5f5d..e336a7a600ab7ca2b7c87260a1940cb8976e9d13 100644 --- a/src/kernel/mem_region.rs +++ b/src/kernel/mem_region.rs @@ -12,12 +12,6 @@ use alloc::vec::Vec; use spin::Mutex; -use crate::arch::PAGE_SIZE; -use crate::utils::{BitAlloc, BitAlloc4K, BitAlloc64K, BitMap}; -use crate::utils::memset; - -use super::AllocError; - const TOTAL_MEM_REGION_MAX: usize = 16; #[derive(Copy, Clone, Eq, Debug)] @@ -55,111 +49,6 @@ impl MemRegion { } } -/// Heap Region Struct, using a bitmap to manage heap memory -pub struct HeapRegion { - pub map: BitMap, - pub region: MemRegion, -} - -impl HeapRegion { - pub fn region_init(&mut self, base: usize, size: usize, free: usize, last: usize) { - self.region.init(base, size, free, last); - } - - // base addr need to align to PAGE_SIZE - /// Reserve a range of pages in heap region, to avoid allocation of this range - pub fn reserve_pages(&mut self, base: usize, size: usize) { - debug!( - "reserve pages from {:x}, size {:x}, region from{:x}", - base, size, self.region.base - ); - let offset = (base - self.region.base) / PAGE_SIZE; - for i in 0..size { - if self.map.get(offset + i) != 0 { - panic!("Heap Page 0x{:x} has been alloc", base + i * PAGE_SIZE); - } - self.map.set(offset + i); - } - } - - fn first_fit(&self, size: usize) -> Option { - if size <= 1 && self.map.get(self.region.last) == 0 { - Some(self.region.last) - } else { - let mut bit = None; - let mut count = 0; - for i in 0..self.region.size { - if self.map.get(i) == 0 { - count += 1; - if count >= size { - bit = Some(i + 1 - count); - break; - } - } else { - count = 0; - } - } - bit - } - } - - /// alloc continuous pages for use - pub fn alloc_pages(&mut self, size: usize) -> Result { - let res = self.first_fit(size); - if res.is_none() { - error!( - "alloc_pages: allocate {} pages failed (heap_base 0x{:x} remain {} total {})", - size, self.region.base, self.region.free, self.region.size - ); - return Err(AllocError::OutOfFrame); - } - let bit = res.unwrap(); - - for i in bit..bit + size { - self.map.set(i); - } - self.region.free -= size; - if bit + size < self.region.size { - self.region.last = bit + size; - } else { - self.region.last = 0; - } - - let addr = self.region.base + bit * PAGE_SIZE; - // SAFETY: - // The addr is writable for the Hypervisor in EL2. - // The 'c' is a valid value of type u8 without overflow. - unsafe { - memset(addr as *mut u8, 0, size * PAGE_SIZE); - debug!("mem heap alloc alocate {:x}, size {:x}", addr, size * PAGE_SIZE); - } - Ok(addr) - } - - /// free a segment of continuous pages - pub fn free_pages(&mut self, base: usize, size: usize) -> bool { - use crate::utils::range_in_range; - if !range_in_range(base, size * PAGE_SIZE, self.region.base, self.region.size * PAGE_SIZE) { - panic!( - "free_page: out of range (addr 0x{:x} page num {} heap base 0x{:x} heap size 0x{:x})", - base, - size, - self.region.base, - self.region.size * PAGE_SIZE - ); - // return false; - } - - let page_idx = (base - self.region.base) / PAGE_SIZE; - for idx in page_idx..page_idx + size { - self.map.clear(idx); - } - self.region.free += size; - self.region.last = page_idx; - true - } -} - /// Vm memory region struct pub struct VmRegion { pub region: Vec, @@ -171,22 +60,6 @@ impl VmRegion { } } -pub static HEAP_REGION: Mutex = Mutex::new(HeapRegion { - map: BitAlloc64K::default(), - region: MemRegion::new(), -}); - pub static VM_REGION: Mutex = Mutex::new(VmRegion { region: Vec::::new(), }); - -pub fn bits_to_pages(bits: usize) -> usize { - use crate::utils::round_up; - round_up(bits, PAGE_SIZE) -} - -/// check if a physical address is in heap region -pub fn pa_in_heap_region(pa: usize) -> bool { - let heap_region = HEAP_REGION.lock(); - pa > heap_region.region.base && pa < (heap_region.region.base * PAGE_SIZE + heap_region.region.size) -} diff --git a/src/kernel/sched/sched_rr.rs b/src/kernel/sched/sched_rr.rs index 3412ed2427ff958bae90a0142cf53a72a4e281c5..3a6e9623bc3246d4b00ed270fc574ab9155bd9bc 100644 --- a/src/kernel/sched/sched_rr.rs +++ b/src/kernel/sched/sched_rr.rs @@ -54,8 +54,6 @@ impl Scheduler for SchedulerRR { /// Schedule to the next vcpu object fn do_schedule(&mut self) { - // let next_vcpu = self.next().unwrap(); - // current_cpu().schedule_to(next_vcpu); if let Some(next_vcpu) = self.next() { current_cpu().schedule_to(next_vcpu); } else { @@ -72,12 +70,6 @@ impl Scheduler for SchedulerRR { /// put vcpu into sleep, and remove it from scheduler fn sleep(&mut self, vcpu: Vcpu) { - // println!( - // "SchedulerRR: Core {} sleep VM[{}] vcpu {}", - // current_cpu().id, - // vcpu.vm_id(), - // vcpu.id() - // ); let mut need_schedule = false; { let queue = &mut self.queue; @@ -120,7 +112,7 @@ impl Scheduler for SchedulerRR { /// yield to another cpu, only used when vcpu is new added and want to be excuted immediately fn yield_to(&mut self, vcpu: Vcpu) { - let queue = &mut self.queue; + let queue: &mut Vec = &mut self.queue; queue.push(vcpu.clone()); self.active_idx = queue.len() - 1; current_cpu().schedule_to(vcpu); @@ -138,7 +130,7 @@ impl SchedulerUpdate for SchedulerRR { let vm_id = vcpu.vm_id(); let vcpu_id = vcpu.id(); let vm = vm(vm_id).unwrap(); - new_rr.queue.push(vm.vcpu(vcpu_id).unwrap()); + new_rr.queue.push(vm.vcpu(vcpu_id).unwrap().clone()); } new_rr.active_idx = src_rr.active_idx; new_rr.base_slice = src_rr.base_slice; diff --git a/src/kernel/timer.rs b/src/kernel/timer.rs index fd64966933a79be55d04dbb3dbe6740959323f2b..07e3ca99cc17894c3d8816882165a245925e0c4e 100644 --- a/src/kernel/timer.rs +++ b/src/kernel/timer.rs @@ -9,7 +9,7 @@ // See the Mulan PSL v2 for more details. use crate::arch::traits::InterruptController; -use crate::arch::IntCtrl; +use crate::arch::{is_boot_core, IntCtrl}; use crate::kernel::{current_cpu, Scheduler}; // #[derive(Copy, Clone)] @@ -36,7 +36,7 @@ pub fn timer_init() { #[cfg(not(feature = "secondary_start"))] crate::utils::barrier(); - if current_cpu().id == 0 { + if is_boot_core(current_cpu().id) { crate::kernel::interrupt_reserve_int(IntCtrl::IRQ_HYPERVISOR_TIMER, timer_irq_handler); info!("Timer frequency: {}Hz", crate::arch::timer_arch_get_frequency()); info!("Timer init ok"); @@ -64,11 +64,27 @@ fn timer_notify_after(ms: usize) { timer_arch_enable_irq(); } +pub const TIMER_INTERVAL: usize = 10; + pub fn timer_irq_handler() { use crate::arch::timer_arch_disable_irq; timer_arch_disable_irq(); current_cpu().scheduler().do_schedule(); - timer_notify_after(1); + #[cfg(target_arch = "riscv64")] + { + let vcpu_option = current_cpu().active_vcpu.as_ref(); + if let Some(vcpu) = vcpu_option { + let next_timer = vcpu.inner.inner_mut.lock().vm_ctx.next_timer_intr; + // next_timer == 0 indicates the initialization condition. + // In this case, no clock interrupt is triggered + if next_timer <= crate::arch::timer_arch_get_counter() as u64 && next_timer != 0 { + // Timer is expired, inject interrupt! + crate::arch::IntCtrl::vm_inject(vcpu.vm().as_ref().unwrap(), vcpu, crate::arch::IRQ_GUEST_TIMER); + } + } + } + + timer_notify_after(TIMER_INTERVAL); } diff --git a/src/kernel/vcpu.rs b/src/kernel/vcpu.rs index 85c3d89093b26a4782ad8ed84f4db79e11e1e914..df473319e2c73b840ea76b98caa569e2aaa04d0f 100644 --- a/src/kernel/vcpu.rs +++ b/src/kernel/vcpu.rs @@ -8,13 +8,21 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::sync::Arc; +use alloc::sync::{Arc, Weak}; use alloc::vec::Vec; +#[cfg(target_arch = "riscv64")] +use riscv::register::{hgatp, hstatus, sie}; +#[cfg(target_arch = "riscv64")] +use core::arch::riscv64::hlv_wu; use core::mem::size_of; use spin::Mutex; +use crate::arch::traits::VmContextTrait; -use crate::arch::{ContextFrame, ContextFrameTrait, GicContext, VmContext, timer_arch_get_counter}; -use crate::board::PlatOperation; +use crate::arch::{ArchTrait, ContextFrame, ContextFrameTrait, GicContext, VmContext}; +#[cfg(target_arch = "riscv64")] +use crate::arch::{get_trapframe_for_hart, SSTATUS_FS, SSTATUS_SPIE, SSTATUS_SPP, SSTATUS_VS, TP_NUM}; +use crate::board::{PlatOperation, PLATFORM_CPU_NUM_MAX}; +use crate::config::VmConfigEntry; use crate::kernel::{current_cpu, interrupt_vm_inject, vm_if_set_state}; use crate::kernel::{active_vcpu_id, active_vm_id}; use crate::utils::memcpy; @@ -31,8 +39,9 @@ pub enum VcpuState { } struct VcpuInnerConst { - id: usize, - vm: Option, + id: usize, // vcpu id + vm: Weak, // weak ref to related vm + phys_id: usize, // binding physical CPU's id } pub struct VcpuInner { @@ -46,20 +55,31 @@ pub struct Vcpu { pub inner: Arc, } +impl PartialEq for Vcpu { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + impl Vcpu { - pub fn new(vm: Vm, vcpu_id: usize) -> Self { - let this = Self { + pub fn new(vm: Weak, vcpu_id: usize, phys_id: usize) -> Self { + Self { inner: Arc::new(VcpuInner { inner_const: VcpuInnerConst { id: vcpu_id, - vm: Some(vm.clone()), + vm, + phys_id, }, inner_mut: Mutex::new(VcpuInnerMut::new()), }), - }; - crate::arch::vcpu_arch_init(vm, this.clone()); - this.reset_context(); - this + } + } + + pub fn init(&self, config: &VmConfigEntry) { + self.init_boot_info(config); + #[cfg(target_arch = "aarch64")] + self.init_spsr(); + self.reset_context(); } /// shutdown this vcpu @@ -70,28 +90,37 @@ impl Vcpu { active_vm_id(), active_vcpu_id() ); + // TODO: Wrong behavior. You should shut down the current vcpu, not the current cpu crate::board::Platform::cpu_shutdown(); } pub fn context_vm_store(&self) { + // Save general registers's value to inner self.save_cpu_ctx(); let mut inner = self.inner.inner_mut.lock(); + + // Save VM's state inner.vm_ctx.ext_regs_store(); inner.vm_ctx.fpsimd_save_context(); inner.vm_ctx.gic_save_state(); } pub fn context_vm_restore(&self) { + // Restore inner's state to cpu.ctx (actually on the stack) self.restore_cpu_ctx(); let inner = self.inner.inner_mut.lock(); + // restore vm's VFP and SIMD + // restore vm's state inner.vm_ctx.fpsimd_restore_context(); inner.vm_ctx.gic_restore_state(); inner.vm_ctx.ext_regs_restore(); drop(inner); + // Note: You do not need to set hstatus to skip to GuestOS the next time + self.inject_int_inlist(); } @@ -143,22 +172,6 @@ impl Vcpu { } } - pub fn set_phys_id(&self, phys_id: usize) { - let mut inner = self.inner.inner_mut.lock(); - debug!("set vcpu {} phys id {}", self.id(), phys_id); - inner.phys_id = phys_id; - } - - pub fn set_gich_ctlr(&self, ctlr: u32) { - let mut inner = self.inner.inner_mut.lock(); - inner.vm_ctx.gic_state.ctlr = ctlr; - } - - pub fn set_hcr(&self, hcr: u64) { - let mut inner = self.inner.inner_mut.lock(); - inner.vm_ctx.hcr_el2 = hcr; - } - pub fn state(&self) -> VcpuState { let inner = self.inner.inner_mut.lock(); inner.state @@ -173,13 +186,13 @@ impl Vcpu { self.inner.inner_const.id } - pub fn vm(&self) -> Option { - self.inner.inner_const.vm.clone() + pub fn vm(&self) -> Option> { + self.inner.inner_const.vm.upgrade() } + #[inline] pub fn phys_id(&self) -> usize { - let inner = self.inner.inner_mut.lock(); - inner.phys_id + self.inner.inner_const.phys_id } pub fn vm_id(&self) -> usize { @@ -192,25 +205,11 @@ impl Vcpu { pub fn reset_context(&self) { let mut inner = self.inner.inner_mut.lock(); - let mut vmpidr = 0; - vmpidr |= 1 << 31; //bit[31]:res1 - - if self.vm().as_ref().unwrap().config().cpu_num() == 1 { - vmpidr |= 1 << 30; //bit[30]: Indicates a Uniprocessor system + #[cfg(target_arch = "aarch64")] + { + inner.vm_ctx.vmpidr_el2 = self.get_vmpidr() as u64; } - - #[cfg(feature = "tx2")] - if self.vm_id() == 0 { - // A57 is cluster #1 for L4T - vmpidr |= 0x100; - } - - vmpidr |= if cfg!(feature = "rk3588") { - 0x100_0000 | (self.id() << 8) - } else { - self.id() - }; - inner.vm_ctx.vmpidr_el2 = vmpidr as u64; + inner.gic_ctx_reset(); let vm_id = self.vm().unwrap().id(); use crate::kernel::vm_if_get_type; if vm_if_get_type(vm_id) == VmType::VmTBma { @@ -223,8 +222,7 @@ impl Vcpu { pub fn reset_vtimer_offset(&self) { let mut inner = self.inner.inner_mut.lock(); - let curpct = timer_arch_get_counter() as u64; - inner.vm_ctx.cntvoff_el2 = curpct - inner.vm_ctx.cntvct_el0; + inner.vm_ctx.reset_vtimer_offset(); } pub fn context_ext_regs_store(&self) { @@ -268,35 +266,59 @@ impl Vcpu { inner.int_list.clear(); drop(inner); for int in int_list { - // println!("schedule: inject int {} for vm {}", int, vm.id()); - interrupt_vm_inject(vm.clone(), self.clone(), int); + interrupt_vm_inject(&vm, self, int); } } } } pub fn get_vmpidr(&self) -> usize { - let inner = self.inner.inner_mut.lock(); - inner.vm_ctx.vmpidr_el2 as usize + 1 << 31 + | if cfg!(feature = "rk3588") { + 0x100_0000 | (self.id() << 8) + } else if cfg!(feature = "tx2") && self.vm_id() == 0 { + // A57 is cluster #1 for L4T + 0x100 | self.id() + } else { + self.id() + } } } +#[derive(Clone, Copy)] struct IdleThread { pub ctx: ContextFrame, } fn idle_thread() { loop { - cortex_a::asm::wfi(); + crate::arch::Arch::wait_for_interrupt(); } } -static IDLE_THREAD: spin::Lazy = spin::Lazy::new(|| { - let mut ctx = ContextFrame::new(idle_thread as usize, current_cpu().stack_top(), 0); - use cortex_a::registers::SPSR_EL2; - ctx.set_exception_pc(idle_thread as usize); - ctx.spsr = (SPSR_EL2::M::EL2h + SPSR_EL2::F::Masked + SPSR_EL2::A::Masked + SPSR_EL2::D::Masked).value; - IdleThread { ctx } +static IDLE_THREAD: spin::Lazy<[Option; PLATFORM_CPU_NUM_MAX]> = spin::Lazy::new(|| { + const ARRAY_REPEAT_VALUE: Option = None; + let mut idle_threads: [Option; PLATFORM_CPU_NUM_MAX] = [ARRAY_REPEAT_VALUE; PLATFORM_CPU_NUM_MAX]; + for i in 0..PLATFORM_CPU_NUM_MAX { + let mut ctx = ContextFrame::new(idle_thread as usize, current_cpu().stack_top(), 0); + ctx.set_exception_pc(idle_thread as usize); + // Set the next state to jump to the S-mode + + #[cfg(target_arch = "riscv64")] + { + ctx.sstatus = SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_VS; + ctx.gpr[TP_NUM] = crate::kernel::get_cpu_info_addr(i); + } + + #[cfg(target_arch = "aarch64")] + { + use cortex_a::registers::SPSR_EL2; + ctx.spsr = (SPSR_EL2::M::EL2h + SPSR_EL2::F::Masked + SPSR_EL2::A::Masked + SPSR_EL2::D::Masked).value; + } + idle_threads[i] = Some(IdleThread { ctx }); + } + + idle_threads }); pub fn run_idle_thread() { @@ -308,14 +330,13 @@ pub fn run_idle_thread() { unsafe { crate::utils::memcpy( current_cpu().ctx as *const u8, - &(IDLE_THREAD.ctx) as *const _ as *const u8, + &(IDLE_THREAD[current_cpu().id].unwrap().ctx) as *const _ as *const u8, core::mem::size_of::(), ); } } pub struct VcpuInnerMut { - pub phys_id: usize, pub state: VcpuState, pub int_list: Vec, pub vcpu_ctx: ContextFrame, @@ -326,25 +347,22 @@ pub struct VcpuInnerMut { impl VcpuInnerMut { fn new() -> VcpuInnerMut { VcpuInnerMut { - phys_id: 0, state: VcpuState::Invalid, int_list: vec![], vcpu_ctx: ContextFrame::default(), vm_ctx: VmContext::default(), + #[allow(clippy::default_constructed_unit_structs)] gic_ctx: GicContext::default(), } } fn gic_ctx_reset(&mut self) { - use crate::arch::gich_lrs_num; - for i in 0..gich_lrs_num() { - self.vm_ctx.gic_state.lr[i] = 0; - } - self.vm_ctx.gic_state.hcr |= 1 << 2; // init hcr + self.vm_ctx.gic_ctx_reset(); } } // WARNING: No Auto `drop` in this function +// The first time a vcpu runs a VM, it needs to initialize some hardware registers pub fn vcpu_run(announce: bool) -> ! { { let vcpu = current_cpu().active_vcpu.clone().unwrap(); @@ -352,18 +370,46 @@ pub fn vcpu_run(announce: bool) -> ! { current_cpu().cpu_state = CpuState::CpuRun; vm_if_set_state(active_vm_id(), super::VmState::VmActive); + crate::arch::Arch::install_vm_page_table(vm.pt_dir(), vm.id()); vcpu.context_vm_restore(); if announce { - crate::device::virtio_net_announce(vm); + crate::device::virtio_net_announce(vm.clone()); + } + crate::arch::tlb_invalidate_guest_all(); + for i in 0..vm.mem_region_num() { + unsafe { + crate::arch::cache_invalidate_d(vm.pa_start(i), vm.pa_length(i)); + } + } + } + + #[cfg(target_arch = "riscv64")] + { + // set ssratch, used to save VM's TrapFrame + current_cpu().ctx_mut().unwrap().sscratch = get_trapframe_for_hart(current_cpu().id); + + trace!("Prepare to enter context vm entry..."); + let sepc = current_cpu().ctx_mut().unwrap().sepc; + let sscratch = current_cpu().ctx_mut().unwrap().sscratch; + trace!( + "sepc: {:#x}, sscratch: {:#x}, hgatp: {:#x}, hstatus: {:#x}, sstatus: {:#x}, sie: {:#x}", + sepc, + sscratch, + hgatp::read(), + hstatus::read(), + current_cpu().ctx_mut().unwrap().sstatus, + sie::read().bits() + ); + trace!("ctx: \n{}", current_cpu().ctx_mut().unwrap()); + + let val; + unsafe { + val = hlv_wu(sepc as *const u32); } - // tlb_invalidate_guest_all(); - // for i in 0..vm.mem_region_num() { - // unsafe { - // cache_invalidate_d(vm.pa_start(i), vm.pa_length(i)); - // } - // } + trace!("test entry_point(sepc) memory: hlv_wu(*0x{:#08x}): {:#010x}", sepc, val); } + extern "C" { fn context_vm_entry(ctx: usize) -> !; } diff --git a/src/kernel/vcpu_array.rs b/src/kernel/vcpu_array.rs index 46d112b83d1eba3bf67901d71b60f3f8fa7e0ea4..ddb7a955900bc79ba2ec4169782c72e270eb1610 100644 --- a/src/kernel/vcpu_array.rs +++ b/src/kernel/vcpu_array.rs @@ -32,8 +32,11 @@ impl VcpuArray { } #[inline] - pub fn pop_vcpu_through_vmid(&self, vm_id: usize) -> Option { - self.array[vm_id].clone() + pub fn pop_vcpu_through_vmid(&self, vm_id: usize) -> Option<&Vcpu> { + match self.array.get(vm_id) { + Some(vcpu) => vcpu.as_ref(), + None => None, + } } #[inline] @@ -44,24 +47,17 @@ impl VcpuArray { pub fn append_vcpu(&mut self, vcpu: Vcpu) { // There is only 1 VCPU from a VM in a PCPU let vm_id = vcpu.vm_id(); - - info!( - "append_vcpu: append VM[{}] vcpu {} on core {}", - vm_id, - vcpu.id(), - current_cpu().id - ); - - if vm_id >= self.array.len() { - panic!("vm_id > self.array.len()"); - } - if self.array[vm_id].is_some() { - panic!("self.array[vm_id].is_some()"); + match self.array.get_mut(vm_id) { + Some(x) => match x { + Some(_) => error!("self.array[{vm_id}] is not None"), + None => { + debug_assert_eq!(current_cpu().id, vcpu.phys_id()); + *x = Some(vcpu); + self.len += 1; + } + }, + None => error!("vm_id > self.array.len()"), } - vcpu.set_phys_id(current_cpu().id); - - self.array[vm_id] = Some(vcpu); - self.len += 1; } pub fn remove_vcpu(&mut self, vm_id: usize) -> Option { @@ -126,7 +122,7 @@ pub fn restore_vcpu_gic(cur_vcpu: Option, trgt_vcpu: Vcpu) { } /// save gic context for current vcpu -pub fn save_vcpu_gic(cur_vcpu: Option, trgt_vcpu: Vcpu) { +pub fn save_vcpu_gic(cur_vcpu: Option, trgt_vcpu: &Vcpu) { match cur_vcpu { None => { trgt_vcpu.gic_save_context(); diff --git a/src/kernel/vm.rs b/src/kernel/vm.rs index a6efe8b57eed273390083be95e9d7271cb04bb16..e7bf44116ad06d512c0d002efeb675795edc8aa0 100644 --- a/src/kernel/vm.rs +++ b/src/kernel/vm.rs @@ -8,29 +8,22 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use alloc::sync::Arc; +use alloc::sync::{Arc, Weak}; use alloc::vec::Vec; -use core::mem::size_of; +use spin::{Mutex, Once}; -use spin::Mutex; - -use crate::arch::{PAGE_SIZE, PTE_S2_FIELD_AP_RO}; -use crate::arch::GICC_CTLR_EN_BIT; -use crate::arch::PageTable; +#[cfg(target_arch = "aarch64")] use crate::arch::Vgic; +#[cfg(target_arch = "riscv64")] +use crate::arch::VPlic; +use crate::arch::{PAGE_SIZE, emu_intc_init, PageTable}; use crate::config::VmConfigEntry; -use crate::device::{EmuDevs, VirtioMmioData}; +use crate::device::{EmuDev, emu_virtio_mmio_init}; +use crate::kernel::{shyper_init, emu_iommu_init}; use crate::utils::*; use crate::mm::PageFrame; use super::vcpu::Vcpu; -pub enum EmuDevData { - VirtioBlk(VirtioMmioData), - VirtioNet(VirtioMmioData), - VirtioConsole(VirtioMmioData), - None, -} - pub const VM_NUM_MAX: usize = 8; pub static VM_IF_LIST: [Mutex; VM_NUM_MAX] = [const { Mutex::new(VmInterface::default()) }; VM_NUM_MAX]; @@ -60,29 +53,17 @@ pub fn vm_if_get_type(vm_id: usize) -> VmType { } fn vm_if_set_cpu_id(vm_id: usize, master_cpu_id: usize) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.master_cpu_id = master_cpu_id; + let vm_if = VM_IF_LIST[vm_id].lock(); + vm_if.master_cpu_id.call_once(|| master_cpu_id); debug!( "vm_if_list_set_cpu_id vm [{}] set master_cpu_id {}", vm_id, master_cpu_id ); } -// todo: rewrite return val to Option -pub fn vm_if_get_cpu_id(vm_id: usize) -> usize { +pub fn vm_if_get_cpu_id(vm_id: usize) -> Option { let vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.master_cpu_id -} - -/// compare vm_if mac addr with frame mac addr -pub fn vm_if_cmp_mac(vm_id: usize, frame: &[u8]) -> bool { - let vm_if = VM_IF_LIST[vm_id].lock(); - for (i, &b) in vm_if.mac.iter().enumerate() { - if b != frame[i] { - return false; - } - } - true + vm_if.master_cpu_id.get().cloned() } pub fn vm_if_set_ivc_arg(vm_id: usize, ivc_arg: usize) { @@ -104,142 +85,61 @@ pub fn vm_if_ivc_arg_ptr(vm_id: usize) -> usize { let vm_if = VM_IF_LIST[vm_id].lock(); vm_if.ivc_arg_ptr } - -// new if for vm migration -pub fn vm_if_init_mem_map(vm_id: usize, len: usize) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map = Some(FlexBitmap::new(len)); -} - -pub fn vm_if_set_mem_map_cache(vm_id: usize, pf: PageFrame) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map_cache = Some(Arc::new(pf)); -} - -pub fn vm_if_mem_map_cache(vm_id: usize) -> Option> { - let vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map_cache.clone() -} - -pub fn vm_if_dirty_mem_map(vm_id: usize) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map.as_mut().unwrap().init_dirty(); -} - -pub fn vm_if_set_mem_map_bit(vm: Vm, pa: usize) { - let mut vm_if = VM_IF_LIST[vm.id()].lock(); - let mut bit = 0; - for i in 0..vm.region_num() { - let start = vm.pa_start(i); - let len = vm.pa_length(i); - if pa >= start && pa < start + len { - bit += (pa - start) / PAGE_SIZE; - vm_if.mem_map.as_mut().unwrap().set(bit, true); - return; - } else { - bit += len / PAGE_SIZE; - } - } - panic!("vm_if_set_mem_map_bit: illegal pa 0x{:x}", pa); -} - -pub fn vm_if_set_mem_map(vm_id: usize, bit: usize, len: usize) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map.as_mut().unwrap().set_bits(bit, len, true); -} - -pub fn vm_if_clear_mem_map(vm_id: usize) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map.as_mut().unwrap().clear(); -} - -pub fn vm_if_copy_mem_map(vm_id: usize) { - let mut vm_if = VM_IF_LIST[vm_id].lock(); - let mem_map_cache = vm_if.mem_map_cache.clone(); - let map = vm_if.mem_map.as_mut().unwrap(); - // SAFETY: - // We have both read and write access to the src and dst memory regions. - // The copied size will not exceed the memory region. - unsafe { - memcpy( - mem_map_cache.as_ref().unwrap().pa() as *const u8, - map.slice() as *const _ as *const u8, - size_of::() * map.vec_len(), - ); - } - // clear bitmap after copy - map.clear(); -} - -pub fn vm_if_mem_map_page_num(vm_id: usize) -> usize { - let vm_if = VM_IF_LIST[vm_id].lock(); - let map = vm_if.mem_map.as_ref().unwrap(); - 8 * map.vec_len() / PAGE_SIZE -} - -pub fn vm_if_mem_map_dirty_sum(vm_id: usize) -> usize { - let vm_if = VM_IF_LIST[vm_id].lock(); - vm_if.mem_map.as_ref().unwrap().sum() -} // End vm interface func implementation -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub enum VmState { + #[default] VmInv = 0, VmPending = 1, VmActive = 2, } -#[derive(Clone, Copy, PartialEq)] +#[derive(Default, Clone, Copy, PartialEq, Eq)] pub enum VmType { + #[default] VmTOs = 0, - VmTBma = 1, + VmTBma = 1, // Bare Metal Application } -impl VmType { - pub fn from_usize(value: usize) -> VmType { +impl From for VmType { + fn from(value: usize) -> Self { match value { - 0 => VmType::VmTOs, - 1 => VmType::VmTBma, + 0 => Self::VmTOs, + 1 => Self::VmTBma, _ => panic!("Unknown VmType value: {}", value), } } } pub struct VmInterface { - pub master_cpu_id: usize, + pub master_cpu_id: Once, pub state: VmState, pub vm_type: VmType, pub mac: [u8; 6], pub ivc_arg: usize, pub ivc_arg_ptr: usize, - pub mem_map: Option, - pub mem_map_cache: Option>, } impl VmInterface { const fn default() -> VmInterface { VmInterface { - master_cpu_id: 0, + master_cpu_id: Once::new(), state: VmState::VmPending, - vm_type: VmType::VmTBma, + vm_type: VmType::VmTOs, mac: [0; 6], ivc_arg: 0, ivc_arg_ptr: 0, - mem_map: None, - mem_map_cache: None, } } fn reset(&mut self) { - self.master_cpu_id = 0; + self.master_cpu_id = Once::new(); self.state = VmState::VmPending; - self.vm_type = VmType::VmTBma; + self.vm_type = VmType::VmTOs; self.mac = [0; 6]; self.ivc_arg = 0; self.ivc_arg_ptr = 0; - self.mem_map = None; - self.mem_map_cache = None; } } @@ -260,100 +160,235 @@ impl VmPa { } } -// #[repr(align(4096))] -#[derive(Clone)] -pub struct Vm { - pub inner: Arc>, +/// Vm interrupt controller type +#[derive(Clone, Copy, Default, Debug)] +pub enum IntCtrlType { + #[default] + Emulated, + Passthrough, } -impl Vm { - pub fn inner(&self) -> Arc> { - self.inner.clone() +pub struct Vm { + inner_const: VmInnerConst, + inner_mut: Mutex, +} + +struct VmInnerConst { + id: usize, + config: VmConfigEntry, + // Vcpu config + vcpu_list: Vec, + // Interrupt config + intc_type: IntCtrlType, + int_bitmap: BitAlloc4K, + #[cfg(target_arch = "aarch64")] + arch_intc_dev: Option>, + #[cfg(target_arch = "riscv64")] + arch_intc_dev: Option>, + // Emul devs config + emu_devs: Vec>, +} + +struct VmInnerMut { + pt: Option, + // Memory config + mem_region_num: usize, + pa_region: Vec, + iommu_ctx_id: Option, +} + +impl VmInnerConst { + fn new(id: usize, config: VmConfigEntry, vm: Weak) -> VmInnerConst { + let phys_id_list = cal_phys_id_list(&config); + info!("VM[{}] phys_id_list {:?}", id, phys_id_list); + + // cpu total number count must equal to the number of cpu in config + assert_eq!(phys_id_list.len(), config.cpu_num()); + // set master cpu id + vm_if_set_cpu_id(id, *phys_id_list.first().unwrap()); + + let mut vcpu_list = Vec::with_capacity(config.cpu_num()); + + for (vcpu_id, phys_id) in phys_id_list.into_iter().enumerate() { + vcpu_list.push(Vcpu::new(vm.clone(), vcpu_id, phys_id)); + } + let mut this = Self { + id, + config, + vcpu_list, + intc_type: IntCtrlType::Emulated, + arch_intc_dev: None, + int_bitmap: BitAlloc4K::default(), + emu_devs: Vec::new(), + }; + this.init_devices(vm); + this + } + + fn init_devices(&mut self, vm: Weak) -> bool { + use crate::device::EmuDeviceType::*; + for (idx, emu_cfg) in self.config.emulated_device_list().iter().enumerate() { + let dev = match emu_cfg.emu_type { + #[cfg(target_arch = "aarch64")] + EmuDeviceTGicd => { + self.intc_type = IntCtrlType::Emulated; + emu_intc_init(emu_cfg, &self.vcpu_list).map(|vgic| { + self.arch_intc_dev = vgic.clone().into_any_arc().downcast::().ok(); + vgic + }) + } + #[cfg(target_arch = "riscv64")] + EmuDeviceTPlic => { + self.intc_type = IntCtrlType::Emulated; + emu_intc_init(emu_cfg, &self.vcpu_list).map(|vplic| { + self.arch_intc_dev = vplic.clone().into_any_arc().downcast::().ok(); + vplic + }) + } + #[cfg(feature = "gicv3")] + EmuDeviceTGICR => { + if let Some(vgic) = &self.arch_intc_dev { + crate::arch::emu_vgicr_init(emu_cfg, vgic.clone()) + } else { + panic!("init_device: vgic not init"); + } + } + #[cfg(target_arch = "aarch64")] + EmuDeviceTGPPT => { + self.intc_type = IntCtrlType::Passthrough; + crate::arch::partial_passthrough_intc_init(emu_cfg) + } + EmuDeviceTVirtioConsole | EmuDeviceTVirtioNet | EmuDeviceTVirtioBlk => { + emu_virtio_mmio_init(vm.clone(), emu_cfg) + } + EmuDeviceTIOMMU => emu_iommu_init(emu_cfg), + EmuDeviceTShyper => shyper_init(vm.clone(), emu_cfg.base_ipa, emu_cfg.length), + _ => { + panic!("init_device: unknown emu dev type {:?}", emu_cfg.emu_type); + } + }; + // Then add the dev to the emu_devs list + if let Ok(emu_dev) = dev { + if self.emu_devs.iter().any(|dev| { + emu_dev.address_range().contains(&dev.address_range().start) + || dev.address_range().contains(&emu_dev.address_range().start) + }) { + panic!( + "duplicated emul address region: prev address {:x?}", + emu_dev.address_range(), + ); + } else { + self.emu_devs.push(emu_dev); + } + } else { + panic!("init_device: failed to init emu dev"); + } + // Then init int_bitmap + if emu_cfg.irq_id != 0 { + self.int_bitmap.set(emu_cfg.irq_id); + } + info!( + "VM {} registers emulated device: id=<{}>, name=\"{:?}\", ipa=<{:#x}>", + self.id, idx, emu_cfg.emu_type, emu_cfg.base_ipa + ); + } + // Passthrough irqs + for irq in self.config.passthrough_device_irqs() { + self.int_bitmap.set(*irq); + } + true } +} - pub fn default() -> Vm { - Vm { - inner: Arc::new(Mutex::new(VmInner::default())), +fn cal_phys_id_list(config: &VmConfigEntry) -> Vec { + // generate the vcpu physical id list + let mut phys_id_list = vec![]; + let mut cfg_cpu_allocate_bitmap = config.cpu_allocated_bitmap(); + if let Some(cpu_master) = config.cpu_master() { + if cfg_cpu_allocate_bitmap & (1 << cpu_master) != 0 { + phys_id_list.push(cpu_master); + } + let mut phys_id = 0; + while cfg_cpu_allocate_bitmap != 0 { + if cfg_cpu_allocate_bitmap & 1 != 0 && phys_id != cpu_master { + phys_id_list.push(phys_id); + } + phys_id += 1; + cfg_cpu_allocate_bitmap >>= 1; + } + } else { + let mut phys_id = 0; + while cfg_cpu_allocate_bitmap != 0 { + if cfg_cpu_allocate_bitmap & 1 != 0 { + phys_id_list.push(phys_id); + } + phys_id += 1; + cfg_cpu_allocate_bitmap >>= 1; } } + phys_id_list +} - pub fn new(id: usize) -> Vm { - Vm { - inner: Arc::new(Mutex::new(VmInner::new(id))), +impl VmInnerMut { + fn new() -> Self { + VmInnerMut { + pt: None, + mem_region_num: 0, + pa_region: Vec::new(), + iommu_ctx_id: None, } } +} - /// Init the VM's interrupt controller mode. - pub fn init_intc_mode(&self, emu: bool) { - let vm_inner = self.inner.lock(); - for vcpu in &vm_inner.vcpu_list { - info!( - "vm {} vcpu {} set {} hcr", - vm_inner.id, - vcpu.id(), - if emu { "emu" } else { "partial passthrough" } - ); - if !emu { - vcpu.set_gich_ctlr((GICC_CTLR_EN_BIT) as u32); - vcpu.set_hcr(0x80080001); // HCR_EL2_GIC_PASSTHROUGH_VAL - } else { - #[cfg(not(feature = "gicv3"))] - vcpu.set_gich_ctlr((GICC_CTLR_EN_BIT | crate::arch::GICC_CTLR_EOIMODENS_BIT) as u32); - vcpu.set_hcr(0x80080019); - } +impl Vm { + pub fn new(id: usize, config: VmConfigEntry) -> Arc { + let this = Arc::new_cyclic(|weak| Vm { + inner_const: VmInnerConst::new(id, config, weak.clone()), + inner_mut: Mutex::new(VmInnerMut::new()), + }); + for vcpu in this.vcpu_list() { + vcpu.init(this.config()); } + this.init_intc_mode(this.inner_const.intc_type); + this } pub fn set_iommu_ctx_id(&self, id: usize) { - let mut vm_inner = self.inner.lock(); + let mut vm_inner = self.inner_mut.lock(); vm_inner.iommu_ctx_id = Some(id); } pub fn iommu_ctx_id(&self) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); match vm_inner.iommu_ctx_id { None => { - panic!("vm {} do not have iommu context bank", vm_inner.id); + panic!("vm {} do not have iommu context bank", self.id()); } Some(id) => id, } } pub fn med_blk_id(&self) -> usize { - let vm_inner = self.inner.lock(); - // match self.config().mediated_block_index() { - // None => { - // panic!("vm {} do not have mediated blk", vm_inner.id); - // } - // Some(idx) => idx, - // } - match vm_inner.config.as_ref().unwrap().mediated_block_index() { + match self.config().mediated_block_index() { None => { - panic!("vm {} do not have mediated blk", vm_inner.id); + panic!("vm {} do not have mediated blk", self.id()); } Some(idx) => idx, } - // match vm_inner.med_blk_id { - // None => { - // panic!("vm {} do not have mediated blk", vm_inner.id); - // } - // Some(idx) => idx, - // } } - pub fn dtb(&self) -> Option<*mut fdt::myctypes::c_void> { - let vm_inner = self.inner.lock(); - vm_inner.dtb.map(|x| x as *mut fdt::myctypes::c_void) + #[inline] + pub fn vcpu_list(&self) -> &[Vcpu] { + &self.inner_const.vcpu_list } - pub fn set_dtb(&self, val: *mut fdt::myctypes::c_void) { - let mut vm_inner = self.inner.lock(); - vm_inner.dtb = Some(val as usize); + #[inline] + pub fn id(&self) -> usize { + self.inner_const.id } - pub fn vcpu(&self, index: usize) -> Option { - let vm_inner = self.inner.lock(); - match vm_inner.vcpu_list.get(index).cloned() { + pub fn vcpu(&self, index: usize) -> Option<&Vcpu> { + match self.vcpu_list().get(index) { Some(vcpu) => { assert_eq!(index, vcpu.id()); Some(vcpu) @@ -362,291 +397,144 @@ impl Vm { error!( "vcpu idx {} is to large than vcpu_list len {}", index, - vm_inner.vcpu_list.len() + self.vcpu_list().len() ); None } } } - pub fn push_vcpu(&self, vcpu: Vcpu) { - let mut vm_inner = self.inner.lock(); - if vcpu.id() >= vm_inner.vcpu_list.len() { - vm_inner.vcpu_list.push(vcpu); - } else { - trace!("VM[{}] insert VCPU {}", vm_inner.id, vcpu.id()); - vm_inner.vcpu_list.insert(vcpu.id(), vcpu); - } - } - - // avoid circular references - pub fn clear_list(&self) { - let mut vm_inner = self.inner.lock(); - vm_inner.emu_devs.clear(); - vm_inner.vcpu_list.clear(); - } - - pub fn select_vcpu2assign(&self, cpu_id: usize) -> Option { - let cfg_master = self.config().cpu_master(); - let cfg_cpu_num = self.config().cpu_num(); - let cfg_cpu_allocate_bitmap = self.config().cpu_allocated_bitmap(); - // make sure that vcpu assign is executed sequentially, otherwise - // the PCPUs may found that vm.cpu_num() == 0 at the same time and - // if cfg_master is not setted, they will not set master vcpu for VM - let mut vm_inner = self.inner.lock(); - if (cfg_cpu_allocate_bitmap & (1 << cpu_id)) != 0 && vm_inner.cpu_num < cfg_cpu_num { - // vm.vcpu(0) must be the VM's master vcpu - let trgt_id = if cpu_id == cfg_master || (!vm_inner.has_master && vm_inner.cpu_num == cfg_cpu_num - 1) { - 0 - } else if vm_inner.has_master { - cfg_cpu_num - vm_inner.cpu_num - } else { - // if master vcpu is not assigned, retain id 0 for it - cfg_cpu_num - vm_inner.cpu_num - 1 - }; - match vm_inner.vcpu_list.get(trgt_id).cloned() { - None => None, - Some(vcpu) => { - if vcpu.id() == 0 { - vm_if_set_cpu_id(vm_inner.id, cpu_id); - vm_inner.has_master = true; - } - vm_inner.cpu_num += 1; - vm_inner.ncpu |= 1 << cpu_id; - Some(vcpu) - } - } - } else { - None - } - } - - pub fn set_entry_point(&self, entry_point: usize) { - let mut vm_inner = self.inner.lock(); - vm_inner.entry_point = entry_point; - } - - pub fn set_emu_devs(&self, idx: usize, emu: EmuDevs) { - let mut vm_inner = self.inner.lock(); - if idx < vm_inner.emu_devs.len() { - if let EmuDevs::None = vm_inner.emu_devs[idx] { - // println!("set_emu_devs: cover a None emu dev"); - vm_inner.emu_devs[idx] = emu; - return; - } else { - panic!("set_emu_devs: set an exsit emu dev"); - } - } - vm_inner.emu_devs.resize(idx, EmuDevs::None); - vm_inner.emu_devs.push(emu); - } - - pub fn set_intc_dev_id(&self, intc_dev_id: usize) { - let mut vm_inner = self.inner.lock(); - vm_inner.intc_dev_id = intc_dev_id; - } - - pub fn set_int_bit_map(&self, int_id: usize) { - let mut vm_inner = self.inner.lock(); - vm_inner.int_bitmap.as_mut().unwrap().set(int_id); - } - - pub fn set_config_entry(&self, config: Option) { - let mut vm_inner = self.inner.lock(); - vm_inner.config = config; - } - - pub fn intc_dev_id(&self) -> usize { - let vm_inner = self.inner.lock(); - vm_inner.intc_dev_id + pub fn find_emu_dev(&self, ipa: usize) -> Option> { + self.inner_const + .emu_devs + .iter() + .find(|&dev| dev.address_range().contains(&ipa)) + .cloned() } pub fn pt_map_range(&self, ipa: usize, len: usize, pa: usize, pte: usize, map_block: bool) { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); match &vm_inner.pt { Some(pt) => pt.pt_map_range(ipa, len, pa, pte, map_block), None => { - panic!("Vm::pt_map_range: vm{} pt is empty", vm_inner.id); + panic!("Vm::pt_map_range: vm{} pt is empty", self.id()); } } } pub fn pt_unmap_range(&self, ipa: usize, len: usize, map_block: bool) { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); match &vm_inner.pt { Some(pt) => pt.pt_unmap_range(ipa, len, map_block), None => { - panic!("Vm::pt_umnmap_range: vm{} pt is empty", vm_inner.id); + panic!("Vm::pt_umnmap_range: vm{} pt is empty", self.id()); } } } // ap: access permission pub fn pt_set_access_permission(&self, ipa: usize, ap: usize) -> (usize, usize) { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); match &vm_inner.pt { Some(pt) => pt.access_permission(ipa, PAGE_SIZE, ap), None => { - panic!("pt_set_access_permission: vm{} pt is empty", vm_inner.id); - } - } - } - - pub fn pt_read_only(&self) { - let vm_inner = self.inner.lock(); - match vm_inner.pt.clone() { - Some(pt) => { - let num = vm_inner.mem_region_num; - drop(vm_inner); - for i in 0..num { - let vm_inner = self.inner.lock(); - let ipa_start = vm_inner.pa_region[i].pa_start + vm_inner.pa_region[i].offset as usize; - let len = vm_inner.pa_region[i].pa_length; - drop(vm_inner); - pt.access_permission(ipa_start, len, PTE_S2_FIELD_AP_RO); - } - } - None => { - panic!("Vm::read_only: vm{} pt is empty", vm_inner.id); + panic!("pt_set_access_permission: vm{} pt is empty", self.id()); } } } pub fn set_pt(&self, pt_dir_frame: PageFrame) { - let mut vm_inner = self.inner.lock(); + let mut vm_inner = self.inner_mut.lock(); vm_inner.pt = Some(PageTable::new(pt_dir_frame)) } pub fn pt_dir(&self) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); match &vm_inner.pt { Some(pt) => pt.base_pa(), None => { - panic!("Vm::pt_dir: vm{} pt is empty", vm_inner.id); + panic!("Vm::pt_dir: vm{} pt is empty", self.id()); } } } pub fn cpu_num(&self) -> usize { - let vm_inner = self.inner.lock(); - vm_inner.cpu_num + self.inner_const.config.cpu_num() } - pub fn id(&self) -> usize { - let vm_inner = self.inner.lock(); - vm_inner.id - } - - pub fn config(&self) -> VmConfigEntry { - let vm_inner = self.inner.lock(); - match &vm_inner.config { - None => { - panic!("VM[{}] do not have vm config entry", vm_inner.id); - } - Some(config) => config.clone(), - } + #[inline] + pub fn config(&self) -> &VmConfigEntry { + &self.inner_const.config } pub fn add_region(&self, region: VmPa) { - let mut vm_inner = self.inner.lock(); + let mut vm_inner = self.inner_mut.lock(); vm_inner.pa_region.push(region); } pub fn region_num(&self) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); vm_inner.pa_region.len() } pub fn pa_start(&self, idx: usize) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); vm_inner.pa_region[idx].pa_start } pub fn pa_length(&self, idx: usize) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); vm_inner.pa_region[idx].pa_length } pub fn pa_offset(&self, idx: usize) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); vm_inner.pa_region[idx].offset as usize } pub fn set_mem_region_num(&self, mem_region_num: usize) { - let mut vm_inner = self.inner.lock(); + let mut vm_inner = self.inner_mut.lock(); vm_inner.mem_region_num = mem_region_num; } pub fn mem_region_num(&self) -> usize { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); vm_inner.mem_region_num } - pub fn vgic(&self) -> Arc { - let vm_inner = self.inner.lock(); - match &vm_inner.emu_devs[vm_inner.intc_dev_id] { - EmuDevs::Vgic(vgic) => vgic.clone(), - _ => { - panic!("vm{} cannot find vgic", vm_inner.id); - } + #[cfg(target_arch = "aarch64")] + pub fn vgic(&self) -> &Vgic { + if let Some(vgic) = self.inner_const.arch_intc_dev.as_ref() { + return vgic; } + panic!("vm{} cannot find vgic", self.id()); } + #[cfg(target_arch = "aarch64")] pub fn has_vgic(&self) -> bool { - let vm_inner = self.inner.lock(); - if vm_inner.intc_dev_id >= vm_inner.emu_devs.len() { - return false; - } - matches!(&vm_inner.emu_devs[vm_inner.intc_dev_id], EmuDevs::Vgic(_)) - } - - pub fn emu_dev(&self, dev_id: usize) -> EmuDevs { - let vm_inner = self.inner.lock(); - vm_inner.emu_devs[dev_id].clone() + self.inner_const.arch_intc_dev.is_some() } - pub fn emu_net_dev(&self, id: usize) -> EmuDevs { - let vm_inner = self.inner.lock(); - let mut dev_num = 0; - - for i in 0..vm_inner.emu_devs.len() { - if let EmuDevs::VirtioNet(_) = vm_inner.emu_devs[i] { - if dev_num == id { - return vm_inner.emu_devs[i].clone(); - } - dev_num += 1; - } - } - EmuDevs::None - } - - pub fn emu_blk_dev(&self) -> EmuDevs { - for emu in &self.inner.lock().emu_devs { - if let EmuDevs::VirtioBlk(_) = emu { - return emu.clone(); - } + #[cfg(target_arch = "riscv64")] + pub fn vplic(&self) -> &VPlic { + if let Some(vplic) = self.inner_const.arch_intc_dev.as_ref() { + return vplic; } - EmuDevs::None + panic!("vm{} cannot find vgic", self.id()); } - // Get console dev by ipa. - pub fn emu_console_dev(&self, ipa: usize) -> EmuDevs { - for (idx, emu_dev_cfg) in self.config().emulated_device_list().iter().enumerate() { - if emu_dev_cfg.base_ipa == ipa { - return self.inner.lock().emu_devs[idx].clone(); - } - } - EmuDevs::None + #[cfg(target_arch = "riscv64")] + pub fn has_vplic(&self) -> bool { + self.inner_const.arch_intc_dev.is_some() } pub fn ncpu(&self) -> usize { - let vm_inner = self.inner.lock(); - vm_inner.ncpu + self.inner_const.config.cpu_allocated_bitmap() as usize } + // Whether there is a pass-through interrupt int_id pub fn has_interrupt(&self, int_id: usize) -> bool { - let mut vm_inner = self.inner.lock(); - vm_inner.int_bitmap.as_mut().unwrap().get(int_id) != 0 + self.inner_const.int_bitmap.get(int_id) != 0 } pub fn emu_has_interrupt(&self, int_id: usize) -> bool { @@ -659,26 +547,17 @@ impl Vm { } pub fn vcpuid_to_vcpu(&self, vcpuid: usize) -> Option { - let inner = self.inner.lock(); - inner.vcpu_list.iter().find(|vcpu| vcpu.id() == vcpuid).cloned() + self.vcpu_list().iter().find(|vcpu| vcpu.id() == vcpuid).cloned() } pub fn vcpuid_to_pcpuid(&self, vcpuid: usize) -> Result { - let vm_inner = self.inner.lock(); - if vcpuid < vm_inner.cpu_num { - let vcpu = vm_inner.vcpu_list[vcpuid].clone(); - drop(vm_inner); - Ok(vcpu.phys_id()) - } else { - Err(()) - } + self.vcpu_list().get(vcpuid).map(|vcpu| vcpu.phys_id()).ok_or(()) } pub fn pcpuid_to_vcpuid(&self, pcpuid: usize) -> Result { - let vm_inner = self.inner.lock(); - for vcpuid in 0..vm_inner.cpu_num { - if vm_inner.vcpu_list[vcpuid].phys_id() == pcpuid { - return Ok(vcpuid); + for vcpu in self.vcpu_list() { + if vcpu.phys_id() == pcpuid { + return Ok(vcpu.id()); } } Err(()) @@ -707,22 +586,11 @@ impl Vm { } pub fn show_pagetable(&self, ipa: usize) { - let vm_inner = self.inner.lock(); + let vm_inner = self.inner_mut.lock(); vm_inner.pt.as_ref().unwrap().show_pt(ipa); } - pub fn ready(&self) -> bool { - let vm_inner = self.inner.lock(); - vm_inner.ready - } - - pub fn set_ready(&self, _ready: bool) { - let mut vm_inner = self.inner.lock(); - vm_inner.ready = _ready; - } - pub fn get_vcpu_by_mpidr(&self, mpdir: usize) -> Option { - let inner = self.inner.lock(); let cpuid = if (mpdir >> 8) & 0xff != 0 { if cfg!(feature = "rk3588") { mpdir >> 8 @@ -732,107 +600,56 @@ impl Vm { } else { mpdir & 0xff }; - inner.vcpu_list.iter().find(|vcpu| vcpu.id() == cpuid).cloned() + self.vcpu_list().iter().find(|vcpu| vcpu.id() == cpuid).cloned() } -} - -#[repr(align(4096))] -pub struct VmInner { - pub id: usize, - pub ready: bool, - pub config: Option, - pub dtb: Option, - // memory config - pub pt: Option, - pub mem_region_num: usize, - pub pa_region: Vec, // Option<[VmPa; VM_MEM_REGION_MAX]>, - - // image config - pub entry_point: usize, - - // vcpu config - pub has_master: bool, - pub vcpu_list: Vec, - pub cpu_num: usize, - pub ncpu: usize, - - // interrupt - pub intc_dev_id: usize, - pub int_bitmap: Option>, - - // iommu - pub iommu_ctx_id: Option, - - // emul devs - pub emu_devs: Vec, - pub med_blk_id: Option, -} - -impl VmInner { - pub const fn default() -> VmInner { - VmInner { - id: 0, - ready: false, - config: None, - dtb: None, - pt: None, - mem_region_num: 0, - pa_region: Vec::new(), - entry_point: 0, - - has_master: false, - vcpu_list: Vec::new(), - cpu_num: 0, - ncpu: 0, - intc_dev_id: 0, - int_bitmap: Some(BitAlloc4K::default()), + pub fn ipa2pa(&self, ipa: usize) -> usize { + if ipa == 0 { + error!("vm_ipa2pa: VM {} access invalid ipa {:x}", self.id(), ipa); + return 0; + } - iommu_ctx_id: None, - emu_devs: Vec::new(), - med_blk_id: None, + for i in 0..self.mem_region_num() { + if in_range( + (ipa as isize - self.pa_offset(i) as isize) as usize, + self.pa_start(i), + self.pa_length(i), + ) { + return (ipa as isize - self.pa_offset(i) as isize) as usize; + } } - } - pub fn new(id: usize) -> VmInner { - VmInner { - id, - ready: false, - config: None, - dtb: None, - pt: None, - mem_region_num: 0, - pa_region: Vec::new(), - entry_point: 0, + error!("vm_ipa2pa: VM {} access invalid ipa {:x}", self.id(), ipa); + 0 + } +} - has_master: false, - vcpu_list: Vec::new(), - cpu_num: 0, - ncpu: 0, +static VM_LIST: Mutex>> = Mutex::new(Vec::new()); - intc_dev_id: 0, - int_bitmap: Some(BitAlloc4K::default()), - iommu_ctx_id: None, - emu_devs: Vec::new(), - med_blk_id: None, - } +#[inline] +pub fn vm_list_walker(mut f: F) +where + F: FnMut(&Arc), +{ + let vm_list = VM_LIST.lock(); + for vm in vm_list.iter() { + f(vm); } } -pub static VM_LIST: Mutex> = Mutex::new(Vec::new()); - -pub fn push_vm(id: usize) -> Result<(), ()> { +pub fn push_vm(id: usize, config: VmConfigEntry) -> Result, ()> { let mut vm_list = VM_LIST.lock(); if vm_list.iter().any(|x| x.id() == id) { error!("push_vm: vm {} already exists", id); Err(()) } else { - vm_list.push(Vm::new(id)); - Ok(()) + let vm = Vm::new(id, config); + vm_list.push(vm.clone()); + Ok(vm) } } -pub fn remove_vm(id: usize) -> Vm { +pub fn remove_vm(id: usize) -> Arc { let mut vm_list = VM_LIST.lock(); match vm_list.iter().position(|x| x.id() == id) { None => { @@ -842,7 +659,7 @@ pub fn remove_vm(id: usize) -> Vm { } } -pub fn vm(id: usize) -> Option { +pub fn vm(id: usize) -> Option> { let vm_list = VM_LIST.lock(); vm_list.iter().find(|&x| x.id() == id).cloned() } @@ -852,7 +669,7 @@ pub fn vm_list_size() -> usize { vm_list.len() } -pub fn vm_ipa2pa(vm: Vm, ipa: usize) -> usize { +pub fn vm_ipa2pa(vm: &Vm, ipa: usize) -> usize { if ipa == 0 { error!("vm_ipa2pa: VM {} access invalid ipa {:x}", vm.id(), ipa); return 0; @@ -872,58 +689,6 @@ pub fn vm_ipa2pa(vm: Vm, ipa: usize) -> usize { 0 } -pub fn vm_pa2ipa(vm: Vm, pa: usize) -> usize { - if pa == 0 { - error!("vm_pa2ipa: VM {} access invalid pa {:x}", vm.id(), pa); - return 0; - } - - for i in 0..vm.mem_region_num() { - if in_range(pa, vm.pa_start(i), vm.pa_length(i)) { - return (pa as isize + vm.pa_offset(i) as isize) as usize; - } - } - - error!("vm_pa2ipa: VM {} access invalid pa {:x}", vm.id(), pa); - 0 -} - -pub fn pa2ipa(pa_region: &[VmPa], pa: usize) -> usize { - if pa == 0 { - error!("pa2ipa: access invalid pa {:x}", pa); - return 0; - } - - for region in pa_region.iter() { - if in_range(pa, region.pa_start, region.pa_length) { - return (pa as isize + region.offset) as usize; - } - } - - error!("pa2ipa: access invalid pa {:x}", pa); - 0 -} - -pub fn ipa2pa(pa_region: &[VmPa], ipa: usize) -> usize { - if ipa == 0 { - // println!("ipa2pa: access invalid ipa {:x}", ipa); - return 0; - } - - for region in pa_region.iter() { - if in_range( - (ipa as isize - region.offset) as usize, - region.pa_start, - region.pa_length, - ) { - return (ipa as isize - region.offset) as usize; - } - } - - // println!("ipa2pa: access invalid ipa {:x}", ipa); - 0 -} - pub fn cpuid2mpidr(cpuid: usize) -> usize { if cfg!(feature = "rk3588") { 0x81000000 | (cpuid << 8) diff --git a/src/lib.rs b/src/lib.rs index 6fb698ba355d657140d51ef3b8ac3b3b078dcca5..6a663e531bdc0d652a8847b8b6bce2cf70bb0be9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,9 @@ #![feature(extract_if)] #![feature(inline_const)] #![feature(naked_functions)] +#![feature(stdsimd)] +// use hlv.w instr +#![cfg_attr(target_arch = "riscv64", feature(riscv_ext_intrinsics))] #![feature(asm_const)] #![feature(error_in_core)] #![feature(slice_group_by)] @@ -183,9 +186,14 @@ pub fn init(dtb: &mut fdt::myctypes::c_void) { } iommu_init(); cpu_init(); + info!("cpu init ok"); + interrupt_init(); + info!("interrupt init ok"); + timer_init(); cpu_sched_init(); + info!("sched init ok"); #[cfg(not(feature = "secondary_start"))] crate::utils::barrier(); @@ -204,11 +212,15 @@ pub fn init(dtb: &mut fdt::myctypes::c_void) { // Other cores will execute this function pub fn secondary_init(mpidr: usize) { + info!("secondary core {:#x} init", mpidr); cpu_init(); interrupt_init(); + info!("secondary core {:#x} interrupt init", mpidr); timer_init(); cpu_sched_init(); + info!("[boot] sched init ok at core {:#x}", mpidr); + #[cfg(not(feature = "secondary_start"))] crate::utils::barrier(); use crate::arch::guest_cpu_on; diff --git a/src/macros.rs b/src/macros.rs index b242b5c3c021b5a96dc7c0ac1d7b69e4fb2f53c2..8e7f2f17b4f3a11393427b20ebbd7444c994b95f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,8 +30,8 @@ macro_rules! print { /// ``` #[macro_export] macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); + () => (print!("\n")); + ($($arg:tt)*) => (print!("{}\n", format_args!($($arg)*))); } /// A macro for declaring an enum with associated handlers. diff --git a/src/mm/heap.rs b/src/mm/heap.rs index 7a4878b6eb1ff6e6f9fcfeb23cd34e4ae9deffb5..4246305341e54bb477458211a3a9cbd00b12c85b 100644 --- a/src/mm/heap.rs +++ b/src/mm/heap.rs @@ -8,12 +8,17 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use buddy_system_allocator::LockedHeap; - // rCore buddy system allocator -use crate::arch::PAGE_SIZE; +use core::sync::atomic::{AtomicBool, Ordering}; +use buddy_system_allocator::{Heap, LockedHeapWithRescue}; + +use crate::{arch::PAGE_SIZE, board::PLAT_DESC, utils::round_up}; -const HEAP_SIZE: usize = 10 * 1024 * PAGE_SIZE; // 40MB +extern "C" { + fn _image_end(); +} +const HEAP_SIZE: usize = 1024 * PAGE_SIZE; // 4MB +static ALLOCED: AtomicBool = AtomicBool::new(false); #[repr(align(4096))] struct HeapRegion([u8; HEAP_SIZE]); @@ -22,7 +27,7 @@ static mut HEAP_REGION: HeapRegion = HeapRegion([0; HEAP_SIZE]); #[global_allocator] /// Global heap allocator -pub static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::empty(); +static HEAP_ALLOCATOR: LockedHeapWithRescue<32> = LockedHeapWithRescue::new(heap_rescue); /// Initialize heap allocator pub fn heap_init() { @@ -30,8 +35,9 @@ pub fn heap_init() { // HEAP_REGION is aligned and HEAP_SIZE is a multiple of PAGE_SIZE unsafe { println!( - "init buddy system, heap start from {:x}", - HEAP_REGION.0.as_mut_ptr() as usize + "init buddy system, heap start from {:x} with size {} MB", + HEAP_REGION.0.as_mut_ptr() as usize, + HEAP_SIZE / (1024 * 1024) ); HEAP_ALLOCATOR .lock() @@ -39,7 +45,26 @@ pub fn heap_init() { } } +fn heap_rescue(heap: &mut Heap<32>, layout: &core::alloc::Layout) { + if !ALLOCED.fetch_or(true, Ordering::SeqCst) { + // SAFETY: + // The '_image_end' is the end of kernel image and we will use 'round_up' to make it page aligned. + // The new heap range is valid and not overlap with the old one. + unsafe { + heap.add_to_heap( + round_up(_image_end as usize, PAGE_SIZE), + PLAT_DESC.mem_desc.regions[0].size + PLAT_DESC.mem_desc.regions[0].base, + ); + } + } else { + panic!( + "Rescure failed! MemoryRegion_0 has been exhausted\nOut Of Memory: Heap allocation error, layout = {:x?}", + layout + ) + } +} + #[alloc_error_handler] -fn alloc_error_handler(_: core::alloc::Layout) -> ! { - panic!("alloc_error_handler: heap Out Of Memory"); +fn alloc_error_handler(layout: core::alloc::Layout) -> ! { + panic!("Out Of Memory: Heap allocation error, layout = {:x?}", layout); } diff --git a/src/mm/page_frame.rs b/src/mm/page_frame.rs index cbe1b6ca62d04c1fa8bc1bc8bcdf48e9c4f76a9c..287e0ac9385f72a01ba45d0356d8d80e3e2c7ece 100644 --- a/src/mm/page_frame.rs +++ b/src/mm/page_frame.rs @@ -7,28 +7,39 @@ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use alloc::alloc; +use core::alloc::Layout; use crate::arch::PAGE_SIZE; -use crate::kernel::{mem_heap_free, mem_heap_alloc, AllocError}; +use crate::kernel::AllocError; #[derive(Debug)] /// PageFrame struct represents a page frame, consisting of physical address and page number. pub struct PageFrame { pub pa: usize, - pub page_num: usize, + layout: Layout, } impl PageFrame { - pub fn new(pa: usize, page_num: usize) -> Self { - assert_eq!(pa % PAGE_SIZE, 0); - PageFrame { pa, page_num } + pub fn new(pa: usize, layout: Layout) -> Self { + Self { pa, layout } } /// Allocate a page frame with given page number. - pub fn alloc_pages(page_num: usize) -> Result { - match mem_heap_alloc(page_num, false) { - Ok(pa) => Ok(Self::new(pa, page_num)), - Err(err) => Err(err), + pub fn alloc_pages(page_num: usize, align_page: usize) -> Result { + if page_num == 0 { + return Err(AllocError::AllocZeroPage); + } + + match Layout::from_size_align(page_num * PAGE_SIZE, align_page * PAGE_SIZE) { + Ok(layout) => { + let pa = unsafe { alloc::alloc_zeroed(layout) as usize }; + Ok(Self::new(pa, layout)) + } + Err(err) => { + error!("alloc_pages: Layout error {}", err); + Err(AllocError::OutOfFrame) + } } } @@ -39,6 +50,10 @@ impl PageFrame { impl Drop for PageFrame { fn drop(&mut self) { - mem_heap_free(self.pa, 1); + debug!( + "drop page frame: {:#x}, with Layput {:x?} page(s)", + self.pa, self.layout + ); + unsafe { alloc::dealloc(self.pa as *mut u8, self.layout) } } } diff --git a/src/panic.rs b/src/panic.rs index fdeae81515e046b724d7a4c0fde74fcbba509e52..a9c370eaea2b359956bb2cff317902482c8faaa1 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -12,10 +12,12 @@ use core::panic::PanicInfo; +use crate::kernel::current_cpu; + #[cfg_attr(target_os = "none", panic_handler)] fn panic(info: &PanicInfo) -> ! { - println!("[Panic]"); - println!("{}", info); + println!("\u{1B}[1;31m[Panic] core {}", current_cpu().id); // 1;31 BrightRed + println!("{}\u{1B}[0m", info); loop { core::hint::spin_loop(); } diff --git a/src/utils/bitmap.rs b/src/utils/bitmap.rs index 716dc893eec6e3254cc684952d968f33e0f10742..c1e81a377ff7e3149fa526c43434b98e1d804f89 100644 --- a/src/utils/bitmap.rs +++ b/src/utils/bitmap.rs @@ -181,7 +181,7 @@ impl FlexBitmap { if bit + len > self.len { panic!("set_bits: too large idx {} for set bitmap", bit); } - // 默认2MB或1KB对齐 + // The default is 2MB or 1KB alignment if len == 1 { self.set(bit, val); } else { diff --git a/src/utils/downcast.rs b/src/utils/downcast.rs new file mode 100644 index 0000000000000000000000000000000000000000..74979d3e472622445101faca85fa048ad09d64df --- /dev/null +++ b/src/utils/downcast.rs @@ -0,0 +1,22 @@ +use alloc::sync::Arc; +use core::any::Any; + +pub trait Downcast { + fn as_any(&self) -> &dyn Any; +} + +impl Downcast for T { + fn as_any(&self) -> &dyn Any { + self + } +} + +pub trait DowncastSync: Downcast + Send + Sync { + fn into_any_arc(self: Arc) -> Arc; +} + +impl DowncastSync for T { + fn into_any_arc(self: Arc) -> Arc { + self + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 469c0c9bb0516bf51926f8e5166757086986b1e2..81b025e8969345f8fa41040f4eb9aaec2f4ec5bd 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -20,6 +20,7 @@ pub use self::util::*; mod barrier; mod bitmap; pub mod device_ref; +pub mod downcast; pub mod interval; mod print; mod string; diff --git a/src/utils/print.rs b/src/utils/print.rs index a81bf7f02e75934590262a0c28f8dd36966bd2f9..b822ef9823aa1adea1de910ad0fd040541176783 100644 --- a/src/utils/print.rs +++ b/src/utils/print.rs @@ -9,7 +9,6 @@ // See the Mulan PSL v2 for more details. use core::fmt::{Arguments, Write}; - use spin::Mutex; pub struct Writer; @@ -19,7 +18,10 @@ static WRITER: Mutex = Mutex::new(Writer); impl Write for Writer { fn write_str(&mut self, s: &str) -> core::fmt::Result { for b in s.bytes() { + #[cfg(target_arch = "aarch64")] crate::driver::putc(b); + #[cfg(target_arch = "riscv64")] + sbi::legacy::console_putchar(b); } Ok(()) } diff --git a/src/vmm/init.rs b/src/vmm/init.rs index 816db07e21c0dbdde73f0f6625c4aabdf7b4bff8..50576df64c59e3aa657d4ae119763adea4cc4e22 100644 --- a/src/vmm/init.rs +++ b/src/vmm/init.rs @@ -8,34 +8,35 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use alloc::string::String; use alloc::vec::Vec; +use alloc::sync::Arc; -use crate::arch::{ - emu_intc_handler, emu_intc_init, emu_smmu_handler, partial_passthrough_intc_handler, partial_passthrough_intc_init, -}; -#[cfg(feature = "gicv3")] -use crate::arch::{vgic_icc_sre_handler, vgic_icc_sgir_handler, emu_vgicr_init, emul_vgicr_handler}; +use crate::arch::is_boot_core; use crate::arch::{PTE_S2_DEVICE, PTE_S2_NORMAL, PTE_S2_NORMALNOCACHE}; -use crate::arch::PAGE_SIZE; #[cfg(not(feature = "gicv3"))] use crate::board::*; use crate::config::vm_cfg_entry; -use crate::device::{emu_register_dev, emu_virtio_mmio_handler, emu_virtio_mmio_init, meta}; -#[cfg(feature = "gicv3")] -use crate::device::{emu_register_reg, EmuRegType}; +use crate::config::VmConfigEntry; use crate::device::create_fdt; use crate::device::EmuDeviceType::*; -use crate::kernel::{ - add_async_used_info, cpu_idle, current_cpu, iommmu_vm_init, shyper_init, vm_if_init_mem_map, VM_IF_LIST, VmPa, - VmType, iommu_add_device, -}; -use crate::kernel::{mem_page_alloc, mem_vm_region_alloc}; -use crate::kernel::{vm, Vm}; +use crate::kernel::iommmu_vm_init; +use crate::kernel::ipi_send_msg; +use crate::kernel::IpiVmmPercoreMsg; +use crate::kernel::{add_async_used_info, cpu_idle, current_cpu, VmPa, VmType, iommu_add_device, IpiType, IpiInnerMsg}; +use crate::kernel::mem_vm_region_alloc; +#[cfg(target_arch = "riscv64")] +use crate::kernel::mem_pages_alloc_align; +#[cfg(target_arch = "aarch64")] +use crate::kernel::mem_page_alloc; +use crate::kernel::Vm; use crate::kernel::{active_vcpu_id, vcpu_run}; use crate::kernel::interrupt_vm_register; use crate::kernel::VM_NUM_MAX; use crate::utils::trace; use crate::error::Result; +use crate::vmm::VmmPercoreEvent; +use fdt::*; use fdt::binding::*; @@ -44,11 +45,17 @@ pub static CPIO_RAMDISK: &[u8] = include_bytes!("../../image/net_rootfs.cpio"); #[cfg(not(feature = "ramdisk"))] pub static CPIO_RAMDISK: &[u8] = &[]; -fn vmm_init_memory(vm: Vm) -> bool { - let result = mem_page_alloc(); +fn vmm_init_memory(vm: Arc) -> bool { let vm_id = vm.id(); let config = vm.config(); - let mut vm_mem_size: usize = 0; // size for pages + + // The aarch64 root page table only needs to allocate one page + #[cfg(target_arch = "aarch64")] + let result = mem_page_alloc(); + + // The riscv64 root page table needs to be allocated 4 consecutive 16KB aligned pages + #[cfg(target_arch = "riscv64")] + let result = mem_pages_alloc_align(4, 4); if let Ok(pt_dir_frame) = result { vm.set_pt(pt_dir_frame); @@ -60,7 +67,6 @@ fn vmm_init_memory(vm: Vm) -> bool { for vm_region in config.memory_region() { let pa = mem_vm_region_alloc(vm_region.length); - vm_mem_size += vm_region.length; if pa == 0 { error!("vmm_init_memory: vm memory region is not large enough"); @@ -72,19 +78,18 @@ fn vmm_init_memory(vm: Vm) -> bool { vm_id, vm_region.ipa_start, pa, vm_region.length ); vm.pt_map_range(vm_region.ipa_start, vm_region.length, pa, PTE_S2_NORMAL, false); - vm.add_region(VmPa { pa_start: pa, pa_length: vm_region.length, offset: vm_region.ipa_start as isize - pa as isize, }); + info!("successfully add a region!"); } - vm_if_init_mem_map(vm_id, (vm_mem_size + PAGE_SIZE - 1) / PAGE_SIZE); true } -pub fn vmm_load_image(vm: Vm, bin: &[u8]) { +pub fn vmm_load_image(vm: &Vm, bin: &[u8]) { let size = bin.len(); let config = vm.config(); let load_ipa = config.kernel_load_ipa(); @@ -109,6 +114,15 @@ pub fn vmm_load_image(vm: Vm, bin: &[u8]) { // The 'size' is the length of Image binary. let dst = unsafe { core::slice::from_raw_parts_mut((vm.pa_start(idx) + offset) as *mut u8, size) }; dst.clone_from_slice(bin); + + trace!( + "image dst bytes: {:#x} {:#x} {:#x} {:#x}", + dst[0], + dst[1], + dst[2], + dst[3] + ); + return; } panic!("vmm_load_image: Image config conflicts with memory config"); @@ -125,7 +139,7 @@ fn overlay_fdt(vm: &Vm, dtb: &[u8], overlay: &mut [u8]) -> Result { Ok(buf) } -pub fn vmm_init_image(vm: Vm) -> bool { +pub fn vmm_init_image(vm: &Vm) -> bool { let vm_id = vm.id(); let config = vm.config(); @@ -134,8 +148,6 @@ pub fn vmm_init_image(vm: Vm) -> bool { return false; } - vm.set_entry_point(config.kernel_entry_point()); - // Only load MVM kernel image "L4T" from binding. // Load GVM kernel image from shyper-cli, you may check it for more information. if config.os_type == VmType::VmTOs { @@ -157,11 +169,11 @@ pub fn vmm_init_image(vm: Vm) -> bool { _binary_vm0img_size as usize, ) }; - vmm_load_image(vm.clone(), vm0image); + vmm_load_image(vm, vm0image); } else if name == "Image_vanilla" { info!("VM {} loading default Linux Image", vm.id()); #[cfg(feature = "static-config")] - vmm_load_image(vm.clone(), include_bytes!("../../image/Image_vanilla")); + vmm_load_image(vm, include_bytes!("../../image/Image_vanilla")); #[cfg(not(feature = "static-config"))] info!("*** Please enable feature `static-config`"); } else { @@ -175,6 +187,7 @@ pub fn vmm_init_image(vm: Vm) -> bool { fn _binary_vm0img_start(); fn _binary_vm0img_size(); } + info!("MVM {} loading Image", vm.id()); // SAFETY: // The '_binary_vm0img_start' and '_binary_vm0img_size' are valid from linker script. let vm0image = unsafe { @@ -183,7 +196,7 @@ pub fn vmm_init_image(vm: Vm) -> bool { _binary_vm0img_size as usize, ) }; - vmm_load_image(vm.clone(), vm0image); + vmm_load_image(vm, vm0image); } #[cfg(feature = "rk3588")] if name == "Linux-5.10" { @@ -200,11 +213,11 @@ pub fn vmm_init_image(vm: Vm) -> bool { _binary_vm0img_size as usize, ) }; - vmm_load_image(vm.clone(), vm0image); + vmm_load_image(vm, vm0image); } else if name == "Image_vanilla" { info!("VM {} loading default Linux Image", vm.id()); #[cfg(feature = "static-config")] - vmm_load_image(vm.clone(), include_bytes!("../../image/Image_vanilla")); + vmm_load_image(vm, include_bytes!("../../image/Image_vanilla")); #[cfg(not(feature = "static-config"))] info!("*** Please enable feature `static-config`"); } else { @@ -223,24 +236,23 @@ pub fn vmm_init_image(vm: Vm) -> bool { // Init dtb for MVM. use crate::SYSTEM_FDT; let offset = config.device_tree_load_ipa() - config.memory_region()[0].ipa_start; - debug!("MVM[{}] dtb addr 0x{:x}", vm_id, vm.pa_start(0) + offset); - vm.set_dtb((vm.pa_start(0) + offset) as *mut fdt::myctypes::c_void); // SAFETY: // Offset is computed from config.device_tree_load_ipa() and config.memory_region()[0].ipa_start which are both valid. // The 'vm.pa_start(0) + offset' is in range of our memory configuration. - // The 'vm.dtb' have been set to vm.pa_start(0) + offset which is in range of our memory configuration. + // The 'dtb' have been set to vm.pa_start(0) + offset which is in range of our memory configuration. unsafe { let src = SYSTEM_FDT.get().unwrap(); let len = src.len(); + trace!("fdt_len: {:08x}", len); let dst = core::slice::from_raw_parts_mut((vm.pa_start(0) + offset) as *mut u8, len); dst.clone_from_slice(src); - vmm_setup_fdt(vm.clone()); + vmm_setup_fdt(vm.config(), dst.as_mut_ptr() as *mut _); } } else { // Init dtb for GVM. - match create_fdt(config.clone()) { + match create_fdt(config) { Ok(dtb) => { - let mut overlay = config.fdt_overlay.lock(); + let mut overlay = config.fdt_overlay.clone(); let offset = config.device_tree_load_ipa() - vm.config().memory_region()[0].ipa_start; let target = (vm.pa_start(0) + offset) as *mut u8; debug!( @@ -257,7 +269,7 @@ pub fn vmm_init_image(vm: Vm) -> bool { core::ptr::copy_nonoverlapping(dtb.as_ptr(), target, dtb.len()); } } else { - let buf = match overlay_fdt(&vm, &dtb, &mut overlay) { + let buf = match overlay_fdt(vm, &dtb, &mut overlay) { Ok(x) => x, Err(e) => { error!("overlay_fdt failed: {:?}", e); @@ -274,8 +286,8 @@ pub fn vmm_init_image(vm: Vm) -> bool { } } } - _ => { - panic!("vmm_setup_config: create fdt for vm{} fail", vm.id()); + Err(err) => { + panic!("vmm_setup_config: create fdt for vm{} fail, err: {}", vm.id(), err); } } } @@ -304,149 +316,7 @@ pub fn vmm_init_image(vm: Vm) -> bool { true } -fn vmm_init_emulated_device(vm: Vm) -> bool { - let config: Vec = vm.config().emulated_device_list(); - - for (idx, emu_dev) in config.iter().enumerate() { - match emu_dev.emu_type { - EmuDeviceTGicd => { - vm.set_intc_dev_id(idx); - emu_register_dev( - EmuDeviceTGicd, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - emu_intc_handler, - ); - emu_intc_init(vm.clone(), idx); - } - EmuDeviceTGPPT => { - vm.set_intc_dev_id(idx); - emu_register_dev( - EmuDeviceTGPPT, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - partial_passthrough_intc_handler, - ); - partial_passthrough_intc_init(vm.clone()); - } - EmuDeviceTVirtioBlk => { - emu_register_dev( - EmuDeviceTVirtioBlk, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - emu_virtio_mmio_handler, - ); - if !emu_virtio_mmio_init(vm.clone(), idx, emu_dev.mediated) { - return false; - } - } - EmuDeviceTVirtioNet => { - emu_register_dev( - EmuDeviceTVirtioNet, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - emu_virtio_mmio_handler, - ); - if !emu_virtio_mmio_init(vm.clone(), idx, emu_dev.mediated) { - return false; - } - let mut vm_if_list = VM_IF_LIST[vm.id()].lock(); - for i in 0..6 { - vm_if_list.mac[i] = emu_dev.cfg_list[i] as u8; - } - drop(vm_if_list); - } - EmuDeviceTVirtioConsole => { - emu_register_dev( - EmuDeviceTVirtioConsole, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - emu_virtio_mmio_handler, - ); - if !emu_virtio_mmio_init(vm.clone(), idx, emu_dev.mediated) { - return false; - } - } - EmuDeviceTIOMMU => { - emu_register_dev( - EmuDeviceTIOMMU, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - emu_smmu_handler, - ); - if !iommmu_vm_init(vm.clone()) { - return false; - } - } - EmuDeviceTShyper => { - if !shyper_init(vm.clone(), emu_dev.base_ipa, emu_dev.length) { - return false; - } - } - #[cfg(feature = "gicv3")] - EmuDeviceTICCSRE => { - emu_register_reg(EmuRegType::SysReg, emu_dev.base_ipa, vgic_icc_sre_handler); - } - #[cfg(feature = "gicv3")] - EmuDeviceTSGIR => { - emu_register_reg(EmuRegType::SysReg, emu_dev.base_ipa, vgic_icc_sgir_handler); - } - #[cfg(feature = "gicv3")] - EmuDeviceTGICR => { - emu_register_dev( - EmuDeviceTGICR, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - emul_vgicr_handler, - ); - emu_vgicr_init(vm.clone(), idx); - } - EmuDeviceTMeta => { - if meta::register(idx, &vm, emu_dev).is_ok() { - emu_register_dev( - EmuDeviceTMeta, - vm.id(), - idx, - emu_dev.base_ipa, - emu_dev.length, - meta::emu_meta_handler, - ); - } else { - return false; - } - } - _ => { - warn!("vmm_init_emulated_device: unknown emulated device"); - return false; - } - } - info!( - "VM {} registers emulated device: id=<{}>, name=\"{}\", ipa=<0x{:x}>", - vm.id(), - idx, - emu_dev.emu_type, - emu_dev.base_ipa - ); - } - - true -} - -fn vmm_init_passthrough_device(vm: Vm) -> bool { +fn vmm_init_passthrough_device(vm: Arc) -> bool { for region in vm.config().passthrough_device_regions() { // TODO: specify the region property more accurately. // The 'dev_property' in a device region means cacheable here. @@ -466,108 +336,215 @@ fn vmm_init_passthrough_device(vm: Vm) -> bool { ); } for irq in vm.config().passthrough_device_irqs() { - if !interrupt_vm_register(vm.clone(), irq) { + if !interrupt_vm_register(&vm, *irq) { return false; } } true } -fn vmm_init_iommu_device(vm: Vm) -> bool { +fn vmm_init_iommu_device(vm: Arc) -> bool { + for emu_cfg in vm.config().emulated_device_list().iter() { + if emu_cfg.emu_type == EmuDeviceTIOMMU { + if !iommmu_vm_init(&vm) { + return false; + } else { + break; + } + } + } for stream_id in vm.config().passthrough_device_stread_ids() { - if stream_id == 0 { + if *stream_id == 0 { break; } - if !iommu_add_device(vm.clone(), stream_id) { + if !iommu_add_device(&vm, *stream_id) { return false; } } true } +/// Add a virtio node to fdt for riscv64 +/// # Safety: +/// 1. 'dtb' is a valid pointer to a device tree blob +/// 2. 'name' is a string not too long +/// 3. 'irq_id' is a valid interrupt id +/// 4. 'base_ipa' is a valid ipa +unsafe fn fdt_add_virtio_riscv64( + dtb: *mut fdt::myctypes::c_void, + name: String, + irq_id: u32, + base_ipa: u64, + length: u64, +) { + let node = fdt_create_node(dtb, "/soc\0".as_ptr(), name.as_ptr()); + if node < 0 { + 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); + } + + 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 mut regs = [base_ipa, length]; + let ret = fdt_add_property_u64_array(dtb, node, "reg\0".as_ptr(), regs.as_mut_ptr(), 2); + if ret < 0 { + panic!("fdt_add_property_u64_array failed {}", ret); + } + + fdt_add_property_string(dtb, node, "compatible\0".as_ptr(), "virtio,mmio\0".as_ptr()); + trace!("fdt_add_virtio: {} irq = {}", name, irq_id); +} + +/// Add a vm_service node to fdt for riscv64 +/// # Safety: +/// 1. 'dtb' is a valid pointer to a device tree blob +/// 2. 'irq_id' is a valid interrupt id +/// 3. 'base_ipa' is a valid ipa +unsafe fn fdt_add_vm_service_riscv64(dtb: *mut fdt::myctypes::c_void, irq_id: u32, base_ipa: u64, length: u64) { + let node = fdt_create_node(dtb, "/soc\0".as_ptr(), "vm_service\0".as_ptr()); + if node < 0 { + panic!("fdt_create_node failed {}", node); + } + + let ret = fdt_add_property_string(dtb, node, "compatible\0".as_ptr(), "shyper\0".as_ptr()); + if ret < 0 { + 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); + } + + let mut regs = [base_ipa, length]; + let ret = fdt_add_property_u64_array(dtb, node, "reg\0".as_ptr(), regs.as_mut_ptr(), 2); + 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 /// # Safety: /// This function is unsafe because it trusts the caller to pass a valid pointer to a valid dtb. /// So the caller must ensure that the vm.dtb() have configured correctly before calling this function. -pub unsafe fn vmm_setup_fdt(vm: Vm) { +pub unsafe fn vmm_setup_fdt(config: &VmConfigEntry, dtb: *mut fdt::myctypes::c_void) { use fdt::*; - let config = vm.config(); - match vm.dtb() { - Some(dtb) => { - let mut mr = Vec::new(); - for r in config.memory_region() { - mr.push(region { - ipa_start: r.ipa_start as u64, - length: r.length as u64, - }); - } - #[cfg(feature = "tx2")] - fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@90000000\0".as_ptr()); - #[cfg(feature = "pi4")] - fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@200000\0".as_ptr()); - #[cfg(feature = "qemu")] - fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@50000000\0".as_ptr()); - #[cfg(feature = "rk3588")] - fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@10000000\0".as_ptr()); - // FDT+TIMER - //fdt_add_timer(dtb, 0x04); - // FDT+BOOTCMD - fdt_set_bootcmd(dtb, config.cmdline.as_ptr()); - #[cfg(feature = "tx2")] - fdt_set_stdout_path(dtb, "/serial@3100000\0".as_ptr()); - // #[cfg(feature = "pi4")] - // fdt_set_stdout_path(dtb, "/serial@fe340000\0".as_ptr()); - #[cfg(feature = "rk3588")] - fdt_set_stdout_path(dtb, "/serial@feba0000\0".as_ptr()); - - if !config.emulated_device_list().is_empty() { - for emu_cfg in config.emulated_device_list() { - match emu_cfg.emu_type { - EmuDeviceTGicd | EmuDeviceTGPPT => { - print!("trace fdt_setup_gic\n"); - #[cfg(not(feature = "gicv3"))] - #[cfg(any(feature = "tx2", feature = "qemu"))] - fdt_setup_gic( + let mut mr = Vec::new(); + for r in config.memory_region() { + mr.push(region { + ipa_start: r.ipa_start as u64, + length: r.length as u64, + }); + } + #[cfg(feature = "tx2")] + fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@90000000\0".as_ptr()); + #[cfg(feature = "pi4")] + fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@200000\0".as_ptr()); + #[cfg(all(feature = "qemu", target_arch = "aarch64"))] + fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@50000000\0".as_ptr()); + #[cfg(all(feature = "qemu", target_arch = "riscv64"))] + fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@90000000\0".as_ptr()); + #[cfg(feature = "rk3588")] + fdt_set_memory(dtb, mr.len() as u64, mr.as_ptr(), "memory@10000000\0".as_ptr()); + // FDT+TIMER + //fdt_add_timer(dtb, 0x04); + // FDT+BOOTCMD + fdt_set_bootcmd(dtb, config.cmdline.as_ptr()); + #[cfg(feature = "tx2")] + fdt_set_stdout_path(dtb, "/serial@3100000\0".as_ptr()); + #[cfg(feature = "rk3588")] + fdt_set_stdout_path(dtb, "/serial@feba0000\0".as_ptr()); + + if !config.emulated_device_list().is_empty() { + for emu_cfg in config.emulated_device_list() { + match emu_cfg.emu_type { + EmuDeviceTGicd | EmuDeviceTGPPT => { + #[cfg(not(feature = "gicv3"))] + #[cfg(any(feature = "tx2", feature = "qemu"))] + fdt_setup_gic( + dtb, + Platform::GICD_BASE as u64, + Platform::GICC_BASE as u64, + emu_cfg.name.as_ptr(), + ); + #[cfg(feature = "pi4")] + let _r = fdt_setup_gic( + dtb, + (Platform::GICD_BASE | 0xF_0000_0000) as u64, + (Platform::GICC_BASE | 0xF_0000_0000) as u64, + emu_cfg.name.as_ptr(), + ); + } + EmuDeviceTVirtioNet | EmuDeviceTVirtioConsole => { + cfg_if::cfg_if! { + if #[cfg(all(any(feature = "tx2", feature = "qemu", feature = "rk3588"), target_arch = "aarch64"))] { + fdt_add_virtio( dtb, - Platform::GICD_BASE as u64, - Platform::GICC_BASE as u64, emu_cfg.name.as_ptr(), + emu_cfg.irq_id as u32 - 0x20, + emu_cfg.base_ipa as u64, ); - #[cfg(feature = "pi4")] - let _r = fdt_setup_gic( + info!("apply aarch64"); + } else if #[cfg(target_arch = "riscv64")] { + fdt_add_virtio_riscv64( dtb, - (Platform::GICD_BASE | 0xF_0000_0000) as u64, - (Platform::GICC_BASE | 0xF_0000_0000) as u64, - emu_cfg.name.as_ptr(), + emu_cfg.name.clone(), + emu_cfg.irq_id as u32, + emu_cfg.base_ipa as u64, + emu_cfg.length as u64, ); } - EmuDeviceTVirtioNet | EmuDeviceTVirtioConsole => { - #[cfg(any(feature = "tx2", feature = "qemu", feature = "rk3588"))] - fdt_add_virtio( + } + } + EmuDeviceTShyper => { + // Add vm_service node, in order to provide kernel module information about irq_id + info!("fdt add vm_service irq = {}", emu_cfg.irq_id); + + cfg_if::cfg_if! { + if #[cfg(all(any(feature = "tx2", feature = "qemu", feature = "rk3588"), target_arch = "aarch64"))] { + fdt_add_vm_service( dtb, - emu_cfg.name.as_ptr(), emu_cfg.irq_id as u32 - 0x20, emu_cfg.base_ipa as u64, + emu_cfg.length as u64, ); - } - EmuDeviceTShyper => { - #[cfg(any(feature = "tx2", feature = "qemu", feature = "rk3588"))] - fdt_add_vm_service( + } else if #[cfg(target_arch = "riscv64")] { + fdt_add_vm_service_riscv64( dtb, - emu_cfg.irq_id as u32 - 0x20, + emu_cfg.irq_id as u32, emu_cfg.base_ipa as u64, emu_cfg.length as u64, ); } - _ => {} } } + _ => {} } - debug!("after dtb size {}", fdt_size(dtb)); - } - None => { - warn!("None dtb"); } } + debug!("after dtb size {}", fdt_size(dtb)); } /* Setup VM Configuration before boot. @@ -576,14 +553,8 @@ pub unsafe fn vmm_setup_fdt(vm: Vm) { * * @param[in] vm_id: target VM id to set up config. */ -pub fn vmm_setup_config(vm_id: usize) { - let vm = match vm(vm_id) { - Some(vm) => vm, - None => { - panic!("vmm_setup_config vm id {} doesn't exist", vm_id); - } - }; - +pub fn vmm_setup_config(vm: Arc) { + let vm_id = vm.id(); let config = match vm_cfg_entry(vm_id) { Some(config) => config, None => { @@ -598,6 +569,9 @@ pub fn vmm_setup_config(vm_id: usize) { current_cpu().id ); + // need ipi, must after push to global list + vmm_init_cpu(vm.clone()); + if vm_id >= VM_NUM_MAX { panic!("vmm_setup_config: out of vm"); } @@ -605,13 +579,9 @@ pub fn vmm_setup_config(vm_id: usize) { panic!("vmm_setup_config: vmm_init_memory failed"); } - if !vmm_init_image(vm.clone()) { + if !vmm_init_image(&vm) { panic!("vmm_setup_config: vmm_init_image failed"); } - - if !vmm_init_emulated_device(vm.clone()) { - panic!("vmm_setup_config: vmm_init_emulated_device failed"); - } if !vmm_init_passthrough_device(vm.clone()) { panic!("vmm_setup_config: vmm_init_passthrough_device failed"); } @@ -623,52 +593,54 @@ pub fn vmm_setup_config(vm_id: usize) { info!("VM {} id {} init ok", vm.id(), vm.config().name); } -pub fn vmm_cpu_assign_vcpu(vm_id: usize) { - let cpu_id = current_cpu().id; - if current_cpu().assigned() { - debug!("vmm_cpu_assign_vcpu vm[{}] cpu {} is assigned", vm_id, cpu_id); - } - - // let cpu_config = vm(vm_id).config().cpu; - let vm = vm(vm_id).unwrap(); - let cfg_master = vm.config().cpu_master(); - let cfg_cpu_num = vm.config().cpu_num(); - let cfg_cpu_allocate_bitmap = vm.config().cpu_allocated_bitmap(); - - if cfg_cpu_num != cfg_cpu_allocate_bitmap.count_ones() as usize { - panic!( - "vmm_cpu_assign_vcpu: VM[{}] cpu_num {} not match cpu_allocated_bitmap {:#b}", - vm_id, cfg_cpu_num, cfg_cpu_allocate_bitmap - ); - } - +fn vmm_init_cpu(vm: Arc) { + let vm_id = vm.id(); info!( - "vmm_cpu_assign_vcpu: vm[{}] cpu {} cfg_master {} cfg_cpu_num {} cfg_cpu_allocate_bitmap {:#b}", - vm_id, cpu_id, cfg_master, cfg_cpu_num, cfg_cpu_allocate_bitmap + "VM {} init cpu: cores=<{}>, allocat_bits=<{:#b}>", + vm.id(), + vm.config().cpu_num(), + vm.config().cpu_allocated_bitmap() ); - // Judge if current cpu is allocated. - if (cfg_cpu_allocate_bitmap & (1 << cpu_id)) != 0 { - let vcpu = match vm.select_vcpu2assign(cpu_id) { - None => panic!("core {} vm {} cannot find proper vcpu to assign", cpu_id, vm_id), - Some(vcpu) => vcpu, - }; - if vcpu.id() == 0 { - info!("* Core {} is assigned => vm {}, vcpu {}", cpu_id, vm_id, vcpu.id()); + for vcpu in vm.vcpu_list() { + let target_cpu_id = vcpu.phys_id(); + if target_cpu_id != current_cpu().id { + let m = IpiVmmPercoreMsg { + vm: vm.clone(), + event: VmmPercoreEvent::VmmAssignCpu, + }; + if !ipi_send_msg(target_cpu_id, IpiType::IpiTVMM, IpiInnerMsg::VmmPercoreMsg(m)) { + error!("vmm_init_cpu: failed to send ipi to Core {}", target_cpu_id); + } } else { - info!("Core {} is assigned => vm {}, vcpu {}", cpu_id, vm_id, vcpu.id()); + vmm_assign_vcpu_percore(&vm); } - current_cpu().vcpu_array.append_vcpu(vcpu); } - #[cfg(not(feature = "secondary_start"))] - if cfg_cpu_num == vm.cpu_num() { - vm.set_ready(true); + info!("vmm_init_cpu: VM [{}] is ready", vm_id); +} + +pub fn vmm_assign_vcpu_percore(vm: &Vm) { + let cpu_id = current_cpu().id; + if current_cpu().assigned() { + debug!("vmm_cpu_assign_vcpu vm[{}] cpu {} is assigned", vm.id(), cpu_id); + } + + for vcpu in vm.vcpu_list() { + if vcpu.phys_id() == current_cpu().id { + if vcpu.id() == 0 { + info!("* Core {} is assigned => vm {}, vcpu {}", cpu_id, vm.id(), vcpu.id()); + } else { + info!("Core {} is assigned => vm {}, vcpu {}", cpu_id, vm.id(), vcpu.id()); + } + current_cpu().vcpu_array.append_vcpu(vcpu.clone()); + break; + } } } pub fn vm_init() { - if current_cpu().id == 0 { + if is_boot_core(current_cpu().id) { // Set up basic config. crate::config::mvm_config_init(); // Add VM 0 diff --git a/src/vmm/manager.rs b/src/vmm/manager.rs index d4c63e7578f4c27ba9980b7ea25cfcb1030a373b..3f695635ed2864e0c7c46e9cea75164ef58e107b 100644 --- a/src/vmm/manager.rs +++ b/src/vmm/manager.rs @@ -8,19 +8,18 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use alloc::sync::Arc; use alloc::vec::Vec; -use crate::arch::traits::InterruptController; -use crate::arch::{power_arch_vm_shutdown_secondary_cores, IntCtrl}; -use crate::board::PLATFORM_CPU_NUM_MAX; +use crate::arch::{is_boot_core, power_arch_vm_shutdown_secondary_cores, IntCtrl, InterruptController}; use crate::config::{vm_id_list, vm_num}; // use crate::config::{init_tmp_config_for_bma1, init_tmp_config_for_bma2, init_tmp_config_for_vm1, init_tmp_config_for_vm2}; use crate::config::NAME_MAX_LEN; use crate::config::vm_cfg_entry; use crate::config::vm_type; use crate::kernel::{ - active_vcpu_id, active_vm, current_cpu, push_vm, vm, Vm, vm_if_get_state, vm_if_set_ivc_arg, vm_if_set_ivc_arg_ptr, - vm_ipa2pa, VM_NUM_MAX, Scheduler, + active_vcpu_id, active_vm, current_cpu, push_vm, vm, vm_if_get_state, vm_if_set_ivc_arg, vm_if_set_ivc_arg_ptr, + vm_if_set_type, vm_ipa2pa, Scheduler, Vm, VM_NUM_MAX, }; use crate::kernel::{active_vm_id, vm_if_get_cpu_id}; use crate::kernel::{ipi_send_msg, IpiInnerMsg, IpiMessage, IpiType, IpiVmmMsg}; @@ -30,15 +29,18 @@ use crate::kernel::HVC_CONFIG_UPLOAD_KERNEL_IMAGE; use crate::kernel::HVC_VMM; use crate::kernel::HVC_VMM_REBOOT_VM; use crate::utils::{bit_extract, memcpy, memset}; -use crate::vmm::{vmm_cpu_assign_vcpu, vmm_boot, vmm_init_image, vmm_setup_config, vmm_cpu_remove_vcpu}; +use crate::vmm::{vmm_assign_vcpu_percore, vmm_boot, vmm_init_image, vmm_setup_config, vmm_cpu_remove_vcpu}; #[derive(Copy, Clone)] pub enum VmmEvent { VmmBoot, VmmReboot, VmmShutdown, +} + +#[derive(Copy, Clone)] +pub enum VmmPercoreEvent { VmmAssignCpu, - VmmAssignVcpu, VmmRemoveCpu, } @@ -50,134 +52,17 @@ pub fn vmm_shutdown_secondary_vm() { * * @param[in] vm_id: new added VM id. */ -pub fn vmm_push_vm(vm_id: usize) { +pub fn vmm_push_vm(vm_id: usize) -> Result, ()> { info!("vmm_push_vm: add vm {} on cpu {}", vm_id, current_cpu().id); - if push_vm(vm_id).is_err() { - return; - } - let vm = vm(vm_id).unwrap(); let vm_cfg = match vm_cfg_entry(vm_id) { Some(vm_cfg) => vm_cfg, None => { error!("vmm_push_vm: failed to find config for vm {}", vm_id); - return; - } - }; - vm.set_config_entry(Some(vm_cfg)); - - use crate::kernel::vm_if_set_type; - vm_if_set_type(vm_id, vm_type(vm_id)); -} - -pub fn vmm_alloc_vcpu(vm_id: usize) { - let vm = match vm(vm_id) { - None => { - panic!( - "vmm_alloc_vcpu: on core {}, VM [{}] is not added yet", - current_cpu().id, - vm_id - ); + return Err(()); } - Some(vm) => vm, }; - - for i in 0..vm.config().cpu_num() { - let vcpu = crate::kernel::Vcpu::new(vm.clone(), i); - vm.push_vcpu(vcpu); - } - - info!( - "VM {} init cpu: cores=<{}>, allocat_bits=<0b{:b}>", - vm.id(), - vm.config().cpu_num(), - vm.config().cpu_allocated_bitmap() - ); -} - -/* Finish cpu assignment before set up VM config. - * Only VM0 will go through this function. - * - * @param[in] vm_id: new added VM id. - */ -pub fn vmm_set_up_cpu(vm_id: usize) { - info!("vmm_set_up_cpu: set up vm {} on cpu {}", vm_id, current_cpu().id); - let vm = match vm(vm_id) { - None => { - panic!( - "vmm_set_up_cpu: on core {}, VM [{}] is not added yet", - current_cpu().id, - vm_id - ); - } - Some(vm) => vm, - }; - - vmm_alloc_vcpu(vm_id); - - let mut cpu_allocate_bitmap = vm.config().cpu_allocated_bitmap(); - let mut target_cpu_id = 0; - let mut cpu_num = 0; - while cpu_allocate_bitmap != 0 && target_cpu_id < PLATFORM_CPU_NUM_MAX { - if cpu_allocate_bitmap & 1 != 0 { - info!("vmm_set_up_cpu: vm {} physical cpu id {}", vm_id, target_cpu_id); - cpu_num += 1; - - if cfg!(feature = "secondary_start") { - // Judge if current cpu is allocated. - let vcpu = match vm.select_vcpu2assign(target_cpu_id) { - None => panic!("core {} vm {} cannot find proper vcpu to assign", target_cpu_id, vm_id), - Some(vcpu) => vcpu, - }; - if vcpu.id() == 0 { - info!( - "* Core {} is assigned => vm {}, vcpu {}", - target_cpu_id, - vm_id, - vcpu.id() - ); - } else { - info!("Core {} is assigned => vm {}, vcpu {}", target_cpu_id, vm_id, vcpu.id()); - } - vcpu.set_phys_id(target_cpu_id); - - let cfg_master = vm.config().cpu_master(); - if target_cpu_id == cfg_master && target_cpu_id == current_cpu().id { - current_cpu().vcpu_array.append_vcpu(vcpu); - } - } else if target_cpu_id != current_cpu().id { - let m = IpiVmmMsg { - vmid: vm_id, - event: VmmEvent::VmmAssignCpu, - }; - if !ipi_send_msg(target_cpu_id, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { - error!("vmm_set_up_cpu: failed to send ipi to Core {}", target_cpu_id); - } - } else { - vmm_cpu_assign_vcpu(vm_id); - } - } - cpu_allocate_bitmap >>= 1; - target_cpu_id += 1; - } - info!( - "vmm_set_up_cpu: vm {} total physical cpu num {} bitmap {:#b}", - vm_id, - cpu_num, - vm.config().cpu_allocated_bitmap() - ); - - // Waiting till others set up. - info!( - "vmm_set_up_cpu: on core {}, waiting VM [{}] to be set up", - current_cpu().id, - vm_id - ); - #[cfg(not(feature = "secondary_start"))] - while !vm.ready() { - use crate::utils::sleep; - sleep(10); - } - info!("vmm_set_up_cpu: VM [{}] is ready", vm_id); + vm_if_set_type(vm_id, vm_cfg.os_type); + push_vm(vm_id, vm_cfg) } /* Init VM before boot. @@ -187,12 +72,12 @@ pub fn vmm_set_up_cpu(vm_id: usize) { */ pub fn vmm_init_gvm(vm_id: usize) { // Before boot, we need to set up the VM config. - if current_cpu().id == 0 || (active_vm_id() == 0 && active_vm_id() != vm_id) { - vmm_push_vm(vm_id); - - vmm_set_up_cpu(vm_id); - - vmm_setup_config(vm_id); + if is_boot_core(current_cpu().id) || (active_vm_id() == 0 && active_vm_id() != vm_id) { + if let Ok(vm) = vmm_push_vm(vm_id) { + vmm_setup_config(vm); + } else { + error!("vmm_init_gvm: failed to push vm {}", vm_id); + } } else { error!( "VM[{}] Core {} should not init VM [{}]", @@ -208,36 +93,41 @@ pub fn vmm_init_gvm(vm_id: usize) { * @param[in] vm_id: target VM id to boot. */ pub fn vmm_boot_vm(vm_id: usize) { - let phys_id = vm_if_get_cpu_id(vm_id); - if phys_id != current_cpu().id { - use crate::kernel::{CPU_IF_LIST, CpuState}; - use crate::arch::psci_vm_maincpu_on; - let state = CPU_IF_LIST.lock().get(phys_id).unwrap().state_for_start; - if state == CpuState::CpuInv { - let vmpidr = vm(vm_id).unwrap().pcpuid_to_vcpuid(phys_id).unwrap(); - info!("now start cpu on! vmpidr={vmpidr}"); - psci_vm_maincpu_on(vmpidr, vmm_boot_vm as usize, 0, vm_id); + if let Some(phys_id) = vm_if_get_cpu_id(vm_id) { + if phys_id != current_cpu().id { + use crate::kernel::{CPU_IF_LIST, CpuState}; + use crate::arch::psci_vm_maincpu_on; + let state = CPU_IF_LIST.lock().get(phys_id).unwrap().state_for_start; + if state == CpuState::CpuInv { + let vmpidr = vm(vm_id).unwrap().pcpuid_to_vcpuid(phys_id).unwrap(); + info!("now start cpu on! vmpidr={vmpidr}"); + psci_vm_maincpu_on(vmpidr, vmm_boot_vm as usize, 0, vm_id); + } else { + let m = IpiVmmMsg { + vmid: vm_id, + event: VmmEvent::VmmBoot, + }; + if !ipi_send_msg(phys_id, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { + error!("vmm_boot_vm: failed to send ipi to Core {}", phys_id); + } + } } else { - let m = IpiVmmMsg { - vmid: vm_id, - event: VmmEvent::VmmBoot, - }; - if !ipi_send_msg(phys_id, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { - error!("vmm_boot_vm: failed to send ipi to Core {}", phys_id); + match current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id) { + None => { + error!("vmm_boot_vm: failed to get vcpu for vm {}, it is not configured", vm_id); + } + Some(vcpu) => { + IntCtrl::clear_current_irq(true); + current_cpu().scheduler().yield_to(vcpu.clone()); + vmm_boot(); + } } } } else { - if current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id).is_none() { - let vm = vm(vm_id).unwrap(); - let vcpuid = vm.pcpuid_to_vcpuid(phys_id).unwrap(); - let vcpu = vm.vcpuid_to_vcpu(vcpuid); - current_cpu().vcpu_array.append_vcpu(vcpu.unwrap()); - }; - let vcpu = current_cpu().vcpu_array.pop_vcpu_through_vmid(vm_id).unwrap(); - IntCtrl::clear_current_irq(true); - // TODO: try to use `wakeup` (still bugs when booting multi-shared-core VM using wakeup) - current_cpu().scheduler().yield_to(vcpu); - vmm_boot(); + error!( + "vmm_boot_vm: failed to get cpu id for vm {}, it is not configured", + vm_id + ); } } @@ -257,8 +147,7 @@ pub fn vmm_reboot_vm(arg: usize) { if force { if cur_vm.id() == vm_id { vmm_reboot(); - } else { - let cpu_trgt = vm_if_get_cpu_id(vm_id); + } else if let Some(cpu_trgt) = vm_if_get_cpu_id(vm_id) { let m = IpiVmmMsg { vmid: vm_id, event: VmmEvent::VmmReboot, @@ -266,6 +155,8 @@ pub fn vmm_reboot_vm(arg: usize) { if !ipi_send_msg(cpu_trgt, IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { error!("vmm_reboot_vm: failed to send ipi to Core {}", cpu_trgt); } + } else { + error!("vmm_reboot_vm: failed to get cpu id for vm {}", vm_id); } return; } @@ -300,7 +191,7 @@ pub fn vmm_reboot() { // Reset GVM. let vcpu = current_cpu().active_vcpu.clone().unwrap(); info!("VM [{}] reset...", vm.id()); - power_arch_vm_shutdown_secondary_cores(vm.clone()); + power_arch_vm_shutdown_secondary_cores(&vm); info!( "Core {} (VM [{}] vcpu {}) shutdown ok", current_cpu().id, @@ -326,7 +217,7 @@ pub fn vmm_reboot() { } // Reset image. - if !vmm_init_image(vm.clone()) { + if !vmm_init_image(&vm) { panic!("vmm_reboot: vmm_init_image failed"); } @@ -335,15 +226,13 @@ pub fn vmm_reboot() { vm_if_set_ivc_arg_ptr(vm.id(), 0); IntCtrl::clear(); - crate::arch::vcpu_arch_init(vm.clone(), vm.vcpu(0).unwrap()); + vcpu.init(vm.config()); vcpu.reset_context(); - vmm_load_image_from_mvm(vm); - - // vcpu_run(); + vmm_load_image_from_mvm(&vm); } -pub fn vmm_load_image_from_mvm(vm: Vm) { +pub fn vmm_load_image_from_mvm(vm: &Vm) { let vm_id = vm.id(); let msg = HvcManageMsg { fid: HVC_CONFIG, @@ -362,9 +251,9 @@ pub fn vmm_load_image_from_mvm(vm: Vm) { */ pub fn get_vm_id(id_ipa: usize) -> bool { let vm = active_vm().unwrap(); - let id_pa = vm_ipa2pa(vm.clone(), id_ipa); + let id_pa = vm_ipa2pa(&vm, id_ipa); if id_pa == 0 { - error!("illegal id_pa {:x}", id_pa); + error!("illegal id_ipa {:x}", id_ipa); return false; } // SAFETY: The 'id_pa' is a valid address checked by shyper.ko @@ -393,7 +282,7 @@ struct VMInfoList { * @param[in] vm_info_ipa : vm info list ipa. */ pub fn vmm_list_vm(vm_info_ipa: usize) -> Result { - let vm_info_pa = vm_ipa2pa(active_vm().unwrap(), vm_info_ipa); + let vm_info_pa = vm_ipa2pa(&active_vm().unwrap(), vm_info_ipa); if vm_info_pa == 0 { error!("illegal vm_info_ipa {:x}", vm_info_ipa); return Err(()); @@ -440,7 +329,8 @@ pub fn vmm_list_vm(vm_info_ipa: usize) -> Result { Ok(0) } -pub fn vmm_ipi_handler(msg: &IpiMessage) { +pub fn vmm_ipi_handler(msg: IpiMessage) { + info!("vmm_ipi_handler: core {} receive ipi", current_cpu().id); match msg.ipi_message { IpiInnerMsg::VmmMsg(vmm) => match vmm.event { VmmEvent::VmmBoot => { @@ -449,24 +339,26 @@ pub fn vmm_ipi_handler(msg: &IpiMessage) { VmmEvent::VmmReboot => { vmm_reboot(); } - VmmEvent::VmmAssignCpu => { - info!( + _ => { + todo!(); + } + }, + IpiInnerMsg::VmmPercoreMsg(msg) => match msg.event { + VmmPercoreEvent::VmmAssignCpu => { + debug!( "vmm_ipi_handler: core {} receive assign vcpu request for vm[{}]", current_cpu().id, - vmm.vmid + msg.vm.id() ); - vmm_cpu_assign_vcpu(vmm.vmid); + vmm_assign_vcpu_percore(&msg.vm); } - VmmEvent::VmmRemoveCpu => { - info!( + VmmPercoreEvent::VmmRemoveCpu => { + debug!( "vmm_ipi_handler: core {} remove vcpu for vm[{}]", current_cpu().id, - vmm.vmid + msg.vm.id() ); - vmm_cpu_remove_vcpu(vmm.vmid); - } - _ => { - todo!(); + vmm_cpu_remove_vcpu(msg.vm.id()); } }, _ => { diff --git a/src/vmm/remove.rs b/src/vmm/remove.rs index dcbca6366d0282c11e995e41b6e5b34ef341a3bb..363d55c366fd83a1f7f0dfa93c4dc00f98e6cad2 100644 --- a/src/vmm/remove.rs +++ b/src/vmm/remove.rs @@ -8,17 +8,19 @@ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::arch::{GIC_SGIS_NUM, gicc_clear_current_irq}; +use alloc::sync::Arc; + +use crate::arch::{gicc_clear_current_irq, IntCtrl, InterruptController, GIC_SGIS_NUM}; use crate::config::vm_cfg_remove_vm_entry; -use crate::device::{emu_remove_dev, EmuDeviceType, meta}; use crate::kernel::{ - current_cpu, interrupt_vm_remove, ipi_send_msg, IpiInnerMsg, IpiType, IpiVmmMsg, mem_vm_region_free, - remove_async_used_info, remove_vm, remove_vm_async_task, vm, Vm, Scheduler, cpu_idle, + current_cpu, interrupt_vm_remove, ipi_send_msg, IpiInnerMsg, IpiType, mem_vm_region_free, remove_async_used_info, + remove_vm, remove_vm_async_task, vm, Vm, Scheduler, cpu_idle, IpiVmmPercoreMsg, }; use crate::kernel::vm_if_reset; -use crate::vmm::VmmEvent; use crate::utils::memset; +use super::VmmPercoreEvent; + pub fn vmm_remove_vm(vm_id: usize) { if vm_id == 0 { warn!("Rust-Shyper do not support remove vm0"); @@ -34,7 +36,7 @@ pub fn vmm_remove_vm(vm_id: usize) { }; // vcpu - vmm_remove_vcpu(vm.clone()); + vmm_remove_vcpu(&vm); // reset vm interface vm_if_reset(vm_id); // free mem @@ -46,12 +48,12 @@ pub fn vmm_remove_vm(vm_id: usize) { } mem_vm_region_free(vm.pa_start(idx), vm.pa_length(idx)); } - // emu dev - vmm_remove_emulated_device(vm.clone()); // passthrough dev - vmm_remove_passthrough_device(vm.clone()); + vmm_remove_passthrough_device(&vm); // clear async task list remove_vm_async_task(vm_id); + // virtio nic + crate::device::remove_virtio_nic(vm_id); // async used info remove_async_used_info(vm_id); // remove vm: page table / mmio / vgic will be removed with struct vm @@ -65,8 +67,7 @@ pub fn vmm_remove_vm(vm_id: usize) { } fn vmm_remove_vm_list(vm_id: usize) { - let vm = remove_vm(vm_id); - vm.clear_list(); + remove_vm(vm_id); } pub fn vmm_cpu_remove_vcpu(vmid: usize) { @@ -81,50 +82,34 @@ pub fn vmm_cpu_remove_vcpu(vmid: usize) { } } -fn vmm_remove_vcpu(vm: Vm) { - for idx in 0..vm.cpu_num() { - let vcpu = vm.vcpu(idx).unwrap(); +fn vmm_remove_vcpu(vm: &Arc) { + for vcpu in vm.vcpu_list() { if vcpu.phys_id() == current_cpu().id { - vmm_cpu_remove_vcpu(vm.id()); + vmm_remove_vcpu_percore(vm); } else { - let m = IpiVmmMsg { - vmid: vm.id(), - event: VmmEvent::VmmRemoveCpu, + let m = IpiVmmPercoreMsg { + vm: vm.clone(), + event: VmmPercoreEvent::VmmRemoveCpu, }; - if !ipi_send_msg(vcpu.phys_id(), IpiType::IpiTVMM, IpiInnerMsg::VmmMsg(m)) { + if !ipi_send_msg(vcpu.phys_id(), IpiType::IpiTVMM, IpiInnerMsg::VmmPercoreMsg(m)) { warn!("vmm_remove_vcpu: failed to send ipi to Core {}", vcpu.phys_id()); } } } } -fn vmm_remove_emulated_device(vm: Vm) { - let config = vm.config().emulated_device_list(); - for (idx, emu_dev) in config.iter().enumerate() { - // mmio / vgic will be removed with struct vm - if !emu_dev.emu_type.removable() { - warn!("vmm_remove_emulated_device: cannot remove device {}", emu_dev.emu_type); - return; - } - if emu_dev.emu_type == EmuDeviceType::EmuDeviceTMeta { - meta::unregister(idx); - } - emu_remove_dev(vm.id(), idx, emu_dev.base_ipa, emu_dev.length); - // println!( - // "VM[{}] removes emulated device: id=<{}>, name=\"{}\", ipa=<0x{:x}>", - // vm.id(), - // idx, - // emu_dev.emu_type, - // emu_dev.base_ipa - // ); +pub fn vmm_remove_vcpu_percore(vm: &Vm) { + current_cpu().vcpu_array.remove_vcpu(vm.id()); + if !current_cpu().assigned() { + IntCtrl::enable(IntCtrl::IRQ_GUEST_TIMER, false); + IntCtrl::clear(); } } -fn vmm_remove_passthrough_device(vm: Vm) { +fn vmm_remove_passthrough_device(vm: &Vm) { for irq in vm.config().passthrough_device_irqs() { - if irq > GIC_SGIS_NUM { - interrupt_vm_remove(vm.clone(), irq); - // println!("VM[{}] remove irq {}", vm.id(), irq); + if *irq > GIC_SGIS_NUM { + interrupt_vm_remove(vm, *irq); } } } diff --git a/tools/shyper.ko b/tools/shyper.ko index ca501e517df61cbde77030be5f95d0274a5d81a7..cb3d6a659b5ae020674866f65c633e7b7536f5a1 100644 Binary files a/tools/shyper.ko and b/tools/shyper.ko differ diff --git a/tools/shyper_riscv64.ko b/tools/shyper_riscv64.ko new file mode 100644 index 0000000000000000000000000000000000000000..c1d0466221144832ecd116dae399611ac98ea350 Binary files /dev/null and b/tools/shyper_riscv64.ko differ