diff --git a/.cargo/config b/.cargo/config index b1c72c1e..64b1bb5d 100644 --- a/.cargo/config +++ b/.cargo/config @@ -5,8 +5,16 @@ rustflags = [ ] [target.aarch64-unknown-none] +runner = "___HVISOR_SRC___/tools/cargo_test.sh" rustflags = [ "-Clink-arg=-Tscripts/qemu-aarch64.ld", "-Ctarget-feature=+a53,+v8a,+strict-align,-neon,-fp-armv8", "-Cforce-frame-pointers=yes", +] + +[target.loongarch64-unknown-none] +linker = "loongarch64-unknown-linux-gnu-gcc" +rustflags = [ + "-Clink-arg=-Tscripts/3a5000-loongarch64.ld", + "-Cforce-frame-pointers=yes", ] \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..c725678d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,77 @@ +# wheatfox +# hvisor Github Actions CI +# version: 2025.2.28 +name: CI +on: + push: + branches: + - main + - dev + pull_request: + branches: + - main + - dev + workflow_dispatch: +env: + MODE: debug + BOARD: qemu # we can only run auto tests in emulator... +jobs: + fmt: + name: linter + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Install Rust Toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - name: linter + run: make fmt-test + test: + name: test + runs-on: ubuntu-latest + strategy: + fail-fast: false # continue to run all jobs even if some fail + matrix: + include: + # aarch64 + - arch: aarch64 + rustc_target: aarch64-unknown-none + features: "platform_qemu,gicv3" + - arch: aarch64 + rustc_target: aarch64-unknown-none + features: "platform_qemu,gicv2" + + # riscv64 will be supported in the future but not now, + # let's first focus on unittest and system test(root linux) in aarch64 qemu... + + # - arch: riscv64 + # rustc_target: riscv64gc-unknown-none-elf + # features: "platform_qemu,plic" + # - arch: riscv64 + # rustc_target: riscv64gc-unknown-none-elf + # features: "platform_qemu,aia" + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Install Rust Toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.rustc_target }} + components: rust-src + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y qemu-system-aarch64 qemu-system-riscv64 gdb-multiarch llvm-dev libclang-dev wget + cargo install --version 0.3.0 cargo-binutils + cargo install cargo-xbuild + - name: Set up environment variables + run: | + echo "ARCH=${{ matrix.arch }}" >> $GITHUB_ENV + echo "FEATURES=${{ matrix.features }}" >> $GITHUB_ENV + - name: build + run: make + - name: unittest + run: make test diff --git a/.gitignore b/.gitignore index ae8eaa1f..e9f15bba 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,11 @@ /images/aarch64/virtdisk/* /images/aarch64/kernel/* /images/aarch64/devicetree/*.dtb +/images/riscv64/virtdisk/* +/images/riscv64/kernel/* +/images/riscv64/devicetree/*.dtb /tools/hvisor +/tmp *.mod.[co] *.mod *.[oa] @@ -15,3 +19,16 @@ Module.symvers modules.order driver/main.ko .gdb_history +.vscode-ctags +Image* +*.ext4 +*.qcow2 +*.dtb +.DS_Store +temp-fit.its +fitImage +sd.img +flash.img* +.vscode/settings.json +compile.sh + diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.old.json similarity index 100% rename from .vscode/c_cpp_properties.json rename to .vscode/c_cpp_properties.old.json diff --git a/.vscode/settings-example.json b/.vscode/settings-example.json new file mode 100644 index 00000000..8a99c430 --- /dev/null +++ b/.vscode/settings-example.json @@ -0,0 +1,20 @@ +{ + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ], + // Prevent "can't find crate for `test`" error on no_std + // Ref: https://github.com/rust-lang/vscode-rust/issues/729 + // For vscode-rust plugin users: + "rust.target": "aarch64-unknown-none", + // "rust.target": "riscv64gc-unknown-none-elf", + // "rust.target": "loongarch64-unknown-none", + "rust.all_targets": false, + // For Rust Analyzer plugin users: + "rust-analyzer.cargo.target": "aarch64-unknown-none", + // "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", + // "rust-analyzer.cargo.target": "loongarch64-unknown-none", + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.cargo.features": [ + "platform_qemu" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index fd7b0212..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "rust-analyzer.linkedProjects": [ - "./Cargo.toml" - ], - // Prevent "can't find crate for `test`" error on no_std - // Ref: https://github.com/rust-lang/vscode-rust/issues/729 - // For vscode-rust plugin users: - "rust.target": "aarch64-unknown-none", - "rust.all_targets": false, - // For Rust Analyzer plugin users: - "rust-analyzer.cargo.target": "aarch64-unknown-none", - "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.checkOnSave": true, - "files.associations": { - "event_monitor.h": "c", - "log.h": "c", - "virtio.h": "c", - "compare": "c", - "type_traits": "c", - "string.h": "c", - "hvisor.h": "c", - "ioctl.h": "c", - "getopt.h": "c", - "stdlib.h": "c", - "pthread.h": "c", - "array": "c", - "atomic": "c", - "bit": "c", - "*.tcc": "c", - "chrono": "c", - "cmath": "c", - "concepts": "c", - "string": "c", - "unordered_map": "c", - "exception": "c", - "algorithm": "c", - "memory": "c", - "memory_resource": "c", - "optional": "c", - "random": "c", - "string_view": "c", - "utility": "c", - "functional": "c", - "iosfwd": "c", - "limits": "c", - "new": "c", - "numeric": "c", - "ostream": "c", - "ranges": "c", - "ratio": "c", - "streambuf": "c", - "system_error": "c", - "tuple": "c", - "typeinfo": "c", - "virtio_ring.h": "c", - "virtio_blk.h": "c", - "stdint.h": "c" - }, - "rust-analyzer.cargo.features": [ - "platform_imx8mp" - ] -} - -// { -// "rust-analyzer.linkedProjects": [ -// "./Cargo.toml" -// ], -// // Prevent "can't find crate for `test`" error on no_std -// // Ref: https://github.com/rust-lang/vscode-rust/issues/729 -// // For vscode-rust plugin users: -// "rust.target": "riscv64gc-unknown-none-elf", -// "rust.all_targets": false, -// // For Rust Analyzer plugin users: -// "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", -// "rust-analyzer.checkOnSave.allTargets": false, -// // "rust-analyzer.cargo.features": [ -// // "board_qemu" -// // ] -// } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f9f6052b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog + +> ⚠️ Please update this file for any changes to the hvisor project along with your name and GitHub profile link under the CURRENT section. + +## CURRENT-hvisor v0.1.1 + +- + +## History Release + +### hvisor v0.1.0 + +- [platform] architecture officially supported: riscv64, loongarch64 ([Jingyu Liu](https://github.com/liulog), [wheatfox](https://github.com/enkerewpo)) +- [tool] adapting hvisor-tool virtio-gpu, virtio-console ([KouweiLee](https://github.com/KouweiLee), [Roxy](https://github.com/Misaka19986), [wheatfox](https://github.com/enkerewpo)) +- [bugfix] refactor aarch64 pagetable code ([dallasxy](https://github.com/dallasxy)) +- [platform] Xilinx Ultrascale+ ZCU102 PS processor support ([Ren HangQi](https://github.com/ForeverYolo)) +- [platform] Loongson 3A5000+7A2000 support ([wheatfox](https://github.com/enkerewpo), [BoneInscri](https://github.com/BoneInscri)) +- [feature] SMMUv3 support ([Zhongkai Xu](https://github.com/ZhongkaiXu)) +- [feature] PCIe support ([Zhongkai Xu](https://github.com/ZhongkaiXu), [dallasxy](https://github.com/dallasxy), [Ren HangQi](https://github.com/ForeverYolo)) +- [feature] network interface card support ([Ren HangQi](https://github.com/ForeverYolo)) +- [feature] riscv64: IOMMU support ([Jingyu Liu](https://github.com/liulog)) +- [feature] aarch64: GICv2 support ([Ren HangQi](https://github.com/ForeverYolo)) +- [feature] basic inter-vm communication(ivc) support ([KouweiLee](https://github.com/KouweiLee)) +- [test] unittest and github ci support ([wheatfox](https://github.com/enkerewpo)) +- [tool] hvisor-tool: support virtio-console, virtio-blk, virtio-net ([KouweiLee](https://github.com/KouweiLee)) +- [platform] basic support for riscv64 ([likey99](https://github.com/likey99)) +- [tool] aarch64: management tool in root zone linux, can create, stop, suspend and destroy working zones ([KouweiLee](https://github.com/KouweiLee)) +- [platform] basic support for aarch64 with root and nonroot zone booting ([Nehckl](https://github.com/Inquisitor-201)) diff --git a/Cargo.lock b/Cargo.lock index 3ec70c69..e65fa930 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,6 +76,25 @@ dependencies = [ "spin 0.7.1", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cortex-a" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8256fd5103e10027467cc7a97c9ff27fcc4547ea24864da0aff2e7aef6e18e28" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "fdt" +version = "0.1.5" + [[package]] name = "hvisor" version = "0.1.0" @@ -85,10 +104,15 @@ dependencies = [ "bitflags 2.5.0", "bitmap-allocator", "buddy_system_allocator", + "cfg-if", + "cortex-a", + "fdt", "lazy_static", "log", + "loongArch64", "numeric-enum-macro", "psci", + "qemu-exit", "riscv", "riscv-decode", "sbi-rt", @@ -121,6 +145,16 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loongArch64" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd48200d465466664e4e899b204b77b5447d60b1ababdad3a2c49ae85417b552" +dependencies = [ + "bit_field 0.10.2", + "bitflags 1.3.2", +] + [[package]] name = "memchr" version = "2.7.2" @@ -139,6 +173,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b6fb7be1022539b1ea394ff4bcbad807a55c93841bb12c733f0be1048ea3e53" +[[package]] +name = "qemu-exit" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb0fd6580eeed0103c054e3fba2c2618ff476943762f28a645b63b8692b21c9" + [[package]] name = "regex" version = "1.10.4" diff --git a/Cargo.toml b/Cargo.toml index cf6c59c1..07a6542c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ buddy_system_allocator = "0.8" tock-registers = "0.8" lazy_static = { version = "1.4", features = ["spin_no_std"] } bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev = "03bd9909" } +fdt = { path = "./vendor/fdt" } +qemu-exit = "3.0.2" +cortex-a = "8.1.1" +cfg-if = "1.0" [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "9.4.0" @@ -25,13 +29,25 @@ sbi-rt = { version = "0.0.2", features = ["legacy"] } riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } riscv-decode = "0.2.1" +[target.'cfg(target_arch = "loongarch64")'.dependencies] +loongArch64 = "0.2.4" + [features] platform_qemu = [] platform_imx8mp = [] +platform_zcu102 = [] +gicv3 = [] +gicv2 = [] +plic = [] +aia = [] [profile.dev] -panic = "abort" +# panic = "abort" # avoid test compiler erros(still a bug for rust) debug = 2 [profile.release] -panic = "abort" \ No newline at end of file +# panic = "abort" + +[unstable] +build-std-features = ["compiler-builtins-mem"] +build-std = ["core", "compiler_builtins"] \ No newline at end of file diff --git a/Makefile b/Makefile index b6075dce..afc17d11 100644 --- a/Makefile +++ b/Makefile @@ -5,25 +5,36 @@ STATS ?= off PORT ?= 2333 MODE ?= debug OBJCOPY ?= rust-objcopy --binary-architecture=$(ARCH) -KDIR ?= ../../linux -FEATURES ?= platform_imx8mp + +# AVAIABLE "FEATURES" VALUES: +# - platform_qemu, platform_zcu102, platform_imx8mp +# - gicv2, gicv3 (for aarch64) +# - plic, aia (for riscv64) +FEATURES ?= platform_qemu,gicv3 + +# AVAIABLE "BOARD" VALUES: +# - qemu, zcu102, imx8mp, 3a5000 +BOARD ?= qemu ifeq ($(ARCH),aarch64) RUSTC_TARGET := aarch64-unknown-none GDB_ARCH := aarch64 +else ifeq ($(ARCH),riscv64) + RUSTC_TARGET := riscv64gc-unknown-none-elf + GDB_ARCH := riscv:rv64 +else ifeq ($(ARCH),loongarch64) + RUSTC_TARGET := loongarch64-unknown-none + GDB_ARCH := loongarch64 else - ifeq ($(ARCH),riscv64) - RUSTC_TARGET := riscv64gc-unknown-none-elf - GDB_ARCH := riscv:rv64 - else - $(error Unsupported ARCH value: $(ARCH)) - endif +$(error Unsupported ARCH value: $(ARCH)) endif export MODE export LOG export ARCH -export KDIR +export RUSTC_TARGET +export FEATURES +export BOARD # Build paths build_path := target/$(RUSTC_TARGET)/$(MODE) @@ -38,7 +49,6 @@ build_args += --target $(RUSTC_TARGET) build_args += -Z build-std=core,alloc build_args += -Z build-std-features=compiler-builtins-mem - ifeq ($(MODE), release) build_args += --release endif @@ -51,12 +61,9 @@ elf: cargo build $(build_args) disa: - aarch64-none-elf-readelf -a $(hvisor_elf) > hvisor-elf.txt - rust-objdump --disassemble $(hvisor_elf) > hvisor.S - -tools: - make -C tools && \ - make -C driver + readelf -a $(hvisor_elf) > hvisor-elf.txt +# rust-objdump --disassemble $(hvisor_elf) > hvisor.S + rust-objdump --disassemble --source $(hvisor_elf) > hvisor.S run: all $(QEMU) $(QEMU_ARGS) @@ -76,12 +83,43 @@ monitor: jlink-server: JLinkGDBServer -select USB -if JTAG -device Cortex-A53 -port 1234 -cp: all +cp: cp $(hvisor_bin) ~/tftp +test-pre: download-test-img + chmod +x ./tools/cargo_test.sh + @echo "pass" + +fmt-test: + cargo fmt --all -- --check + +fmt: + cargo fmt --all + +clippy: + cargo clippy $(build_args) + +flash-img: +# run this will erase all environment for uboot, be careful +# the flash.img in repo will contains the correct bootcmd + qemu-img create -f raw flash.img 64M + +download-test-img: +# first check whether the file exists + @if [ ! -f "flash.img" ]; then echo "\nflash.img not found, downloading...\n" && \ + wget https://github.com/enkerewpo/hvisor-uboot-env-img/releases/download/v20241227/flash.img.partial && \ + ./tools/extract.sh ; \ + else echo "\nflash.img found\n"; \ + fi + +test: test-pre + cp .cargo/config .cargo/config.bak + sed "s|___HVISOR_SRC___|$(shell pwd)|g" .cargo/config.bak > .cargo/config + cargo test $(build_args) -vv + clean: cargo clean - make -C tools clean - make -C driver clean -include scripts/qemu-$(ARCH).mk \ No newline at end of file +# set the BOARD variable to "3a5000"/qemu/zcu102/imx8mp to +# include the corresponding script under the ./scripts directory +include scripts/${BOARD}-${ARCH}.mk \ No newline at end of file diff --git a/README-zh.md b/README-zh.md index 202283b3..e7f0222f 100644 --- a/README-zh.md +++ b/README-zh.md @@ -1,7 +1,9 @@ -# hvisor



- + +

+ + @@ -10,70 +12,71 @@

-README:[中文](./README-zh.md) | [English](README.md) - -一款轻量级Type-1虚拟机监控器,使用Rust语言编写。 - -## 进展 - -- [x] Architecture: aarch64 -- [x] Platform: Qemu virt aarch64 -- [x] Exception -- [x] Gicv3 -- [x] Memory -- [x] Enable non root linux -- [ ] VirtIO device: block, net -- [ ] Architecture: riscv64 -- [ ] Platform: nxp - -## 如何运行 - -**启动qemu** +README:[中文](./README-zh.md) | [English](./README.md) -在 `hvisor` 目录下,执行: +hvisor 是一个用 Rust 实现的 Type-1 裸机虚拟机监控器,采用分离内核(Separation Kernel)设计,提供高效的硬件资源虚拟化和隔离能力。该虚拟机监控器实现了严格的虚拟机环境分离,通过不同的区域(zone)确保虚拟化环境的性能和安全性。 -``` -make run -``` +## 特性 -**观察终端输出信息** +- **分离内核设计**:虚拟机被划分为三个区域:zone0(管理区)、zoneU(用户区)、zoneR(实时区),之间有严格的隔离。 +- **简洁轻量**:该虚拟机监控器采用 Rust 实现,具有简洁的设计。 + - CPU 虚拟化:静态分区的物理 CPU(pCPUs),不进行动态调度。 + - 内存虚拟化:通过配置文件对虚拟机内存空间进行预分配。 + - I/O 虚拟化:支持设备直通和 virtio 半虚拟化。 +- **多平台支持**:支持多种架构,包括 `aarch64`、`riscv64` 和 `loongarch64`。 +- **虚拟机管理**:虚拟机通过 zone0(root-linux)中的 Linux 环境进行管理,管理任务通过命令行工具 [hvisor-tool](https://github.com/syswonder/hvisor-tool) 完成,提供创建、启动、停止和删除虚拟机的基本管理功能。 +- **形式化验证**:部分 hvisor 代码正在使用 [verus](https://github.com/verus-lang/verus) 工具进行形式化验证。 -``` -char device redirected to /dev/pts/num (label serial) -``` +## 设备支持 -这里的 `num` 是一个具体的数字,记住它。 +| **类别** | **设备** | **支持架构** | **备注** | +| ------------------ | --------------------- | ------------------------ | ------------------------------- | +| **Virtio 设备** | virtio-blk | `aarch64` | | +| | virtio-net | `aarch64` | | +| | virtio-console | `aarch64`, `loongarch64` | | +| | virtio-gpu | `aarch64` | 仅支持 QEMU | +| **串行设备/UARTs** | PL011 | `aarch64` | | +| | imx-uart | `aarch64` | NXP i.MX8MP | +| | NS16550A | `loongarch64` | | +| | xuartps | `aarch64` | Xilinx Ultrascale+ MPSoC ZCU102 | +| **中断控制器** | GIC irq controller | `aarch64` | | +| | 7A2000 irq controller | `loongarch64` | | +| | PLIC | `riscv64` | | +| | AIA-APIC | `riscv64` | 仅支持 MSI 模式 | +| **PCIe 直通** | PCIe | `aarch64`, `riscv` | | +| **GPU 直通** | GPU | `aarch64` | NXP i.MX8MP | -**在uboot中输入启动命令** +## 板卡支持 -该启动命令会从物理地址`0x40400000`启动hvisor,设备树的地址为`0x40000000` +### aarch64 -``` -bootm 0x40400000 - 0x40000000 -``` +- [x] QEMU virt aarch64 +- [x] NXP i.MX8MP +- [x] Xilinx Ultrascale+ MPSoC ZCU102 +- [ ] Rockchip RK3588 +- [ ] Rockchip RK3568 +- [ ] Forlinx OK6254-C -hvisor启动时,会自动启动root linux(用于管理的Linux),并进入root linux的shell界面。 +### riscv64 -**启动non-root-linux** +- [x] QEMU virt riscv64 +- [ ] FPGA 香山(昆明湖)on S2C Prodigy S7-19PS-2 +- [ ] FPGA RocketChip on Xilinx Ultrascale+ MPSoC ZCU102 -在root linux中,进入`/home`目录,执行脚本: +### loongarch64 -``` -./start-linux.sh -``` +- [x] Loongson 3A5000+7A2000 +- [ ] Loongson 3A6000 -在宿主机上输入以下指令,启动另一个终端,用于第二个Linux的输出: +## 开始使用 -``` -sudo screen /dev/pts/num -``` +请参阅 hvisor 文档中的 **《hvisor 快速上手指南》**,了解所有支持平台的构建和运行教程:[hvisor 文档](https://hvisor.syswonder.org/) -`num` 是上一步在root终端中输出的具体数字。 +## 路线图 -**验证地址空间的不同** +- 支持在 NXP i.MX8MP 硬件平台上实现 Android nonroot +- 支持在 `x86_64` 架构上运行 hvisor -现在启动了两个终端,可以通过以下命令验证两个内核使用了不同的地址空间。 +## 致谢 -```shell -cat /proc/iomem -``` +本项目的部分实现参考了 [RVM1.5](https://github.com/rcore-os/RVM1.5) 和 [jailhouse](https://github.com/siemens/jailhouse)。 \ No newline at end of file diff --git a/README.md b/README.md index 2d1e3e7a..e502d2fd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -# hvisor



- + +

+ + @@ -10,96 +12,71 @@

-README:[中文](./README-zh.md) | [English](./README.md) - -Armv8 hypervisor based on Linux & implemented in Rust,porting from [RVM1.5](https://github.com/rcore-os/RVM1.5) & [jailhouse](https://github.com/siemens/jailhouse). - -🚧 Working In Progress. - -## Progress - -- [x] Architecture: aarch64 -- [x] Platform: Qemu virt aarch64 -- [x] Exception -- [x] Gicv3 -- [x] Memory -- [x] Enable non root linux -- [ ] VirtIO device: block, net -- [ ] Architecture: riscv64 -- [ ] Platform: nxp - -## Build & Run - -For detailed build and running tutorials, including building the development environment and creating a file system, please refer to [here](https://report.syswonder.org/#/2023/20230421_ARM64-QEMU-jailhouse). - -To make it easy to get started, [here](https://bhpan.buaa.edu.cn/link/AA1BF35BBB05DA40EB8A837C2B2B3C8277) (extraction code: `sysH`) provides a Linux kernel `Image` and a file system `ubuntu-20.04-rootfs_ext4.img` with the username `arm64` and the password as a whitespace. The directories are organized as follows: - -``` -├── home - ├── arm64 - ├── images: Contains a Linux Image and ramfs. - ├── hvisor: Files required to run hvisor. - ├── jailhouse: Files required to run jailhouse. -``` +README: [中文](./README-zh.md) | [English](./README.md) -The following describes how to run a non-root-linux on jailhouse/hvisor based on `ubuntu-20.04-rootfs_ext4.img`: +hvisor is a Type-1 bare-metal virtual machine monitor implemented in Rust, featuring a separation kernel design to provide efficient hardware resource virtualization and isolation. This virtual machine monitor allows strict system environment separation, ensuring performance and security of the virtualized environments through distinct regions. -1. Build `hvisor.bin`: +## Features - ```bash - make all - ``` +- **Separation Kernel Design**: The virtual machine is divided into three regions: zone0 (management zone), zoneU (user zone), and zoneR (real-time zone), with strict isolation between them. +- **Simple and Lightweight**: hvisor is implemented in Rust with a minimal design. + - CPU Virtualization: Static partitioning of physical CPUs (pCPUs), without dynamic scheduling. + - Memory Virtualization: Pre-allocated virtual machine memory space via configuration files. + - I/O Virtualization: Supports device passthrough and virtio paravirtualization. +- **Multi-platform Support**: Supports various architectures, including `aarch64`, `riscv64`, and `loongarch64`. +- **Virtual Machine Management**: Virtual machines are managed through a Linux environment in zone0 (root-linux), with basic management tasks (create, start, stop, delete) handled via the command-line tool [hvisor-tool](https://github.com/syswonder/hvisor-tool). +- **Formal Verification**: Part of the hvisor code is undergoing formal verification using the [verus](https://github.com/verus-lang/verus) tool. - Then copy `target/aarch64/debug/hvisor.bin` to `~/hvisor/` in `ubuntu-20.04-rootfs_ext4.img`. +## Device Support -2. Start QEMU: +| **Category** | **Device** | **Supported Architectures** | **Notes** | +| ------------------------- | --------------------- | --------------------------- | ------------------------------- | +| **Virtio Devices** | virtio-blk | `aarch64` | | +| | virtio-net | `aarch64` | | +| | virtio-console | `aarch64`, `loongarch64` | | +| | virtio-gpu | `aarch64` | Only supports QEMU | +| **Serial Devices/UARTs** | PL011 | `aarch64` | | +| | imx-uart | `aarch64` | NXP i.MX8MP | +| | NS16550A | `loongarch64` | | +| | xuartps | `aarch64` | Xilinx Ultrascale+ MPSoC ZCU102 | +| **Interrupt Controllers** | GIC irq controller | `aarch64` | | +| | 7A2000 irq controller | `loongarch64` | | +| | PLIC | `riscv64` | | +| | AIA-APIC | `riscv64` | Only supports MSI mode | +| **PCIe Passthrough** | PCIe | `aarch64`, `riscv` | | +| **GPU Passthrough** | GPU | `aarch64` | NXP i.MX8MP | - ```bash - sudo qemu-system-aarch64 \ - -machine virt,gic_version=3 \ - -machine virtualization=true \ - -cpu cortex-a53 \ - -machine type=virt \ - -nographic \ - -smp 16 \ - -m 1024 \ - -kernel your-linux-Image-path/Image \ - -append "console=ttyAMA0 root=/dev/vda rw mem=768m" \ - -drive if=none,file=your-rootfs-path/ubuntu-20.04-rootfs_ext4.img,id=hd0,format=raw \ - -device virtio-blk-device,drive=hd0 \ - -net nic \ - -net user,hostfwd=tcp::2333-:22 - ``` +## Supported Boards -3. Enter the username `arm64` and the password as a whitespace after startup. +### aarch64 -4. Go to the home directory and start non-root-linux: +- [x] QEMU virt aarch64 +- [x] NXP i.MX8MP +- [x] Xilinx Ultrascale+ MPSoC ZCU102 +- [ ] Rockchip RK3588 +- [ ] Rockchip RK3568 +- [ ] Forlinx OK6254-C - * For hvisor: go to the `hvisor` folder and run: +### riscv64 - ``` - ./setup.sh - ./linux.sh - ``` +- [x] QEMU virt riscv64 +- [ ] FPGA XiangShan(KunMingHu) on S2C Prodigy S7-19PS-2 +- [ ] FPGA RocketChip on Xilinx Ultrascale+ MPSoC ZCU102 - * For Jailhouse: go to the `jailhouse` folder and run: +### loongarch64 - ``` - ./linux.sh - ``` +- [x] Loongson 3A5000 and 7A2000 bridge chip +- [ ] Loongson 3A6000 -### Enable a second serial console +## Getting Started -If someone wants non-root-linux and root-linux in two different terminals, add this line at the end of the qemu startup command: +Please refer to the hvisor documentation for the quick start guide, which includes build and run instructions for all supported platforms: [hvisor Documentation](https://hvisor.syswonder.org/) -``` --device virtio-serial-device -chardev pty,id=serial3 -device virtconsole,chardev=serial3 -``` +## Roadmap -After starting qemu, the `char device redirected to /dev/pts/num (label serial3)` message will output by the first terminal, execute this in another terminal: +- Support for Android non-root on the NXP i.MX8MP hardware platform +- Support for running hvisor on the `x86_64` architecture -``` -sudo screen /dev/pts/num -``` +## Acknowledgments -where num is a specific number. +Some implementations of this project reference [RVM1.5](https://github.com/rcore-os/RVM1.5) and [jailhouse](https://github.com/siemens/jailhouse). \ No newline at end of file diff --git a/images/aarch64/bootloader/u-boot-atf.bin b/images/aarch64/bootloader/u-boot-atf.bin new file mode 100644 index 00000000..11d89350 Binary files /dev/null and b/images/aarch64/bootloader/u-boot-atf.bin differ diff --git a/images/aarch64/bootloader/u-boot-v2.bin b/images/aarch64/bootloader/u-boot-v2.bin new file mode 100644 index 00000000..51405820 Binary files /dev/null and b/images/aarch64/bootloader/u-boot-v2.bin differ diff --git a/images/aarch64/devicetree/linux1-v2.dts b/images/aarch64/devicetree/linux1-v2.dts new file mode 100644 index 00000000..ce6b8dfa --- /dev/null +++ b/images/aarch64/devicetree/linux1-v2.dts @@ -0,0 +1,97 @@ +/dts-v1/; + +/ { + #size-cells = <0x02>; + #address-cells = <0x02>; + interrupt-parent = <0x01>; + model = "linux,dummy-virt"; + compatible = "linux,dummy-virt"; + + cpus { + #size-cells = <0x00>; + #address-cells = <0x01>; + + cpu@0 { + phandle = <0x8010>; + reg = <0x00>; + enable-method = "psci"; + compatible = "arm,cortex-a53"; + device_type = "cpu"; + }; + + cpu@1 { + phandle = <0x800f>; + reg = <0x01>; + enable-method = "psci"; + compatible = "arm,cortex-a53"; + device_type = "cpu"; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + memory@50000000 { + device_type = "memory"; + reg = <0x00 0x50000000 0x00 0x80000000>; + }; + + intc@8000000 { + phandle = <0x01>; + interrupts = <0x01 0x09 0x04>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>; + compatible = "arm,cortex-a15-gic"; + ranges; + #size-cells = <0x02>; + #address-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x03>; + }; + + apb-pclk { + phandle = <0x8000>; + clock-output-names = "clk24mhz"; + clock-frequency = <0x16e3600>; + #clock-cells = <0x00>; + compatible = "fixed-clock"; + }; + + pl011@9000000 { + clock-names = "uartclk\0apb_pclk"; + clocks = <0x8000 0x8000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x01 0x04>; + reg = <0x00 0x9000000 0x00 0x1000>; + compatible = "arm,pl011\0arm,primecell"; + }; + + timer { + interrupt-parent = <0x01>; + interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; + always-on; + compatible = "arm,armv8-timer\0arm,armv7-timer"; + }; + + virtio_mmio@a003a00 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x00 0x2d 0x01>; + reg = <0x00 0xa003a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003e00 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x00 0x2f 0x01>; + reg = <0x00 0xa003e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + chosen { + bootargs = "earlycon console=ttyAMA0 root=/dev/vda mem=768M rw"; + stdout-path = "/pl011@9000000"; + }; +}; diff --git a/images/aarch64/devicetree/linux1.dts b/images/aarch64/devicetree/linux1.dts index a4ec14a8..da2f225f 100644 --- a/images/aarch64/devicetree/linux1.dts +++ b/images/aarch64/devicetree/linux1.dts @@ -15,7 +15,7 @@ phandle = <0x8010>; reg = <0x00>; enable-method = "psci"; - compatible = "arm,cortex-a53"; + compatible = "arm,cortex-a72"; device_type = "cpu"; }; @@ -23,7 +23,7 @@ phandle = <0x800f>; reg = <0x01>; enable-method = "psci"; - compatible = "arm,cortex-a53"; + compatible = "arm,cortex-a72"; device_type = "cpu"; }; @@ -47,15 +47,46 @@ memory@50000000 { device_type = "memory"; - reg = <0x0 0x50000000 0x0 0x80000000>; + reg = <0x0 0x50000000 0x0 0x70000000>; }; - gic@80000000 { + intc@80000000 { + phandle = <0x01>; + interrupts = <0x01 0x09 0x04>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; + #redistributor-regions = <0x01>; compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; + ranges; + #size-cells = <0x02>; + #address-cells = <0x02>; interrupt-controller; - reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; - phandle = <0x01>; + #interrupt-cells = <0x03>; + + its@8080000 { + phandle = <0x8006>; + reg = <0x00 0x8080000 0x00 0x20000>; + #msi-cells = <0x01>; + msi-controller; + compatible = "arm,gic-v3-its"; + }; + }; + + pcie@10000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x05 0x04>; + #interrupt-cells = <0x01>; + ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 + 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 + 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; + reg = <0x40 0x10000000 0x00 0x10000000>; + msi-map = <0x00 0x8006 0x00 0x10000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + #size-cells = <0x02>; + #address-cells = <0x03>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; }; apb-pclk { @@ -82,50 +113,213 @@ compatible = "arm,armv8-timer", "arm,armv7-timer"; }; - // virtio_mmio@a003000 { - // dma-coherent; - // interrupt-parent = <0x01>; - // interrupts = <0x0 0x28 0x1>; - // reg = <0x0 0xa003000 0x0 0x200>; - // compatible = "virtio,mmio"; - // }; - - // virtio_mmio@a003200 { - // dma-coherent; - // interrupt-parent = <0x01>; - // interrupts = <0x0 0x29 0x1>; - // reg = <0x0 0xa003200 0x0 0x200>; - // compatible = "virtio,mmio"; - // }; - - - // virtio_mmio@a003400 { - // dma-coherent; - // interrupt-parent = <0x01>; - // interrupts = <0x0 0x2a 0x1>; - // reg = <0x0 0xa003400 0x0 0x200>; - // compatible = "virtio,mmio"; - // }; - - // virtio 9p-pci - // virtio_mmio@a003600 { - // dma-coherent; - // interrupt-parent = <0x01>; - // interrupts = <0x0 0x2b 0x1>; - // reg = <0x0 0xa003600 0x0 0x200>; - // compatible = "virtio,mmio"; - // }; - - // virtio serial - // virtio_mmio@a003800 { - // dma-coherent; - // interrupt-parent = <0x01>; - // interrupts = <0x0 0x2c 0x1>; - // reg = <0x0 0xa003800 0x0 0x200>; - // compatible = "virtio,mmio"; - // }; - - // virtio net + virtio_mmio@a000000 { + dma-coherent; + interrupts = <0x00 0x10 0x01>; + reg = <0x00 0xa000000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000200 { + dma-coherent; + interrupts = <0x00 0x11 0x01>; + reg = <0x00 0xa000200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000400 { + dma-coherent; + interrupts = <0x00 0x12 0x01>; + reg = <0x00 0xa000400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000600 { + dma-coherent; + interrupts = <0x00 0x13 0x01>; + reg = <0x00 0xa000600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000800 { + dma-coherent; + interrupts = <0x00 0x14 0x01>; + reg = <0x00 0xa000800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000a00 { + dma-coherent; + interrupts = <0x00 0x15 0x01>; + reg = <0x00 0xa000a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000c00 { + dma-coherent; + interrupts = <0x00 0x16 0x01>; + reg = <0x00 0xa000c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a000e00 { + dma-coherent; + interrupts = <0x00 0x17 0x01>; + reg = <0x00 0xa000e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001000 { + dma-coherent; + interrupts = <0x00 0x18 0x01>; + reg = <0x00 0xa001000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001200 { + dma-coherent; + interrupts = <0x00 0x19 0x01>; + reg = <0x00 0xa001200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001400 { + dma-coherent; + interrupts = <0x00 0x1a 0x01>; + reg = <0x00 0xa001400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001600 { + dma-coherent; + interrupts = <0x00 0x1b 0x01>; + reg = <0x00 0xa001600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001800 { + dma-coherent; + interrupts = <0x00 0x1c 0x01>; + reg = <0x00 0xa001800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001a00 { + dma-coherent; + interrupts = <0x00 0x1d 0x01>; + reg = <0x00 0xa001a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001c00 { + dma-coherent; + interrupts = <0x00 0x1e 0x01>; + reg = <0x00 0xa001c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a001e00 { + dma-coherent; + interrupts = <0x00 0x1f 0x01>; + reg = <0x00 0xa001e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002000 { + dma-coherent; + interrupts = <0x00 0x20 0x01>; + reg = <0x00 0xa002000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002200 { + dma-coherent; + interrupts = <0x00 0x21 0x01>; + reg = <0x00 0xa002200 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002400 { + dma-coherent; + interrupts = <0x00 0x22 0x01>; + reg = <0x00 0xa002400 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002600 { + dma-coherent; + interrupts = <0x00 0x23 0x01>; + reg = <0x00 0xa002600 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002800 { + dma-coherent; + interrupts = <0x00 0x24 0x01>; + reg = <0x00 0xa002800 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002a00 { + dma-coherent; + interrupts = <0x00 0x25 0x01>; + reg = <0x00 0xa002a00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002c00 { + dma-coherent; + interrupts = <0x00 0x26 0x01>; + reg = <0x00 0xa002c00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a002e00 { + dma-coherent; + interrupts = <0x00 0x27 0x01>; + reg = <0x00 0xa002e00 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003000 { + dma-coherent; + interrupts = <0x00 0x28 0x01>; + reg = <0x00 0xa003000 0x00 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003200 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x29 0x1>; + reg = <0x0 0xa003200 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003400 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x2a 0x1>; + reg = <0x0 0xa003400 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003600 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x2b 0x1>; + reg = <0x0 0xa003600 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@a003800 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x2c 0x1>; + reg = <0x0 0xa003800 0x0 0x200>; + compatible = "virtio,mmio"; + }; + virtio_mmio@a003a00 { dma-coherent; interrupt-parent = <0x01>; @@ -134,16 +328,15 @@ compatible = "virtio,mmio"; }; - // virtio disk-second - // virtio_mmio@a003c00 { - // dma-coherent; - // interrupt-parent = <0x01>; - // interrupts = <0x0 0x2e 0x1>; - // reg = <0x0 0xa003c00 0x0 0x200>; - // compatible = "virtio,mmio"; - // }; + virtio_mmio@a003c00 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x2e 0x1>; + reg = <0x0 0xa003c00 0x0 0x200>; + compatible = "virtio,mmio"; + }; - // virtio disk-first + // virtio disk virtio_mmio@a003e00 { dma-coherent; interrupt-parent = <0x01>; @@ -158,7 +351,7 @@ stdout-path = "/pl011@9000000"; }; - hvisor_device { + hvisor_virtio_device { compatible = "hvisor"; interrupt-parent = <0x01>; interrupts = <0x00 0x20 0x01>; diff --git a/images/aarch64/devicetree/linux2.dts b/images/aarch64/devicetree/linux2.dts index 1575d382..9780b77f 100644 --- a/images/aarch64/devicetree/linux2.dts +++ b/images/aarch64/devicetree/linux2.dts @@ -14,17 +14,9 @@ cpu@2 { reg = <0x02>; enable-method = "psci"; - compatible = "arm,cortex-a53"; + compatible = "arm,cortex-a72"; device_type = "cpu"; }; - - cpu@3 { - reg = <0x03>; - enable-method = "psci"; - compatible = "arm,cortex-a53"; - device_type = "cpu"; - }; - }; psci { @@ -37,12 +29,43 @@ reg = <0x0 0x50000000 0x0 0x30000000>; }; - gic@80000000 { + intc@80000000 { + phandle = <0x01>; + interrupts = <0x01 0x09 0x04>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; + #redistributor-regions = <0x01>; compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; + ranges; + #size-cells = <0x02>; + #address-cells = <0x02>; interrupt-controller; - reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; - phandle = <0x01>; + #interrupt-cells = <0x03>; + + its@8080000 { + phandle = <0x8006>; + reg = <0x00 0x8080000 0x00 0x20000>; + #msi-cells = <0x01>; + msi-controller; + compatible = "arm,gic-v3-its"; + }; + }; + + pcie@10000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x05 0x04>; + #interrupt-cells = <0x01>; + ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 + 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 + 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; + reg = <0x40 0x10000000 0x00 0x10000000>; + msi-map = <0x00 0x8006 0x00 0x10000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + #size-cells = <0x02>; + #address-cells = <0x03>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; }; apb-pclk { @@ -89,14 +112,7 @@ // reg = <0x0 0xa003400 0x0 0x200>; // compatible = "virtio,mmio"; // }; - // virtio-net - virtio_mmio@a003600 { - dma-coherent; - interrupt-parent = <0x01>; - interrupts = <0x0 0x2b 0x1>; - reg = <0x0 0xa003600 0x0 0x200>; - compatible = "virtio,mmio"; - }; + // virtio serial virtio_mmio@a003800 { @@ -133,9 +149,7 @@ // }; chosen { - bootargs = "earlycon console=hvc0 root=/dev/vda rw"; - // bootargs = "root=/dev/vda mem=768M"; + bootargs = "earlycon=virtio,mmio,0xa003800 console=hvc0 root=/dev/vda rw"; stdout-path = "/virtio_mmio@a003800"; }; - }; diff --git a/images/aarch64/devicetree/linux2.json b/images/aarch64/devicetree/linux2.json index 7ea4511c..516adaaf 100644 --- a/images/aarch64/devicetree/linux2.json +++ b/images/aarch64/devicetree/linux2.json @@ -1,7 +1,8 @@ { "arch": "arm64", + "name": "linux2", "zone_id": 1, - "cpus": [2, 3], + "cpus": [2], "memory_regions": [ { "type": "ram", @@ -10,16 +11,46 @@ "size": "0x30000000" }, { - "type": "io", - "physical_start": "0xa003000", - "virtual_start": "0xa003000", - "size": "0x1000" + "type": "virtio", + "physical_start": "0xa003c00", + "virtual_start": "0xa003c00", + "size": "0x200" + }, + { + "type": "virtio", + "physical_start": "0xa003800", + "virtual_start": "0xa003800", + "size": "0x200" } ], - "interrupts": [75, 76, 78], + "interrupts": [75, 76, 78, 35, 36, 37, 38], + "ivc_configs": [], "kernel_filepath": "./Image", "dtb_filepath": "./linux2.dtb", "kernel_load_paddr": "0x50400000", "dtb_load_paddr": "0x50000000", - "entry_point": "0x50400000" + "entry_point": "0x50400000", + "arch_config": { + "gicd_base": "0x8000000", + "gicd_size": "0x10000", + "gicr_base": "0x80a0000", + "gicr_size": "0xf60000", + "gits_base": "0x8080000", + "gits_size": "0x20000" + }, + "pci_config": { + "ecam_base": "0x4010000000", + "ecam_size": "0x10000000", + "io_base": "0x3eff0000", + "io_size": "0x10000", + "pci_io_base": "0x0", + "mem32_base": "0x10000000", + "mem32_size": "0x2eff0000", + "pci_mem32_base": "0x10000000", + "mem64_base": "0x8000000000", + "mem64_size": "0x8000000000", + "pci_mem64_base": "0x8000000000" + }, + "num_pci_devs": 2, + "alloc_pci_devs": [0, 16] } \ No newline at end of file diff --git a/images/aarch64/devicetree/linux3.dts b/images/aarch64/devicetree/linux3.dts new file mode 100644 index 00000000..11c05bc9 --- /dev/null +++ b/images/aarch64/devicetree/linux3.dts @@ -0,0 +1,111 @@ +/dts-v1/; + +/ { + #size-cells = <0x2>; + #address-cells = <0x2>; + interrupt-parent = <0x01>; + model = "linux,dummy-virt"; + compatible = "linux,dummy-virt"; + + cpus { + #size-cells = <0x00>; + #address-cells = <0x01>; + + cpu@3 { + reg = <0x03>; + enable-method = "psci"; + compatible = "arm,cortex-a57"; + device_type = "cpu"; + }; + + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + memory@50000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x10000000>; + }; + + intc@80000000 { + phandle = <0x01>; + interrupts = <0x01 0x09 0x04>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; + #redistributor-regions = <0x01>; + compatible = "arm,gic-v3"; + ranges; + #size-cells = <0x02>; + #address-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x03>; + + its@8080000 { + phandle = <0x8006>; + reg = <0x00 0x8080000 0x00 0x20000>; + #msi-cells = <0x01>; + msi-controller; + compatible = "arm,gic-v3-its"; + }; + }; + + pcie@10000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x05 0x04>; + #interrupt-cells = <0x01>; + ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 + 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 + 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; + reg = <0x40 0x10000000 0x00 0x10000000>; + msi-map = <0x00 0x8006 0x00 0x10000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + #size-cells = <0x02>; + #address-cells = <0x03>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; + }; + + apb-pclk { + phandle = <0x8000>; + clock-output-names = "clk24mhz"; + clock-frequency = <0x16e3600>; + #clock-cells = <0x00>; + compatible = "fixed-clock"; + }; + + timer { + interrupt-parent = <0x01>; + interrupts = <0x1 0xd 0xf04 0x1 0xe 0xf04 0x1 0xb 0xf04 0x1 0xa 0xf04>; + always-on; + compatible = "arm,armv8-timer", "arm,armv7-timer"; + }; + + // virtio-blk + virtio_mmio@a003600 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x2e 0x1>; + reg = <0x0 0xa003600 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + // virtio serial + virtio_mmio@a003400 { + dma-coherent; + interrupt-parent = <0x01>; + interrupts = <0x0 0x2c 0x1>; + reg = <0x0 0xa003400 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + chosen { + bootargs = "earlycon=virtio,mmio,0xa004000 console=hvc0 root=/dev/vda rw"; + // bootargs = "root=/dev/vda mem=768M"; + stdout-path = "/virtio_mmio@a004200"; + }; + +}; diff --git a/images/aarch64/devicetree/linux3.json b/images/aarch64/devicetree/linux3.json new file mode 100644 index 00000000..296a219a --- /dev/null +++ b/images/aarch64/devicetree/linux3.json @@ -0,0 +1,56 @@ +{ + "arch": "arm64", + "name": "linux3", + "zone_id": 2, + "cpus": [3], + "memory_regions": [ + { + "type": "ram", + "physical_start": "0x80000000", + "virtual_start": "0x80000000", + "size": "0x10000000" + }, + { + "type": "virtio", + "physical_start": "0xa003600", + "virtual_start": "0xa003600", + "size": "0x200" + }, + { + "type": "virtio", + "physical_start": "0xa003400", + "virtual_start": "0xa003400", + "size": "0x200" + } + ], + "interrupts": [75, 76, 78], + "ivc_configs": [], + "kernel_filepath": "./Image", + "dtb_filepath": "./linux3.dtb", + "kernel_load_paddr": "0x80400000", + "dtb_load_paddr": "0x80000000", + "entry_point": "0x80400000", + "arch_config": { + "gicd_base": "0x8000000", + "gicd_size": "0x10000", + "gicr_base": "0x80a0000", + "gicr_size": "0xf60000", + "gits_base": "0x8080000", + "gits_size": "0x20000" + }, + "pci_config": { + "ecam_base": "0x4010000000", + "ecam_size": "0x10000000", + "io_base": "0x3eff0000", + "io_size": "0x10000", + "pci_io_base": "0x0", + "mem32_base": "0x10000000", + "mem32_size": "0x2eff0000", + "pci_mem32_base": "0x10000000", + "mem64_base": "0x8000000000", + "mem64_size": "0x8000000000", + "pci_mem64_base": "0x8000000000" + }, + "num_pci_devs": 1, + "alloc_pci_devs": [0, 24] +} \ No newline at end of file diff --git a/images/aarch64/devicetree/trans_file.sh b/images/aarch64/devicetree/trans_file.sh new file mode 100755 index 00000000..082a721e --- /dev/null +++ b/images/aarch64/devicetree/trans_file.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +file_path=$1 +disk_path="../virtdisk" +# 检查文件是否存在 +if [ ! -f "$file_path" ]; then + echo "Error: File '$file_path' not found." + exit 1 +fi + +sudo mount "$disk_path"/rootfs1.ext4 "$disk_path"/rootfs +sudo cp "$file_path" "$disk_path"/rootfs/home/arm64/ + +if [ $? -eq 0 ]; then + echo "File has been successfully copied" +else + echo "Error: Failed to copy the file." + exit 1 +fi +sudo umount "$disk_path"/rootfs diff --git a/images/aarch64/devicetree/virtio_cfg2.json b/images/aarch64/devicetree/virtio_cfg2.json new file mode 100644 index 00000000..e9924ea6 --- /dev/null +++ b/images/aarch64/devicetree/virtio_cfg2.json @@ -0,0 +1,40 @@ +{ + "zones": [ + { + "id": 1, + "memory_region": [ + { + "zone0_ipa": "0x50000000", + "zonex_ipa": "0x50000000", + "size": "0x30000000" + } + ], + "devices": [ + { + "type": "blk", + "addr": "0xa003c00", + "len": "0x200", + "irq": 78, + "img": "rootfs2.ext4", + "status": "enable" + }, + { + "type": "console", + "addr": "0xa003800", + "len": "0x200", + "irq": 76, + "status": "enable" + }, + { + "type": "net", + "addr": "0xa003600", + "len": "0x200", + "irq": 75, + "tap": "tap0", + "mac": ["0x00", "0x16", "0x3e", "0x10", "0x10", "0x10"], + "status": "disable" + } + ] + } + ] +} \ No newline at end of file diff --git a/images/aarch64/devicetree/virtio_cfg3.json b/images/aarch64/devicetree/virtio_cfg3.json new file mode 100644 index 00000000..8cea0b90 --- /dev/null +++ b/images/aarch64/devicetree/virtio_cfg3.json @@ -0,0 +1,31 @@ +{ + "zones": [ + { + "id": 2, + "memory_region": [ + { + "zone0_ipa": "0x80000000", + "zonex_ipa": "0x80000000", + "size": "0x10000000" + } + ], + "devices": [ + { + "type": "blk", + "addr": "0xa003600", + "len": "0x200", + "irq": 78, + "img": "rootfs3.ext4", + "status": "enable" + }, + { + "type": "console", + "addr": "0xa003400", + "len": "0x200", + "irq": 76, + "status": "enable" + } + ] + } + ] +} \ No newline at end of file diff --git a/images/aarch64/devicetree/zcu102-nonroot-aarch64.dts b/images/aarch64/devicetree/zcu102-nonroot-aarch64.dts new file mode 100644 index 00000000..28bc94d1 --- /dev/null +++ b/images/aarch64/devicetree/zcu102-nonroot-aarch64.dts @@ -0,0 +1,126 @@ +/dts-v1/; + +/ { + compatible = "xlnx,zynqmp-zcu102-revB\0xlnx,zynqmp-zcu102\0xlnx,zynqmp"; + #address-cells = <0x02>; + #size-cells = <0x02>; + model = "ZynqMP ZCU102 RevB"; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + + cpu@2 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + enable-method = "psci"; + reg = <0x02>; + operating-points-v2 = <0x01>; + cpu-idle-states = <0x02>; + next-level-cache = <0x03>; + phandle = <0x08>; + }; + + cpu@3 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + enable-method = "psci"; + reg = <0x03>; + operating-points-v2 = <0x01>; + cpu-idle-states = <0x02>; + next-level-cache = <0x03>; + phandle = <0x09>; + }; + + l2-cache { + compatible = "cache"; + cache-level = <0x02>; + phandle = <0x03>; + }; + + idle-states { + entry-method = "psci"; + + cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x40000000>; + local-timer-stop; + entry-latency-us = <0x12c>; + exit-latency-us = <0x258>; + min-residency-us = <0x2710>; + phandle = <0x02>; + }; + }; + }; + + pmu { + compatible = "arm,armv8-pmuv3"; + interrupt-parent = <0x05>; + interrupts = <0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04>; + interrupt-affinity = <0x06 0x07 0x08 0x09>; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x00 0x50000000 0x00 0x25000000>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <0x05>; + interrupts = <0x01 0x0d 0xf08 0x01 0x0e 0xf08 0x01 0x0b 0xf08 0x01 0x0a 0xf08>; + }; + + axi { + compatible = "simple-bus"; + bootph-all; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + phandle = <0x58>; + + interrupt-controller@f9010000 { + compatible = "arm,gic-400"; + #interrupt-cells = <0x03>; + reg = <0x00 0xf9010000 0x00 0x10000 0x00 0xf9020000 0x00 0x20000 0x00 0xf9040000 0x00 0x20000 0x00 0xf9060000 0x00 0x20000>; + interrupt-controller; + interrupt-parent = <0x05>; + interrupts = <0x01 0x09 0xf04>; + num_cpus = <0x02>; + num_interrupts = <0x60>; + phandle = <0x05>; + }; + }; + + + aliases { + }; + + // virtio blk + virtio_mmio@ff9d0000 { + dma-coherent; + interrupt-parent = <0x05>; + interrupts = <0x0 0x2e 0x1>; + reg = <0x0 0xff9d0000 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + // virtio serial + virtio_mmio@ff9e0000 { + dma-coherent; + interrupt-parent = <0x05>; + interrupts = <0x0 0x2c 0x1>; + reg = <0x0 0xff9e0000 0x0 0x200>; + compatible = "virtio,mmio"; + }; + + chosen { + bootargs = "earlycon=virtio,mmio,0xff9e0000 console=hvc0 root=/dev/vda rootwait rw"; + stdout-path = "/virtio_mmio@0xff9e0000"; + }; +}; diff --git a/images/aarch64/devicetree/zcu102-root-aarch64.dts b/images/aarch64/devicetree/zcu102-root-aarch64.dts new file mode 100644 index 00000000..9accfe59 --- /dev/null +++ b/images/aarch64/devicetree/zcu102-root-aarch64.dts @@ -0,0 +1,376 @@ +/dts-v1/; + +/ { + compatible = "xlnx,zynqmp-zcu102-revB\0xlnx,zynqmp-zcu102\0xlnx,zynqmp"; + #address-cells = <0x02>; + #size-cells = <0x02>; + model = "ZynqMP ZCU102 RevB"; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + + cpu@0 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + enable-method = "psci"; + operating-points-v2 = <0x01>; + reg = <0x00>; + cpu-idle-states = <0x02>; + next-level-cache = <0x03>; + clocks = <0x04 0x0a>; + phandle = <0x06>; + }; + + cpu@1 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + enable-method = "psci"; + reg = <0x01>; + operating-points-v2 = <0x01>; + cpu-idle-states = <0x02>; + next-level-cache = <0x03>; + phandle = <0x07>; + }; + + cpu@2 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + enable-method = "psci"; + reg = <0x02>; + operating-points-v2 = <0x01>; + cpu-idle-states = <0x02>; + next-level-cache = <0x03>; + phandle = <0x08>; + }; + + cpu@3 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + enable-method = "psci"; + reg = <0x03>; + operating-points-v2 = <0x01>; + cpu-idle-states = <0x02>; + next-level-cache = <0x03>; + phandle = <0x09>; + }; + + l2-cache { + compatible = "cache"; + cache-level = <0x02>; + phandle = <0x03>; + }; + + idle-states { + entry-method = "psci"; + + cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x40000000>; + local-timer-stop; + entry-latency-us = <0x12c>; + exit-latency-us = <0x258>; + min-residency-us = <0x2710>; + phandle = <0x02>; + }; + }; + }; + + zynqmp-ipi { + bootph-all; + compatible = "xlnx,zynqmp-ipi-mailbox"; + interrupt-parent = <0x05>; + interrupts = <0x00 0x23 0x04>; + xlnx,ipi-id = <0x00>; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + phandle = <0x40>; + + mailbox@ff9905c0 { + bootph-all; + reg = <0x00 0xff9905c0 0x00 0x20 0x00 0xff9905e0 0x00 0x20 0x00 0xff990e80 0x00 0x20 0x00 0xff990ea0 0x00 0x20>; + reg-names = "local_request_region\0local_response_region\0remote_request_region\0remote_response_region"; + #mbox-cells = <0x01>; + xlnx,ipi-id = <0x04>; + phandle = <0x0a>; + }; + }; + + pmu { + compatible = "arm,armv8-pmuv3"; + interrupt-parent = <0x05>; + interrupts = <0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04>; + interrupt-affinity = <0x06 0x07 0x08 0x09>; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + memory@0 { + device_type = "memory"; + // reg = <0x00 0x00 0x00 0x7ff00000 0x08 0x00 0x00 0x80000000>; + reg = <0x00 0x00 0x00 0x40000000>; + }; + + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <0x05>; + interrupts = <0x01 0x0d 0xf08 0x01 0x0e 0xf08 0x01 0x0b 0xf08 0x01 0x0a 0xf08>; + }; + + reset-controller { + compatible = "xlnx,zynqmp-reset"; + #reset-cells = <0x01>; + phandle = <0x11>; + }; + + fpga-region { + compatible = "fpga-region"; + fpga-mgr = <0x10>; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + phandle = <0x57>; + }; + + axi { + compatible = "simple-bus"; + bootph-all; + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + phandle = <0x58>; + + interrupt-controller@f9010000 { + compatible = "arm,gic-400"; + #interrupt-cells = <0x03>; + reg = <0x00 0xf9010000 0x00 0x10000 0x00 0xf9020000 0x00 0x20000 0x00 0xf9040000 0x00 0x20000 0x00 0xf9060000 0x00 0x20000>; + interrupt-controller; + interrupt-parent = <0x05>; + interrupts = <0x01 0x09 0xf04>; + num_cpus = <0x02>; + num_interrupts = <0x60>; + phandle = <0x05>; + }; + + serial@ff000000 { + bootph-all; + compatible = "xlnx,zynqmp-uart\0cdns,uart-r1p12"; + status = "okay"; + interrupt-parent = <0x05>; + interrupts = <0x00 0x15 0x04>; + reg = <0x00 0xff000000 0x00 0x1000>; + clock-names = "uart_clk\0pclk"; + power-domains = <0x12 0x21>; + clocks = <0x04 0x38 0x04 0x1f>; + pinctrl-names = "default"; + pinctrl-0 = <0x25>; + cts-override; + device_type = "serial"; + port-number = <0x00>; + u-boot,dm-pre-reloc; + phandle = <0x95>; + }; + + + mmc@ff170000 { + bootph-all; + compatible = "xlnx,zynqmp-8.9a\0arasan,sdhci-8.9a"; + status = "okay"; + interrupt-parent = <0x05>; + interrupts = <0x00 0x31 0x04>; + reg = <0x00 0xff170000 0x00 0x1000>; + clock-names = "clk_xin\0clk_ahb"; + iommus = <0x14 0x871>; + power-domains = <0x12 0x28>; + #clock-cells = <0x01>; + clock-output-names = "clk_out_sd1\0clk_in_sd1"; + resets = <0x11 0x27>; + clocks = <0x04 0x37 0x04 0x1f>; + assigned-clocks = <0x04 0x37>; + no-1-8-v; + pinctrl-names = "default"; + pinctrl-0 = <0x24>; + clock-frequency = <0xa98a581>; + xlnx,mio-bank = <0x01>; + phandle = <0x8e>; + }; + + smmu@fd800000 { + compatible = "arm,mmu-500"; + reg = <0x00 0xfd800000 0x00 0x20000>; + #iommu-cells = <0x01>; + status = "disabled"; + #global-interrupts = <0x01>; + interrupt-parent = <0x05>; + interrupts = <0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04 0x00 0x9b 0x04>; + phandle = <0x14>; + }; + }; + + pss_ref_clk { + bootph-all; + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x1fca055>; + phandle = <0x0b>; + }; + + video_clk { + bootph-all; + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x19bfcc0>; + phandle = <0x0c>; + }; + + pss_alt_ref_clk { + bootph-all; + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x00>; + phandle = <0x0d>; + }; + + gt_crx_ref_clk { + bootph-all; + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x66ff300>; + phandle = <0x0f>; + }; + + aux_ref_clk { + bootph-all; + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x19bfcc0>; + phandle = <0x0e>; + }; + + aliases { + mmc0 = "/axi/mmc@ff170000"; + serial0 = "/axi/serial@ff000000"; + }; + + firmware { + + zynqmp-firmware { + compatible = "xlnx,zynqmp-firmware"; + bootph-all; + method = "smc"; + #power-domain-cells = <0x01>; + phandle = <0x12>; + + zynqmp-power { + bootph-all; + compatible = "xlnx,zynqmp-power"; + interrupt-parent = <0x05>; + interrupts = <0x00 0x23 0x04>; + mboxes = <0x0a 0x00 0x0a 0x01>; + mbox-names = "tx\0rx"; + phandle = <0x42>; + }; + + pcap { + compatible = "xlnx,zynqmp-pcap-fpga"; + phandle = <0x10>; + }; + + pinctrl { + compatible = "xlnx,zynqmp-pinctrl"; + status = "okay"; + phandle = <0x56>; + + uart0-default { + phandle = <0x25>; + + mux { + groups = "uart0_4_grp"; + function = "uart0"; + }; + + conf { + groups = "uart0_4_grp"; + slew-rate = <0x01>; + power-source = <0x01>; + }; + + conf-rx { + pins = "MIO18"; + bias-high-impedance; + }; + + conf-tx { + pins = "MIO19"; + bias-disable; + }; + }; + + sdhci1-default { + phandle = <0x24>; + + mux { + groups = "sdio1_0_grp"; + function = "sdio1"; + }; + + conf { + groups = "sdio1_0_grp"; + slew-rate = <0x01>; + power-source = <0x01>; + bias-disable; + }; + + mux-cd { + groups = "sdio1_cd_0_grp"; + function = "sdio1_cd"; + }; + + conf-cd { + groups = "sdio1_cd_0_grp"; + bias-high-impedance; + bias-pull-up; + slew-rate = <0x01>; + power-source = <0x01>; + }; + + mux-wp { + groups = "sdio1_wp_0_grp"; + function = "sdio1_wp"; + }; + + conf-wp { + groups = "sdio1_wp_0_grp"; + bias-high-impedance; + bias-pull-up; + slew-rate = <0x01>; + power-source = <0x01>; + }; + }; + }; + + + clock-controller { + bootph-all; + #clock-cells = <0x01>; + compatible = "xlnx,zynqmp-clk"; + clocks = <0x0b 0x0c 0x0d 0x0e 0x0f>; + clock-names = "pss_ref_clk\0video_clk\0pss_alt_ref_clk\0aux_ref_clk\0gt_crx_ref_clk"; + phandle = <0x04>; + }; + }; + }; + + chosen { + bootargs = "earlycon console=ttyPS0,115200 root=/dev/mmcblk0p2 rootwait rw"; + // linux,initrd-start = <0x4000000>; + // linux,initrd-end = <0x4536393>; + // linux,initrd-end = <0x4800000>; + stdout-path = "serial0:115200n8"; + }; +}; diff --git a/images/riscv64/rCore-Tutorial-v3/.gitignore b/images/loongarch64/devicetree/.gitignore similarity index 100% rename from images/riscv64/rCore-Tutorial-v3/.gitignore rename to images/loongarch64/devicetree/.gitignore diff --git a/images/loongarch64/devicetree/Makefile b/images/loongarch64/devicetree/Makefile new file mode 100644 index 00000000..df4cda2a --- /dev/null +++ b/images/loongarch64/devicetree/Makefile @@ -0,0 +1,19 @@ +# Find all .dts files in the current directory +DTS_FILES := $(wildcard *.dts) + +# Replace the .dts file extension with .dtb for all files found +DTB_FILES := $(DTS_FILES:.dts=.dtb) + +# Default target +all: $(DTB_FILES) + +# Pattern rule: how to generate a .dtb from a .dts +%.dtb: %.dts +# dtc -I dts -O dtb $< -o $@ + cpp -nostdinc -I include -undef -x assembler-with-cpp -o $*.dts.tmp $< + dtc -I dts -O dtb $*.dts.tmp -o $@ + rm -f $*.dts.tmp + +# Clean target to remove generated files +clean: + rm -f $(DTB_FILES) diff --git a/images/loongarch64/devicetree/loongson3.dtsi b/images/loongarch64/devicetree/loongson3.dtsi new file mode 100644 index 00000000..c21c1a42 --- /dev/null +++ b/images/loongarch64/devicetree/loongson3.dtsi @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +/ { + /* + * Loongson-3 may have as many as 4 nodes, each node has 4 cores. + * Each core has its own pcache and cores in the same node share scache. + */ + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "loongson,loongson3"; + device_type = "cpu"; + reg = <0x0>; + l2-cache = <&vcache0>; + next-level-cache = <&scache0>; + numa-node-id = <0>; + }; + + // cpu@1 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x1>; + // l2-cache = <&vcache1>; + // next-level-cache = <&scache0>; + // }; + + // cpu@2 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x2>; + // l2-cache = <&vcache2>; + // next-level-cache = <&scache0>; + // }; + + // cpu@3 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x3>; + // l2-cache = <&vcache3>; + // next-level-cache = <&scache0>; + // }; + + // cpu@4 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x4>; + // l2-cache = <&vcache4>; + // next-level-cache = <&scache1>; + // }; + + // cpu@5 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x5>; + // l2-cache = <&vcache5>; + // next-level-cache = <&scache1>; + // }; + + // cpu@6 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x6>; + // l2-cache = <&vcache6>; + // next-level-cache = <&scache1>; + // }; + + // cpu@7 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x7>; + // l2-cache = <&vcache7>; + // next-level-cache = <&scache1>; + // }; + + // cpu@8 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x8>; + // l2-cache = <&vcache8>; + // next-level-cache = <&scache2>; + // }; + + // cpu@9 { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0x9>; + // l2-cache = <&vcache9>; + // next-level-cache = <&scache2>; + // }; + + // cpu@a { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0xa>; + // l2-cache = <&vcachea>; + // next-level-cache = <&scache2>; + // }; + + // cpu@b { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0xb>; + // l2-cache = <&vcacheb>; + // next-level-cache = <&scache2>; + // }; + + // cpu@c { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0xc>; + // l2-cache = <&vcachec>; + // next-level-cache = <&scache3>; + // }; + + // cpu@d { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0xd>; + // l2-cache = <&vcached>; + // next-level-cache = <&scache3>; + // }; + + // cpu@e { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0xe>; + // l2-cache = <&vcachee>; + // next-level-cache = <&scache3>; + // }; + + // cpu@f { + // compatible = "loongson,loongson3"; + // device_type = "cpu"; + // reg = <0xf>; + // l2-cache = <&vcachef>; + // next-level-cache = <&scache3>; + // }; + + vcache0: l2-cache0 { + compatible = "cache"; + next-level-cache = <&scache0>; + }; + + vcache1: l2-cache1 { + compatible = "cache"; + next-level-cache = <&scache0>; + }; + + vcache2: l2-cache2 { + compatible = "cache"; + next-level-cache = <&scache0>; + }; + + vcache3: l2-cache3 { + compatible = "cache"; + next-level-cache = <&scache0>; + }; + + vcache4: l2-cache4 { + compatible = "cache"; + next-level-cache = <&scache1>; + }; + + vcache5: l2-cache5 { + compatible = "cache"; + next-level-cache = <&scache1>; + }; + + vcache6: l2-cache6 { + compatible = "cahce"; + next-level-cache = <&scache1>; + }; + + vcache7: l2-cache7 { + compatible = "cache"; + next-level-cache = <&scache1>; + }; + + vcache8: l2-cache8 { + compatible = "cache"; + next-level-cache = <&scache2>; + }; + + vcache9: l2-cache9 { + compatible = "cache"; + next-level-cache = <&scache2>; + }; + + vcachea: l2-cachea { + compatible = "cache"; + next-level-cache = <&scache2>; + }; + + vcacheb: l2-cacheb { + compatible = "cache"; + next-level-cache = <&scache2>; + }; + + vcachec: l2-cachec { + compatible = "cache"; + next-level-cache = <&scache3>; + }; + + vcached: l2-cached { + compatible = "cache"; + next-level-cache = <&scache3>; + }; + + vcachee: l2-cachee { + compatible = "cache"; + next-level-cache = <&scache3>; + }; + + vcachef: l2-cachef { + compatible = "cache"; + next-level-cache = <&scache3>; + }; + + scache0: l3-cache0 { + compatible = "cache"; + }; + + scache1: l3-cache1 { + compatible = "cache"; + }; + + scache2: l3-cache2 { + compatible = "cache"; + }; + + scache3: l3-cache3 { + compatible = "cache"; + }; + }; +}; diff --git a/images/loongarch64/devicetree/loongson3_ls7a.dts b/images/loongarch64/devicetree/loongson3_ls7a.dts new file mode 100644 index 00000000..949045a3 --- /dev/null +++ b/images/loongarch64/devicetree/loongson3_ls7a.dts @@ -0,0 +1,105 @@ +/dts-v1/; +/* +This is a customized dts file for 3A5000+7A2000 board. +wheatfox 2024 +*/ +#include "loongson3.dtsi" +/ { + model = "loongson,generic"; + compatible = "loongson,loongson3"; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &uart0; + // serial1 = &uart1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs = "earlycon console=ttyS0,115200n8 initramfs_async=false rootwait"; + // linux,initrd-start = <0 0x90000000>; + // linux,initrd-end = <0 0x93c00000>; // size = 60M ext4 + }; + + mem: memory { + name = "memory"; + device_type = "memory"; + // reg = <0 0x00200000 0 0x0ee00000 + // 0 0x90000000 0 0x10000000>; + reg = <0 0x00200000 0 0x0ee00000 + 0 0x90000000 0 0x10000000>; + }; + + cpuic: interrupt-controller { + compatible = "loongson,cpu-interrupt-controller"; + interrupt-controller; + #interrupt-cells = <1>; + }; + + icu: interrupt-controller@1fe01400 { + compatible = "loongson"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0 0x1fe01400 0 0x40 + 0 0x1fe01040 0 16>; + interrupt-parent = <&cpuic>; + interrupt-names = "cascade"; + interrupts = <3>; /* HW IP1 */ + }; + + board: platform { + compatible = "loongson,nbus", "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + // enable-lpc-irq; + // ranges = <0x000 0x00000000 0x000 0x00000000 0x20000000 + // 0x000 0x40000000 0x000 0x40000000 0x40000000 + // 0xe00 0x00000000 0xe00 0x00000000 0x80000000>; + ranges = <0 0x10000000 0 0x10000000 0 0x10000000 + 0 0x2000000 0 0x2000000 0 0x2000000 + 0 0x20000000 0 0x20000000 0 0x10000000 + 0 0x40000000 0 0x40000000 0 0x40000000 + 0xfe 0x00000000 0xfe 0x00000000 0 0x40000000>; + + osc_clk: osc-clock@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <100000000>; + clock-output-names = "ref_clk"; + reg = <0 0 0 0>; + }; + + clks: clock-controller@1fe00480 { + compatible = "loongson,ls2x-clk"; + reg = <0 0x1fe00480 0 1>; + clocks = <&osc_clk>; + clock-names = "ref_clk"; + #clock-cells = <1>; + }; + + uart0: serial@1fe001e0 { + device_type = "serial"; + compatible = "ns16550a"; + reg = <0 0x1fe001e0 0 0x10>; + clocks = <&clks 12>; + clock-frequency = <100000000>; + // interrupts = <10>; + interrupt-parent = <&icu>; + no-loopback-test; + status = "okay"; + }; + + // uart1: serial@1fe001e8 { + // device_type = "serial"; + // compatible = "ns16550a"; + // reg = <0 0x1fe001e8 0 0x10>; + // clocks = <&clks 12>; + // clock-frequency = <100000000>; + // // interrupts = <11>; + // // interrupt-parent = <&icu>; + // no-loopback-test; + // status = "disabled"; + // }; + }; +}; diff --git a/images/riscv64/.gitignore b/images/riscv64/.gitignore deleted file mode 100644 index 60193e29..00000000 --- a/images/riscv64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.img \ No newline at end of file diff --git a/images/riscv64/Image-62 b/images/riscv64/Image-62 deleted file mode 100755 index bd67f4d1..00000000 Binary files a/images/riscv64/Image-62 and /dev/null differ diff --git a/images/riscv64/bao-linux.bin b/images/riscv64/bao-linux.bin deleted file mode 100755 index b4fb1881..00000000 Binary files a/images/riscv64/bao-linux.bin and /dev/null differ diff --git a/images/riscv64/devicetree/Makefile b/images/riscv64/devicetree/Makefile new file mode 100644 index 00000000..c45c1b29 --- /dev/null +++ b/images/riscv64/devicetree/Makefile @@ -0,0 +1,16 @@ +# Find all .dts files in the current directory +DTS_FILES := $(wildcard *.dts) + +# Replace the .dts file extension with .dtb for all files found +DTB_FILES := $(DTS_FILES:.dts=.dtb) + +# Default target +all: $(DTB_FILES) + +# Pattern rule: how to generate a .dtb from a .dts +%.dtb: %.dts + dtc -I dts -O dtb $< -o $@ + +# Clean target to remove generated files +clean: + rm -f $(DTB_FILES) diff --git a/images/riscv64/devicetree/linux1-aia.dts b/images/riscv64/devicetree/linux1-aia.dts new file mode 100644 index 00000000..a4f7332b --- /dev/null +++ b/images/riscv64/devicetree/linux1-aia.dts @@ -0,0 +1,171 @@ +/dts-v1/; + +/ { + #address-cells = <0x2>; + #size-cells = <0x2>; + + cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + timebase-frequency = <10000000>; + cpu@0 { + device_type = "cpu"; + reg = <0x0>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_ssaia_sstc"; + mmu-type = "riscv,sv39"; + + cpu0_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + cpu@1 { + device_type = "cpu"; + reg = <0x1>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_ssaia_sstc"; + mmu-type = "riscv,sv39"; + + cpu1_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + cpu@2 { + device_type = "cpu"; + reg = <0x2>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_ssaia_sstc"; + mmu-type = "riscv,sv39"; + + cpu2_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + }; + + memory@83000000 { + device_type = "memory"; + reg = <0x0 0x83000000 0x0 0x1D000000>; + }; + + reserved-memory { + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + + nonroot@0x83000000 { + no-map; + reg = <0x00 0x83000000 0x00 0x0C000000>; + }; + + dtbfile@0x8f000000 { + no-map; + reg = <0x00 0x8f000000 0x00 0x01000000>; + }; + }; + +soc{ + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + + aplic@d000000 { + phandle = <0x08>; + riscv,num-sources = <0x60>; + reg = <0x00 0xd000000 0x00 0x8000>; + msi-parent = <0x06>; + interrupt-controller; + #interrupt-cells = <0x02>; + compatible = "riscv,aplic"; + }; + + imsics@28000000 { + phandle = <0x06>; + riscv,num-ids = <0xff>; + reg = <0x00 0x28000000 0x00 0x4000>; + interrupts-extended = < + &cpu0_intc 9 + &cpu1_intc 9 + &cpu2_intc 9 + >; + msi-controller; + interrupt-controller; + #interrupt-cells = <0x00>; + compatible = "riscv,imsics"; + }; + + virtio_mmio@10008000 { + interrupts = <0x8 0x04>; + interrupt-parent = <0x08>; + reg = <0x0 0x10008000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + virtio_mmio@10005000 { + interrupts = <0x5 0x04>; + interrupt-parent = <0x08>; + reg = <0x0 0x10005000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + virtio_mmio@10004000 { + interrupts = <0x4 0x04>; + interrupt-parent = <0x08>; + reg = <0x0 0x10004000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + virtio_mmio@10003000 { + interrupts = <0x3 0x04>; + interrupt-parent = <0x08>; + reg = <0x0 0x10003000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + virtio_mmio@10002000 { + interrupts = <0x2 0x04>; + interrupt-parent = <0x08>; + reg = <0x0 0x10002000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + virtio_mmio@10001000 { + interrupts = <0x1 0x04>; + interrupt-parent = <0x08>; + reg = <0x0 0x10001000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + pci@30000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x09 0x20 0x00 0x00 0x00 0x02 0x09 0x21 0x00 0x00 0x00 0x03 0x09 0x22 0x00 0x00 0x00 0x04 0x09 0x23 0x800 0x00 0x00 0x01 0x09 0x21 0x800 0x00 0x00 0x02 0x09 0x22 0x800 0x00 0x00 0x03 0x09 0x23 0x800 0x00 0x00 0x04 0x09 0x20 0x1000 0x00 0x00 0x01 0x09 0x22 0x1000 0x00 0x00 0x02 0x09 0x23 0x1000 0x00 0x00 0x03 0x09 0x20 0x1000 0x00 0x00 0x04 0x09 0x21 0x1800 0x00 0x00 0x01 0x09 0x23 0x1800 0x00 0x00 0x02 0x09 0x20 0x1800 0x00 0x00 0x03 0x09 0x21 0x1800 0x00 0x00 0x04 0x09 0x22>; + ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; + reg = <0x00 0x30000000 0x00 0x10000000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; + #size-cells = <0x02>; + #interrupt-cells = <0x01>; + #address-cells = <0x03>; + }; + uart@10000000 { + interrupts = <0x0a 0x04>; + interrupt-parent = <0x08>; + clock-frequency = "\08@"; + reg = <0x00 0x10000000 0x00 0x100>; + compatible = "ns16550a"; + }; + +}; + chosen { + bootargs = "root=/dev/vda rw earlycon console=ttyS0 init=/bin/bash"; + }; + +}; \ No newline at end of file diff --git a/images/riscv64/devicetree/linux1.dts b/images/riscv64/devicetree/linux1.dts new file mode 100644 index 00000000..84cdb0ee --- /dev/null +++ b/images/riscv64/devicetree/linux1.dts @@ -0,0 +1,176 @@ +/dts-v1/; + +/ { + #address-cells = <0x2>; + #size-cells = <0x2>; + + cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + timebase-frequency = <10000000>; + + cpu@0 { + device_type = "cpu"; + reg = <0x0>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_sstc"; + mmu-type = "riscv,sv39"; + + cpu0_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x1>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_sstc"; + mmu-type = "riscv,sv39"; + + cpu1_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + cpu@2 { + device_type = "cpu"; + reg = <0x2>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_sstc"; + mmu-type = "riscv,sv39"; + + cpu2_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + + }; + + memory@83000000 { + device_type = "memory"; + reg = <0x0 0x83000000 0x0 0x1D000000>; + }; + + reserved-memory { + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + + nonroot@0x83000000 { + no-map; + reg = <0x00 0x83000000 0x00 0x0C000000>; + }; + + dtbfile@0x8f000000 { + no-map; + reg = <0x00 0x8f000000 0x00 0x01000000>; + }; + }; + + soc{ + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + plic: interrupt-controller@c000000 { + riscv,ndev = <60>; + reg = <0x0 0xc000000 0x0 0x4000000>; + interrupts-extended = < + &cpu0_intc 11 &cpu0_intc 9 + &cpu1_intc 11 &cpu1_intc 9 + &cpu2_intc 11 &cpu2_intc 9 + >; + interrupt-controller; + compatible = "riscv,plic0"; + #interrupt-cells = <0x1>; + }; + uart@10000000 { + interrupts = <0x0a>; + interrupt-parent = <&plic>; + clock-frequency = "\08@"; + reg = <0x00 0x10000000 0x00 0x100>; + compatible = "ns16550a"; + }; + pci@30000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x09 0x20 0x00 0x00 0x00 0x02 0x09 0x21 0x00 0x00 0x00 0x03 0x09 0x22 0x00 0x00 0x00 0x04 0x09 0x23 0x800 0x00 0x00 0x01 0x09 0x21 0x800 0x00 0x00 0x02 0x09 0x22 0x800 0x00 0x00 0x03 0x09 0x23 0x800 0x00 0x00 0x04 0x09 0x20 0x1000 0x00 0x00 0x01 0x09 0x22 0x1000 0x00 0x00 0x02 0x09 0x23 0x1000 0x00 0x00 0x03 0x09 0x20 0x1000 0x00 0x00 0x04 0x09 0x21 0x1800 0x00 0x00 0x01 0x09 0x23 0x1800 0x00 0x00 0x02 0x09 0x20 0x1800 0x00 0x00 0x03 0x09 0x21 0x1800 0x00 0x00 0x04 0x09 0x22>; + ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; + reg = <0x00 0x30000000 0x00 0x10000000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; + #size-cells = <0x02>; + #interrupt-cells = <0x01>; + #address-cells = <0x03>; + }; + + virtio_mmio@10008000 { + interrupts = <0x8>; + interrupt-parent = <&plic>; + reg = <0x0 0x10008000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + // virtio_mmio@10007000 { + // interrupts = <0x7>; + // interrupt-parent = <&plic>; + // reg = <0x0 0x10007000 0x0 0x1000>; + // compatible = "virtio,mmio"; + // }; + // virtio_mmio@10006000 { + // interrupts = <0x6>; + // interrupt-parent = <&plic>; + // reg = <0x0 0x10006000 0x0 0x1000>; + // compatible = "virtio,mmio"; + // }; + + virtio_mmio@10005000 { + interrupts = <0x5>; + interrupt-parent = <&plic>; + reg = <0x0 0x10005000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10004000 { + interrupts = <0x4>; + interrupt-parent = <&plic>; + reg = <0x0 0x10004000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10003000 { + interrupts = <0x3>; + interrupt-parent = <&plic>; + reg = <0x0 0x10003000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10002000 { + interrupts = <0x2>; + interrupt-parent = <&plic>; + reg = <0x0 0x10002000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10001000 { + interrupts = <0x1>; + interrupt-parent = <&plic>; + reg = <0x0 0x10001000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + }; + chosen { + bootargs = "root=/dev/vda rw earlycon console=ttyS0 init=/bin/bash"; + }; + +}; diff --git a/images/riscv64/devicetree/linux2.dts b/images/riscv64/devicetree/linux2.dts new file mode 100644 index 00000000..487cfeca --- /dev/null +++ b/images/riscv64/devicetree/linux2.dts @@ -0,0 +1,107 @@ +/dts-v1/; + +/ { + #address-cells = <0x2>; + #size-cells = <0x2>; + + cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + timebase-frequency = <10000000>; + cpu@3 { + device_type = "cpu"; + reg = <0x3>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_sstc"; + mmu-type = "riscv,sv39"; + + cpu3_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + + }; + + memory@84000000 { + device_type = "memory"; + reg = <0x0 0x84000000 0x0 0x08000000>; + }; +soc{ + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + plic: interrupt-controller@c000000 { + riscv,ndev = <60>; + reg = <0x0 0xc000000 0x0 0x4000000>; + interrupts-extended = < + &cpu3_intc 11 &cpu3_intc 9 + >; + interrupt-controller; + compatible = "riscv,plic0"; + #interrupt-cells = <0x1>; + }; + +// virtio_mmio@10008000 { +// interrupts = <0x8>; +// interrupt-parent = <&plic>; +// reg = <0x0 0x10008000 0x0 0x1000>; +// compatible = "virtio,mmio"; +// }; + virtio_mmio@10007000 { + interrupts = <0x7>; + interrupt-parent = <&plic>; + reg = <0x0 0x10007000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; + virtio_mmio@10006000 { + interrupts = <0x6>; + interrupt-parent = <&plic>; + reg = <0x0 0x10006000 0x0 0x1000>; + compatible = "virtio,mmio"; + }; +// +// virtio_mmio@10005000 { +// interrupts = <0x5>; +// interrupt-parent = <&plic>; +// reg = <0x0 0x10005000 0x0 0x1000>; +// compatible = "virtio,mmio"; +// }; +// +// virtio_mmio@10004000 { +// interrupts = <0x4>; +// interrupt-parent = <&plic>; +// reg = <0x0 0x10004000 0x0 0x1000>; +// compatible = "virtio,mmio"; +// }; +// +// virtio_mmio@10003000 { +// interrupts = <0x3>; +// interrupt-parent = <&plic>; +// reg = <0x0 0x10003000 0x0 0x1000>; +// compatible = "virtio,mmio"; +// }; +// +// virtio_mmio@10002000 { +// interrupts = <0x2>; +// interrupt-parent = <&plic>; +// reg = <0x0 0x10002000 0x0 0x1000>; +// compatible = "virtio,mmio"; +// }; +// +// virtio_mmio@10001000 { +// interrupts = <0x1>; +// interrupt-parent = <&plic>; +// reg = <0x0 0x10001000 0x0 0x1000>; +// compatible = "virtio,mmio"; +// }; + +}; + chosen { + bootargs = "root=/dev/vda rw earlycon console=hvc0"; + }; + +}; \ No newline at end of file diff --git a/images/riscv64/linux3.dts b/images/riscv64/devicetree/linux4.dts similarity index 58% rename from images/riscv64/linux3.dts rename to images/riscv64/devicetree/linux4.dts index c0ef7c94..92c27f21 100644 --- a/images/riscv64/linux3.dts +++ b/images/riscv64/devicetree/linux4.dts @@ -14,7 +14,7 @@ reg = <0x0>; status = "okay"; compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; + riscv,isa = "rv64imafdcsu_sstc"; mmu-type = "riscv,sv39"; cpu0_intc: interrupt-controller { @@ -29,7 +29,7 @@ reg = <0x1>; status = "okay"; compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; + riscv,isa = "rv64imafdcsu_sstc"; mmu-type = "riscv,sv39"; cpu1_intc: interrupt-controller { @@ -43,7 +43,7 @@ reg = <0x2>; status = "okay"; compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; + riscv,isa = "rv64imafdcsu_sstc"; mmu-type = "riscv,sv39"; cpu2_intc: interrupt-controller { @@ -52,6 +52,21 @@ compatible = "riscv,cpu-intc"; }; }; + cpu@3 { + device_type = "cpu"; + reg = <0x3>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdcsu_sstc"; + mmu-type = "riscv,sv39"; + + cpu3_intc: interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + }; + }; + }; @@ -76,20 +91,28 @@ soc{ compatible = "riscv,plic0"; #interrupt-cells = <0x1>; }; + uart@10000000 { + interrupts = <0x0a>; + interrupt-parent = <&plic>; + clock-frequency = "\08@"; + reg = <0x00 0x10000000 0x00 0x100>; + compatible = "ns16550a"; + }; + pci@30000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x09 0x20 0x00 0x00 0x00 0x02 0x09 0x21 0x00 0x00 0x00 0x03 0x09 0x22 0x00 0x00 0x00 0x04 0x09 0x23 0x800 0x00 0x00 0x01 0x09 0x21 0x800 0x00 0x00 0x02 0x09 0x22 0x800 0x00 0x00 0x03 0x09 0x23 0x800 0x00 0x00 0x04 0x09 0x20 0x1000 0x00 0x00 0x01 0x09 0x22 0x1000 0x00 0x00 0x02 0x09 0x23 0x1000 0x00 0x00 0x03 0x09 0x20 0x1000 0x00 0x00 0x04 0x09 0x21 0x1800 0x00 0x00 0x01 0x09 0x23 0x1800 0x00 0x00 0x02 0x09 0x20 0x1800 0x00 0x00 0x03 0x09 0x21 0x1800 0x00 0x00 0x04 0x09 0x22>; + ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; + reg = <0x00 0x30000000 0x00 0x10000000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; + #size-cells = <0x02>; + #interrupt-cells = <0x01>; + #address-cells = <0x03>; + }; - virtio_mmio@10008000 { - interrupts = <0x8>; - interrupt-parent = <&plic>; - reg = <0x0 0x10008000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x7>; - interrupt-parent = <&plic>; - reg = <0x0 0x10007000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; virtio_mmio@10006000 { interrupts = <0x6>; @@ -132,9 +155,12 @@ soc{ reg = <0x0 0x10001000 0x0 0x1000>; compatible = "virtio,mmio"; }; + + + }; chosen { - bootargs = "root=/dev/vda rw earlycon console=hvc0"; + bootargs = "root=/dev/vda rw earlycon console=ttyS0 ip=192.168.42.15"; }; -}; +}; \ No newline at end of file diff --git a/images/riscv64/fw_jump.bin b/images/riscv64/fw_jump.bin deleted file mode 100755 index 1082689d..00000000 Binary files a/images/riscv64/fw_jump.bin and /dev/null differ diff --git a/images/riscv64/linux.dtb b/images/riscv64/linux.dtb deleted file mode 100644 index 7ef1fa25..00000000 Binary files a/images/riscv64/linux.dtb and /dev/null differ diff --git a/images/riscv64/linux.dts b/images/riscv64/linux.dts deleted file mode 100644 index 32b6d597..00000000 --- a/images/riscv64/linux.dts +++ /dev/null @@ -1,198 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "riscv-virtio"; - model = "riscv-virtio,qemu"; - - fw-cfg@10100000 { - dma-coherent; - reg = <0x00 0x10100000 0x00 0x18>; - compatible = "qemu,fw-cfg-mmio"; - }; - - flash@20000000 { - bank-width = <0x04>; - reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; - compatible = "cfi-flash"; - }; - - chosen { - bootargs = "root=/dev/vda rw earlycon console=ttyS0"; - }; - - platform@4000000 { - interrupt-parent = <0x03>; - ranges = <0x00 0x00 0x4000000 0x2000000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "qemu,platform\0simple-bus"; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x00 0x90000000 0x00 0x10000000>; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - timebase-frequency = <0x989680>; - - cpu@0 { - phandle = <0x01>; - device_type = "cpu"; - reg = <0x00>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x02>; - }; - }; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x01>; - }; - }; - }; - }; - soc { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "simple-bus"; - ranges; - - rtc@101000 { - interrupts = <0x0b>; - interrupt-parent = <0x03>; - reg = <0x00 0x101000 0x00 0x1000>; - compatible = "google,goldfish-rtc"; - }; - - uart@10000000 { - interrupts = <0x0a>; - interrupt-parent = <0x03>; - clock-frequency = "\08@"; - reg = <0x00 0x10000000 0x00 0x100>; - compatible = "ns16550a"; - }; - - poweroff { - value = <0x5555>; - offset = <0x00>; - regmap = <0x04>; - compatible = "syscon-poweroff"; - }; - - reboot { - value = <0x7777>; - offset = <0x00>; - regmap = <0x04>; - compatible = "syscon-reboot"; - }; - - test@100000 { - phandle = <0x04>; - reg = <0x00 0x100000 0x00 0x1000>; - compatible = "sifive,test1\0sifive,test0\0syscon"; - }; - - pci@30000000 { - interrupt-map-mask = <0x1800 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; - ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; - reg = <0x00 0x30000000 0x00 0x10000000>; - dma-coherent; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - device_type = "pci"; - compatible = "pci-host-ecam-generic"; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - #address-cells = <0x03>; - }; - - virtio_mmio@10008000 { - interrupts = <0x08>; - interrupt-parent = <0x03>; - reg = <0x00 0x10008000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x07>; - interrupt-parent = <0x03>; - reg = <0x00 0x10007000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10006000 { - interrupts = <0x06>; - interrupt-parent = <0x03>; - reg = <0x00 0x10006000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10005000 { - interrupts = <0x05>; - interrupt-parent = <0x03>; - reg = <0x00 0x10005000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10004000 { - interrupts = <0x04>; - interrupt-parent = <0x03>; - reg = <0x00 0x10004000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10003000 { - interrupts = <0x03>; - interrupt-parent = <0x03>; - reg = <0x00 0x10003000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10002000 { - interrupts = <0x02>; - interrupt-parent = <0x03>; - reg = <0x00 0x10002000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10001000 { - interrupts = <0x01>; - interrupt-parent = <0x03>; - reg = <0x00 0x10001000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - plic@c000000 { - phandle = <0x03>; - riscv,ndev = <0x60>; - reg = <0x00 0xc000000 0x00 0x600000>; - interrupts-extended = <0x02 0x0b 0x02 0x09>; - interrupt-controller; - compatible = "sifive,plic-1.0.0\0riscv,plic0"; - #interrupt-cells = <0x01>; - }; - - clint@2000000 { - interrupts-extended = <0x02 0x03 0x02 0x07>; - reg = <0x00 0x2000000 0x00 0x10000>; - compatible = "sifive,clint0\0riscv,clint0"; - }; - }; -}; diff --git a/images/riscv64/linux2.dtb b/images/riscv64/linux2.dtb deleted file mode 100644 index bb0ef546..00000000 Binary files a/images/riscv64/linux2.dtb and /dev/null differ diff --git a/images/riscv64/linux2.dts b/images/riscv64/linux2.dts deleted file mode 100644 index 017df11a..00000000 --- a/images/riscv64/linux2.dts +++ /dev/null @@ -1,191 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "riscv-virtio"; - model = "riscv-virtio,qemu"; - - fw-cfg@10100000 { - dma-coherent; - reg = <0x00 0x10100000 0x00 0x18>; - compatible = "qemu,fw-cfg-mmio"; - }; - - flash@20000000 { - bank-width = <0x04>; - reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; - compatible = "cfi-flash"; - }; - - chosen { - bootargs = "root=/dev/vda rw earlycon console=hvc0"; - }; - - platform@4000000 { - interrupt-parent = <0x03>; - ranges = <0x00 0x00 0x4000000 0x2000000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "qemu,platform\0simple-bus"; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x00 0x90000000 0x00 0x10000000>; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - timebase-frequency = <0x989680>; - - cpu@0 { - phandle = <0x01>; - device_type = "cpu"; - reg = <0x00>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x02>; - }; - }; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x01>; - }; - }; - }; - }; - soc { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "simple-bus"; - ranges; - - rtc@101000 { - interrupts = <0x0b>; - interrupt-parent = <0x03>; - reg = <0x00 0x101000 0x00 0x1000>; - compatible = "google,goldfish-rtc"; - }; - - - poweroff { - value = <0x5555>; - offset = <0x00>; - regmap = <0x04>; - compatible = "syscon-poweroff"; - }; - - reboot { - value = <0x7777>; - offset = <0x00>; - regmap = <0x04>; - compatible = "syscon-reboot"; - }; - - test@100000 { - phandle = <0x04>; - reg = <0x00 0x100000 0x00 0x1000>; - compatible = "sifive,test1\0sifive,test0\0syscon"; - }; - - pci@30000000 { - interrupt-map-mask = <0x1800 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; - ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; - reg = <0x00 0x30000000 0x00 0x10000000>; - dma-coherent; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - device_type = "pci"; - compatible = "pci-host-ecam-generic"; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - #address-cells = <0x03>; - }; - - virtio_mmio@10008000 { - interrupts = <0x08>; - interrupt-parent = <0x03>; - reg = <0x00 0x10008000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x07>; - interrupt-parent = <0x03>; - reg = <0x00 0x10007000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10006000 { - interrupts = <0x06>; - interrupt-parent = <0x03>; - reg = <0x00 0x10006000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10005000 { - interrupts = <0x05>; - interrupt-parent = <0x03>; - reg = <0x00 0x10005000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10004000 { - interrupts = <0x04>; - interrupt-parent = <0x03>; - reg = <0x00 0x10004000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10003000 { - interrupts = <0x03>; - interrupt-parent = <0x03>; - reg = <0x00 0x10003000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10002000 { - interrupts = <0x02>; - interrupt-parent = <0x03>; - reg = <0x00 0x10002000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10001000 { - interrupts = <0x01>; - interrupt-parent = <0x03>; - reg = <0x00 0x10001000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - plic@c000000 { - phandle = <0x03>; - riscv,ndev = <0x60>; - reg = <0x00 0xc000000 0x00 0x600000>; - interrupts-extended = <0x02 0x0b 0x02 0x09>; - interrupt-controller; - compatible = "sifive,plic-1.0.0\0riscv,plic0"; - #interrupt-cells = <0x01>; - }; - - clint@2000000 { - interrupts-extended = <0x02 0x03 0x02 0x07>; - reg = <0x00 0x2000000 0x00 0x10000>; - compatible = "sifive,clint0\0riscv,clint0"; - }; - }; -}; diff --git a/images/riscv64/linux3.dtb b/images/riscv64/linux3.dtb deleted file mode 100644 index 0fb3d51a..00000000 Binary files a/images/riscv64/linux3.dtb and /dev/null differ diff --git a/images/riscv64/linux4.dtb b/images/riscv64/linux4.dtb deleted file mode 100644 index dfed8bc3..00000000 Binary files a/images/riscv64/linux4.dtb and /dev/null differ diff --git a/images/riscv64/linux4.dts b/images/riscv64/linux4.dts deleted file mode 100644 index 8068dd45..00000000 --- a/images/riscv64/linux4.dts +++ /dev/null @@ -1,136 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x2>; - #size-cells = <0x2>; - - cpus { - #address-cells = <0x1>; - #size-cells = <0x0>; - timebase-frequency = <10000000>; - - cpu@0 { - device_type = "cpu"; - reg = <0x0>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv48"; - - cpu0_intc: interrupt-controller { - #interrupt-cells = <0x1>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - }; - }; - - cpu@1 { - device_type = "cpu"; - reg = <0x1>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv48"; - - cpu1_intc: interrupt-controller { - #interrupt-cells = <0x1>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - }; - }; - - cpu@2 { - device_type = "cpu"; - reg = <0x2>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv48"; - - cpu2_intc: interrupt-controller { - #interrupt-cells = <0x1>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - }; - }; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x0 0x90000000 0x0 0x40000000>; - }; - - plic: interrupt-controller@c000000 { - riscv,ndev = <60>; - reg = <0x0 0xc000000 0x0 0x4000000>; - interrupts-extended = < - &cpu0_intc 11 &cpu0_intc 9 - &cpu1_intc 11 &cpu1_intc 9 - &cpu2_intc 11 &cpu2_intc 9 - >; - interrupt-controller; - compatible = "riscv,plic0"; - #interrupt-cells = <0x1>; - }; - - virtio_mmio@10008000 { - interrupts = <0x8>; - interrupt-parent = <&plic>; - reg = <0x0 0x10008000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x7>; - interrupt-parent = <&plic>; - reg = <0x0 0x10007000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10006000 { - interrupts = <0x6>; - interrupt-parent = <&plic>; - reg = <0x0 0x10006000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10005000 { - interrupts = <0x5>; - interrupt-parent = <&plic>; - reg = <0x0 0x10005000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10004000 { - interrupts = <0x4>; - interrupt-parent = <&plic>; - reg = <0x0 0x10004000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10003000 { - interrupts = <0x3>; - interrupt-parent = <&plic>; - reg = <0x0 0x10003000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10002000 { - interrupts = <0x2>; - interrupt-parent = <&plic>; - reg = <0x0 0x10002000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10001000 { - interrupts = <0x1>; - interrupt-parent = <&plic>; - reg = <0x0 0x10001000 0x0 0x1000>; - compatible = "virtio,mmio"; - }; - - chosen { - bootargs = "earlycon console=hvc0"; - }; - -}; \ No newline at end of file diff --git a/images/riscv64/os b/images/riscv64/os deleted file mode 100755 index c7588c18..00000000 Binary files a/images/riscv64/os and /dev/null differ diff --git a/images/riscv64/os.bin b/images/riscv64/os.bin deleted file mode 100755 index c0e125b5..00000000 Binary files a/images/riscv64/os.bin and /dev/null differ diff --git a/images/riscv64/os_ch4_802.bin b/images/riscv64/os_ch4_802.bin deleted file mode 100755 index 010a5d42..00000000 Binary files a/images/riscv64/os_ch4_802.bin and /dev/null differ diff --git a/images/riscv64/os_ch4_804.bin b/images/riscv64/os_ch4_804.bin deleted file mode 100755 index 759a9407..00000000 Binary files a/images/riscv64/os_ch4_804.bin and /dev/null differ diff --git a/images/riscv64/os_ch5.dtb b/images/riscv64/os_ch5.dtb deleted file mode 100644 index 8d3d604b..00000000 Binary files a/images/riscv64/os_ch5.dtb and /dev/null differ diff --git a/images/riscv64/os_ch5.dts b/images/riscv64/os_ch5.dts deleted file mode 100644 index 7f7c8ff0..00000000 --- a/images/riscv64/os_ch5.dts +++ /dev/null @@ -1,62 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x2>; - #size-cells = <0x2>; - - cpus { - #address-cells = <0x1>; - #size-cells = <0x0>; - timebase-frequency = <10000000>; - - cpu@3 { - device_type = "cpu"; - reg = <0x3>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - cpu3_intc: interrupt-controller { - #interrupt-cells = <0x1>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - }; - }; - - }; - - memory@80000000 { - device_type = "memory"; - reg = <0x0 0x80200000 0x0 0x1000000>; - }; -soc{ - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "simple-bus"; - ranges; - plic: interrupt-controller@c000000 { - riscv,ndev = <60>; - reg = <0x0 0xc000000 0x0 0x4000000>; - interrupts-extended = < - &cpu3_intc 11 &cpu3_intc 9 - >; - interrupt-controller; - compatible = "riscv,plic0"; - #interrupt-cells = <0x1>; - }; - - uart@10000000 { - interrupts = <0x0a>; - interrupt-parent = <&plic>; - clock-frequency = "\08@"; - reg = <0x00 0x10000000 0x00 0x100>; - compatible = "ns16550a"; - }; - -}; - chosen { - bootargs = "root=/dev/vda rw earlycon console=ttyS0"; - }; - -}; diff --git a/images/riscv64/os_ch5_802.bin b/images/riscv64/os_ch5_802.bin deleted file mode 100755 index 091696c2..00000000 Binary files a/images/riscv64/os_ch5_802.bin and /dev/null differ diff --git a/images/riscv64/os_ch5_804.bin b/images/riscv64/os_ch5_804.bin deleted file mode 100755 index 569633e2..00000000 Binary files a/images/riscv64/os_ch5_804.bin and /dev/null differ diff --git a/images/riscv64/qemu.dtb b/images/riscv64/qemu.dtb deleted file mode 100644 index 7420e083..00000000 Binary files a/images/riscv64/qemu.dtb and /dev/null differ diff --git a/images/riscv64/qemu.dts b/images/riscv64/qemu.dts deleted file mode 100644 index a1b842e0..00000000 --- a/images/riscv64/qemu.dts +++ /dev/null @@ -1,264 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "riscv-virtio"; - model = "riscv-virtio,qemu"; - - - fw-cfg@10100000 { - dma-coherent; - reg = <0x00 0x10100000 0x00 0x18>; - compatible = "qemu,fw-cfg-mmio"; - }; - - flash@20000000 { - bank-width = <0x04>; - reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; - compatible = "cfi-flash"; - }; - - chosen { - bootargs = "root=/dev/vda rw earlycon console=hvc0"; - }; - - poweroff { - value = <0x5555>; - offset = <0x00>; - regmap = <0x0a>; - compatible = "syscon-poweroff"; - }; - - reboot { - value = <0x7777>; - offset = <0x00>; - regmap = <0x0a>; - compatible = "syscon-reboot"; - }; - - platform-bus@4000000 { - interrupt-parent = <0x09>; - ranges = <0x00 0x00 0x4000000 0x2000000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "qemu,platform\0simple-bus"; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x0 0x90000000 0x0 0x10000000>; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - timebase-frequency = <0x989680>; - - cpu@0 { - phandle = <0x07>; - device_type = "cpu"; - reg = <0x00>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x08>; - }; - }; - - cpu@1 { - phandle = <0x05>; - device_type = "cpu"; - reg = <0x01>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x06>; - }; - }; - - cpu@2 { - phandle = <0x03>; - device_type = "cpu"; - reg = <0x02>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x04>; - }; - }; - - cpu@3 { - phandle = <0x01>; - device_type = "cpu"; - reg = <0x03>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdcsu"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x02>; - }; - }; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x07>; - }; - - core1 { - cpu = <0x05>; - }; - - core2 { - cpu = <0x03>; - }; - - core3 { - cpu = <0x01>; - }; - }; - }; - }; - - soc { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "simple-bus"; - ranges; - - rtc@101000 { - interrupts = <0x0b>; - interrupt-parent = <0x09>; - reg = <0x00 0x101000 0x00 0x1000>; - compatible = "google,goldfish-rtc"; - }; - - uart@10000000 { - interrupts = <0x0a>; - interrupt-parent = <0x09>; - clock-frequency = "\08@"; - reg = <0x00 0x10000000 0x00 0x100>; - compatible = "ns16550a"; - }; - - test@100000 { - phandle = <0x0a>; - reg = <0x00 0x100000 0x00 0x1000>; - compatible = "sifive,test1\0sifive,test0\0syscon"; - }; - - pci@30000000 { - interrupt-map-mask = <0x1800 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x09 0x20 0x00 0x00 0x00 0x02 0x09 0x21 0x00 0x00 0x00 0x03 0x09 0x22 0x00 0x00 0x00 0x04 0x09 0x23 0x800 0x00 0x00 0x01 0x09 0x21 0x800 0x00 0x00 0x02 0x09 0x22 0x800 0x00 0x00 0x03 0x09 0x23 0x800 0x00 0x00 0x04 0x09 0x20 0x1000 0x00 0x00 0x01 0x09 0x22 0x1000 0x00 0x00 0x02 0x09 0x23 0x1000 0x00 0x00 0x03 0x09 0x20 0x1000 0x00 0x00 0x04 0x09 0x21 0x1800 0x00 0x00 0x01 0x09 0x23 0x1800 0x00 0x00 0x02 0x09 0x20 0x1800 0x00 0x00 0x03 0x09 0x21 0x1800 0x00 0x00 0x04 0x09 0x22>; - ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; - reg = <0x00 0x30000000 0x00 0x10000000>; - dma-coherent; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - device_type = "pci"; - compatible = "pci-host-ecam-generic"; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - #address-cells = <0x03>; - }; - - virtio_mmio@10008000 { - interrupts = <0x08>; - interrupt-parent = <0x09>; - reg = <0x00 0x10008000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x07>; - interrupt-parent = <0x09>; - reg = <0x00 0x10007000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10006000 { - interrupts = <0x06>; - interrupt-parent = <0x09>; - reg = <0x00 0x10006000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10005000 { - interrupts = <0x05>; - interrupt-parent = <0x09>; - reg = <0x00 0x10005000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10004000 { - interrupts = <0x04>; - interrupt-parent = <0x09>; - reg = <0x00 0x10004000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10003000 { - interrupts = <0x03>; - interrupt-parent = <0x09>; - reg = <0x00 0x10003000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10002000 { - interrupts = <0x02>; - interrupt-parent = <0x09>; - reg = <0x00 0x10002000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10001000 { - interrupts = <0x01>; - interrupt-parent = <0x09>; - reg = <0x00 0x10001000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - plic@c000000 { - phandle = <0x09>; - riscv,ndev = <0x60>; - reg = <0x00 0xc000000 0x00 0x600000>; - interrupts-extended = <0x08 0x0b 0x08 0x09 0x06 0x0b 0x06 0x09 0x04 0x0b 0x04 0x09 0x02 0x0b 0x02 0x09>; - interrupt-controller; - compatible = "sifive,plic-1.0.0\0riscv,plic0"; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - }; - - clint@2000000 { - interrupts-extended = <0x08 0x03 0x08 0x07 0x06 0x03 0x06 0x07 0x04 0x03 0x04 0x07 0x02 0x03 0x02 0x07>; - reg = <0x00 0x2000000 0x00 0x10000>; - compatible = "sifive,clint0\0riscv,clint0"; - }; - }; -}; diff --git a/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.bin b/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.bin deleted file mode 100755 index f817ad7d..00000000 Binary files a/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.bin and /dev/null differ diff --git a/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.dts b/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.dts deleted file mode 100644 index 36dcef15..00000000 --- a/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.dts +++ /dev/null @@ -1,199 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "riscv-virtio"; - model = "riscv-virtio,qemu"; - - fw-cfg@10100000 { - dma-coherent; - reg = <0x00 0x10100000 0x00 0x18>; - compatible = "qemu,fw-cfg-mmio"; - }; - - flash@20000000 { - bank-width = <0x04>; - reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; - compatible = "cfi-flash"; - }; - - chosen { - stdout-path = "/soc/uart@10000000"; - }; - - platform@4000000 { - interrupt-parent = <0x03>; - ranges = <0x00 0x00 0x4000000 0x2000000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "qemu,platform\0simple-bus"; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x00 0x90200000 0x00 0x8000000>; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - timebase-frequency = <0x989680>; - - cpu@0 { - phandle = <0x01>; - device_type = "cpu"; - reg = <0x00>; - status = "okay"; - compatible = "riscv"; - riscv,isa = "rv64imafdch_zicsr_zifencei_zba_zbb_zbc_zbs"; - mmu-type = "riscv,sv48"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x02>; - }; - }; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x01>; - }; - }; - }; - }; - - soc { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "simple-bus"; - ranges; - - rtc@101000 { - interrupts = <0x0b>; - interrupt-parent = <0x03>; - reg = <0x00 0x101000 0x00 0x1000>; - compatible = "google,goldfish-rtc"; - }; - - uart@10000000 { - interrupts = <0x0a>; - interrupt-parent = <0x03>; - clock-frequency = "\08@"; - reg = <0x00 0x10000000 0x00 0x100>; - compatible = "ns16550a"; - }; - - poweroff { - value = <0x5555>; - offset = <0x00>; - regmap = <0x04>; - compatible = "syscon-poweroff"; - }; - - reboot { - value = <0x7777>; - offset = <0x00>; - regmap = <0x04>; - compatible = "syscon-reboot"; - }; - - test@100000 { - phandle = <0x04>; - reg = <0x00 0x100000 0x00 0x1000>; - compatible = "sifive,test1\0sifive,test0\0syscon"; - }; - - pci@30000000 { - interrupt-map-mask = <0x1800 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; - ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; - reg = <0x00 0x30000000 0x00 0x10000000>; - dma-coherent; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - device_type = "pci"; - compatible = "pci-host-ecam-generic"; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - #address-cells = <0x03>; - }; - - virtio_mmio@10008000 { - interrupts = <0x08>; - interrupt-parent = <0x03>; - reg = <0x00 0x10008000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x07>; - interrupt-parent = <0x03>; - reg = <0x00 0x10007000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10006000 { - interrupts = <0x06>; - interrupt-parent = <0x03>; - reg = <0x00 0x10006000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10005000 { - interrupts = <0x05>; - interrupt-parent = <0x03>; - reg = <0x00 0x10005000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10004000 { - interrupts = <0x04>; - interrupt-parent = <0x03>; - reg = <0x00 0x10004000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10003000 { - interrupts = <0x03>; - interrupt-parent = <0x03>; - reg = <0x00 0x10003000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10002000 { - interrupts = <0x02>; - interrupt-parent = <0x03>; - reg = <0x00 0x10002000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10001000 { - interrupts = <0x01>; - interrupt-parent = <0x03>; - reg = <0x00 0x10001000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - plic@c000000 { - phandle = <0x03>; - riscv,ndev = <0x60>; - reg = <0x00 0xc000000 0x00 0x600000>; - interrupts-extended = <0x02 0x0b 0x02 0x09>; - interrupt-controller; - compatible = "sifive,plic-1.0.0\0riscv,plic0"; - #interrupt-cells = <0x01>; - }; - - clint@2000000 { - interrupts-extended = <0x02 0x03 0x02 0x07>; - reg = <0x00 0x2000000 0x00 0x10000>; - compatible = "sifive,clint0\0riscv,clint0"; - }; - }; -}; \ No newline at end of file diff --git a/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.elf b/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.elf deleted file mode 100755 index d568a7ac..00000000 Binary files a/images/riscv64/rCore-Tutorial-v3/rCore-Tutorial-v3.elf and /dev/null differ diff --git a/images/riscv64/rcore-os b/images/riscv64/rcore-os deleted file mode 100755 index 69ada15a..00000000 Binary files a/images/riscv64/rcore-os and /dev/null differ diff --git a/images/riscv64/rcore-os.bin b/images/riscv64/rcore-os.bin deleted file mode 100755 index 309d38d6..00000000 Binary files a/images/riscv64/rcore-os.bin and /dev/null differ diff --git a/images/riscv64/rootfs-buildroot.qcow2 b/images/riscv64/rootfs-buildroot.qcow2 deleted file mode 100755 index 90461a4a..00000000 Binary files a/images/riscv64/rootfs-buildroot.qcow2 and /dev/null differ diff --git a/images/riscv64/rootfs-busybox.qcow2 b/images/riscv64/rootfs-busybox.qcow2 deleted file mode 100644 index 9f9f715b..00000000 Binary files a/images/riscv64/rootfs-busybox.qcow2 and /dev/null differ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 29653eab..38e241c2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] profile = "minimal" # use the nightly version of the last stable toolchain, see -channel = "nightly-2023-07-12" +channel = "nightly-2023-12-28" components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] \ No newline at end of file diff --git a/scripts/3a5000-loongarch64.ld b/scripts/3a5000-loongarch64.ld new file mode 100644 index 00000000..14575576 --- /dev/null +++ b/scripts/3a5000-loongarch64.ld @@ -0,0 +1,59 @@ +ENTRY(arch_entry) +BASE_ADDRESS = 0x9000000100010000; + + +SECTIONS +{ + . = BASE_ADDRESS; + skernel = .; + + stext = .; + .text : { + *(.text.entry) + *(.text .text.*) + } + + . = ALIGN(4K); + .trap_entry : { + *(.trap_entry) + . = ALIGN(4K); + *(.tlbrefill_entry) + } + + . = ALIGN(4K); + etext = .; + srodata = .; + .rodata : { + *(.rodata .rodata.*) + *(.srodata .srodata.*) + } + + . = ALIGN(4K); + erodata = .; + sdata = .; + .data : { + *(.data .data.*) + *(.sdata .sdata.*) + + } + . = ALIGN(4K); + edata = .; + + .bss : { + *(.bss.stack) + sbss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + } + + . = ALIGN(4K); + ebss = .; + + ekernel = .; + + /DISCARD/ : { + *(.eh_frame) + } + . = ALIGN(4K); + __core_end = .; +} \ No newline at end of file diff --git a/scripts/3a5000-loongarch64.mk b/scripts/3a5000-loongarch64.mk new file mode 100644 index 00000000..999b7fe4 --- /dev/null +++ b/scripts/3a5000-loongarch64.mk @@ -0,0 +1,24 @@ +# hvisor for loongarch64 makefile +# wheatfox(wheatfox17@icloud.com) 2024.6 + +# HVISOR ENTRY +HVISOR_ENTRY_PA := 0x9000000080000000 + +# zone0_kernel := $(image_dir)/kernel/Image +# zone0_dtb := $(image_dir)/devicetree/linux.dtb + +# QEMU for loongarch64 doesn't support LVZ extension yet +# so no qemu related stuff here, we have to debug it on +# REAL hardware with UEFI firmware interface + +QEMU := qemu-system-loongarch64 +QEMU_ARGS := -machine virt +QEMU_ARGS += -smp 4 +QEMU_ARGS += -m 2G +QEMU_ARGS += -nographic +QEMU_ARGS += -kernel $(hvisor_elf) +# QEMU_ARGS += -device loader,file="$(zone0_kernel)",addr=0x90000000,force-raw=on +# QEMU_ARGS += -device loader,file="$(zone0_dtb)",addr=0x8f000000,force-raw=on + +$(hvisor_bin): elf + $(OBJCOPY) $(hvisor_elf) --strip-all -O binary $@ \ No newline at end of file diff --git a/scripts/qemu-aarch64.mk b/scripts/qemu-aarch64.mk index 8b9264a6..c9a2ff22 100644 --- a/scripts/qemu-aarch64.mk +++ b/scripts/qemu-aarch64.mk @@ -1,20 +1,25 @@ -QEMU := sudo qemu-system-aarch64 - -UBOOT := $(image_dir)/bootloader/u-boot.bin +QEMU := qemu-system-aarch64 + +ifeq ($(findstring gicv3, $(FEATURES)),gicv3) + UBOOT := $(image_dir)/bootloader/u-boot-atf.bin + zone0_dtb := $(image_dir)/devicetree/linux1.dtb + QEMU_ARGS := -machine virt,secure=on,gic-version=3,virtualization=on,iommu=smmuv3 + MESSAGE := "Note: Feature contains gicv3" +else + UBOOT := $(image_dir)/bootloader/u-boot-v2.bin + zone0_dtb := $(image_dir)/devicetree/linux1-v2.dtb + QEMU_ARGS := -machine virt,secure=on,gic-version=2,virtualization=on,iommu=smmuv3 + MESSAGE := "Note: Feature contains gicv2" +endif FSIMG1 := $(image_dir)/virtdisk/rootfs1.ext4 FSIMG2 := $(image_dir)/virtdisk/rootfs2.ext4 zone0_kernel := $(image_dir)/kernel/Image -zone1_kernel := $(image_dir)/kernel/Image -zone0_dtb := $(image_dir)/devicetree/linux1.dtb -zone1_dtb := $(image_dir)/devicetree/linux2.dtb - -QEMU_ARGS := -machine virt,secure=on,gic-version=3,virtualization=on -# QEMU_ARGS += -d int +QEMU_ARGS += -global arm-smmuv3.stage=2 -QEMU_ARGS += -cpu cortex-a53 +QEMU_ARGS += -cpu cortex-a72 QEMU_ARGS += -smp 4 QEMU_ARGS += -m 2G QEMU_ARGS += -nographic @@ -23,28 +28,10 @@ QEMU_ARGS += -bios $(UBOOT) QEMU_ARGS += -device loader,file="$(hvisor_bin)",addr=0x40400000,force-raw=on QEMU_ARGS += -device loader,file="$(zone0_kernel)",addr=0xa0400000,force-raw=on QEMU_ARGS += -device loader,file="$(zone0_dtb)",addr=0xa0000000,force-raw=on -# QEMU_ARGS += -device loader,file="$(zone1_kernel)",addr=0x70000000,force-raw=on -# QEMU_ARGS += -device loader,file="$(zone1_dtb)",addr=0x91000000,force-raw=on QEMU_ARGS += -drive if=none,file=$(FSIMG1),id=Xa003e000,format=raw QEMU_ARGS += -device virtio-blk-device,drive=Xa003e000,bus=virtio-mmio-bus.31 -QEMU_ARGS += -drive if=none,file=$(FSIMG2),id=Xa003c000,format=raw -QEMU_ARGS += -device virtio-blk-device,drive=Xa003c000 - -# QEMU_ARGS += -netdev tap,id=Xa003a000,ifname=tap0,script=no,downscript=no -# QEMU_ARGS += -device virtio-net-device,netdev=Xa003a000,mac=52:55:00:d1:55:01 -# QEMU_ARGS += -netdev user,id=n0,hostfwd=tcp::5555-:22 -device virtio-net-device,bus=virtio-mmio-bus.29,netdev=n0 - -QEMU_ARGS += -chardev pty,id=Xa0038000 -QEMU_ARGS += -device virtio-serial-device,bus=virtio-mmio-bus.28 -device virtconsole,chardev=Xa0038000 - -# QEMU_ARGS += --fsdev local,id=Xa0036000,path=./9p/,security_model=none -# QEMU_ARGS += -device virtio-9p-pci,fsdev=Xa0036000,mount_tag=kmod_mount - -# trace-event gicv3_icc_generate_sgi on -# trace-event gicv3_redist_send_sgi on - $(hvisor_bin): elf @if ! command -v mkimage > /dev/null; then \ sudo apt update && sudo apt install u-boot-tools; \ diff --git a/scripts/qemu-riscv64.ld b/scripts/qemu-riscv64.ld index 99c9266b..eb62b0fe 100644 --- a/scripts/qemu-riscv64.ld +++ b/scripts/qemu-riscv64.ld @@ -1,6 +1,7 @@ ENTRY(arch_entry) BASE_ADDRESS = 0x80200000; + SECTIONS { . = BASE_ADDRESS; @@ -41,25 +42,9 @@ SECTIONS ebss = .; ekernel = .; - /DISCARD/ : { *(.eh_frame) } . = ALIGN(4K); __core_end = .; - . = . + 0x2000000; - - .dtb : { - *(.dtb) - } - . = ALIGN(4K); - .rcore : { - *(.rcore) - } - . = 0x90000000; - vmimg = .; - .initrd : { - *(.initrd) - } - } \ No newline at end of file diff --git a/scripts/qemu-riscv64.mk b/scripts/qemu-riscv64.mk index 69e70b6f..8a1e9aac 100644 --- a/scripts/qemu-riscv64.mk +++ b/scripts/qemu-riscv64.mk @@ -1,33 +1,43 @@ QEMU := qemu-system-riscv64 -QEMU_ARGS := -machine virt -QEMU_ARGS += -nographic -QEMU_ARGS += -cpu rv64 -QEMU_ARGS += -m 3G -QEMU_ARGS += -smp 4 + +FSIMG1 := $(image_dir)/virtdisk/rootfs1.ext4 +FSIMG2 := $(image_dir)/virtdisk/rootfs-busybox.qcow2 +# HVISOR ENTRY +HVISOR_ENTRY_PA := 0x80200000 +zone0_kernel := $(image_dir)/kernel/Image-aia-6.10 +zone0_dtb := $(image_dir)/devicetree/linux1.dtb +zone0_aia_dtb := $(image_dir)/devicetree/linux1-aia.dtb +# zone1_kernel := $(image_dir)/kernel/Image +# zone1_dtb := $(image_dir)/devicetree/linux.dtb + +ifeq ($(findstring aia, $(FEATURES)),aia) + QEMU_ARGS := -machine virt,aclint=on,aia=aplic-imsic,aia-guests=1 + QEMU_ARGS += -device loader,file="$(zone0_aia_dtb)",addr=0x8f000000,force-raw=on + MESSAGE := "Note: Feature contains AIA" +else + QEMU_ARGS := -machine virt + QEMU_ARGS += -device loader,file="$(zone0_dtb)",addr=0x8f000000,force-raw=on + MESSAGE := "Note: Feature contains PLIC" +endif QEMU_ARGS += -bios default -# QEMU_ARGS +=-bios $(BOOTLOADER) +QEMU_ARGS += -cpu rv64 +QEMU_ARGS += -smp 4 +QEMU_ARGS += -m 2G +QEMU_ARGS += -nographic + QEMU_ARGS += -kernel $(hvisor_bin) -QEMU_ARGS += -drive file=imgs/rootfs-busybox.qcow2,format=qcow2,id=hd0 -#QEMU_ARGS +=-drive file=../guests/rootfs-buildroot.qcow2,format=qcow2,id=hd0 -QEMU_ARGS += -device virtio-blk-device,drive=hd0 -QEMU_ARGS += -append "root=/dev/vda rw console=ttyS0" - -#QEMU_ARGS += -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -#QEMU_ARGS += -device loader,file=../guests/os_ch5_802.bin,addr=0x80400000 -#QEMU_ARGS += -device virtio-serial-port -chardev pty,id=serial3 -device virtconsole,chardev=serial3 -QEMU_ARGS += -device virtio-serial-device -chardev pty,id=serial3 -device virtconsole,chardev=serial3 - -# QEMU_ARGS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic -# QEMU_ARGS +=-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -# QEMU_ARGS +=-drive file=../guests/rCore-Tutorial-v3/fs.img,if=none,format=raw,id=x0 -# QEMU_ARGS +=-device virtio-blk-device,drive=x0 -# QEMU_ARGS +=-device virtio-gpu-device -# QEMU_ARGS +=-device virtio-keyboard-device -# QEMU_ARGS +=-device virtio-mouse-device -# QEMU_ARGS +=-device virtio-net-device,netdev=net0 -# QEMU_ARGS +=-netdev user,id=net0,hostfwd=udp::6200-:2000 +QEMU_ARGS += -device loader,file="$(zone0_kernel)",addr=0x90000000,force-raw=on +# QEMU_ARGS += -device loader,file="$(zone1_aia_kernel)",addr=0x84000000,force-raw=on +# QEMU_ARGS += -device loader,file="$(zone1_aia_dtb)",addr=0x83000000,force-raw=on +# QEMU_ARGS += -device loader,file="$(zone1_kernel)",addr=0x84000000,force-raw=on +# QEMU_ARGS += -device loader,file="$(zone1_dtb)",addr=0x83000000,force-raw=on +QEMU_ARGS += -drive if=none,file=$(FSIMG1),id=X10008000,format=raw +QEMU_ARGS += -device virtio-blk-device,drive=X10008000,bus=virtio-mmio-bus.7 +QEMU_ARGS += -device virtio-serial-device,bus=virtio-mmio-bus.6 -chardev pty,id=X10007000 -device virtconsole,chardev=X10007000 -S +QEMU_ARGS += -drive if=none,file=$(FSIMG2),id=X10006000,format=qcow2 +QEMU_ARGS += -device virtio-blk-device,drive=X10006000,bus=virtio-mmio-bus.5 # ------------------------------------------------------------------- # QEMU_ARGS := -machine virt diff --git a/scripts/zcu102-aarch64-fit.its b/scripts/zcu102-aarch64-fit.its new file mode 100644 index 00000000..7e4bd392 --- /dev/null +++ b/scripts/zcu102-aarch64-fit.its @@ -0,0 +1,56 @@ +/dts-v1/; +/ { + description = "FIT image for HVISOR with Linux kernel, root filesystem, and DTB"; + + images { + root_linux { + description = "Linux kernel"; + data = /incbin/("__ROOT_LINUX_IMAGE__"); + type = "kernel"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x200000>; + entry = <0x200000>; + }; + + root_rootfs { + description = "Root filesystem"; + data = /incbin/("__ROOT_LINUX_ROOTFS__"); + type = "filesystem"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x4000000>; + }; + + root_dtb { + description = "Device Tree Blob"; + data = /incbin/("__ROOT_LINUX_DTB__"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + load = <0x04000000>; + }; + + hvisor { + description = "Hypervisor"; + data = /incbin/("__HVISOR_TMP_PATH__"); + type = "kernel"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x40400000>; + entry = <0x40400000>; + }; + }; + + configurations { + default = "config@1"; + config@1 { + description = "default"; + kernel = "hvisor"; + fdt = "root_dtb"; + }; + }; +}; diff --git a/scripts/zcu102-aarch64.ld b/scripts/zcu102-aarch64.ld new file mode 100644 index 00000000..4cd76ea3 --- /dev/null +++ b/scripts/zcu102-aarch64.ld @@ -0,0 +1,49 @@ +ENTRY(arch_entry) +BASE_ADDRESS = 0x40400000; + +SECTIONS +{ + . = BASE_ADDRESS; + skernel = .; + + stext = .; + .text : { + *(.text.entry) + *(.text .text.*) + } + + . = ALIGN(4K); + etext = .; + srodata = .; + .rodata : { + *(.rodata .rodata.*) + *(.srodata .srodata.*) + } + + . = ALIGN(4K); + erodata = .; + sdata = .; + .data : { + *(.data .data.*) + *(.sdata .sdata.*) + } + + . = ALIGN(4K); + edata = .; + .bss : { + *(.bss.stack) + sbss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + } + + . = ALIGN(4K); + ebss = .; + ekernel = .; + + /DISCARD/ : { + *(.eh_frame) + } + . = ALIGN(4K); + __core_end = .; +} \ No newline at end of file diff --git a/scripts/zcu102-aarch64.mk b/scripts/zcu102-aarch64.mk new file mode 100644 index 00000000..dbd51c1b --- /dev/null +++ b/scripts/zcu102-aarch64.mk @@ -0,0 +1,139 @@ +# Makefile for Xilinx ZCU102 AArch64 platform +# created on 2024.12.2, wheatfox(wheatfox17@icloud.com) + +# according to petalinux-boot qemu +# however we must use petalinux-boot qemu to use it because it need some background servers ? + +$(hvisor_bin): elf + @if ! command -v mkimage > /dev/null; then \ + if [ "$(shell uname)" = "Linux" ]; then \ + echo "mkimage not found. Installing using apt..."; \ + sudo apt update && sudo apt install -y u-boot-tools; \ + elif [ "$(shell uname)" = "Darwin" ]; then \ + echo "mkimage not found. Installing using brew, you may need to reopen the Terminal App"; \ + brew install u-boot-tools; \ + else \ + echo "Unsupported operating system. Please install u-boot-tools manually."; \ + exit 1; \ + fi; \ + fi && \ + $(OBJCOPY) $(hvisor_elf) --strip-all -O binary $(hvisor_bin).tmp && \ + mkimage -n hvisor_img -A arm64 -O linux -C none -T kernel -a 0x40400000 \ + -e 0x40400000 -d $(hvisor_bin).tmp $(hvisor_bin) + +HVISOR_BIN_FULL_PATH = $(shell readlink -f $(hvisor_bin)) +DEFAULT_PETALINUX_PROJECT_PATH = /home/wheatfox/Documents/Code/petalinux_projects/wheatfox_hw0 +DEFAULT_PETALINUX_SDK_PATH = /home/wheatfox/petalinux_sdk + +ROOT_LINUX_IMAGE = $(PETALINUX_PROJECT_PATH)/images/linux/vmlinux +ROOT_LINUX_IMAGE_BIN = $(ROOT_LINUX_IMAGE).bin +ROOT_LINUX_ROOTFS = $(PETALINUX_PROJECT_PATH)/images/linux/rootfs.cpio.gz.u-boot +# ROOT_LINUX_DTB = $(PETALINUX_PROJECT_PATH)/images/linux/system.dtb +ROOT_LINUX_DTB = $(shell readlink -f ./images/aarch64/devicetree/zcu102-root-aarch64.dtb) +ROOT_LINUX_SD_IMG = $(shell readlink -f ./sd.img) + +# notes on uboot FIT: +# please pass the raw vmlinux and hvisor stripped binary in uboot's its +# because when generating FIT image, uboot will create the "Image" format binary and embed it in the FIT image +# so, don't pass the "Image" format binary or hvisor.bin to uboot's its because +# this will cause double wrapping of the binary ! - wheatfox +TARGET_FIT_IMAGE = fitImage +TARGET_FIT_IMAGE_PATH = $(shell readlink -f $(TARGET_FIT_IMAGE)) + +GDB ?= aarch64-linux-gnu-gdb +READELF ?= aarch64-linux-gnu-readelf +OBJDUMP = aarch64-linux-gnu-objdump + +ifndef PETALINUX_SDK_ROOT + PETALINUX_SDK_ROOT = $(DEFAULT_PETALINUX_SDK_PATH) +else +# set PETALINUX_SDK_ROOT in your environment to override the default + PETALINUX_SDK_ROOT = $(PETALINUX_SDK_ROOT) +endif + + +ifndef PETALINUX_PROJECT_PATH + PETALINUX_PROJECT_PATH = $(DEFAULT_PETALINUX_PROJECT_PATH) +else +# set PETALINUX_PROJECT_PATH in your environment to override the default + PETALINUX_PROJECT_PATH = $(PETALINUX_PROJECT_PATH) +endif + +HVISOR_TMP_PATH = $(shell readlink -f $(hvisor_bin).tmp) +GCC_OBJCOPY = aarch64-linux-gnu-objcopy + +.PHONY: dtb +dtb: + make -C ./images/aarch64/devicetree + +.PHONY: gen-fit +gen-fit: $(hvisor_bin) dtb + @if [ ! -f scripts/zcu102-aarch64-fit.its ]; then \ + echo "Error: ITS file scripts/zcu102-aarch64-fit.its not found."; \ + exit 1; \ + fi + $(OBJCOPY) $(hvisor_elf) --strip-all -O binary $(HVISOR_TMP_PATH) +# now we need to create the vmlinux.bin + $(GCC_OBJCOPY) $(ROOT_LINUX_IMAGE) --strip-all -O binary $(ROOT_LINUX_IMAGE_BIN) + @sed \ + -e "s|__ROOT_LINUX_IMAGE__|$(ROOT_LINUX_IMAGE_BIN)|g" \ + -e "s|__ROOT_LINUX_ROOTFS__|$(ROOT_LINUX_ROOTFS)|g" \ + -e "s|__ROOT_LINUX_DTB__|$(ROOT_LINUX_DTB)|g" \ + -e "s|__HVISOR_TMP_PATH__|$(HVISOR_TMP_PATH)|g" \ + scripts/zcu102-aarch64-fit.its > temp-fit.its + @mkimage -f temp-fit.its $(TARGET_FIT_IMAGE) + @echo "Generated FIT image: $(TARGET_FIT_IMAGE)" + +# "pl" is short for "petalinux" +# args passed to xilinx's qemu +EXTRA_QEMU_ARGS = +EXTRA_QEMU_ARGS += -device loader,file=$(TARGET_FIT_IMAGE_PATH),addr=0x10000000,force-raw=on +# add SD +EXTRA_QEMU_ARGS += -drive file=$(ROOT_LINUX_SD_IMG),format=raw,if=sd + +.PHONY: run-pl-qemu +run-pl-qemu: $(hvisor_bin) gen-fit + @echo "Running petalinux qemu..." +# petalinux only works in bash +# it will open a gdb server on tcp:localhost:9000 + bash -c "source $(PETALINUX_SDK_ROOT)/settings.sh && \ + cd $(PETALINUX_PROJECT_PATH) && petalinux-boot qemu \ + --prebuilt 2 \ + --qemu-args '$(EXTRA_QEMU_ARGS)' \ + " + +# uboot cmds:\ +setenv fit_addr 0x10000000;setenv root_linux_load 0x200000;setenv root_rootfs_load 0x4000000;imxtract ${fit_addr} root_linux ${root_linux_load};imxtract ${fit_addr} root_rootfs ${root_rootfs_load};md ${root_linux_load} 20;bootm ${fit_addr}; + +.PHONY: debug-pl-qemu +debug-pl-qemu: + @echo "Starting gdb client..." + $(GDB) -ex "target remote localhost:9000" -ex "layout asm" + +# below are some quick commands +.PHONY: pl-build +pl-build: + @echo "Building petalinux project..." + bash -c "source $(PETALINUX_SDK_ROOT)/settings.sh && \ + cd $(PETALINUX_PROJECT_PATH) && petalinux-build" + +.PHONY: vmlinux-info +vmlinux-info: + @$(READELF) -h $(ROOT_LINUX_IMAGE) | grep "Entry point address" | awk '{print $$0}' + @$(READELF) -l $(ROOT_LINUX_IMAGE) | awk '{print $$0}' + +.PHONY: disasm-vmlinux +disasm-vmlinux: + @$(OBJDUMP) -d $(ROOT_LINUX_IMAGE) > target/petalinux-vmlinux-disasm.txt + +.PHONY: pl-config +pl-config: + @echo "Configuring petalinux project..." + bash -c "source $(PETALINUX_SDK_ROOT)/settings.sh && \ + cd $(PETALINUX_PROJECT_PATH) && petalinux-config" + +.PHONY: sd-image +# you can customized the real rootfs by changing the content of this sd.img +sd-image: + @echo "Creating SD card image..." + qemu-img create -f raw sd.img 1G \ No newline at end of file diff --git a/src/arch/aarch64/boot_pt.S b/src/arch/aarch64/boot_pt.S index c036c9c1..04991a44 100644 --- a/src/arch/aarch64/boot_pt.S +++ b/src/arch/aarch64/boot_pt.S @@ -5,7 +5,8 @@ MAIR_FLAG=0x004404ff //10001000000010011111111 SCTLR_FLAG=0x30c51835 //110000110001010001100000110101 TCR_FLAG=0x80853510 //10000000100001010011010100010000 - + .extern uart_puts_1 + .extern uart_puts_2 .section .bss .align 12 // 对齐到页边界 boot_pt_l0: @@ -18,22 +19,24 @@ boot_pt_l1: .section .text .global set_boot_pt boot_pt_init: + // TODO: re-enable hvisor pt when system boot success adrp x1, boot_pt_l0 adrp x2, boot_pt_l1 add x2, x2, PTE_TABLE_FLAGS - str x2, [x1] // set boot_pt_l0, index=0 -> boot_pt_l1 + str x2, [x1] // set boot_pt_l0, index=0 -> boot_pt_l1 adrp x1, boot_pt_l1 - ldr x2, =(0x0 | PAGE_DEVICE_FLAG) // device memory at 0x0 ~ 0x40000000 + ldr x2, =(0x0 | PAGE_DEFAULT_FLAG) // device memory at 0x0 ~ 0x40000000 str x2, [x1] ldr x2, =(0x40000000 | PAGE_DEFAULT_FLAG) // RAM at 0x40000000 ~ 0x80000000 str x2, [x1, #0x8] ldr x2, =(0x80000000 | PAGE_DEFAULT_FLAG) // RAM at 0x80000000 ~ 0xC0000000 str x2, [x1, #0x10] - ldr x2, =(0xC0000000 | PAGE_DEFAULT_FLAG) // RAM at 0xC0000000 ~ 0x100000000 + ldr x2, =(0xC0000000 | PAGE_DEVICE_FLAG) // RAM at 0xC0000000 ~ 0x100000000 str x2, [x1, #0x18] ret + enable_boot_pt: adrp x1, boot_pt_l0 msr ttbr0_el2, x1 @@ -43,7 +46,7 @@ enable_boot_pt: dsb nsh // enable mmu... - ldr x1, =MAIR_FLAG + ldr x1, =MAIR_FLAG msr mair_el2, x1 // memory attributes for pagetable ldr x1, =TCR_FLAG msr tcr_el2, x1 // translate control, virt range = [0, 2^48) diff --git a/src/arch/aarch64/cpu.rs b/src/arch/aarch64/cpu.rs index 5776bb27..6c53f9f1 100644 --- a/src/arch/aarch64/cpu.rs +++ b/src/arch/aarch64/cpu.rs @@ -43,18 +43,22 @@ impl GeneralRegisters { #[derive(Debug)] pub struct ArchCpu { pub cpuid: usize, - pub psci_on: bool, + pub power_on: bool, } impl ArchCpu { pub fn new(cpuid: usize) -> Self { Self { cpuid, - psci_on: false, + power_on: false, } } pub fn reset(&mut self, entry: usize, dtb: usize) { + debug!( + "cpu {} reset, entry: {:#x}, dtb: {:#x}", + self.cpuid, entry, dtb + ); ELR_EL2.set(entry as _); SPSR_EL2.set(0x3c5); let regs = self.guest_reg(); @@ -147,7 +151,8 @@ impl ArchCpu { assert!(this_cpu_id() == self.cpuid); this_cpu_data().activate_gpm(); self.reset(this_cpu_data().cpu_on_entry, this_cpu_data().dtb_ipa); - self.psci_on = true; + self.power_on = true; + info!("cpu {} started", self.cpuid); unsafe { vmreturn(self.guest_reg() as *mut _ as usize); } @@ -157,9 +162,10 @@ impl ArchCpu { assert!(this_cpu_id() == self.cpuid); let cpu_data = this_cpu_data(); let _lock = cpu_data.ctrl_lock.lock(); - self.psci_on = false; + self.power_on = false; drop(_lock); + info!("cpu {} idle", self.cpuid); // reset current cpu -> pc = 0x0 (wfi) PARKING_MEMORY_SET.call_once(|| { let parking_code: [u8; 8] = [0x7f, 0x20, 0x03, 0xd5, 0xff, 0xff, 0xff, 0x17]; // 1: wfi; b 1b @@ -180,6 +186,7 @@ impl ArchCpu { self.reset(0, this_cpu_data().dtb_ipa); unsafe { PARKING_MEMORY_SET.get().unwrap().activate(); + info!("cpu {} started from parking", self.cpuid); vmreturn(self.guest_reg() as *mut _ as usize); } } diff --git a/src/arch/aarch64/entry.rs b/src/arch/aarch64/entry.rs index 33aa86a2..9c75507c 100644 --- a/src/arch/aarch64/entry.rs +++ b/src/arch/aarch64/entry.rs @@ -2,7 +2,7 @@ use core::arch::global_asm; use crate::consts::PER_CPU_SIZE; -global_asm!(include_str!("boot_pt.S")); +//global_asm!(include_str!("boot_pt.S")); #[naked] #[no_mangle] @@ -13,28 +13,46 @@ pub unsafe extern "C" fn arch_entry() -> i32 { " // x0 = dtbaddr mov x18, x0 - mrs x0, mpidr_el1 - and x0, x0, #0xff + mrs x17, mpidr_el1 + and x17, x17, #0xff adrp x2, __core_end // x2 = &__core_end mov x3, {per_cpu_size} // x3 = per_cpu_size - madd x4, x0, x3, x3 // x4 = cpuid * per_cpu_size + madd x4, x17, x3, x3 // x4 = cpuid * per_cpu_size add x5, x2, x4 mov sp, x5 // sp = &__core_end + (cpuid + 1) * per_cpu_size - cmp x0, 0 + // disable cache and MMU + mrs x1, sctlr_el2 + bic x1, x1, #0xf + msr sctlr_el2, x1 + + cmp x17, 0 b.ne 1f + bl {clear_bss} - bl boot_pt_init + //bl boot_pt_init + adrp x0, {BOOT_PT_L0} + adrp x1, {BOOT_PT_L1} + bl {boot_pt_init} 1: - bl enable_boot_pt + adrp x0, {BOOT_PT_L0} + bl {mmu_init} + bl {mmu_enable} mov x1, x18 + mov x0, x17 mov x18, 0 + mov x17, 0 bl {rust_main} // x0 = cpuid, x1 = dtbaddr ", options(noreturn), per_cpu_size=const PER_CPU_SIZE, rust_main = sym crate::rust_main, clear_bss = sym crate::clear_bss, + BOOT_PT_L0 = sym super::mmu::BOOT_PT_L0, + BOOT_PT_L1 = sym super::mmu::BOOT_PT_L1, + boot_pt_init = sym super::mmu::boot_pt_init, + mmu_init = sym super::mmu::mmu_init, + mmu_enable = sym super::mmu::mmu_enable, ); } } diff --git a/src/arch/aarch64/iommu.rs b/src/arch/aarch64/iommu.rs new file mode 100644 index 00000000..61bbeacd --- /dev/null +++ b/src/arch/aarch64/iommu.rs @@ -0,0 +1,590 @@ +use crate::{ + arch::mm::new_s2_memory_set, + consts::{MAX_ZONE_NUM, PAGE_SIZE}, + memory::{Frame, GuestPhysAddr, MemFlags, MemoryRegion, MemorySet, PhysAddr}, +}; +use aarch64_cpu::registers::{Readable, Writeable}; +use alloc::vec::Vec; +use spin::mutex::Mutex; +use tock_registers::{ + register_structs, + registers::{ReadOnly, ReadWrite}, +}; + +use super::Stage2PageTable; + +#[allow(dead_code)] +const SMMU_BASE_ADDR: PhysAddr = 0x09050000; +#[allow(dead_code)] +const SMMU_SIZE: PhysAddr = 0x20000; + +const CR0_SMMUEN: usize = 1; +const ARM_SMMU_SYNC_TIMEOUT: usize = 0x1000000; +const CR0_CMDQEN: usize = 1 << 3; +// const CR0_EVTQEN:usize = 1 << 2; + +const CR1_TABLE_SH_OFF: usize = 10; +const CR1_TABLE_OC_OFF: usize = 8; +const CR1_TABLE_IC_OFF: usize = 6; +const CR1_QUEUE_SH_OFF: usize = 4; +const CR1_QUEUE_OC_OFF: usize = 2; +const CR1_QUEUE_IC_OFF: usize = 0; +const SH_ISH: usize = 3; +const MEMATTR_OIWB: usize = 0xf; +const CR1_CACHE_WB: usize = 1; + +const IDR0_S2P_BIT: usize = 1; +const IDR0_S1P_BIT: usize = 1 << 1; +const IDR0_ST_LEVEL_OFF: usize = 27; +const IDR0_ST_LEVEL_LEN: usize = 2; +const IDR0_VMID16_BIT: usize = 1 << 18; + +const IDR1_SIDSIZE_OFF: usize = 0; +const IDR1_SIDSIZE_LEN: usize = 6; +const IDR1_CMDQS_OFF: usize = 21; +const IDR1_CMDQS_LEN: usize = 5; +// const IDR1_EVTQS_OFF:usize = 16; +// const IDR1_EVTQS_LEN:usize = 5; +const CMDQ_MAX_SZ_SHIFT: usize = 8; +// const EVTQ_MAX_SZ_SHIFT:usize = 7; + +const STRTAB_BASE_OFF: usize = 6; +const STRTAB_BASE_LEN: usize = 46; +const STRTAB_BASE_RA: usize = 1 << 62; + +const STRTAB_STE_DWORDS_BITS: usize = 3; +const STRTAB_STE_DWORDS: usize = 1 << STRTAB_STE_DWORDS_BITS; +const STRTAB_STE_SIZE: usize = STRTAB_STE_DWORDS << 3; +const STRTAB_STE_0_V: usize = 1; +const STRTAB_STE_0_INVALID: usize = 0; +const STRTAB_STE_0_CFG_OFF: usize = 1; +const STRTAB_STE_0_CFG_BYPASS: usize = 4; +const STRTAB_STE_0_CFG_S2_TRANS: usize = 6; +const STRTAB_STE_1_SHCFG_OFF: usize = 44; +const STRTAB_STE_1_SHCFG_INCOMING: usize = 1; +const STRTAB_STE_2_VTCR_OFF: usize = 32; +const STRTAB_STE_2_VTCR_LEN: usize = 19; +const STRTAB_STE_2_S2VMID_OFF: usize = 0; +const STRTAB_STE_2_S2PTW: usize = 1 << 54; +const STRTAB_STE_2_S2AA64: usize = 1 << 51; +const STRTAB_STE_2_S2R: usize = 1 << 58; +const STRTAB_STE_3_S2TTB_OFF: usize = 4; +const STRTAB_STE_3_S2TTB_LEN: usize = 48; + +const STRTAB_BASE_CFG_FMT_OFF: usize = 16; +const STRTAB_BASE_CFG_FMT_LINEAR: usize = 0 << 16; +const STRTAB_BASE_CFG_LOG2SIZE_OFF: usize = 0; + +const Q_BASE_RWA: usize = 1 << 62; +const Q_BASE_ADDR_OFF: usize = 5; +const Q_BASE_ADDR_LEN: usize = 47; +const Q_BASE_LOG2SIZE_OFF: usize = 0; +const Q_BASE_LOG2SIZE_LEN: usize = 5; + +const CMDQ_ENT_DWORDS: usize = 2; +const CMDQ_ENT_SIZE: usize = CMDQ_ENT_DWORDS << 3; + +const CMDQ_OP_CMD_SYNC: usize = 0x46; +// const CMDQ_SYNC_0_CS_SEV:usize = 2; +// const CMDQ_SYNC_0_CS_OFF:usize = 12; +const CMDQ_SYNC_0_MSH_OFF: usize = 22; +const CMDQ_SYNC_0_MSI_ATTR_OFF: usize = 24; + +const CMDQ_OP_CFGI_STE: usize = 3; +const CMDQ_CFGI_0_SID_OFF: usize = 32; +const CMDQ_CFGI_1_LEAF: usize = 1; + +const DEFAULT_VCTR: usize = + 20 + (2 << 6) + (1 << 8) + (1 << 10) + (3 << 12) + (0 << 14) + (4 << 16); + +// page0 + page1 +register_structs! { + #[allow(non_snake_case)] + pub RegisterPage{ + (0x0000 => IDR0:ReadOnly), + (0x0004 => IDR1:ReadOnly), + (0x0008 => IDR2:ReadOnly), + (0x000c => IDR3:ReadOnly), + (0x0010 => IDR4:ReadOnly), + (0x0014 => IDR5:ReadOnly), + (0x0018 => IIDR:ReadOnly), + (0x001c => AIDR:ReadOnly), + (0x0020 => CR0:ReadWrite), + (0x0024 => CR0ACK:ReadOnly), + (0x0028 => CR1:ReadWrite), + (0x002c => CR2:ReadWrite), + (0x0030 => _reserved0), + (0x0050 => IRQ_CTRL:ReadWrite), + (0x0054 => IRQ_CTRLACK:ReadOnly), + (0x0058 => _reserved1), + (0x0060 => GERROR:ReadOnly), + (0x0064 => GERRORN:ReadWrite), + (0x0068 => GERROR_IRQ_CFG0:ReadWrite), + (0x0070 => _reserved2), + (0x0080 => STRTAB_BASE:ReadWrite), + (0x0088 => STRTAB_BASE_CFG:ReadWrite), + (0x008c => _reserved3), + (0x0090 => CMDQ_BASE:ReadWrite), + (0x0098 => CMDQ_PROD:ReadWrite), + (0x009c => CMDQ_CONS:ReadWrite), + (0x00a0 => EVENTQ_BASE:ReadWrite), + (0x00a8 => _reserved4), + (0x00b0 => EVENTQ_IRQ_CFG0:ReadWrite), + (0x00b8 => EVENTQ_IRQ_CFG1:ReadWrite), + (0x00bc => EVENTQ_IRQ_CFG2:ReadWrite), + (0x00c0 => _reserved5), + (0x100a8 => EVENTQ_PROD:ReadWrite), + (0x100ac => EVENTQ_CONS:ReadWrite), + (0x100b0 => _reserved6), + (0x20000 => @END), + } +} + +unsafe impl Sync for RegisterPage {} + +pub fn extract_bits(value: usize, start: usize, length: usize) -> usize { + let mask = (1 << length) - 1; + (value >> start) & mask +} + +pub struct StreamTableEntry([u64; STRTAB_STE_DWORDS]); + +pub struct LinearStreamTable { + base: PhysAddr, + sid_max_bits: usize, + frames: Vec, +} + +impl LinearStreamTable { + fn new() -> Self { + Self { + base: 0, + sid_max_bits: 0, + frames: vec![], + } + } + + fn init_with_base(&mut self, base: PhysAddr, frame: Frame) { + self.base = base; + self.frames.push(frame); + } + + fn get_base(&self) -> PhysAddr { + self.base + } + + fn set_max_sid(&mut self, sid_max_bits: usize) { + self.sid_max_bits = sid_max_bits; + } + + fn get_max_sid(&self) -> usize { + self.sid_max_bits + } + + fn ste(&self, sid: usize) -> &mut StreamTableEntry { + let base = self.base + sid * STRTAB_STE_SIZE; + unsafe { &mut *(base as *mut StreamTableEntry) } + } + + fn init_bypass_ste(&self, sid: usize) { + let tab = self.ste(sid); + let mut val: usize = 0; + val |= STRTAB_STE_0_INVALID; + val |= STRTAB_STE_0_V; + val |= STRTAB_STE_0_CFG_BYPASS << STRTAB_STE_0_CFG_OFF; + tab.0[0] = val as _; + tab.0[1] = (STRTAB_STE_1_SHCFG_INCOMING << STRTAB_STE_1_SHCFG_OFF) as _; + } + + fn write_ste(&self, sid: usize, vmid: usize, root_pt: usize) { + info!( + "write ste, sid: 0x{:x}, vmid: 0x{:x}, ste_addr:0x{:x}, root_pt: 0x{:x}", + sid, + vmid, + self.base + sid * STRTAB_STE_SIZE, + root_pt + ); + let tab = self.ste(sid); + let mut val0: usize = 0; + val0 |= STRTAB_STE_0_V; + val0 |= STRTAB_STE_0_CFG_S2_TRANS << STRTAB_STE_0_CFG_OFF; + let mut val2: usize = 0; + val2 |= vmid << STRTAB_STE_2_S2VMID_OFF; + val2 |= STRTAB_STE_2_S2PTW; + val2 |= STRTAB_STE_2_S2AA64; + val2 |= STRTAB_STE_2_S2R; + let vtcr = DEFAULT_VCTR; + let v = extract_bits(vtcr as _, 0, STRTAB_STE_2_VTCR_LEN); + val2 |= v << STRTAB_STE_2_VTCR_OFF; + let vttbr = extract_bits(root_pt, STRTAB_STE_3_S2TTB_OFF, STRTAB_STE_3_S2TTB_LEN); + tab.0[0] |= val0 as u64; + tab.0[2] |= val2 as u64; + tab.0[3] |= (vttbr << STRTAB_STE_3_S2TTB_OFF) as u64; + } +} + +pub struct CmdQueue { + base_reg: usize, + base: PhysAddr, + prod: u32, + cons: u32, + max_n_shift: u32, + q_frame: Frame, +} + +pub struct Cmd([u64; CMDQ_ENT_DWORDS]); + +impl Cmd { + fn new() -> Self { + Cmd([0; CMDQ_ENT_DWORDS]) + } +} + +impl CmdQueue { + fn new() -> Self { + let r = Self { + base_reg: 0, + base: 0, + prod: 0, + cons: 0, + max_n_shift: 0, + q_frame: Frame::new_zero().unwrap(), + }; + r + } + + fn init(&mut self, shift_bits: u32) { + self.base = self.q_frame.start_paddr(); + self.base_reg = Q_BASE_RWA; + self.max_n_shift = shift_bits; + let addr_mask = extract_bits(self.q_frame.start_paddr(), Q_BASE_ADDR_OFF, Q_BASE_ADDR_LEN); + self.base_reg |= addr_mask << Q_BASE_ADDR_OFF; + self.base_reg |= extract_bits( + self.max_n_shift as _, + Q_BASE_LOG2SIZE_OFF, + Q_BASE_LOG2SIZE_LEN, + ); + } + + fn q_idx(&self, reg: u32) -> u32 { + (reg) & ((1 << (self.max_n_shift)) - 1) + } + + fn q_wrap(&self, reg: u32) -> u32 { + (reg) & (1 << (self.max_n_shift)) + } + + fn q_ovf(&self, reg: u32) -> u32 { + reg & (1 << 31) + } + + fn q_empty(&self) -> bool { + (self.q_idx(self.prod) == self.q_idx(self.cons)) + && (self.q_wrap(self.prod) == self.q_wrap(self.cons)) + } + + fn q_full(&self) -> bool { + (self.q_idx(self.prod) == self.q_idx(self.cons)) + && (self.q_wrap(self.prod) != self.q_wrap(self.cons)) + } + + fn sync_cons(&mut self, cons: u32) { + self.cons = cons; + } + + fn inc_prod(&mut self) -> u32 { + let prod: u32 = (self.q_wrap(self.prod) | self.q_idx(self.prod)) + 1; + self.prod = self.q_ovf(self.prod) | self.q_wrap(prod) | self.q_idx(prod); + self.prod + } + + fn queue_entry(&self, reg: u32) -> usize { + let entry = self.base + ((self.q_idx(reg) as usize) * CMDQ_ENT_SIZE); + entry + } + + fn queue_write(&mut self, cmd: Cmd) { + unsafe { + let entry = &mut *(self.queue_entry(self.prod) as *mut Cmd); + entry.0[0] = cmd.0[0]; + entry.0[1] = cmd.0[1]; + } + } + + // CMD_SYNC + fn build_sync_cmd(&self) -> Cmd { + let mut cmd: Cmd = Cmd::new(); + cmd.0[0] |= CMDQ_OP_CMD_SYNC as u64; + cmd.0[0] |= (SH_ISH << CMDQ_SYNC_0_MSH_OFF) as u64; + cmd.0[0] |= (MEMATTR_OIWB << CMDQ_SYNC_0_MSI_ATTR_OFF) as u64; + cmd + } + + // CFGI_STE + fn build_cfgi_cmd(&self, sid: usize) -> Cmd { + let mut cmd: Cmd = Cmd::new(); + cmd.0[0] |= (sid << CMDQ_CFGI_0_SID_OFF) as u64; + cmd.0[0] |= CMDQ_OP_CFGI_STE as u64; + cmd.0[1] |= CMDQ_CFGI_1_LEAF as u64; + cmd + } +} + +pub struct Smmuv3 { + rp: &'static RegisterPage, + strtab: LinearStreamTable, + iommu_pt_list: Vec>, + cmdq: CmdQueue, +} + +impl Smmuv3 { + fn new() -> Self { + let rp = unsafe { &*(SMMU_BASE_ADDR as *const RegisterPage) }; + let mut r = Self { + rp: rp, + strtab: LinearStreamTable::new(), + iommu_pt_list: vec![], + cmdq: CmdQueue::new(), + }; + + for _ in 0..MAX_ZONE_NUM { + r.iommu_pt_list.push(new_s2_memory_set()); + } + + info!("pagetables for iommu, init done!"); + + r.check_env(); + r.init_limited_pt(); + r.init_structures(); + r.device_reset(); + r + } + + fn check_env(&mut self) { + let idr0 = self.rp.IDR0.get() as usize; + info!("Smmuv3 IDR0:{:b}", idr0); + // supported types of stream tables. + let stb_support = extract_bits(idr0, IDR0_ST_LEVEL_OFF, IDR0_ST_LEVEL_LEN); + match stb_support { + 0 => info!("Smmuv3 Linear Stream Table Supported."), + 1 => info!("Smmuv3 2-level Stream Table Supoorted."), + _ => error!("Smmuv3 don't support any stream table."), + } + // supported address translation stages. + let s1p_support = idr0 & IDR0_S1P_BIT; + match s1p_support { + 0 => info!("Smmuv3 Stage-1 translation not supported."), + _ => info!("Smmuv3 Stage-1 translation supported."), + } + let s2p_support = idr0 & IDR0_S2P_BIT; + match s2p_support { + 0 => error!("Smmuv3 Stage-2 translation not supported."), + _ => info!("Smmuv3 Stage-2 translation supported."), + } + // 16-bit VMID supported. + match idr0 & IDR0_VMID16_BIT { + 0 => info!("Smmuv3 16-bit VMID not supported."), + _ => info!("Smmuv3 16-bit VMID supported."), + } + let idr1 = self.rp.IDR1.get() as usize; + let sid_max_bits = extract_bits(idr1, IDR1_SIDSIZE_OFF, IDR1_SIDSIZE_LEN); + info!("Smmuv3 SID_MAX_BITS:{:?}", sid_max_bits); + self.strtab.set_max_sid(sid_max_bits); + if sid_max_bits >= 7 && extract_bits(idr0, IDR0_ST_LEVEL_OFF, IDR0_ST_LEVEL_LEN) == 0 { + error!("Smmuv3 the system must support for 2-level table"); + } + if sid_max_bits <= 8 { + info!("Smmuv3 must use linear stream table!"); + } + } + + fn init_limited_pt(&mut self) { + // its + for pt in self.iommu_pt_list.iter_mut() { + pt.insert(MemoryRegion::new_with_offset_mapper( + 0x8080000 as GuestPhysAddr, + 0x8080000, + 0x20000, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + } + + // ram + self.iommu_pt_list[0] + .insert(MemoryRegion::new_with_offset_mapper( + 0x80000000 as GuestPhysAddr, + 0x80000000, + 0x50000000, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + + self.iommu_pt_list[1] + .insert(MemoryRegion::new_with_offset_mapper( + 0x50000000 as GuestPhysAddr, + 0x50000000, + 0x30000000, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + + self.iommu_pt_list[2] + .insert(MemoryRegion::new_with_offset_mapper( + 0x80000000 as GuestPhysAddr, + 0x80000000, + 0x10000000, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + } + + fn init_structures(&mut self) { + self.init_strtab(); + self.init_queues(); + } + + fn init_strtab(&mut self) { + // linear stream table is our priority + self.init_linear_strtab(); + } + + // strtab + fn init_linear_strtab(&mut self) { + info!("Smmuv3 initing linear stream table"); + // The lower (5 + self.sid_max_bits) bits must be 0. + let tab_size = (1 << self.strtab.sid_max_bits) * STRTAB_STE_SIZE; + let frame_count = tab_size / PAGE_SIZE; // need 4MB + info!( + "stream table frame cnts:{}, align is {}", + frame_count, + 5 + self.strtab.sid_max_bits + ); + if let Ok(frame) = + Frame::new_contiguous_with_base(frame_count, 5 + self.strtab.sid_max_bits) + { + self.strtab.init_with_base(frame.start_paddr(), frame); + } else { + error!("stream table frames alloc err!!!") + } + info!("strtab_base:0x{:x}", self.strtab.get_base()); + let mut base = extract_bits(self.strtab.get_base(), STRTAB_BASE_OFF, STRTAB_BASE_LEN); + base = base << STRTAB_BASE_OFF; + base |= STRTAB_BASE_RA; + self.rp.STRTAB_BASE.set(base as _); + // strtab_base_cfg + let mut cfg: usize = 0; + cfg |= STRTAB_BASE_CFG_FMT_LINEAR << STRTAB_BASE_CFG_FMT_OFF; + cfg |= self.strtab.get_max_sid() << STRTAB_BASE_CFG_LOG2SIZE_OFF; + self.rp.STRTAB_BASE_CFG.set(cfg as _); + self.init_bypass_stes(); + } + + fn init_bypass_stes(&mut self) { + let entry_num: usize = 1 << self.strtab.get_max_sid(); + for sid in 0..entry_num { + self.strtab.init_bypass_ste(sid); + } + } + + fn init_queues(&mut self) { + self.init_cmdq(); + } + + fn init_cmdq(&mut self) { + let idr1: usize = self.rp.IDR1.get() as _; + let shift = extract_bits(idr1, IDR1_CMDQS_OFF, IDR1_CMDQS_LEN); + if shift > CMDQ_MAX_SZ_SHIFT { + self.cmdq.init(CMDQ_MAX_SZ_SHIFT as _); + } else { + self.cmdq.init(shift as _); + } + self.rp.CMDQ_BASE.set(self.cmdq.base_reg as _); + self.rp.CMDQ_CONS.set(self.cmdq.cons); + self.rp.CMDQ_PROD.set(self.cmdq.prod); + } + + fn sync_write_cr0(&mut self, value: usize) { + self.rp.CR0.set(value as _); + for _timeout in 0..ARM_SMMU_SYNC_TIMEOUT { + let val = self.rp.CR0ACK.get() as usize; + if val == value { + return; + } + } + error!("CRO write err!"); + } + + fn device_reset(&mut self) { + /* CR1 (table and queue memory attributes) */ + let mut reg = SH_ISH << CR1_TABLE_SH_OFF; + reg |= CR1_CACHE_WB << CR1_TABLE_OC_OFF; + reg |= CR1_CACHE_WB << CR1_TABLE_IC_OFF; + reg |= SH_ISH << CR1_QUEUE_SH_OFF; + reg |= CR1_CACHE_WB << CR1_QUEUE_OC_OFF; + reg |= CR1_CACHE_WB << CR1_QUEUE_IC_OFF; + self.rp.CR1.set(reg as _); + let mut cr0 = CR0_SMMUEN; + cr0 |= CR0_CMDQEN; + self.sync_write_cr0(cr0); + } + + // s1 bypass and s2 translate + fn write_ste(&mut self, sid: usize, vmid: usize) { + self.sync_ste(sid); + + assert!(vmid < MAX_ZONE_NUM, "Invalid zone id!"); + + self.strtab + .write_ste(sid, vmid, self.iommu_pt_list[vmid].root_paddr()); + } + + // invalidate the ste + fn sync_ste(&mut self, sid: usize) { + let cmd = self.cmdq.build_cfgi_cmd(sid); + self.cmd_insert(cmd); + self.sync_issue(); + } + + fn cmd_insert(&mut self, cmd: Cmd) { + while self.cmdq.q_full() { + self.cmdq.sync_cons(self.rp.CMDQ_CONS.get() as _); + } + self.cmdq.queue_write(cmd); + self.rp.CMDQ_PROD.set(self.cmdq.inc_prod() as _); + while !self.cmdq.q_empty() { + self.cmdq.sync_cons(self.rp.CMDQ_CONS.get() as _); + } + } + + fn sync_issue(&mut self) { + let cmd = self.cmdq.build_sync_cmd(); + self.cmd_insert(cmd); + } +} + +static SMMUV3: spin::Once> = spin::Once::new(); + +/// smmuv3 init +pub fn iommu_init() { + info!("Smmuv3 init..."); + SMMUV3.call_once(|| Mutex::new(Smmuv3::new())); +} + +/// smmuv3_base +#[allow(dead_code)] +pub fn smmuv3_base() -> usize { + SMMU_BASE_ADDR.into() +} + +/// smmuv3_size +#[allow(dead_code)] +pub fn smmuv3_size() -> usize { + SMMU_SIZE.into() +} + +/// write ste +pub fn iommu_add_device(vmid: usize, sid: usize) { + let mut smmu = SMMUV3.get().unwrap().lock(); + smmu.write_ste(sid as _, vmid as _); +} diff --git a/src/arch/aarch64/ipi.rs b/src/arch/aarch64/ipi.rs index c408ba0a..e659136e 100644 --- a/src/arch/aarch64/ipi.rs +++ b/src/arch/aarch64/ipi.rs @@ -1,13 +1,24 @@ -use super::sysreg::write_sysreg; - +#[cfg(feature = "gicv3")] +use crate::arch::sysreg::write_sysreg; +#[cfg(feature = "gicv2")] +use crate::device::irqchip::set_sgi_irq; pub fn arch_send_event(cpu_id: u64, sgi_num: u64) { - let aff3: u64 = 0 << 48; - let aff2: u64 = 0 << 32; - let aff1: u64 = 0 << 16; - let irm: u64 = 0 << 40; - let sgi_id: u64 = sgi_num << 24; - let target_list: u64 = 1 << cpu_id; - let val: u64 = aff1 | aff2 | aff3 | irm | sgi_id | target_list; - write_sysreg!(icc_sgi1r_el1, val); - debug!("write sgi sys value = {:#x}", val); + #[cfg(feature = "gicv3")] + { + let aff3: u64 = 0 << 48; + let aff2: u64 = 0 << 32; + let aff1: u64 = 0 << 16; + let irm: u64 = 0 << 40; + let sgi_id: u64 = sgi_num << 24; + let target_list: u64 = 1 << cpu_id; + let val: u64 = aff1 | aff2 | aff3 | irm | sgi_id | target_list; + write_sysreg!(icc_sgi1r_el1, val); + debug!("write sgi sys value = {:#x}", val); + } + #[cfg(feature = "gicv2")] + { + let sgi_id: u64 = sgi_num; + let target_list: u64 = 1 << cpu_id; + set_sgi_irq(sgi_id as usize, target_list as usize, 0); + } } diff --git a/src/arch/aarch64/mm.rs b/src/arch/aarch64/mm.rs index 66bbd9a1..5f470843 100644 --- a/src/arch/aarch64/mm.rs +++ b/src/arch/aarch64/mm.rs @@ -3,11 +3,7 @@ use core::sync::atomic::AtomicU32; use spin::RwLock; use crate::{ - arch::Stage2PageTable, - consts::MAX_CPU_NUM, - error::HvResult, - memory::MemorySet, - wait_for, + arch::Stage2PageTable, consts::MAX_CPU_NUM, error::HvResult, memory::MemorySet, wait_for, }; use super::sysreg::read_sysreg; diff --git a/src/arch/aarch64/mmu.rs b/src/arch/aarch64/mmu.rs new file mode 100644 index 00000000..c96478a5 --- /dev/null +++ b/src/arch/aarch64/mmu.rs @@ -0,0 +1,191 @@ +use cfg_if::cfg_if; +use cortex_a::registers::{MAIR_EL1, SCTLR_EL2}; +use tock_registers::interfaces::*; +use tock_registers::*; + +register_bitfields! {u64, + pub S1PageAndBlockDescriptor [ + RES OFFSET(55) NUMBITS(4) [], + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + PXN OFFSET(53) NUMBITS(1) [ + False = 0, + True = 1 + ], + OAB OFFSET(12) NUMBITS(36) [], + NLTA OFFSET(12) NUMBITS(36) [], + AF OFFSET(10) NUMBITS(1) [ + False = 0, + True = 1 + ], + SH OFFSET(8) NUMBITS(2) [ + NonShareable = 0b00, + Reserved = 0b01, + OuterShareable = 0b10, + InnerShareable = 0b11 + ], + AP OFFSET(6) NUMBITS(2) [ + PrivRW = 0b00, + PrivRW_UnprivRW = 0b01, + PrivRO = 0b10, + PrivRO_UnprivRO = 0b11 + ], + AttrIndx OFFSET(2) NUMBITS(3) [ + Attr0 = 0b000, + Attr1 = 0b001, + Attr2 = 0b010 + ], + TYPE OFFSET(1) NUMBITS(1) [ + Block = 0, + Page = 1 + ], + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} + +pub const PAGE_SIZE: usize = 4096; +pub const PAGE_SHIFT: usize = 12; + +pub const ENTRY_PER_PAGE: usize = PAGE_SIZE / 8; + +pub const WORD_SIZE: usize = 8; +pub const PTE_PER_PAGE: usize = PAGE_SIZE / WORD_SIZE; + +enum MemoryType { + Normal, + Device, + Null, +} + +enum PTEType { + Block, + Page, +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +struct PTEDescriptor(u64); + +impl PTEDescriptor { + fn new(addr: usize, mem_type: MemoryType, pte_type: PTEType) -> PTEDescriptor { + match pte_type { + PTEType::Block => PTEDescriptor( + (S1PageAndBlockDescriptor::OAB.val((addr >> PAGE_SHIFT) as u64) + + S1PageAndBlockDescriptor::AF::True + + S1PageAndBlockDescriptor::AP::PrivRW + + S1PageAndBlockDescriptor::TYPE::Block + + S1PageAndBlockDescriptor::VALID::True + + match mem_type { + MemoryType::Device => { + S1PageAndBlockDescriptor::AttrIndx::Attr0 + + S1PageAndBlockDescriptor::SH::OuterShareable + } + MemoryType::Normal => { + S1PageAndBlockDescriptor::AttrIndx::Attr1 + + S1PageAndBlockDescriptor::SH::InnerShareable + } + _ => { + S1PageAndBlockDescriptor::AttrIndx::Attr0 + + S1PageAndBlockDescriptor::SH::OuterShareable + } + }) + .value, + ), + PTEType::Page => PTEDescriptor( + (S1PageAndBlockDescriptor::NLTA.val((addr >> PAGE_SHIFT) as u64) + + S1PageAndBlockDescriptor::VALID::True + + S1PageAndBlockDescriptor::TYPE::Page) + .value, + ), + } + } + + const fn invalid() -> PTEDescriptor { + PTEDescriptor(0) + } +} + +#[repr(C)] +#[repr(align(4096))] +pub struct PageTables { + entry: [PTEDescriptor; ENTRY_PER_PAGE], +} + +// l1 page table +pub static BOOT_PT_L0: PageTables = PageTables { + entry: [PTEDescriptor(0); ENTRY_PER_PAGE], +}; + +// l2 page table +pub static BOOT_PT_L1: PageTables = PageTables { + entry: [PTEDescriptor(0); ENTRY_PER_PAGE], +}; + +//TODO: use memset from crate +pub unsafe fn memset(s: *mut u8, c: i32, n: usize) { + if (s as usize) < 0x1000 { + panic!("illegal addr for memset s {:x}", s as usize); + } + core::ptr::write_bytes(s, c as u8, n); +} + +// #[link_section = ".text.boot"] +pub extern "C" fn boot_pt_init(l0_pt: &mut PageTables, l1_pt: &mut PageTables) { + let l0_pt_entry: usize = l0_pt as *const _ as usize; + let l1_pt_entry: usize = l1_pt as *const _ as usize; + + unsafe { + memset(l0_pt_entry as *mut u8, 0, PAGE_SIZE); + memset(l1_pt_entry as *mut u8, 0, PAGE_SIZE); + } + cfg_if! { + if #[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] { + l0_pt.entry[0] = PTEDescriptor::new(0x0, MemoryType::Device, PTEType::Block); + for i in 1..ENTRY_PER_PAGE { + l0_pt.entry[i] = PTEDescriptor::new(0x40000000*i, MemoryType::Normal, PTEType::Block); + } + } else { + l0_pt.entry[0] = PTEDescriptor::new(0x0, MemoryType::Device, PTEType::Block); + for i in 1..7 { + l0_pt.entry[i] = PTEDescriptor::new(0x40000000*i, MemoryType::Normal, PTEType::Block); + } + for i in 8..ENTRY_PER_PAGE { + l0_pt.entry[i] = PTEDescriptor::invalid(); + } + } + } +} + +// init mmu +// #[link_section = ".text.boot"] +#[no_mangle] +pub extern "C" fn mmu_init(pt: &PageTables) { + use cortex_a::registers::*; + MAIR_EL2.write( + MAIR_EL2::Attr0_Device::nonGathering_nonReordering_noEarlyWriteAck + + MAIR_EL2::Attr1_Normal_Outer::WriteBack_NonTransient_ReadWriteAlloc + + MAIR_EL2::Attr1_Normal_Inner::WriteBack_NonTransient_ReadWriteAlloc + + MAIR_EL2::Attr2_Normal_Outer::NonCacheable + + MAIR_EL2::Attr2_Normal_Inner::NonCacheable, + ); + TTBR0_EL2.set(&pt.entry as *const _ as u64); + + TCR_EL2.write( + TCR_EL2::PS::Bits_48 + + TCR_EL2::SH0::Inner + + TCR_EL2::TG0::KiB_4 + + TCR_EL2::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL2::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL2::T0SZ.val(64 - 39), + ); +} + +// #[link_section = ".text.boot"] +pub extern "C" fn mmu_enable() { + SCTLR_EL2.modify(SCTLR_EL2::M::Enable + SCTLR_EL2::C::Cacheable + SCTLR_EL2::I::Cacheable); +} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index c8e765c3..8172b430 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -1,7 +1,9 @@ pub mod cpu; pub mod entry; +pub mod iommu; pub mod ipi; pub mod mm; +pub mod mmu; pub mod paging; pub mod s1pt; pub mod s2pt; diff --git a/src/arch/aarch64/sysreg.rs b/src/arch/aarch64/sysreg.rs index d6dcf454..29487d74 100644 --- a/src/arch/aarch64/sysreg.rs +++ b/src/arch/aarch64/sysreg.rs @@ -19,6 +19,7 @@ macro_rules! read_sysreg { } } } +use psci::smccc::smc64; pub(crate) use read_sysreg; /// Writes the given value to the given aarch64 system register. @@ -44,6 +45,27 @@ macro_rules! smc_arg1 { }}; } pub(crate) use smc_arg1; + +macro_rules! smc_call { + ($x0:expr, $x1:expr, $x2:expr, $x3:expr) => {{ + let mut x0_val: u64 = $x0; + let mut x1_val: u64 = $x1; + let mut x2_val: u64 = $x2; + let mut x3_val: u64 = $x3; + + ::core::arch::asm!( + "smc #0", + inout("x0") x0_val, + inout("x1") x1_val, + inout("x2") x2_val, + inout("x3") x3_val, + options(nomem, nostack), + ); + (x0_val,x1_val,x2_val,x3_val) + }}; +} +pub(crate) use smc_call; + // macro_rules! read_lrreg { // ($lr:expr) => { // { diff --git a/src/arch/aarch64/trap.rs b/src/arch/aarch64/trap.rs index e46aa589..41b25de9 100644 --- a/src/arch/aarch64/trap.rs +++ b/src/arch/aarch64/trap.rs @@ -1,12 +1,14 @@ use aarch64_cpu::{asm::wfi, registers::*}; use core::arch::global_asm; +use super::cpu::GeneralRegisters; +use crate::arch::sysreg::smc_call; use crate::{ arch::{ cpu::mpidr_to_cpuid, sysreg::{read_sysreg, write_sysreg}, }, - device::irqchip::gicv3::gicv3_handle_irq_el1, + device::irqchip::gic_handle_irq, event::{send_event, IPI_EVENT_SHUTDOWN, IPI_EVENT_WAKEUP}, hypercall::{HyperCall, SGI_IPI_ID}, memory::{mmio_handle_access, MMIOAccess}, @@ -14,8 +16,6 @@ use crate::{ zone::{is_this_root_zone, remove_zone}, }; -use super::cpu::GeneralRegisters; - global_asm!( include_str!("./trap.S"), sym arch_handle_exit @@ -35,11 +35,13 @@ const SMC_TYPE_MASK: u64 = 0x3F000000; pub mod SmcType { pub const ARCH_SC: u64 = 0x0; pub const STANDARD_SC: u64 = 0x4000000; + pub const SIP_SC: u64 = 0x2000000; } const PSCI_VERSION_1_1: u64 = 0x10001; const PSCI_TOS_NOT_PRESENT_MP: u64 = 2; const ARM_SMCCC_VERSION_1_0: u64 = 0x10000; +const ARM_SMCCC_NOT_SUPPORTED: i64 = -1; extern "C" { fn _hyp_trap_vector(); @@ -98,7 +100,7 @@ pub fn arch_handle_exit(regs: &mut GeneralRegisters) -> ! { fn irqchip_handle_irq1() { trace!("irq from el1"); - gicv3_handle_irq_el1(); + gic_handle_irq(); } fn irqchip_handle_irq2() { @@ -145,11 +147,11 @@ fn arch_handle_trap_el2(_regs: &mut GeneralRegisters) { println!("EL2 Exception: SMC64 call, ELR_EL2: {:#x?}", ELR_EL2.get()); } Some(ESR_EL2::EC::Value::DataAbortCurrentEL) => { - loop {} println!( - "EL2 Exception: Data Abort, ELR_EL2: {:#x?}, FAR_EL2: {:#x?}", - elr, esr + "EL2 Exception: Data Abort, ELR_EL2: {:#x?}, ESR_EL2: {:#x?}, FAR_EL2: {:#x?}", + elr, esr, far ); + loop {} } Some(ESR_EL2::EC::Value::InstrAbortCurrentEL) => { println!( @@ -159,7 +161,10 @@ fn arch_handle_trap_el2(_regs: &mut GeneralRegisters) { ); } _ => { - println!("Unhandled EL2 Exception: EC={:#x?}", 1); + println!( + "Unhandled EL2 Exception: EC={:#x?}", + ESR_EL2.read(ESR_EL2::EC) + ); } } loop {} @@ -229,7 +234,7 @@ fn handle_sysreg(regs: &mut GeneralRegisters) { let val = regs.usr[rt as usize]; trace!("esr_el2 rt{}: {:#x?}", rt, val); let sgi_id: u64 = (val & (0xf << 24)) >> 24; - if !this_cpu_data().arch_cpu.psci_on { + if !this_cpu_data().arch_cpu.power_on { warn!("skip send sgi {:#x?}", sgi_id); } else { trace!("send sgi {:#x?}", sgi_id); @@ -266,19 +271,23 @@ fn handle_hvc(regs: &mut GeneralRegisters) { fn handle_smc(regs: &mut GeneralRegisters) { let (code, arg0, arg1, arg2) = (regs.usr[0], regs.usr[1], regs.usr[2], regs.usr[3]); let cpu_data = this_cpu_data() as &mut PerCpu; - info!( - "SMC from CPU{}, func_id:{:#x?}, arg0:{:#x?}, arg1:{:#x?}, arg2:{:#x?}", - cpu_data.id, code, arg0, arg1, arg2 - ); + //info!( + // "SMC from CPU{}, func_id:{:#x?}, arg0:{:#x?}, arg1:{:#x?}, arg2:{:#x?}", + // cpu_data.id, code, arg0, arg1, arg2 + //); let result = match code & SMC_TYPE_MASK { SmcType::ARCH_SC => handle_arch_smc(regs, code, arg0, arg1, arg2), SmcType::STANDARD_SC => handle_psci_smc(regs, code, arg0, arg1, arg2), + SmcType::SIP_SC => unsafe { + (regs.usr[0], regs.usr[1], regs.usr[2], regs.usr[3]) = + smc_call!(code, arg0, arg1, arg2); + regs.usr[0] + }, _ => { warn!("unsupported smc"); 0 } }; - regs.usr[0] = result; arch_skip_instruction(regs); //skip the smc ins @@ -308,9 +317,9 @@ fn psci_emulate_cpu_on(regs: &mut GeneralRegisters) -> u64 { let target_data = get_cpu_data(cpu as _); let _lock = target_data.ctrl_lock.lock(); - if !target_data.arch_cpu.psci_on { + if !target_data.arch_cpu.power_on { target_data.cpu_on_entry = regs.usr[2] as _; - target_data.arch_cpu.psci_on = true; + target_data.arch_cpu.power_on = true; send_event(cpu as _, SGI_IPI_ID as _, IPI_EVENT_WAKEUP); } else { error!("psci: cpu {} already on", cpu); @@ -332,14 +341,14 @@ fn handle_psci_smc( PsciFnId::PSCI_VERSION => PSCI_VERSION_1_1, PsciFnId::PSCI_CPU_SUSPEND_32 | PsciFnId::PSCI_CPU_SUSPEND_64 => { wfi(); - gicv3_handle_irq_el1(); + gic_handle_irq(); 0 - }, + } PsciFnId::PSCI_CPU_OFF_32 | PsciFnId::PSCI_CPU_OFF_64 => { todo!(); } PsciFnId::PSCI_AFFINITY_INFO_32 | PsciFnId::PSCI_AFFINITY_INFO_64 => { - !get_cpu_data(arg0 as _).arch_cpu.psci_on as _ + !get_cpu_data(arg0 as _).arch_cpu.power_on as _ } PsciFnId::PSCI_MIG_INFO_TYPE => PSCI_TOS_NOT_PRESENT_MP, PsciFnId::PSCI_FEATURES => psci_emulate_features_info(regs.usr[1]), @@ -439,5 +448,5 @@ pub unsafe extern "C" fn vmreturn(_gu_regs: usize) -> ! { ", options(noreturn), - ); + ) } diff --git a/src/arch/aarch64/zone.rs b/src/arch/aarch64/zone.rs index b8b6aa0f..c1d142f7 100644 --- a/src/arch/aarch64/zone.rs +++ b/src/arch/aarch64/zone.rs @@ -1,10 +1,8 @@ use core::panic; -use alloc::vec::Vec; - use crate::{ config::*, - device::virtio_trampoline::{mmio_virtio_handler, VIRTIO_BRIDGE}, + device::virtio_trampoline::mmio_virtio_handler, error::HvResult, memory::{GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion}, zone::Zone, @@ -45,17 +43,22 @@ impl Zone { info!("VM stage 2 memory set: {:#x?}", self.gpm); Ok(()) } - - pub fn mmio_init(&mut self, hv_config: &HvArchZoneConfig) { - self.vgicv3_mmio_init(hv_config); - } } #[repr(C)] #[derive(Debug, Clone)] pub struct HvArchZoneConfig { pub gicd_base: usize, - pub gicr_base: usize, pub gicd_size: usize, + pub gicr_base: usize, pub gicr_size: usize, + pub gits_base: usize, + pub gits_size: usize, + pub gicc_base: usize, + pub gicc_offset: usize, + pub gicc_size: usize, + pub gich_base: usize, + pub gich_size: usize, + pub gicv_base: usize, + pub gicv_size: usize, } diff --git a/src/arch/loongarch64/cpu.rs b/src/arch/loongarch64/cpu.rs new file mode 100644 index 00000000..087b070a --- /dev/null +++ b/src/arch/loongarch64/cpu.rs @@ -0,0 +1,149 @@ +use super::ipi::*; +use super::zone::ZoneContext; +use crate::arch::zone::disable_hwi_through; +use crate::device::common::MMIODerefWrapper; +use crate::percpu::this_cpu_data; +use core::arch::asm; +use core::fmt::{self, Debug, Formatter}; +use loongArch64::register::crmd::Crmd; +use loongArch64::register::pgdl; +use loongArch64::register::{cpuid, crmd}; +use tock_registers::interfaces::Writeable; + +use crate::{ + consts::{PER_CPU_ARRAY_PTR, PER_CPU_SIZE}, + memory::VirtAddr, +}; + +#[repr(C)] +#[derive(Debug)] +pub struct ArchCpu { + pub ctx: ZoneContext, + pub stack_top: usize, + pub cpuid: usize, + pub power_on: bool, + pub init: bool, +} + +impl ArchCpu { + pub fn new(cpuid: usize) -> Self { + let ret = ArchCpu { + ctx: super::trap::dump_reset_gcsrs(), + stack_top: 0, + cpuid, + power_on: false, + init: false, + }; + ret + } + pub fn get_cpuid(&self) -> usize { + self.cpuid + } + pub fn stack_top(&self) -> VirtAddr { + PER_CPU_ARRAY_PTR as VirtAddr + (self.get_cpuid() + 1) as usize * PER_CPU_SIZE + } + pub fn init(&mut self, entry: usize, cpu_id: usize, dtb: usize) { + self.ctx.sepc = entry; + self.stack_top = self.stack_top() as usize; + } + pub fn run(&mut self) -> ! { + assert!(this_cpu_id() == self.get_cpuid()); + this_cpu_data().activate_gpm(); + self.power_on = true; + if !self.init { + self.init(this_cpu_data().cpu_on_entry, this_cpu_data().id, 0); + self.init = true; + } + // set x[] to all 0 + for i in 0..32 { + self.ctx.x[i] = 0; + } + info!( + "loongarch64: CPU{} run@{:#x}", + self.get_cpuid(), + self.ctx.sepc + ); + info!("loongarch64: @{:#x?}", self); + // step 1: enable guest mode + // step 2: set guest entry to era + // step 3: run ertn and enter guest mode + let ctx_addr = &mut self.ctx as *mut ZoneContext; + debug!( + "loongarch64: ArchCpu::run: percpu_s={:#x}", + self.stack_top() - PER_CPU_SIZE + ); + debug!( + "loongarch64: ArchCpu::run: ctx_addr={:#x}, size={}", + ctx_addr as usize, + core::mem::size_of::() + ); + debug!( + "loongarch64: ArchCpu::run: stack_tp={:#x}", + self.stack_top() + ); + + let cpuid = self.get_cpuid(); + if cpuid != 0 { + // on loongarch64 we only allow direct interrupt through on cpu0 with rootlinux + // root linux use cpuintc->liointc->uart0 for IO irqs, we put it through to use uart0 + // on nonroot, we only need to inject virtio irq so let's disable it - wheatfox + disable_hwi_through(); + } + + unsafe { + asm!( + "csrwr {}, {LOONGARCH_CSR_SAVE3}", + "csrwr {}, {LOONGARCH_CSR_SAVE4}", + in(reg) (ctx_addr as usize + core::mem::size_of::()), + in(reg) self.stack_top(), + LOONGARCH_CSR_SAVE3 = const 0x33, + LOONGARCH_CSR_SAVE4 = const 0x34, + ); + } + + unsafe { + asm!("invtlb 0, $r0, $r0"); // flush TLBs + } + + super::trap::_vcpu_return(ctx_addr as usize); + + panic!("loongarch64: ArchCpu::run: unreachable"); + } + pub fn idle(&mut self) -> ! { + let ctx_addr = &mut self.ctx as *mut ZoneContext; + unsafe { + asm!( + "csrwr {}, {LOONGARCH_CSR_SAVE3}", + "csrwr {}, {LOONGARCH_CSR_SAVE4}", + in(reg) (ctx_addr as usize + core::mem::size_of::()), + in(reg) self.stack_top(), + LOONGARCH_CSR_SAVE3 = const 0x33, + LOONGARCH_CSR_SAVE4 = const 0x34, + ); + } + info!("loongarch64: ArchCpu::idle: cpuid={}", self.get_cpuid()); + // enable ipi on ecfg + ecfg_ipi_enable(); + loop {} + } +} + +pub fn this_cpu_id() -> usize { + cpuid::read().core_id() +} + +pub fn cpu_start(cpuid: usize, start_addr: usize, opaque: usize) { + let start_addr = start_addr & 0x0000_ffff_ffff_ffff; + let ipi: &MMIODerefWrapper = match cpuid { + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + panic!("loongarch64: cpu_start: invalid cpuid={}", cpuid); + } + }; + ipi.ipi_enable.write(IpiEnable::IPIENABLE.val(0xffffffff)); + let entry_addr = start_addr; + mail_send(entry_addr, cpuid, 0); + ipi_write_action(cpuid, SMP_BOOT_CPU); +} diff --git a/src/arch/loongarch64/entry.rs b/src/arch/loongarch64/entry.rs new file mode 100644 index 00000000..2fab239c --- /dev/null +++ b/src/arch/loongarch64/entry.rs @@ -0,0 +1,81 @@ +use crate::consts::PER_CPU_SIZE; + +const DMW_DA_BITS: usize = 48; +const CSR_DMW0_PLV0: usize = 1 << 0; +const CSR_DMW0_VSEG: usize = 0x8000; +const CSR_DMW0_BASE: usize = CSR_DMW0_VSEG << DMW_DA_BITS; +const CSR_DMW0_INIT: usize = CSR_DMW0_BASE | CSR_DMW0_PLV0; + +const CSR_DMW1_PLV0: usize = 1 << 0; +const CSR_DMW1_MAT: usize = 1 << 4; +const CSR_DMW1_VSEG: usize = 0x9000; +const CSR_DMW1_BASE: usize = CSR_DMW1_VSEG << DMW_DA_BITS; +const CSR_DMW1_INIT: usize = CSR_DMW1_BASE | CSR_DMW1_PLV0 | CSR_DMW1_MAT; + +#[naked] +#[no_mangle] +#[link_section = ".text.entry"] +pub unsafe extern "C" fn arch_entry() -> i32 { + // a0/r4: CPU_ID read from CSR 0x20 CPUID + + // .macro JUMP_VIRT_ADDR temp1 temp2 (r12, r13) + // li.d \temp1, CACHE_BASE (0x9000_0000_0000_0000) + // pcaddi \temp2, 0 + // or \temp1, \temp1, \temp2 + // jirl zero, \temp1, 0xc // 0xc is beacuse the above pcaddi + 0xc will jump to exacly the next instruction after jirl - wheatfox + // .endm + core::arch::asm!( + " + 0: + li.d $r12, {CSR_DMW0_INIT} // 0x8 + csrwr $r12, {LOONGARCH_CSR_DMW0} + li.d $r12, {CSR_DMW1_INIT} // 0x9 + csrwr $r12, {LOONGARCH_CSR_DMW1} + + // first JUMP_VIRT_ADDR + li.d $r12, {CSR_DMW1_BASE} + pcaddi $r13, 0 + or $r12, $r12, $r13 + jirl $zero, $r12, 0xc + // end of JUMP_VIRT_ADDR + + li.w $r12, 0xb0 // PLV=0, IE=0, PG=1 + csrwr $r12, {LOONGARCH_CSR_CRMD} + li.w $r12, 0x04 // PLV=0, PIE=1, PWE=0 + csrwr $r12, {LOONGARCH_CSR_PRMD} + li.w $r12, 0x00 // FPE=0, SXE=0, ASXE=0, BTE=0 + csrwr $r12, {LOONGARCH_CSR_EUEN} + + csrrd $r4, {CSR_CPUID} + la.pcrel $r12, __core_end + li.d $r13, {per_cpu_size} + mul.d $r14, $r4, $r13 + add.d $r14, $r13, $r14 + add.d $r15, $r12, $r14 // this is the stack top of CPU[CPU_ID] + addi.d $sp, $r15, 0 // set sp + // la.pcrel $r12, sbss // bss start, t0 as ptr + // la.pcrel $r13, ebss // bss end + // 1: + // st.d $zero, $r12, 0 // MEM[ptr] = 0 + // beq $r12, $r13, 2f // break if ptr == ebss, and we just set MEM[ebss] = 0 + // addi.d $r12, $r12, 8 // ptr += 8 + // b 1b + 2: + ibar 0 + dbar 0 + bl {rust_main} + ", + CSR_DMW0_INIT = const CSR_DMW0_INIT, + CSR_DMW1_INIT = const CSR_DMW1_INIT, + LOONGARCH_CSR_CRMD = const 0x0, + LOONGARCH_CSR_PRMD = const 0x1, + LOONGARCH_CSR_EUEN = const 0x2, + LOONGARCH_CSR_DMW0 = const 0x180, + LOONGARCH_CSR_DMW1 = const 0x181, + CSR_DMW1_BASE = const 0x9000000000000000usize, + rust_main = sym crate::rust_main, + per_cpu_size = const PER_CPU_SIZE, + CSR_CPUID = const 0x20, + options(noreturn), + ); +} diff --git a/src/arch/loongarch64/ipi.rs b/src/arch/loongarch64/ipi.rs new file mode 100644 index 00000000..c4a6d9b9 --- /dev/null +++ b/src/arch/loongarch64/ipi.rs @@ -0,0 +1,327 @@ +use crate::arch::cpu::this_cpu_id; +use crate::device::common::MMIODerefWrapper; +use core::arch::asm; +use core::ptr::write_volatile; +use loongArch64::cpu; +use loongArch64::register::ecfg::LineBasedInterrupt; +use loongArch64::register::*; +use loongArch64::time; +use tock_registers::fields::FieldValue; +use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; +use tock_registers::register_bitfields; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +pub fn arch_send_event(cpu_id: u64, sgi_num: u64) { + debug!( + "loongarch64: arch_send_event: sending event to cpu: {}, sgi_num: {}", + cpu_id, sgi_num + ); + // just call ipi_write_action + ipi_write_action(cpu_id as usize, sgi_num as usize); +} + +register_bitfields! [ + u32, + pub IpiStatus [ IPISTATUS OFFSET(0) NUMBITS(32) ], + pub IpiEnable [ IPIENABLE OFFSET(0) NUMBITS(32) ], + pub IpiSet [ IPISET OFFSET(0) NUMBITS(32) ], + pub IpiClear [ IPICLEAR OFFSET(0) NUMBITS(32) ], +]; + +register_bitfields! [ + u64, + pub Mailbox0 [ MAILBOX0 OFFSET(0) NUMBITS(64) ], + pub Mailbox1 [ MAILBOX1 OFFSET(0) NUMBITS(64) ], + pub Mailbox2 [ MAILBOX2 OFFSET(0) NUMBITS(64) ], + pub Mailbox3 [ MAILBOX3 OFFSET(0) NUMBITS(64) ], +]; + +register_structs! { + #[allow(non_snake_case)] + pub IpiRegisters { + (0x00 => pub ipi_status: ReadOnly), + (0x04 => pub ipi_enable: ReadWrite), + (0x08 => pub ipi_set: WriteOnly), + (0x0c => pub ipi_clear: WriteOnly), + (0x10 => _reserved0: [u8; 0x10]), + (0x20 => pub mailbox0: ReadWrite), + (0x28 => pub mailbox1: ReadWrite), + (0x30 => pub mailbox2: ReadWrite), + (0x38 => pub mailbox3: ReadWrite), + (0x40 => @END), + } +} + +const MMIO_BASE: usize = 0x8000_0000_1fe0_0000; +const IPI_MMIO_BASE: usize = MMIO_BASE; + +// IPI registers, use this if you don't want to use the percore-IPI feature +pub static CORE0_IPI: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(IPI_MMIO_BASE + 0x1000) }; +pub static CORE1_IPI: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(IPI_MMIO_BASE + 0x1100) }; +pub static CORE2_IPI: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(IPI_MMIO_BASE + 0x1200) }; +pub static CORE3_IPI: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(IPI_MMIO_BASE + 0x1300) }; + +// ipi actions +pub const SMP_BOOT_CPU: usize = 0x1; +pub const SMP_RESCHEDULE: usize = 0x2; +pub const SMP_CALL_FUNCTION: usize = 0x4; +// customized actions :), since there is no docs on this yet +pub const HVISOR_START_VCPU: usize = 0x8; + +fn iocsr_mbuf_send_box_lo(a: usize) -> usize { + a << 1 +} +fn iocsr_mbuf_send_box_hi(a: usize) -> usize { + (a << 1) + 1 +} + +// allow unused for now +#[allow(unused_assignments)] +pub fn mail_send_percore(data: usize, cpu_id: usize, mailbox_id: usize) { + // the high and low 32 bits should be sent separately + // first high 32 bits, then low 32 bits + let mut high = data >> 32; + let mut low = data & 0xffffffff; + let mut val: usize = 0; + // send high 32 bits + val = 1 << 31; + val |= iocsr_mbuf_send_box_hi(mailbox_id) << 2; + val |= cpu_id << 16; + val |= high << 32; + // debug!("(mail_send) sending high 32 bits, actual packed value: {:#x}", val); + unsafe { + // asm!("iocsrwr.d {}, {}", in(reg) val, in(reg) 0x1048); + write_volatile(IPI_MMIO_MAIL_SEND as *mut u64, val as u64); + } + // send low 32 bits + val = 1 << 31; + val |= iocsr_mbuf_send_box_lo(mailbox_id) << 2; + val |= cpu_id << 16; + val |= low << 32; + // debug!("(mail_send) sending low 32 bits, actual packed value: {:#x}", val); + unsafe { + // asm!("iocsrwr.d {}, {}", in(reg) val, in(reg) 0x1048); + write_volatile(IPI_MMIO_MAIL_SEND as *mut u64, val as u64); + } +} + +fn ffs(a: usize) -> usize { + // find first set bit, least significant bit is at position 1 + // if a is 0, return 0 + if a == 0 { + return 0; + } + let mut a = a; + let mut i = 0; + while (a & 1) == 0 { + a >>= 1; + i += 1; + } + i + 1 +} + +const IPI_MMIO_IPI_SEND: usize = MMIO_BASE + 0x1040; // 32 bits Write Only +const IPI_MMIO_MAIL_SEND: usize = MMIO_BASE + 0x1048; // 64 bits Write Only + +#[allow(unused_assignments)] +pub fn ipi_write_action_percore(cpu_id: usize, _action: usize) { + let mut irq: u32 = 0; + let mut action = _action; + debug!( + "loongarch64::ipi_write_action sending action: {:#x} to cpu: {}", + action, cpu_id + ); + loop { + irq = ffs(action) as u32; + if irq == 0 { + break; + } + let mut val: u32 = 1 << 31; + val |= irq - 1; + val |= (cpu_id as u32) << 16; + debug!( + "loongarch64::ipi_write_action writing value {:#x} to MMIO address: {:#x}", + val, IPI_MMIO_IPI_SEND + ); + unsafe { + // asm!("iocsrwr.w {}, {}", in(reg) val, in(reg) 0x1040); + write_volatile(IPI_MMIO_IPI_SEND as *mut u32, val); + } + debug!( + "loongarch64::ipi_write_action sent irq: {} to cpu: {} !", + irq, cpu_id + ); + action &= !(1 << (irq - 1)); + } + debug!( + "loongarch64::ipi_write_action finished sending to cpu: {}", + cpu_id + ); +} + +pub fn ipi_write_action(cpu_id: usize, _action: usize) { + // just write _action directly to the target cpu legacy IPI registers + // which is the IPI_SET register + debug!( + "ipi_write_action_legacy: sending action: {:#x} to cpu: {}", + _action, cpu_id + ); + let ipi: &MMIODerefWrapper = match cpu_id { + 0 => &CORE0_IPI, + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + error!("ipi_write_action_legacy: invalid cpu_id: {}", cpu_id); + return; + } + }; + ipi.ipi_set.write(IpiSet::IPISET.val(_action as u32)); + debug!( + "ipi_write_action_legacy: finished sending action: {:#x} to cpu: {}", + _action, cpu_id + ); +} + +pub fn mail_send(data: usize, cpu_id: usize, mailbox_id: usize) { + // just write data to the target cpu mailbox registers + // which is the mailbox0 register + debug!( + "mail_send: sending data: {:#x} to cpu: {}, mailbox_id: {}", + data, cpu_id, mailbox_id + ); + let ipi: &MMIODerefWrapper = match cpu_id { + 0 => &CORE0_IPI, + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + error!("mail_send: invalid cpu_id: {}", cpu_id); + return; + } + }; + match mailbox_id { + 0 => ipi.mailbox0.write(Mailbox0::MAILBOX0.val(data as u64)), + 1 => ipi.mailbox1.write(Mailbox1::MAILBOX1.val(data as u64)), + 2 => ipi.mailbox2.write(Mailbox2::MAILBOX2.val(data as u64)), + 3 => ipi.mailbox3.write(Mailbox3::MAILBOX3.val(data as u64)), + _ => { + error!("mail_send: invalid mailbox_id: {}", mailbox_id); + return; + } + } + debug!( + "mail_send: finished sending data: {:#x} to cpu: {}, mailbox_id: {}", + data, cpu_id, mailbox_id + ); +} + +pub fn enable_ipi(cpu_id: usize) { + let ipi: &MMIODerefWrapper = match cpu_id { + 0 => &CORE0_IPI, + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + error!("enable_ipi: invalid cpu_id: {}", cpu_id); + return; + } + }; + ipi.ipi_enable.write(IpiEnable::IPIENABLE.val(0xffffffff)); + debug!("enable_ipi: IPI enabled for cpu {}", cpu_id); +} + +pub fn clear_all_ipi(cpu_id: usize) { + let ipi: &MMIODerefWrapper = match cpu_id { + 0 => &CORE0_IPI, + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + error!("clear_all_ipi: invalid cpu_id: {}", cpu_id); + return; + } + }; + ipi.ipi_clear.write(IpiClear::IPICLEAR.val(0xffffffff)); + debug!( + "clear_all_ipi: IPI status for cpu {}: {:#x}", + cpu_id, + ipi.ipi_status.read(IpiStatus::IPISTATUS) + ); +} + +pub fn reset_ipi(cpu_id: usize) { + // clear all IPIs and enable all IPIs + clear_all_ipi(cpu_id); + enable_ipi(cpu_id); +} + +pub fn get_ipi_status(cpu_id: usize) -> u32 { + let ipi: &MMIODerefWrapper = match cpu_id { + 0 => &CORE0_IPI, + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + error!("get_ipi_status: invalid cpu_id: {}", cpu_id); + return 0; + } + }; + ipi.ipi_status.read(IpiStatus::IPISTATUS) +} + +pub fn ecfg_ipi_enable() { + let mut lie_ = ecfg::read().lie(); + lie_ = lie_ | LineBasedInterrupt::IPI; + ecfg::set_lie(lie_); + info!( + "ecfg ipi enabled on cpu {}, current lie: {:?}", + this_cpu_id(), + lie_ + ); +} + +pub fn ecfg_ipi_disable() { + let mut lie_ = ecfg::read().lie(); + lie_ = lie_ & !LineBasedInterrupt::IPI; + ecfg::set_lie(lie_); + info!( + "ecfg ipi disabled on cpu {}, current lie: {:?}", + this_cpu_id(), + lie_ + ); +} + +pub fn dump_ipi_registers() { + info!( + "dump_ipi_registers: dumping IPI registers for this cpu {}", + this_cpu_id() + ); + let ipi: &MMIODerefWrapper = match this_cpu_id() { + 0 => &CORE0_IPI, + 1 => &CORE1_IPI, + 2 => &CORE2_IPI, + 3 => &CORE3_IPI, + _ => { + error!("dump_ipi_registers: invalid cpu_id: {}", this_cpu_id()); + return; + } + }; + println!( + "ipi_status: {:#x}, ipi_enable: {:#x}", + ipi.ipi_status.read(IpiStatus::IPISTATUS), + ipi.ipi_enable.read(IpiEnable::IPIENABLE), + ); + println!( + "mailbox0: {:#x}, mailbox1: {:#x}, mailbox2: {:#x}, mailbox3: {:#x}", + ipi.mailbox0.read(Mailbox0::MAILBOX0), + ipi.mailbox1.read(Mailbox1::MAILBOX1), + ipi.mailbox2.read(Mailbox2::MAILBOX2), + ipi.mailbox3.read(Mailbox3::MAILBOX3) + ); +} diff --git a/src/arch/loongarch64/mm.rs b/src/arch/loongarch64/mm.rs new file mode 100644 index 00000000..e017e997 --- /dev/null +++ b/src/arch/loongarch64/mm.rs @@ -0,0 +1,48 @@ +use crate::{ + arch::s1pt::Stage1PageTable, + arch::s2pt::Stage2PageTable, + consts::PAGE_SIZE, + error::HvResult, + memory::{ + addr::{align_down, align_up}, + GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet, HV_PT, + }, +}; +use spin::*; + +pub const LOONGARCH64_CACHED_DMW_PREFIX: u64 = 0x9000_0000_0000_0000; +pub const LOONGARCH64_UNCACHED_DMW_PREFIX: u64 = 0x8000_0000_0000_0000; + +pub fn init_hv_page_table(fdt: &fdt::Fdt) -> HvResult { + let mut hv_pt: MemorySet = MemorySet::new(4); + // let mem_region = fdt.memory().regions().next().unwrap(); + // info!("loongarch64: mm: mem_region: {:#x?}", mem_region); + // find all serial + for node in fdt.find_all_nodes("/platform/serial") { + if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + let paddr = reg.starting_address as HostPhysAddr; + let size = reg.size.unwrap(); + info!( + "loongarch64: mm: map serial addr: {:#x}, size: {:#x}", + paddr, size + ); + let paddr = align_down(paddr); + let size = align_up(size); + hv_pt.insert(MemoryRegion::new_with_offset_mapper( + paddr as GuestPhysAddr, + paddr, + size, + MemFlags::READ | MemFlags::WRITE | MemFlags::IO, + ))?; + } + } + info!("loongarch64: mm: init_hv_page_table done"); + debug!("Hypervisor virtual memory set: {:#x?}", hv_pt); + + HV_PT.call_once(|| RwLock::new(hv_pt)); + Ok(()) +} + +pub fn new_s2_memory_set() -> MemorySet { + MemorySet::new(4) +} diff --git a/src/arch/loongarch64/mod.rs b/src/arch/loongarch64/mod.rs new file mode 100644 index 00000000..04209e7b --- /dev/null +++ b/src/arch/loongarch64/mod.rs @@ -0,0 +1,15 @@ +#![allow(unused)] + +pub mod cpu; +pub mod entry; +pub mod ipi; +pub mod mm; +pub mod paging; +pub mod register; +pub mod s1pt; +pub mod s2pt; +pub mod trap; +pub mod zone; + +pub use s1pt::Stage1PageTable; +pub use s2pt::Stage2PageTable; diff --git a/src/arch/loongarch64/paging.rs b/src/arch/loongarch64/paging.rs new file mode 100644 index 00000000..b5d82981 --- /dev/null +++ b/src/arch/loongarch64/paging.rs @@ -0,0 +1,618 @@ +use crate::error::{HvError, HvResult}; +use crate::memory::addr::is_aligned; +use crate::memory::mapper::Mapper; +use crate::memory::{Frame, MemFlags, MemoryRegion, PhysAddr, VirtAddr}; +use alloc::{sync::Arc, vec::Vec}; +use core::{fmt::Debug, marker::PhantomData, slice}; +use spin::Mutex; + +use loongArch64::register::pwch::{set_dir3_base, set_dir3_width, set_dir4_base, set_dir4_width}; +use loongArch64::register::pwcl::{ + set_dir1_base, set_dir1_width, set_dir2_base, set_dir2_width, set_ptbase, set_pte_width, + set_ptwidth, +}; +use loongArch64::register::stlbps::set_ps; +use loongArch64::register::MemoryAccessType; +use loongArch64::register::{crmd, pwch, pwcl, tlbrentry}; +use loongArch64::register::{pgd, pgdh, pgdl}; + +#[derive(Debug)] +pub enum PagingError { + NoMemory, + NotMapped, + AlreadyMapped, + MappedToHugePage, +} + +pub type PagingResult = Result; + +impl From for HvError { + fn from(err: PagingError) -> Self { + match err { + PagingError::NoMemory => hv_err!(ENOMEM), + _ => hv_err!(EFAULT, format!("{:?}", err)), + } + } +} + +#[repr(usize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum PageSize { + Size4K = 0x1000, + Size2M = 0x20_0000, + Size1G = 0x4000_0000, +} + +#[derive(Debug, Copy, Clone)] +pub struct Page { + vaddr: VA, + size: PageSize, +} + +impl PageSize { + pub const fn is_aligned(self, addr: usize) -> bool { + self.page_offset(addr) == 0 + } + + pub const fn align_down(self, addr: usize) -> usize { + addr & !(self as usize - 1) + } + + pub const fn page_offset(self, addr: usize) -> usize { + addr & (self as usize - 1) + } + + pub const fn is_huge(self) -> bool { + matches!(self, Self::Size1G | Self::Size2M) + } +} + +impl + Copy> Page { + pub fn new_aligned(vaddr: VA, size: PageSize) -> Self { + debug_assert!(size.is_aligned(vaddr.into())); + Self { vaddr, size } + } +} + +pub trait GenericPTE: Debug + Clone { + /// Returns the physical address mapped by this entry. + fn addr(&self) -> PhysAddr; + /// Returns the flags of this entry. + fn flags(&self) -> MemFlags; + /// Returns whether this entry is zero. + fn is_unused(&self) -> bool; + /// Returns whether this entry flag indicates present. + fn is_present(&self) -> bool; + /// Returns whether this entry maps to a huge frame. + fn is_huge(&self) -> bool; + /// Set physical address for terminal entries. + fn set_addr(&mut self, paddr: PhysAddr); + /// Set flags for terminal entries. + fn set_flags(&mut self, flags: MemFlags, is_huge: bool); + /// Set physical address and flags for intermediate table entries. + fn set_table(&mut self, paddr: PhysAddr); + /// Set this entry to zero. + fn clear(&mut self); +} + +const ENTRY_COUNT: usize = 512; + +pub trait PagingInstr { + unsafe fn activate(root_paddr: PhysAddr); + fn flush(vaddr: Option); +} + +/// A basic read-only page table for address query only. +pub trait GenericPageTableImmut: Sized { + type VA: From + Into + Copy; + + unsafe fn from_root(root_paddr: PhysAddr) -> Self; + fn root_paddr(&self) -> PhysAddr; + fn query(&self, vaddr: Self::VA) -> PagingResult<(PhysAddr, MemFlags, PageSize)>; +} + +/// A extended mutable page table can change mappings. +pub trait GenericPageTable: GenericPageTableImmut { + fn new() -> Self; + + fn map(&mut self, region: &MemoryRegion) -> HvResult; + fn unmap(&mut self, region: &MemoryRegion) -> HvResult; + fn update( + &mut self, + vaddr: Self::VA, + paddr: PhysAddr, + flags: MemFlags, + ) -> PagingResult; + + fn clone(&self) -> Self; + + unsafe fn activate(&self); + fn flush(&self, vaddr: Option); +} + +/// A immutable level-4 page table implements `GenericPageTableImmut`. +pub struct Level4PageTableImmut { + /// Root table frame. + root: Frame, + /// Phantom data. + _phantom: PhantomData<(VA, PTE)>, +} + +impl Level4PageTableImmut +where + VA: From + Into + Copy, + PTE: GenericPTE, +{ + fn new() -> Self { + Self { + root: Frame::new_zero().expect("failed to allocate root frame for host page table"), + _phantom: PhantomData, + } + } + + fn get_entry_mut(&self, vaddr: VA) -> PagingResult<(&mut PTE, PageSize)> { + let vaddr = vaddr.into(); + let p4 = table_of_mut::(self.root_paddr()); + let p4e = &mut p4[p4_index(vaddr)]; + + let p3 = next_table_mut(p4e)?; + let p3e = &mut p3[p3_index(vaddr)]; + if p3e.is_huge() { + return Ok((p3e, PageSize::Size1G)); + } + + let p2 = next_table_mut(p3e)?; + let p2e = &mut p2[p2_index(vaddr)]; + if p2e.is_huge() { + return Ok((p2e, PageSize::Size2M)); + } + + let p1 = next_table_mut(p2e)?; + let p1e = &mut p1[p1_index(vaddr)]; + Ok((p1e, PageSize::Size4K)) + } + + fn walk( + &self, + table: &[PTE], + level: usize, + start_vaddr: usize, + limit: usize, + func: &impl Fn(usize, usize, usize, &PTE), + ) { + let mut n = 0; + for (i, entry) in table.iter().enumerate() { + let vaddr = start_vaddr + (i << (12 + (3 - level) * 9)); + if entry.is_present() { + func(level, i, vaddr, entry); + if level < 3 { + match next_table_mut(entry) { + Ok(entry) => self.walk(entry, level + 1, vaddr, limit, func), + Err(PagingError::MappedToHugePage) => {} + _ => unreachable!(), + } + } + n += 1; + if n >= limit { + break; + } + } + } + } + + fn dump(&self, limit: usize) { + static LOCK: Mutex<()> = Mutex::new(()); + let _lock = LOCK.lock(); + + println!("Root: {:x?}", self.root_paddr()); + self.walk( + table_of(self.root_paddr()), + 0, + 0, + limit, + &|level: usize, idx: usize, vaddr: usize, entry: &PTE| { + for _ in 0..level * 2 { + print!(" "); + } + println!( + "[ADDR:{:#x?} level:{} - idx:{:03}], vaddr:{:08x?}: {:x?}", + entry as *const _ as VirtAddr, level, idx, vaddr, entry + ); + }, + ); + } +} + +impl GenericPageTableImmut for Level4PageTableImmut +where + VA: From + Into + Copy, + PTE: GenericPTE, +{ + type VA = VA; + + unsafe fn from_root(root_paddr: PhysAddr) -> Self { + Self { + root: Frame::from_paddr(root_paddr), + _phantom: PhantomData, + } + } + + fn root_paddr(&self) -> PhysAddr { + self.root.start_paddr() + } + + fn query(&self, vaddr: VA) -> PagingResult<(PhysAddr, MemFlags, PageSize)> { + let (entry, size) = self.get_entry_mut(vaddr)?; + if entry.is_unused() { + return Err(PagingError::NotMapped); + } + let off = size.page_offset(vaddr.into()); + Ok((entry.addr() + off, entry.flags(), size)) + } +} + +/// A extended level-4 page table that can change its mapping. It also tracks all intermediate +/// level tables. Locks need to be used if change the same page table concurrently. +struct Level4PageTableUnlocked { + inner: Level4PageTableImmut, + /// Intermediate level table frames. + intrm_tables: Vec, + /// Phantom data. + _phantom: PhantomData<(VA, PTE, I)>, +} + +impl Level4PageTableUnlocked +where + VA: From + Into + Copy, + PTE: GenericPTE, + I: PagingInstr, +{ + fn new() -> Self { + Self { + inner: Level4PageTableImmut::new(), + intrm_tables: Vec::new(), + _phantom: PhantomData, + } + } + + unsafe fn from_root(root_paddr: PhysAddr) -> Self { + Self { + inner: Level4PageTableImmut::from_root(root_paddr), + intrm_tables: Vec::new(), + _phantom: PhantomData, + } + } + + fn alloc_intrm_table(&mut self) -> HvResult { + let frame = Frame::new_zero()?; + let paddr = frame.start_paddr(); + self.intrm_tables.push(frame); + Ok(paddr) + } + + fn _dealloc_intrm_table(&mut self, _paddr: PhysAddr) {} + + /// Get the mutable entry for the given virtual address. If the entry is not present, create + fn get_entry_mut_or_create(&mut self, page: Page) -> PagingResult<&mut PTE> { + let vaddr: usize = page.vaddr.into(); + let p4 = table_of_mut::(self.inner.root_paddr()); + let p4e = &mut p4[p4_index(vaddr)]; + + trace!( + "loongarch64: get_entry_mut_or_create: vaddr={:#x}, p4e={:#x?}", + vaddr, + p4e + ); + + let p3 = next_table_mut_or_create(p4e, || self.alloc_intrm_table())?; + let p3e = &mut p3[p3_index(vaddr)]; + if page.size == PageSize::Size1G { + return Ok(p3e); + } + + trace!( + "loongarch64: get_entry_mut_or_create: p3e={:#x?}, page.size={:#x?}", + p3e, + page.size + ); + + let p2 = next_table_mut_or_create(p3e, || self.alloc_intrm_table())?; + let p2e = &mut p2[p2_index(vaddr)]; + if page.size == PageSize::Size2M { + return Ok(p2e); + } + + trace!( + "loongarch64: get_entry_mut_or_create: p2e={:#x?}, page.size={:#x?}", + p2e, + page.size + ); + + let p1 = next_table_mut_or_create(p2e, || self.alloc_intrm_table())?; + let p1e = &mut p1[p1_index(vaddr)]; + + trace!( + "loongarch64: get_entry_mut_or_create: p1e={:#x?}, page.size={:#x?}", + p1e, + page.size + ); + + Ok(p1e) + } + + fn map_page( + &mut self, + page: Page, + paddr: PhysAddr, + flags: MemFlags, + ) -> PagingResult<&mut PTE> { + trace!( + "loongarch64: map_page: vaddr={:#x}, size={:?}, paddr={:#x}, flags={:?}", + page.vaddr.into(), + page.size, + paddr, + flags + ); + let entry: &mut PTE = self.get_entry_mut_or_create(page)?; + if !entry.is_unused() { + return Err(PagingError::AlreadyMapped); + } + trace!("loongarch64: map_page: entry is unused, continue"); + + entry.set_addr(page.size.align_down(paddr)); + entry.set_flags(flags, page.size.is_huge()); + + trace!( + "loongarch64: map_page: set addr and flags done, entry.addr={:#x?}, entry.flags={:?}", + entry.addr(), + entry.flags() + ); + Ok(entry) + } + + fn unmap_page(&mut self, vaddr: VA) -> PagingResult<(PhysAddr, PageSize)> { + let (entry, size) = self.inner.get_entry_mut(vaddr)?; + if entry.is_unused() { + return Err(PagingError::NotMapped); + } + let paddr = entry.addr(); + entry.clear(); + Ok((paddr, size)) + } + + fn update(&mut self, vaddr: VA, paddr: PhysAddr, flags: MemFlags) -> PagingResult { + let (entry, size) = self.inner.get_entry_mut(vaddr)?; + entry.set_addr(paddr); + entry.set_flags(flags, size.is_huge()); + Ok(size) + } +} + +/// A extended level-4 page table implements `GenericPageTable`. It use locks to avoid data +/// racing between it and its clonees. +pub struct Level4PageTable { + inner: Level4PageTableUnlocked, + /// Make sure all accesses to the page table and its clonees is exclusive. + clonee_lock: Arc>, +} + +impl Level4PageTable +where + VA: From + Into + Copy, + PTE: GenericPTE, + I: PagingInstr, +{ + #[allow(dead_code)] + pub fn dump(&self, limit: usize) { + self.inner.inner.dump(limit) + } + + /// Clone only the top level page table mapping from `src`. + pub fn clone_from(src: &impl GenericPageTableImmut) -> Self { + // XXX: The clonee won't track intermediate tables, must ensure it lives shorter than the + // original page table. + let pt = Self::new(); + let dst_p4_table = + unsafe { slice::from_raw_parts_mut(pt.root_paddr() as *mut PTE, ENTRY_COUNT) }; + let src_p4_table = + unsafe { slice::from_raw_parts(src.root_paddr() as *const PTE, ENTRY_COUNT) }; + dst_p4_table.clone_from_slice(src_p4_table); + pt + } +} + +impl GenericPageTableImmut for Level4PageTable +where + VA: From + Into + Copy, + PTE: GenericPTE, + I: PagingInstr, +{ + type VA = VA; + + unsafe fn from_root(root_paddr: PhysAddr) -> Self { + Self { + inner: Level4PageTableUnlocked::from_root(root_paddr), + clonee_lock: Arc::new(Mutex::new(())), + } + } + + fn root_paddr(&self) -> PhysAddr { + self.inner.inner.root_paddr() + } + + fn query(&self, vaddr: VA) -> PagingResult<(PhysAddr, MemFlags, PageSize)> { + let _lock = self.clonee_lock.lock(); + self.inner.inner.query(vaddr) + } +} + +impl GenericPageTable for Level4PageTable +where + VA: From + Into + Copy, + PTE: GenericPTE, + I: PagingInstr, +{ + fn new() -> Self { + Self { + inner: Level4PageTableUnlocked::new(), + clonee_lock: Arc::new(Mutex::new(())), + } + } + + fn map(&mut self, region: &MemoryRegion) -> HvResult { + assert!( + is_aligned(region.start.into()), + "region.start = {:#x?}", + region.start.into() + ); + assert!(is_aligned(region.size), "region.size = {:#x?}", region.size); + trace!( + "create mapping in {}: {:#x?}", + core::any::type_name::(), + region + ); + let _lock = self.clonee_lock.lock(); + let mut vaddr = region.start.into(); + let mut size = region.size; + while size > 0 { + let paddr = region.mapper.map_fn(vaddr); + let page_size = PageSize::Size4K; // now let's support STLB only + trace!( + "loongarch64: mapping page: {:#x?}({:?}) -> {:#x?}, {:?}", + vaddr, + page_size, + paddr, + region.flags + ); + let page = Page::new_aligned(vaddr.into(), page_size); + self.inner + .map_page(page, paddr, region.flags) + .map_err(|e: PagingError| { + error!( + "failed to map page: {:#x?}({:?}) -> {:#x?}, {:?}", + vaddr, page_size, paddr, e + ); + e + })?; + vaddr += page_size as usize; + size -= page_size as usize; + } + Ok(()) + } + + fn unmap(&mut self, region: &MemoryRegion) -> HvResult { + trace!( + "destroy mapping in {}: {:#x?}", + core::any::type_name::(), + region + ); + let _lock = self.clonee_lock.lock(); + let mut vaddr = region.start.into(); + let mut size = region.size; + while size > 0 { + let (_, page_size) = self.inner.unmap_page(vaddr.into()).map_err(|e| { + error!("failed to unmap page: {:#x?}, {:?}", vaddr, e); + e + })?; + if !page_size.is_aligned(vaddr) { + error!("error vaddr={:#x?}", vaddr); + loop {} + } + assert!(page_size.is_aligned(vaddr)); + assert!(page_size as usize <= size); + vaddr += page_size as usize; + size -= page_size as usize; + } + Ok(()) + } + + fn update(&mut self, vaddr: VA, paddr: PhysAddr, flags: MemFlags) -> PagingResult { + let _lock = self.clonee_lock.lock(); + self.inner.update(vaddr, paddr, flags) + } + + fn clone(&self) -> Self { + let mut pt = Self::clone_from(self); + // clone with lock to avoid data racing between it and its clonees. + pt.clonee_lock = self.clonee_lock.clone(); + pt + } + + unsafe fn activate(&self) { + I::activate(self.root_paddr()) + } + + fn flush(&self, vaddr: Option) { + I::flush(vaddr.map(Into::into)) + } +} + +const fn p4_index(vaddr: usize) -> usize { + (vaddr >> (12 + 27)) & (ENTRY_COUNT - 1) +} + +const fn p3_index(vaddr: usize) -> usize { + (vaddr >> (12 + 18)) & (ENTRY_COUNT - 1) +} + +const fn p2_index(vaddr: usize) -> usize { + (vaddr >> (12 + 9)) & (ENTRY_COUNT - 1) +} + +const fn p1_index(vaddr: usize) -> usize { + (vaddr >> 12) & (ENTRY_COUNT - 1) +} + +fn table_of<'a, E>(paddr: PhysAddr) -> &'a [E] { + let ptr = paddr as *const E; + unsafe { slice::from_raw_parts(ptr, ENTRY_COUNT) } +} + +fn table_of_mut<'a, E>(paddr: PhysAddr) -> &'a mut [E] { + let ptr = paddr as *mut E; + unsafe { slice::from_raw_parts_mut(ptr, ENTRY_COUNT) } +} + +fn next_table_mut<'a, E: GenericPTE>(entry: &E) -> PagingResult<&'a mut [E]> { + // if !entry.is_present() { + // Err(PagingError::NotMapped) + // } else if entry.is_huge() { + // Err(PagingError::MappedToHugePage) + // } else { + // Ok(table_of_mut(entry.addr())) + // } + let next_pt_addr = entry.addr() | crate::arch::mm::LOONGARCH64_CACHED_DMW_PREFIX as usize; + trace!( + "loongarch64: next_table_mut: next_pt_addr={:#x?}", + next_pt_addr + ); + Ok(table_of_mut(next_pt_addr)) +} + +/// Get the next level table for the given entry. +/// If the entry is not present, create a new table. +fn next_table_mut_or_create<'a, E: GenericPTE>( + entry: &mut E, + mut allocator: impl FnMut() -> HvResult, +) -> PagingResult<&'a mut [E]> { + if entry.is_unused() { + let paddr = allocator().map_err(|_| PagingError::NoMemory)?; + entry.set_table(paddr); + Ok(table_of_mut(paddr)) + } else { + next_table_mut(entry) + } +} + +/// set pagetable format in loongarch64 as 4-level pagetable +pub fn set_pwcl_pwch() { + set_dir3_base(12 + 9 + 9 + 9); + set_dir3_width(9); + set_dir2_base(12 + 9 + 9); + set_dir2_width(9); + set_dir1_base(12 + 9); + set_dir1_width(9); + set_ptbase(12); + set_ptwidth(9); + set_pte_width(8); // 64 bits -> 8 bytes +} diff --git a/src/arch/loongarch64/register/gcfg.rs b/src/arch/loongarch64/register/gcfg.rs new file mode 100644 index 00000000..dfc17bf1 --- /dev/null +++ b/src/arch/loongarch64/register/gcfg.rs @@ -0,0 +1,87 @@ +use bit_field::BitField; + +impl_define_csr!(Gcfg, "GCFG"); +impl_read_csr!(0x51, Gcfg); + +impl Gcfg { + pub fn matp(&self) -> usize { + self.bits.get_bits(0..=3) + } + pub fn matc(&self) -> usize { + self.bits.get_bits(4..=5) + } + pub fn topip(&self) -> bool { + self.bits.get_bit(6) + } + pub fn topi(&self) -> bool { + self.bits.get_bit(7) + } + pub fn totip(&self) -> bool { + self.bits.get_bit(8) + } + pub fn toti(&self) -> bool { + self.bits.get_bit(9) + } + pub fn toep(&self) -> bool { + self.bits.get_bit(10) + } + pub fn toe(&self) -> bool { + self.bits.get_bit(11) + } + pub fn topp(&self) -> bool { + self.bits.get_bit(12) + } + pub fn top(&self) -> bool { + self.bits.get_bit(13) + } + pub fn tohup(&self) -> bool { + self.bits.get_bit(14) + } + pub fn tohu(&self) -> bool { + self.bits.get_bit(15) + } + pub fn tocip(&self) -> usize { + self.bits.get_bits(16..=19) + } + pub fn toci(&self) -> usize { + self.bits.get_bits(20..=21) + } + pub fn gpmp(&self) -> bool { + self.bits.get_bit(23) + } + pub fn gpm_num(&self) -> usize { + self.bits.get_bits(24..=26) + } +} + +pub fn set_matc(matc: usize) { + set_csr_loong_bits!(0x51, 4..=5, matc); +} + +pub fn set_topi(topi: bool) { + set_csr_loong_bit!(0x51, 7, topi); +} + +pub fn set_toti(toti: bool) { + set_csr_loong_bit!(0x51, 9, toti); +} + +pub fn set_toe(toe: bool) { + set_csr_loong_bit!(0x51, 11, toe); +} + +pub fn set_top(top: bool) { + set_csr_loong_bit!(0x51, 13, top); +} + +pub fn set_tohu(tohu: bool) { + set_csr_loong_bit!(0x51, 15, tohu); +} + +pub fn set_toci(toci: usize) { + set_csr_loong_bits!(0x51, 20..=21, toci); +} + +pub fn set_gpm_num(gpm_num: usize) { + set_csr_loong_bits!(0x51, 24..=26, gpm_num); +} diff --git a/src/arch/loongarch64/register/gcntc.rs b/src/arch/loongarch64/register/gcntc.rs new file mode 100644 index 00000000..a20b380f --- /dev/null +++ b/src/arch/loongarch64/register/gcntc.rs @@ -0,0 +1,16 @@ +use bit_field::BitField; + +impl_define_csr!(Gcntc, "GCNTC"); +impl_read_csr!(0x53, Gcntc); + +const GRLEN: usize = 64; // not sure what is GRLEN, set to 64 for now... + +impl Gcntc { + pub fn compensation(&self) -> usize { + self.bits.get_bits(0..=GRLEN - 1) + } +} + +pub fn set_compensation(value: usize) { + set_csr_loong_bits!(0x53, 0..=GRLEN - 1, value); +} diff --git a/src/arch/loongarch64/register/gintc.rs b/src/arch/loongarch64/register/gintc.rs new file mode 100644 index 00000000..be386dab --- /dev/null +++ b/src/arch/loongarch64/register/gintc.rs @@ -0,0 +1,28 @@ +use bit_field::BitField; + +impl_define_csr!(Gintc, "GINTC"); +impl_read_csr!(0x52, Gintc); + +impl Gintc { + pub fn hwis(&self) -> usize { + self.bits.get_bits(0..=7) + } + pub fn hwip(&self) -> usize { + self.bits.get_bits(8..=15) + } + pub fn hwic(&self) -> usize { + self.bits.get_bits(16..=23) + } +} + +pub fn set_hwis(hwis: usize) { + set_csr_loong_bits!(0x52, 0..=7, hwis); +} + +pub fn set_hwip(hwip: usize) { + set_csr_loong_bits!(0x52, 8..=15, hwip); +} + +pub fn set_hwic(hwic: usize) { + set_csr_loong_bits!(0x52, 16..=23, hwic); +} diff --git a/src/arch/loongarch64/register/gstat.rs b/src/arch/loongarch64/register/gstat.rs new file mode 100644 index 00000000..ad398800 --- /dev/null +++ b/src/arch/loongarch64/register/gstat.rs @@ -0,0 +1,24 @@ +use bit_field::BitField; + +impl_define_csr!(Gstat, "GSTAT"); +impl_read_csr!(0x50, Gstat); + +impl Gstat { + pub fn pgm(&self) -> bool { + self.bits.get_bit(1) + } + pub fn gidbits(&self) -> usize { + self.bits.get_bits(4..=9) + } + pub fn gid(&self) -> usize { + self.bits.get_bits(16..=23) + } +} + +pub fn set_gid(gid: usize) { + set_csr_loong_bits!(0x50, 16..=23, gid); +} + +pub fn set_pgm(pgm: bool) { + set_csr_loong_bit!(0x50, 1, pgm); +} diff --git a/src/arch/loongarch64/register/gtlbc.rs b/src/arch/loongarch64/register/gtlbc.rs new file mode 100644 index 00000000..7f3414be --- /dev/null +++ b/src/arch/loongarch64/register/gtlbc.rs @@ -0,0 +1,35 @@ +use bit_field::BitField; + +impl_define_csr!(Gtlbc, "GTLBC"); +impl_read_csr!(0x15, Gtlbc); + +impl Gtlbc { + pub fn gmtlb_num(&self) -> usize { + self.bits.get_bits(0..=5) + } + pub fn use_tgid(&self) -> bool { + self.bits.get_bit(12) + } + pub fn totlbinv(&self) -> bool { + self.bits.get_bit(13) + } + pub fn tgid(&self) -> usize { + self.bits.get_bits(16..=23) + } +} + +pub fn set_gmtlb_num(gmtlb_num: usize) { + set_csr_loong_bits!(0x15, 0..=5, gmtlb_num); +} + +pub fn set_use_tgid(use_tgid: bool) { + set_csr_loong_bit!(0x15, 12, use_tgid); +} + +pub fn set_totlbinv(totlbinv: bool) { + set_csr_loong_bit!(0x15, 13, totlbinv); +} + +pub fn set_tgid(tgid: usize) { + set_csr_loong_bits!(0x15, 16..=23, tgid); +} diff --git a/src/arch/loongarch64/register/macros.rs b/src/arch/loongarch64/register/macros.rs new file mode 100644 index 00000000..62aea6f2 --- /dev/null +++ b/src/arch/loongarch64/register/macros.rs @@ -0,0 +1,238 @@ +/* + this file is forked from extern crate loongArch64::register::macros; + wheatfox +*/ +#![allow(unused)] +macro_rules! impl_tlbelo { + ($ident:ident,$number:expr) => { + impl $ident { + fn valid(&self) -> bool { + self.bits.get_bit(0) + } + fn dirty(&self) -> bool { + self.bits.get_bit(1) + } + fn plv(&self) -> usize { + self.bits.get_bits(2..=3) + } + fn mat(&self) -> MemoryAccessType { + self.bits.get_bits(4..=5).into() + } + fn global(&self) -> bool { + self.bits.get_bit(6) + } + fn ppn(&self) -> usize { + self.bits.get_bits(12..PALEN) + } + fn not_readable(&self) -> bool { + self.bits.get_bit(61) + } + fn not_executable(&self) -> bool { + self.bits.get_bit(62) + } + fn rplv(&self) -> bool { + self.bits.get_bit(63) + } + } + pub fn set_valid(valid: bool) { + set_csr_loong_bit!($number, 0, valid); + } + pub fn set_dirty(dirty: bool) { + set_csr_loong_bit!($number, 1, dirty); + } + pub fn set_plv(plv: usize) { + set_csr_loong_bits!($number, 2..=3, plv); + } + pub fn set_mat(mem_access_type: MemoryAccessType) { + set_csr_loong_bits!($number, 4..=5, mem_access_type as usize); + } + pub fn set_global(global_flag: bool) { + set_csr_loong_bit!($number, 6, global_flag); + } + pub fn set_ppn(ppn: usize) { + set_csr_loong_bits!($number, 14..PALEN, ppn); + } + pub fn set_not_readable(not_readable: bool) { + set_csr_loong_bit!($number, 61, not_readable); + } + pub fn set_not_executable(not_executable: bool) { + set_csr_loong_bit!($number, 62, not_executable); + } + pub fn set_rplv(rplv: bool) { + set_csr_loong_bit!($number, 63, rplv); + } + }; +} +macro_rules! impl_dwm { + ($ident:ident,$number:expr) => { + impl $ident { + fn plv0(&self) -> bool { + self.bits.get_bit(0) + } + fn plv1(&self) -> bool { + self.bits.get_bit(1) + } + fn plv2(&self) -> bool { + self.bits.get_bit(2) + } + fn plv3(&self) -> bool { + self.bits.get_bit(3) + } + fn mat(&self) -> MemoryAccessType { + self.bits.get_bits(4..=5).into() + } + fn vseg(&self) -> usize { + self.bits.get_bits(60..=63) + } + } + pub fn set_plv0(plv0: bool) { + set_csr_loong_bit!($number, 0, plv0); + } + pub fn set_plv1(plv1: bool) { + set_csr_loong_bit!($number, 1, plv1); + } + pub fn set_plv2(plv2: bool) { + set_csr_loong_bit!($number, 2, plv2); + } + pub fn set_plv3(plv3: bool) { + set_csr_loong_bit!($number, 3, plv3); + } + pub fn set_mat(mat: MemoryAccessType) { + set_csr_loong_bits!($number, 4..=5, mat as usize); + } + pub fn set_vseg(vseg: usize) { + set_csr_loong_bits!($number, 60..=63, vseg); + } + impl Debug for $ident { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("DMW0") + .field("MAT", &self.mat()) + .field("vseg", &self.vseg()) + .field("plv0", &self.plv0()) + .field("plv1", &self.plv1()) + .field("plv2", &self.plv2()) + .field("plv3", &self.plv3()) + .finish() + } + } + }; +} + +macro_rules! impl_read_csr { + ($csr_number:literal,$csr_ident:ident) => { + #[inline(always)] + pub fn read() -> $csr_ident { + $csr_ident { + bits: unsafe { + let bits:usize; + core::arch::asm!("csrrd {},{}", out(reg) bits, const $csr_number); + bits + }, + } + } + }; +} +macro_rules! impl_define_csr { + ($csr_ident:ident,$doc:expr) => { + #[doc = $doc] + #[derive(Copy, Clone)] + pub struct $csr_ident { + bits: usize, + } + }; +} + +// csr +macro_rules! read_csr_loong { + ($csr_number:literal) => { + unsafe{ + let bits:usize; + core::arch::asm!("csrrd {},{}", out(reg) bits, const $csr_number); + bits + } + }; +} +macro_rules! write_csr_loong { + ($csr_number:literal,$value:expr) => { + unsafe {core::arch::asm!("csrwr {},{}", in(reg) $value, const $csr_number);} + }; +} + +macro_rules! set_csr_loong_bits { + ($csr_number:literal,$range:expr,$value:expr) => { + let mut tmp = read_csr_loong!($csr_number); + tmp.set_bits($range, $value); + write_csr_loong!($csr_number, tmp); + }; +} + +macro_rules! set_csr_loong_bit { + ($csr_number:literal,$range:expr,$value:expr) => { + let mut tmp = read_csr_loong!($csr_number); + tmp.set_bit($range, $value); + write_csr_loong!($csr_number, tmp); + }; +} + +// gcsr + +macro_rules! impl_read_gcsr { + ($csr_number:literal,$csr_ident:ident) => { + #[inline(always)] + pub fn read() -> $csr_ident { + $csr_ident { + bits: unsafe { + let bits:usize; + core::arch::asm!("gcsrrd {},{}", out(reg) bits, const $csr_number); + bits + }, + } + } + }; +} + +macro_rules! impl_define_gcsr { + ($csr_ident:ident,$doc:expr) => { + #[doc = $doc] + #[derive(Copy, Clone)] + pub struct $csr_ident { + bits: usize, + } + }; +} + +#[macro_export] +macro_rules! read_gcsr_loong { + ($csr_number:literal) => { + unsafe{ + let bits:usize; + core::arch::asm!("gcsrrd {},{}", out(reg) bits, const $csr_number); + bits + } + }; +} + +#[macro_export] +macro_rules! write_gcsr_loong { + ($csr_number:literal,$value:expr) => { + unsafe {core::arch::asm!("gcsrwr {},{}", in(reg) $value, const $csr_number);} + }; +} + +#[macro_export] +macro_rules! set_gcsr_loong_bits { + ($csr_number:literal,$range:expr,$value:expr) => { + let mut tmp = read_gcsr_loong!($csr_number); + tmp.set_bits($range, $value); + write_gcsr_loong!($csr_number, tmp); + }; +} + +#[macro_export] +macro_rules! set_gcsr_loong_bit { + ($csr_number:literal,$range:expr,$value:expr) => { + let mut tmp = read_gcsr_loong!($csr_number); + tmp.set_bit($range, $value); + write_gcsr_loong!($csr_number, tmp); + }; +} diff --git a/src/arch/loongarch64/register/merrctl.rs b/src/arch/loongarch64/register/merrctl.rs new file mode 100644 index 00000000..3425368f --- /dev/null +++ b/src/arch/loongarch64/register/merrctl.rs @@ -0,0 +1,93 @@ +use bit_field::BitField; +impl_define_csr!(MerrCtl, "Machine Error Controller\n\ + Since the timing of machine error exceptions cannot be predicted and controlled by the software,\n\ + a separate set of CSRs is defined for machine error exceptions to preserve other registers when machine error exceptions are triggered,\n\ + which is used by the system software to save and restore other sites.\n\ + This set of independent CSRs except MERRERA and MERRSAVE, the rest are concentrated in MERRCTL register."); + +impl_read_csr!(0x90, MerrCtl); + +impl MerrCtl { + /// Returns whether the machine error exception is triggered. + pub fn is_merr(&self) -> bool { + self.bits.get_bit(0) + } + /// Returns whether the machine error exception is repairable. + /// + /// If the machine error exception is repairable, the system software can ignore the error and continue to execute the program; + pub fn repairable(&self) -> bool { + self.bits.get_bit(1) + } + pub fn pplv(&self) -> usize { + self.bits.get_bits(2..=3) + } + + pub fn pie(&self) -> bool { + self.bits.get_bit(4) + } + pub fn pwe(&self) -> bool { + self.bits.get_bit(6) + } + + pub fn pda(&self) -> bool { + self.bits.get_bit(7) + } + + pub fn ppg(&self) -> bool { + self.bits.get_bit(8) + } + + pub fn pdatf(&self) -> usize { + self.bits.get_bits(9..=10) + } + + pub fn pdatm(&self) -> usize { + self.bits.get_bits(11..=12) + } + + pub fn cause(&self) -> MachineError { + self.bits.get_bits(13..=15).into() + } +} +#[derive(Copy, Clone, Debug)] +#[repr(usize)] +pub enum MachineError { + CacheCheckError, +} + +impl From for MachineError { + fn from(code: usize) -> Self { + match code { + 0 => MachineError::CacheCheckError, + _ => panic!("Unknown MachineError code: {}", code), + } + } +} + +pub fn set_pplv(pplv: usize) { + set_csr_loong_bits!(0x90, 2..=3, pplv); +} + +pub fn set_pie(pie: bool) { + set_csr_loong_bit!(0x90, 4, pie); +} + +pub fn set_pwe(pwe: bool) { + set_csr_loong_bit!(0x90, 6, pwe); +} + +pub fn set_pda(pda: bool) { + set_csr_loong_bit!(0x90, 7, pda); +} + +pub fn set_ppg(ppg: bool) { + set_csr_loong_bit!(0x90, 8, ppg); +} + +pub fn set_pdatf(pdatf: usize) { + set_csr_loong_bits!(0x90, 9..=10, pdatf); +} + +pub fn set_pdatm(pdatm: usize) { + set_csr_loong_bits!(0x90, 11..=12, pdatm); +} diff --git a/src/arch/loongarch64/register/merrentry.rs b/src/arch/loongarch64/register/merrentry.rs new file mode 100644 index 00000000..cecf65e7 --- /dev/null +++ b/src/arch/loongarch64/register/merrentry.rs @@ -0,0 +1,17 @@ +impl_define_csr!(MerrEntry,"Machine Error Exception Entry Base Address (MERRENTRY)\n\ + This register is used to configure the entry base address of the machine error exception.\n\ + Since the processor core enters the direct address translation mode once the machine error exception is triggered,\n\ + the entry base address filled here should be the physical address."); + +impl_read_csr!(0x93, MerrEntry); + +impl MerrEntry { + pub fn addr(&self) -> usize { + self.bits + } +} + +pub fn set_merrentry(addr: usize) { + debug_assert_eq!(addr & 0xFFF, 0); + write_csr_loong!(0x93, addr); +} diff --git a/src/arch/loongarch64/register/merrera.rs b/src/arch/loongarch64/register/merrera.rs new file mode 100644 index 00000000..47b91775 --- /dev/null +++ b/src/arch/loongarch64/register/merrera.rs @@ -0,0 +1,13 @@ +impl_define_csr!(MerrEra,"Machine Error Exception Data Save Register\n\ + This register is used to record the PC of the instruction that triggered the machine error exception."); +impl_read_csr!(0x94, MerrEra); + +impl MerrEra { + pub fn pc(&self) -> usize { + self.bits + } +} + +pub fn set_pc(pc: usize) { + write_csr_loong!(0x94, pc); +} diff --git a/src/arch/loongarch64/register/merrsave.rs b/src/arch/loongarch64/register/merrsave.rs new file mode 100644 index 00000000..2afd6091 --- /dev/null +++ b/src/arch/loongarch64/register/merrsave.rs @@ -0,0 +1,19 @@ +impl_define_csr!(MerrSave, "Machine Error Exception Data Save Register\n\ + This register is used to store data temporarily for the system software.\n\ + Each dava save register can hold the data of one general-purpose register.\n\ + Two major causes contribute to the motivation of an extra SAVE register for machine error handler.\n\ + One is the unpredictability of the machine error exception for software,\n\ + and its potential of interrupting in the processing of any other exception handling.\n\ + You can simply consider this as yet another scratch register."); + +impl_read_csr!(0x95, MerrSave); + +impl MerrSave { + pub fn data(&self) -> usize { + self.bits + } +} + +pub fn set_data(value: usize) { + write_csr_loong!(0x95, value); +} diff --git a/src/arch/loongarch64/register/mod.rs b/src/arch/loongarch64/register/mod.rs new file mode 100644 index 00000000..6caf26e8 --- /dev/null +++ b/src/arch/loongarch64/register/mod.rs @@ -0,0 +1,289 @@ +// File: mod.rs +// Description: this is the register file of loongarch64's LVZ extension +// Authors: wheatfox(wheatfox17@icloud.com) +// Created: 2023-12-20 + +#![allow(unused)] + +use bit_field::BitField; +use loongArch64::register::{tcfg, tval}; + +#[macro_use] +mod macros; + +// LVZ registers +pub mod gcfg; +pub mod gcntc; +pub mod gintc; +pub mod gstat; +pub mod gtlbc; +pub mod trgp; + +// ras +pub mod merrctl; +pub mod merrentry; +pub mod merrera; +pub mod merrsave; + +// offset of all GCSR available registers +pub const GCSR_CRMD: usize = 0x0; +pub const GCSR_PRMD: usize = 0x1; +pub const GCSR_EUEN: usize = 0x2; +pub const GCSR_MISC: usize = 0x3; +pub const GCSR_ECTL: usize = 0x4; +pub const GCSR_ESTAT: usize = 0x5; +pub const GCSR_ERA: usize = 0x6; +pub const GCSR_BADV: usize = 0x7; +pub const GCSR_BADI: usize = 0x8; +pub const GCSR_EENTRY: usize = 0xc; +pub const GCSR_TLBIDX: usize = 0x10; +pub const GCSR_TLBEHI: usize = 0x11; +pub const GCSR_TLBELO0: usize = 0x12; +pub const GCSR_TLBELO1: usize = 0x13; +pub const GCSR_ASID: usize = 0x18; +pub const GCSR_PGDL: usize = 0x19; +pub const GCSR_PGDH: usize = 0x1a; +pub const GCSR_PGD: usize = 0x1b; +pub const GCSR_PWCL: usize = 0x1c; +pub const GCSR_PWCH: usize = 0x1d; +pub const GCSR_STLBPS: usize = 0x1e; +pub const GCSR_RAVCFG: usize = 0x1f; +pub const GCSR_CPUID: usize = 0x20; +pub const GCSR_PRCFG1: usize = 0x21; +pub const GCSR_PRCFG2: usize = 0x22; +pub const GCSR_PRCFG3: usize = 0x23; +pub const GCSR_SAVE0: usize = 0x30; +pub const GCSR_SAVE1: usize = 0x31; +pub const GCSR_SAVE2: usize = 0x32; +pub const GCSR_SAVE3: usize = 0x33; +pub const GCSR_SAVE4: usize = 0x34; +pub const GCSR_SAVE5: usize = 0x35; +pub const GCSR_SAVE6: usize = 0x36; +pub const GCSR_SAVE7: usize = 0x37; +pub const GCSR_SAVE8: usize = 0x38; +pub const GCSR_SAVE9: usize = 0x39; +pub const GCSR_SAVE10: usize = 0x3a; +pub const GCSR_SAVE11: usize = 0x3b; +pub const GCSR_SAVE12: usize = 0x3c; +pub const GCSR_SAVE13: usize = 0x3d; +pub const GCSR_SAVE14: usize = 0x3e; +pub const GCSR_SAVE15: usize = 0x3f; +pub const GCSR_TID: usize = 0x40; +pub const GCSR_TCFG: usize = 0x41; +pub const GCSR_TVAL: usize = 0x42; +pub const GCSR_CNTC: usize = 0x43; +pub const GCSR_TICLR: usize = 0x44; +pub const GCSR_LLBCTL: usize = 0x60; +pub const GCSR_TLBRENTRY: usize = 0x88; +pub const GCSR_TLBRBADV: usize = 0x89; +pub const GCSR_TLBRERA: usize = 0x8a; +pub const GCSR_TLBRSAVE: usize = 0x8b; +pub const GCSR_TLBRELO0: usize = 0x8c; +pub const GCSR_TLBRELO1: usize = 0x8d; +pub const GCSR_TLBREHI: usize = 0x8e; +pub const GCSR_TLBRPRMD: usize = 0x8f; +pub const GCSR_DMW0: usize = 0x180; +pub const GCSR_DMW1: usize = 0x181; +pub const GCSR_DMW2: usize = 0x182; +pub const GCSR_DMW3: usize = 0x183; +// and some more, which are performance monitoring related + +// READ GCSR +pub fn read_gcsr_crmd() -> usize { + read_gcsr_loong!(0x0) +} +pub fn read_gcsr_prmd() -> usize { + read_gcsr_loong!(0x1) +} +pub fn read_gcsr_euen() -> usize { + read_gcsr_loong!(0x2) +} +pub fn read_gcsr_misc() -> usize { + read_gcsr_loong!(0x3) +} +pub fn read_gcsr_ectl() -> usize { + read_gcsr_loong!(0x4) +} +pub fn read_gcsr_estat() -> usize { + read_gcsr_loong!(0x5) +} +pub fn write_gcsr_estat(value: usize) { + write_gcsr_loong!(0x5, value); +} +pub fn read_gcsr_era() -> usize { + read_gcsr_loong!(0x6) +} +pub fn read_gcsr_badv() -> usize { + read_gcsr_loong!(0x7) +} +pub fn read_gcsr_badi() -> usize { + read_gcsr_loong!(0x8) +} +pub fn read_gcsr_eentry() -> usize { + read_gcsr_loong!(0xc) +} +pub fn read_gcsr_tlbidx() -> usize { + read_gcsr_loong!(0x10) +} +pub fn read_gcsr_tlbehi() -> usize { + read_gcsr_loong!(0x11) +} +pub fn read_gcsr_tlbelo0() -> usize { + read_gcsr_loong!(0x12) +} +pub fn read_gcsr_tlbelo1() -> usize { + read_gcsr_loong!(0x13) +} +pub fn read_gcsr_asid() -> usize { + read_gcsr_loong!(0x18) +} +pub fn read_gcsr_pgdl() -> usize { + read_gcsr_loong!(0x19) +} +pub fn read_gcsr_pgdh() -> usize { + read_gcsr_loong!(0x1a) +} +pub fn read_gcsr_pgd() -> usize { + read_gcsr_loong!(0x1b) +} +pub fn read_gcsr_pwcl() -> usize { + read_gcsr_loong!(0x1c) +} +pub fn read_gcsr_pwch() -> usize { + read_gcsr_loong!(0x1d) +} +pub fn read_gcsr_stlbps() -> usize { + read_gcsr_loong!(0x1e) +} +pub fn read_gcsr_ravcfg() -> usize { + read_gcsr_loong!(0x1f) +} +pub fn read_gcsr_cpuid() -> usize { + read_gcsr_loong!(0x20) +} +pub fn read_gcsr_prcfg1() -> usize { + read_gcsr_loong!(0x21) +} +pub fn read_gcsr_prcfg2() -> usize { + read_gcsr_loong!(0x22) +} +pub fn read_gcsr_prcfg3() -> usize { + read_gcsr_loong!(0x23) +} +pub fn read_gcsr_save0() -> usize { + read_gcsr_loong!(0x30) +} +pub fn read_gcsr_save1() -> usize { + read_gcsr_loong!(0x31) +} +pub fn read_gcsr_save2() -> usize { + read_gcsr_loong!(0x32) +} +pub fn read_gcsr_save3() -> usize { + read_gcsr_loong!(0x33) +} +pub fn read_gcsr_save4() -> usize { + read_gcsr_loong!(0x34) +} +pub fn read_gcsr_save5() -> usize { + read_gcsr_loong!(0x35) +} +pub fn read_gcsr_save6() -> usize { + read_gcsr_loong!(0x36) +} +pub fn read_gcsr_save7() -> usize { + read_gcsr_loong!(0x37) +} +pub fn read_gcsr_save8() -> usize { + read_gcsr_loong!(0x38) +} +pub fn read_gcsr_save9() -> usize { + read_gcsr_loong!(0x39) +} +pub fn read_gcsr_save10() -> usize { + read_gcsr_loong!(0x3a) +} +pub fn read_gcsr_save11() -> usize { + read_gcsr_loong!(0x3b) +} +pub fn read_gcsr_save12() -> usize { + read_gcsr_loong!(0x3c) +} +pub fn read_gcsr_save13() -> usize { + read_gcsr_loong!(0x3d) +} +pub fn read_gcsr_save14() -> usize { + read_gcsr_loong!(0x3e) +} +pub fn read_gcsr_save15() -> usize { + read_gcsr_loong!(0x3f) +} +pub fn read_gcsr_tid() -> usize { + read_gcsr_loong!(0x40) +} +pub fn read_gcsr_tcfg() -> usize { + read_gcsr_loong!(0x41) +} +pub fn write_gcsr_tcfg(value: usize) { + write_gcsr_loong!(0x41, value); +} +pub fn read_gcsr_tval() -> usize { + read_gcsr_loong!(0x42) +} +pub fn write_gcsr_tval(value: usize) { + write_gcsr_loong!(0x42, value); +} +pub fn read_gcsr_cntc() -> usize { + read_gcsr_loong!(0x43) +} +pub fn read_gcsr_ticlr() -> usize { + read_gcsr_loong!(0x44) +} +pub fn write_gcsr_ticlr(value: usize) { + write_gcsr_loong!(0x44, value); +} +pub fn read_gcsr_llbctl() -> usize { + read_gcsr_loong!(0x60) +} +pub fn read_gcsr_tlbrentry() -> usize { + read_gcsr_loong!(0x88) +} +pub fn read_gcsr_tlbrbadv() -> usize { + read_gcsr_loong!(0x89) +} +pub fn read_gcsr_tlbrera() -> usize { + read_gcsr_loong!(0x8a) +} +pub fn read_gcsr_tlbrsave() -> usize { + read_gcsr_loong!(0x8b) +} +pub fn read_gcsr_tlbrrelo0() -> usize { + read_gcsr_loong!(0x8c) +} +pub fn read_gcsr_tlbrrelo1() -> usize { + read_gcsr_loong!(0x8d) +} +pub fn read_gcsr_tlbrrehi() -> usize { + read_gcsr_loong!(0x8e) +} +pub fn read_gcsr_tlbrprmd() -> usize { + read_gcsr_loong!(0x8f) +} +pub fn read_gcsr_dmw0() -> usize { + read_gcsr_loong!(0x180) +} +pub fn read_gcsr_dmw1() -> usize { + read_gcsr_loong!(0x181) +} +pub fn read_gcsr_dmw2() -> usize { + read_gcsr_loong!(0x182) +} +pub fn read_gcsr_dmw3() -> usize { + read_gcsr_loong!(0x183) +} +pub fn write_gcsr_ectl(range: core::ops::RangeInclusive, value: usize) { + set_gcsr_loong_bits!(0x4, range, value); +} +pub fn write_gcsr_eentry(range: core::ops::RangeInclusive, value: usize) { + set_gcsr_loong_bits!(0xc, range, value); +} diff --git a/src/arch/loongarch64/register/trgp.rs b/src/arch/loongarch64/register/trgp.rs new file mode 100644 index 00000000..8434e12f --- /dev/null +++ b/src/arch/loongarch64/register/trgp.rs @@ -0,0 +1,21 @@ +use bit_field::BitField; + +impl_define_csr!(Trgp, "TRGP"); +impl_read_csr!(0x16, Trgp); + +impl Trgp { + pub fn gtep(&self) -> bool { + self.bits.get_bit(0) + } + pub fn trgid(&self) -> usize { + self.bits.get_bits(16..=23) + } +} + +pub fn set_gtep(gtep: bool) { + set_csr_loong_bit!(0x16, 0, gtep); +} + +pub fn set_trgid(trgid: usize) { + set_csr_loong_bits!(0x16, 16..=23, trgid); +} diff --git a/src/arch/loongarch64/s1pt.rs b/src/arch/loongarch64/s1pt.rs new file mode 100644 index 00000000..3b04afa9 --- /dev/null +++ b/src/arch/loongarch64/s1pt.rs @@ -0,0 +1,140 @@ +use super::paging::PagingInstr; +use crate::arch::paging::GenericPTE; +use crate::arch::paging::Level4PageTable; +use crate::memory::GuestPhysAddr; +use crate::memory::HostPhysAddr; +use crate::memory::MemFlags; +use crate::memory::PhysAddr; +use loongArch64::register::MemoryAccessType; + +bitflags::bitflags! { + /// Memory attribute fields in the LoongArch64 translation table format descriptors. + #[derive(Clone, Copy, Debug)] + pub struct DescriptorAttr: usize { + const V = 1 << 0; // Valid + const D = 1 << 1; // Dirty + const PLV = 0b11 << 2; // Privilege Level + const MAT = 0b11 << 4; // Memory Access Type + const G = 1 << 6; // Global + const P = 1 << 7; // Present + const W = 1 << 8; // Writable + const NR = 1 << 61; // Not Readable + const NX = 1 << 62; // Not Executable + const RPLV = 1 << 63; // Relative Privilege Level Check + } +} + +impl From for MemFlags { + fn from(attr: DescriptorAttr) -> Self { + let mut flags = Self::empty(); + if attr.contains(DescriptorAttr::W) { + flags |= Self::WRITE; + } + if !attr.contains(DescriptorAttr::NR) { + flags |= Self::READ; + } + if !attr.contains(DescriptorAttr::NX) { + flags |= Self::EXECUTE; + } + flags + } +} + +impl From for DescriptorAttr { + fn from(flags: MemFlags) -> Self { + let mut attr = Self::empty(); + if flags.contains(MemFlags::WRITE) { + attr |= Self::W; + } + if !flags.contains(MemFlags::READ) { + attr |= Self::NR; + } + if !flags.contains(MemFlags::EXECUTE) { + attr |= Self::NX; + } + attr + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct PageTableEntry(usize); + +// 12-47 +const PTE_PPN_MASK: usize = 0x0000_ffff_ffff_f000; + +impl PageTableEntry { + pub const fn empty() -> Self { + Self(0) + } +} + +impl GenericPTE for PageTableEntry { + fn addr(&self) -> HostPhysAddr { + PhysAddr::from(self.0 as usize & PTE_PPN_MASK as usize) + } + fn flags(&self) -> MemFlags { + DescriptorAttr::from_bits_truncate(self.0).into() + } + fn is_unused(&self) -> bool { + self.0 == 0 + } + fn is_present(&self) -> bool { + // check the P bit + self.0 & DescriptorAttr::P.bits() != 0 + } + fn set_addr(&mut self, addr: HostPhysAddr) { + // set the PPN range to the new address + self.0 = (self.0 & !PTE_PPN_MASK) | addr; + } + fn set_flags(&mut self, flags: MemFlags, is_huge: bool) { + self.0 = DescriptorAttr::from(flags).bits(); + } + fn set_table(&mut self, pa: HostPhysAddr) { + self.set_addr(pa); + self.set_flags_and_mat( + DescriptorAttr::V | DescriptorAttr::W, + MemoryAccessType::CoherentCached, + ); + } + fn clear(&mut self) { + self.0 = 0; + } + fn is_huge(&self) -> bool { + false + } +} + +impl PageTableEntry { + pub fn set_flags_and_mat(&mut self, flags: DescriptorAttr, mat: MemoryAccessType) { + self.0 = (self.0 & !DescriptorAttr::MAT.bits()) | flags.bits() | mat as usize; + } +} + +pub struct S1PTInstr; + +impl PagingInstr for S1PTInstr { + unsafe fn activate(root_pa: HostPhysAddr) { + info!("loongarch64: S1PTInstr::activate: root_pa: {:#x?}", root_pa); + extern "C" { + fn tlb_refill_handler(); + } + super::paging::set_pwcl_pwch(); + use loongArch64::register::tlbrentry; + use loongArch64::register::{pgd, pgdh, pgdl}; + pgdh::set_base(root_pa); + pgdl::set_base(root_pa); + unsafe { + tlbrentry::set_tlbrentry(tlb_refill_handler as usize); + } + info!( + "loongarch64: S1PTInstr::activate: set tlbrentry to {:#x?} done!", + tlbrentry::read().addr() + ); + } + fn flush(vaddr: Option) { + warn!("loongarch64: S1PTInstr::flush: vaddr: {:#x?}", vaddr); + } +} + +pub type Stage1PageTable = Level4PageTable; diff --git a/src/arch/loongarch64/s2pt.rs b/src/arch/loongarch64/s2pt.rs new file mode 100644 index 00000000..d79b078e --- /dev/null +++ b/src/arch/loongarch64/s2pt.rs @@ -0,0 +1,174 @@ +use super::paging::PagingInstr; +use crate::arch::paging::GenericPTE; +use crate::arch::paging::Level4PageTable; +use crate::memory::GuestPhysAddr; +use crate::memory::HostPhysAddr; +use crate::memory::MemFlags; +use crate::memory::PhysAddr; +use loongArch64::register::MemoryAccessType; + +bitflags::bitflags! { + /// Memory attribute fields in the LoongArch64 translation table format descriptors. + #[derive(Clone, Copy, Debug)] + pub struct DescriptorAttr: usize { + const V = 1 << 0; // Valid + const D = 1 << 1; // Dirty + + const PLV = 0b11 << 2; // Privilege Level Range + const PLV0 = 0b00 << 2; // PLV0 + const PLV1 = 0b01 << 2; // PLV1 + const PLV2 = 0b10 << 2; // PLV2 + const PLV3 = 0b11 << 2; // PLV3 + + const MAT = 0b11 << 4; // Memory Access Type Range + const MAT_SUC = 0b00 << 4; // Strongly-Ordered Uncached + const MAT_CC = 0b01 << 4; // Coherent Cached + const MAT_WB = 0b10 << 4; // Weakly-Ordered Uncached + + const G = 1 << 6; // Global + const P = 1 << 7; // Present + const W = 1 << 8; // Writable + const NR = 1 << 61; // Not Readable + const NX = 1 << 62; // Not Executable + const RPLV = 1 << 63; // Relative Privilege Level Check + } +} + +impl From for MemFlags { + fn from(attr: DescriptorAttr) -> Self { + let mut flags = Self::empty(); + if attr.contains(DescriptorAttr::W) { + flags |= Self::WRITE; + } + if !attr.contains(DescriptorAttr::NR) { + flags |= Self::READ; + } + if !attr.contains(DescriptorAttr::NX) { + flags |= Self::EXECUTE; + } + flags + } +} + +impl From for DescriptorAttr { + fn from(flags: MemFlags) -> Self { + let mut attr = Self::empty(); + if flags.contains(MemFlags::WRITE) { + attr |= Self::W; + } + if !flags.contains(MemFlags::READ) { + attr |= Self::NR; + } + if !flags.contains(MemFlags::EXECUTE) { + attr |= Self::NX; + } + // default we should set the VALID=1 since we are using it + // we set MemFlag::USER as NULL for invaliding this PTE + // DIRTY=1 to avoid Page Modified Exception + // attr |= Self::V | Self::D; + if !flags.contains(MemFlags::USER) { + attr |= Self::V | Self::D; + } + // now let's handle MAT, if flags includes IO then we use StronglyUncached + // otherwise we use CoherentCached + if flags.contains(MemFlags::IO) { + attr |= DescriptorAttr::MAT_SUC; // Strongly-Ordered Uncached + } else { + attr |= DescriptorAttr::MAT_CC; // Coherent Cached + } + attr + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct PageTableEntry(usize); + +// 12-47 +const PTE_PPN_MASK: usize = 0x0000_ffff_ffff_f000; + +impl PageTableEntry { + pub const fn empty() -> Self { + Self(0) + } +} + +impl GenericPTE for PageTableEntry { + fn addr(&self) -> HostPhysAddr { + let addr = self.0 & PTE_PPN_MASK; + // trace!( + // "loongarch64: PageTableEntry::addr: addr: {:#x?}, self.0: {:#x?}", + // addr, + // self.0 + // ); + PhysAddr::from(self.0 as usize & PTE_PPN_MASK as usize) + } + fn flags(&self) -> MemFlags { + DescriptorAttr::from_bits_truncate(self.0).into() + } + fn is_unused(&self) -> bool { + // if all 64 bits are 0, then it is regarded as unused + self.0 == 0 + } + fn is_present(&self) -> bool { + // check the P bit + self.0 & DescriptorAttr::P.bits() != 0 + } + fn set_addr(&mut self, addr: HostPhysAddr) { + // set the PPN range to the new address + self.0 = (self.0 & !PTE_PPN_MASK) | addr; + // trace!( + // "loongarch64: PageTableEntry::set_addr: addr: {:#x?}, self.0: {:#x?}", + // addr, + // self.0 + // ); + } + fn set_flags(&mut self, flags: MemFlags, is_huge: bool) { + let ppn = self.0 & PTE_PPN_MASK; + self.0 = DescriptorAttr::from(flags).bits() | ppn; + } + fn set_table(&mut self, pa: HostPhysAddr) { + self.set_addr(pa); + self.0 |= DescriptorAttr::V.bits(); + } + fn clear(&mut self) { + self.0 = 0; + } + fn is_huge(&self) -> bool { + false + } +} + +impl PageTableEntry { + pub fn set_flags_and_mat(&mut self, flags: DescriptorAttr, mat: MemoryAccessType) { + self.0 = (self.0 & !DescriptorAttr::MAT.bits()) | flags.bits() | mat as usize; + } +} + +pub struct S2PTInstr; + +impl PagingInstr for S2PTInstr { + unsafe fn activate(root_pa: HostPhysAddr) { + info!("loongarch64: S2PTInstr::activate: root_pa: {:#x?}", root_pa); + super::paging::set_pwcl_pwch(); + extern "C" { + fn tlb_refill_handler(); + } + use loongArch64::register::tlbrentry; + use loongArch64::register::{pgd, pgdh, pgdl}; + pgdh::set_base(root_pa); + pgdl::set_base(root_pa); + unsafe { + tlbrentry::set_tlbrentry(tlb_refill_handler as usize); + } + info!( + "loongarch64: S2PTInstr::activate: set tlbrentry to {:#x?} done!", + tlbrentry::read().addr() + ); + } + fn flush(vaddr: Option) { + warn!("loongarch64: S2PTInstr::flush: vaddr: {:#x?}", vaddr); + } +} + +pub type Stage2PageTable = Level4PageTable; diff --git a/src/arch/loongarch64/trap.rs b/src/arch/loongarch64/trap.rs new file mode 100644 index 00000000..0504d144 --- /dev/null +++ b/src/arch/loongarch64/trap.rs @@ -0,0 +1,1581 @@ +use crate::arch::cpu::this_cpu_id; +use crate::device::irqchip::inject_irq; +use crate::event::check_events; +use crate::event::dump_cpu_events; +use crate::event::dump_events; +use crate::hypercall::SGI_IPI_ID; +use crate::memory::addr; +use crate::memory::mmio_handle_access; +use crate::memory::MMIOAccess; +use crate::percpu::this_cpu_data; +use crate::zone::Zone; + +use super::register::*; +use super::zone::ZoneContext; +use core::arch; +use core::arch::asm; +use core::panic; +use loongArch64::cpu; +use loongArch64::register::ecfg::LineBasedInterrupt; +use loongArch64::register::*; +use loongArch64::time; + +pub fn install_trap_vector() { + // force disable INT here + crmd::set_ie(false); + // clear UEFI firmware's previous timer configs + tcfg::set_en(false); + ticlr::clear_timer_interrupt(); + // timer_init(); + // set CSR.EENTRY to _hyp_trap_vector and int vector offset to 0 + ecfg::set_vs(0); + eentry::set_eentry(_hyp_trap_vector as usize); + + // enable floating point + euen::set_fpe(true); // basic floating point + euen::set_sxe(true); // 128-bit SIMD + euen::set_asxe(true); // 256-bit SIMD + + enable_global_interrupt() +} + +/// enable CRMD.IE +pub fn enable_global_interrupt() { + crmd::set_ie(true); +} + +/// disable CRMD.IE +pub fn disable_global_interrupt() { + crmd::set_ie(false); +} + +pub fn get_ms_counter(ms: usize) -> usize { + ms * (time::get_timer_freq() / 1000) +} + +pub fn get_us_counter(us: usize) -> usize { + us * (time::get_timer_freq() / 1000_000) +} + +/// read the current stable counter value +pub fn ktime_get() -> usize { + let mut current_counter_time; + unsafe { + asm!( + "rdtime.d {}, {}", + out(reg) current_counter_time, + in(reg) 0, + ); + } + current_counter_time +} + +pub fn timer_init() { + // uefi firmware leaves timer interrupt pending, we need to clear it manually + ticlr::clear_timer_interrupt(); + // get timer frequency + let timer_freq = time::get_timer_freq(); + // 100_000_000 + // 1s = 1000 ms = 1000_000 us + // set timer + tcfg::set_periodic(true); + // let init_val = get_ms_counter(500); + let init_val = get_ms_counter(6000); + tcfg::set_init_val(init_val); + // println!("loongarch64: timer_init: timer init value = {}", init_val); + + tcfg::set_en(true); + + let mut lie_ = ecfg::read().lie(); + lie_ = lie_ | LineBasedInterrupt::TIMER; + ecfg::set_lie(lie_); +} + +pub fn ipi_init() { + // enable IPI + let mut lie_ = ecfg::read().lie(); + lie_ = lie_ | LineBasedInterrupt::IPI; + ecfg::set_lie(lie_); +} + +/// Translate exception code to string +pub fn ecode2str(ecode: usize, esubcode: usize) -> &'static str { + match ecode { + 0x0 => "INT(Interrupt)", + 0x1 => "PIL(Page Illegal Load)", + 0x2 => "PIS(Page Illegal Store)", + 0x3 => "PIF(Page Illegal Fetch)", + 0x4 => "PME(Page Modify Exception)", + 0x5 => "PNR(Page Not Readable)", + 0x6 => "PNX(Page Not Executable)", + 0x7 => "PPI(Page Privilege Illegal)", + 0x8 => match esubcode { + 0x0 => "ADEF(Instruction Fetch Address Exception)", + 0x1 => "ADEM(Memory Access Address Exception)", + _ => "error_esubcode", + }, + 0x9 => "ALE(Address Misaligned Exception)", + 0xa => "BCE(Edge Check Exception)", + 0xb => "SYS(System Call Exception)", + 0xc => "BRK(Breakpoint Exception)", + 0xd => "INE(Instruction Not Exist)", + 0xe => "IPE(Instruction Privilege Exception)", + 0xf => "FPD(Floating Point Disabled)", + 0x10 => "SXD(128-bit SIMD Disabled)", + 0x11 => "ASXD(256-bit SIMD Disabled)", + 0x12 => match esubcode { + 0x0 => "FPE(Floating Point Exception)", + 0x1 => "VFPE(Vector Floating Point Exception)", + _ => "error_esubcode", + }, + 0x13 => match esubcode { + 0x0 => "WPEF(Watchpoint Exception Fetch)", + 0x1 => "WPEM(Watchpoint Exception Memory)", + _ => "error_esubcode", + }, + 0x14 => "BTD(Binary Translation Disabled)", + 0x15 => "BTE(Binary Translation Exception)", + 0x16 => "GSPR(Guest Sensitive Privileged Resource)", + 0x17 => "HVC(Hypervisor Call)", + 0x18 => match esubcode { + 0x0 => "GCSC(Guest CSR Software Change)", + 0x1 => "GCHC(Guest CSR Hardware Change)", + _ => "error_esubcode", + }, + _ => "reserved_ecode", + } +} + +fn handle_page_modify_fault() { + let badv_ = badv::read(); + info!( + "loongarch64: handling page modify exception, vaddr = 0x{:x}", + badv_.vaddr() + ); + info!("loongarch64: ignoring this exception, todo: set dirty bit in page table entry"); +} + +#[no_mangle] +pub fn trap_handler(mut ctx: &mut ZoneContext) { + let cur = ktime_get(); + // print ctx addr + trace!("loongarch64: trap_handler: ctx addr = {:p}", &ctx); + + let estat_ = estat::read(); + let ecode = estat_.ecode(); + let esubcode = estat_.esubcode(); + let is = estat_.is(); + let badv_ = badv::read(); + let badi_ = badi::read(); + let era_ = era::read(); + // TLB dump + let tlbrera_ = tlbrera::read(); + let tlbrbadv_ = tlbrbadv::read(); + let tlbrelo0_ = tlbrelo0::read(); + let tlbrelo1_ = tlbrelo1::read(); + + let mut is_idle = false; + if ecode == ECODE_GSPR && badi_.inst() == 0b0000_0110_0100_1000_1000_0000_0000_0000 { + is_idle = true; + } + + if !is_idle { + debug!( + "loongarch64: trap_handler: {} ecode={:#x} esubcode={:#x} is={:#x} badv={:#x} badi={:#x} era={:#x}", + ecode2str(ecode, esubcode), + ecode, + esubcode, + is, + badv_.vaddr(), + badi_.inst(), + era_.raw(), + ); + print!("\0"); + } + + handle_exception( + ecode, + esubcode, + era_.raw(), + is, + badi_.inst() as usize, + badv_.vaddr(), + ctx, + ); + + // restore timer + let cfg = ctx.gcsr_tcfg; + write_gcsr_tcfg(0); + // restore GCSR_ESTAT and GCSR_TCFG + write_gcsr_estat(ctx.gcsr_estat); + write_gcsr_tcfg(ctx.gcsr_tcfg); + if cfg & (1 << 0) == 0 { + // guest has disabled timer, we just restore the tval + write_gcsr_tval(ctx.gcsr_tval); + } else { + // guest has enabled timer + let ticks = ctx.gcsr_tval; + let estat = ctx.gcsr_estat; + if !((cfg & 2) != 0 && (ticks > cfg)) { + write_gcsr_tval(0); // inject timer irq + if estat & 0x800 != 0 { + // set GCSR.TICLR[0] to 1 + write_gcsr_ticlr(1); + } + } + } + + let cur1 = ktime_get(); + // calculate the time spent in trap_handler + let time_spent = cur1 - cur; + // set guest timer compensation + let gcntc = gcntc::read(); + let gcntc_com = gcntc.compensation(); + gcntc::set_compensation(gcntc_com.wrapping_sub(time_spent)); + + if !is_idle { + debug!("loongarch64: trap_handler: return"); + print!("\0"); + } + + unsafe { + let _ctx_ptr = ctx as *mut ZoneContext; + _vcpu_return(_ctx_ptr as usize); + } +} + +const ECODE_INT: usize = 0x0; +const ECODE_GSPR: usize = 0x16; +const ECODE_PIL: usize = 0x1; +const ECODE_PIS: usize = 0x2; +const ECODE_HVC: usize = 0x17; +const ECODE_PNR: usize = 0x5; + +fn handle_exception( + ecode: usize, + esubcode: usize, + era: usize, + is: usize, + badi: usize, + badv: usize, + ctx: &mut ZoneContext, +) { + match ecode { + ECODE_INT => { + debug!( + "This is an interrupt exception, is={:#x}, ecfg.lie={:?}", + is, + ecfg::read().lie() + ); + // INT = 0x0, Interrupt + handle_interrupt(is); + } + ECODE_GSPR => { + // according to kvm's code, we should emulate the instruction that cause the GSPR exception - wheatfox 2024.4.12 + // GSPR = 0x16, Guest Sensitive Privileged Resource + trace!( + "This is a GSPR exception, badv={:#x}, badi={:#x}", + badv, + badi + ); + // arch_send_event(1, 0x7); + emulate_instruction(era, badi, ctx); + } + ECODE_HVC => { + // HVC = 0x17, Hypervisor Call + // code = a0(r4), arg0 = a1(r5), arg1 = a2(r6) + handle_hvc(ctx); + } + ECODE_PIL | ECODE_PIS | ECODE_PNR => { + debug!("exception: {}: ecode={:#x}, esubcode={:#x}, era={:#x}, is={:#x}, badi={:#x}, badv={:#x}", + ecode2str(ecode,esubcode), ecode, esubcode, era, is, badi, badv); + // we first assume this lies in virtio region + // since we didn't add these regions into VMM Pages + /* + LD.B rd, rj, si12 0010100000 si12 rj5 rd5 + LD.H rd, rj, si12 0010100001 si12 rj5 rd5 + LD.W rd, rj, si12 0010100010 si12 rj5 rd5 + LD.D rd, rj, si12 0010100011 si12 rj5 rd5 + ST.B rd, rj, si12 0010100100 si12 rj5 rd5 + ST.H rd, rj, si12 0010100101 si12 rj5 rd5 + ST.W rd, rj, si12 0010100110 si12 rj5 rd5 + ST.D rd, rj, si12 0010100111 si12 rj5 rd5 + LD.BU rd, rj, si12 0010101000 si12 rj5 rd5 + LD.HU rd, rj, si12 0010101001 si12 rj5 rd5 + LD.WU rd, rj, si12 0010101010 si12 rj5 rd5 + LDPTR.W rd, rj, si14 00100100 si14 rj5 rd5 + STPTR.W rd, rj, si14 00100101 si14 rj5 rd5 + LDPTR.D rd, rj, si14 00100110 si14 rj5 rd5 + STPTR.D rd, rj, si14 00100111 si14 rj5 rd5 + LDX.B rd, rj, rk 00111000000000 000 rk rj rd5 + LDX.H rd, rj, rk 00111000000001 000 rk rj rd5 + LDX.W rd, rj, rk 00111000000010 000 rk rj rd5 + LDX.D rd, rj, rk 00111000000011 000 rk rj rd5 + STX.B rd, rj, rk 00111000000100 000 rk rj rd5 + STX.H rd, rj, rk 00111000000101 000 rk rj rd5 + STX.W rd, rj, rk 00111000000110 000 rk rj rd5 + STX.D rd, rj, rk 00111000000111 000 rk rj rd5 + LDX.BU rd, rj, rk 00111000001000 000 rk rj rd5 + LDX.HU rd, rj, rk 00111000001001 000 rk rj rd5 + LDX.WU rd, rj, rk 00111000001010 000 rk rj rd5 + */ + let ins = badi; + let mut is_write = false; + let mut is_u = false; + let mut value = 0; + let mut size = 0; + let mut addr = 0; + let mut target_rd_idx = 0; + let prefix6 = extract_field(ins, 26, 6); + if prefix6 == 0b001010 { + // load/store + let rd = extract_field(ins, 0, 5); + target_rd_idx = rd; + let rj = extract_field(ins, 5, 5); + let si12 = extract_field(ins, 10, 12); + let ty = extract_field(ins, 24, 2); // ld/st/ldu - 0b00/0b01/0b10 + let sz = extract_field(ins, 22, 2); // 0b00=byte, 0b01=half, 0b10=word, 0b11=double + match ty { + 0b00 => { + // LD + is_write = false; + } + 0b01 => { + // ST + is_write = true; + value = ctx.x[rd]; + } + 0b10 => { + // LDU + is_write = false; + is_u = true; + } + _ => panic!("unhandled type"), + } + size = match sz { + 0b00 => 1, + 0b01 => 2, + 0b10 => 4, + 0b11 => 8, + _ => panic!("unhandled size"), + }; + } else if prefix6 == 0b001001 { + // load/store pointer + let rd = extract_field(ins, 0, 5); + target_rd_idx = rd; + let rj = extract_field(ins, 5, 5); + let si14 = extract_field(ins, 10, 14); + let mem_addr = ctx.x[rj] as usize + si14 as usize; + let ty = extract_field(ins, 24, 2); + match ty { + 0b00 => { + // LDPTR.W + is_write = false; + size = 4; + } + 0b01 => { + // STPTR.W + is_write = true; + size = 4; + value = ctx.x[rd]; + } + 0b10 => { + // LDPTR.D + is_write = false; + size = 8; + } + 0b11 => { + // STPTR.D + is_write = true; + size = 8; + value = ctx.x[rd]; + } + _ => panic!("unhandled size"), + } + } else if prefix6 == 0b001110 { + // load/store extended + let rd = extract_field(ins, 0, 5); + target_rd_idx = rd; + let rj = extract_field(ins, 5, 5); + let rk = extract_field(ins, 10, 5); + let sz = extract_field(ins, 18, 2); + let ty = extract_field(ins, 20, 2); + match ty { + 0b00 => { + // LDX + is_write = false; + } + 0b01 => { + // STX + is_write = true; + value = ctx.x[rd]; + } + 0b10 => { + // LDXU + is_write = false; + is_u = true; + } + _ => panic!("unhandled type"), + } + size = match sz { + 0b00 => 1, + 0b01 => 2, + 0b10 => 4, + 0b11 => 8, + _ => panic!("unhandled size"), + }; + } else { + panic!("unhandled instruction: {:#b}/{:#x}", ins, ins); + } + + let mut mmio_access = MMIOAccess { + address: badv, + size, + is_write, + value, + }; + // debug!( + // "mmio_access, addr={:#x}, size={:#x}, is_write={}, value={:#x}", + // mmio_access.address, mmio_access.size, mmio_access.is_write, mmio_access.value + // ); + debug!( + "!!!! {} mmio_access@{:#x} s={:#x} v={:#x}", + if is_write { "->write" } else { "<- read" }, + mmio_access.address, + mmio_access.size, + mmio_access.value + ); + let res = mmio_handle_access(&mut mmio_access); + match res { + Ok(_) => { + debug!("handle mmio success, v={:#x}", mmio_access.value); + if !is_write { + // we read an usize from our zone0 virtio-daemon + // need to trim and extend it to 64-bit reg according to is_u and size + // ctx.x[target_rd_idx] = mmio_access.value; + let trimmed_by_size = + mmio_access.value & ((1 << (mmio_access.size * 8)) - 1); + let extended = if !is_u { + // normal instruction with no .u use sign extension + signed_ext(trimmed_by_size, mmio_access.size * 8) + } else { + // .u instruction zero extend + trimmed_by_size + }; + debug!( + "read from mmio, raw={:#x}, trimmed={:#x}, extended={:#x}", + mmio_access.value, trimmed_by_size, extended + ); + ctx.x[target_rd_idx] = extended; + } + // we should jump to next instruction because we 'emulated' the instruction + ctx.sepc += 4; + } + Err(e) => { + error!( + "mmio access failed, error = {:?}, this is a real page fault", + e + ); + panic!("unhandled exception: {}: ecode={:#x}, esubcode={:#x}, era={:#x}, is={:#x}, badi={:#x}, badv={:#x}", + ecode2str(ecode,esubcode), ecode, esubcode, era, is, badi, badv) + } + } + } + _ => { + panic!("unhandled exception: {}: ecode={:#x}, esubcode={:#x}, era={:#x}, is={:#x}, badi={:#x}, badv={:#x}", + ecode2str(ecode,esubcode), ecode, esubcode, era, is, badi, badv) + } + } +} + +fn signed_ext(value: usize, size: usize) -> usize { + let sign_bit = 1 << (size - 1); + if value & sign_bit != 0 { + value | !((1 << size) - 1) + } else { + value + } +} + +#[no_mangle] +pub fn _vcpu_return(ctx: usize) { + let z = this_cpu_data().zone.as_ref(); + let vm_id; + if z.is_none() { + trace!("loongarch64: _vcpu_return: no zone found for cpu {}, maybe this is a kernel exception return", this_cpu_id()); + vm_id = 0; + } else { + // since LVZ use GID=0 for hypervisor TLB, we cannot use zone id 0 here + // so we add it by 1 - wheatfox + vm_id = z.unwrap().read().id + 1; + } + gstat::set_gid(vm_id); + gstat::set_pgm(true); + trace!( + "loongarch64: _vcpu_return: set hardware Guest ID to {}", + vm_id + ); + // Configure guest TLB control + gtlbc::set_use_tgid(true); + gtlbc::set_tgid(vm_id); + let gtlbc_ = gtlbc::read(); + trace!( + "loongarch64: _vcpu_return: gtlbc.use_tgid = {}", + gtlbc_.use_tgid() + ); + trace!("loongarch64: _vcpu_return: gtlbc.tgid = {}", gtlbc_.tgid()); + // Configure guest control + gcfg::set_matc(0x1); + let gcfg_ = gcfg::read(); + // Disable GSPR guest sensitive privileged resource exception + gcfg::set_topi(false); + gcfg::set_toti(false); + gcfg::set_toe(false); + gcfg::set_top(false); + gcfg::set_tohu(false); + gcfg::set_toci(0x2); + // when booting linux, linux is waiting for a HWI, but it never really comes + // to guest vm, in JTAG it's already in host CSR: ESTATE=0000000000000004,which is HWI0(UART...) + // so we need to relay host HWI to guest - wheatfox 2024.4.15 + gintc::set_hwip(0xff); // HWI7-HWI0 + + // Enable interrupt + prmd::set_pie(true); + trace!( + "loongarch64: _vcpu_return: calling _hyp_trap_return with ctx = {:#x}", + ctx + ); + unsafe { + _hyp_trap_return(ctx); + } +} + +#[no_mangle] +#[naked] +#[link_section = ".trap_entry"] +extern "C" fn _hyp_trap_vector() { + unsafe { + asm!( + "csrwr $r3, {LOONGARCH_CSR_DESAVE}", + "csrrd $r3, {LOONGARCH_CSR_SAVE3}", + //parpare VmContext for zone_trap_handler + //save 32 GPRS except $r3 + //save gcsrs managed by guest + "addi.d $r3, $r3, -768", + "st.d $r0, $r3, 0", + "st.d $r1, $r3, 8", + "st.d $r2, $r3, 16", + "st.d $r4, $r3, 32", + "st.d $r5, $r3, 40", + "st.d $r6, $r3, 48", + "st.d $r7, $r3, 56", + "st.d $r8, $r3, 64", + "st.d $r9, $r3, 72", + "st.d $r10, $r3, 80", + "st.d $r11, $r3, 88", + "st.d $r12, $r3, 96", + "st.d $r13, $r3, 104", + "st.d $r14, $r3, 112", + "st.d $r15, $r3, 120", + "st.d $r16, $r3, 128", + "st.d $r17, $r3, 136", + "st.d $r18, $r3, 144", + "st.d $r19, $r3, 152", + "st.d $r20, $r3, 160", + "st.d $r21, $r3, 168", + "st.d $r22, $r3, 176", + "st.d $r23, $r3, 184", + "st.d $r24, $r3, 192", + "st.d $r25, $r3, 200", + "st.d $r26, $r3, 208", + "st.d $r27, $r3, 216", + "st.d $r28, $r3, 224", + "st.d $r29, $r3, 232", + "st.d $r30, $r3, 240", + "st.d $r31, $r3, 248", + // save ERA + "csrrd $r12, {LOONGARCH_CSR_ERA}", + "st.d $r12, $r3, 256", + + // save GCSRS + "gcsrrd $r12, {LOONGARCH_GCSR_CRMD}", + "st.d $r12, $r3, 256+8*1", + "gcsrrd $r12, {LOONGARCH_GCSR_PRMD}", + "st.d $r12, $r3, 256+8*2", + "gcsrrd $r12, {LOONGARCH_GCSR_EUEN}", + "st.d $r12, $r3, 256+8*3", + "gcsrrd $r12, {LOONGARCH_GCSR_MISC}", + "st.d $r12, $r3, 256+8*4", + "gcsrrd $r12, {LOONGARCH_GCSR_ECTL}", + "st.d $r12, $r3, 256+8*5", + "gcsrrd $r12, {LOONGARCH_GCSR_ESTAT}", + "st.d $r12, $r3, 256+8*6", + "gcsrrd $r12, {LOONGARCH_GCSR_ERA}", + "st.d $r12, $r3, 256+8*7", + "gcsrrd $r12, {LOONGARCH_GCSR_BADV}", + "st.d $r12, $r3, 256+8*8", + "gcsrrd $r12, {LOONGARCH_GCSR_BADI}", + "st.d $r12, $r3, 256+8*9", + "gcsrrd $r12, {LOONGARCH_GCSR_EENTRY}", + "st.d $r12, $r3, 256+8*10", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBIDX}", + "st.d $r12, $r3, 256+8*11", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBEHI}", + "st.d $r12, $r3, 256+8*12", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBELO0}", + "st.d $r12, $r3, 256+8*13", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBELO1}", + "st.d $r12, $r3, 256+8*14", + "gcsrrd $r12, {LOONGARCH_GCSR_ASID}", + "st.d $r12, $r3, 256+8*15", + "gcsrrd $r12, {LOONGARCH_GCSR_PGDL}", + "st.d $r12, $r3, 256+8*16", + "gcsrrd $r12, {LOONGARCH_GCSR_PGDH}", + "st.d $r12, $r3, 256+8*17", + "gcsrrd $r12, {LOONGARCH_GCSR_PGD}", + "st.d $r12, $r3, 256+8*18", + "gcsrrd $r12, {LOONGARCH_GCSR_PWCL}", + "st.d $r12, $r3, 256+8*19", + "gcsrrd $r12, {LOONGARCH_GCSR_PWCH}", + "st.d $r12, $r3, 256+8*20", + "gcsrrd $r12, {LOONGARCH_GCSR_STLBPS}", + "st.d $r12, $r3, 256+8*21", + "gcsrrd $r12, {LOONGARCH_GCSR_RAVCFG}", + "st.d $r12, $r3, 256+8*22", + "gcsrrd $r12, {LOONGARCH_GCSR_CPUID}", + "st.d $r12, $r3, 256+8*23", + "gcsrrd $r12, {LOONGARCH_GCSR_PRCFG1}", + "st.d $r12, $r3, 256+8*24", + "gcsrrd $r12, {LOONGARCH_GCSR_PRCFG2}", + "st.d $r12, $r3, 256+8*25", + "gcsrrd $r12, {LOONGARCH_GCSR_PRCFG3}", + "st.d $r12, $r3, 256+8*26", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE0}", + "st.d $r12, $r3, 256+8*27", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE1}", + "st.d $r12, $r3, 256+8*28", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE2}", + "st.d $r12, $r3, 256+8*29", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE3}", + "st.d $r12, $r3, 256+8*30", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE4}", + "st.d $r12, $r3, 256+8*31", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE5}", + "st.d $r12, $r3, 256+8*32", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE6}", + "st.d $r12, $r3, 256+8*33", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE7}", + "st.d $r12, $r3, 256+8*34", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE8}", + "st.d $r12, $r3, 256+8*35", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE9}", + "st.d $r12, $r3, 256+8*36", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE10}", + "st.d $r12, $r3, 256+8*37", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE11}", + "st.d $r12, $r3, 256+8*38", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE12}", + "st.d $r12, $r3, 256+8*39", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE13}", + "st.d $r12, $r3, 256+8*40", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE14}", + "st.d $r12, $r3, 256+8*41", + "gcsrrd $r12, {LOONGARCH_GCSR_SAVE15}", + "st.d $r12, $r3, 256+8*42", + "gcsrrd $r12, {LOONGARCH_GCSR_TID}", + "st.d $r12, $r3, 256+8*43", + "gcsrrd $r12, {LOONGARCH_GCSR_TCFG}", + "st.d $r12, $r3, 256+8*44", + "gcsrrd $r12, {LOONGARCH_GCSR_TVAL}", + "st.d $r12, $r3, 256+8*45", + "gcsrrd $r12, {LOONGARCH_GCSR_CNTC}", + "st.d $r12, $r3, 256+8*46", + "gcsrrd $r12, {LOONGARCH_GCSR_TICLR}", + "st.d $r12, $r3, 256+8*47", + "gcsrrd $r12, {LOONGARCH_GCSR_LLBCTL}", + "st.d $r12, $r3, 256+8*48", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRENTRY}", + "st.d $r12, $r3, 256+8*49", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRBADV}", + "st.d $r12, $r3, 256+8*50", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRERA}", + "st.d $r12, $r3, 256+8*51", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRSAVE}", + "st.d $r12, $r3, 256+8*52", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRELO0}", + "st.d $r12, $r3, 256+8*53", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRELO1}", + "st.d $r12, $r3, 256+8*54", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBREHI}", + "st.d $r12, $r3, 256+8*55", + "gcsrrd $r12, {LOONGARCH_GCSR_TLBRPRMD}", + "st.d $r12, $r3, 256+8*56", + "gcsrrd $r12, {LOONGARCH_GCSR_DMW0}", + "st.d $r12, $r3, 256+8*57", + "gcsrrd $r12, {LOONGARCH_GCSR_DMW1}", + "st.d $r12, $r3, 256+8*58", + "gcsrrd $r12, {LOONGARCH_GCSR_DMW2}", + "st.d $r12, $r3, 256+8*59", + "gcsrrd $r12, {LOONGARCH_GCSR_DMW3}", + "st.d $r12, $r3, 256+8*60", + // // now let's save the zone's pgd to ZoneContext + // "csrrd $r12, {LOONGARCH_CSR_PGDL}", + // "st.d $r12, $r3, 256+8*61", // PGDL + // "csrrd $r13, {LOONGARCH_CSR_PGDH}", + // "st.d $r13, $r3, 256+8*62", // PGDH + // // now let's switch KSAVE5 and KSAVE6, which should already + // // be set to kernel's pagetable base + // "csrwr $r12, {LOONGARCH_CSR_SAVE5}", + // "csrwr $r13, {LOONGARCH_CSR_SAVE6}", + // "csrwr $r12, {LOONGARCH_CSR_PGDL}", + // "csrwr $r13, {LOONGARCH_CSR_PGDH}", + // "invtlb 0, $r0, $r0", + // save $r3 (previously saved in DESAVE) this is guest sp + "csrrd $r12, {LOONGARCH_CSR_DESAVE}", + "st.d $r12, $r3, 24", + // $r3 -> a0, now the param of zone_trap_handler is ok + "move $r4, $r3", + // rewind sp to PerCpu default stack top from KSAVE4 + "csrrd $r3, {LOONGARCH_CSR_SAVE4}", + "bl trap_handler", + LOONGARCH_CSR_SAVE3 = const 0x33, + LOONGARCH_CSR_SAVE4 = const 0x34, + LOONGARCH_CSR_DESAVE = const 0x502, + LOONGARCH_CSR_ERA = const 0x6, + LOONGARCH_GCSR_CRMD = const 0x0, + LOONGARCH_GCSR_PRMD = const 0x1, + LOONGARCH_GCSR_EUEN = const 0x2, + LOONGARCH_GCSR_MISC = const 0x3, + LOONGARCH_GCSR_ECTL = const 0x4, + LOONGARCH_GCSR_ESTAT = const 0x5, + LOONGARCH_GCSR_ERA = const 0x6, + LOONGARCH_GCSR_BADV = const 0x7, + LOONGARCH_GCSR_BADI = const 0x8, + LOONGARCH_GCSR_EENTRY = const 0xc, + LOONGARCH_GCSR_TLBIDX = const 0x10, + LOONGARCH_GCSR_TLBEHI = const 0x11, + LOONGARCH_GCSR_TLBELO0 = const 0x12, + LOONGARCH_GCSR_TLBELO1 = const 0x13, + LOONGARCH_GCSR_ASID = const 0x18, + LOONGARCH_GCSR_PGDL = const 0x19, + LOONGARCH_GCSR_PGDH = const 0x1a, + LOONGARCH_GCSR_PGD = const 0x1b, + LOONGARCH_GCSR_PWCL = const 0x1c, + LOONGARCH_GCSR_PWCH = const 0x1d, + LOONGARCH_GCSR_STLBPS = const 0x1e, + LOONGARCH_GCSR_RAVCFG = const 0x1f, + LOONGARCH_GCSR_CPUID = const 0x20, + LOONGARCH_GCSR_PRCFG1 = const 0x21, + LOONGARCH_GCSR_PRCFG2 = const 0x22, + LOONGARCH_GCSR_PRCFG3 = const 0x23, + LOONGARCH_GCSR_SAVE0 = const 0x30, + LOONGARCH_GCSR_SAVE1 = const 0x31, + LOONGARCH_GCSR_SAVE2 = const 0x32, + LOONGARCH_GCSR_SAVE3 = const 0x33, + LOONGARCH_GCSR_SAVE4 = const 0x34, + LOONGARCH_GCSR_SAVE5 = const 0x35, + LOONGARCH_GCSR_SAVE6 = const 0x36, + LOONGARCH_GCSR_SAVE7 = const 0x37, + LOONGARCH_GCSR_SAVE8 = const 0x38, + LOONGARCH_GCSR_SAVE9 = const 0x39, + LOONGARCH_GCSR_SAVE10 = const 0x3a, + LOONGARCH_GCSR_SAVE11 = const 0x3b, + LOONGARCH_GCSR_SAVE12 = const 0x3c, + LOONGARCH_GCSR_SAVE13 = const 0x3d, + LOONGARCH_GCSR_SAVE14 = const 0x3e, + LOONGARCH_GCSR_SAVE15 = const 0x3f, + LOONGARCH_GCSR_TID = const 0x40, + LOONGARCH_GCSR_TCFG = const 0x41, + LOONGARCH_GCSR_TVAL = const 0x42, + LOONGARCH_GCSR_CNTC = const 0x43, + LOONGARCH_GCSR_TICLR = const 0x44, + LOONGARCH_GCSR_LLBCTL = const 0x60, + LOONGARCH_GCSR_TLBRENTRY = const 0x88, + LOONGARCH_GCSR_TLBRBADV = const 0x89, + LOONGARCH_GCSR_TLBRERA = const 0x8a, + LOONGARCH_GCSR_TLBRSAVE = const 0x8b, + LOONGARCH_GCSR_TLBRELO0 = const 0x8c, + LOONGARCH_GCSR_TLBRELO1 = const 0x8d, + LOONGARCH_GCSR_TLBREHI = const 0x8e, + LOONGARCH_GCSR_TLBRPRMD = const 0x8f, + LOONGARCH_GCSR_DMW0 = const 0x180, + LOONGARCH_GCSR_DMW1 = const 0x181, + LOONGARCH_GCSR_DMW2 = const 0x182, + LOONGARCH_GCSR_DMW3 = const 0x183, + // LOONGARCH_CSR_PGDL = const 0x19, + // LOONGARCH_CSR_PGDH = const 0x1a, + // LOONGARCH_CSR_SAVE5 = const 0x35, + // LOONGARCH_CSR_SAVE6 = const 0x36, + options(noreturn) + ); + } +} + +#[no_mangle] +pub unsafe extern "C" fn _hyp_trap_return(ctx: usize) { + unsafe { + asm!( + // a0 -> sp + "move $r3, $r4", + // restore ERA + "ld.d $r12, $r3, 256", + "csrwr $r12, {LOONGARCH_CSR_ERA}", + // restore GCSRS + "ld.d $r12, $r3, 256+8*1", + "gcsrwr $r12, {LOONGARCH_GCSR_CRMD}", + "ld.d $r12, $r3, 256+8*2", + "gcsrwr $r12, {LOONGARCH_GCSR_PRMD}", + "ld.d $r12, $r3, 256+8*3", + "gcsrwr $r12, {LOONGARCH_GCSR_EUEN}", + "ld.d $r12, $r3, 256+8*4", + "gcsrwr $r12, {LOONGARCH_GCSR_MISC}", + "ld.d $r12, $r3, 256+8*5", + "gcsrwr $r12, {LOONGARCH_GCSR_ECTL}", + // "ld.d $r12, $r3, 256+8*6", + // "gcsrwr $r12, {LOONGARCH_GCSR_ESTAT}", + "ld.d $r12, $r3, 256+8*7", + "gcsrwr $r12, {LOONGARCH_GCSR_ERA}", + "ld.d $r12, $r3, 256+8*8", + "gcsrwr $r12, {LOONGARCH_GCSR_BADV}", + "ld.d $r12, $r3, 256+8*9", + "gcsrwr $r12, {LOONGARCH_GCSR_BADI}", + "ld.d $r12, $r3, 256+8*10", + "gcsrwr $r12, {LOONGARCH_GCSR_EENTRY}", + "ld.d $r12, $r3, 256+8*11", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBIDX}", + "ld.d $r12, $r3, 256+8*12", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBEHI}", + "ld.d $r12, $r3, 256+8*13", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBELO0}", + "ld.d $r12, $r3, 256+8*14", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBELO1}", + "ld.d $r12, $r3, 256+8*15", + "gcsrwr $r12, {LOONGARCH_GCSR_ASID}", + "ld.d $r12, $r3, 256+8*16", + "gcsrwr $r12, {LOONGARCH_GCSR_PGDL}", + "ld.d $r12, $r3, 256+8*17", + "gcsrwr $r12, {LOONGARCH_GCSR_PGDH}", + "ld.d $r12, $r3, 256+8*18", + "gcsrwr $r12, {LOONGARCH_GCSR_PGD}", + "ld.d $r12, $r3, 256+8*19", + "gcsrwr $r12, {LOONGARCH_GCSR_PWCL}", + "ld.d $r12, $r3, 256+8*20", + "gcsrwr $r12, {LOONGARCH_GCSR_PWCH}", + "ld.d $r12, $r3, 256+8*21", + "gcsrwr $r12, {LOONGARCH_GCSR_STLBPS}", + "ld.d $r12, $r3, 256+8*22", + "gcsrwr $r12, {LOONGARCH_GCSR_RAVCFG}", + "ld.d $r12, $r3, 256+8*23", + "gcsrwr $r12, {LOONGARCH_GCSR_CPUID}", + "ld.d $r12, $r3, 256+8*24", + "gcsrwr $r12, {LOONGARCH_GCSR_PRCFG1}", + "ld.d $r12, $r3, 256+8*25", + "gcsrwr $r12, {LOONGARCH_GCSR_PRCFG2}", + "ld.d $r12, $r3, 256+8*26", + "gcsrwr $r12, {LOONGARCH_GCSR_PRCFG3}", + "ld.d $r12, $r3, 256+8*27", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE0}", + "ld.d $r12, $r3, 256+8*28", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE1}", + "ld.d $r12, $r3, 256+8*29", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE2}", + "ld.d $r12, $r3, 256+8*30", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE3}", + "ld.d $r12, $r3, 256+8*31", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE4}", + "ld.d $r12, $r3, 256+8*32", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE5}", + "ld.d $r12, $r3, 256+8*33", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE6}", + "ld.d $r12, $r3, 256+8*34", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE7}", + "ld.d $r12, $r3, 256+8*35", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE8}", + "ld.d $r12, $r3, 256+8*36", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE9}", + "ld.d $r12, $r3, 256+8*37", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE10}", + "ld.d $r12, $r3, 256+8*38", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE11}", + "ld.d $r12, $r3, 256+8*39", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE12}", + "ld.d $r12, $r3, 256+8*40", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE13}", + "ld.d $r12, $r3, 256+8*41", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE14}", + "ld.d $r12, $r3, 256+8*42", + "gcsrwr $r12, {LOONGARCH_GCSR_SAVE15}", + // "ld.d $r12, $r3, 256+8*43", + // "gcsrwr $r12, {LOONGARCH_GCSR_TID}", + // "ld.d $r12, $r3, 256+8*44", + // "gcsrwr $r12, {LOONGARCH_GCSR_TCFG}", + // "ld.d $r12, $r3, 256+8*45", + // "gcsrwr $r12, {LOONGARCH_GCSR_TVAL}", + // "ld.d $r12, $r3, 256+8*46", + // "gcsrwr $r12, {LOONGARCH_GCSR_CNTC}", + // "ld.d $r12, $r3, 256+8*47", + // "gcsrwr $r12, {LOONGARCH_GCSR_TICLR}", + "ld.d $r12, $r3, 256+8*48", + "gcsrwr $r12, {LOONGARCH_GCSR_LLBCTL}", + "ld.d $r12, $r3, 256+8*49", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRENTRY}", + "ld.d $r12, $r3, 256+8*50", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRBADV}", + "ld.d $r12, $r3, 256+8*51", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRERA}", + "ld.d $r12, $r3, 256+8*52", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRSAVE}", + "ld.d $r12, $r3, 256+8*53", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRELO0}", + "ld.d $r12, $r3, 256+8*54", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRELO1}", + "ld.d $r12, $r3, 256+8*55", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBREHI}", + "ld.d $r12, $r3, 256+8*56", + "gcsrwr $r12, {LOONGARCH_GCSR_TLBRPRMD}", + "ld.d $r12, $r3, 256+8*57", + "gcsrwr $r12, {LOONGARCH_GCSR_DMW0}", + "ld.d $r12, $r3, 256+8*58", + "gcsrwr $r12, {LOONGARCH_GCSR_DMW1}", + "ld.d $r12, $r3, 256+8*59", + "gcsrwr $r12, {LOONGARCH_GCSR_DMW2}", + "ld.d $r12, $r3, 256+8*60", + "gcsrwr $r12, {LOONGARCH_GCSR_DMW3}", + LOONGARCH_CSR_ERA = const 0x6, + LOONGARCH_GCSR_CRMD = const 0x0, + LOONGARCH_GCSR_PRMD = const 0x1, + LOONGARCH_GCSR_EUEN = const 0x2, + LOONGARCH_GCSR_MISC = const 0x3, + LOONGARCH_GCSR_ECTL = const 0x4, + // LOONGARCH_GCSR_ESTAT = const 0x5, + LOONGARCH_GCSR_ERA = const 0x6, + LOONGARCH_GCSR_BADV = const 0x7, + LOONGARCH_GCSR_BADI = const 0x8, + LOONGARCH_GCSR_EENTRY = const 0xc, + LOONGARCH_GCSR_TLBIDX = const 0x10, + LOONGARCH_GCSR_TLBEHI = const 0x11, + LOONGARCH_GCSR_TLBELO0 = const 0x12, + LOONGARCH_GCSR_TLBELO1 = const 0x13, + LOONGARCH_GCSR_ASID = const 0x18, + LOONGARCH_GCSR_PGDL = const 0x19, + LOONGARCH_GCSR_PGDH = const 0x1a, + LOONGARCH_GCSR_PGD = const 0x1b, + LOONGARCH_GCSR_PWCL = const 0x1c, + LOONGARCH_GCSR_PWCH = const 0x1d, + LOONGARCH_GCSR_STLBPS = const 0x1e, + LOONGARCH_GCSR_RAVCFG = const 0x1f, + LOONGARCH_GCSR_CPUID = const 0x20, + LOONGARCH_GCSR_PRCFG1 = const 0x21, + LOONGARCH_GCSR_PRCFG2 = const 0x22, + LOONGARCH_GCSR_PRCFG3 = const 0x23, + LOONGARCH_GCSR_SAVE0 = const 0x30, + LOONGARCH_GCSR_SAVE1 = const 0x31, + LOONGARCH_GCSR_SAVE2 = const 0x32, + LOONGARCH_GCSR_SAVE3 = const 0x33, + LOONGARCH_GCSR_SAVE4 = const 0x34, + LOONGARCH_GCSR_SAVE5 = const 0x35, + LOONGARCH_GCSR_SAVE6 = const 0x36, + LOONGARCH_GCSR_SAVE7 = const 0x37, + LOONGARCH_GCSR_SAVE8 = const 0x38, + LOONGARCH_GCSR_SAVE9 = const 0x39, + LOONGARCH_GCSR_SAVE10 = const 0x3a, + LOONGARCH_GCSR_SAVE11 = const 0x3b, + LOONGARCH_GCSR_SAVE12 = const 0x3c, + LOONGARCH_GCSR_SAVE13 = const 0x3d, + LOONGARCH_GCSR_SAVE14 = const 0x3e, + LOONGARCH_GCSR_SAVE15 = const 0x3f, + // LOONGARCH_GCSR_TID = const 0x40, + // LOONGARCH_GCSR_TCFG = const 0x41, + // LOONGARCH_GCSR_TVAL = const 0x42, + // LOONGARCH_GCSR_CNTC = const 0x43, + // LOONGARCH_GCSR_TICLR = const 0x44, + LOONGARCH_GCSR_LLBCTL = const 0x60, + LOONGARCH_GCSR_TLBRENTRY = const 0x88, + LOONGARCH_GCSR_TLBRBADV = const 0x89, + LOONGARCH_GCSR_TLBRERA = const 0x8a, + LOONGARCH_GCSR_TLBRSAVE = const 0x8b, + LOONGARCH_GCSR_TLBRELO0 = const 0x8c, + LOONGARCH_GCSR_TLBRELO1 = const 0x8d, + LOONGARCH_GCSR_TLBREHI = const 0x8e, + LOONGARCH_GCSR_TLBRPRMD = const 0x8f, + LOONGARCH_GCSR_DMW0 = const 0x180, + LOONGARCH_GCSR_DMW1 = const 0x181, + LOONGARCH_GCSR_DMW2 = const 0x182, + LOONGARCH_GCSR_DMW3 = const 0x183, + ); + // asm!( + // // vm-pagetable -> save5 and save6 + // "ld.d $r12, $r3, 256+8*61", + // "csrwr $r12, {LOONGARCH_CSR_SAVE5}", + // "ld.d $r12, $r3, 256+8*62", + // "csrwr $r12, {LOONGARCH_CSR_SAVE6}", + // // kernel-pagetable -> r12 and r13 + // "csrrd $r12, {LOONGARCH_CSR_PGDL}", + // "csrrd $r13, {LOONGARCH_CSR_PGDH}", + // // kernel_pagetable -> save5 and save6 + // // old save5/save6(vm_pagetable) -> r12/r13 + // "csrwr $r12, {LOONGARCH_CSR_SAVE5}", + // "csrwr $r13, {LOONGARCH_CSR_SAVE6}", + // // change pagetable from kernel pagetable to vm page table + // "csrwr $r12, {LOONGARCH_CSR_PGDL}", + // "csrwr $r13, {LOONGARCH_CSR_PGDH}", + // "invtlb 0, $r0, $r0", + // LOONGARCH_CSR_SAVE5 = const 0x35, + // LOONGARCH_CSR_SAVE6 = const 0x36, + // LOONGARCH_CSR_PGDL = const 0x19, + // LOONGARCH_CSR_PGDH = const 0x1a, + // ); + asm!( + // restore sp + "ld.d $r12, $r3, 24", + "csrwr $r12, {LOONGARCH_CSR_DESAVE}", + // restore 32 GPRS: + "ld.d $r0, $r3, 0", + "ld.d $r1, $r3, 8", + "ld.d $r2, $r3, 16", + //ld.d $r3, $r3, 24 + "ld.d $r4, $r3, 32", + "ld.d $r5, $r3, 40", + "ld.d $r6, $r3, 48", + "ld.d $r7, $r3, 56", + "ld.d $r8, $r3, 64", + "ld.d $r9, $r3, 72", + "ld.d $r10, $r3, 80", + "ld.d $r11, $r3, 88", + "ld.d $r12, $r3, 96", + "ld.d $r13, $r3, 104", + "ld.d $r14, $r3, 112", + "ld.d $r15, $r3, 120", + "ld.d $r16, $r3, 128", + "ld.d $r17, $r3, 136", + "ld.d $r18, $r3, 144", + "ld.d $r19, $r3, 152", + "ld.d $r20, $r3, 160", + "ld.d $r21, $r3, 168", + "ld.d $r22, $r3, 176", + "ld.d $r23, $r3, 184", + "ld.d $r24, $r3, 192", + "ld.d $r25, $r3, 200", + "ld.d $r26, $r3, 208", + "ld.d $r27, $r3, 216", + "ld.d $r28, $r3, 224", + "ld.d $r29, $r3, 232", + "ld.d $r30, $r3, 240", + "ld.d $r31, $r3, 248", + "csrwr $r3, {LOONGARCH_CSR_DESAVE}", + "ertn", + LOONGARCH_CSR_DESAVE = const 0x502 + ); + } +} + +/// call this before running any guests to acquire the default GCSR values +pub fn dump_reset_gcsrs() -> ZoneContext { + let mut ctx = ZoneContext::new(); + ctx.gcsr_crmd = read_gcsr_crmd(); + ctx.gcsr_prmd = read_gcsr_prmd(); + ctx.gcsr_euen = read_gcsr_euen(); + ctx.gcsr_misc = read_gcsr_misc(); + ctx.gcsr_ectl = read_gcsr_ectl(); + ctx.gcsr_estat = read_gcsr_estat(); + ctx.gcsr_era = read_gcsr_era(); + ctx.gcsr_badv = read_gcsr_badv(); + ctx.gcsr_badi = read_gcsr_badi(); + ctx.gcsr_eentry = read_gcsr_eentry(); + ctx.gcsr_tlbidx = read_gcsr_tlbidx(); + ctx.gcsr_tlbehi = read_gcsr_tlbehi(); + ctx.gcsr_tlbelo0 = read_gcsr_tlbelo0(); + ctx.gcsr_tlbelo1 = read_gcsr_tlbelo1(); + ctx.gcsr_asid = read_gcsr_asid(); + ctx.gcsr_pgdl = read_gcsr_pgdl(); + ctx.gcsr_pgdh = read_gcsr_pgdh(); + ctx.gcsr_pgd = read_gcsr_pgd(); + ctx.gcsr_pwcl = read_gcsr_pwcl(); + ctx.gcsr_pwch = read_gcsr_pwch(); + ctx.gcsr_stlbps = read_gcsr_stlbps(); + ctx.gcsr_ravcfg = read_gcsr_ravcfg(); + ctx.gcsr_cpuid = read_gcsr_cpuid(); + ctx.gcsr_prcfg1 = read_gcsr_prcfg1(); + ctx.gcsr_prcfg2 = read_gcsr_prcfg2(); + ctx.gcsr_prcfg3 = read_gcsr_prcfg3(); + ctx.gcsr_save0 = read_gcsr_save0(); + ctx.gcsr_save1 = read_gcsr_save1(); + ctx.gcsr_save2 = read_gcsr_save2(); + ctx.gcsr_save3 = read_gcsr_save3(); + ctx.gcsr_save4 = read_gcsr_save4(); + ctx.gcsr_save5 = read_gcsr_save5(); + ctx.gcsr_save6 = read_gcsr_save6(); + ctx.gcsr_save7 = read_gcsr_save7(); + ctx.gcsr_save8 = read_gcsr_save8(); + ctx.gcsr_save9 = read_gcsr_save9(); + ctx.gcsr_save10 = read_gcsr_save10(); + ctx.gcsr_save11 = read_gcsr_save11(); + ctx.gcsr_save12 = read_gcsr_save12(); + ctx.gcsr_save13 = read_gcsr_save13(); + ctx.gcsr_save14 = read_gcsr_save14(); + ctx.gcsr_save15 = read_gcsr_save15(); + ctx.gcsr_tid = read_gcsr_tid(); + ctx.gcsr_tcfg = read_gcsr_tcfg(); + ctx.gcsr_tval = read_gcsr_tval(); + ctx.gcsr_cntc = read_gcsr_cntc(); + ctx.gcsr_ticlr = read_gcsr_ticlr(); + ctx.gcsr_llbctl = read_gcsr_llbctl(); + ctx.gcsr_tlbrentry = read_gcsr_tlbrentry(); + ctx.gcsr_tlbrbadv = read_gcsr_tlbrbadv(); + ctx.gcsr_tlbrera = read_gcsr_tlbrera(); + ctx.gcsr_tlbrsave = read_gcsr_tlbrsave(); + ctx.gcsr_tlbrelo0 = read_gcsr_tlbrrelo0(); + ctx.gcsr_tlbrelo1 = read_gcsr_tlbrrelo1(); + ctx.gcsr_tlbrehi = read_gcsr_tlbrrehi(); + ctx.gcsr_tlbrprmd = read_gcsr_tlbrprmd(); + ctx.gcsr_dmw0 = read_gcsr_dmw0(); + ctx.gcsr_dmw1 = read_gcsr_dmw1(); + ctx.gcsr_dmw2 = read_gcsr_dmw2(); + ctx.gcsr_dmw3 = read_gcsr_dmw3(); + + ctx +} + +fn extract_field(inst: usize, offset: usize, length: usize) -> usize { + let mask = (1 << length) - 1; + (inst >> offset) & mask +} + +/// get the sign-extended imm12 to i64 +fn imm12toi64(imm12: usize) -> isize { + let imm12 = imm12 as isize; + let imm12 = imm12 << 52; + imm12 >> 52 +} + +use crate::arch::ipi::*; +const INT_IPI: usize = 12; +const IPI_BIT: usize = 1 << 12; +const TIMER_BIT: usize = 1 << 11; + +/// handle loongarch64 interrupts here +fn handle_interrupt(is: usize) { + match is { + _ if is & IPI_BIT != 0 => { + let ipi_status = get_ipi_status(this_cpu_id()); + debug!("ipi interrupt, status = {:#x}", ipi_status); + // handle IPI + if ipi_status == SGI_IPI_ID as _ { + // 0x7 is a SGI in hvisor, but we still need ot know which event it is + let events = dump_cpu_events(this_cpu_id()); + debug!("this cpu's events: {:?}", events); + // for _ in 0..count { + // let result = check_events(); + // if result { + // debug!("ipi event handled :)"); + // } else { + // error!("ipi event not handled !!!"); + // } + // } + while check_events() {} + } else if ipi_status == 0x8 { + debug!("not handled IPI status {:#x} for now", ipi_status); + } else { + warn!("ignored IPI status {:#x}", ipi_status); + } + reset_ipi(this_cpu_id()); + } + _ if is & TIMER_BIT != 0 => { + use loongArch64::register; + register::ticlr::clear_timer_interrupt(); + } + _ => { + info!("not handled interrupt"); + } + } +} + +/// hypercall handler +fn handle_hvc(ctx: &mut ZoneContext) { + // HVC + let code = ctx.get_a0(); + let arg0 = ctx.get_a1(); + let arg1 = ctx.get_a2(); + + debug!( + "HVC exception, HVC call code: {:#x}, arg0: {:#x}, arg1: {:#x}", + code, arg0, arg1 + ); + use crate::hypercall::*; + let cpu_data = this_cpu_data(); + let res = match HyperCall::new(cpu_data).hypercall(code as _, arg0 as _, arg1 as _) { + Ok(ret) => ret as _, + Err(e) => { + error!("HVC exception failed: {:?}", e); + e.code() + } + }; + debug!("HVC result: {:#x}", res); + ctx.set_a0(res as _); + ctx.sepc += 4; +} + +fn emulate_cpucfg(ins: usize, ctx: &mut ZoneContext) { + // cpucfg + // now let get rd and rj, cpucfg rd[4:0], rj[9:5] + // let rd = ins & 0x1f; + // let rj = (ins >> 5) & 0x1f; + // let cpucfg_target_idx = ctx.x[rj]; + let rd = extract_field(ins, 0, 5); + let rj = extract_field(ins, 5, 5); + let cpucfg_target_idx = ctx.x[rj]; + + const MAX_CPUCFG_REGS: usize = 21; + + info!( + "cpucfg emulation, target cpucfg index is {:#x}", + cpucfg_target_idx + ); + + if cpucfg_target_idx >= MAX_CPUCFG_REGS { + // invalid cpucfg target + warn!("invalid cpucfg target"); + ctx.x[rd] = 0; + // according to manual, we should set result to 0 if index is invalid + } else { + // just run cpucfg here + let result: usize; + unsafe { + asm!("cpucfg {}, {}", out(reg) result, in(reg) cpucfg_target_idx); + } + ctx.x[rd] = result; + // finish the emulation by tweaking the ZoneContext's registers + // as ctx.sepc is already added by 4 which means we will jump to next instruction - wheatfox + } +} + +fn emulate_csrx(ins: usize, ctx: &mut ZoneContext) { + // csrrd csrwr csrxchg + + // let ty = (ins >> 5) & 0x1f; + // let rd = ins & 0x1f; + // let csr = (ins >> 10) & 0x3fff; + let ty = extract_field(ins, 5, 5); + let rd = extract_field(ins, 0, 5); + let csr = extract_field(ins, 10, 14); + // ty: [9:5], 0 - csrrd, 1 - csrwr, else - csrxchg + // rd [4:0] + // csr [23:10] 14 bits + match ty { + 0 => { + // csrrd + info!("csrrd emulation for CSR {:#x}", csr); + ctx.x[rd] = 0; + // just set it to 0 + } + 1 => { + // csrwr + info!("csrwr emulation for CSR {:#x}", csr); + ctx.x[rd] = 0; + // do nothing to GCSR, but we also need to set rd to 0 + } + _ => { + // csrxchg + info!("csrxchg emulation for CSR {:#x}", csr); + ctx.x[rd] = 0; + // do nothing to GCSR, but we also need to set rd to 0 + } + } +} + +fn emulate_cacop(ins: usize, ctx: &mut ZoneContext) { + // cacop code,rj,si12 0000011000 si12 rj[9:5] code[4:0] + warn!("cacop emulation not implemented, skipped this instruction"); +} + +fn emulate_idle(ins: usize, ctx: &mut ZoneContext) { + // idle level 0000011001 0010001 level[14:0] + let level = extract_field(ins, 0, 15); + trace!("guest request an idle at level {:#x}", level); + // // wait until GCSR.ESTAT.IS != 0 + // loop { + // let estat = read_gcsr_estat(); + // // ESTAT[12:0] = IS + // if estat & 0x1fff != 0 { + // let is = estat & 0x1fff; + // debug!("idle waited, interrupt status is {:#x}", is); + // break; + // } + // } +} + +fn emulate_iocsr(ins: usize, ctx: &mut ZoneContext) { + // iocsrrd.b rd, rj 0000011001 001000000000 rj[9:5] rd[4:0] + // iocsrrd.h rd, rj 0000011001 001000000001 rj[9:5] rd[4:0] + // iocsrrd.w rd, rj 0000011001 001000000010 rj[9:5] rd[4:0] + // iocsrrd.d rd, rj 0000011001 001000000011 rj[9:5] rd[4:0] + // iocsrwr.b rd, rj 0000011001 001000000100 rj[9:5] rd[4:0] + // iocsrwr.h rd, rj 0000011001 001000000101 rj[9:5] rd[4:0] + // iocsrwr.w rd, rj 0000011001 001000000110 rj[9:5] rd[4:0] + // iocsrwr.d rd, rj 0000011001 001000000111 rj[9:5] rd[4:0] + // let ty = (ins >> 10) & 0x7; + // let rd = ins & 0x1f; + // let rj = (ins >> 5) & 0x1f; + let ty = extract_field(ins, 10, 3); + let rd = extract_field(ins, 0, 5); + let rj = extract_field(ins, 5, 5); + info!("iocsr emulation, ty = {}, rd = {}, rj = {}", ty, rd, rj); + info!("GPR[rd] = {:#x}, GPR[rj] = {:#x}", ctx.x[rd], ctx.x[rj]); + match ty { + 0 => { + // iocsrrd.b + // GPR[rd] = iocsrrd.b(GPR[rj]) + let mut val = 0; + unsafe { + asm!("iocsrrd.b {}, {}", out(reg) val, in(reg) ctx.x[rj]); + } + ctx.x[rd] = val; + } + 1 => { + // iocsrrd.h + // GPR[rd] = iocsrrd.h(GPR[rj]) + let mut val = 0; + unsafe { + asm!("iocsrrd.h {}, {}", out(reg) val, in(reg) ctx.x[rj]); + } + ctx.x[rd] = val; + } + 2 => { + // iocsrrd.w + // GPR[rd] = iocsrrd.w(GPR[rj]) + let mut val = 0; + unsafe { + asm!("iocsrrd.w {}, {}", out(reg) val, in(reg) ctx.x[rj]); + } + ctx.x[rd] = val; + } + 3 => { + // iocsrrd.d + // GPR[rd] = iocsrrd.d(GPR[rj]) + let mut val = 0; + unsafe { + asm!("iocsrrd.d {}, {}", out(reg) val, in(reg) ctx.x[rj]); + } + ctx.x[rd] = val; + } + 4 => { + // iocsrwr.b + // iocsrwr.b(GPR[rd], GPR[rj]) + unsafe { + asm!("iocsrwr.b {}, {}", in(reg) ctx.x[rd], in(reg) ctx.x[rj]); + } + } + 5 => { + // iocsrwr.h + // iocsrwr.h(GPR[rd], GPR[rj]) + unsafe { + asm!("iocsrwr.h {}, {}", in(reg) ctx.x[rd], in(reg) ctx.x[rj]); + } + } + 6 => { + // iocsrwr.w + // iocsrwr.w(GPR[rd], GPR[rj]) + unsafe { + asm!("iocsrwr.w {}, {}", in(reg) ctx.x[rd], in(reg) ctx.x[rj]); + } + } + 7 => { + // iocsrwr.d + // iocsrwr.d(GPR[rd], GPR[rj]) + unsafe { + asm!("iocsrwr.d {}, {}", in(reg) ctx.x[rd], in(reg) ctx.x[rj]); + } + } + _ => { + // should not reach here + panic!("invalid iocsr type, this is impossible"); + } + } +} + +const UART0_BASE: usize = 0x1fe001e0; +const UART0_END: usize = 0x1fe001e8; + +fn emulate_ld_b(ins: usize, ctx: &mut ZoneContext) { + // ld.b rd, rj, si12 opcode[31:22]=0010100000 si12[21:10] rj[9:5] rd[4:0] + // let rd = ins & 0x1f; + // let rj = (ins >> 5) & 0x1f; + // let si12 = (ins >> 10) & 0x3ff; ??? should be 0xfff + let rd = extract_field(ins, 0, 5); + let rj = extract_field(ins, 5, 5); + let si12 = extract_field(ins, 10, 12); + + info!("ld.b emulation, rd = {}, rj = {}, si12 = {}", rd, rj, si12); + // vaddr = GR[rj] + SignExt(si12, GRLEN(64)) + // paddr = translate(vaddr) + // byte = load (paddr, BYTE) + // GR[rd] = byte + let vaddr = ctx.x[rj] as isize + imm12toi64(si12); + info!("vaddr = 0x{:x}", vaddr as usize); + let offset = (vaddr - UART0_BASE as isize) as usize; // minus the UART0 base address + // let mut uart0 = UART_EMU.lock(); + // let byte = uart0.read(offset); + // info!("byte = 0x{:x}", byte as usize); + // ctx.x[rd] = byte as usize; +} + +fn emulate_st_b(ins: usize, ctx: &mut ZoneContext) { + // st.b rd, rj, si12 opcode[31:22]=0010100100 si12[21:10] rj[9:5] rd[4:0] + // let rd = ins & 0x1f; + // let rj = (ins >> 5) & 0x1f; + // let si12 = (ins >> 10) & 0x3ff; + let rd = extract_field(ins, 0, 5); + let rj = extract_field(ins, 5, 5); + let si12 = extract_field(ins, 10, 12); + // info!("st.b emulation, rd = {}, rj = {}, si12 = {}", rd, rj, si12); + // vaddr = GR[rj] + SignExt(si12, GRLEN(64)) + // paddr = translate(vaddr) + // store (paddr, BYTE, GR[rd]) + let vaddr = ctx.x[rj] as isize + imm12toi64(si12); + // info!("vaddr = 0x{:x}", vaddr as usize); + let offset = (vaddr - UART0_BASE as isize) as usize; // minus the UART0 base address + // for VGA + // let mut uart0 = UART_EMU.lock(); + // let byte = ctx.x[rd] as u8; + // info!("byte = 0x{:x}", byte as usize); + // let cur_zone = current_vcpu().unwrap().get_zone().unwrap(); + // let cur_zone_id = cur_zone.get_zone_id(); + // uart0.write(offset, byte, false, (cur_zone_id - 1) as i32); + // drop(uart0); // !!!! very important + // cur_zone.inner.lock().uart_emu.write(offset, byte, true, 0); +} + +fn emulate_ld_bu(ins: usize, ctx: &mut ZoneContext) { + // ld.bu rd, rj, si12 opcode[31:22]=0010101000 si12[21:10] rj[9:5] rd[4:0] + // let rd = ins & 0x1f; + // let rj = (ins >> 5) & 0x1f; + // let si12 = (ins >> 10) & 0x3ff; + let rd = extract_field(ins, 0, 5); + let rj = extract_field(ins, 5, 5); + let si12 = extract_field(ins, 10, 12); + + // info!("ld.bu emulation, rd = {}, rj = {}, si12 = {}", rd, rj, si12); + // vaddr = GR[rj] + SignExt(si12, GRLEN(64)) + // paddr = translate(vaddr) + // byte = load (paddr, BYTE) + // GR[rd] = byte + let vaddr = ctx.x[rj] as isize + imm12toi64(si12); + // info!("vaddr = 0x{:x}", vaddr as usize); + let offset = (vaddr - UART0_BASE as isize) as usize; // minus the UART0 base address + // let mut uart0 = UART_EMU.lock(); + // let byte = uart0.read(offset); + // info!("byte = 0x{:x}", byte as usize); + // ctx.x[rd] = byte as usize; +} + +fn check_op_type(inst: usize, opcode: usize, opcode_length: usize) -> bool { + let mask = (1 << opcode_length) - 1; + let shifted = inst >> (32 - opcode_length); + (shifted & mask) == opcode +} + +const OPCODE_CPUCFG: usize = 0b0000000000000000011011; +const OPCODE_CPUCFG_LENGTH: usize = 22; +const OPCODE_CACOP: usize = 0b0000011000; +const OPCODE_CACOP_LENGTH: usize = 10; +const OPCODE_IDLE: usize = 0b00000_11001_0010001; +const OPCODE_IDLE_LENGTH: usize = 17; +const OPCODE_CSRX: usize = 0b00000100; +const OPCODE_CSRX_LENGTH: usize = 8; +const OPCODE_IOCSR: usize = 0b00000_11001_001000000; +const OPCODE_IOCSR_LENGTH: usize = 19; +const OPCODE_LD_B: usize = 0b0010100000; +const OPCODE_LD_B_LENGTH: usize = 10; +const OPCODE_ST_B: usize = 0b0010100100; +const OPCODE_ST_B_LENGTH: usize = 10; +const OPCODE_LD_BU: usize = 0b0010101000; +const OPCODE_LD_BU_LENGTH: usize = 10; +type OpcodeHandler = fn(usize, &mut ZoneContext); + +fn emulate_instruction(era: usize, ins: usize, ctx: &mut ZoneContext) { + let pc = era; + // after we emulate the instruction, we should jump to next instruction + ctx.sepc = pc + 4; + + let opcodes = vec![ + ( + OPCODE_CPUCFG, + OPCODE_CPUCFG_LENGTH, + emulate_cpucfg as OpcodeHandler, + ), + ( + OPCODE_CACOP, + OPCODE_CACOP_LENGTH, + emulate_cacop as OpcodeHandler, + ), + ( + OPCODE_IDLE, + OPCODE_IDLE_LENGTH, + emulate_idle as OpcodeHandler, + ), + ( + OPCODE_CSRX, + OPCODE_CSRX_LENGTH, + emulate_csrx as OpcodeHandler, + ), + ( + OPCODE_IOCSR, + OPCODE_IOCSR_LENGTH, + emulate_iocsr as OpcodeHandler, + ), + ( + OPCODE_LD_B, + OPCODE_LD_B_LENGTH, + emulate_ld_b as OpcodeHandler, + ), + ( + OPCODE_ST_B, + OPCODE_ST_B_LENGTH, + emulate_st_b as OpcodeHandler, + ), + ( + OPCODE_LD_BU, + OPCODE_LD_BU_LENGTH, + emulate_ld_bu as OpcodeHandler, + ), + ]; + for &(code, length, handler) in &opcodes { + if check_op_type(ins, code, length) { + handler(ins, ctx); + return; + } + } + + panic!("Unexpected opcode encountered, ins = {:#x}", ins); +} + +/* TLB REFILL HANDLER */ +#[no_mangle] +#[naked] +#[link_section = ".tlbrefill_entry"] +extern "C" fn tlb_refill_handler() { + unsafe { + asm!( + "csrwr $r12, {LOONGARCH_CSR_TLBRSAVE}", + "csrrd $r12, {LOONGARCH_CSR_PGD}", + "lddir $r12, $r12, 3", + "ori $r12, $r12, {PAGE_WALK_MASK}", + "xori $r12, $r12, {PAGE_WALK_MASK}", + "lddir $r12, $r12, 2", + "ori $r12, $r12, {PAGE_WALK_MASK}", + "xori $r12, $r12, {PAGE_WALK_MASK}", + "lddir $r12, $r12, 1", + "ori $r12, $r12, {PAGE_WALK_MASK}", + "xori $r12, $r12, {PAGE_WALK_MASK}", + "ldpte $r12, 0", + "ldpte $r12, 1", + "tlbfill", + "csrrd $r12, {LOONGARCH_CSR_TLBRSAVE}", + "ertn", + LOONGARCH_CSR_TLBRSAVE = const 0x8b, + LOONGARCH_CSR_PGD = const 0x1b, + PAGE_WALK_MASK = const 0xfff, + options(noreturn) + ); + } +} diff --git a/src/arch/loongarch64/zone.rs b/src/arch/loongarch64/zone.rs new file mode 100644 index 00000000..698b7856 --- /dev/null +++ b/src/arch/loongarch64/zone.rs @@ -0,0 +1,371 @@ +use crate::{ + config::*, + consts::PAGE_SIZE, + device::virtio_trampoline::mmio_virtio_handler, + error::HvResult, + memory::{ + addr::{align_down, align_up}, + mmio_generic_handler, GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, + }, + zone::Zone, +}; +use alloc::vec::Vec; +use core::arch::asm; +use core::sync::atomic::{fence, Ordering}; + +impl Zone { + pub fn pt_init(&mut self, mem_regions: &[HvConfigMemoryRegion]) -> HvResult { + // use the new zone config type of init + for region in mem_regions { + trace!("loongarch64: pt_init: process region: {:#x?}", region); + let mem_type = region.mem_type; + match mem_type { + MEM_TYPE_RAM => { + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + region.virtual_start as GuestPhysAddr, + region.physical_start as HostPhysAddr, + region.size as _, + MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, + ))?; + } + MEM_TYPE_IO => { + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + region.virtual_start as GuestPhysAddr, + region.physical_start as HostPhysAddr, + region.size as _, + MemFlags::READ | MemFlags::WRITE | MemFlags::IO, + ))?; + } + MEM_TYPE_VIRTIO => { + info!( + "loongarch64: pt_init: register virtio mmio region: {:#x?}", + region + ); + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + region.virtual_start as GuestPhysAddr, + region.physical_start as HostPhysAddr, + PAGE_SIZE, // since we only need 0x200 size for virtio mmio, but the minimal size is PAGE_SIZE + MemFlags::USER, // we use the USER as a hint flag for invalidating this stage-2 PTE + ))?; + self.mmio_region_register( + region.physical_start as _, + region.size as _, + mmio_virtio_handler, + region.physical_start as _, + ); + } + _ => { + error!("loongarch64: pt_init: unknown mem type: {}", mem_type); + return hv_result_err!(EINVAL); + } + } + } + debug!("zone stage-2 memory set: {:#x?}", self.gpm); + unsafe { + // test the page table by querying the first page + if mem_regions.len() > 0 { + let r = self + .gpm + .page_table_query(mem_regions[0].virtual_start as GuestPhysAddr); + debug!("query 0x{:x}: {:#x?}", mem_regions[0].virtual_start, r); + // check whether the first page is mapped + let va = mem_regions[0].virtual_start as GuestPhysAddr; + let result_pa = r.unwrap().0; + if result_pa != mem_regions[0].physical_start as HostPhysAddr { + error!( + "loongarch64: pt_init: page table test failed: va: {:#x}, pa: {:#x}, expected pa: {:#x}", + va, result_pa, mem_regions[0].physical_start + ); + return hv_result_err!(EINVAL, "page table test failed"); + } + } + } + Ok(()) + } + pub fn isa_init(&mut self, fdt: &fdt::Fdt) { + warn!("loongarch64: mm: isa_init do nothing"); + } + pub fn irq_bitmap_init(&mut self, irqs: &[u32]) {} +} + +pub fn disable_hwi_through() { + info!("loongarch64: disable_hwi_through"); + use crate::arch::register::*; + gintc::set_hwip(0x0); // stop passing through all 8 HWIs +} + +pub fn enable_hwi_through() { + info!("loongarch64: enable_hwi_through"); + use crate::arch::register::*; + gintc::set_hwip(0xff); // pass through all HWI7-0 +} + +#[repr(C)] +#[repr(align(16))] +#[derive(Debug, Copy, Clone)] +pub struct LoongArch64ZoneContext { + pub x: [usize; 32], + pub sepc: usize, + // General Control and Status Registers + pub gcsr_crmd: usize, // CRMD + pub gcsr_prmd: usize, // PRMD + pub gcsr_euen: usize, // EUEN + pub gcsr_misc: usize, // MISC + pub gcsr_ectl: usize, // ECTL + pub gcsr_estat: usize, // ESTAT + pub gcsr_era: usize, // ERA + pub gcsr_badv: usize, // BADV + pub gcsr_badi: usize, // BADI + pub gcsr_eentry: usize, // EENTRY + + // TLB Registers + pub gcsr_tlbidx: usize, // TLBIDX + pub gcsr_tlbehi: usize, // TLBEHI + pub gcsr_tlbelo0: usize, // TLBELO0 + pub gcsr_tlbelo1: usize, // TLBELO1 + + // Page Table Registers + pub gcsr_asid: usize, // ASID + pub gcsr_pgdl: usize, // PGDL + pub gcsr_pgdh: usize, // PGDH + pub gcsr_pgd: usize, // PGD + pub gcsr_pwcl: usize, // PWCL + pub gcsr_pwch: usize, // PWCH + + // Second Level TLB Registers + pub gcsr_stlbps: usize, // STLBPS + pub gcsr_ravcfg: usize, // RAVCFG + + // Processor Registers + pub gcsr_cpuid: usize, // CPUID + pub gcsr_prcfg1: usize, // PRCFG1 + pub gcsr_prcfg2: usize, // PRCFG2 + pub gcsr_prcfg3: usize, // PRCFG3 + + // Saved Registers + pub gcsr_save0: usize, // SAVE0 + pub gcsr_save1: usize, // SAVE1 + pub gcsr_save2: usize, // SAVE2 + pub gcsr_save3: usize, // SAVE3 + pub gcsr_save4: usize, // SAVE4 + pub gcsr_save5: usize, // SAVE5 + pub gcsr_save6: usize, // SAVE6 + pub gcsr_save7: usize, // SAVE7 + pub gcsr_save8: usize, // SAVE8 + pub gcsr_save9: usize, // SAVE9 + pub gcsr_save10: usize, // SAVE10 + pub gcsr_save11: usize, // SAVE11 + pub gcsr_save12: usize, // SAVE12 + pub gcsr_save13: usize, // SAVE13 + pub gcsr_save14: usize, // SAVE14 + pub gcsr_save15: usize, // SAVE15 + + // Timer Registers + pub gcsr_tid: usize, // TID + pub gcsr_tcfg: usize, // TCFG + pub gcsr_tval: usize, // TVAL + pub gcsr_cntc: usize, // CNTC + pub gcsr_ticlr: usize, // TICLR + + // Load Linked Buffers Registers + pub gcsr_llbctl: usize, // LLBCTL + + // TLB Read Entry Registers + pub gcsr_tlbrentry: usize, // TLBRENTRY + pub gcsr_tlbrbadv: usize, // TLBRBADV + pub gcsr_tlbrera: usize, // TLBRERA + pub gcsr_tlbrsave: usize, // TLBRSAVE + pub gcsr_tlbrelo0: usize, // TLBRELO0 + pub gcsr_tlbrelo1: usize, // TLBRELO1 + pub gcsr_tlbrehi: usize, // TLBREHI + pub gcsr_tlbrprmd: usize, // TLBRPRMD + + // Data Memory Write Registers + pub gcsr_dmw0: usize, // DMW0 + pub gcsr_dmw1: usize, // DMW1 + pub gcsr_dmw2: usize, // DMW2 + pub gcsr_dmw3: usize, // DMW3 + + // Pagetable address + pub pgdl: usize, + pub pgdh: usize, +} + +macro_rules! gprs_getters { + ($($reg_name:ident, $index:expr),*) => { + $( + pub fn $reg_name(&self) -> usize { + self.x[$index] + } + )* + } +} + +macro_rules! gprs_setters { + ($($set_name:ident, $index:expr),*) => { + $( + pub fn $set_name(&mut self, val: usize) { + self.x[$index] = val; + } + )* + } +} + +impl LoongArch64ZoneContext { + pub const fn new() -> LoongArch64ZoneContext { + LoongArch64ZoneContext { + x: [0; 32], + sepc: 0, + gcsr_crmd: 0, + gcsr_prmd: 0, + gcsr_euen: 0, + gcsr_misc: 0, + gcsr_ectl: 0, + gcsr_estat: 0, + gcsr_era: 0, + gcsr_badv: 0, + gcsr_badi: 0, + gcsr_eentry: 0, + gcsr_tlbidx: 0, + gcsr_tlbehi: 0, + gcsr_tlbelo0: 0, + gcsr_tlbelo1: 0, + gcsr_asid: 0, + gcsr_pgdl: 0, + gcsr_pgdh: 0, + gcsr_pgd: 0, + gcsr_pwcl: 0, + gcsr_pwch: 0, + gcsr_stlbps: 0, + gcsr_ravcfg: 0, + gcsr_cpuid: 0, + gcsr_prcfg1: 0, + gcsr_prcfg2: 0, + gcsr_prcfg3: 0, + gcsr_save0: 0, + gcsr_save1: 0, + gcsr_save2: 0, + gcsr_save3: 0, + gcsr_save4: 0, + gcsr_save5: 0, + gcsr_save6: 0, + gcsr_save7: 0, + gcsr_save8: 0, + gcsr_save9: 0, + gcsr_save10: 0, + gcsr_save11: 0, + gcsr_save12: 0, + gcsr_save13: 0, + gcsr_save14: 0, + gcsr_save15: 0, + gcsr_tid: 0, + gcsr_tcfg: 0, + gcsr_tval: 0, + gcsr_cntc: 0, + gcsr_ticlr: 0, + gcsr_llbctl: 0, + gcsr_tlbrentry: 0, + gcsr_tlbrbadv: 0, + gcsr_tlbrera: 0, + gcsr_tlbrsave: 0, + gcsr_tlbrelo0: 0, + gcsr_tlbrelo1: 0, + gcsr_tlbrehi: 0, + gcsr_tlbrprmd: 0, + gcsr_dmw0: 0, + gcsr_dmw1: 0, + gcsr_dmw2: 0, + gcsr_dmw3: 0, + // pagetable of zone + pgdl: 0, + pgdh: 0, + } + } + + pub fn print_zone_context(&self) { + info!("=============ZONE CONTEXT============"); + // get self addr in memory + let self_addr = self as *const _ as usize; + info!("self addr: {:#x}", self_addr); + for (index, ®ister) in self.x.iter().enumerate() { + info!("$r[{}]: {:#x}", index, register); + } + info!("sepc: {:#x}", self.sepc); + info!("gcsr_crmd: {:#x}", self.gcsr_crmd); + info!("gcsr_prmd: {:#x}", self.gcsr_prmd); + info!("gcsr_euen: {:#x}", self.gcsr_euen); + info!("gcsr_misc: {:#x}", self.gcsr_misc); + info!("gcsr_ectl: {:#x}", self.gcsr_ectl); + info!("gcsr_estat: {:#x}", self.gcsr_estat); + info!("gcsr_era: {:#x}", self.gcsr_era); + info!("gcsr_badv: {:#x}", self.gcsr_badv); + info!("gcsr_badi: {:#x}", self.gcsr_badi); + info!("gcsr_eentry: {:#x}", self.gcsr_eentry); + info!("gcsr_tlbidx: {:#x}", self.gcsr_tlbidx); + info!("gcsr_tlbehi: {:#x}", self.gcsr_tlbehi); + info!("gcsr_tlbelo0: {:#x}", self.gcsr_tlbelo0); + info!("gcsr_tlbelo1: {:#x}", self.gcsr_tlbelo1); + info!("gcsr_asid: {:#x}", self.gcsr_asid); + info!("gcsr_pgdl: {:#x}", self.gcsr_pgdl); + info!("gcsr_pgdh: {:#x}", self.gcsr_pgdh); + info!("gcsr_pgd: {:#x}", self.gcsr_pgd); + info!("gcsr_pwcl: {:#x}", self.gcsr_pwcl); + info!("gcsr_pwch: {:#x}", self.gcsr_pwch); + info!("gcsr_stlbps: {:#x}", self.gcsr_stlbps); + info!("gcsr_ravcfg: {:#x}", self.gcsr_ravcfg); + info!("gcsr_cpuid: {:#x}", self.gcsr_cpuid); + info!("gcsr_prcfg1: {:#x}", self.gcsr_prcfg1); + info!("gcsr_prcfg2: {:#x}", self.gcsr_prcfg2); + info!("gcsr_prcfg3: {:#x}", self.gcsr_prcfg3); + info!("gcsr_save0: {:#x}", self.gcsr_save0); + info!("gcsr_save1: {:#x}", self.gcsr_save1); + info!("gcsr_save2: {:#x}", self.gcsr_save2); + info!("gcsr_save3: {:#x}", self.gcsr_save3); + info!("gcsr_save4: {:#x}", self.gcsr_save4); + info!("gcsr_save5: {:#x}", self.gcsr_save5); + info!("gcsr_save6: {:#x}", self.gcsr_save6); + info!("gcsr_save7: {:#x}", self.gcsr_save7); + info!("gcsr_save8: {:#x}", self.gcsr_save8); + info!("gcsr_save9: {:#x}", self.gcsr_save9); + info!("gcsr_save10: {:#x}", self.gcsr_save10); + info!("gcsr_save11: {:#x}", self.gcsr_save11); + info!("gcsr_save12: {:#x}", self.gcsr_save12); + info!("gcsr_save13: {:#x}", self.gcsr_save13); + info!("gcsr_save14: {:#x}", self.gcsr_save14); + info!("gcsr_save15: {:#x}", self.gcsr_save15); + info!("gcsr_tid: {:#x}", self.gcsr_tid); + info!("gcsr_tcfg: {:#x}", self.gcsr_tcfg); + info!("gcsr_tval: {:#x}", self.gcsr_tval); + info!("gcsr_cntc: {:#x}", self.gcsr_cntc); + info!("gcsr_ticlr: {:#x}", self.gcsr_ticlr); + info!("gcsr_llbctl: {:#x}", self.gcsr_llbctl); + info!("gcsr_tlbrentry: {:#x}", self.gcsr_tlbrentry); + info!("gcsr_tlbrbadv: {:#x}", self.gcsr_tlbrbadv); + info!("gcsr_tlbrera: {:#x}", self.gcsr_tlbrera); + info!("gcsr_tlbrsave: {:#x}", self.gcsr_tlbrsave); + info!("gcsr_tlbrelo0: {:#x}", self.gcsr_tlbrelo0); + info!("gcsr_tlbrelo1: {:#x}", self.gcsr_tlbrelo1); + info!("gcsr_tlbrehi: {:#x}", self.gcsr_tlbrehi); + info!("gcsr_tlbrprmd: {:#x}", self.gcsr_tlbrprmd); + info!("gcsr_dmw0: {:#x}", self.gcsr_dmw0); + info!("gcsr_dmw1: {:#x}", self.gcsr_dmw1); + info!("gcsr_dmw2: {:#x}", self.gcsr_dmw2); + info!("gcsr_dmw3: {:#x}", self.gcsr_dmw3); + info!("pgdl: {:#x}", self.pgdl); + info!("pgdh: {:#x}", self.pgdh); + } + + gprs_getters!( + get_ra, 1, get_a0, 4, get_a1, 5, get_a2, 6, get_a3, 7, get_a4, 8, get_a5, 9, get_a6, 10, + get_a7, 11 + ); + gprs_setters!(set_a0, 4); +} + +pub type ZoneContext = LoongArch64ZoneContext; + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct HvArchZoneConfig { + pub dummy: usize, +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index b6243522..4904a4ee 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -4,8 +4,15 @@ pub mod aarch64; #[cfg(target_arch = "riscv64")] pub mod riscv64; +#[cfg(target_arch = "loongarch64")] +pub mod loongarch64; + +// export modules for external use #[cfg(target_arch = "aarch64")] pub use aarch64::*; #[cfg(target_arch = "riscv64")] pub use riscv64::*; + +#[cfg(target_arch = "loongarch64")] +pub use loongarch64::*; diff --git a/src/arch/riscv64/cpu.rs b/src/arch/riscv64/cpu.rs index ce59d484..6ce27966 100644 --- a/src/arch/riscv64/cpu.rs +++ b/src/arch/riscv64/cpu.rs @@ -1,7 +1,14 @@ use super::csr::*; +use crate::arch::Stage2PageTable; +use crate::percpu::this_cpu_data; use crate::{ - consts::{PER_CPU_ARRAY_PTR, PER_CPU_SIZE}, - memory::{PhysAddr, VirtAddr}, + arch::mm::new_s2_memory_set, + consts::{PAGE_SIZE, PER_CPU_ARRAY_PTR, PER_CPU_SIZE}, + memory::PhysAddr, + memory::{ + addr::PHYS_VIRT_OFFSET, mm::PARKING_MEMORY_SET, GuestPhysAddr, HostPhysAddr, MemFlags, + MemoryRegion, MemorySet, VirtAddr, PARKING_INST_PAGE, + }, }; #[repr(C)] @@ -13,18 +20,27 @@ pub struct ArchCpu { pub sepc: usize, pub stack_top: usize, pub cpuid: usize, + // pub first_cpu: usize, + pub power_on: bool, + pub init: bool, + pub sstc: bool, } impl ArchCpu { pub fn new(cpuid: usize) -> Self { - ArchCpu { + let ret = ArchCpu { x: [0; 32], hstatus: 0, sstatus: 0, sepc: 0, stack_top: 0, cpuid, - } + // first_cpu: 0, + power_on: false, + init: false, + sstc: false, + }; + ret } pub fn get_cpuid(&self) -> usize { self.cpuid @@ -36,12 +52,19 @@ impl ArchCpu { //self.sepc = guest_test as usize as u64; write_csr!(CSR_SSCRATCH, self as *const _ as usize); //arch cpu pointer self.sepc = entry; - self.hstatus = 1 << 7 | 2 << 32; //HSTATUS_SPV | HSTATUS_VSXL_64 + #[cfg(feature = "plic")] + { + self.hstatus = 1 << 7 | 2 << 32; //HSTATUS_SPV | HSTATUS_VSXL_64 + } + #[cfg(feature = "aia")] + { + self.hstatus = 1 << 7 | 2 << 32 | 1 << 12; //HSTATUS_SPV | HSTATUS_VSXL_64 | HSTATUS_VGEIN + } self.sstatus = 1 << 8 | 1 << 63 | 3 << 13 | 3 << 15; //SPP self.stack_top = self.stack_top() as usize; self.x[10] = cpu_id; //cpu id self.x[11] = dtb; //dtb addr - trace!("stack_top: {:#x}", self.stack_top); + // trace!("stack_top: {:#x}", self.stack_top); // write_csr!(CSR_SSTATUS, self.sstatus); // write_csr!(CSR_HSTATUS, self.hstatus); @@ -66,34 +89,86 @@ impl ArchCpu { write_csr!(CSR_VSTVAL, 0); write_csr!(CSR_HVIP, 0); write_csr!(CSR_VSATP, 0); - let mut value: usize; - value = read_csr!(CSR_SEPC); - info!("CSR_SEPC: {:#x}", value); - value = read_csr!(CSR_STVEC); - info!("CSR_STVEC: {:#x}", value); - value = read_csr!(CSR_VSATP); - info!("CSR_VSATP: {:#x}", value); - value = read_csr!(CSR_HGATP); - info!("CSR_HGATP: {:#x}", value); + // let mut value: usize; + // value = read_csr!(CSR_SEPC); + // info!("CSR_SEPC: {:#x}", value); + // value = read_csr!(CSR_STVEC); + // info!("CSR_STVEC: {:#x}", value); + // value = read_csr!(CSR_VSATP); + // info!("CSR_VSATP: {:#x}", value); + // value = read_csr!(CSR_HGATP); + // info!("CSR_HGATP: {:#x}", value); //unreachable!(); } - pub fn run(&mut self) { + pub fn run(&mut self) -> ! { extern "C" { - fn vcpu_arch_entry(); + fn vcpu_arch_entry() -> !; } + + assert!(this_cpu_id() == self.cpuid); + //change power_on + this_cpu_data().activate_gpm(); + if !self.init { + self.init( + this_cpu_data().cpu_on_entry, + this_cpu_data().id, + this_cpu_data().dtb_ipa, //dtb_ipa + ); + self.init = true; + } + + self.power_on = true; + info!("CPU{} run@{:#x}", self.cpuid, self.sepc); + info!("CPU{:#x?}", self); unsafe { vcpu_arch_entry(); } } - pub fn idle(&self) { + pub fn idle(&mut self) -> ! { + extern "C" { + fn vcpu_arch_entry() -> !; + } + assert!(this_cpu_id() == self.cpuid); + self.init(0, this_cpu_data().id, this_cpu_data().dtb_ipa); + // reset current cpu -> pc = 0x0 (wfi) + PARKING_MEMORY_SET.call_once(|| { + let parking_code: [u8; 4] = [0x73, 0x00, 0x50, 0x10]; // 1: wfi; b 1b + unsafe { + PARKING_INST_PAGE[..4].copy_from_slice(&parking_code); + } + + let mut gpm = new_s2_memory_set(); + gpm.insert(MemoryRegion::new_with_offset_mapper( + 0 as GuestPhysAddr, + unsafe { &PARKING_INST_PAGE as *const _ as HostPhysAddr - PHYS_VIRT_OFFSET }, + PAGE_SIZE, + MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, + )) + .unwrap(); + gpm + }); unsafe { - core::arch::asm!("wfi"); + PARKING_MEMORY_SET.get().unwrap().activate(); + vcpu_arch_entry(); } - println!("CPU{} weakup!", self.cpuid); - debug!("sip: {:#x}", read_csr!(CSR_SIP)); - clear_csr!(CSR_SIP, 1 << 1); - debug!("sip*: {:#x}", read_csr!(CSR_SIP)); + // info!("CPU{} sleep...", self.cpuid); + // info!("CPU{:#x?}", self); + // unsafe { + // core::arch::asm!("wfi"); + // } + // //according to riscv priv spec, after wfi, interrupt trap will be taken on the following instruction,then excute the code after wfi. + // //but in qemu, it seems that the interrupt trap will be taken after sret in vcpu_arch_entry(). + // //this may cause error in hardware. + // info!("CPU{} wakeup!", self.cpuid); + // debug!("sip: {:#x}", read_csr!(CSR_SIP)); + // // clear_csr!(CSR_SIP, 1 << 1); + // debug!("sip*: {:#x}", read_csr!(CSR_SIP)); + // self.init = true; + + // unsafe { + // vcpu_arch_entry(); + // } } } @@ -109,11 +184,8 @@ pub fn this_cpu_id() -> usize { this_cpu_arch().get_cpuid() } -const HV_BASE: VirtAddr = 0x80200000; -const HV_PHY_BASE: PhysAddr = 0x80200000; - pub fn cpu_start(cpuid: usize, start_addr: usize, opaque: usize) { - if let Some(e) = sbi_rt::hart_start(cpuid, HV_PHY_BASE, opaque).err() { + if let Some(e) = sbi_rt::hart_start(cpuid, start_addr, opaque).err() { panic!("cpu_start error: {:#x?}", e); } } diff --git a/src/arch/riscv64/entry.rs b/src/arch/riscv64/entry.rs index a6005242..b69d2076 100644 --- a/src/arch/riscv64/entry.rs +++ b/src/arch/riscv64/entry.rs @@ -1,5 +1,4 @@ use crate::consts::PER_CPU_SIZE; -use core::arch::global_asm; #[naked] #[no_mangle] diff --git a/src/arch/riscv64/ipi.rs b/src/arch/riscv64/ipi.rs new file mode 100644 index 00000000..b7c1a544 --- /dev/null +++ b/src/arch/riscv64/ipi.rs @@ -0,0 +1,3 @@ +pub fn arch_send_event(cpu_id: u64, _sgi_num: u64) { + sbi_rt::send_ipi(1 << cpu_id, 0); +} diff --git a/src/arch/riscv64/mm.rs b/src/arch/riscv64/mm.rs index 258fdbd1..776e59d1 100644 --- a/src/arch/riscv64/mm.rs +++ b/src/arch/riscv64/mm.rs @@ -1,122 +1,127 @@ use spin::RwLock; use crate::{ - arch::s1pt::Stage1PageTable, + arch::Stage2PageTable, error::HvResult, memory::{ addr::align_up, GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion, MemorySet, HV_PT, }, }; -pub fn init_hv_page_table(fdt: &fdt::Fdt) -> HvResult { - let mut hv_pt: MemorySet = MemorySet::new(); - // let _ = hv_pt.insert(MemoryRegion::new_with_offset_mapper( - // 0x8000_0000 as HostVirtAddr, - // hv_phys_start as HostPhysAddr, - // (hv_phys_end - hv_phys_start) as usize, +pub fn init_hv_page_table() -> HvResult { + todo!(); + // let mut hv_pt: MemorySet = MemorySet::new(); + // // let _ = hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // // 0x8000_0000 as HostVirtAddr, + // // hv_phys_start as HostPhysAddr, + // // (hv_phys_end - hv_phys_start) as usize, + // // MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, + // // )); + // trace!("fdt: {:?}", fdt); + // // The first memory region is used to map the guest physical memory. + // let mem_region = fdt.memory().regions().next().unwrap(); + // debug!("map mem_region: {:#x?}", mem_region); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // mem_region.starting_address as GuestPhysAddr, + // mem_region.starting_address as HostPhysAddr, + // mem_region.size.unwrap(), // MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, - // )); - trace!("fdt: {:?}", fdt); - // The first memory region is used to map the guest physical memory. - let mem_region = fdt.memory().regions().next().unwrap(); - debug!("map mem_region: {:#x?}", mem_region); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - mem_region.starting_address as GuestPhysAddr, - mem_region.starting_address as HostPhysAddr, - mem_region.size.unwrap(), - MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, - ))?; - // probe virtio mmio device - for node in fdt.find_all_nodes("/soc/virtio_mmio") { - if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { - let paddr = reg.starting_address as HostPhysAddr; - let size = reg.size.unwrap(); - debug!("map virtio mmio addr: {:#x}, size: {:#x}", paddr, size); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - paddr as GuestPhysAddr, - paddr, - size, - MemFlags::READ | MemFlags::WRITE, - ))?; - } - } + // ))?; + // // probe virtio mmio device + // for node in fdt.find_all_nodes("/soc/virtio_mmio") { + // if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + // let paddr = reg.starting_address as HostPhysAddr; + // let size = reg.size.unwrap(); + // debug!("map virtio mmio addr: {:#x}, size: {:#x}", paddr, size); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // paddr as GuestPhysAddr, + // paddr, + // size, + // MemFlags::READ | MemFlags::WRITE, + // ))?; + // } + // } - // probe virt test - for node in fdt.find_all_nodes("/soc/test") { - if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { - let paddr = reg.starting_address as HostPhysAddr; - let size = reg.size.unwrap() + 0x1000; - debug!("map test addr: {:#x}, size: {:#x}", paddr, size); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - paddr as GuestPhysAddr, - paddr, - size, - MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, - ))?; - } - } + // // probe virt test + // for node in fdt.find_all_nodes("/soc/test") { + // if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + // let paddr = reg.starting_address as HostPhysAddr; + // let size = reg.size.unwrap() + 0x1000; + // debug!("map test addr: {:#x}, size: {:#x}", paddr, size); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // paddr as GuestPhysAddr, + // paddr, + // size, + // MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE, + // ))?; + // } + // } - // probe uart device - for node in fdt.find_all_nodes("/soc/uart") { - if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { - let paddr = reg.starting_address as HostPhysAddr; - let size = align_up(reg.size.unwrap()); - debug!("map uart addr: {:#x}, size: {:#x}", paddr, size); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - paddr as GuestPhysAddr, - paddr, - size, - MemFlags::READ | MemFlags::WRITE, - ))?; - } - } + // // probe uart device + // for node in fdt.find_all_nodes("/soc/uart") { + // if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + // let paddr = reg.starting_address as HostPhysAddr; + // let size = align_up(reg.size.unwrap()); + // debug!("map uart addr: {:#x}, size: {:#x}", paddr, size); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // paddr as GuestPhysAddr, + // paddr, + // size, + // MemFlags::READ | MemFlags::WRITE, + // ))?; + // } + // } - // probe clint(core local interrupter) - for node in fdt.find_all_nodes("/soc/clint") { - if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { - let paddr = reg.starting_address as HostPhysAddr; - let size = reg.size.unwrap(); - debug!("map clint addr: {:#x}, size: {:#x}", paddr, size); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - paddr as GuestPhysAddr, - paddr, - size, - MemFlags::READ | MemFlags::WRITE, - ))?; - } - } + // // probe clint(core local interrupter) + // for node in fdt.find_all_nodes("/soc/clint") { + // if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + // let paddr = reg.starting_address as HostPhysAddr; + // let size = reg.size.unwrap(); + // debug!("map clint addr: {:#x}, size: {:#x}", paddr, size); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // paddr as GuestPhysAddr, + // paddr, + // size, + // MemFlags::READ | MemFlags::WRITE, + // ))?; + // } + // } - // probe plic - for node in fdt.find_all_nodes("/soc/plic") { - if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { - let paddr = reg.starting_address as HostPhysAddr; - let size = reg.size.unwrap(); - debug!("map plic addr: {:#x}, size: {:#x}", paddr, size); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - paddr as GuestPhysAddr, - paddr, - size, - MemFlags::READ | MemFlags::WRITE, - ))?; - } - } + // // probe plic + // for node in fdt.find_all_nodes("/soc/plic") { + // if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + // let paddr = reg.starting_address as HostPhysAddr; + // let size = reg.size.unwrap(); + // debug!("map plic addr: {:#x}, size: {:#x}", paddr, size); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // paddr as GuestPhysAddr, + // paddr, + // size, + // MemFlags::READ | MemFlags::WRITE, + // ))?; + // } + // } - for node in fdt.find_all_nodes("/soc/pci") { - if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { - let paddr = reg.starting_address as HostPhysAddr; - let size = reg.size.unwrap(); - debug!("map pci addr: {:#x}, size: {:#x}", paddr, size); - hv_pt.insert(MemoryRegion::new_with_offset_mapper( - paddr as GuestPhysAddr, - paddr, - size, - MemFlags::READ | MemFlags::WRITE, - ))?; - } - } - info!("Hypervisor page table init end."); - debug!("Hypervisor virtual memory set: {:#x?}", hv_pt); + // for node in fdt.find_all_nodes("/soc/pci") { + // if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { + // let paddr = reg.starting_address as HostPhysAddr; + // let size = reg.size.unwrap(); + // debug!("map pci addr: {:#x}, size: {:#x}", paddr, size); + // hv_pt.insert(MemoryRegion::new_with_offset_mapper( + // paddr as GuestPhysAddr, + // paddr, + // size, + // MemFlags::READ | MemFlags::WRITE, + // ))?; + // } + // } + // info!("Hypervisor page table init end."); + // debug!("Hypervisor virtual memory set: {:#x?}", hv_pt); - HV_PT.call_once(|| RwLock::new(hv_pt)); - Ok(()) + // HV_PT.call_once(|| RwLock::new(hv_pt)); + // Ok(()) +} + +pub fn new_s2_memory_set() -> MemorySet { + MemorySet::new(3) } diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index 768944b9..ca61b387 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -1,9 +1,14 @@ pub mod cpu; pub mod csr; pub mod entry; +pub mod ipi; pub mod mm; pub mod paging; pub mod s1pt; pub mod s2pt; pub mod sbi; pub mod trap; +pub mod zone; + +pub use s1pt::Stage1PageTable; +pub use s2pt::Stage2PageTable; diff --git a/src/arch/riscv64/paging.rs b/src/arch/riscv64/paging.rs index 55420c10..c8865a00 100644 --- a/src/arch/riscv64/paging.rs +++ b/src/arch/riscv64/paging.rs @@ -5,7 +5,7 @@ use spin::Mutex; use crate::error::{HvError, HvResult}; use crate::memory::addr::{is_aligned, phys_to_virt, virt_to_phys}; -use crate::memory::{Frame, MemFlags, MemoryRegion, PhysAddr, VirtAddr, TEMPORARY_MAPPING_BASE}; +use crate::memory::{Frame, MemFlags, MemoryRegion, PhysAddr, VirtAddr}; #[derive(Debug)] pub enum PagingError { @@ -52,7 +52,7 @@ impl PageSize { pub const fn page_offset(self, addr: usize) -> usize { addr & (self as usize - 1) } - + #[allow(unused)] pub const fn is_huge(self) -> bool { matches!(self, Self::Size1G | Self::Size2M) } @@ -316,8 +316,7 @@ where mut flags: MemFlags, ) -> PagingResult<&mut PTE> { let entry: &mut PTE = self.get_entry_mut_or_create(page, &mut flags)?; - if !entry.is_unused() && page.vaddr.into() != TEMPORARY_MAPPING_BASE { - error!("AlreadyMapped"); + if !entry.is_unused() { return Err(PagingError::AlreadyMapped); } entry.set_addr(page.size.align_down(paddr)); @@ -559,7 +558,7 @@ fn next_table_mut_or_create<'a, E: GenericPTE>( next_table_mut(entry) } } - +#[allow(unused)] pub fn npages(sz: usize) -> usize { if sz & 0xfff == 0 { sz >> 12 diff --git a/src/arch/riscv64/sbi.rs b/src/arch/riscv64/sbi.rs index 86e8308a..2ebcaa62 100644 --- a/src/arch/riscv64/sbi.rs +++ b/src/arch/riscv64/sbi.rs @@ -1,15 +1,34 @@ -#![allow(dead_code)] +//! SBI call wrappers -//use crate::arch::riscv::csr::*; +#![allow(unused)] +use crate::hypercall::HyperCall; +use crate::percpu::{get_cpu_data, this_cpu_data}; + +use super::cpu::ArchCpu; +use crate::arch::csr::*; +use crate::event::{send_event, IPI_EVENT_WAKEUP}; use riscv::register::{hvip, sie}; +#[allow(non_snake_case)] pub mod SBI_EID { + pub const BASE_EXTID: usize = 0x10; pub const SET_TIMER: usize = 0x54494D45; pub const EXTID_HSM: usize = 0x48534D; pub const SEND_IPI: usize = 0x735049; + pub const RFENCE: usize = 0x52464E43; + pub const PMU: usize = 0x504D55; + pub const HVISOR: usize = 0x114514; +} +pub const SBI_SUCCESS: i64 = 0; +pub const SBI_ERR_FAILURE: i64 = -1; +pub const SBI_ERR_NOT_SUPPORTED: i64 = -2; +pub const SBI_ERR_INVALID_PARAM: i64 = -3; +pub const SBI_ERR_DENIED: i64 = -4; +pub const SBI_ERR_INVALID_ADDRESS: i64 = -5; +pub const SBI_ERR_ALREADY_AVAILABLE: i64 = -6; +pub struct SbiRet { + error: i64, + value: i64, } - -use super::cpu::ArchCpu; - /// use sbi call to putchar in console (qemu uart handler) pub fn console_putchar(c: u8) { #[allow(deprecated)] @@ -40,21 +59,99 @@ pub fn shutdown(failure: bool) -> ! { } unreachable!() } + pub fn sbi_vs_handler(current_cpu: &mut ArchCpu) { - let ret = sbi_call_5( - current_cpu.x[17], - current_cpu.x[16], - current_cpu.x[10], - current_cpu.x[11], - current_cpu.x[12], - current_cpu.x[13], - current_cpu.x[14], - ); - current_cpu.sepc += 4; - current_cpu.x[10] = ret.0; - current_cpu.x[11] = ret.1; - trace!("sbi_call_5: error:{:#x}, value:{:#x}", ret.0, ret.1); + let eid: usize = current_cpu.x[17]; + let fid: usize = current_cpu.x[16]; + let sbi_ret; + match eid { + //SBI_EXTID_BASE => sbi_ret = sbi_base_handler(fid, current_cpu), + SBI_EID::BASE_EXTID => { + trace!("SBI_EID::BASE,fid:{:#x}", fid); + sbi_ret = sbi_call_5( + eid, + fid, + current_cpu.x[10], + current_cpu.x[11], + current_cpu.x[12], + current_cpu.x[13], + current_cpu.x[14], + ); + } + SBI_EID::SET_TIMER => { + //debug!("SBI_EID::SET_TIMER on CPU {}", current_cpu.cpuid); + sbi_ret = sbi_time_handler(fid, current_cpu); + } + SBI_EID::EXTID_HSM => { + info!("SBI_EID::EXTID_HSM on CPU {}", current_cpu.cpuid); + sbi_ret = sbi_hsm_handler(fid, current_cpu); + } + SBI_EID::SEND_IPI => { + trace!("SBI_EID::SEND_IPI on CPU {}", current_cpu.cpuid); + trace!( + "SBI_EID::SEND_IPI,cpuid:{:#x},mask:{:#x}", + current_cpu.x[10], + current_cpu.x[11] + ); + sbi_ret = sbi_call_5( + eid, + fid, + current_cpu.x[10], + current_cpu.x[11], + current_cpu.x[12], + current_cpu.x[13], + current_cpu.x[14], + ); + } + SBI_EID::RFENCE => { + trace!("SBI_EID::RFENCE,mask:{:#x}", current_cpu.x[10]); + sbi_ret = sbi_call_5( + eid, + fid, + current_cpu.x[10], + current_cpu.x[11], + current_cpu.x[12], + current_cpu.x[13], + current_cpu.x[14], + ); + } + SBI_EID::PMU => { + trace!("SBI_EID::PMU,fid:{:#x}", fid); + sbi_ret = sbi_call_5( + eid, + fid, + current_cpu.x[10], + current_cpu.x[11], + current_cpu.x[12], + current_cpu.x[13], + current_cpu.x[14], + ); + } + SBI_EID::HVISOR => { + trace!("SBI_EID::HVISOR,fid:{:#x}", fid); + sbi_ret = sbi_hvisor_handler(current_cpu); + } + //_ => sbi_ret = sbi_dummy_handler(), + _ => { + warn!( + "Pass through SBI call eid {:#x} fid:{:#x} on CPU {}", + eid, fid, current_cpu.cpuid + ); + sbi_ret = sbi_call_5( + eid, + fid, + current_cpu.x[10], + current_cpu.x[11], + current_cpu.x[12], + current_cpu.x[13], + current_cpu.x[14], + ); + } + } + current_cpu.x[10] = sbi_ret.error as usize; + current_cpu.x[11] = sbi_ret.value as usize; } + pub fn sbi_call_5( eid: usize, fid: usize, @@ -63,27 +160,7 @@ pub fn sbi_call_5( arg2: usize, arg3: usize, arg4: usize, -) -> (usize, usize) { - trace!("sbi_call_5: eid:{:#x}, fid:{:#x}", eid, fid); - match eid { - SBI_EID::SET_TIMER => { - { - info!("VS set timer"); - // write_csr!(CSR_HVIP, 0); //VSTIP - // write_csr!(CSR_SIE, 1 << 9 | 1 << 5 | 1 << 1); - set_timer(arg0); - unsafe { - // clear guest timer interrupt pending - hvip::clear_vstip(); - // enable timer interrupt - sie::set_stimer(); - } - return (0, 0); - } - } - //_ => sbi_ret = sbi_dummy_handler(), - _ => debug!("Pass through SBI call eid {:#x} fid:{:#x}", eid, fid), - } +) -> SbiRet { let (error, value); unsafe { core::arch::asm!( @@ -97,181 +174,94 @@ pub fn sbi_call_5( in("a4") arg4, ); } - (error, value) + SbiRet { error, value } } -//other -pub const SBI_CONSOLE_PUTCHAR: usize = 1; -pub const SBI_CONSOLE_GETCHAR: usize = 2; - -pub const SBI_SET_TIMER: usize = 0; - -pub const SBI_SUCCESS: usize = 0; -pub const SBI_ERR_FAILUER: isize = -1; -pub const SBI_ERR_NOT_SUPPORTED: isize = -2; -pub const SBI_ERR_INAVLID_PARAM: isize = -3; -pub const SBI_ERR_DENIED: isize = -4; -pub const SBI_ERR_INVALID_ADDRESS: isize = -5; -pub const SBI_ERR_ALREADY_AVAILABLE: isize = -6; - -pub const SBI_EXTID_BASE: usize = 0x10; -pub const SBI_GET_SBI_SPEC_VERSION_FID: usize = 0; -pub const SBI_GET_SBI_IMPL_ID_FID: usize = 1; -pub const SBI_GET_SBI_IMPL_VERSION_FID: usize = 2; -pub const SBI_PROBE_EXTENSION_FID: usize = 3; -pub const SBI_GET_MVENDORID_FID: usize = 4; -pub const SBI_GET_MARCHID_FID: usize = 5; -pub const SBI_GET_MIMPID_FID: usize = 6; - -pub const SBI_EXTID_TIME: usize = 0x54494D45; -pub const SBI_SET_TIMER_FID: usize = 0x0; - -pub const SBI_EXTID_IPI: usize = 0x735049; -pub const SBI_SEND_IPI_FID: usize = 0x0; - -pub const SBI_EXTID_HSM: usize = 0x48534D; -pub const SBI_HART_START_FID: usize = 0; -pub const SBI_HART_STOP_FID: usize = 1; -pub const SBI_HART_STATUS_FID: usize = 2; - -pub const SBI_EXTID_RFNC: usize = 0x52464E43; -pub const SBI_REMOTE_FENCE_I_FID: usize = 0; -pub const SBI_REMOTE_SFENCE_VMA_FID: usize = 1; -pub const SBI_REMOTE_SFENCE_VMA_ASID_FID: usize = 2; -pub const SBI_REMOTE_HFENCE_GVMA_FID: usize = 3; -pub const SBI_REMOTE_HFENCE_GVMA_VMID_FID: usize = 4; -pub const SBI_REMOTE_HFENCE_VVMA_FIDL: usize = 5; -pub const SBI_REMOTE_HFENCE_VVMA_ASID_FID: usize = 6; -pub struct SbiRet { - error: usize, - value: usize, +pub fn sbi_time_handler(fid: usize, current_cpu: &mut ArchCpu) -> SbiRet { + let mut sbi_ret = SbiRet { + error: SBI_SUCCESS, + value: 0, + }; + let stime = current_cpu.x[10]; + warn!("SBI_SET_TIMER stime: {:#x}", stime); + if current_cpu.sstc { + write_csr!(CSR_VSTIMECMP, stime); + } else { + set_timer(stime); + unsafe { + // clear guest timer interrupt pending + hvip::clear_vstip(); + // enable timer interrupt + sie::set_stimer(); + } + } + //debug!("SBI_SET_TIMER stime: {:#x}", stime); + return sbi_ret; } - -#[inline(always)] -pub(crate) fn sbi_call_1(eid: usize, fid: usize, arg0: usize) -> SbiRet { - let (error, value); - unsafe { - core::arch::asm!( - "ecall", - in("a7") eid, - in("a6") fid, - inlateout("a0") arg0 => error, - lateout("a1") value, - ); +pub fn sbi_hsm_handler(fid: usize, current_cpu: &mut ArchCpu) -> SbiRet { + let mut sbi_ret = SbiRet { + error: SBI_SUCCESS, + value: 0, + }; + match fid { + 0 => { + // hsm start + sbi_ret = sbi_hsm_start_handler(current_cpu); + } + _ => { + error!("Unsupported HSM function {:#x}", fid); + } } - SbiRet { error, value } + sbi_ret } +pub fn sbi_hsm_start_handler(current_cpu: &mut ArchCpu) -> SbiRet { + let mut sbi_ret = SbiRet { + error: SBI_SUCCESS, + value: 0, + }; + let cpuid = current_cpu.x[10]; -// pub fn sbi_vs_handler(current_cpu: &mut ArchCpu) { -// let ext_id: usize = current_cpu.x[17]; -// let fid: usize = current_cpu.x[16]; -// let sbi_ret; - -// match ext_id { -// SBI_EXTID_BASE => sbi_ret = sbi_base_handler(fid, current_cpu), -// SBI_EXTID_TIME => sbi_ret = sbi_time_handler(current_cpu.x[10], fid), -// SBI_CONSOLE_PUTCHAR => sbi_ret = sbi_console_putchar_handler(current_cpu.x[10]), -// SBI_CONSOLE_GETCHAR => sbi_ret = sbi_console_getchar_handler(), -// SBI_SET_TIMER => sbi_ret = sbi_legacy_set_time(current_cpu.x[10]), -// //_ => sbi_ret = sbi_dummy_handler(), -// _ => panic!("Unsupported SBI call id {:#x}", ext_id), -// } -// current_cpu.x[10] = sbi_ret.error; -// current_cpu.x[11] = sbi_ret.value; -// } - -// pub fn sbi_base_handler(fid: usize, current_cpu: &mut ArchCpu) -> SbiRet { -// let mut sbi_ret = SbiRet { -// error: SBI_SUCCESS, -// value: 0, -// }; -// match fid { -// SBI_GET_SBI_SPEC_VERSION_FID => { -// sbi_ret = sbi_call_1(SBI_EXTID_BASE, fid, 0); -// debug!("GetSepcificationVersion: {}", sbi_ret.value); -// } -// SBI_GET_SBI_IMPL_ID_FID => { -// sbi_ret.value = sbi_rt::get_sbi_impl_id(); -// debug!("GetImplementationId: {}", sbi_ret.value); -// } -// SBI_GET_SBI_IMPL_VERSION_FID => { -// sbi_ret.value = sbi_rt::get_sbi_impl_version(); -// debug!("GetImplementationVersion: {}", sbi_ret.value); -// } -// SBI_PROBE_EXTENSION_FID => { -// let extension = current_cpu.x[10]; -// sbi_ret = sbi_call_1(SBI_EXTID_BASE, fid, extension); -// debug!("ProbeExtension: {}", sbi_ret.value); -// } -// SBI_GET_MVENDORID_FID => { -// sbi_ret.value = sbi_rt::get_mvendorid(); -// debug!("GetVendorId: {}", sbi_ret.value); -// } -// SBI_GET_MARCHID_FID => { -// sbi_ret.value = sbi_rt::get_marchid(); -// debug!("GetArchId: {}", sbi_ret.value); -// } -// SBI_GET_MIMPID_FID => { -// sbi_ret.value = sbi_rt::get_mimpid(); -// debug!("GetMimpId: {}", sbi_ret.value); -// } -// _ => panic!("sbi base handler fid: {}", fid), -// } -// sbi_ret -// } - -// pub fn sbi_console_putchar_handler(c: usize) -> SbiRet { -// console_putchar(c); -// return SbiRet { -// error: SBI_SUCCESS, -// value: 0, -// }; -// } - -// pub fn sbi_console_getchar_handler() -> SbiRet { -// let c = console_getchar(); -// return SbiRet { -// error: SBI_SUCCESS, -// value: c, -// }; -// } - -// pub fn sbi_time_handler(stime: usize, fid: usize) -> SbiRet { -// let mut sbi_ret = SbiRet { -// error: SBI_SUCCESS, -// value: 0, -// }; -// if fid != SBI_SET_TIMER_FID { -// sbi_ret.error = SBI_ERR_NOT_SUPPORTED as usize; -// return sbi_ret; -// } - -// // debug!("set timer: {}", stime); -// set_timer(stime); -// unsafe { -// // clear guest timer interrupt pending -// hvip::clear_vstip(); -// // enable timer interrupt -// sie::set_stimer(); -// } -// return sbi_ret; -// } + if (cpuid == current_cpu.cpuid) { + sbi_ret.error = SBI_ERR_ALREADY_AVAILABLE; + } else { + //TODO:add sbi conext in archcpu + let cpuid = current_cpu.x[10]; + let start_addr = current_cpu.x[11]; + let opaque = current_cpu.x[12]; -// pub fn sbi_rfence_handler(fid: usize) { + info!("sbi: try to wake up cpu {} run@{:#x}", cpuid, start_addr); + let target_cpu = get_cpu_data(cpuid); + //todo add power_on check + let _lock = target_cpu.ctrl_lock.lock(); + target_cpu.cpu_on_entry = start_addr; + target_cpu.dtb_ipa = opaque; + send_event(cpuid, 0, IPI_EVENT_WAKEUP); -// } + drop(_lock); + } + sbi_ret +} +pub fn sbi_hvisor_handler(current_cpu: &mut ArchCpu) -> SbiRet { + let mut sbi_ret = SbiRet { + error: SBI_SUCCESS, + value: 0, + }; + let (code, arg0, arg1) = (current_cpu.x[10], current_cpu.x[11], current_cpu.x[12]); -// pub fn sbi_legacy_set_time(stime: usize) -> SbiRet { -// let sbi_ret = SbiRet { -// error: SBI_SUCCESS, -// value: 0, -// }; -// set_timer(stime); -// unsafe { -// // clear guest timer interrupt pending -// hvip::clear_vstip(); -// // enable timer interrupt -// sie::set_stimer(); -// } -// return sbi_ret; -// } + let cpu_data = this_cpu_data(); + info!( + "HVC from CPU{},code:{:#x?},arg0:{:#x?},arg1:{:#x?}", + cpu_data.id, code, arg0, arg1 + ); + let result = HyperCall::new(cpu_data).hypercall(code as _, arg0 as _, arg1 as _); + match result { + Ok(ret) => { + sbi_ret.value = ret as _; + } + Err(e) => { + sbi_ret.error = SBI_ERR_FAILURE; + sbi_ret.value = e.code() as _; + } + } + sbi_ret +} diff --git a/src/arch/riscv64/trap.rs b/src/arch/riscv64/trap.rs index 815c12e2..890dbb9c 100644 --- a/src/arch/riscv64/trap.rs +++ b/src/arch/riscv64/trap.rs @@ -2,46 +2,51 @@ use super::cpu::ArchCpu; use crate::arch::csr::read_csr; use crate::arch::csr::*; use crate::arch::sbi::sbi_vs_handler; +#[cfg(feature = "aia")] +use crate::device::irqchip::aia::aplic::{host_aplic, vaplic_emul_handler}; +#[cfg(feature = "plic")] +use crate::device::irqchip::plic::{host_plic, vplic_global_emul_handler, vplic_hart_emul_handler}; +use crate::event::check_events; use crate::memory::{GuestPhysAddr, HostPhysAddr}; +use crate::platform::qemu_riscv64::*; use core::arch::{asm, global_asm}; use riscv::register::mtvec::TrapMode; use riscv::register::stvec; +use riscv::register::{hvip, sie}; use riscv_decode::Instruction; - extern "C" { fn _hyp_trap_vector(); } global_asm!(include_str!("trap.S"), sync_exception_handler=sym sync_exception_handler, interrupts_arch_handle=sym interrupts_arch_handle); - +#[allow(non_snake_case)] pub mod ExceptionType { pub const ECALL_VU: usize = 8; pub const ECALL_VS: usize = 10; pub const LOAD_GUEST_PAGE_FAULT: usize = 21; pub const STORE_GUEST_PAGE_FAULT: usize = 23; } - +#[allow(non_snake_case)] pub mod InterruptType { pub const SSI: usize = 1; pub const STI: usize = 5; pub const SEI: usize = 9; } -pub fn init() { +pub fn install_trap_vector() { unsafe { // Set the trap vector. stvec::write(_hyp_trap_vector as usize, TrapMode::Direct); } } pub fn sync_exception_handler(current_cpu: &mut ArchCpu) { - trace!("sync_exception_handler"); trace!("current_cpu: stack{:#x}", current_cpu.stack_top); let trap_code = read_csr!(CSR_SCAUSE); trace!("CSR_SCAUSE: {}", trap_code); if (read_csr!(CSR_HSTATUS) & (1 << 7)) == 0 { //HSTATUS_SPV error!("exception from HS mode"); - unreachable!(); + //unreachable!(); } let trap_value = read_csr!(CSR_HTVAL); trace!("CSR_HTVAL: {:#x}", trap_value); @@ -76,50 +81,93 @@ pub fn sync_exception_handler(current_cpu: &mut ArchCpu) { let raw_inst = read_inst(trap_pc); let inst = riscv_decode::decode(raw_inst); warn!("trap ins: {:#x} {:?}", raw_inst, inst); - current_cpu.sepc += 4; - panic!("unhandled trap"); + // current_cpu.sepc += 4; + error!("unhandled trap"); current_cpu.idle(); } } } pub fn guest_page_fault_handler(current_cpu: &mut ArchCpu) { - todo!(); - // let addr: HostPhysAddr = read_csr!(CSR_HTVAL) << 2; - // trace!("guest page fault at {:#x}", addr); - // //TODO: get plic addr range from dtb or vpliv object - // if addr >= 0x0c00_0000 && addr < 0x1000_0000 { - // trace!("PLIC access"); - // let mut inst: u32 = read_csr!(CSR_HTINST) as u32; - // if inst == 0 { - // let inst_addr: GuestPhysAddr = current_cpu.sepc; - // //load real ins from guest memmory - // inst = read_inst(inst_addr); - // } else if inst == 0x3020 || inst == 0x3000 { - // // TODO: we should reinject this in the guest as a fault access - // error!("fault on 1st stage page table walk"); - // } else { - // // If htinst is valid and is not a pseudo instructon make sure - // // the opcode is valid even if it was a compressed instruction, - // // but before save the real instruction size. - // error!("unhandled guest page fault at {:#x}", addr); - // } - // //TODO: decode inst to real instruction - // let (len, inst) = decode_inst(inst); - // if let Some(inst) = inst { - // if addr >= host_plic().read().base + PLIC_GLOBAL_SIZE { - // vplic_hart_emul_handler(current_cpu, addr, inst); - // } - // vplic_global_emul_handler(current_cpu, addr, inst); - // current_cpu.sepc += len; - // } else { - // error!("Invalid instruction at {:#x}", current_cpu.sepc); - // } - // } else { - // panic!("unmaped memmory at {:#x}", addr); - // } + let addr: HostPhysAddr = read_csr!(CSR_HTVAL) << 2; + trace!("guest page fault at {:#x}", addr); + #[cfg(feature = "plic")] + { + let host_plic_base = host_plic().read().base; + let mut ins_size: usize = 0; + //TODO: get plic addr range from dtb or vpliv object + if addr >= host_plic_base && addr < host_plic_base + PLIC_TOTAL_SIZE { + trace!("PLIC access"); + let mut inst: u32 = read_csr!(CSR_HTINST) as u32; + if inst == 0 { + let inst_addr: GuestPhysAddr = current_cpu.sepc; + //load real ins from guest memmory + inst = read_inst(inst_addr); + ins_size = if inst & 0x3 == 3 { 4 } else { 2 }; + } else if inst == 0x3020 || inst == 0x3000 { + // TODO: we should reinject this in the guest as a fault access + error!("fault on 1st stage page table walk"); + } else { + // If htinst is valid and is not a pseudo instructon make sure + // the opcode is valid even if it was a compressed instruction, + // but before save the real instruction size. + ins_size = if (inst) & 0x2 == 0 { 2 } else { 4 }; + inst = inst | 0b10; + // error!("unhandled guest page fault at {:#x}", addr); + // panic!("inst{:#x}", inst); + } + //TODO: decode inst to real instruction + let (_len, inst) = decode_inst(inst); + if let Some(inst) = inst { + if addr >= host_plic_base + PLIC_GLOBAL_SIZE { + vplic_hart_emul_handler(current_cpu, addr, inst); + } else { + vplic_global_emul_handler(current_cpu, addr, inst); + } + current_cpu.sepc += ins_size; + } else { + error!("Invalid instruction at {:#x}", current_cpu.sepc); + panic!(); + } + } else { + panic!("CPU {} unmaped memmory at {:#x}", current_cpu.cpuid, addr); + } + } + #[cfg(feature = "aia")] + { + let host_aplic_base = host_aplic().read().base; + let host_aplic_size = host_aplic().read().size; + + if addr >= host_aplic_base && addr < host_aplic_base + host_aplic_size { + trace!("APLIC access"); + let mut inst: u32 = read_csr!(CSR_HTINST) as u32; + let mut ins_size: usize = 0; + if inst == 0 { + let inst_addr: GuestPhysAddr = current_cpu.sepc; + inst = read_inst(inst_addr); + ins_size = if inst & 0x3 == 3 { 4 } else { 2 }; + } else if inst == 0x3020 || inst == 0x3000 { + error!("fault on 1st stage page table walk"); + } else { + ins_size = if (inst) & 0x2 == 0 { 2 } else { 4 }; + inst = inst | 0b10; + // error!("unhandled guest page fault at {:#x}", addr); + } + // let (len, inst) = decode_inst(inst); + let (_, inst) = decode_inst(inst); + + if let Some(inst) = inst { + vaplic_emul_handler(current_cpu, addr, inst); + current_cpu.sepc += ins_size; + } else { + error!("Invalid instruction at {:#x}", current_cpu.sepc); + } + } else { + panic!("CPU {} unmaped memmory at {:#x}", current_cpu.cpuid, addr); + } + } } fn read_inst(addr: GuestPhysAddr) -> u32 { - let mut ins: u32 = 0; + let mut ins: u32; if addr & 0b1 != 0 { error!("trying to read guest unaligned instruction"); } @@ -157,66 +205,72 @@ fn decode_inst(inst: u32) -> (usize, Option) { } /// handle external interrupt pub fn interrupts_arch_handle(current_cpu: &mut ArchCpu) { - todo!(); - // trace!("interrupts_arch_handle @CPU{}", current_cpu.cpuid); - // let trap_code: usize; - // trap_code = read_csr!(CSR_SCAUSE); - // trace!("CSR_SCAUSE: {:#x}", trap_code); - // match trap_code & 0xfff { - // InterruptType::STI => { - // trace!("STI on CPU{}", current_cpu.cpuid); - // unsafe { - // hvip::set_vstip(); - // sie::clear_stimer(); - // } - // trace!("sip{:#x}", read_csr!(CSR_SIP)); - // trace!("sie {:#x}", read_csr!(CSR_SIE)); - // } - // InterruptType::SSI => { - // debug!("SSI on CPU {}", current_cpu.cpuid); - // handle_ssi(current_cpu); - // } - // InterruptType::SEI => { - // debug!("SEI on CPU {}", current_cpu.cpuid); - // handle_eirq(current_cpu) - // } - // _ => { - // error!( - // "unhandled trap {:#x},sepc: {:#x}", - // trap_code, current_cpu.sepc - // ); - // unreachable!(); - // } - // } + trace!("interrupts_arch_handle @CPU{}", current_cpu.cpuid); + let trap_code: usize; + trap_code = read_csr!(CSR_SCAUSE); + trace!("CSR_SCAUSE: {:#x}", trap_code); + match trap_code & 0xfff { + InterruptType::STI => { + trace!("STI on CPU{}", current_cpu.cpuid); + unsafe { + hvip::set_vstip(); + sie::clear_stimer(); + } + trace!("sip{:#x}", read_csr!(CSR_SIP)); + trace!("sie {:#x}", read_csr!(CSR_SIE)); + } + InterruptType::SSI => { + trace!("SSI on CPU {}", current_cpu.cpuid); + handle_ssi(current_cpu); + } + InterruptType::SEI => { + debug!("SEI on CPU {}", current_cpu.cpuid); + handle_eirq(current_cpu) + } + _ => { + error!( + "unhandled trap {:#x},sepc: {:#x}", + trap_code, current_cpu.sepc + ); + unreachable!(); + } + } } /// handle interrupt request(current only external interrupt) pub fn handle_eirq(current_cpu: &mut ArchCpu) { - todo!(); - // // TODO: handle other irq - // // check external interrupt && handle - // // sifive plic: context0=>cpu0,M mode,context1=>cpu0,S mode... - // let context_id = 2 * current_cpu.cpuid + 1; - // let mut host_plic = host_plic(); - // let claim_and_complete_addr = - // host_plic.read().base + PLIC_GLOBAL_SIZE + 0x1000 * context_id + 0x4; - // let mut irq = unsafe { core::ptr::read_volatile(claim_and_complete_addr as *const u32) }; - // debug!( - // "CPU{} get external irq{}@{:#x}", - // current_cpu.cpuid, irq, claim_and_complete_addr - // ); - // host_plic.write().claim_complete[context_id] = irq; - // // set external interrupt pending, which trigger guest interrupt - // unsafe { hvip::set_vseip() }; + #[cfg(feature = "plic")] + { + // TODO: handle other irq + // check external interrupt && handle + // sifive plic: context0=>cpu0,M mode,context1=>cpu0,S mode... + let context_id = 2 * current_cpu.cpuid + 1; + let host_plic = host_plic(); + let claim_and_complete_addr = + host_plic.read().base + PLIC_GLOBAL_SIZE + 0x1000 * context_id + 0x4; + let irq = unsafe { core::ptr::read_volatile(claim_and_complete_addr as *const u32) }; + debug!( + "CPU{} get external irq{}@{:#x}", + current_cpu.cpuid, irq, claim_and_complete_addr + ); + host_plic.write().claim_complete[context_id] = irq; + // set external interrupt pending, which trigger guest interrupt + unsafe { hvip::set_vseip() }; + } + #[cfg(feature = "aia")] + { + panic!("HS extensional interrupt") + } } pub fn handle_ssi(current_cpu: &mut ArchCpu) { - todo!(); - // let sip = read_csr!(CSR_SIP); - // debug!("CPU{} sip: {:#x}", current_cpu.cpuid, sip); - // clear_csr!(CSR_SIP, 1 << 1); - // let sip2 = read_csr!(CSR_SIP); - // debug!("CPU{} sip*: {:#x}", current_cpu.cpuid, sip2); + trace!("handle_ssi"); + let sip = read_csr!(CSR_SIP); + trace!("CPU{} sip: {:#x}", current_cpu.cpuid, sip); + clear_csr!(CSR_SIP, 1 << 1); + let sip2 = read_csr!(CSR_SIP); + trace!("CPU{} sip*: {:#x}", current_cpu.cpuid, sip2); - // debug!("hvip: {:#x}", read_csr!(CSR_HVIP)); - // set_csr!(CSR_HVIP, 1 << 2); + trace!("hvip: {:#x}", read_csr!(CSR_HVIP)); + set_csr!(CSR_HVIP, 1 << 2); + check_events(); } diff --git a/src/arch/riscv64/zone.rs b/src/arch/riscv64/zone.rs new file mode 100644 index 00000000..bd7388ef --- /dev/null +++ b/src/arch/riscv64/zone.rs @@ -0,0 +1,110 @@ +use crate::{ + config::*, + device::virtio_trampoline::{mmio_virtio_handler, VIRTIO_BRIDGE}, + error::HvResult, + memory::{addr::align_up, GuestPhysAddr, HostPhysAddr, MemFlags, MemoryRegion}, + percpu::get_cpu_data, + zone::Zone, +}; +impl Zone { + pub fn pt_init(&mut self, mem_regions: &[HvConfigMemoryRegion]) -> HvResult { + for mem_region in mem_regions.iter() { + let mut flags = MemFlags::READ | MemFlags::WRITE | MemFlags::EXECUTE; + if mem_region.mem_type == MEM_TYPE_IO { + flags |= MemFlags::IO; + } + match mem_region.mem_type { + MEM_TYPE_RAM | MEM_TYPE_IO => { + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + mem_region.virtual_start as GuestPhysAddr, + mem_region.physical_start as HostPhysAddr, + mem_region.size as _, + flags, + ))? + } + MEM_TYPE_VIRTIO => { + self.mmio_region_register( + mem_region.physical_start as _, + mem_region.size as _, + mmio_virtio_handler, + mem_region.physical_start as _, + ); + } + _ => { + panic!("Unsupported memory type: {}", mem_region.mem_type) + } + } + } + #[cfg(feature = "aia")] + { + use crate::memory::PAGE_SIZE; + let paddr = 0x2800_0000 as HostPhysAddr; + let size = PAGE_SIZE; + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + paddr as GuestPhysAddr, + paddr + PAGE_SIZE * 1, + size, + MemFlags::READ | MemFlags::WRITE, + ))?; + + let paddr = 0x2800_1000 as HostPhysAddr; + let size = PAGE_SIZE; + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + paddr as GuestPhysAddr, + paddr + PAGE_SIZE * 2, + size, + MemFlags::READ | MemFlags::WRITE, + ))?; + + let paddr = 0x2800_2000 as HostPhysAddr; + let size = PAGE_SIZE; + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + paddr as GuestPhysAddr, + paddr + PAGE_SIZE * 3, + size, + MemFlags::READ | MemFlags::WRITE, + ))?; + + let paddr = 0x2800_3000 as HostPhysAddr; + let size = PAGE_SIZE; + self.gpm.insert(MemoryRegion::new_with_offset_mapper( + paddr as GuestPhysAddr, + paddr + PAGE_SIZE * 4, + size, + MemFlags::READ | MemFlags::WRITE, + ))?; + } + info!("VM stage 2 memory set: {:#x?}", self.gpm); + Ok(()) + } + // pub fn mmio_init(&mut self, hv_config: &HvArchZoneConfig) { + // //TODO + // } + pub fn irq_bitmap_init(&mut self, irqs: &[u32]) {} + pub fn isa_init(&mut self, fdt: &fdt::Fdt) { + let cpu_set = self.cpu_set; + cpu_set.iter().for_each(|cpuid| { + let cpu_data = get_cpu_data(cpuid); + let cpu_isa = fdt + .cpus() + .find(|cpu| cpu.ids().all().next().unwrap() == cpuid) + .unwrap() + .properties() + .find(|p| p.name == "riscv,isa") + .unwrap(); + if cpu_isa.as_str().unwrap().contains("sstc") { + println!("cpu{} support sstc", cpuid); + cpu_data.arch_cpu.sstc = true; + } + }) + } +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct HvArchZoneConfig { + pub plic_base: usize, + pub plic_size: usize, + pub aplic_base: usize, + pub aplic_size: usize, +} diff --git a/src/config.rs b/src/config.rs index f1db6a2f..86916ba1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,14 +3,18 @@ use spin::Once; use crate::{arch::zone::HvArchZoneConfig, platform}; +#[cfg(test)] +mod tests; + pub const MEM_TYPE_RAM: u32 = 0; pub const MEM_TYPE_IO: u32 = 1; pub const MEM_TYPE_VIRTIO: u32 = 2; pub const CONFIG_MAX_MEMORY_REGIONS: usize = 16; pub const CONFIG_MAX_INTERRUPTS: usize = 32; - -// pub const CONFIG_KERNEL_ARGS_MAXLEN: usize = 256; +pub const CONFIG_NAME_MAXLEN: usize = 32; +pub const CONFIG_MAX_IVC_CONGIGS: usize = 2; +pub const CONFIG_MAX_PCI_DEV: usize = 16; #[repr(C)] #[derive(Debug, Clone, Copy)] @@ -33,6 +37,40 @@ impl HvConfigMemoryRegion { } } +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct HvPciConfig { + pub ecam_base: u64, + pub ecam_size: u64, + pub io_base: u64, + pub io_size: u64, + pub pci_io_base: u64, + pub mem32_base: u64, + pub mem32_size: u64, + pub pci_mem32_base: u64, + pub mem64_base: u64, + pub mem64_size: u64, + pub pci_mem64_base: u64, +} + +impl HvPciConfig { + pub const fn new_empty() -> Self { + Self { + ecam_base: 0, + ecam_size: 0, + io_base: 0, + io_size: 0, + pci_io_base: 0, + mem32_base: 0, + mem32_size: 0, + pci_mem32_base: 0, + mem64_base: 0, + mem64_size: 0, + pci_mem64_base: 0, + } + } +} + #[repr(C)] #[derive(Debug, Clone)] pub struct HvZoneConfig { @@ -42,13 +80,18 @@ pub struct HvZoneConfig { memory_regions: [HvConfigMemoryRegion; CONFIG_MAX_MEMORY_REGIONS], num_interrupts: u32, interrupts: [u32; CONFIG_MAX_INTERRUPTS], + num_ivc_configs: u32, + ivc_configs: [HvIvcConfig; CONFIG_MAX_IVC_CONGIGS], pub entry_point: u64, pub kernel_load_paddr: u64, pub kernel_size: u64, pub dtb_load_paddr: u64, pub dtb_size: u64, - - pub arch: HvArchZoneConfig, + pub name: [u8; CONFIG_NAME_MAXLEN], + pub arch_config: HvArchZoneConfig, + pub pci_config: HvPciConfig, + pub num_pci_devs: u64, + pub alloc_pci_devs: [u64; CONFIG_MAX_PCI_DEV], } impl HvZoneConfig { @@ -59,12 +102,18 @@ impl HvZoneConfig { memory_regions: [HvConfigMemoryRegion; CONFIG_MAX_MEMORY_REGIONS], num_interrupts: u32, interrupts: [u32; CONFIG_MAX_INTERRUPTS], + num_ivc_configs: u32, + ivc_configs: [HvIvcConfig; CONFIG_MAX_IVC_CONGIGS], entry_point: u64, kernel_load_paddr: u64, - kernel_size:u64, - dtb_load_paddr:u64, + kernel_size: u64, + dtb_load_paddr: u64, dtb_size: u64, + name: [u8; CONFIG_NAME_MAXLEN], arch: HvArchZoneConfig, + pci: HvPciConfig, + num_pci_devs: u64, + alloc_pci_devs: [u64; CONFIG_MAX_PCI_DEV], ) -> Self { Self { zone_id, @@ -73,12 +122,18 @@ impl HvZoneConfig { memory_regions, num_interrupts, interrupts, + num_ivc_configs, + ivc_configs, entry_point, kernel_load_paddr, kernel_size, dtb_load_paddr, dtb_size, - arch, + name, + arch_config: arch, + pci_config: pci, + num_pci_devs: num_pci_devs, + alloc_pci_devs: alloc_pci_devs, } } @@ -105,6 +160,10 @@ impl HvZoneConfig { } v } + + pub fn ivc_config(&self) -> &[HvIvcConfig] { + &self.ivc_configs[..self.num_ivc_configs as usize] + } } pub static mut HV_ROOT_ZONE_CONFIG: Once = Once::new(); @@ -117,3 +176,19 @@ pub fn root_zone_config() -> &'static HvZoneConfig { init(); unsafe { HV_ROOT_ZONE_CONFIG.get().unwrap() } } + +pub const IVC_PROTOCOL_USER: u32 = 0x0; +pub const IVC_PROTOCOL_HVISOR: u32 = 0x1; + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct HvIvcConfig { + pub ivc_id: u32, + pub peer_id: u32, + pub control_table_ipa: u64, + pub shared_mem_ipa: u64, + pub rw_sec_size: u32, + pub out_sec_size: u32, + pub interrupt_num: u32, + pub max_peers: u32, +} diff --git a/src/config/tests.rs b/src/config/tests.rs new file mode 100644 index 00000000..46545be5 --- /dev/null +++ b/src/config/tests.rs @@ -0,0 +1,6 @@ +use super::*; + +#[test_case] +fn test_simple_config() { + // TODO: rewrite test with new gicv2 and gicv3 config system +} diff --git a/src/consts.rs b/src/consts.rs index d97fc9b0..720c9b16 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -6,14 +6,21 @@ pub const HV_HEAP_SIZE: usize = 1024 * 1024; // 1 MB pub const HV_MEM_POOL_SIZE: usize = 16 * 1024 * 1024; // 16 MB /// Size of the per-CPU data (stack and other CPU-local data). -pub const PER_CPU_SIZE: usize = 512 * 1024; // 128KB //may get bigger when dev +pub const PER_CPU_SIZE: usize = 512 * 1024; // 512KB //may get bigger when dev /// Pointer of the per-CPU data array. pub const PER_CPU_ARRAY_PTR: *mut VirtAddr = __core_end as _; pub const INVALID_ADDRESS: usize = usize::MAX; +#[cfg(target_arch = "loongarch64")] pub const MAX_CPU_NUM: usize = 4; +#[cfg(target_arch = "aarch64")] +pub const MAX_CPU_NUM: usize = 4; +#[cfg(target_arch = "riscv64")] +pub const MAX_CPU_NUM: usize = 4; + +pub const MAX_ZONE_NUM: usize = 3; pub fn core_end() -> VirtAddr { __core_end as _ diff --git a/src/device/irqchip/aia/aplic.rs b/src/device/irqchip/aia/aplic.rs new file mode 100644 index 00000000..06689a2a --- /dev/null +++ b/src/device/irqchip/aia/aplic.rs @@ -0,0 +1,471 @@ +use riscv::use_sv32; +use spin::Once; +use spin::RwLock; +// use crate::device::irqchip::aia::imsic::imsic_trigger; +use crate::config::root_zone_config; +use crate::zone::Zone; +use crate::{arch::cpu::ArchCpu, memory::GuestPhysAddr, percpu::this_cpu_data}; +use fdt::Fdt; +use riscv_decode::Instruction; +// S-mode interrupt delivery controller +const APLIC_S_IDC: usize = 0xd00_4000; +pub const APLIC_DOMAINCFG_BASE: usize = 0x0000; +pub const APLIC_SOURCECFG_BASE: usize = 0x0004; +pub const APLIC_SOURCECFG_TOP: usize = 0x1000; +pub const APLIC_MSIADDR_BASE: usize = 0x1BC8; +pub const APLIC_PENDING_BASE: usize = 0x1C00; +pub const APLIC_PENDING_TOP: usize = 0x1C7C; +pub const APLIC_IPNUM_BASE: usize = 0x1CDC; +pub const APLIC_CLRIP_BASE: usize = 0x1D00; +pub const APLIC_ENABLE_BASE: usize = 0x1E00; +pub const APLIC_ENABLE_TOP: usize = 0x1E7C; +pub const APLIC_ENABLE_NUM: usize = 0x1EDC; +pub const APLIC_CLRIE_BASE: usize = 0x1F00; +pub const APLIC_CLRIE_NUM_BASE: usize = 0x1FDC; +pub const APLIC_IPNUM_LE_BASE: usize = 0x2000; +pub const APLIC_TARGET_BASE: usize = 0x3004; +pub const APLIC_IDC_BASE: usize = 0x4000; + +#[repr(u32)] +#[allow(dead_code)] +pub enum SourceModes { + Inactive = 0, + Detached = 1, + RisingEdge = 4, + FallingEdge = 5, + LevelHigh = 6, + LevelLow = 7, +} + +// offset size register name +// 0x0000 4 bytes domaincfg +// 0x0004 4 bytes sourcecfg[1] +// 0x0008 4 bytes sourcecfg[2] +// . . . . . . +// 0x0FFC 4 bytes sourcecfg[1023] +// 0x1BC0 4 bytes mmsiaddrcfg (machine-level interrupt domains only) +// 0x1BC4 4 bytes mmsiaddrcfgh ” +// 0x1BC8 4 bytes smsiaddrcfg ” +// 0x1BCC 4 bytes smsiaddrcfgh ” +// 0x1C00 4 bytes setip[0] +// 0x1C04 4 bytes setip[1] +// . . . . . . +// 0x1C7C 4 bytes setip[31] +// 0x1CDC 4 bytes setipnum +// 0x1D00 4 bytes in clrip[0] +// 0x1D04 4 bytes in clrip[1] +// . . . . . . +// 0x1D7C 4 bytes in clrip[31] +// 0x1DDC 4 bytes clripnum +// 0x1E00 4 bytes setie[0] +// 0x1E04 4 bytes setie[1] +// . . . . . . +// 0x1E7C 4 bytes setie[31] +// 0x1EDC 4 bytes setienum +// 0x1F00 4 bytes clrie[0] +// 0x1F04 4 bytes clrie[1] +// . . . . . . +// 0x1F7C 4 bytes clrie[31] +// 0x1FDC 4 bytes clrienum +// 0x2000 4 bytes setipnum le +// 0x2004 4 bytes setipnum be +// 0x3000 4 bytes genmsi +// 0x3004 4 bytes target[1] +// 0x3008 4 bytes target[2] +// . . . . . . +// 0x3FFC 4 bytes target[1023] + +pub fn primary_init_early() { + let root_config = root_zone_config(); + init_aplic( + root_config.arch_config.aplic_base as usize, + root_config.arch_config.aplic_size as usize, + ); +} +pub fn primary_init_late() { + //nothing to do +} +pub fn percpu_init() { + //nothing to do +} +pub fn inject_irq(_irq: usize, is_hardware: bool) { + //nothing to do +} +pub static APLIC: Once> = Once::new(); +pub fn host_aplic<'a>() -> &'a RwLock { + APLIC.get().expect("Uninitialized hypervisor aplic!") +} + +#[repr(C)] +pub struct Aplic { + pub base: usize, + pub size: usize, +} + +#[allow(dead_code)] +impl Aplic { + pub fn new(base: usize, size: usize) -> Self { + Self { base, size } + } + pub fn set_domaincfg(&self, bigendian: bool, msimode: bool, enabled: bool) { + let enabled = u32::from(enabled); + let msimode = u32::from(msimode); + let bigendian = u32::from(bigendian); + let addr = self.base + APLIC_DOMAINCFG_BASE; + let bigendian = 0; + let src = (enabled << 8) | (msimode << 2) | bigendian; + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + } + } + pub fn get_domaincfg(&self) -> u32 { + let addr = self.base + APLIC_DOMAINCFG_BASE; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn get_msimode(&self) -> bool { + let addr = self.base + APLIC_DOMAINCFG_BASE; + let value = unsafe { core::ptr::read_volatile(addr as *const u32) }; + ((value >> 2) & 0b11) != 0 + } + pub fn set_sourcecfg(&self, irq: u32, mode: SourceModes) { + assert!(irq > 0 && irq < 1024); + let addr = self.base + APLIC_SOURCECFG_BASE + (irq as usize - 1) * 4; + let src = mode as u32; + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + } + } + pub fn set_sourcecfg_delegate(&self, irq: u32, child: u32) { + assert!(irq > 0 && irq < 1024); + let addr = self.base + APLIC_SOURCECFG_BASE + (irq as usize - 1) * 4; + let src = 1 << 10 | child & 0x3ff; + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + } + } + pub fn get_sourcecfg(&self, irq: u32) -> u32 { + assert!(irq > 0 && irq < 1024); + let addr = self.base + APLIC_SOURCECFG_BASE + (irq as usize - 1) * 4; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn set_msiaddr(&self, address: usize) { + let addr = self.base + APLIC_MSIADDR_BASE; + let src = (address >> 12) as u32; + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + core::ptr::write_volatile((addr + 4) as *mut u32, 0); + } + } + pub fn get_ip(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_PENDING_BASE + irqidx * 4; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn get_clrip(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_CLRIP_BASE + irqidx * 4; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn set_ip(&self, irqidx: usize, src: u32, pending: bool) { + assert!(irqidx < 32); + let addr = self.base + APLIC_PENDING_BASE + irqidx * 4; + let clr_addr = self.base + APLIC_CLRIP_BASE + irqidx * 4; + if pending { + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + } + } else { + unsafe { + core::ptr::write_volatile(clr_addr as *mut u32, src); + } + } + } + pub fn set_ipnum(&self, value: u32) { + let addr = self.base + APLIC_IPNUM_BASE; + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + pub fn get_in_clrip(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_CLRIP_BASE + irqidx * 4; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn get_ie(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_ENABLE_BASE + irqidx * 4; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn get_clrie(&self, irqidx: usize) -> u32 { + assert!(irqidx < 32); + let addr = self.base + APLIC_CLRIE_BASE + irqidx * 4; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn setie(&self, irqidx: usize, value: u32, enabled: bool) { + assert!(irqidx < 32); + let addr = self.base + APLIC_ENABLE_BASE + irqidx * 4; + let clr_addr = self.base + APLIC_CLRIE_BASE + irqidx * 4; + if enabled { + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } else { + unsafe { + core::ptr::write_volatile(clr_addr as *mut u32, value); + } + } + } + pub fn setienum(&self, value: u32) { + let addr = self.base + APLIC_ENABLE_NUM; + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + pub fn clrienum(&self, value: u32) { + let addr = self.base + APLIC_CLRIE_NUM_BASE; + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + pub fn setipnum_le(&self, value: u32) { + let addr = self.base + APLIC_IPNUM_LE_BASE; + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + pub fn set_target_msi(&self, irq: u32, hart: u32, guest: u32, eiid: u32) { + let addr = self.base + APLIC_TARGET_BASE + (irq as usize - 1) * 4; + let src = (hart << 18) | (guest << 12) | eiid; + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + } + } + pub fn set_target_direct(&self, irq: u32, hart: u32, prio: u32) { + let addr = self.base + APLIC_TARGET_BASE + (irq as usize - 1) * 4; + let src = (hart << 18) | (prio & 0xFF); + unsafe { + core::ptr::write_volatile(addr as *mut u32, src); + } + } + pub fn get_target_info(&self, irq: u32) -> (u32, u32, u32) { + let addr = self.base + APLIC_TARGET_BASE + (irq as usize - 1) * 4; + unsafe { + let src = core::ptr::read_volatile(addr as *const u32); + let hart = (src >> 18) & 0x3F; + let guest = (src >> 12) & 0x3F; + let eiid = src & 0xFFF; + (hart, guest, eiid) + } + } +} +pub fn vaplic_emul_handler(current_cpu: &mut ArchCpu, addr: GuestPhysAddr, inst: Instruction) { + let host_aplic = host_aplic(); + let offset = addr.wrapping_sub(host_aplic.read().base); + if offset >= APLIC_DOMAINCFG_BASE && offset < APLIC_SOURCECFG_BASE { + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; // 要写入的 value + let enabled = ((value >> 8) & 0x1) != 0; // IE + let msimode = ((value >> 2) & 0b1) != 0; // DM / MSI + let bigendian = (value & 0b1) != 0; // 大小端 + if this_cpu_data().id != 3 { + host_aplic + .write() + .set_domaincfg(bigendian, msimode, enabled); + debug!( + "APLIC set domaincfg write addr@{:#x} bigendian {} msimode {} enabled {}", + addr, bigendian, msimode, enabled + ); + } + } + Instruction::Lw(i) => { + let value = host_aplic.read().get_domaincfg(); + current_cpu.x[i.rd() as usize] = value as usize; + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else if offset >= APLIC_SOURCECFG_BASE && offset < APLIC_SOURCECFG_TOP { + //sourcecfg + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; + let irq = ((offset - APLIC_SOURCECFG_BASE) / 4) + 1; + if (value >> 10) & 0b1 == 1 { + //delegate + let child = value & 0x3ff; + host_aplic.write().set_sourcecfg_delegate(irq as u32, child); + debug!( + "APLIC set sourcecfg_delegate write addr@{:#x} irq {} child {}", + addr, irq, child + ); + } else { + let mode = match value { + 0 => SourceModes::Inactive, + 1 => SourceModes::Detached, + 4 => SourceModes::RisingEdge, + 5 => SourceModes::FallingEdge, + 6 => SourceModes::LevelHigh, + 7 => SourceModes::LevelLow, + _ => panic!("Unknown sourcecfg mode"), + }; + if this_cpu_data().id != 3 || this_cpu_data().id == 3 && (irq == 6 || irq == 7) + { + host_aplic.write().set_sourcecfg(irq as u32, mode); + debug!( + "APLIC set sourcecfg write addr@{:#x} irq {} mode {}", + addr, irq, value + ); + } + } + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else if offset >= APLIC_MSIADDR_BASE && offset <= 0x1BCC { + // msia + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; + let address = (value as usize) << 12; + host_aplic.write().set_msiaddr(address); + debug!( + "APLIC set msiaddr write addr@{:#x} address {}", + addr, address + ); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else if offset >= APLIC_PENDING_BASE && offset < APLIC_PENDING_TOP { + // pending + panic!("setip Unexpected instruction {:?}", inst); + } + // setipnum 区域 0x1CDC - 0x1CE0 + else if offset >= 0x1CDC && offset < 0x1CE0 { + panic!("setipnum Unexpected instruction {:?}", inst) + } else if offset >= APLIC_CLRIP_BASE && offset < 0x1D80 { + // panic!("addr@{:#x} in_clrip Unexpected instruction {:?}", offset ,inst); + match inst { + Instruction::Lw(i) => { + let irqidx = (offset - APLIC_CLRIP_BASE) / 4; + let value = host_aplic.read().get_in_clrip(irqidx); + current_cpu.x[i.rd() as usize] = value as usize; + debug!( + "APLIC read in clrip addr@{:#x} irqidx {} value {}", + addr, irqidx, value + ); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } + // clripnum 区域 + else if offset >= 0x1DDC && offset < 0x1DE0 { + panic!("clripnum Unexpected instruction {:?}", inst) + } + // setie + else if offset >= APLIC_ENABLE_BASE && offset < 0x1E80 { + panic!("setie Unexpected instruction {:?}", inst); + } else if offset >= APLIC_ENABLE_NUM && offset < 0x1EE0 { + // setienum + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; + host_aplic.write().setienum(value); + debug!("APLIC setienum write addr@{:#x} value {}", addr, value); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else if offset >= APLIC_CLRIE_BASE && offset < 0x1FDC { + // clrie + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; + let irqidx = (offset - APLIC_CLRIE_BASE) / 4; + if this_cpu_data().id != 3 { + host_aplic.write().setie(irqidx, value, false); + } + debug!( + "APLIC set clrie write addr@{:#x} irqidx {} value@{:#x}", + addr, irqidx, value + ); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } + // clrienum + else if offset >= 0x1FDC && offset < 0x1FE0 { + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; + host_aplic.write().clrienum(value); + debug!("APLIC set clrienum write addr@{:#x} value{}", offset, value); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } + // setipnum_le + else if offset >= 0x2000 && offset < 0x2004 { + match inst { + Instruction::Sw(i) => { + let value = current_cpu.x[i.rs2() as usize] as u32; + host_aplic.write().setipnum_le(value); + // debug!("APLIC setipnum le write addr@{:#x} value@{:#x}",offset, value); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } + // setipnum_be + else if offset >= 0x2004 && offset < 0x2008 { + panic!("setipnum_be Unexpected instruction {:?}", inst) + } + // genmsi + else if offset >= 0x3000 && offset < 0x3004 { + panic!("genmsi Unexpected instruction {:?}", inst) + } else if offset >= APLIC_TARGET_BASE && offset < APLIC_IDC_BASE { + // target + match inst { + Instruction::Sw(i) => { + let first_cpu = this_cpu_data() + .zone + .as_ref() + .unwrap() + .read() + .cpu_set + .first_cpu() + .unwrap(); + let value = current_cpu.x[i.rs2() as usize] as u32; + let irq = ((offset - APLIC_TARGET_BASE) / 4) as u32 + 1; + let hart = ((value >> 18) & 0x3F) + first_cpu as u32; + if host_aplic.read().get_msimode() { + let guest = ((value >> 12) & 0x3F) + 1; + let eiid = value & 0xFFF; + if this_cpu_data().id != 3 || this_cpu_data().id == 3 && (irq == 6 || irq == 7) + { + host_aplic.write().set_target_msi(irq, hart, guest, eiid); + debug!( + "APLIC set msi target write addr@{:#x} irq {} hart {} guest {} eiid {}", + addr, irq, hart, guest, eiid + ); + } + } else { + let prio = value & 0xFF; + host_aplic.write().set_target_direct(irq, hart, prio); + debug!( + "APLIC set direct target write addr@{:#x} irq {} hart {} prio {}", + addr, irq, hart, prio + ); + } + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else { + panic!("Invalid address: {:#x}", addr); + } +} +pub fn init_aplic(aplic_base: usize, aplic_size: usize) { + let aplic = Aplic::new(aplic_base, aplic_size); + APLIC.call_once(|| RwLock::new(aplic)); +} +impl Zone { + pub fn arch_irqchip_reset(&self) { + //TODO + } +} diff --git a/src/device/irqchip/aia/imsic.rs b/src/device/irqchip/aia/imsic.rs new file mode 100644 index 00000000..2053b021 --- /dev/null +++ b/src/device/irqchip/aia/imsic.rs @@ -0,0 +1,55 @@ +use crate::arch::csr::{write_csr ,read_csr ,CSR_VSISELECT ,CSR_VSIREG ,CSR_VSTOPI ,CSR_VSTOPEI}; +pub const IMSIC_VS: usize = 0x2800_1000; +const IMSIC_VS_HART_STRIDE: usize = 0x2000; + +const XLEN: usize = usize::BITS as usize; +const XLEN_STRIDE: usize = XLEN / 32; + +const EIP: usize = 0x80; + +pub const fn imsic_vs(hart: usize) -> usize { + IMSIC_VS + IMSIC_VS_HART_STRIDE * hart +} +fn imsic_write(reg: usize, val: usize) { + unsafe { + match reg { + CSR_VSISELECT => write_csr!(CSR_VSISELECT, val), + CSR_VSIREG => write_csr!(CSR_VSIREG, val), + CSR_VSTOPI => write_csr!(CSR_VSTOPI, val), + CSR_VSTOPEI => write_csr!(CSR_VSTOPEI, val), + _ => panic!("Unknown CSR {}", reg), + } + } +} + +// Read from an IMSIC CSR + +fn imsic_read(reg: usize) -> usize { + let ret: usize; + unsafe { + ret = match reg { + CSR_VSISELECT => read_csr!(CSR_VSISELECT), + CSR_VSIREG => read_csr!(CSR_VSIREG), + CSR_VSTOPI => read_csr!(CSR_VSTOPI), + CSR_VSTOPEI => read_csr!(CSR_VSTOPEI), + _ => panic!("Unknown CSR {}", reg), + } + } + ret +} +// VS-Mode IMSIC CSRs + + +pub fn imsic_trigger(hart: u32, guest: u32, eiid: u32) { + if guest == 1{ + unsafe { + core::ptr::write_volatile(imsic_vs(hart as usize) as *mut u32, eiid); + } + } else { + panic!( + "Unknown imsic set hart {} guest {} eiid {}", + hart, guest, eiid + ); + } +} + diff --git a/src/device/irqchip/aia/mod.rs b/src/device/irqchip/aia/mod.rs new file mode 100644 index 00000000..428e3741 --- /dev/null +++ b/src/device/irqchip/aia/mod.rs @@ -0,0 +1 @@ +pub mod aplic; diff --git a/src/device/irqchip/gicv2/gic.rs b/src/device/irqchip/gicv2/gic.rs new file mode 100644 index 00000000..2ab7e8eb --- /dev/null +++ b/src/device/irqchip/gicv2/gic.rs @@ -0,0 +1,175 @@ +use crate::arch::cpu::this_cpu_id; +use crate::device::irqchip::gicv2::gicc::GICC; +use crate::device::irqchip::gicv2::gicd::GICV2_SGIS_NUM; +use crate::device::irqchip::gicv2::gich::{ + GICH, GICV2_GICH_HCR_UIE, GICV2_GICH_LR_CPUID_SHIFT, GICV2_GICH_LR_HW, + GICV2_GICH_LR_PENDING_STATE, GICV2_GICH_LR_PHYSID_SHIFT, +}; +use crate::event::check_events; +use crate::hypercall::SGI_IPI_ID; +/// This file defines and implements the functional functions of physical gicv2. +/// author: ForeverYolo +/// reference: +/// 1. gicv2 spec : https://www.cl.cam.ac.uk/research/srg/han/ACS-P35/zynq/arm_gic_architecture_specification.pdf +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use spin::{Mutex, Once}; + +pub const MAX_CPU_NUM: usize = 8; +pub const MAINTENACE_INTERRUPT: u64 = 25; + +pub fn gicv2_handle_irq() { + if let Some(irq_id) = get_pending_irq() { + if irq_id < 8 { + deactivate_irq(irq_id); + let mut ipi_handled = false; + if irq_id == SGI_IPI_ID as _ { + ipi_handled = check_events(); + } + if !ipi_handled { + inject_irq(irq_id, true); + } + } else if irq_id < GICV2_SGIS_NUM { + deactivate_irq(irq_id); + } else if irq_id == MAINTENACE_INTERRUPT as usize { + handle_maintenace_interrupt(); + } else { + deactivate_irq(irq_id); + inject_irq(irq_id, false); + } + } +} + +pub fn get_pending_irq() -> Option { + let iar = GICC.get_iar() as usize; + let irq = iar & 0x3ff; + if irq >= 1023 { + None + } else { + Some(irq) + } +} + +// deactivate irq: GIC doesn't care CPU ID. +pub fn deactivate_irq(irq_id: usize) { + GICC.set_eoir(irq_id as u32); + if irq_id < GICV2_SGIS_NUM { + GICC.set_dir(irq_id as u32); + } +} + +pub fn change_underflow_maintenance(is_enable: bool) { + trace!("enable_maintenace_interrupt, is_enable is {}", is_enable); + let mut hcr = GICH.get_hcr(); + trace!("hcr is {}", hcr); + if is_enable { + hcr |= GICV2_GICH_HCR_UIE; + } else { + hcr &= !GICV2_GICH_HCR_UIE; + } + GICH.set_hcr(hcr); +} + +fn handle_maintenace_interrupt() { + info!("handle_maintenace_interrupt"); + while let Some((irq_id, is_sgi)) = PENDING_VIRQS.get().unwrap().fetch_irq() { + let is_injected: bool = inject_irq(irq_id, is_sgi); + if is_injected { + info!("inject pending irq {:#x} in maintenace interrupt", irq_id); + } + if !is_injected { + PENDING_VIRQS.get().unwrap().add_irq(irq_id, is_sgi); + change_underflow_maintenance(true); + return; + } + } + change_underflow_maintenance(false); +} + +pub fn inject_irq(irq_id: usize, is_sgi: bool) -> bool { + let elrsr: u64 = (GICH.get_elrsr(1) as u64) << 32 | GICH.get_elrsr(0) as u64; + let lr_num: isize = GICH.get_lr_num() as isize; + let lr_pint_mask: usize = 0x3ff << 10; + let mut free_lr: isize = -1; + for i in 0..lr_num { + if (1 << i) & elrsr > 0 { + free_lr = i; + continue; + } + let lr = GICH.get_lr(i as usize) as usize; + let pint = (lr & lr_pint_mask) >> 10; + if pint == irq_id { + trace!("virtual irq {} enables again", irq_id); + return true; + } + } + if free_lr == -1 { + warn!("no free lr"); + for i in 0..lr_num { + let lr = GICH.get_lr(i as usize) as usize; + warn!("lr[{}]: {:#x}", i, lr); + } + PENDING_VIRQS + .get() + .unwrap() + .add_irq(irq_id, is_sgi) + .unwrap(); + change_underflow_maintenance(true); + false + } else { + /* inject gruop 0 irq */ + // config vint bit 0-9 + let mut val = irq_id; + // config pending state bit 31 + val = val | GICV2_GICH_LR_PENDING_STATE; + if is_sgi { + // config cpu bit 10-12 + val |= 1 << GICV2_GICH_LR_CPUID_SHIFT; + } else { + // config pint bit 10-19 + val = val | (irq_id << GICV2_GICH_LR_PHYSID_SHIFT); + // config hw bit 31 + val = val | GICV2_GICH_LR_HW; + } + GICH.set_lr(free_lr as usize, val as u32); + true + } +} + +// virtual interrupts waiting to inject +pub static PENDING_VIRQS: Once = Once::new(); +pub struct PendingIrqs { + inner: Vec>>, +} + +impl PendingIrqs { + pub fn new(max_cpus: usize) -> Self { + let mut vs = vec![]; + for _ in 0..max_cpus { + let v = Mutex::new(VecDeque::new()); + vs.push(v) + } + Self { inner: vs } + } + + fn add_irq(&self, irq_id: usize, is_sgi: bool) -> Option<()> { + match self.inner.get(this_cpu_id()) { + Some(pending_irqs) => { + let mut irqs = pending_irqs.lock(); + irqs.push_back((irq_id, is_sgi)); + Some(()) + } + _ => None, + } + } + + fn fetch_irq(&self) -> Option<(usize, bool)> { + match self.inner.get(this_cpu_id()) { + Some(pending_irqs) => { + let mut irqs = pending_irqs.lock(); + irqs.pop_front() + } + _ => None, + } + } +} diff --git a/src/device/irqchip/gicv2/gic_ref.rs b/src/device/irqchip/gicv2/gic_ref.rs new file mode 100644 index 00000000..887c9f8c --- /dev/null +++ b/src/device/irqchip/gicv2/gic_ref.rs @@ -0,0 +1,39 @@ +use core::marker::PhantomData; +/// A struct definition that wraps around a bare pointer +/// author: ForeverYolo +/// references: +/// rust_shyper: https://gitee.com/openeuler/rust_shyper +use core::ops::Deref; +use core::ptr::NonNull; + +#[derive(Debug)] +pub struct GicRef<'a, T> { + ptr: NonNull, + _marker: PhantomData<&'a T>, +} + +impl GicRef<'_, T> { + pub const unsafe fn new<'a>(ptr: *const T) -> GicRef<'a, T> { + GicRef { + ptr: NonNull::new_unchecked(ptr.cast_mut()), + _marker: PhantomData, + } + } +} + +impl Clone for GicRef<'_, T> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for GicRef<'_, T> {} +unsafe impl Send for GicRef<'_, T> {} +unsafe impl Sync for GicRef<'_, T> {} + +impl Deref for GicRef<'_, T> { + type Target = T; + fn deref(&self) -> &T { + unsafe { self.ptr.as_ref() } + } +} diff --git a/src/device/irqchip/gicv2/gicc.rs b/src/device/irqchip/gicv2/gicc.rs new file mode 100644 index 00000000..17c91193 --- /dev/null +++ b/src/device/irqchip/gicv2/gicc.rs @@ -0,0 +1,122 @@ +#![allow(unused_variables)] +#![allow(dead_code)] +use crate::device::irqchip::gicv2::gic_ref::GicRef; +use crate::device::irqchip::gicv2::gicd::GICD; +use crate::device::irqchip::gicv2::gich::{ + GICH, GICV2_GICH_HCR_EN, GICV2_GICH_VMCR_PMR_SHIFT, GICV2_GICH_VMCR_VEM, + GICV2_GICH_VMCR_VMGRP0EN, +}; +use crate::device::irqchip::gicv2::GICV2; +/// gicc layout definition and functions for gicc operations. +/// author : ForeverYolo +/// reference: +/// 1. gicv2 spec : https://www.cl.cam.ac.uk/research/srg/han/ACS-P35/zynq/arm_gic_architecture_specification.pdf +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +pub const GICV2_PREEMPPTION_MAX: usize = 128; + +pub const GICV2_GICC_APR_REGS_NUM: usize = GICV2_PREEMPPTION_MAX / 32; + +pub const GICV2_GICC_CTRL_GRPEN1: u32 = 0x1; +pub const GICV2_GICC_CTRL_EOIMODES: u32 = 0x1 << 9; +pub const GICV2_GICC_CTLR_REG_OFFSET: usize = 0x0000; +pub const GICV2_GICC_PMR_REG_OFFSET: usize = 0x0004; +pub const GICV2_GICC_BPR_REG_OFFSET: usize = 0x0008; +pub const GICV2_GICC_IAR_REG_OFFSET: usize = 0x000C; +pub const GICV2_GICC_EOIR_REG_OFFSET: usize = 0x0010; +pub const GICV2_GICC_RPR_REG_OFFSET: usize = 0x0014; +pub const GICV2_GICC_HPPIR_REG_OFFSET: usize = 0x0018; +pub const GICV2_GICC_ABPR_REG_OFFSET: usize = 0x001C; +pub const GICV2_GICC_AIAR_REG_OFFSET: usize = 0x0020; +pub const GICV2_GICC_AEOIR_REG_OFFSET: usize = 0x0024; +pub const GICV2_GICC_AHPPIR_REG_OFFSET: usize = 0x0028; +pub const GICV2_GICC_APR_REG_OFFSET: usize = 0x00D0; +pub const GICV2_GICC_NSAPR_REG_OFFSET: usize = 0x00E0; +pub const GICV2_GICC_IIDR_REG_OFFSET: usize = 0x00FC; +pub const GICV2_GICC_DIR_REG_OFFSET: usize = 0x1000; +pub const GICV2_GICC_END: usize = 0x2000; +pub const GICV2_GICC_PMR_DEFAULT: u32 = 0xf0; + +// GICC Register layout. +register_structs! { + #[allow(non_snake_case)] + pub GicCpuInterface { + (0x0000 => CTLR: ReadWrite), + (0x0004 => PMR: ReadWrite), + (0x0008 => BPR: ReadWrite), + (0x000C => IAR: ReadOnly), + (0x0010 => EOIR: WriteOnly), + (0x0014 => RPR: ReadOnly), + (0x0018 => HPPIR: ReadOnly), + (0x001C => ABPR: ReadWrite), + (0x0020 => AIAR: ReadOnly), + (0x0024 => AEOIR: WriteOnly), + (0x0028 => AHPPIR: ReadOnly), + (0x002c => reserved0), + (0x00D0 => APR: [ReadWrite; GICV2_GICC_APR_REGS_NUM]), + (0x00E0 => NSAPR: [ReadWrite; GICV2_GICC_APR_REGS_NUM]), + (0x00f0 => reserved1), + (0x00FC => IIDR: ReadOnly), + (0x0100 => reserved2), + (0x1000 => DIR: WriteOnly), + (0x1004 => reserved3), + (0x2000 => @END), + } +} +unsafe impl Sync for GicCpuInterface {} + +// Each CPU holds one GICC. +pub static GICC: GicRef = + unsafe { GicRef::new(GICV2.gicc_base as *const GicCpuInterface) }; + +impl GicCpuInterface { + // init GICC for each CPU. + pub fn init(&self) { + // Ensure all SGIs disabled. + GICD.set_icenabler(0, 0x0000FFFF); + // get ctrl and pmr value + let gicc_ctrl = self.CTLR.get(); + let gicc_pmr = self.PMR.get(); + // interrupt completion is divided into two steps to improve hypervisor performance. + self.CTLR + .set(GICV2_GICC_CTRL_GRPEN1 | GICV2_GICC_CTRL_EOIMODES); + // Set the priority mask register to default value + self.PMR.set(GICV2_GICC_PMR_DEFAULT); + // VMCR only conyains 5 bits of priority + let mut vmcr = (gicc_pmr >> 3) << GICV2_GICH_VMCR_PMR_SHIFT; + // GICV layout equal to GICC without security extensions. + if gicc_ctrl & GICV2_GICC_CTRL_GRPEN1 != 0 { + vmcr |= GICV2_GICH_VMCR_VMGRP0EN; + } + if gicc_ctrl & GICV2_GICC_CTRL_EOIMODES != 0 { + vmcr |= GICV2_GICH_VMCR_VEM; + } + GICH.set_vmcr(vmcr); + // Enable virtual CPU interface operation. + GICH.set_hcr(GICV2_GICH_HCR_EN); + // Clear all lr registers in GICH. + GICH.clear_all_lr(); + // Deactivate all active and pending SGIS + let gicd_isactive = GICD.get_isactiver(0); + let gicd_ispend = GICD.get_spendsgir(0); + GICD.set_icactiver(0, gicd_isactive & 0xffff); + GICD.set_cpendsgir(0, gicd_ispend & 0xffff); + // re-enable all SGIs + GICD.set_isenabler(0, 0x0000FFFF); + info!("GICV2: GICC init done."); + } + + pub fn get_iar(&self) -> u32 { + self.IAR.get() + } + + pub fn set_eoir(&self, value: u32) { + self.EOIR.set(value); + } + + pub fn set_dir(&self, value: u32) { + self.DIR.set(value); + } +} diff --git a/src/device/irqchip/gicv2/gicd.rs b/src/device/irqchip/gicv2/gicd.rs new file mode 100644 index 00000000..4979f0a5 --- /dev/null +++ b/src/device/irqchip/gicv2/gicd.rs @@ -0,0 +1,191 @@ +#![allow(unused_variables)] +#![allow(dead_code)] +use crate::device::irqchip::gicv2::gic_ref::GicRef; +use crate::device::irqchip::gicv2::GICV2; +/// gicd layout definition and functions for gicd operations. +/// author : ForeverYolo +/// reference: +/// 1. gicv2 spec : https://www.cl.cam.ac.uk/research/srg/han/ACS-P35/zynq/arm_gic_architecture_specification.pdf +use spin::Mutex; +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite}; +// Hypervisor running in non-secure mode,can only access non-secure registers. + +// GICV2 interrupt layout. +pub const GICV2_INTS_MAX: usize = 1024; +pub const GICV2_SGIS_NUM: usize = 16; +pub const GICV2_PPIS_NUM: usize = 16; +pub const GICV2_PRIVATE_INTS_NUM: usize = GICV2_SGIS_NUM + GICV2_PPIS_NUM; + +// GICD Register numbers; +pub const GICV2_INT_REGS_NUM: usize = GICV2_INTS_MAX / 32; +pub const GICV2_PRIO_REGS_NUM: usize = GICV2_INTS_MAX * 8 / 32; +pub const GICV2_TARGET_REGS_NUM: usize = GICV2_INTS_MAX * 8 / 32; +pub const GICV2_CONFIG_REGS_NUM: usize = GICV2_INTS_MAX / 16; +pub const GICV2_SGI_REGS_NUM: usize = GICV2_SGIS_NUM * 8 / 32; +/* OPTIONAL */ +pub const GICV2_NSACR_REGS_NUM: usize = GICV2_INTS_MAX * 2 / 32; + +// GICD BITS +pub const GICD_CTLR_EN_BIT: usize = 0x1; + +// GICD Register offsets. +pub const GICD_CTRL_REG_OFFSET: usize = 0x0000; +pub const GICD_TYPER_REG_OFFSET: usize = 0x0004; +pub const GICD_IIDR_REG_OFFSET: usize = 0x0008; +pub const GICD_IGROUPR_REG_OFFSET: usize = 0x0080; +pub const GICD_ISENABLER_REG_OFFSET: usize = 0x0100; +pub const GICD_ICENABLER_REG_OFFSET: usize = 0x0180; +pub const GICD_ISPENDR_REG_OFFSET: usize = 0x0200; +pub const GICD_ICPENDR_REG_OFFSET: usize = 0x0280; +pub const GICD_ISACTIVER_REG_OFFSET: usize = 0x0300; +pub const GICD_ICACTIVER_REG_OFFSET: usize = 0x0380; +pub const GICD_IPRIORITYR_REG_OFFSET: usize = 0x0400; +pub const GICD_ITARGETSR_REG_OFFSET: usize = 0x0800; +pub const GICD_ICFGR_REG_OFFSET: usize = 0x0C00; +pub const GICD_NSACR_REG_OFFSET: usize = 0x0E00; +pub const GICD_SGIR_REG_OFFSET: usize = 0x0F00; +pub const GICD_CPENDSGIR_REG_OFFSET: usize = 0x0F10; +pub const GICD_SPENDSGIR_REG_OFFSET: usize = 0x0F20; +pub const GICD_IDENTIFICATION_NUM: usize = 12; +pub const GICD_IDENTIFICATION_OFFSET: usize = 0x0FE0; +pub const GICD_END: usize = 0x1000; +pub const GICD_SGIR_ROUTING_SHIFT: usize = 24; +pub const GICD_SGIR_TARGET_LIST_FILTER_SHIFT: usize = 16; +// GICD Register layout. +register_structs! { + #[allow(non_snake_case)] + pub GicDistributer { + (0x0000 => CTRL: ReadWrite), + (0x0004 => TYPER: ReadOnly), + (0x0008 => IIDR: ReadOnly), + (0x000c => reserve0), + (0x0080 => IGROUPR: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0100 => ISENABLER: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0180 => ICENABLER: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0200 => ISPENDR: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0280 => ICPENDR: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0300 => ISACTIVER: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0380 => ICACTIVER: [ReadWrite; GICV2_INT_REGS_NUM]), + (0x0400 => IPRIORITYR: [ReadWrite; GICV2_PRIO_REGS_NUM]), + (0x0800 => ITARGETSR: [ReadWrite; GICV2_TARGET_REGS_NUM]), + (0x0C00 => ICFGR: [ReadWrite; GICV2_CONFIG_REGS_NUM]), + (0x0d00 => reserve1), + (0x0E00 => NSACR: [ReadWrite; GICV2_NSACR_REGS_NUM]), + (0x0F00 => SGIR: ReadWrite), + (0x0f04 => reserve2), + (0x0F10 => CPENDSGIR: [ReadWrite; GICV2_SGI_REGS_NUM]), + (0x0F20 => SPENDSGIR: [ReadWrite; GICV2_SGI_REGS_NUM]), + (0x0F30 => reserved3), + (0x0FD0 => IDENTIFICATION: [ReadOnly; GICD_IDENTIFICATION_NUM]), + (0x1000 => @END), + } +} +unsafe impl Sync for GicDistributer {} + +// GICD is globally unique. +pub static GICD: GicRef = + unsafe { GicRef::new(GICV2.gicd_base as *const GicDistributer) }; +pub static GICD_LOCK: Mutex<()> = Mutex::new(()); + +impl GicDistributer { + // init GICD globally and enable it. + pub fn global_init(&self) { + let prev = self.CTRL.get(); + // Enable the distributor. + self.CTRL.set(prev | GICD_CTLR_EN_BIT as u32); + info!("GICV2: GICD global init done"); + } + + // because some registers are banked, we need every cpu call this function to reset their own interrupt. + pub fn cpu_init(&self) { + // Reset private interrupts. + for i in 0..GICV2_PRIVATE_INTS_NUM / 32 { + self.ICENABLER[i].set(u32::MAX); + self.ICPENDR[i].set(u32::MAX); + self.ICACTIVER[i].set(u32::MAX); + } + // Clear pending sgis. + for i in 0..GICV2_SGI_REGS_NUM / 4 { + self.CPENDSGIR[i].set(u32::MAX); + self.SPENDSGIR[i].set(u32::MAX); + } + // Set all private interrupts has the lowest priority. + for i in 0..GICV2_PRIVATE_INTS_NUM / 4 { + self.IPRIORITYR[i].set(u32::MAX); + } + } + + pub fn set_isenabler(&self, index: usize, value: u32) { + self.ISENABLER[index].set(value); + } + + pub fn set_isactiver(&self, index: usize, value: u32) { + self.ISACTIVER[index].set(value); + } + + pub fn get_isactiver(&self, index: usize) -> u32 { + self.ISACTIVER[index].get() + } + + pub fn set_icenabler(&self, index: usize, value: u32) { + self.ICENABLER[index].set(value); + } + + pub fn set_icactiver(&self, index: usize, value: u32) { + self.ICACTIVER[index].set(value); + } + + pub fn get_itargetsr(&self, index: usize) -> u32 { + self.ITARGETSR[index].get() + } + + pub fn set_sgir(&self, value: u32) { + self.SGIR.set(value); + } + + pub fn get_isenabler(&self, index: usize) -> u32 { + self.ISENABLER[index].get() + } + + pub fn set_icpender(&self, index: usize, value: u32) { + self.ICPENDR[index].set(value); + } + + pub fn get_ispendr(&self, index: usize) -> u32 { + self.ISPENDR[index].get() + } + + pub fn get_spendsgir(&self, index: usize) -> u32 { + self.SPENDSGIR[index].get() + } + + pub fn set_cpendsgir(&self, index: usize, value: u32) { + self.CPENDSGIR[index].set(value); + } + + pub fn set_ispender(&self, index: usize, value: u32) { + self.ISPENDR[index].set(value); + } +} + +// Get the maximum number of interrupt IDs that the GIC supports. +pub fn get_max_int_num() -> usize { + let value = (GICD.TYPER.get() & 0b11111) as usize; + (value + 1) * 32 +} + +// Check if the interrupt is a sgi. +pub fn is_spi(irqn: usize) -> bool { + irqn > 31 && irqn < 1020 +} + +// Get the base address of GICD. +pub fn host_gicd_base() -> usize { + GICV2.gicd_base +} + +pub fn set_ispender(index: usize, value: u32) { + GICD.set_ispender(index, value); +} diff --git a/src/device/irqchip/gicv2/gich.rs b/src/device/irqchip/gicv2/gich.rs new file mode 100644 index 00000000..ebe3362f --- /dev/null +++ b/src/device/irqchip/gicv2/gich.rs @@ -0,0 +1,99 @@ +#![allow(unused_variables)] +#![allow(dead_code)] +use crate::device::irqchip::gicv2::gic_ref::GicRef; +use crate::device::irqchip::gicv2::GICV2; +/// gich layout definition and functions for gich operations. +/// author : ForeverYolo +use tock_registers::interfaces::{Readable, Writeable}; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite}; +pub const GICV2_MAX_LIST_REGS_NUM: usize = 64; +pub const GICV2_GICH_HCR_EN: u32 = 0x1; +pub const GICV2_GICH_VMCR_VEM: u32 = 0x1 << 9; +pub const GICV2_GICH_VMCR_VMGRP1EN: u32 = 0x1 << 1; +pub const GICV2_GICH_VMCR_VMGRP0EN: u32 = 0x1; +pub const GICV2_GICH_LR_GRP1: u32 = 0x1 << 30; +pub const GICV2_GICH_LR_PENDING_STATE: usize = 0x1 << 28; +pub const GICV2_GICH_LR_HW: usize = 0x1 << 31; +pub const GICV2_GICH_HCR_REG_OFFSET: usize = 0x0000; +pub const GICV2_GICH_VTR_REG_OFFSET: usize = 0x0004; +pub const GICV2_GICH_VMCR_REG_OFFSET: usize = 0x0008; +pub const GICV2_GICH_MISR_REG_OFFSET: usize = 0x0010; +pub const GICV2_GICH_EISR_REG_OFFSET: usize = 0x0020; +pub const GICV2_GICH_ELRSR_REG_OFFSET: usize = 0x0030; +pub const GICV2_GICH_APR_REG_OFFSET: usize = 0x00f0; +pub const GICV2_GICH_LR_REG_OFFSET: usize = 0x0100; +pub const GICV2_GICH_END: usize = 0x1000; +pub const GICV2_GICH_VMCR_PMR_SHIFT: u32 = 27; +pub const GICV2_GICH_LR_CPUID_SHIFT: u32 = 10; +pub const GICV2_GICH_LR_PHYSID_SHIFT: u32 = 10; +pub const GICV2_GICH_HCR_UIE: u32 = 0x1 << 1; + +// GICH Register layout. +register_structs! { + #[allow(non_snake_case)] + pub GicHypervisorInterface { + (0x0000 => HCR: ReadWrite), + (0x0004 => VTR: ReadOnly), + (0x0008 => VMCR: ReadWrite), + (0x000c => reserve0), + (0x0010 => MISR: ReadOnly), + (0x0014 => reserve1), + (0x0020 => EISR: [ReadOnly; GICV2_MAX_LIST_REGS_NUM / 32]), + (0x0028 => reserve2), + (0x0030 => ELRSR: [ReadOnly; GICV2_MAX_LIST_REGS_NUM / 32]), + (0x0038 => reserve3), + (0x00f0 => APR: ReadWrite), + (0x00f4 => reserve4), + (0x0100 => LR: [ReadWrite; GICV2_MAX_LIST_REGS_NUM]), + (0x0200 => reserve5), + (0x1000 => @END), + } +} +unsafe impl Sync for GicHypervisorInterface {} +// Each CPU holds one GICH. +pub static GICH: GicRef = + unsafe { GicRef::new(GICV2.gich_base as *const GicHypervisorInterface) }; + +impl GicHypervisorInterface { + // init GICH for each CPU. + pub fn get_lrs_num(&self) -> u32 { + let vtr = self.VTR.get(); + (vtr & 0b11111) + 1 + } + + pub fn clear_all_lr(&self) { + for i in 0..self.get_lrs_num() as usize { + self.LR[i].set(0); + } + self.APR.set(0); + } + + pub fn set_hcr(&self, value: u32) { + self.HCR.set(value); + } + + pub fn set_vmcr(&self, value: u32) { + self.VMCR.set(value); + } + + pub fn get_lr_num(&self) -> u32 { + self.VTR.get() & 0b11111 + } + + pub fn get_elrsr(&self, index: usize) -> u32 { + self.ELRSR[index].get() + } + + pub fn get_lr(&self, index: usize) -> u32 { + self.LR[index].get() + } + + pub fn set_lr(&self, index: usize, value: u32) { + self.LR[index].set(value); + } + + pub fn get_hcr(&self) -> u32 { + self.HCR.get() + } +} diff --git a/src/device/irqchip/gicv2/gicv.rs b/src/device/irqchip/gicv2/gicv.rs new file mode 100644 index 00000000..848cbc81 --- /dev/null +++ b/src/device/irqchip/gicv2/gicv.rs @@ -0,0 +1,14 @@ +#![allow(unused_variables)] +#![allow(dead_code)] +/// gicv layout definition and functions for gicv operations. +/// author : ForeverYolo +/// reference: +/// 1. gicv2 spec : https://www.cl.cam.ac.uk/research/srg/han/ACS-P35/zynq/arm_gic_architecture_specification.pdf +/// note: We don't actually use it. This is to ensure the integrity of GICV2. +use crate::device::irqchip::gicv2::gic_ref::GicRef; +use crate::device::irqchip::gicv2::gicc::GicCpuInterface; +use crate::device::irqchip::gicv2::GICV2; + +// Each CPU holds one GICV, and it has the same register layout as GICC. +pub static GICV: GicRef = + unsafe { GicRef::new(GICV2.gicv_base as *const GicCpuInterface) }; diff --git a/src/device/irqchip/gicv2/mod.rs b/src/device/irqchip/gicv2/mod.rs new file mode 100644 index 00000000..2526f2c7 --- /dev/null +++ b/src/device/irqchip/gicv2/mod.rs @@ -0,0 +1,73 @@ +use crate::device::irqchip::gicv2::gic::MAX_CPU_NUM; +/// The outer layer is defined using gicv2. +/// author: ForeverYolo +/// reference: +/// 1. gicv2 spec : https://www.cl.cam.ac.uk/research/srg/han/ACS-P35/zynq/arm_gic_architecture_specification.pdf +use crate::device::irqchip::gicv2::gicc::GICC; +use crate::device::irqchip::gicv2::gicd::GICD; +use crate::platform::ROOT_ARCH_ZONE_CONFIG; +use crate::zone::Zone; + +// GIC Distributor Definition. +pub mod gicd; + +// GIC CPU Interface Definition. +pub mod gicc; + +// Virtual GIC realization. +pub mod vgic; + +// Physical GIC realization. +pub mod gic; + +// GIC Hypervisor Interface Definition. +pub mod gich; + +// GIC Virtual CPU Interface Definition. +pub mod gicv; + +// GIC Reference warp. +mod gic_ref; + +pub struct Gicv2 { + gicd_base: usize, + gicc_base: usize, + gich_base: usize, + gicv_base: usize, +} + +pub static GICV2: Gicv2 = Gicv2 { + gicd_base: ROOT_ARCH_ZONE_CONFIG.gicd_base, + /* + * Some boards have the GIC CPU interface registers alias, which will overlap DIR register, so we need to add an offset to + * find the last gic cpu alias region. + * ref: https://github.com/Xilinx/qemu-devicetrees/commit/09d4c3200538dc90082fbda9289e2af9794b9a28 + */ + gicc_base: ROOT_ARCH_ZONE_CONFIG.gicc_base + ROOT_ARCH_ZONE_CONFIG.gicc_offset, + gich_base: ROOT_ARCH_ZONE_CONFIG.gich_base, + gicv_base: ROOT_ARCH_ZONE_CONFIG.gicv_base, +}; + +// get base address of GIC and initialize GIC Structs. +pub fn primary_init_early() { + info!("GicDistributer = {:#x?}", GICV2.gicd_base); + info!("GicCpuInterface = {:#x?}", GICV2.gicc_base); + info!("GicHypervisorInterface = {:#x?}", GICV2.gich_base); + info!("GicVCpuInterface = {:#x?}", GICV2.gicv_base); + gic::PENDING_VIRQS.call_once(|| gic::PendingIrqs::new(MAX_CPU_NUM)); +} + +pub fn percpu_init() { + GICC.init(); +} + +pub fn primary_init_late() { + GICD.global_init(); +} + +impl Zone { + pub fn arch_irqchip_reset(&self) { + // todo + panic!("todo: arch_irqchip_reset") + } +} diff --git a/src/device/irqchip/gicv2/vgic.rs b/src/device/irqchip/gicv2/vgic.rs new file mode 100644 index 00000000..341a5627 --- /dev/null +++ b/src/device/irqchip/gicv2/vgic.rs @@ -0,0 +1,299 @@ +use crate::arch::zone::HvArchZoneConfig; +use crate::device::irqchip::gicv2::gicd::{ + get_max_int_num, GICD, GICD_CTRL_REG_OFFSET, GICD_ICACTIVER_REG_OFFSET, + GICD_ICENABLER_REG_OFFSET, GICD_ICFGR_REG_OFFSET, GICD_ICPENDR_REG_OFFSET, + GICD_IDENTIFICATION_NUM, GICD_IDENTIFICATION_OFFSET, GICD_IGROUPR_REG_OFFSET, + GICD_IIDR_REG_OFFSET, GICD_IPRIORITYR_REG_OFFSET, GICD_ISACTIVER_REG_OFFSET, + GICD_ISENABLER_REG_OFFSET, GICD_ISPENDR_REG_OFFSET, GICD_ITARGETSR_REG_OFFSET, GICD_LOCK, + GICD_SGIR_REG_OFFSET, GICD_SGIR_ROUTING_SHIFT, GICD_SGIR_TARGET_LIST_FILTER_SHIFT, + GICD_TYPER_REG_OFFSET, GICV2_CONFIG_REGS_NUM, GICV2_INT_REGS_NUM, GICV2_PRIO_REGS_NUM, + GICV2_TARGET_REGS_NUM, +}; +use crate::device::irqchip::gicv2::GICV2; +use crate::error::HvResult; +use crate::memory::{mmio_perform_access, MMIOAccess, MemFlags, MemoryRegion}; +use crate::percpu::this_zone; +/// This file defines and implements the functional functions of virtual gicv2. +/// author: ForeverYolo +/// reference: +/// 1. gicv2 spec : https://www.cl.cam.ac.uk/research/srg/han/ACS-P35/zynq/arm_gic_architecture_specification.pdf +use crate::zone::Zone; + +const GICV2_REG_WIDTH: usize = 4; + +impl Zone { + // trap all Guest OS accesses to the GIC Distributor registers. + pub fn vgicv2_mmio_init(&mut self, arch: &HvArchZoneConfig) { + if arch.gicd_base == 0 { + panic!("vgicv2_mmio_init: gicd_base is null"); + } + self.mmio_region_register(arch.gicd_base, arch.gicd_size, vgicv2_dist_handler, 0); + } + + // remap the GIC CPU interface register address space to point to the GIC virtual CPU interface registers. + pub fn vgicv2_remap_init(&mut self, arch: &HvArchZoneConfig) { + if arch.gicc_base == 0 || arch.gicv_base == 0 || arch.gicc_size == 0 || arch.gicv_size == 0 + { + panic!("vgicv2_remap_init: gic related address is null"); + } + if arch.gicv_size != arch.gicc_size { + panic!("vgicv2_remap_init: gicv_size not equal to gicc_size"); + } + // map gicv memory region to gicc memory region. + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + arch.gicc_base, + arch.gicv_base, + arch.gicc_size, + MemFlags::READ | MemFlags::WRITE, + )) + .unwrap(); + } + + // store the interrupt number in the irq_bitmap. + pub fn irq_bitmap_init(&mut self, irqs: &[u32]) { + // Enable each cpu's sgi and ppi access permission + self.irq_bitmap[0] = 0xffff_ffff; + for irq in irqs { + self.insert_irq_to_bitmap(*irq); + } + for (index, &word) in self.irq_bitmap.iter().enumerate() { + for bit_position in 0..32 { + if word & (1 << bit_position) != 0 { + let interrupt_number = index * 32 + bit_position; + info!( + "Found interrupt in Zone {} irq_bitmap: {}", + self.id, interrupt_number + ); + } + } + } + } + + // insert the interrupt number into the irq_bitmap. + fn insert_irq_to_bitmap(&mut self, irq: u32) { + assert!(irq < get_max_int_num() as u32); + let irq_index = irq / 32; + let irq_bit = irq % 32; + self.irq_bitmap[irq_index as usize] |= 1 << irq_bit; + } +} + +pub fn reg_range(base: usize, n: usize, size: usize) -> core::ops::Range { + base..(base + (n - 1) * size) +} + +// extend from gicv3, support half-word and byte access. +fn restrict_bitmask_access( + mmio: &mut MMIOAccess, + reg_index: usize, + bits_per_irq: usize, + is_poke: bool, + gicd_base: usize, +) -> HvResult { + let zone = this_zone(); + let zone_r = zone.read(); + let mut access_mask: usize = 0; + /* + * In order to avoid division, the number of bits per irq is limited + * to powers of 2 for the moment. + */ + let irqs_per_reg = 32 / bits_per_irq; + let irq_bits = (1 << bits_per_irq) - 1; + /* First, extract the first interrupt affected by this access */ + let first_irq = reg_index * irqs_per_reg; + trace!("restrict_bitmask_access: first_irq: {}", first_irq); + trace!("mmio.address: {:#x}", mmio.address); + trace!("mmio.size: {:#x}", mmio.size); + trace!("mmio.value: {:#x}", mmio.value); + for irq in 0..irqs_per_reg { + if zone_r.irq_in_zone((first_irq + irq) as _) { + trace!("restrict visit irq {}", first_irq + irq); + access_mask |= irq_bits << (irq * bits_per_irq); + } + } + + let mut other_mask: usize = 0; + for offset in 0..mmio.size { + other_mask |= 0xff << ((mmio.address + offset) % 4) * 8; + } + trace!( + "access_mask: {:#x}, other_mask: {:#x}", + access_mask, + other_mask + ); + + // add the mask of the other bits in the register + let address = mmio.address & 0xfffffffc; + let size = 4; + let offset = mmio.address & 0x3; + let value = mmio.value << (offset * 8); + let real_mask = access_mask & other_mask; + trace!("mmio.iswrite: {}", mmio.is_write); + trace!( + "address: {:#x}, size: {:#x}, value: {:#x}, real_mask: {:#x}", + address, + size, + value, + real_mask + ); + + let offset = mmio.address & 0x3; + mmio.address = mmio.address & 0xfffffffc; + mmio.size = 4; + mmio.value <<= offset * 8; + access_mask = access_mask & other_mask; + + if !mmio.is_write { + /* Restrict the read value */ + mmio_perform_access(gicd_base, mmio); + mmio.value &= access_mask; + mmio.value >>= offset * 8; + return Ok(()); + } + + if !is_poke { + trace!("is_poke is false"); + /* + * Modify the existing value of this register by first reading + * it into mmio->value + * Relies on a spinlock since we need two mmio accesses. + */ + let access_val = mmio.value; + + let _lock = GICD_LOCK.lock(); + + mmio.is_write = false; + mmio_perform_access(gicd_base, mmio); + + mmio.is_write = true; + mmio.value &= !access_mask; + mmio.value |= access_val & access_mask; + mmio_perform_access(gicd_base, mmio); + + // drop lock automatically here + } else { + trace!("poke is true"); + mmio.value &= access_mask; + mmio_perform_access(gicd_base, mmio); + } + Ok(()) +} + +// general GIC Distributor register access. +fn vgicv2_dist_misc_access(mmio: &mut MMIOAccess, gicd_base: usize) -> HvResult { + let reg = mmio.address; + if reg == GICD_CTRL_REG_OFFSET + || reg == GICD_TYPER_REG_OFFSET + || reg == GICD_IIDR_REG_OFFSET + || reg_range( + GICD_IDENTIFICATION_OFFSET, + GICD_IDENTIFICATION_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) + { + if !mmio.is_write { + // ignore write + mmio_perform_access(gicd_base, mmio); + } + } else { + todo!() + } + + Ok(()) +} + +pub fn set_sgi_irq(irq_id: usize, target_list: usize, routing_mode: usize) { + let val = irq_id + | target_list << GICD_SGIR_TARGET_LIST_FILTER_SHIFT + | routing_mode << GICD_SGIR_ROUTING_SHIFT; + trace!( + "set_sgi_irq: irq_id: {}, target_list: {}, routing_mode: {}", + irq_id, + target_list, + routing_mode + ); + trace!("ISENABLER: {:#x}", GICD.get_isenabler(0)); + GICD.set_sgir(val as u32); +} + +// Handle GIC Distributor register accesses. +pub fn vgicv2_dist_handler(mmio: &mut MMIOAccess, _arg: usize) -> HvResult { + let gicd_base = GICV2.gicd_base; + let reg = mmio.address; + + match reg { + reg if reg == GICD_SGIR_REG_OFFSET => { + if !mmio.is_write { + return Ok(()); + } + mmio_perform_access(gicd_base, mmio); + Ok(()) + } + reg if reg_range( + GICD_ITARGETSR_REG_OFFSET, + GICV2_TARGET_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) => + { + restrict_bitmask_access(mmio, (reg & 0x3ff) / 4, 8, false, gicd_base) + } + reg if reg_range( + GICD_ICENABLER_REG_OFFSET, + GICV2_INT_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) + || reg_range( + GICD_ISENABLER_REG_OFFSET, + GICV2_INT_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) + || reg_range(GICD_ICPENDR_REG_OFFSET, GICV2_INT_REGS_NUM, GICV2_REG_WIDTH) + .contains(®) + || reg_range(GICD_ISPENDR_REG_OFFSET, GICV2_INT_REGS_NUM, GICV2_REG_WIDTH) + .contains(®) + || reg_range( + GICD_ICACTIVER_REG_OFFSET, + GICV2_INT_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) + || reg_range( + GICD_ISACTIVER_REG_OFFSET, + GICV2_INT_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) => + { + restrict_bitmask_access(mmio, (reg & 0x7f) / 4, 1, true, gicd_base) + } + reg if reg_range(GICD_IGROUPR_REG_OFFSET, GICV2_INT_REGS_NUM, GICV2_REG_WIDTH) + .contains(®) => + { + restrict_bitmask_access(mmio, (reg & 0x7f) / 4, 1, false, gicd_base) + } + reg if reg_range( + GICD_ICFGR_REG_OFFSET, + GICV2_CONFIG_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) => + { + restrict_bitmask_access(mmio, (reg & 0xff) / 4, 2, false, gicd_base) + } + reg if reg_range( + GICD_IPRIORITYR_REG_OFFSET, + GICV2_PRIO_REGS_NUM, + GICV2_REG_WIDTH, + ) + .contains(®) => + { + restrict_bitmask_access(mmio, (reg & 0x3ff) / 4, 8, false, gicd_base) + } + _ => vgicv2_dist_misc_access(mmio, gicd_base), + } +} diff --git a/src/device/irqchip/gicv3/gicd.rs b/src/device/irqchip/gicv3/gicd.rs index c333948b..b6e81270 100644 --- a/src/device/irqchip/gicv3/gicd.rs +++ b/src/device/irqchip/gicv3/gicd.rs @@ -7,6 +7,8 @@ //! # Glossary //! - SPI - Shared Peripheral Interrupt. #![allow(dead_code)] + +use core::ptr::write_volatile; use spin::Mutex; use super::host_gicd_base; @@ -19,6 +21,7 @@ pub const GICD_CTLR_GRP1NS_ENA: usize = 1 << 1; pub const GICD_TYPER: usize = 0x0004; pub const GICD_IIDR: usize = 0x0008; +pub const GICD_TYPER2: usize = 0x000c; pub const GICD_IGROUPR: usize = 0x0080; pub const GICD_ISENABLER: usize = 0x0100; pub const GICD_ICENABLER: usize = 0x0180; @@ -46,3 +49,12 @@ pub fn enable_gic_are_ns() { .write_volatile(GICD_CTLR_ARE_NS as u32 | GICD_CTLR_GRP1NS_ENA as u32); } } + +pub fn set_ispender(index: usize, value: u32) { + unsafe { + write_volatile( + (host_gicd_base() + GICD_ISPENDR + index * 4) as *mut u32, + value, + ); + } +} diff --git a/src/device/irqchip/gicv3/gicr.rs b/src/device/irqchip/gicv3/gicr.rs index e9bf1634..d494ceb2 100644 --- a/src/device/irqchip/gicv3/gicr.rs +++ b/src/device/irqchip/gicv3/gicr.rs @@ -4,14 +4,25 @@ //! GICC Driver - GIC CPU interface. -use crate::{arch::cpu::this_cpu_id, hypercall::SGI_IPI_ID}; +use core::ptr; + +use alloc::vec::Vec; +use spin::{mutex::Mutex, Once}; + +use crate::{ + arch::cpu::this_cpu_id, + consts::{MAX_CPU_NUM, MAX_ZONE_NUM, PAGE_SIZE}, + hypercall::SGI_IPI_ID, + memory::Frame, + zone::this_zone_id, +}; use super::{ gicd::{ GICD_ICACTIVER, GICD_ICENABLER, GICD_ICFGR, GICD_ICPENDR, GICD_IGROUPR, GICD_IPRIORITYR, - GICD_ISACTIVER, GICD_ISENABLER, GICD_ISPENDR, + GICD_ISACTIVER, GICD_ISENABLER, GICD_ISPENDR, GICD_TYPER, }, - host_gicr_base, + host_gicd_base, host_gicr_base, MAINTENACE_INTERRUPT, }; pub const GICR_CTLR: usize = 0x0000; @@ -19,6 +30,10 @@ pub const GICR_IIDR: usize = 0x0004; pub const GICR_TYPER: usize = 0x0008; pub const GICR_STATUSR: usize = 0x0010; pub const GICR_WAKER: usize = 0x0014; +pub const GICR_SETLPIR: usize = 0x0040; +pub const GICR_CLRLPIR: usize = 0x0048; +pub const GICR_INVLPIR: usize = 0x00a0; +pub const GICR_INVALLR: usize = 0x00b0; pub const GICR_SYNCR: usize = 0x00c0; pub const GICR_PIDR2: usize = 0xffe8; pub const GICR_SGI_BASE: usize = 0x10000; @@ -34,6 +49,9 @@ pub const GICR_IPRIORITYR: usize = GICD_IPRIORITYR; pub const GICR_ICFGR: usize = GICD_ICFGR; pub const GICR_TYPER_LAST: usize = 1 << 4; +pub const GICR_PROPBASER: usize = 0x0070; +pub const GICR_PENDBASER: usize = 0x0078; + pub fn enable_ipi() { let base = host_gicr_base(this_cpu_id()) + GICR_SGI_BASE; @@ -46,12 +64,12 @@ pub fn enable_ipi() { gicr_igroupr0.write_volatile(gicr_igroupr0.read_volatile() | (1 << SGI_IPI_ID)); let gicr_isenabler0 = (base + GICR_ISENABLER) as *mut u32; - gicr_isenabler0.write_volatile(1 << SGI_IPI_ID); - + gicr_isenabler0.write_volatile(1 << SGI_IPI_ID | 1 << MAINTENACE_INTERRUPT); + trace!("gicr_isenabler0: {}", gicr_isenabler0.read_volatile()); let gicr_ipriorityr0 = (base + GICR_IPRIORITYR) as *mut u32; - { - let reg = SGI_IPI_ID / 4; - let offset = SGI_IPI_ID % 4 * 8; + for irq_id in [SGI_IPI_ID, MAINTENACE_INTERRUPT] { + let reg = irq_id / 4; + let offset = irq_id % 4 * 8; let mask = ((1 << 8) - 1) << offset; let p = gicr_ipriorityr0.add(reg as _); let prio = p.read_volatile(); @@ -60,3 +78,71 @@ pub fn enable_ipi() { } } } + +pub struct LpiPropTable { + phy_addr: usize, + frame: Frame, + baser_list: [usize; MAX_ZONE_NUM], +} + +impl LpiPropTable { + fn new() -> Self { + let gicd_typer = + unsafe { ptr::read_volatile((host_gicd_base() + GICD_TYPER) as *const u32) }; + let id_bits = (gicd_typer >> 19) & 0x1f; + let page_num: usize = ((1 << (id_bits + 1)) - 8192) / PAGE_SIZE; + let f = Frame::new_contiguous(page_num, 0).unwrap(); + let propreg = f.start_paddr() | 0x78f; + for id in 0..MAX_CPU_NUM { + let propbaser = host_gicr_base(id) + GICR_PROPBASER; + unsafe { + ptr::write_volatile(propbaser as *mut u64, propreg as _); + } + } + Self { + phy_addr: f.start_paddr(), + frame: f, + baser_list: [0; MAX_ZONE_NUM], + } + } + + fn set_prop_baser(&mut self, zone_id: usize, value: usize) { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.baser_list[zone_id] = value; + } + + fn read_prop_baser(&self, zone_id: usize) -> usize { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.baser_list[zone_id] + } + + fn enable_one_lpi(&self, lpi: usize) { + let addr = self.phy_addr + lpi; + let val: u8 = 0b1; + // no priority + unsafe { + ptr::write_volatile(addr as *mut u8, val as _); + } + } +} + +pub static LPT: Once> = Once::new(); + +pub fn init_lpi_prop() { + LPT.call_once(|| Mutex::new(LpiPropTable::new())); +} + +pub fn set_prop_baser(value: usize) { + let mut lpt = LPT.get().unwrap().lock(); + lpt.set_prop_baser(this_zone_id(), value); +} + +pub fn read_prop_baser() -> usize { + let lpt = LPT.get().unwrap().lock(); + lpt.read_prop_baser(this_zone_id()) +} + +pub fn enable_one_lpi(lpi: usize) { + let lpt = LPT.get().unwrap().lock(); + lpt.enable_one_lpi(lpi); +} diff --git a/src/device/irqchip/gicv3/gits.rs b/src/device/irqchip/gicv3/gits.rs new file mode 100644 index 00000000..66b3cc93 --- /dev/null +++ b/src/device/irqchip/gicv3/gits.rs @@ -0,0 +1,337 @@ +use core::ptr; + +use spin::{mutex::Mutex, Once}; + +use crate::{ + consts::MAX_ZONE_NUM, device::irqchip::gicv3::gicr::enable_one_lpi, memory::Frame, + zone::this_zone_id, +}; + +use super::host_gits_base; + +pub const GITS_CTRL: usize = 0x0000; // enable / disable +pub const GITS_IIDR: usize = 0x0004; // read-only +pub const GITS_TYPER: usize = 0x0008; // read-only +pub const GITS_MPAMIDR: usize = 0x0010; // read-only +pub const GITS_PARTIDR: usize = 0x0014; // supported MPAM sizes +pub const GITS_MPIDR: usize = 0x0018; // read-only, its affinity +pub const GITS_STATUSR: usize = 0x0040; // error reporting +pub const GITS_UMSIR: usize = 0x0048; // unmapped msi +pub const GITS_CBASER: usize = 0x0080; // the addr of command queue +pub const GITS_CWRITER: usize = 0x0088; // rw, write an command to the cmdq, write this reg to tell hw +pub const GITS_CREADR: usize = 0x0090; // read-only, hardware changes it +pub const GITS_BASER: usize = 0x0100; // itt, desc +pub const GITS_COLLECTION_BASER: usize = GITS_BASER + 0x8; +pub const GITS_TRANSLATER: usize = 0x10000 + 0x0040; // to signal an interrupt, written by devices + +pub const PER_CMD_BYTES: usize = 0x20; +pub const PER_CMD_QWORD: usize = PER_CMD_BYTES >> 3; + +fn ring_ptr_update(val: usize) -> usize { + if val >= 0x10000 { + val - 0x10000 + } else { + val + } +} + +// created by root linux, and make a virtual one to non root +pub struct DeviceTable { + baser: usize, +} + +impl DeviceTable { + fn new() -> Self { + let dt_baser_reg = host_gits_base() + GITS_BASER; + let dt_baser = unsafe { ptr::read_volatile(dt_baser_reg as *mut u64) }; + Self { + baser: dt_baser as _, + } + } + + fn set_baser(&mut self, value: usize) { + self.baser = value; + } + + fn read_baser(&self) -> usize { + self.baser + } +} + +pub struct CollectionTable { + baser: usize, +} + +impl CollectionTable { + fn new() -> Self { + let ct_baser_reg = host_gits_base() + GITS_COLLECTION_BASER; + let ct_baser = unsafe { ptr::read_volatile(ct_baser_reg as *mut u64) }; + Self { + baser: ct_baser as _, + } + } + + fn set_baser(&mut self, value: usize) { + self.baser = value; + } + + fn read_baser(&self) -> usize { + self.baser + } +} + +pub struct Cmdq { + phy_addr: usize, + readr: usize, + writer: usize, + frame: Frame, + + phy_base_list: [usize; MAX_ZONE_NUM], + cbaser_list: [usize; MAX_ZONE_NUM], + creadr_list: [usize; MAX_ZONE_NUM], + cwriter_list: [usize; MAX_ZONE_NUM], +} + +impl Cmdq { + fn new() -> Self { + let f = Frame::new_contiguous(16, 0).unwrap(); + trace!("its cmdq base: 0x{:x}", f.start_paddr()); + let r = Self { + phy_addr: f.start_paddr(), + readr: 0, + writer: 0, + frame: f, + phy_base_list: [0; MAX_ZONE_NUM], + cbaser_list: [0; MAX_ZONE_NUM], + creadr_list: [0; MAX_ZONE_NUM], + cwriter_list: [0; MAX_ZONE_NUM], + }; + r.init_real_cbaser(); + r + } + + fn init_real_cbaser(&self) { + let reg = host_gits_base() + GITS_CBASER; + let writer = host_gits_base() + GITS_CWRITER; + let val = 0xb80000000000040f | self.phy_addr; + let ctrl = host_gits_base() + GITS_CTRL; + unsafe { + let origin_ctrl = ptr::read_volatile(ctrl as *mut u64); + ptr::write_volatile(ctrl as *mut u64, origin_ctrl | 0xfffffffffffffffeu64); // turn off, vm will turn on this ctrl + ptr::write_volatile(reg as *mut u64, val as u64); + ptr::write_volatile(writer as *mut u64, 0 as u64); // init cwriter + } + } + + fn set_cbaser(&mut self, zone_id: usize, value: usize) { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.cbaser_list[zone_id] = value; + self.phy_base_list[zone_id] = value & 0xffffffffff000; + } + + fn read_baser(&self, zone_id: usize) -> usize { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.cbaser_list[zone_id] + } + + fn set_cwriter(&mut self, zone_id: usize, value: usize) { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + if value == 0 { + trace!("ignore first write"); + } else { + self.insert_cmd(zone_id, value); + } + + self.cwriter_list[zone_id] = value; + } + + fn read_cwriter(&mut self, zone_id: usize) -> usize { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.cwriter_list[zone_id] + } + + fn read_creadr(&mut self, zone_id: usize) -> usize { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.creadr_list[zone_id] + } + + fn update_creadr(&mut self, zone_id: usize, writer: usize) { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); + self.creadr_list[zone_id] = writer; + } + + // it's ok to add qemu-args: -trace gicv3_gits_cmd_*, remember to remain `enable one lpi` + fn analyze_cmd(&self, value: [u64; 4]) { + let code = (value[0] & 0xff) as usize; + match code { + 0x0b => { + let id = value[0] & 0xffffffff00000000; + let event = value[1] & 0xffffffff; + let icid = value[2] & 0xffff; + enable_one_lpi((event - 8192) as _); + trace!( + "MAPI cmd, for device {:#x}, event = intid = {:#x} -> icid {:#x}", + id >> 32, + event, + icid + ); + } + 0x08 => { + let id = value[0] & 0xffffffff00000000; + let itt_base = (value[2] & 0x000fffffffffffff) >> 8; + trace!( + "MAPD cmd, set ITT: {:#x} to device {:#x}", + itt_base, + id >> 32 + ); + } + 0x0a => { + let id = value[0] & 0xffffffff00000000; + let event = value[1] & 0xffffffff; + let intid = value[1] >> 32; + let icid = value[2] & 0xffff; + enable_one_lpi((intid - 8192) as _); + trace!( + "MAPTI cmd, for device {:#x}, event {:#x} -> icid {:#x} + intid {:#x}", + id >> 32, + event, + icid, + intid + ); + } + 0x09 => { + let icid = value[2] & 0xffff; + let rd_base = (value[2] >> 16) & 0x7ffffffff; + trace!("MAPC cmd, icid {:#x} -> redist {:#x}", icid, rd_base); + } + 0x05 => { + trace!("SYNC cmd"); + } + 0x04 => { + trace!("CLEAR cmd"); + } + 0x0f => { + trace!("DISCARD cmd"); + } + 0x03 => { + trace!("INT cmd"); + } + 0x0c => { + trace!("INV cmd"); + } + 0x0d => { + trace!("INVALL cmd"); + } + _ => { + trace!("other cmd, code: 0x{:x}", code); + } + } + } + + fn insert_cmd(&mut self, zone_id: usize, writer: usize) { + assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id"); + + let zone_addr = self.phy_base_list[zone_id]; + + let origin_readr = self.creadr_list[zone_id]; + + let cmd_size = writer - origin_readr; + let cmd_num = cmd_size / PER_CMD_BYTES; + + trace!("cmd size: {:#x}, cmd num: {:#x}", cmd_size, cmd_num); + + let mut vm_cmdq_addr = zone_addr + origin_readr; + let mut real_cmdq_addr = self.phy_addr + self.readr; + + for _cmd_id in 0..cmd_num { + unsafe { + let v = ptr::read_volatile(vm_cmdq_addr as *mut [u64; PER_CMD_QWORD]); + self.analyze_cmd(v.clone()); + + for i in 0..PER_CMD_QWORD { + ptr::write_volatile(real_cmdq_addr as *mut u64, v[i] as u64); + real_cmdq_addr += 8; + } + } + vm_cmdq_addr += PER_CMD_BYTES; + vm_cmdq_addr = ring_ptr_update(vm_cmdq_addr - zone_addr) + zone_addr; + real_cmdq_addr = ring_ptr_update(real_cmdq_addr - self.phy_addr) + self.phy_addr; + } + + self.writer += cmd_size; + self.writer = ring_ptr_update(self.writer); // ring buffer ptr + let cwriter = host_gits_base() + GITS_CWRITER; + let readr = host_gits_base() + GITS_CREADR; + unsafe { + ptr::write_volatile(cwriter as *mut u64, self.writer as _); + loop { + self.readr = (ptr::read_volatile(readr as *mut u64)) as usize; // hw readr + if self.readr == self.writer { + trace!( + "readr={:#x}, writer={:#x}, its cmd end", + self.readr, + self.writer + ); + break; + } else { + } + } + } + self.update_creadr(zone_id, writer); + } +} + +pub static DT: Once> = Once::new(); +pub static CMDQ: Once> = Once::new(); +pub static CT: Once> = Once::new(); + +pub fn gits_init() { + DT.call_once(|| Mutex::new(DeviceTable::new())); + CMDQ.call_once(|| Mutex::new(Cmdq::new())); + CT.call_once(|| Mutex::new(CollectionTable::new())); +} + +pub fn set_cbaser(value: usize) { + let mut cmdq = CMDQ.get().unwrap().lock(); + cmdq.set_cbaser(this_zone_id(), value); +} + +pub fn read_cbaser() -> usize { + let cmdq = CMDQ.get().unwrap().lock(); + cmdq.read_baser(this_zone_id()) +} + +pub fn set_cwriter(value: usize) { + let mut cmdq = CMDQ.get().unwrap().lock(); + cmdq.set_cwriter(this_zone_id(), value); +} + +pub fn read_cwriter() -> usize { + let mut cmdq = CMDQ.get().unwrap().lock(); + cmdq.read_cwriter(this_zone_id()) +} + +pub fn read_creadr() -> usize { + let mut cmdq = CMDQ.get().unwrap().lock(); + cmdq.read_creadr(this_zone_id()) +} + +pub fn read_dt_baser() -> usize { + let dt = DT.get().unwrap().lock(); + dt.read_baser() +} + +pub fn set_dt_baser(value: usize) { + let mut dt = DT.get().unwrap().lock(); + dt.set_baser(value); +} + +pub fn read_ct_baser() -> usize { + let ct = CT.get().unwrap().lock(); + ct.read_baser() +} + +pub fn set_ct_baser(value: usize) { + let mut ct = CT.get().unwrap().lock(); + ct.set_baser(value); +} diff --git a/src/device/irqchip/gicv3/mod.rs b/src/device/irqchip/gicv3/mod.rs index f07c6698..261d1ad9 100644 --- a/src/device/irqchip/gicv3/mod.rs +++ b/src/device/irqchip/gicv3/mod.rs @@ -78,17 +78,24 @@ #![allow(dead_code)] pub mod gicd; pub mod gicr; +pub mod gits; pub mod vgic; use core::arch::asm; use core::ptr::write_volatile; use core::sync::atomic::AtomicU64; -use spin::Once; +use alloc::collections::btree_map::BTreeMap; +use alloc::collections::vec_deque::VecDeque; +use alloc::vec::Vec; +use gicr::{init_lpi_prop, GICR_ISENABLER, GICR_SGI_BASE}; +use gits::gits_init; +use spin::{Mutex, Once}; use self::gicd::{enable_gic_are_ns, GICD_ICACTIVER, GICD_ICENABLER}; use self::gicr::enable_ipi; use crate::arch::aarch64::sysreg::{read_sysreg, smc_arg1, write_sysreg}; +use crate::arch::cpu::this_cpu_id; use crate::config::root_zone_config; use crate::consts::MAX_CPU_NUM; @@ -96,6 +103,7 @@ use crate::event::check_events; use crate::hypercall::SGI_IPI_ID; use crate::zone::Zone; +const ICH_HCR_UIE: u64 = 1 << 1; //TODO: add Distributor init pub fn gicc_init() { //TODO: add Redistributor init @@ -145,32 +153,11 @@ static TIMER_INTERRUPT_COUNTER: AtomicU64 = AtomicU64::new(0); const TIMER_INTERRUPT_PRINT_TIMES: u64 = 50; pub fn gicv3_handle_irq_el1() { - if let Some(irq_id) = pending_irq() { - // enum ipi_msg_type { - // IPI_WAKEUP, - // IPI_TIMER, - // IPI_RESCHEDULE, - // IPI_CALL_FUNC, - // IPI_CPU_STOP, - // IPI_IRQ_WORK, - // IPI_COMPLETION, - // /* - // * CPU_BACKTRACE is special and not included in NR_IPI - // * or tracable with trace_ipi_* - // */ - // IPI_CPU_BACKTRACE, - // /* - // * SGI8-15 can be reserved by secure firmware, and thus may - // * not be usable by the kernel. Please keep the above limited - // * to at most 8 entries. - // */ - // }; - //SGI + while let Some(irq_id) = pending_irq() { if irq_id < 8 { deactivate_irq(irq_id); let mut ipi_handled = false; if irq_id == SGI_IPI_ID as _ { - trace!("SGI_IPI_ID"); ipi_handled = check_events(); } if !ipi_handled { @@ -184,17 +171,28 @@ pub fn gicv3_handle_irq_el1() { if irq_id == 27 { // virtual timer interrupt TIMER_INTERRUPT_COUNTER.fetch_add(1, core::sync::atomic::Ordering::SeqCst); - if TIMER_INTERRUPT_COUNTER.load(core::sync::atomic::Ordering::SeqCst) % TIMER_INTERRUPT_PRINT_TIMES == 0 { - debug!("Virtual timer interrupt, counter = {}", TIMER_INTERRUPT_COUNTER.load(core::sync::atomic::Ordering::SeqCst)); + if TIMER_INTERRUPT_COUNTER.load(core::sync::atomic::Ordering::SeqCst) + % TIMER_INTERRUPT_PRINT_TIMES + == 0 + { + debug!( + "Virtual timer interrupt, counter = {}", + TIMER_INTERRUPT_COUNTER.load(core::sync::atomic::Ordering::SeqCst) + ); } - } - // debug!("spi/ppi get {}", irq_id); - //inject phy irq - if irq_id > 31 { + } else if irq_id == 25 { + // maintenace interrupt + handle_maintenace_interrupt(); + } else if irq_id > 31 { + //inject phy irq debug!("*** get spi_irq id = {}", irq_id); + } else { + warn!("not konw irq id = {}", irq_id); + } + if irq_id != 25 { + inject_irq(irq_id, true); } deactivate_irq(irq_id); - inject_irq(irq_id, true); } } trace!("handle done") @@ -202,8 +200,7 @@ pub fn gicv3_handle_irq_el1() { fn pending_irq() -> Option { let iar = read_sysreg!(icc_iar1_el1) as usize; - if iar >= 0x3fe { - // spurious + if iar == 0x3ff { None } else { Some(iar as _) @@ -212,10 +209,9 @@ fn pending_irq() -> Option { fn deactivate_irq(irq_id: usize) { write_sysreg!(icc_eoir1_el1, irq_id as u64); - if irq_id < 16 { + if irq_id < 16 || irq_id == 25 { write_sysreg!(icc_dir_el1, irq_id as u64); } - //write_sysreg!(icc_dir_el1, irq_id as usize); } fn read_lr(id: usize) -> u64 { @@ -271,7 +267,77 @@ fn write_lr(id: usize, val: u64) { } } -pub fn inject_irq(irq_id: usize, is_hardware: bool) { +// virtual interrupts waiting to inject +static PENDING_VIRQS: Once = Once::new(); +pub const MAINTENACE_INTERRUPT: u64 = 25; +struct PendingIrqs { + inner: Vec>>, +} + +impl PendingIrqs { + fn new(max_cpus: usize) -> Self { + let mut vs = vec![]; + for _ in 0..max_cpus { + let v = Mutex::new(VecDeque::new()); + vs.push(v) + } + Self { inner: vs } + } + + fn add_irq(&self, irq_id: usize, is_hardware: bool) -> Option<()> { + match self.inner.get(this_cpu_id()) { + Some(pending_irqs) => { + let mut irqs = pending_irqs.lock(); + irqs.push_back((irq_id, is_hardware)); + Some(()) + } + _ => None, + } + } + + fn fetch_irq(&self) -> Option<(usize, bool)> { + match self.inner.get(this_cpu_id()) { + Some(pending_irqs) => { + let mut irqs = pending_irqs.lock(); + irqs.pop_front() + } + _ => None, + } + } +} + +// Enable or disable an underflow maintenace interrupt. +fn enable_maintenace_interrupt(is_enable: bool) { + trace!("enable_maintenace_interrupt, is_enable is {}", is_enable); + let mut hcr = read_sysreg!(ich_hcr_el2); + trace!("hcr is {}", hcr); + if is_enable { + hcr |= ICH_HCR_UIE; + } else { + hcr &= !ICH_HCR_UIE; + } + write_sysreg!(ich_hcr_el2, hcr); +} + +fn handle_maintenace_interrupt() { + trace!("handle_maintenace_interrupt"); + let pending_irqs = PENDING_VIRQS.get().unwrap(); + while let Some((irq_id, is_hardware)) = pending_irqs.fetch_irq() { + let is_injected: bool = inject_irq(irq_id, is_hardware); + if is_injected { + trace!("inject pending irq in maintenace interrupt"); + } + if !is_injected { + pending_irqs.add_irq(irq_id, is_hardware); + enable_maintenace_interrupt(true); + return; + } + } + enable_maintenace_interrupt(false); +} + +/// Inject virtual interrupt to vCPU, return whether it not needs to add pending queue. +pub fn inject_irq(irq_id: usize, is_hardware: bool) -> bool { // mask const LR_VIRTIRQ_MASK: usize = (1 << 32) - 1; @@ -291,13 +357,24 @@ pub fn inject_irq(irq_id: usize, is_hardware: bool) { // if a virtual interrupt is enabled and equals to the physical interrupt irq_id if (lr_val & LR_VIRTIRQ_MASK) == irq_id { trace!("virtual irq {} enables again", irq_id); - return; + return true; } } - // debug!("To Inject IRQ {}, find lr {}", irq_id, free_ir); + trace!("To Inject IRQ {}, find lr {}", irq_id, free_ir); if free_ir == -1 { - panic!("full lr"); + trace!("all list registers are valid, add to pending queue"); + // If all list registers are valid, add this virtual irq to pending queue, + // and enable an underflow maintenace interrupt. When list registers are + // all invalid or only one is valid, the maintenace interrupt will occur, + // hvisor will execute handle_maintenace_interrupt function. + PENDING_VIRQS + .get() + .unwrap() + .add_irq(irq_id, is_hardware) + .unwrap(); + enable_maintenace_interrupt(true); + return false; } else { let mut val = irq_id as u64; //v intid val |= 1 << 60; //group 1 @@ -308,6 +385,7 @@ pub fn inject_irq(irq_id: usize, is_hardware: bool) { val |= (irq_id as u64) << 32; //pINTID } write_lr(free_ir as usize, val); + return true; } } @@ -320,6 +398,8 @@ pub struct Gic { pub gicr_base: usize, pub gicd_size: usize, pub gicr_size: usize, + pub gits_base: usize, + pub gits_size: usize, } pub fn host_gicd_base() -> usize { @@ -331,6 +411,10 @@ pub fn host_gicr_base(id: usize) -> usize { GIC.get().unwrap().gicr_base + id * PER_GICR_SIZE } +pub fn host_gits_base() -> usize { + GIC.get().unwrap().gits_base +} + pub fn host_gicd_size() -> usize { GIC.get().unwrap().gicd_size } @@ -339,6 +423,10 @@ pub fn host_gicr_size() -> usize { GIC.get().unwrap().gicr_size } +pub fn host_gits_size() -> usize { + GIC.get().unwrap().gits_size +} + pub fn is_spi(irqn: u32) -> bool { irqn > 31 && irqn < 1020 } @@ -357,13 +445,23 @@ pub fn disable_irqs() { pub fn primary_init_early() { let root_config = root_zone_config(); - + GIC.call_once(|| Gic { - gicd_base: root_config.arch.gicd_base, - gicr_base: root_config.arch.gicr_base, - gicd_size: root_config.arch.gicd_size, - gicr_size: root_config.arch.gicr_size, + gicd_base: root_config.arch_config.gicd_base, + gicr_base: root_config.arch_config.gicr_base, + gicd_size: root_config.arch_config.gicd_size, + gicr_size: root_config.arch_config.gicr_size, + gits_base: root_config.arch_config.gits_base, + gits_size: root_config.arch_config.gits_size, }); + + init_lpi_prop(); + + if host_gits_base() != 0 && host_gits_size() != 0 { + gits_init(); + } + + PENDING_VIRQS.call_once(|| PendingIrqs::new(MAX_CPU_NUM)); debug!("gic = {:#x?}", GIC.get().unwrap()); } diff --git a/src/device/irqchip/gicv3/vgic.rs b/src/device/irqchip/gicv3/vgic.rs index 4293bff2..df152907 100644 --- a/src/device/irqchip/gicv3/vgic.rs +++ b/src/device/irqchip/gicv3/vgic.rs @@ -1,8 +1,18 @@ use alloc::sync::Arc; -use super::{gicd::GICD_LOCK, host_gicd_size, is_spi}; +use super::{gicd::GICD_LOCK, is_spi}; use crate::{ - arch::zone::HvArchZoneConfig, consts::MAX_CPU_NUM, device::irqchip::gicv3::{gicd::*, gicr::*, host_gicd_base, host_gicr_base, PER_GICR_SIZE}, error::HvResult, memory::{mmio_perform_access, MMIOAccess}, percpu::{get_cpu_data, this_zone}, zone::Zone + arch::zone::HvArchZoneConfig, + consts::MAX_CPU_NUM, + device::irqchip::gicv3::{ + gicd::*, gicr::*, gits::*, host_gicd_base, host_gicr_base, host_gits_base, + MAINTENACE_INTERRUPT, PER_GICR_SIZE, + }, + error::HvResult, + hypercall::SGI_IPI_ID, + memory::{mmio_perform_access, MMIOAccess}, + percpu::{get_cpu_data, this_zone}, + zone::{this_zone_id, Zone}, }; pub fn reg_range(base: usize, n: usize, size: usize) -> core::ops::Range { @@ -11,13 +21,15 @@ pub fn reg_range(base: usize, n: usize, size: usize) -> core::ops::Range impl Zone { pub fn vgicv3_mmio_init(&mut self, arch: &HvArchZoneConfig) { - let gicd_base = if arch.gicd_base == 0 {host_gicd_base()} else {arch.gicd_base}; - let gicr_base = if arch.gicr_base == 0 {host_gicr_base(0)} else {arch.gicr_base}; - let gicd_size = if arch.gicd_size == 0 {host_gicd_size()} else {arch.gicd_size}; + if arch.gicd_base == 0 || arch.gicr_base == 0 { + panic!("vgicv3_mmio_init: gicd_base or gicr_base is null"); + } + + self.mmio_region_register(arch.gicd_base, arch.gicd_size, vgicv3_dist_handler, 0); + self.mmio_region_register(arch.gits_base, arch.gits_size, vgicv3_its_handler, 0); - self.mmio_region_register(gicd_base, gicd_size, vgicv3_dist_handler, 0); for cpu in 0..MAX_CPU_NUM { - let gicr_base = gicr_base + cpu * PER_GICR_SIZE; + let gicr_base = arch.gicr_base + cpu * PER_GICR_SIZE; debug!("registering gicr {} at {:#x?}", cpu, gicr_base); self.mmio_region_register(gicr_base, PER_GICR_SIZE, vgicv3_redist_handler, cpu); } @@ -111,6 +123,19 @@ pub fn vgicv3_redist_handler(mmio: &mut MMIOAccess, cpu: usize) -> HvResult { trace!("gicr({}) mmio = {:#x?}", cpu, mmio); let gicr_base = host_gicr_base(cpu); match mmio.address { + GICR_CTLR => { + if get_cpu_data(cpu).zone.is_none() { + if !mmio.is_write { + mmio_perform_access(gicr_base, mmio); + } + } else if Arc::ptr_eq(&this_zone(), get_cpu_data(cpu).zone.as_ref().unwrap()) { + mmio_perform_access(gicr_base, mmio); + } else { + if !mmio.is_write { + mmio_perform_access(gicr_base, mmio); + } + } + } GICR_TYPER => { mmio_perform_access(gicr_base, mmio); if cpu == MAX_CPU_NUM - 1 { @@ -121,17 +146,64 @@ pub fn vgicv3_redist_handler(mmio: &mut MMIOAccess, cpu: usize) -> HvResult { // Read-only registers that might be used by a zone to find the redistributor corresponding to a CPU. Keep them accessible. mmio_perform_access(gicr_base, mmio); } + GICR_PENDBASER => { + // every redist have its own pending tbl + mmio_perform_access(gicr_base, mmio); + if mmio.is_write { + trace!("write pending tbl base : 0x{:x}", mmio.value); + } else { + trace!("read pending tbl base : 0x{:x}", mmio.value); + } + } + GICR_PROPBASER => { + // all the redist share one prop tbl + // mmio_perform_access(gicr_base, mmio); + if mmio.is_write { + set_prop_baser(mmio.value); + trace!("write prop tbl base : 0x{:x}!", mmio.value); + } else { + mmio.value = read_prop_baser(); + trace!("read prop tbl base : 0x{:x}", mmio.value); + } + } GICR_SYNCR => { mmio.value = 0; } - _ => { + GICR_SETLPIR => { + mmio_perform_access(gicr_base, mmio); + } + reg if reg == GICR_CLRLPIR || reg == GICR_INVALLR => { + mmio_perform_access(gicr_base, mmio); + } + GICR_INVLPIR => { + // Presume that this write is to enable an LPI. + // Or we need to check all the proptbl created by vm. + enable_one_lpi((mmio.value & 0xffffffff) - 8192); + } + reg if reg == GICR_STATUSR + || reg == GICR_WAKER + || reg == GICR_SGI_BASE + GICR_ISENABLER + || reg == GICR_SGI_BASE + GICR_ICENABLER + || reg == GICR_SGI_BASE + GICR_ISPENDR + || reg == GICR_SGI_BASE + GICR_ICPENDR + || reg == GICR_SGI_BASE + GICR_ISACTIVER + || reg == GICR_SGI_BASE + GICR_ICACTIVER + || reg_range(GICR_SGI_BASE + GICR_IPRIORITYR, 8, 4).contains(®) + || reg_range(GICR_SGI_BASE + GICR_ICFGR, 2, 4).contains(®) => + { if Arc::ptr_eq(&this_zone(), get_cpu_data(cpu).zone.as_ref().unwrap()) { + // avoid linux disable maintenance interrupt + if reg == GICR_SGI_BASE + GICR_ICENABLER { + mmio.value &= !(1 << MAINTENACE_INTERRUPT); + mmio.value &= !(1 << SGI_IPI_ID); + } // ignore access to foreign redistributors mmio_perform_access(gicr_base, mmio); } else { trace!("*** gicv3_gicr_mmio_handler: ignore access to foreign redistributors ***"); } } + _ => {} } HvResult::Ok(()) } @@ -162,6 +234,7 @@ fn vgicv3_dist_misc_access(mmio: &mut MMIOAccess, gicd_base: usize) -> HvResult || reg == GICD_CTLR || reg == GICD_TYPER || reg == GICD_IIDR + || reg == GICD_TYPER2 { if !mmio.is_write { // ignore write @@ -207,3 +280,95 @@ pub fn vgicv3_dist_handler(mmio: &mut MMIOAccess, _arg: usize) -> HvResult { _ => vgicv3_dist_misc_access(mmio, gicd_base), } } + +pub fn vgicv3_its_handler(mmio: &mut MMIOAccess, _arg: usize) -> HvResult { + let gits_base = host_gits_base(); + let reg = mmio.address; + + // mmio_perform_access(gits_base, mmio); + match reg { + GITS_CTRL => { + mmio_perform_access(gits_base, mmio); + if mmio.is_write { + trace!("write GITS_CTRL: {:#x}", mmio.value); + } else { + trace!("read GITS_CTRL: {:#x}", mmio.value); + } + } + GITS_CBASER => { + if mmio.is_write { + set_cbaser(mmio.value); + trace!("write GITS_CBASER: {:#x}", mmio.value); + } else { + mmio.value = read_cbaser(); + trace!("read GITS_CBASER: {:#x}", mmio.value); + } + } + GITS_BASER => { + if this_zone_id() == 0 { + mmio_perform_access(gits_base, mmio); + } else { + if mmio.is_write { + set_dt_baser(mmio.value); + } else { + mmio.value = read_dt_baser(); + } + } + if mmio.is_write { + trace!("write GITS_BASER: 0x{:016x}", mmio.value); + } else { + trace!("read GITS_BASER: 0x{:016x}", mmio.value); + } + } + GITS_COLLECTION_BASER => { + if this_zone_id() == 0 { + mmio_perform_access(gits_base, mmio); + } else { + if mmio.is_write { + set_ct_baser(mmio.value); + } else { + mmio.value = read_ct_baser(); + } + } + if mmio.is_write { + trace!("write GITS_COLL_BASER: 0x{:016x}", mmio.value); + } else { + trace!("read GITS_COLL_BASER: 0x{:016x}", mmio.value); + } + } + GITS_CWRITER => { + if mmio.is_write { + trace!("write GITS_CWRITER: {:#x}", mmio.value); + set_cwriter(mmio.value); + } else { + mmio.value = read_cwriter(); + trace!("read GITS_CWRITER: {:#x}", mmio.value); + } + } + GITS_CREADR => { + mmio.value = read_creadr(); + trace!("read GITS_CREADER: {:#x}", mmio.value); + } + GITS_TYPER => { + mmio_perform_access(gits_base, mmio); + trace!("GITS_TYPER: {:#x}", mmio.value); + } + _ => { + mmio_perform_access(gits_base, mmio); + if mmio.is_write { + trace!( + "write GITS offset: {:#x}, 0x{:016x}", + mmio.address, + mmio.value + ); + } else { + trace!( + "read GITS offset: {:#x}, 0x{:016x}", + mmio.address, + mmio.value + ); + } + } + } + Ok(()) +} diff --git a/src/device/irqchip/ls7a2000/chip.rs b/src/device/irqchip/ls7a2000/chip.rs new file mode 100644 index 00000000..0e406507 --- /dev/null +++ b/src/device/irqchip/ls7a2000/chip.rs @@ -0,0 +1,775 @@ +// for 3A5000 board and 7A2000 bridge chip registers +// wheatfox 2024.2.27 + +use crate::device::common::MMIODerefWrapper; +use alloc::string::String; +use core::ptr::*; +use tock_registers::fields::FieldValue; +use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; +use tock_registers::register_bitfields; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +const PHY_ADDR_BITMASK: usize = 0x0000_ffff_ffff_ffff; +const DMW0_PREFIX: usize = 0x8000_0000_0000_0000; +const DMW1_PREFIX: usize = 0x9000_0000_0000_0000; + +#[macro_export] +macro_rules! DMW_TO_PHY { + ($addr:expr) => { + $addr & 0x0000_ffff_ffff_ffffusize + }; +} + +#[macro_export] +macro_rules! PHY_TO_DMW_CACHED { + ($addr:expr) => { + $addr | 0x9000_0000_0000_0000usize + }; +} + +#[macro_export] +macro_rules! PHY_TO_DMW_UNCACHED { + ($addr:expr) => { + $addr | 0x8000_0000_0000_0000usize + }; +} + +register_bitfields! [ + u8, + // Chip Config Version Register + ChipConfVer [ + VER_NUM OFFSET(0) NUMBITS(8) [], + ], +]; + +register_bitfields! [ + // Chip Feature Register + u16, + ChipFeature [ + CENTIGRADE OFFSET(0) NUMBITS(1) [], + NODE_COUNT OFFSET(1) NUMBITS(1) [], + MSI_SUPPORT OFFSET(2) NUMBITS(1) [], + EXTIOI_SUPPORT OFFSET(3) NUMBITS(1) [], + IPI_PERCORE OFFSET(4) NUMBITS(1) [], + FREQ_PERCORE OFFSET(5) NUMBITS(1) [], + FREQ_SCALE OFFSET(6) NUMBITS(1) [], + DVFS_V1_SUPPORT OFFSET(7) NUMBITS(1) [], + TSENSOR_SUPPORT OFFSET(8) NUMBITS(1) [], + INT_DECODE OFFSET(9) NUMBITS(1) [], + LEGACY_MODE OFFSET(10) NUMBITS(1) [], + GUEST_MODE OFFSET(11) NUMBITS(1) [], + ], +]; + +register_bitfields! [ + u64, + // Manufacturer Name + ManufacturerName [ + VENDOR OFFSET(0) NUMBITS(64) [], + ], + // Chip Name + ChipName [ + ID OFFSET(0) NUMBITS(64) [], + ], + OtherFunctionConfig [ + DISABLE_JTAG OFFSET(0) NUMBITS(1) [], + DISABLE_JTAG_LA464 OFFSET(1) NUMBITS(1) [], + DISABLE_LA132 OFFSET(2) NUMBITS(1) [], + DISABLE_JTAG_LA132 OFFSET(3) NUMBITS(1) [], + DISABLE_ANITFUSE0 OFFSET(4) NUMBITS(1) [], + DISABLE_ANITFUSE1 OFFSET(5) NUMBITS(1) [], + DISABLE_ID OFFSET(6) NUMBITS(1) [], + EXT_INT_EN OFFSET(48) NUMBITS(1) [], + INT_ENCODE OFFSET(49) NUMBITS(1) [], + ], +]; + +register_structs! { + #[allow(non_snake_case)] + pub ChipConfigRegs { + (0x0000 => pub chip_conf_ver: ReadOnly), + (0x0001 => _reserved0: [u8; 7]), + (0x0008 => pub chip_feature: ReadWrite), + (0x000a => _reserved1: [u8; 6]), + (0x0010 => pub manufacturer_name: ReadOnly), + (0x0018 => _reserved2: [u8; 8]), + (0x0020 => pub chip_name: ReadOnly), + (0x0028 => @END), + } +} + +register_bitfields![ + u32, + pub Intisr [ + // please refer to manual for detailed description, the field name is simplified + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Inten [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Intenset [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Intenclr [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Intenedge [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Core0Intisr [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Core1Intisr [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Core2Intisr [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], + pub Core3Intisr [ + SC0 OFFSET(0) NUMBITS(1) [], + SC1 OFFSET(1) NUMBITS(1) [], + SC2 OFFSET(2) NUMBITS(1) [], + SC3 OFFSET(3) NUMBITS(1) [], + GPIO28 OFFSET(4) NUMBITS(1) [], + GPIO29 OFFSET(5) NUMBITS(1) [], + GPIO30 OFFSET(6) NUMBITS(1) [], + GPIO31 OFFSET(7) NUMBITS(1) [], + I2C0 OFFSET(8) NUMBITS(1) [], + I2C1 OFFSET(9) NUMBITS(1) [], + UART0 OFFSET(10) NUMBITS(1) [], + MC0 OFFSET(11) NUMBITS(1) [], + MC1 OFFSET(12) NUMBITS(1) [], + SPI OFFSET(13) NUMBITS(1) [], + THSENS OFFSET(14) NUMBITS(1) [], + UART1 OFFSET(15) NUMBITS(1) [], + HT0 OFFSET(16) NUMBITS(8) [], + HT1 OFFSET(24) NUMBITS(8) [], + ], +]; + +// 3A5000 manual p73 +register_structs! { + #[allow(non_snake_case)] + pub ChipLegacyIntCtrlRegs { + (0x0000 => pub int_isr: ReadWrite), + (0x0004 => pub int_en: ReadOnly), + (0x0008 => pub int_en_set: ReadWrite), + (0x000c => pub int_en_clr: ReadWrite), + (0x0010 => _reserved0: [u8; 4]), + (0x0014 => pub int_en_edge: ReadWrite), + (0x0018 => _reserved1: [u8; 8]), + (0x0020 => pub core0_intisr: ReadWrite), + (0x0024 => _reserved2: [u8; 4]), + (0x0028 => pub core1_intisr: ReadWrite), + (0x002c => _reserved3: [u8; 4]), + (0x0030 => pub core2_intisr: ReadWrite), + (0x0034 => _reserved4: [u8; 4]), + (0x0038 => pub core3_intisr: ReadWrite), + (0x003c => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub ChipOtherFunctionRegs { + (0x0000 => pub other_function_config: ReadWrite), + (0x0008 => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub ChipLegacyIntRouteRegs { + // [3:0] route cpu core id (CPU0-CPU3 on 3A5000), for example, 4'b0101 means CPU0 and CPU2 + // [7:4] route cpu core int pin id (INT0-INT3 on 3A5000), for example, 4'b0010 means INT1 + (0x0000 => pub entry0: ReadWrite), // GPIO24/16/8/0 + (0x0001 => pub entry1: ReadWrite), // GPIO25/17/9/1 + (0x0002 => pub entry2: ReadWrite), // GPIO26/18/10/2 + (0x0003 => pub entry3: ReadWrite), // GPIO27/19/11/3 + (0x0004 => pub entry4: ReadWrite), // GPIO28/20/12/4 + (0x0005 => pub entry5: ReadWrite), // GPIO29/21/13/5 + (0x0006 => pub entry6: ReadWrite), // GPIO30/22/14/6 + (0x0007 => pub entry7: ReadWrite), // GPIO31/23/15/7 + (0x0008 => pub entry8: ReadWrite), // I2C0 + (0x0009 => pub entry9: ReadWrite), // I2C1 + (0x000a => pub entry10: ReadWrite), // UART0 + (0x000b => pub entry11: ReadWrite), // MC0 + (0x000c => pub entry12: ReadWrite), // MC1 + (0x000d => pub entry13: ReadWrite), // SPI + (0x000e => pub entry14: ReadWrite), // THSENS + (0x000f => pub entry15: ReadWrite), // UART1 + (0x0010 => pub entry16: ReadWrite), // HT0-INT0 + (0x0011 => pub entry17: ReadWrite), // HT0-INT1 + (0x0012 => pub entry18: ReadWrite), // HT0-INT2 + (0x0013 => pub entry19: ReadWrite), // HT0-INT3 + (0x0014 => pub entry20: ReadWrite), // HT0-INT4 + (0x0015 => pub entry21: ReadWrite), // HT0-INT5 + (0x0016 => pub entry22: ReadWrite), // HT0-INT6 + (0x0017 => pub entry23: ReadWrite), // HT0-INT7 + (0x0018 => pub entry24: ReadWrite), // HT1-INT0 + (0x0019 => pub entry25: ReadWrite), // HT1-INT1 + (0x001a => pub entry26: ReadWrite), // HT1-INT2 + (0x001b => pub entry27: ReadWrite), // HT1-INT3 + (0x001c => pub entry28: ReadWrite), // HT1-INT4 + (0x001d => pub entry29: ReadWrite), // HT1-INT5 + (0x001e => pub entry30: ReadWrite), // HT1-INT6 + (0x001f => pub entry31: ReadWrite), // HT1-INT7 + (0x0020 => @END), + } +} + +register_bitfields![ + u64, + Extioi_en0 [ + EXTIOI_EN0 OFFSET(0) NUMBITS(63) [] + ], + Extioi_en1 [ + EXTIOI_EN1 OFFSET(0) NUMBITS(63) [] + ], + Extioi_en2 [ + EXTIOI_EN2 OFFSET(0) NUMBITS(63) [] + ], + Extioi_en3 [ + EXTIOI_EN3 OFFSET(0) NUMBITS(63) [] + ], + Extioi_sr0 [ + EXTIOI_SR0 OFFSET(0) NUMBITS(63) [] + ], + Extioi_sr1 [ + EXTIOI_SR1 OFFSET(0) NUMBITS(63) [] + ], + Extioi_sr2 [ + EXTIOI_SR2 OFFSET(0) NUMBITS(63) [] + ], + Extioi_sr3 [ + EXTIOI_SR3 OFFSET(0) NUMBITS(63) [] + ], +]; + +// 3A5000 manual p75 +register_structs! { + #[allow(non_snake_case)] + pub ChipExtioiEnableRegs { + (0x0000 => pub extioi_en0: ReadWrite), + (0x0008 => pub extioi_en1: ReadWrite), + (0x0010 => pub extioi_en2: ReadWrite), + (0x0018 => pub extioi_en3: ReadWrite), + (0x0020 => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub ChipExtioiBounceRegs { + (0x0000 => pub extioi_bounce0: ReadWrite), + (0x0008 => pub extioi_bounce1: ReadWrite), + (0x0010 => pub extioi_bounce2: ReadWrite), + (0x0018 => pub extioi_bounce3: ReadWrite), + (0x0020 => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub ChipExtioiStatusRegs { + (0x0000 => pub extioi_sr0: ReadOnly), + (0x0008 => pub extioi_sr1: ReadOnly), + (0x0010 => pub extioi_sr2: ReadOnly), + (0x0018 => pub extioi_sr3: ReadOnly), + (0x0020 => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + pub ChipExtioiRouteRegs { + (0x0000 => pub extioi_map0: ReadWrite), + (0x0001 => pub extioi_map1: ReadWrite), + (0x0002 => pub extioi_map2: ReadWrite), + (0x0003 => pub extioi_map3: ReadWrite), + (0x0004 => pub extioi_map4: ReadWrite), + (0x0005 => pub extioi_map5: ReadWrite), + (0x0006 => pub extioi_map6: ReadWrite), + (0x0007 => pub extioi_map7: ReadWrite), + (0x0008 => @END), + } +} + +const MMIO_BASE: usize = PHY_TO_DMW_UNCACHED!(0x1fe0_0000); + +const CHIP_CONFIG_BASE: usize = MMIO_BASE; +pub static CHIP_CONFIG: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_CONFIG_BASE as usize) }; + +const CHIP_LEGACY_INT_CTRL_BASE: usize = MMIO_BASE + 0x1420; +pub static CHIP_LEGACY_INT_CTRL: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_LEGACY_INT_CTRL_BASE as usize) }; + +const CHIP_OTHER_FUNCTION_BASE: usize = MMIO_BASE + 0x420; +pub static CHIP_OTHER_FUNCTION: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_OTHER_FUNCTION_BASE as usize) }; + +const CHIP_LEGACY_INT_ROUTE_BASE: usize = MMIO_BASE + 0x1400; +pub static CHIP_LEGACY_INT_ROUTE: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_LEGACY_INT_ROUTE_BASE as usize) }; + +const CHIP_EXTIOI_ENABLE_BASE: usize = MMIO_BASE + 0x1600; +pub static CHIP_EXTIOI_ENABLE: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_EXTIOI_ENABLE_BASE as usize) }; + +const CHIP_EXTIOI_STATUS_BASE: usize = MMIO_BASE + 0x1700; +pub static CHIP_EXTIOI_STATUS: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_EXTIOI_STATUS_BASE as usize) }; + +// this indicates the configs for irq routing to which INT pin, not target cpu core +// the 256 irqs are grouped into 8 group to control the target INT pin - wheatfox +const CHIP_EXTIOI_ROUTE_BASE: usize = MMIO_BASE + 0x14c2; +pub static CHIP_EXTIOI_ROUTE: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_EXTIOI_ROUTE_BASE as usize) }; + +const CHIP_EXTIOI_BOUNCE_BASE: usize = MMIO_BASE + 0x1680; +pub static CHIP_EXTIOI_BOUNCE: MMIODerefWrapper = + unsafe { MMIODerefWrapper::new(CHIP_EXTIOI_BOUNCE_BASE as usize) }; + +const CHIP_EXTIOI_DEBUG_SEND_BASE: usize = MMIO_BASE + 0x1140; + +// this is the target cpu core for all 256 irq sources - wheatfox +const CHIP_EXTIOI_ROUTE_CORE_BASE: usize = MMIO_BASE + 0x1c00; +const CHIP_EXTIOI_NODE_TYPE_BASE: usize = MMIO_BASE + 0x14a0; + +// 3A5000 manual p118 +const CHIP_HT_CONFIG_BASE: usize = PHY_TO_DMW_UNCACHED!(0xfd_fb00_0000); +const CHIP_HT_INT_VECTOR_BASE: usize = CHIP_HT_CONFIG_BASE + 0x80; +const CHIP_HT_INT_EN_BASE: usize = CHIP_HT_CONFIG_BASE + 0xa0; + +/******************************************** */ +/* SOME BASIC FUCNTIONS */ +/******************************************** */ + +pub fn get_chip_conf_ver() -> u64 { + CHIP_CONFIG.chip_conf_ver.read(ChipConfVer::VER_NUM) as u64 +} + +pub fn get_chip_ht_device_id() -> usize { + // offset 0x00, size 0x2 bytes + let mut device_id: usize; + unsafe { + device_id = read_volatile((CHIP_HT_CONFIG_BASE + 0x00) as *const u16) as usize; + } + device_id +} + +pub fn get_chip_ht_vendor_id() -> usize { + // offset 0x02, size 0x2 bytes + let mut vendor_id: usize; + unsafe { + vendor_id = read_volatile((CHIP_HT_CONFIG_BASE + 0x02) as *const u16) as usize; + } + vendor_id +} + +pub fn debug_set_extioi_intvec(irq: u8) { + unsafe { + write_volatile((CHIP_EXTIOI_DEBUG_SEND_BASE + 0x00) as *mut u8, irq); + } +} + +pub fn get_ipi_percore() -> bool { + CHIP_CONFIG.chip_feature.read(ChipFeature::IPI_PERCORE) != 0 +} + +pub fn get_guest_mode() -> bool { + CHIP_CONFIG.chip_feature.read(ChipFeature::GUEST_MODE) != 0 +} + +pub fn set_guest_mode() { + CHIP_CONFIG + .chip_feature + .modify(ChipFeature::GUEST_MODE::SET); +} + +pub fn clear_guest_mode() { + CHIP_CONFIG + .chip_feature + .modify(ChipFeature::GUEST_MODE::CLEAR); +} + +fn u64tostr(x: u64) -> String { + // 0x00003030_30354133 to 3A5000 + let mut s = String::new(); + for i in 0..8 { + let c = (x >> (i * 8)) & 0xff; + if c == 0 { + break; + } + s.push(c as u8 as char); + } + s +} + +#[no_mangle] +pub fn print_chip_info() { + info!( + "loongarch64:: irqchip:: chip config version: {:#x}", + get_chip_conf_ver() + ); + info!( + "loongarch64:: irqchip:: chip feature extioi support: {}", + CHIP_CONFIG.chip_feature.read(ChipFeature::EXTIOI_SUPPORT) != 0 + ); + info!( + "loongarch64:: irqchip:: manufacturer name: {}", + u64tostr(CHIP_CONFIG.manufacturer_name.read(ManufacturerName::VENDOR)) + ); + info!( + "loongarch64:: irqchip:: chip name: {}", + u64tostr(CHIP_CONFIG.chip_name.read(ChipName::ID)) + ); +} + +/******************************************** */ +/* LEGACY INT FUCNTIONS */ +/******************************************** */ + +pub fn legacy_int_enable_all() { + CHIP_LEGACY_INT_CTRL.int_en_set.modify(Intenset::UART0::SET); + // CHIP_LEGACY_INT_CTRL.int_en_set.set(0xffff_ffff); +} + +pub fn csr_disable_new_codec() { + // set CSR[0x420][49] to false to use legacy vector mask route + use core::arch::asm; + let mut tmp_: usize; + unsafe { + asm!("csrrd {}, 0x420", out(reg) tmp_); + } + tmp_ &= !(1 << 49); + unsafe { + asm!("csrwr {}, 0x420", in(reg) tmp_); + } +} + +pub fn legacy_int_route_all() { + // route to CPU0 INT0 for testing + let cpu_mask = 0b0001; // route to CPU0 + let int_mask = 0b0001; // route to INT0 + let mask = (int_mask << 4) | cpu_mask; + CHIP_LEGACY_INT_ROUTE.entry10.set(mask); + CHIP_LEGACY_INT_ROUTE.entry31.set(mask); + info!("(legacy_int_route_all) route all legacy int to CPU0 INT0"); +} + +pub fn legacy_int_dump() { + info!( + "(legacy_int_dump) int_isr_raw = 0x{:x}", + CHIP_LEGACY_INT_CTRL.int_isr.get() + ); + info!( + "(legacy_int_dump) int_en_raw = 0x{:x}", + CHIP_LEGACY_INT_CTRL.int_en.get() + ); + info!( + "(legacy_int_dump) int_en_set_raw = 0x{:x}", + CHIP_LEGACY_INT_CTRL.int_en_set.get() + ); + info!( + "(legacy_int_dump) int_en_clr_raw = 0x{:x}", + CHIP_LEGACY_INT_CTRL.int_en_clr.get() + ); + info!( + "(legacy_int_dump) int_en_edge_raw = 0x{:x}", + CHIP_LEGACY_INT_CTRL.int_en_edge.get() + ); + info!( + "(legacy_int_dump) core0_intisr_raw = 0x{:x}", + CHIP_LEGACY_INT_CTRL.core0_intisr.get() + ); +} + +/******************************************** */ +/* EXTIOI FUCNTIONS */ +/******************************************** */ + +pub fn extioi_mode_enable() { + CHIP_OTHER_FUNCTION + .other_function_config + .modify(OtherFunctionConfig::EXT_INT_EN::SET); +} + +pub fn extioi_mode_disable() { + CHIP_OTHER_FUNCTION + .other_function_config + .modify(OtherFunctionConfig::EXT_INT_EN::CLEAR); +} + +pub fn extioi_is_enabled() -> bool { + let status = CHIP_OTHER_FUNCTION + .other_function_config + .read(OtherFunctionConfig::EXT_INT_EN); + status != 0 +} + +pub fn extioi_int_enable_all() { + CHIP_EXTIOI_ENABLE.extioi_en0.set(0xffff_ffff_ffff_ffff); + CHIP_EXTIOI_ENABLE.extioi_en1.set(0xffff_ffff_ffff_ffff); + CHIP_EXTIOI_ENABLE.extioi_en2.set(0xffff_ffff_ffff_ffff); + CHIP_EXTIOI_ENABLE.extioi_en3.set(0xffff_ffff_ffff_ffff); + #[cfg(feature = "extioi_debug")] + { + // dump ht int vector and enable + /* + info!("(extioi_int_enable_all) ht int vector dump:"); + for i in 0..8 { + let addr = CHIP_HT_INT_VECTOR_BASE + i * 32; + let mut val: u32; + unsafe { + val = read_volatile(addr as *const u32); + } + info!("(extioi_int_enable_all) ht int vector[{}]: 0x{:x}", i, val); + } + */ + // enable all int in HT config reg + for i in 0..8 { + let addr = CHIP_HT_INT_EN_BASE + i * 32; + unsafe { + write_volatile(addr as *mut u32, 0xffff_ffff); + } + } + info!("(extioi_int_enable_all) ht int enable dump:"); + for i in 0..8 { + let addr = CHIP_HT_INT_EN_BASE + i * 32; + let mut val: u32; + unsafe { + val = read_volatile(addr as *const u32); + } + info!("(extioi_int_enable_all) ht int enable[{}]: 0x{:x}", i, val); + } + } +} + +// rcore refenrence +// https://github.com/Godones/rCoreloongArch/blob/master/kernel/src/loongarch/extioi.rs +pub fn extioi_int_route_pin_all() { + csr_disable_new_codec(); // use legacy vector codec + let mask = 0b0000_0001u8; // INT1 + CHIP_EXTIOI_ROUTE.extioi_map0.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map1.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map2.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map3.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map4.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map5.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map6.set(mask); + CHIP_EXTIOI_ROUTE.extioi_map7.set(mask); +} + +pub fn extioi_int_route_core_all() { + // first disable all extioi bounce + CHIP_EXTIOI_BOUNCE.extioi_bounce0.set(0); + CHIP_EXTIOI_BOUNCE.extioi_bounce1.set(0); + CHIP_EXTIOI_BOUNCE.extioi_bounce2.set(0); + CHIP_EXTIOI_BOUNCE.extioi_bounce3.set(0); + // write CHIP_EXTIOI_NODE_TYPE_BASE mmio (each with 2 bytes) + unsafe { + // set to 16'b0000_0000_0000_0001 + // which means only trigger node0 + core::ptr::write_volatile(CHIP_EXTIOI_NODE_TYPE_BASE as *mut u16, 0x0001); + } + // from CHIP_EXTIOI_ROUTE_CORE_BASE tp CHIP_EXTIOI_ROUTE_CORE_BASE + 0xff + // for each MMIO byte, set to cput core 0, data = 8'b0000_0001 + // which is [EXT_IOI_node_type0][CPU0 MASK] + let mask = 0b0000_0001u8; + for i in 0..256 { + let addr = CHIP_EXTIOI_ROUTE_CORE_BASE + i; + unsafe { + core::ptr::write_volatile(addr as *mut u8, mask); + } + } +} + +pub fn extioi_dump() { + info!( + "(extioi_dump) extioi_en0=0x{:x}", + CHIP_EXTIOI_ENABLE.extioi_en0.get() + ); + info!( + "(extioi_dump) extioi_en1=0x{:x}", + CHIP_EXTIOI_ENABLE.extioi_en1.get() + ); + info!( + "(extioi_dump) extioi_en2=0x{:x}", + CHIP_EXTIOI_ENABLE.extioi_en2.get() + ); + info!( + "(extioi_dump) extioi_en3=0x{:x}", + CHIP_EXTIOI_ENABLE.extioi_en3.get() + ); + info!( + "(extioi_dump) extioi_bounce0=0x{:x}", + CHIP_EXTIOI_BOUNCE.extioi_bounce0.get() + ); + info!( + "(extioi_dump) extioi_bounce1=0x{:x}", + CHIP_EXTIOI_BOUNCE.extioi_bounce1.get() + ); + info!( + "(extioi_dump) extioi_bounce2=0x{:x}", + CHIP_EXTIOI_BOUNCE.extioi_bounce2.get() + ); + info!( + "(extioi_dump) extioi_bounce3=0x{:x}", + CHIP_EXTIOI_BOUNCE.extioi_bounce3.get() + ); + info!( + "(extioi_dump) extioi_sr0=0x{:x}", + CHIP_EXTIOI_STATUS.extioi_sr0.get() + ); + info!( + "(extioi_dump) extioi_sr1=0x{:x}", + CHIP_EXTIOI_STATUS.extioi_sr1.get() + ); + info!( + "(extioi_dump) extioi_sr2=0x{:x}", + CHIP_EXTIOI_STATUS.extioi_sr2.get() + ); + info!( + "(extioi_dump) extioi_sr3=0x{:x}", + CHIP_EXTIOI_STATUS.extioi_sr3.get() + ); +} diff --git a/src/device/irqchip/ls7a2000/mod.rs b/src/device/irqchip/ls7a2000/mod.rs new file mode 100644 index 00000000..b96fe263 --- /dev/null +++ b/src/device/irqchip/ls7a2000/mod.rs @@ -0,0 +1,137 @@ +#![allow(unused)] + +use crate::{ + arch::{ + cpu::this_cpu_id, + ipi::*, + register::{read_gcsr_estat, write_gcsr_estat}, + }, + consts::MAX_CPU_NUM, + zone::Zone, +}; +use chip::*; +use spin::Mutex; + +pub mod chip; + +pub fn primary_init_early() { + if this_cpu_id() != 0 { + info!("loongarch64: irqchip: primary_init_early do nothing on secondary cpus"); + return; + } + info!("loongarch64: irqchip: primary_init_early checking iochip configs"); + print_chip_info(); + csr_disable_new_codec(); + legacy_int_enable_all(); + extioi_mode_disable(); + info!("loongarch64: irqchip: testing percore IPI feature"); + let is_ipi_percore = get_ipi_percore(); + info!( + "loongarch64: irqchip: percore IPI feature: {}", + is_ipi_percore + ); +} +pub fn primary_init_late() { + warn!("loongarch64: irqchip: primary_init_late do nothing"); +} +pub fn percpu_init() { + info!("loongarch64: irqchip: running percpu_init"); + clear_all_ipi(this_cpu_id()); + enable_ipi(this_cpu_id()); + ecfg_ipi_enable(); + info!("loongarch64: irqchip: dumping ipi registers"); + dump_ipi_registers(); +} + +const INT_SWI0: usize = 0; +const INT_SWI1: usize = 1; +const INT_HWI0: usize = 2; +const INT_HWI1: usize = 3; +const INT_HWI2: usize = 4; +const INT_HWI3: usize = 5; +const INT_HWI4: usize = 6; +const INT_HWI5: usize = 7; +const INT_HWI6: usize = 8; +const INT_HWI7: usize = 9; +const INT_PERF: usize = 10; +const INT_TIMER: usize = 11; +const INT_IPI: usize = 12; + +/// inject irq to THIS cpu +pub fn inject_irq(_irq: usize, is_hardware: bool) { + debug!( + "loongarch64: inject_irq, _irq: {}, is_hardware: {}", + _irq, is_hardware + ); + print!("\0"); + if _irq > INT_IPI { + error!("loongarch64: inject_irq: _irq > {}, not valid", INT_IPI); + return; + } + let bit = 1 << _irq; + if _irq >= INT_HWI0 && _irq <= INT_HWI7 { + // use gintc to inject + use crate::arch::register::gintc; + gintc::set_hwis(bit >> INT_HWI0); + } else { + // use gcsr to inject, just set the bit + let mut gcsr_estat = read_gcsr_estat(); + gcsr_estat |= bit; + write_gcsr_estat(gcsr_estat); + } + let mut status = GLOBAL_IRQ_INJECT_STATUS.lock(); + status.cpu_status[this_cpu_id()].status = InjectionStatus::Injecting; + drop(status); +} + +/// clear the injecting irq ctrl bit on THIS cpu +pub fn clear_hwi_injected_irq() { + use crate::arch::register::gintc; + gintc::set_hwis(0); + // gintc::set_hwip(0); + // gintc::set_hwic(0xff); + let mut gintc_raw = 0usize; + use core::arch::asm; + unsafe { + asm!("csrrd {0}, 0x52", out(reg) gintc_raw); + } + debug!( + "loongarch64: clear_hwi_injected_irq, current gintc: {:#x}", + gintc_raw + ); + print!("\0"); + let mut status = GLOBAL_IRQ_INJECT_STATUS.lock(); + status.cpu_status[this_cpu_id()].status = InjectionStatus::Idle; + drop(status); +} + +impl Zone { + pub fn arch_irqchip_reset(&self) { + warn!("loongarch64: irqchip: arch_irqchip_reset do nothing"); + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum InjectionStatus { + Injecting, + Idle, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PercpuInjectionStatus { + pub status: InjectionStatus, + pub irqs: [u32; 32], +} + +#[derive(Debug)] +pub struct GlobalInjectionStatus { + pub cpu_status: [PercpuInjectionStatus; MAX_CPU_NUM], +} + +pub static GLOBAL_IRQ_INJECT_STATUS: Mutex = + Mutex::new(GlobalInjectionStatus { + cpu_status: [PercpuInjectionStatus { + status: InjectionStatus::Idle, + irqs: [0; 32], + }; MAX_CPU_NUM], + }); diff --git a/src/device/irqchip/mod.rs b/src/device/irqchip/mod.rs index 5e59644b..c676a7d7 100644 --- a/src/device/irqchip/mod.rs +++ b/src/device/irqchip/mod.rs @@ -1,11 +1,68 @@ -#[cfg(target_arch = "aarch64")] +use crate::arch::zone::HvArchZoneConfig; +use crate::zone::Zone; + +#[cfg(all(feature = "gicv2", target_arch = "aarch64"))] +pub mod gicv2; +#[cfg(all(feature = "gicv2", target_arch = "aarch64"))] +pub use gicv2::{ + gic::inject_irq, gicd::set_ispender, percpu_init, primary_init_early, primary_init_late, + vgic::set_sgi_irq, +}; + +#[cfg(all(feature = "gicv3", target_arch = "aarch64"))] pub mod gicv3; +#[cfg(all(feature = "gicv3", target_arch = "aarch64"))] +pub use gicv3::{ + gicd::set_ispender, inject_irq, percpu_init, primary_init_early, primary_init_late, +}; #[cfg(target_arch = "riscv64")] +#[cfg(feature = "plic")] pub mod plic; -#[cfg(target_arch = "aarch64")] -pub use gicv3::{percpu_init, primary_init_early, primary_init_late}; +#[cfg(target_arch = "riscv64")] +#[cfg(feature = "aia")] +pub mod aia; + +#[cfg(target_arch = "loongarch64")] +pub mod ls7a2000; + +pub fn gic_handle_irq() { + #[cfg(all(feature = "gicv2", target_arch = "aarch64"))] + gicv2::gic::gicv2_handle_irq(); + #[cfg(all(feature = "gicv3", target_arch = "aarch64"))] + gicv3::gicv3_handle_irq_el1(); +} + +impl Zone { + pub fn mmio_init(&mut self, hv_config: &HvArchZoneConfig) { + #[cfg(all(feature = "gicv2", target_arch = "aarch64"))] + { + self.vgicv2_mmio_init(hv_config); + self.vgicv2_remap_init(hv_config); + } + #[cfg(all(feature = "gicv3", target_arch = "aarch64"))] + { + self.vgicv3_mmio_init(hv_config); + } + } +} + +#[cfg(target_arch = "riscv64")] +#[cfg(feature = "plic")] +pub use plic::{ + host_plic, inject_irq, percpu_init, primary_init_early, primary_init_late, + vplic_global_emul_handler, vplic_hart_emul_handler, +}; #[cfg(target_arch = "riscv64")] -pub use plic::{init_early, init_late, irqchip_cpu_init, per_cpu_init}; +#[cfg(feature = "aia")] +pub use aia::aplic::{ + host_aplic, inject_irq, percpu_init, primary_init_early, primary_init_late, vaplic_emul_handler, +}; + +#[cfg(target_arch = "loongarch64")] +pub mod ls7a2000; + +#[cfg(target_arch = "loongarch64")] +pub use ls7a2000::{inject_irq, percpu_init, primary_init_early, primary_init_late}; diff --git a/src/device/irqchip/plic/mod.rs b/src/device/irqchip/plic/mod.rs index cbdfb18f..918cd52e 100644 --- a/src/device/irqchip/plic/mod.rs +++ b/src/device/irqchip/plic/mod.rs @@ -1,18 +1,31 @@ -use fdt::Fdt; +use crate::config::root_zone_config; +use crate::memory::GuestPhysAddr; +use crate::platform::qemu_riscv64::*; +use crate::zone::Zone; +use crate::{arch::cpu::ArchCpu, percpu::this_cpu_data}; +use riscv::register::hvip; +use riscv_decode::Instruction; use spin::{Once, RwLock}; - -use crate::platform::qemu_riscv64::PLIC_MAX_CONTEXT; - -pub fn init_early(host_fdt: &Fdt) { - let plic_info = host_fdt.find_node("/soc/plic").unwrap(); +pub fn primary_init_early() { + let root_config = root_zone_config(); init_plic( - plic_info.reg().unwrap().next().unwrap().starting_address as usize, - plic_info.reg().unwrap().next().unwrap().size.unwrap(), + root_config.arch_config.plic_base as usize, + root_config.arch_config.plic_size as usize, ); } - +pub fn primary_init_late() { + //nothing to do +} +pub fn percpu_init() { + //nothing to do +} +pub fn inject_irq(_irq: usize, _is_hardware: bool) { + //nothing to do +} pub static PLIC: Once> = Once::new(); - +pub fn host_plic<'a>() -> &'a RwLock { + PLIC.get().expect("Uninitialized hypervisor plic!") +} pub struct Plic { pub base: usize, pub size: usize, @@ -27,9 +40,199 @@ impl Plic { claim_complete: [0u32; PLIC_MAX_CONTEXT], } } + pub fn set_priority(&self, irq_id: usize, priority: u32) { + let addr = self.base + PLIC_PRIORITY_BASE + irq_id * 4; + unsafe { + core::ptr::write_volatile(addr as *mut u32, priority); + } + } + pub fn read_enable(&self, context: usize, irq_base: usize) -> u32 { + let addr = self.base + PLIC_ENABLE_BASE + context * 0x80 + irq_base; + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + pub fn set_enable(&self, context: usize, irq_base: usize, value: u32) { + let addr = self.base + PLIC_ENABLE_BASE + context * 0x80 + irq_base; + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + pub fn set_threshold(&self, context: usize, value: u32) { + let addr = self.base + PLIC_GLOBAL_SIZE + context * 0x1000; + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + ///TODO:move to vplic + pub fn emul_claim(&self, context: usize) -> u32 { + self.claim_complete[context] + } + pub fn emul_complete(&mut self, context: usize, irq_id: u32) { + let addr = self.base + PLIC_GLOBAL_SIZE + 0x1000 * context + 0x4; + unsafe { + core::ptr::write_volatile(addr as *mut u32, irq_id as u32); + } + + self.claim_complete[context] = 0; + unsafe { + hvip::clear_vseip(); + } + } } +pub fn vplic_global_emul_handler( + current_cpu: &mut ArchCpu, + addr: GuestPhysAddr, + inst: Instruction, +) { + //TODO:check irq id for vm + let host_plic = host_plic(); + let offset = addr.wrapping_sub(host_plic.read().base); + // priority/pending/enable + if offset >= PLIC_PRIORITY_BASE && offset < PLIC_ENABLE_BASE { + // priority/pending + match inst { + Instruction::Sw(i) => { + // guest write irq priority + //TODO:check irq id for vm + let irq_id = offset / 4; + let value = current_cpu.x[i.rs2() as usize] as u32; + host_plic.write().set_priority(irq_id, value); + debug!( + "PLIC set priority write addr@{:#x} irq id {} valuse{:#x}", + addr, irq_id, value + ); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else if offset >= PLIC_ENABLE_BASE && offset < PLIC_GLOBAL_SIZE { + //enable + match inst { + Instruction::Lw(i) => { + // guest read + let vcontext = (offset - 0x002000) / 0x80; + let first_cpu = this_cpu_data() + .zone + .as_ref() + .unwrap() + .read() + .cpu_set + .first_cpu() + .unwrap(); + let context = vcontext + first_cpu * 2; + let irq_base = (offset - 0x002000) % 0x80; + let value = host_plic.read().read_enable(context, irq_base); + current_cpu.x[i.rd() as usize] = value as usize; + debug!( + "PLIC set enable read addr@{:#x} -> context {}=>{} irq_base {}~{} value {:#x}", + addr, + vcontext, + context, + irq_base * 8, + irq_base * 8 + 31, + value + ); + } + Instruction::Sw(i) => { + // guest write irq enable + let vcontext = (offset - 0x002000) / 0x80; + let first_cpu = this_cpu_data() + .zone + .as_ref() + .unwrap() + .read() + .cpu_set + .first_cpu() + .unwrap(); + let context = vcontext + first_cpu * 2; + let irq_base = (offset - 0x002000) % 0x80; + let value = current_cpu.x[i.rs2() as usize] as u32; + host_plic.write().set_enable(context, irq_base, value); + debug!( + "PLIC set enable write addr@{:#x} -> context{}=>{} irq_base {}~{} value {:#x}", + addr, + vcontext, + context, + irq_base * 8, + irq_base * 8 + 31, + value + ); + } + _ => panic!("Unexpected instruction {:?}", inst), + } + } else { + panic!("Invalid address: {:#x}", addr); + } +} +pub fn vplic_hart_emul_handler(current_cpu: &mut ArchCpu, addr: GuestPhysAddr, inst: Instruction) { + trace!("handle PLIC access addr@{:#x}", addr); + let host_plic = host_plic(); + let offset = addr.wrapping_sub(host_plic.read().base); + // threshold/claim/complete + if offset >= PLIC_GLOBAL_SIZE && offset < PLIC_TOTAL_SIZE { + let vcontext = (offset - PLIC_GLOBAL_SIZE) / 0x1000; + let first_cpu = this_cpu_data() + .zone + .as_ref() + .unwrap() + .read() + .cpu_set + .first_cpu() + .unwrap(); + let context = vcontext + first_cpu * 2; + let index = (offset - PLIC_GLOBAL_SIZE) & 0xfff; + if index == 0 { + // threshold + match inst { + Instruction::Sw(i) => { + // guest write threshold register to plic core + let value = current_cpu.x[i.rs2() as usize] as u32; + host_plic.write().set_threshold(context, value); + debug!( + "PLIC set threshold write addr@{:#x} context{} -> {:#x}", + addr, context, value + ); + } + _ => panic!("Unexpected instruction threshold {:?}", inst), + } + } else if index == 0x4 { + // claim/complete + // htracking!("claim/complete"); + match inst { + Instruction::Lw(i) => { + // guest read claim from plic core + current_cpu.x[i.rd() as usize] = host_plic.read().emul_claim(context) as usize; + debug!( + "PLIC claim read addr@{:#x} context{} -> {:#x}", + addr, + context, + host_plic.read().claim_complete[context] + ); + } + Instruction::Sw(i) => { + // guest write complete to plic core + let value = current_cpu.x[i.rs2() as usize] as u32; + host_plic.write().emul_complete(context, value); + // todo: guest pa -> host pa + debug!( + "PLIC complete write addr@:{:#x} context {} -> {:#x}", + addr, context, value + ); + } + _ => panic!("Unexpected instruction claim/complete {:?}", inst), + } + } else { + panic!("Invalid address: {:#x}", addr); + } + } else { + panic!("Invalid address: {:#x}", addr); + } +} pub fn init_plic(plic_base: usize, plic_size: usize) { let plic = Plic::new(plic_base, plic_size); PLIC.call_once(|| RwLock::new(plic)); } +impl Zone { + pub fn arch_irqchip_reset(&self) { + //TODO + } +} diff --git a/src/device/uart/loongson_uart/mod.rs b/src/device/uart/loongson_uart/mod.rs new file mode 100644 index 00000000..a4872166 --- /dev/null +++ b/src/device/uart/loongson_uart/mod.rs @@ -0,0 +1,184 @@ +#![allow(dead_code)] +use crate::device::common::MMIODerefWrapper; +use crate::memory::addr::{PhysAddr, VirtAddr}; +use core::arch::global_asm; +use spin::Mutex; +use tock_registers::register_bitfields; +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +pub const UART_BASE_PHYS: PhysAddr = 0x1fe001e0; +pub const UART_BASE_VIRT: VirtAddr = 0x80000000_1fe001e0; + +const UART_REF_CLK: usize = 100000000; // 100MHz for 3A5000's SYS_CLK +const UART_DIV_HI: usize = ((UART_REF_CLK + (115200 * 8)) / (115200 * 16)) >> 8; +const UART_DIV_LO: usize = ((UART_REF_CLK + (115200 * 8)) / (115200 * 16)) & 0xff; + +const BOARD_UART0_VADDR: usize = UART_BASE_VIRT; +const BOARD_UART1_VADDR: usize = BOARD_UART0_VADDR + 0x100; +const BOARD_UART2_VADDR: usize = BOARD_UART0_VADDR + 0x200; +const BOARD_UART3_VADDR: usize = BOARD_UART0_VADDR + 0x300; + +global_asm!( + include_str!("uart.S"), + CONSOLE_BASE_ADDR = const BOARD_UART0_VADDR, + UART_DIV_HI = const UART_DIV_HI, + UART_DIV_LO = const UART_DIV_LO +); + +extern "C" { + fn init_serial(); + fn print_char(c: u8); + fn get_char() -> u8; +} + +register_bitfields! { + u8, // register width + + // UART DATA REGISTER + DAT [ + TxFIFO OFFSET(0) NUMBITS(8) [] + ], + + // UART INTERRUPT ENABLE REGISTER + IER [ + IME OFFSET(3) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ], + ILE OFFSET(2) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ], + ITxE OFFSET(1) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ], + IRxE OFFSET(0) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ] + ], + + // UART INTERRUPT INDICATOR REGISTER + IIR [ + II OFFSET(1) NUMBITS(3) [ + RecvLineStatus = 0b011, + RecvDataAvail = 0b010, + CharTimeout = 0b110, + THREmpty = 0b001, + ModemStatus = 0b000, + ], + INTp OFFSET(0) NUMBITS(1) [], + ], + + // UART FIFO CONTROL REGISTER + FCR [ + TL OFFSET(6) NUMBITS(2) [], + Txset OFFSET(2) NUMBITS(1) [], + Rxset OFFSET(1) NUMBITS(1) [], + ], + + // UART LINE CONTROL REGISTER + LCR [ + dlab OFFSET(7) NUMBITS(1) [], + bcb OFFSET(6) NUMBITS(1) [], + spb OFFSET(5) NUMBITS(1) [], + eps OFFSET(4) NUMBITS(1) [], + pe OFFSET(3) NUMBITS(1) [], + sb OFFSET(2) NUMBITS(1) [], + bec OFFSET(1) NUMBITS(1) [], + ], + + // UART MODEM CONTROL REGISTER + MCR [ + Loop OFFSET(4) NUMBITS(1) [], + OUT2 OFFSET(3) NUMBITS(1) [], + OUT1 OFFSET(2) NUMBITS(1) [], + RTSC OFFSET(1) NUMBITS(1) [], + DTRC OFFSET(0) NUMBITS(1) [], + ], + + // UART LINE STATUS REGISTER + LSR [ + ERROR OFFSET(7) NUMBITS(1) [], + TE OFFSET(6) NUMBITS(1) [], + TFE OFFSET(5) NUMBITS(1) [], + BI OFFSET(4) NUMBITS(1) [], + FE OFFSET(3) NUMBITS(1) [], + PE OFFSET(2) NUMBITS(1) [], + OE OFFSET(1) NUMBITS(1) [], + DR OFFSET(0) NUMBITS(1) [], + ], + + // UART MODEM STATUS REGISTER + MSR [ + CDCD OFFSET(7) NUMBITS(1) [], + CRI OFFSET(6) NUMBITS(1) [], + CDSR OFFSET(5) NUMBITS(1) [], + CCTS OFFSET(4) NUMBITS(1) [], + DDCD OFFSET(3) NUMBITS(1) [], + TERI OFFSET(2) NUMBITS(1) [], + DDSR OFFSET(1) NUMBITS(1) [], + DCTS OFFSET(0) NUMBITS(1) [], + ], +} + +register_structs!( + UartRegs { + (0x00 => dat: ReadWrite), + (0x01 => ier: ReadWrite), + (0x02 => iir: ReadOnly), + (0x03 => fcr: WriteOnly), + (0x04 => lcr: ReadWrite), + (0x05 => mcr: ReadWrite), + (0x06 => lsr: ReadOnly), + (0x07 => msr: ReadOnly), + (0x08 => @END), + } +); +#[allow(dead_code)] +pub struct Uart { + base_addr: usize, + regs: MMIODerefWrapper, +} + +impl Uart { + pub const fn new(base_addr: usize) -> Self { + Self { + base_addr, + regs: unsafe { MMIODerefWrapper::new(base_addr as usize) }, + } + } + pub fn init(&mut self) { + unsafe { + init_serial(); + } + } + pub fn putchar(&mut self, c: u8) { + unsafe { + print_char(c); + } + } + pub fn send_str(&mut self, s: &str) { + for c in s.bytes() { + self.putchar(c); + if c == b'\n' { + self.putchar(b'\r'); + } + } + } + pub fn getchar(&mut self) -> u8 { + unsafe { get_char() } + } +} + +pub static UART0: Mutex = Mutex::new(Uart::new(BOARD_UART0_VADDR)); + +pub fn console_putchar(c: u8) { + UART0.lock().putchar(c); +} + +pub fn console_getchar() -> Option { + UART0.lock().getchar().into() +} diff --git a/src/device/uart/loongson_uart/uart.S b/src/device/uart/loongson_uart/uart.S new file mode 100644 index 00000000..087b496e --- /dev/null +++ b/src/device/uart/loongson_uart/uart.S @@ -0,0 +1,82 @@ +// This file is forked from uboot's source code for booting 2K1000. + +// Note: the Address mapping window setting up already. +// so, use the Mapped address 0x8xxx... or 0x9xxx... +/* + * Simple character printing routine used before full initialization + */ + +.globl init_serial +init_serial: + or $a4, $ra, $zero + + li.d $a0, {CONSOLE_BASE_ADDR} + li.w $r18, 0x80 + // [UART0_BASE+3, LCR] = 1000_0000, dlab=1, enable access to freq divisor registers + st.b $r18, $a0, 3 + + // CAUTION: now we are accessing the divisor registers! not the regular uart registers + + li.w $r18, {UART_DIV_HI} + // [UART0_BASE+1, DIV1] = {UART_DIV_HI} + st.b $r18, $a0, 1 + + li.w $r18, {UART_DIV_LO} + // [UART0_BASE+0, DIV0] = {UART_DIV_LO} + st.b $r18, $a0, 0 + + li.w $r18, 3 + // [UART0_BASE+3, LCR] = 0000_0011, bec=2'b11, 8bit data + st.b $r18, $a0, 3 + + li.w $r18, 71 + // [UART0_BASE+2, FCR] = 71 = 0100_0111, TL=2'b01, reset rx/tx fifo, and we also stop access to divisor registers + st.b $r18, $a0, 2 + + // enable receive data available interrupt + // li.w $r18, 0x0f + // [UART0_BASE+1, IER][0] = 1 (IRxE) + // st.b $r18, $a0, 1 + + or $ra, $a4, $zero + jirl $zero, $ra, 0 + +.globl print_char +print_char: + li.d $r18, {CONSOLE_BASE_ADDR} +1: + ld.bu $r17, $r18, 0x5 // [UART0_BASE+5, LSR] + andi $r17, $r17, 0x20 // & 0x20(0010_0000), TFE=1, transmit fifo empty, so can send data! + beqz $r17, 1b // if TFE=0, loop and wait for TFE=1 + + st.b $a0, $r18, 0 // [UART0_BASE+0, DATA] = a0 which is the char to print + + // buffer at 0x9000_0003_5000_0008 + // buffer_tot at 0x9000_0003_6000_0000 + // li.d $r19, 0x350000000 + // li.d $r20, 0x350000008 + // ld.d $r21, $r19, 0 // r[21] = buffer_tot(u64) + // add.d $r20, $r20, $r21 // r[20] = buffer + buffer_tot + // st.b $a0, $r20, 0 // buffer[buffer_tot] = a0 + // addi.d $r21, $r21, 1 + // st.d $r21, $r19, 0 // buffer_tot += 1 + + jirl $zero, $ra, 0 + + +// after enabled uart confregs's interrupt, we can use this function to get char from uart. +// wheatfox 2024.3.11 +.globl get_char +get_char: + li.d $r18, {CONSOLE_BASE_ADDR} + +1: + ld.bu $r17, $r18, 0x5 + // check [UART0_BASE+5, LSR][0](DR,Data Received) bit + andi $r17, $r17, 0x01 + beqz $r17, 1b + + // read [UART0_BASE+0, DATA] to $a0 which is the char to return + ld.bu $r4, $r18, 0 + + jirl $zero, $ra, 0 \ No newline at end of file diff --git a/src/device/uart/mod.rs b/src/device/uart/mod.rs index b5095b6e..83788269 100644 --- a/src/device/uart/mod.rs +++ b/src/device/uart/mod.rs @@ -6,9 +6,20 @@ pub use pl011::{console_getchar, console_putchar}; #[cfg(all(feature = "platform_imx8mp", target_arch = "aarch64"))] mod imx_uart; +#[cfg(all(feature = "platform_zcu102", target_arch = "aarch64"))] +mod xuartps; + +#[cfg(all(feature = "platform_zcu102", target_arch = "aarch64"))] +pub use xuartps::{console_getchar, console_putchar}; #[cfg(all(feature = "platform_imx8mp", target_arch = "aarch64"))] pub use imx_uart::{console_getchar, console_putchar}; #[cfg(target_arch = "riscv64")] pub use crate::arch::riscv64::sbi::{console_getchar, console_putchar}; + +#[cfg(target_arch = "loongarch64")] +mod loongson_uart; + +#[cfg(target_arch = "loongarch64")] +pub use loongson_uart::{console_getchar, console_putchar}; diff --git a/src/device/uart/xuartps.rs b/src/device/uart/xuartps.rs new file mode 100644 index 00000000..e162d9da --- /dev/null +++ b/src/device/uart/xuartps.rs @@ -0,0 +1,203 @@ +#![allow(unused_variables)] +#![allow(dead_code)] +/// UART driver for Xilinx Zynq Ultrascale+ MPSoC ZCU102 board. +/// author: wheatfox (wheatfox17@icloud.com) +/// references: +/// 1. Zynq UltraScale+ Device TRM UG1085 (v2.4) December 21, 2023 Chapter 21 +/// 2. https://github.com/Xilinx/linux-xlnx :: drivers/tty/serial/xilinx_uartps.c +/// 3. https://github.com/torvalds/linux/blob/master/drivers/tty/serial/xilinx_uartps.c +use crate::memory::addr::{PhysAddr, VirtAddr}; +use spin::Mutex; +use tock_registers::{ + interfaces::{ReadWriteable, Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite, WriteOnly}, +}; + +pub const UART0_BASE: PhysAddr = 0xff000000; +pub const UART1_BASE: PhysAddr = 0xff010000; + +pub const UART_FIFO_SIZE: usize = 64; +pub const UART_REGS_REGION_SIZE: usize = 0x1000; +pub const UART_TX_TIMEOUT: usize = 500000; +pub const UART_DEFAULT_BAUDRATE: u32 = 115200; +pub const UART_REF_CLK: u32 = 50_000_000; // 50 MHz +pub const UART_BAUDRATE_ACTUAL_CD: u32 = 62; +pub const UART_BAUDRATE_BDIV: u32 = 6; + +lazy_static! { + static ref UART0: Mutex = { + let mut uart = ZynqUart::new(UART0_BASE); + // uart.init(); + Mutex::new(uart) + }; +} + +lazy_static! { + static ref UART1: Mutex = { + let mut uart = ZynqUart::new(UART1_BASE); + // uart.init(); + Mutex::new(uart) + }; +} + +register_structs! { + ZynqUartRegs { + (0x00 => cr: ReadWrite), // Control Register + (0x04 => mr: ReadWrite), // Mode Register + (0x08 => ier: WriteOnly), // Interrupt Enable Register + (0x0C => idr: WriteOnly), // Interrupt Disable Register + (0x10 => imr: ReadOnly), // Interrupt Mask Register + (0x14 => isr: ReadOnly), // Interrupt Status Register + (0x18 => baudgen: ReadWrite), // Baud Rate Generator + (0x1C => rxtout: ReadWrite), // RX Timeout + (0x20 => rxwm: ReadWrite), // RX FIFO Trigger Level + (0x24 => modemcr: ReadWrite), // Modem Control Register + (0x28 => modemsr: ReadOnly), // Modem Status Register + (0x2C => sr: ReadOnly), // Channel Status Register + (0x30 => fifo: ReadWrite), // FIFO + (0x34 => bauddiv: ReadWrite), // Baud Rate Divider + (0x38 => flowdel: ReadWrite), // Flow Delay + (0x3C => irrx_pwidth: ReadWrite), // IR Min Received Pulse Width + (0x40 => irtx_pwidth: ReadWrite), // IR Transmitted pulse Width + (0x44 => txwm: ReadWrite), // TX FIFO Trigger Level + (0x48 => rxbs: ReadOnly), // RX FIFO byte status register + (0x4C => @END), + } +} + +register_bitfields! { + u32, + CR [ + stop_break OFFSET(8) NUMBITS(1) [], + start_break OFFSET(7) NUMBITS(1) [], + tx_disable OFFSET(5) NUMBITS(1) [], + tx_enable OFFSET(4) NUMBITS(1) [], + rx_disable OFFSET(3) NUMBITS(1) [], + rx_enable OFFSET(2) NUMBITS(1) [], + tx_reset OFFSET(1) NUMBITS(1) [], + rx_reset OFFSET(0) NUMBITS(1) [] + ], + MR [ + clock_select OFFSET(0) NUMBITS(1) [ + Normal = 0, + Div8 = 1 + ], + char_length OFFSET(1) NUMBITS(2) [ + Bits8 = 0b00, + Bits7 = 0b10, + Bits6 = 0b11 + ], + parity OFFSET(3) NUMBITS(3) [ + Even = 0b000, + Odd = 0b001, + Space = 0b010, + Mark = 0b011, + None = 0b100 + ], + stop_mode OFFSET(7) NUMBITS(1) [ + OneStopBit = 0, + TwoStopBits = 1 + ], + channel_mode OFFSET(8) NUMBITS(2) [ + Normal = 0b00, + LocalLoopback = 0b10 + ] + ], + RXBS [ + parity_error OFFSET(0) NUMBITS(1) [], + frame_error OFFSET(1) NUMBITS(1) [], + overrun_error OFFSET(2) NUMBITS(1) [], + ], + IXR [ + // all IER, IDR, IMR, ISR use the same bitfields + tout OFFSET(8) NUMBITS(1) [], + parity OFFSET(7) NUMBITS(1) [], + framing OFFSET(6) NUMBITS(1) [], + overrun OFFSET(5) NUMBITS(1) [], + txfull OFFSET(4) NUMBITS(1) [], + txempty OFFSET(3) NUMBITS(1) [], + rxfull OFFSET(2) NUMBITS(1) [], + rxempty OFFSET(1) NUMBITS(1) [], + rxtrig OFFSET(0) NUMBITS(1) [], + ], + MODEMCR [ + fcm OFFSET(5) NUMBITS(1) [], + rts OFFSET(1) NUMBITS(1) [], + dtr OFFSET(0) NUMBITS(1) [] + ], + MODEMSR [ + dcd OFFSET(7) NUMBITS(1) [], + ri OFFSET(6) NUMBITS(1) [], + dsr OFFSET(5) NUMBITS(1) [], + cts OFFSET(4) NUMBITS(1) [] + ], + SR [ + sr_txfull OFFSET(4) NUMBITS(1) [], + sr_txempty OFFSET(3) NUMBITS(1) [], + sr_rxempty OFFSET(1) NUMBITS(1) [], + sr_rxtrig OFFSET(0) NUMBITS(1) [], + tactive OFFSET(11) NUMBITS(1) [] + ] +} + +struct ZynqUart { + base_vaddr: VirtAddr, +} + +impl ZynqUart { + const fn new(base_vaddr: VirtAddr) -> Self { + Self { base_vaddr } + } + const fn regs(&self) -> &ZynqUartRegs { + unsafe { &*(self.base_vaddr as *const _) } + } + /// the UART initialization sequence according to the manual + /// the baudrate is set to 115200, 8bit data, no parity, 1 stop bit + fn init(&mut self) { + // read clock_select from MR, assert it to 0 because we don't use 9600 baudrate + assert_eq!( + self.regs().mr.read(MR::clock_select), + MR::clock_select::Normal.into() + ); + self.regs() + .cr + .modify(CR::tx_disable::SET + CR::rx_disable::SET); + self.regs().baudgen.set(UART_BAUDRATE_ACTUAL_CD); + self.regs().bauddiv.set(UART_BAUDRATE_BDIV); + self.regs().cr.modify(CR::tx_reset::SET + CR::rx_reset::SET); + self.regs() + .cr + .modify(CR::tx_enable::SET + CR::rx_enable::SET); + self.regs().mr.write( + MR::char_length::Bits8 + + MR::parity::None + + MR::stop_mode::OneStopBit + + MR::channel_mode::Normal, + ); + self.regs().rxwm.set(0x8); + self.regs().rxtout.set(0x1); + self.regs().idr.set(0x1fff); + } + fn self_test(&mut self) { + todo!(); + } + fn putchar(&mut self, c: u8) { + // disable interrupts txempty, txfull + self.regs().idr.write(IXR::txempty::SET + IXR::txfull::SET); + // check sr that if tx fifo is full, send nothing + while self.regs().sr.is_set(SR::sr_txfull) {} + self.regs().fifo.set(c as u32); + } + fn getchar(&mut self) -> Option { + todo!(); + } +} + +pub fn console_putchar(c: u8) { + UART0.lock().putchar(c) +} + +pub fn console_getchar() -> Option { + UART0.lock().getchar() +} diff --git a/src/device/virtio_trampoline.rs b/src/device/virtio_trampoline.rs index 85db9671..2fef5f0b 100644 --- a/src/device/virtio_trampoline.rs +++ b/src/device/virtio_trampoline.rs @@ -7,7 +7,8 @@ use core::sync::atomic::Ordering; use spin::Mutex; use crate::arch::cpu::this_cpu_id; -use crate::device::irqchip::gicv3::inject_irq; +use crate::consts::MAX_CPU_NUM; +use crate::device::irqchip::inject_irq; use crate::event::send_event; use crate::event::IPI_EVENT_WAKEUP_VIRTIO_DEVICE; use crate::hypercall::SGI_IPI_ID; @@ -23,12 +24,12 @@ pub static VIRTIO_BRIDGE: Mutex = Mutex::new(VirtioBridgeReg const QUEUE_NOTIFY: usize = 0x50; pub const MAX_REQ: u32 = 32; pub const MAX_DEVS: usize = 4; // Attention: The max virtio-dev number for vm is 4. -pub const MAX_CPUS: usize = 16; +pub const MAX_CPUS: usize = 4; pub const IRQ_WAKEUP_VIRTIO_DEVICE: usize = 32 + 0x20; /// non root zone's virtio request handler pub fn mmio_virtio_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { - debug!("mmio virtio handler"); + // debug!("mmio virtio handler"); let need_interrupt = if mmio.address == QUEUE_NOTIFY { 1 } else { 0 }; if need_interrupt == 1 { debug!("notify !!!, cpu id is {}", this_cpu_id()); @@ -50,17 +51,21 @@ pub fn mmio_virtio_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { mmio.is_write, need_interrupt, ); + // debug!("non root sends req: {:#x?}", hreq); let (cfg_flags, cfg_values) = unsafe { ( - core::slice::from_raw_parts(dev.get_cfg_flags(), MAX_CPUS), - core::slice::from_raw_parts(dev.get_cfg_values(), MAX_CPUS), + core::slice::from_raw_parts(dev.get_cfg_flags(), MAX_CPU_NUM), + core::slice::from_raw_parts(dev.get_cfg_values(), MAX_CPU_NUM), ) }; let cpu_id = this_cpu_id() as usize; let old_cfg_flag = cfg_flags[cpu_id]; + // debug!("old cfg flag: {:#x?}", old_cfg_flag); dev.push_req(hreq); // If req list is empty, send sgi to root linux to wake up virtio device. + #[cfg(not(target_arch = "loongarch64"))] if dev.need_wakeup() { + debug!("need wakeup, sending ipi to wake up virtio device"); let root_cpu = root_zone().read().cpu_set.first_cpu().unwrap(); send_event(root_cpu, SGI_IPI_ID as _, IPI_EVENT_WAKEUP_VIRTIO_DEVICE); } @@ -70,19 +75,21 @@ pub fn mmio_virtio_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { if need_interrupt == 0 { // when virtio backend finish the req, it will add 1 to cfg_flag. while cfg_flags[cpu_id] == old_cfg_flag { - fence(Ordering::Acquire); + // fence(Ordering::Acquire); count += 1; if count > 1000000 { - warn!("virtio backend is too slow, please check it!"); + // warn!("virtio backend is too slow, please check it!"); + fence(Ordering::Acquire); + count = 0; } } if !mmio.is_write { // ensure cfg value is right. mmio.value = cfg_values[cpu_id] as _; - debug!("non root receives value: {:#x?}", mmio.value); + // debug!("non root receives value: {:#x?}", mmio.value); } } - debug!("non root returns"); + // debug!("non root returns"); Ok(()) } @@ -168,7 +175,7 @@ impl VirtioBridgeRegion { fence(Ordering::SeqCst); region.req_rear = (region.req_rear + 1) % MAX_REQ; // Write barrier so that device can see change after this method returns - fence(Ordering::SeqCst); + // fence(Ordering::SeqCst); } pub fn get_cfg_flags(&self) -> *const u64 { @@ -202,11 +209,11 @@ pub struct VirtioBridge { /// The first elem of res list, only hvisor updates pub res_front: u32, /// The last elem's next place of res list, only virtio device updates - res_rear: u32, + pub res_rear: u32, pub req_list: [HvisorDeviceReq; MAX_REQ as usize], pub res_list: [HvisorDeviceRes; MAX_REQ as usize], // irqs - cfg_flags: [u64; MAX_CPUS], - cfg_values: [u64; MAX_CPUS], + cfg_flags: [u64; MAX_CPU_NUM], + cfg_values: [u64; MAX_CPU_NUM], pub mmio_addrs: [u64; MAX_DEVS], pub mmio_avail: u8, pub need_wakeup: u8, @@ -225,6 +232,7 @@ impl Debug for VirtioBridge { /// Hvisor device requests #[repr(C)] +#[derive(Debug)] pub struct HvisorDeviceReq { pub src_cpu: u64, address: u64, diff --git a/src/error.rs b/src/error.rs index 195b8fdc..f65269c5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,18 +6,18 @@ use core::fmt::{Debug, Formatter, Result}; #[allow(dead_code, clippy::upper_case_acronyms)] /// POSIX errno pub enum HvErrorNum { - EPERM = 1, - ENOENT = 2, - EIO = 5, - E2BIG = 7, - ENOMEM = 12, - EFAULT = 14, - EBUSY = 16, - EEXIST = 17, - ENODEV = 19, - EINVAL = 22, - ERANGE = 34, - ENOSYS = 38, + EPERM = 1, // Operation not permitted. + ENOENT = 2, // No such file or directory. + EIO = 5, // I/O error. + E2BIG = 7, // Argument list too long. + ENOMEM = 12, // Not enough space. + EFAULT = 14, // Bad address. + EBUSY = 16, // Device or resource busy. + EEXIST = 17, // File exists. + ENODEV = 19, // No such device. + EINVAL = 22, // Invalid argument. + ERANGE = 34, // Result too large. + ENOSYS = 38, // Function not implemented. } pub struct HvError { diff --git a/src/event.rs b/src/event.rs index 9e7d3ec7..0ffb0ba7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,7 +1,7 @@ use crate::{ arch::ipi::arch_send_event, device::{ - irqchip::gicv3::inject_irq, + irqchip::{self, inject_irq}, virtio_trampoline::{handle_virtio_irq, IRQ_WAKEUP_VIRTIO_DEVICE}, }, percpu::this_cpu_data, @@ -9,10 +9,15 @@ use crate::{ use alloc::{collections::VecDeque, vec::Vec}; use spin::{Mutex, Once}; +#[cfg(test)] +mod tests; + pub const IPI_EVENT_WAKEUP: usize = 0; pub const IPI_EVENT_SHUTDOWN: usize = 1; pub const IPI_EVENT_VIRTIO_INJECT_IRQ: usize = 2; pub const IPI_EVENT_WAKEUP_VIRTIO_DEVICE: usize = 3; +pub const IPI_EVENT_CLEAR_INJECT_IRQ: usize = 4; + static EVENT_MANAGER: Once = Once::new(); struct EventManager { @@ -49,6 +54,22 @@ impl EventManager { None => None, } } + + fn dump(&self) { + for (cpu, events) in self.inner.iter().enumerate() { + let e = events.lock(); + debug!("event manager: cpu: {}, events: {:?}", cpu, e); + } + } + + fn dump_cpu(&self, cpu: usize) -> Vec { + let mut res = Vec::new(); + let e = self.inner[cpu].lock(); + for i in e.iter() { + res.push(*i); + } + res + } } fn add_event(cpu: usize, event_id: usize) -> Option<()> { @@ -63,10 +84,20 @@ pub fn init(max_cpus: usize) { EVENT_MANAGER.call_once(|| EventManager::new(max_cpus)); } +pub fn dump_events() { + EVENT_MANAGER.get().unwrap().dump(); +} + +pub fn dump_cpu_events(cpu: usize) -> Vec { + EVENT_MANAGER.get().unwrap().dump_cpu(cpu) +} + pub fn check_events() -> bool { + trace!("check_events"); let cpu_data = this_cpu_data(); match fetch_event(cpu_data.id) { Some(IPI_EVENT_WAKEUP) => { + info!("cpu {} wakeup", cpu_data.id); cpu_data.arch_cpu.run(); } Some(IPI_EVENT_SHUTDOWN) => { @@ -80,11 +111,26 @@ pub fn check_events() -> bool { inject_irq(IRQ_WAKEUP_VIRTIO_DEVICE, false); true } + #[cfg(target_arch = "loongarch64")] + Some(IPI_EVENT_CLEAR_INJECT_IRQ) => { + irqchip::ls7a2000::clear_hwi_injected_irq(); + true + } _ => false, } } pub fn send_event(cpu_id: usize, ipi_int_id: usize, event_id: usize) { + #[cfg(target_arch = "loongarch64")] + { + // block until the previous event is processed, which means + // the target queue is empty + while !fetch_event(cpu_id).is_none() {} + debug!( + "loongarch64:: send_event: cpu_id: {}, ipi_int_id: {}, event_id: {}", + cpu_id, ipi_int_id, event_id + ); + } add_event(cpu_id, event_id); arch_send_event(cpu_id as _, ipi_int_id as _); } diff --git a/src/event/tests.rs b/src/event/tests.rs new file mode 100644 index 00000000..8ed94ce6 --- /dev/null +++ b/src/event/tests.rs @@ -0,0 +1,8 @@ +use super::*; + +#[test_case] +fn test_simple_send_event() { + init(1); + send_event(0, 0, IPI_EVENT_WAKEUP); + assert_eq!(fetch_event(0), Some(IPI_EVENT_WAKEUP)); +} diff --git a/src/hypercall/mod.rs b/src/hypercall/mod.rs index 86e33c8a..524488c8 100644 --- a/src/hypercall/mod.rs +++ b/src/hypercall/mod.rs @@ -1,15 +1,22 @@ #![allow(dead_code)] +use crate::arch::cpu::this_cpu_id; use crate::config::HvZoneConfig; -use crate::consts::{INVALID_ADDRESS, PAGE_SIZE}; -use crate::device::virtio_trampoline::{VIRTIO_BRIDGE, MAX_DEVS, MAX_REQ, VIRTIO_IRQS}; +use crate::consts::{INVALID_ADDRESS, MAX_CPU_NUM, PAGE_SIZE}; +use crate::device::irqchip::inject_irq; +use crate::device::virtio_trampoline::{MAX_DEVS, MAX_REQ, VIRTIO_BRIDGE, VIRTIO_IRQS}; use crate::error::HvResult; -use crate::percpu::{get_cpu_data, PerCpu}; -use crate::zone::{find_zone, is_this_root_zone, remove_zone, zone_create}; +use crate::percpu::{get_cpu_data, this_zone, PerCpu}; +use crate::zone::{ + all_zones_info, find_zone, is_this_root_zone, remove_zone, this_zone_id, zone_create, ZoneInfo, +}; use crate::event::{send_event, IPI_EVENT_SHUTDOWN, IPI_EVENT_VIRTIO_INJECT_IRQ, IPI_EVENT_WAKEUP}; use core::convert::TryFrom; use core::sync::atomic::{fence, Ordering}; +#[cfg(target_arch = "aarch64")] +use crate::ivc::{IvcInfo, IVC_INFOS}; + use numeric_enum_macro::numeric_enum; numeric_enum! { @@ -20,6 +27,9 @@ numeric_enum! { HvVirtioInjectIrq = 1, HvZoneStart = 2, HvZoneShutdown = 3, + HvZoneList = 4, + HvClearInjectIrq = 20, + HvIvcInfo = 5, } } pub const SGI_IPI_ID: u64 = 7; @@ -35,7 +45,7 @@ impl<'a> HyperCall<'a> { Self { cpu_data } } - pub fn hypercall(&mut self, code: u64, arg0: u64, _arg1: u64) -> HyperCallResult { + pub fn hypercall(&mut self, code: u64, arg0: u64, arg1: u64) -> HyperCallResult { let code = match HyperCallCode::try_from(code) { Ok(code) => code, Err(_) => { @@ -43,16 +53,63 @@ impl<'a> HyperCall<'a> { return Ok(0); } }; + debug!( + "hypercall: code={:?}, arg0={:#x}, arg1={:#x}", + code, arg0, arg1 + ); unsafe { match code { HyperCallCode::HvVirtioInit => self.hv_virtio_init(arg0), HyperCallCode::HvVirtioInjectIrq => self.hv_virtio_inject_irq(), - HyperCallCode::HvZoneStart => self.hv_zone_start(&*(arg0 as *const HvZoneConfig)), + HyperCallCode::HvZoneStart => { + self.hv_zone_start(&*(arg0 as *const HvZoneConfig), arg1) + } HyperCallCode::HvZoneShutdown => self.hv_zone_shutdown(arg0), + HyperCallCode::HvZoneList => self.hv_zone_list(&mut *(arg0 as *mut ZoneInfo), arg1), + HyperCallCode::HvClearInjectIrq => { + use crate::event::IPI_EVENT_CLEAR_INJECT_IRQ; + for i in 1..MAX_CPU_NUM { + // if target cpu status is not running, we skip it + if !get_cpu_data(i).arch_cpu.power_on { + continue; + } + send_event(i, SGI_IPI_ID as _, IPI_EVENT_CLEAR_INJECT_IRQ); + } + HyperCallResult::Ok(0) + } + #[cfg(target_arch = "aarch64")] + HyperCallCode::HvIvcInfo => self.hv_ivc_info(arg0), + _ => { + warn!("hypercall id={} unsupported!", code as u64); + Ok(0) + } } } } + #[cfg(target_arch = "aarch64")] + fn hv_ivc_info(&mut self, ivc_info_ipa: u64) -> HyperCallResult { + let zone_id = this_zone_id(); + let zone = this_zone(); + // ipa->hpa->hva + let hpa = unsafe { + zone.read() + .gpm + .page_table_query(ivc_info_ipa as _) + .unwrap() + .0 + }; + // hva == hpa + let ivc_info = unsafe { &mut *(hpa as *mut IvcInfo) }; + let ivc_infos = IVC_INFOS.lock(); + let zone_ivc_info = ivc_infos.get(&(zone_id as _)); + match zone_ivc_info { + Some(zone_ivc_info) => *ivc_info = *zone_ivc_info, + None => return hv_result_err!(ENODEV, "Zone {zone_id} has no ivc config!"), + } + HyperCallResult::Ok(0) + } + // only root zone calls the function and set virtio shared region between el1 and el2. fn hv_virtio_init(&mut self, shared_region_addr: u64) -> HyperCallResult { info!( @@ -62,7 +119,12 @@ impl<'a> HyperCall<'a> { if !is_this_root_zone() { return hv_result_err!(EPERM, "Init virtio over non-root zones: unsupported!"); } + let shared_region_addr_pa = shared_region_addr as usize; + #[cfg(target_arch = "loongarch64")] + let shared_region_addr_pa = + shared_region_addr_pa | crate::arch::mm::LOONGARCH64_CACHED_DMW_PREFIX as usize; + assert!(shared_region_addr_pa % PAGE_SIZE == 0); // let offset = shared_region_addr_pa & (PAGE_SIZE - 1); // memory::hv_page_table() @@ -83,6 +145,7 @@ impl<'a> HyperCall<'a> { // Inject virtio device's irq to non root when a virtio device finishes one IO request. Only root zone calls. fn hv_virtio_inject_irq(&self) -> HyperCallResult { + debug!("hv_virtio_inject_irq: hypercall for trigger target cpu to inject irq"); if !is_this_root_zone() { return hv_result_err!( EPERM, @@ -96,14 +159,23 @@ impl<'a> HyperCall<'a> { let res_front = region.res_front as usize; let irq_id = region.res_list[res_front].irq_id as u64; let target_zone = region.res_list[res_front].target_zone; - // TODO: only the first cpu receives the irq, is that reasonable??? - let target_cpu = find_zone(target_zone as _) - .unwrap() - .read() - .cpu_set - .first_cpu() - .unwrap(); + let target_cpu = match find_zone(target_zone as _) { + Some(zone) => zone.read().cpu_set.first_cpu().unwrap(), + _ => continue, + }; + let irq_list = map_irq.entry(target_cpu).or_insert([0; MAX_DEVS + 1]); + #[cfg(target_arch = "loongarch64")] + { + use crate::device::irqchip::ls7a2000::*; + let status = GLOBAL_IRQ_INJECT_STATUS.lock(); + debug!( + "hv_virtio_inject_irq: cpu {} status: {:?}", + target_cpu, status.cpu_status[target_cpu].status + ); + drop(status); + irq_list[0] = 0; // CAUTION: this is a workaround for loongarch64 + } if !irq_list[1..=irq_list[0] as usize].contains(&irq_id) { let len = irq_list[0] as usize; assert!(len + 1 < MAX_DEVS); @@ -124,7 +196,14 @@ impl<'a> HyperCall<'a> { HyperCallResult::Ok(0) } - pub fn hv_zone_start(&mut self, config: &HvZoneConfig) -> HyperCallResult { + pub fn hv_zone_start(&mut self, config: &HvZoneConfig, config_size: u64) -> HyperCallResult { + #[cfg(target_arch = "loongarch64")] + let config = unsafe { + &*((config as *const HvZoneConfig as u64 + | crate::arch::mm::LOONGARCH64_CACHED_DMW_PREFIX) + as *const HvZoneConfig) + }; + info!("hv_zone_start: config: {:#x?}", config); if !is_this_root_zone() { return hv_result_err!( @@ -132,18 +211,27 @@ impl<'a> HyperCall<'a> { "Start zone operation over non-root zones: unsupported!" ); } + if config_size != core::mem::size_of::() as _ { + return hv_result_err!(EINVAL, "Invalid config!"); + } let zone = zone_create(config)?; let boot_cpu = zone.read().cpu_set.first_cpu().unwrap(); let target_data = get_cpu_data(boot_cpu as _); let _lock = target_data.ctrl_lock.lock(); - if !target_data.arch_cpu.psci_on { + if !target_data.arch_cpu.power_on { send_event(boot_cpu, SGI_IPI_ID as _, IPI_EVENT_WAKEUP); } else { error!("hv_zone_start: cpu {} already on", boot_cpu); return hv_result_err!(EBUSY); }; + #[cfg(target_arch = "loongarch64")] + { + // assert this is cpu 0 + let cpuid = this_cpu_id(); + assert_eq!(cpuid, 0); + } drop(_lock); HyperCallResult::Ok(0) } @@ -168,11 +256,18 @@ impl<'a> HyperCall<'a> { // // return zone's cpus to root_zone zone_r.cpu_set.iter().for_each(|cpu_id| { let _lock = get_cpu_data(cpu_id).ctrl_lock.lock(); - get_cpu_data(cpu_id).zone = None; get_cpu_data(cpu_id).cpu_on_entry = INVALID_ADDRESS; send_event(cpu_id, SGI_IPI_ID as _, IPI_EVENT_SHUTDOWN); }); - + // wait all zone's cpus shutdown + while zone_r.cpu_set.iter().any(|cpu_id| { + let _lock = get_cpu_data(cpu_id).ctrl_lock.lock(); + get_cpu_data(cpu_id).arch_cpu.power_on + }) {} + zone_r.cpu_set.iter().for_each(|cpu_id| { + let _lock = get_cpu_data(cpu_id).ctrl_lock.lock(); + get_cpu_data(cpu_id).zone = None; + }); zone_r.arch_irqchip_reset(); drop(zone_r); @@ -181,4 +276,29 @@ impl<'a> HyperCall<'a> { HyperCallResult::Ok(0) } + + fn hv_zone_list(&self, zones: *mut ZoneInfo, cnt: u64) -> HyperCallResult { + if zones.is_null() { + return hv_result_err!(EINVAL, "hv_zone_list: zones is null"); + } + let zones_info = all_zones_info(); + let slice = unsafe { core::slice::from_raw_parts_mut(zones, cnt as usize) }; + + #[cfg(target_arch = "loongarch64")] + let slice = unsafe { + core::slice::from_raw_parts_mut( + (zones as u64 | crate::arch::mm::LOONGARCH64_CACHED_DMW_PREFIX) as *mut ZoneInfo, + cnt as usize, + ) + }; + + for (i, zone_info) in slice.iter_mut().enumerate() { + if i < zones_info.len() { + *zone_info = zones_info[i].clone(); + } else { + break; + } + } + HyperCallResult::Ok(core::cmp::min(cnt as _, zones_info.len())) + } } diff --git a/src/ivc.rs b/src/ivc.rs new file mode 100644 index 00000000..2dc370d2 --- /dev/null +++ b/src/ivc.rs @@ -0,0 +1,239 @@ +use core::ptr::write_volatile; + +use alloc::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + vec::Vec, +}; +use spin::Mutex; + +use crate::device::irqchip::set_ispender; +use crate::{ + config::{HvIvcConfig, CONFIG_MAX_IVC_CONGIGS}, + consts::PAGE_SIZE, + error::HvResult, + hypercall::SGI_IPI_ID, + memory::{Frame, GuestPhysAddr, MMIOAccess, MemFlags, MemoryRegion}, + zone::{find_zone, this_zone_id, Zone}, +}; + +// ivc_id -> ivc_record +static IVC_RECORDS: Mutex> = Mutex::new(BTreeMap::new()); +// zone id -> zone's IvcInfo +pub static IVC_INFOS: Mutex> = Mutex::new(BTreeMap::new()); + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +/// The ivc info that one zone should first accquire +pub struct IvcInfo { + /// The number that one zone participates in ivc region + pub len: u64, + /// The ivc control table ipa of each ivc region + ivc_ct_ipas: [u64; CONFIG_MAX_IVC_CONGIGS], + /// The ivc shared memory ipa of each ivc region + ivc_shmem_ipas: [u64; CONFIG_MAX_IVC_CONGIGS], + /// The ivc_id of each ivc region + ivc_ids: [u32; CONFIG_MAX_IVC_CONGIGS], + /// The irq number of each ivc region + ivc_irqs: [u32; CONFIG_MAX_IVC_CONGIGS], +} + +impl From<&[HvIvcConfig]> for IvcInfo { + fn from(configs: &[HvIvcConfig]) -> Self { + let mut ivc_ids = [0; CONFIG_MAX_IVC_CONGIGS]; + let mut ivc_ct_ipas = [0; CONFIG_MAX_IVC_CONGIGS]; + let mut ivc_shmem_ipas = [0; CONFIG_MAX_IVC_CONGIGS]; + let mut ivc_irqs = [0; CONFIG_MAX_IVC_CONGIGS]; + for i in 0..configs.len() { + let config = &configs[i]; + ivc_ids[i] = config.ivc_id; + ivc_ct_ipas[i] = config.control_table_ipa; + ivc_shmem_ipas[i] = config.shared_mem_ipa; + ivc_irqs[i] = config.interrupt_num; + } + Self { + len: configs.len() as u64, + ivc_ids, + ivc_shmem_ipas, + ivc_ct_ipas, + ivc_irqs, + } + } +} +fn insert_ivc_record(ivc_config: &HvIvcConfig, zone_id: u32) -> Result<(bool, usize), ()> { + let mut recs = IVC_RECORDS.lock(); + let ivc_id = ivc_config.ivc_id; + if let Some(rec) = recs.get_mut(&ivc_id) { + if rec.max_peers != ivc_config.max_peers + || rec.rw_sec_size != ivc_config.rw_sec_size + || rec.out_sec_size != ivc_config.out_sec_size + { + error!("ivc config conflicts!!!"); + return Err(()); + } + if rec.peer_infos.len() == rec.max_peers as _ { + error!("can't add more peers to ivc_id {}", ivc_id); + return Err(()); + } + rec.peer_infos.insert( + ivc_config.peer_id, + PeerInfo { + zone_id, + irq_num: ivc_config.interrupt_num, + shared_mem_ipa: ivc_config.shared_mem_ipa, + }, + ); + Ok((false, rec.shared_mem.start_paddr())) + } else { + if ivc_config.rw_sec_size as usize % PAGE_SIZE != 0 + || ivc_config.out_sec_size as usize % PAGE_SIZE != 0 + { + error!("section size must be page aligned!!!"); + return Err(()); + } + let mut rec = IvcRecord::from(ivc_config); + let start_paddr = rec.shared_mem.start_paddr(); + rec.peer_infos.insert( + ivc_config.peer_id, + PeerInfo { + zone_id, + irq_num: ivc_config.interrupt_num, + shared_mem_ipa: ivc_config.shared_mem_ipa, + }, + ); + recs.insert(ivc_id, rec); + Ok((true, start_paddr)) + } +} + +struct IvcRecord { + max_peers: u32, + rw_sec_size: u32, + out_sec_size: u32, + // peer id -> PeerInfo + peer_infos: BTreeMap, + shared_mem: Frame, +} + +struct PeerInfo { + zone_id: u32, + irq_num: u32, + shared_mem_ipa: u64, +} + +impl From<&HvIvcConfig> for IvcRecord { + fn from(config: &HvIvcConfig) -> Self { + let frames = Frame::new_contiguous( + ((config.rw_sec_size + config.out_sec_size * config.max_peers) / PAGE_SIZE as u32) + as usize, + 0, + ) + .unwrap(); + Self { + max_peers: config.max_peers, + rw_sec_size: config.rw_sec_size, + out_sec_size: config.out_sec_size, + peer_infos: BTreeMap::new(), + shared_mem: frames, + } + } +} + +impl Zone { + pub fn ivc_init(&mut self, ivc_configs: &[HvIvcConfig]) { + for ivc_config in ivc_configs { + // is_new is ok to remove + if let Ok((is_new, start_paddr)) = insert_ivc_record(ivc_config, self.id as _) { + info!( + "ivc init: zone {}'s shared mem begins at {:x}, ipa is {:x}", + self.id, start_paddr, ivc_config.shared_mem_ipa + ); + let max_peers = ivc_config.max_peers; + let rw_sec_size: usize = ivc_config.rw_sec_size as usize; + let out_sec_size: usize = ivc_config.out_sec_size as usize; + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + ivc_config.shared_mem_ipa as _, + start_paddr, + rw_sec_size as _, + MemFlags::READ | MemFlags::WRITE, + )) + .unwrap(); + for i in 0..ivc_config.max_peers as usize { + let flags = if i == ivc_config.peer_id as _ { + MemFlags::READ | MemFlags::WRITE + } else { + MemFlags::READ + }; + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + ivc_config.shared_mem_ipa as usize + rw_sec_size + i * out_sec_size, + start_paddr + rw_sec_size + i * out_sec_size, + out_sec_size as _, + flags, + )) + .unwrap(); + } + self.mmio_region_register( + ivc_config.control_table_ipa as _, + PAGE_SIZE, + mmio_ivc_handler, + ivc_config.control_table_ipa as _, + ); + } else { + return; + } + } + IVC_INFOS.lock().insert(self.id, IvcInfo::from(ivc_configs)); + } +} + +const CT_IVC_ID: GuestPhysAddr = 0x00; +const CT_MAX_PEERS: GuestPhysAddr = 0x04; +const CT_RW_SEC_SIZE: GuestPhysAddr = 0x08; +const CT_OUT_SEC_SIZE: GuestPhysAddr = 0x0C; +const CT_PEER_ID: GuestPhysAddr = 0x10; +const CT_IPI_INVOKE: GuestPhysAddr = 0x14; + +pub fn mmio_ivc_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { + let zone_id = this_zone_id(); + let is_write = mmio.is_write; + let offset = mmio.address; + let ivc_infos = IVC_INFOS.lock(); + let ivc_info = ivc_infos.get(&zone_id).unwrap(); + let ivc_id = (0..ivc_info.len as usize) + .find(|&i| ivc_info.ivc_ct_ipas[i] == base as _) + .map(|i| ivc_info.ivc_ids[i]) + .unwrap(); + drop(ivc_infos); + let recs = IVC_RECORDS.lock(); + let rec = recs.get(&ivc_id).unwrap(); + mmio.value = match mmio.address { + CT_IVC_ID => ivc_id as usize, + CT_MAX_PEERS => rec.max_peers as usize, + CT_RW_SEC_SIZE => rec.rw_sec_size as usize, + CT_OUT_SEC_SIZE => rec.out_sec_size as usize, + CT_PEER_ID => { + let peer_id = rec + .peer_infos + .iter() + .find(|&(peer_id, info)| info.zone_id == zone_id as _) + .map(|(peer_id, _)| *peer_id) + .unwrap(); + peer_id as usize + } + CT_IPI_INVOKE if is_write => { + let peer_id = mmio.value as u32; + let irq_num = match rec.peer_infos.get(&peer_id) { + Some(info) => info.irq_num, + None => { + error!("zone {} has no peer {}", zone_id, peer_id); + return hv_result_err!(EINVAL); + } + } as usize; + set_ispender(irq_num / 32, 1 << (irq_num % 32)); + return Ok(()); + } + _ => return hv_result_err!(EFAULT), + }; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index e0bb9eab..c46b45c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,10 +12,17 @@ #![no_main] // 不使用main入口,使用自己定义实际入口_start,因为我们还没有初始化堆栈指针 #![feature(asm_const)] -#![feature(naked_functions)] // surpport naked function -#![feature(core_panic)] +#![feature(naked_functions)] +// surpport naked function +// #![feature(core_panic)] // 支持内联汇编 // #![deny(warnings, missing_docs)] // 将warnings作为error + +// unittest +#![feature(custom_test_frameworks)] +#![test_runner(crate::tests::test_main)] +#![reexport_test_harness_main = "test_main"] + #[macro_use] extern crate alloc; extern crate buddy_system_allocator; @@ -28,6 +35,7 @@ extern crate lazy_static; #[macro_use] mod logging; mod arch; +mod config; mod consts; mod device; mod event; @@ -37,15 +45,24 @@ mod panic; mod percpu; mod platform; mod zone; -mod config; +#[cfg(target_arch = "aarch64")] +mod ivc; + +mod pci; +mod tests; + +#[cfg(target_arch = "aarch64")] use crate::arch::mm::setup_parange; use crate::consts::MAX_CPU_NUM; use arch::{cpu::cpu_start, entry::arch_entry}; use config::root_zone_config; -use zone::zone_create; use core::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use percpu::PerCpu; +use zone::zone_create; + +#[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] +use crate::arch::iommu::iommu_init; static INITED_CPUS: AtomicU32 = AtomicU32::new(0); static ENTERED_CPUS: AtomicU32 = AtomicU32::new(0); @@ -88,14 +105,12 @@ fn primary_init_early() { // let revision = system_config.revision; info!("Hypervisor initialization in progress..."); info!( - "build_mode: {}, log_level: {}, arch: {}, vendor: {}, stats: {}", + "build_mode: {}, log_level: {}, arch: {}, stats: {}", option_env!("MODE").unwrap_or(""), option_env!("LOG").unwrap_or(""), option_env!("ARCH").unwrap_or(""), - option_env!("VENDOR").unwrap_or(""), option_env!("STATS").unwrap_or("off"), ); - memory::frame::init(); memory::frame::test(); event::init(MAX_CPU_NUM); @@ -103,7 +118,12 @@ fn primary_init_early() { device::irqchip::primary_init_early(); // crate::arch::mm::init_hv_page_table().unwrap(); + #[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] + iommu_init(); + + #[cfg(not(test))] zone_create(root_zone_config()).unwrap(); + INIT_EARLY_OK.store(1, Ordering::Release); } @@ -141,6 +161,8 @@ fn rust_main(cpuid: usize, host_dtb: usize) { if MASTER_CPU.load(Ordering::Acquire) == -1 { MASTER_CPU.store(cpuid as i32, Ordering::Release); is_primary = true; + #[cfg(target_arch = "riscv64")] + clear_bss(); memory::heap::init(); memory::heap::test(); } @@ -148,8 +170,8 @@ fn rust_main(cpuid: usize, host_dtb: usize) { let cpu = PerCpu::new(cpuid); println!( - "Booting CPU {}: {:p}, DTB: {:#x}", - cpu.id, cpu as *const _, host_dtb + "Booting CPU {}: {:p} arch:{:p}, DTB: {:#x}", + cpu.id, cpu as *const _, &cpu.arch_cpu as *const _, host_dtb ); if is_primary { @@ -179,6 +201,7 @@ fn rust_main(cpuid: usize, host_dtb: usize) { device::irqchip::percpu_init(); INITED_CPUS.fetch_add(1, Ordering::SeqCst); + wait_for_counter(&INITED_CPUS, MAX_CPU_NUM as _); if is_primary { @@ -187,5 +210,12 @@ fn rust_main(cpuid: usize, host_dtb: usize) { wait_for_counter(&INIT_LATE_OK, 1); } + // run all unit tests before starting the root zone + // CAUTION: test_main will quit qemu after all tests are done + #[cfg(test)] + if is_primary { + test_main(); + } + cpu.run_vm(); } diff --git a/src/memory/addr.rs b/src/memory/addr.rs index 9ec9ae15..1527fd6c 100644 --- a/src/memory/addr.rs +++ b/src/memory/addr.rs @@ -2,6 +2,8 @@ #![allow(dead_code)] +use core::fmt::Debug; + use crate::consts::PAGE_SIZE; pub type VirtAddr = usize; diff --git a/src/memory/frame.rs b/src/memory/frame.rs index 5078d702..6c588aa2 100644 --- a/src/memory/frame.rs +++ b/src/memory/frame.rs @@ -127,6 +127,39 @@ impl Frame { } } + /// allocate contigugous frames, and you can specify the alignment, set the lower `align_log2` bits to 0. + pub fn new_contiguous_with_base(frame_count: usize, align_log2: usize) -> HvResult { + let align_mask = (1 << align_log2) - 1; + // Create a vector to keep track of attempted frames + let mut attempted_frames = Vec::new(); + loop { + if let Ok(frame) = Frame::new_contiguous(frame_count, 0) { + if frame.start_paddr() & align_mask == 0 { + info!( + "new contiguous success!!! start_paddr:0x{:x}", + frame.start_paddr() + ); + return Ok(frame); + } else { + let start_paddr = frame.start_paddr(); + let next_aligned_addr = (start_paddr + align_mask) & !align_mask; + let temp_frame_count = (next_aligned_addr - start_paddr) / PAGE_SIZE; + drop(frame); + attempted_frames.push(Frame::new_contiguous(temp_frame_count, 0)); + if let Ok(frame) = Frame::new_contiguous(frame_count, 0) { + info!( + "new contiguous success!!! start_paddr:0x{:x}", + frame.start_paddr() + ); + return Ok(frame); + } + } + } else { + return Err(hv_err!(ENOMEM)); + } + } + } + /// Constructs a frame from a raw physical address without automatically calling the destructor. /// /// # Safety diff --git a/src/memory/heap.rs b/src/memory/heap.rs index af2d7881..392fac58 100644 --- a/src/memory/heap.rs +++ b/src/memory/heap.rs @@ -4,7 +4,7 @@ use buddy_system_allocator::LockedHeap; use crate::consts::HV_HEAP_SIZE; -#[cfg_attr(not(test), global_allocator)] +#[global_allocator] static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new(); /// Initialize the global heap allocator. diff --git a/src/memory/mm.rs b/src/memory/mm.rs index 2c93ce45..f107f046 100644 --- a/src/memory/mm.rs +++ b/src/memory/mm.rs @@ -58,7 +58,12 @@ where pub fn new(pt_level: usize) -> Self { Self { regions: BTreeMap::new(), + #[cfg(target_arch = "aarch64")] pt: PT::new(pt_level), + #[cfg(target_arch = "riscv64")] + pt: PT::new(), + #[cfg(target_arch = "loongarch64")] + pt: PT::new(), } } @@ -69,6 +74,10 @@ where } } + pub fn root_paddr(&self) -> usize { + self.pt.root_paddr() + } + fn test_free_area(&self, other: &MemoryRegion) -> bool { if let Some((_, before)) = self.regions.range(..other.start).last() { if before.is_overlap_with(other) { @@ -85,6 +94,7 @@ where /// Add a memory region to this set. pub fn insert(&mut self, region: MemoryRegion) -> HvResult { + info!("region.start: {:#X}", region.start.into()); assert!(is_aligned(region.start.into())); assert!(is_aligned(region.size)); if region.size == 0 { diff --git a/src/memory/mod.rs b/src/memory/mod.rs index e602cf7c..3c94d9bf 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -42,92 +42,6 @@ pub fn hv_page_table<'a>() -> &'a RwLock> { HV_PT.get().expect("Uninitialized hypervisor page table!") } -// pub fn init_hv_page_table() { -// let sys_config = HvSystemConfig::get(); -// let hv_phys_start = sys_config.hypervisor_memory.phys_start as usize; -// let hv_phys_size = sys_config.hypervisor_memory.size as usize; -// let trampoline_page = TRAMPOLINE_START as usize - unsafe { PHYS_VIRT_OFFSET }; -// let gicd_base = sys_config.platform_info.arch.gicd_base; -// let gicr_base = sys_config.platform_info.arch.gicr_base; -// let gicr_size: u64 = MAX_CPU_NUM as u64 * GICR_SIZE; -// // let mmcfg_start = sys_config.platform_info.pci_mmconfig_base; -// // let mmcfg_size = (sys_config.platform_info.pci_mmconfig_end_bus + 1) as u64 * 256 * 4096; - -// let mut hv_pt: MemorySet = MemorySet::new(); - -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// HV_BASE as GuestPhysAddr, -// hv_phys_start as HostPhysAddr, -// hv_phys_size as usize, -// MemFlags::READ | MemFlags::WRITE | MemFlags::NO_HUGEPAGES, -// ))?; - -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// trampoline_page as GuestPhysAddr, -// trampoline_page as HostPhysAddr, -// PAGE_SIZE as usize, -// MemFlags::READ | MemFlags::WRITE, -// ))?; - -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// UART_BASE_VIRT, -// sys_config.debug_console.address as PhysAddr, -// sys_config.debug_console.size as usize, -// MemFlags::READ | MemFlags::WRITE | MemFlags::IO, -// ))?; - -// // add gicd memory map -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// gicd_base as GuestPhysAddr, -// gicd_base as HostPhysAddr, -// GICD_SIZE as usize, -// MemFlags::READ | MemFlags::WRITE | MemFlags::IO, -// ))?; - -// //add gicr memory map -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// gicr_base as GuestPhysAddr, -// gicr_base as HostPhysAddr, -// gicr_size as usize, -// MemFlags::READ | MemFlags::WRITE | MemFlags::IO, -// ))?; - -// // // Map pci region. Jailhouse doesn't map pci region to el2. -// // // Now we simplify the complex pci handler and just map it. -// // hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// // mmcfg_start as GuestPhysAddr, -// // mmcfg_start as HostPhysAddr, -// // mmcfg_size as usize, -// // MemFlags::READ | MemFlags::WRITE | MemFlags::IO, -// // ))?; - -// // add virtio map -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// 0xa000000 as GuestPhysAddr, -// 0xa000000 as HostPhysAddr, -// 0x4000 as usize, -// MemFlags::READ | MemFlags::WRITE | MemFlags::IO, -// ))?; - -// // add virt gic its -// hv_pt.insert(MemoryRegion::new_with_offset_mapper( -// 0x8080000 as GuestPhysAddr, -// 0x8080000 as HostPhysAddr, -// 0x20000 as usize, -// MemFlags::READ | MemFlags::WRITE | MemFlags::IO, -// ))?; - -// info!("Hypervisor page table init end."); - -// debug!("Hypervisor virtual memory set: {:#x?}", hv_pt); - -// unsafe { -// hv_pt.activate(); -// } - -// HV_PT.call_once(|| RwLock::new(hv_pt)); -// } - #[repr(align(4096))] pub struct AlignedPage([u8; PAGE_SIZE]); diff --git a/src/panic.rs b/src/panic.rs index 0aac07c7..a4618f35 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,8 +1,14 @@ +use crate::tests::*; use core::panic::PanicInfo; #[panic_handler] fn on_panic(info: &PanicInfo) -> ! { error!("panic occurred: {:#?}", info); + if cfg!(test) { + error!("panic occurred when running cargo test, quitting qemu"); + #[cfg(test)] + crate::tests::quit_qemu(HvUnitTestResult::Failed); + } loop {} } diff --git a/src/pci/bridge.rs b/src/pci/bridge.rs new file mode 100644 index 00000000..f9dcd7f5 --- /dev/null +++ b/src/pci/bridge.rs @@ -0,0 +1,65 @@ +use alloc::vec::Vec; + +use super::{ + pcibar::{BarRegion, PciBar}, + NUM_BAR_REGS_TYPE1, +}; + +#[derive(Debug)] +pub struct BridgeConfig { + bars: [PciBar; NUM_BAR_REGS_TYPE1], + pub bdf: usize, +} + +impl BridgeConfig { + pub fn new(bdf: usize) -> Self { + Self { + bars: [PciBar::default(); NUM_BAR_REGS_TYPE1], + bdf: bdf, + } + } + + pub fn bars_init(&mut self, bar_id: usize, origin_val: u32, val: u32) { + self.bars[bar_id].init(origin_val, val); + } + + pub fn get_regions(&self) -> Vec { + let mut regions: Vec = Vec::new(); + let mut bar_id = 0; + while bar_id < NUM_BAR_REGS_TYPE1 { + if self.bars[bar_id].is_mutable() { + if !self.bars[bar_id].mem_type_64() { + regions.push(self.bars[bar_id].get_32b_region()); + bar_id += 1; + } else { + regions.push( + self.bars[bar_id + 1].get_64b_region(self.bars[bar_id].get_32b_region()), + ); + bar_id += 2; + } + } else { + bar_id += 1; + } + } + regions + } + + // // the offset must be valid, to access the reg in hvisor + // pub fn bridge_cfg_access(&mut self, mmio: &mut MMIOAccess){ + // match mmio.address & 0xfff{ + // 0x10 => { + // match mmio.is_write{ + // true => self.bars[0].write(mmio.value as _), + // false => mmio.value = self.bars[0].read() as _, + // } + // }, + // 0x14 => { + // match mmio.is_write{ + // true => self.bars[1].write(mmio.value as _), + // false => mmio.value = self.bars[1].read() as _, + // } + // }, + // _ => unreachable!("invaild bridge cfg offset!!!"), + // } + // } +} diff --git a/src/pci/endpoint.rs b/src/pci/endpoint.rs new file mode 100644 index 00000000..128907a3 --- /dev/null +++ b/src/pci/endpoint.rs @@ -0,0 +1,65 @@ +use alloc::vec::Vec; + +use super::{ + pcibar::{BarRegion, PciBar}, + NUM_BAR_REGS_TYPE0, +}; + +#[derive(Debug)] +pub struct EndpointConfig { + bars: [PciBar; NUM_BAR_REGS_TYPE0], + pub bdf: usize, +} + +impl EndpointConfig { + pub fn new(bdf: usize) -> Self { + let (bars, bdf) = { ([PciBar::default(); NUM_BAR_REGS_TYPE0], bdf) }; + let r = EndpointConfig { bars, bdf }; + r + } + + pub fn bars_init(&mut self, bar_id: usize, origin_val: u32, val: u32) { + self.bars[bar_id].init(origin_val, val); + } + + pub fn get_regions(&self) -> Vec { + let mut regions: Vec = Vec::new(); + let mut bar_id = 0; + while bar_id < NUM_BAR_REGS_TYPE0 { + if self.bars[bar_id].is_mutable() { + if !self.bars[bar_id].mem_type_64() { + regions.push(self.bars[bar_id].get_32b_region()); + bar_id += 1; + } else { + regions.push( + self.bars[bar_id + 1].get_64b_region(self.bars[bar_id].get_32b_region()), + ); + bar_id += 2; + } + } else { + bar_id += 1; + } + } + regions + } + + // pub fn ep_cfg_access(&mut self, mmio: &mut MMIOAccess){ + // let bar_id = match mmio.address & 0xfff { + // 0x10 => 0, + // 0x14 => 1, + // 0x18 => 2, + // 0x1c => 3, + // 0x20 => 4, + // 0x24 => 5, + // _ => 0, + // }; + // match mmio.is_write { + // true => { + // self.bars[bar_id].write(mmio.value as _); + // }, + // false => { + // mmio.value = self.bars[bar_id].read() as _; + // } + // } + // } +} diff --git a/src/pci/mod.rs b/src/pci/mod.rs new file mode 100644 index 00000000..d611fb03 --- /dev/null +++ b/src/pci/mod.rs @@ -0,0 +1,29 @@ +use spin::Once; + +pub mod bridge; +pub mod endpoint; +pub mod pci; +pub mod pcibar; +pub mod phantom_cfg; + +pub const CFG_CMD_OFF: usize = 0x4; //status +pub const CFG_CAP_PTR_OFF: usize = 0x34; // capabilities pointer +pub const CFG_CLASS_CODE_OFF: usize = 0x8; // 4 bytes, include revision and class code + +pub const NUM_BAR_REGS_TYPE0: usize = 6; +pub const NUM_BAR_REGS_TYPE1: usize = 2; +pub const PHANTOM_DEV_HEADER: u32 = 0x77777777u32; + +pub static ECAM_BASE: Once = Once::new(); + +pub fn init_ecam_base(ecam_base: usize) { + ECAM_BASE.call_once(|| ecam_base); +} + +pub fn get_ecam_base() -> usize { + *ECAM_BASE.get().unwrap() as _ +} + +pub fn cfg_base(bdf: usize) -> usize { + get_ecam_base() + (bdf << 12) +} diff --git a/src/pci/pci.rs b/src/pci/pci.rs new file mode 100644 index 00000000..18fe304f --- /dev/null +++ b/src/pci/pci.rs @@ -0,0 +1,377 @@ +use core::{ptr, usize}; + +use crate::config::{HvPciConfig, CONFIG_MAX_PCI_DEV}; +use crate::memory::addr::align_down; +use crate::pci::pcibar::BarType; +use crate::pci::{get_ecam_base, init_ecam_base}; +use crate::percpu::this_zone; +use crate::zone::this_zone_id; +use crate::{ + error::HvResult, + memory::MMIOAccess, + memory::{mmio_perform_access, GuestPhysAddr, MemFlags, MemoryRegion}, + zone::Zone, +}; +use alloc::vec::Vec; + +use super::bridge::BridgeConfig; +use super::endpoint::EndpointConfig; +use super::pcibar::BarRegion; +use super::phantom_cfg::PhantomCfg; +use super::{ + cfg_base, CFG_CAP_PTR_OFF, CFG_CLASS_CODE_OFF, CFG_CMD_OFF, ECAM_BASE, NUM_BAR_REGS_TYPE0, + NUM_BAR_REGS_TYPE1, PHANTOM_DEV_HEADER, +}; + +#[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] +use crate::arch::iommu::iommu_add_device; + +#[derive(Debug)] +pub struct PciRoot { + endpoints: Vec, + bridges: Vec, + alloc_devs: Vec, // include host bridge + phantom_devs: Vec, + bar_regions: Vec, +} +impl PciRoot { + pub fn new() -> Self { + let r = Self { + endpoints: Vec::new(), + bridges: Vec::new(), + alloc_devs: Vec::new(), + phantom_devs: Vec::new(), + bar_regions: Vec::new(), + }; + r + } + + pub fn is_assigned_device(&self, bdf: usize) -> bool { + if self.alloc_devs.contains(&bdf) { + true + } else { + false + } + } + + pub fn bars_register(&mut self) { + self.ep_bars_init(); + self.bridge_bars_init(); + self.get_bars_regions(); + } + + fn get_bars_regions(&mut self) { + for ep in self.endpoints.iter() { + let regions = ep.get_regions(); + for mut region in regions { + if region.size < 0x1000 { + region.size = 0x1000; + } + self.bar_regions.push(region); + } + } + for bridge in self.bridges.iter() { + let regions = bridge.get_regions(); + for mut region in regions { + if region.size < 0x1000 { + region.size = 0x1000; + } + self.bar_regions.push(region); + } + } + info!("PCI BAR regions init done"); + } + + fn ep_bars_init(&mut self) { + for ep in self.endpoints.iter_mut() { + let cfg_base = cfg_base(ep.bdf); + let offsets: [usize; NUM_BAR_REGS_TYPE0] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; + for bar_id in 0..NUM_BAR_REGS_TYPE0 { + unsafe { + let reg_ptr = (cfg_base + offsets[bar_id]) as *mut u32; + let origin_val = *reg_ptr; + *reg_ptr = 0xffffffffu32; + let new_val = *reg_ptr; + ep.bars_init(bar_id, origin_val, new_val); + *reg_ptr = origin_val; + } + } + } + } + + fn bridge_bars_init(&mut self) { + for bridge in self.bridges.iter_mut() { + let cfg_base = cfg_base(bridge.bdf); + let offsets: [usize; NUM_BAR_REGS_TYPE1] = [0x10, 0x14]; + for bar_id in 0..NUM_BAR_REGS_TYPE1 { + unsafe { + let reg_ptr = (cfg_base + offsets[bar_id]) as *mut u32; + let origin_val = *reg_ptr; + *reg_ptr = 0xffffffffu32; + let new_val = *reg_ptr; + bridge.bars_init(bar_id, origin_val, new_val); + *reg_ptr = origin_val; + } + } + } + } +} + +impl Zone { + pub fn pci_init( + &mut self, + pci_config: &HvPciConfig, + num_pci_devs: usize, + alloc_pci_devs: &[u64; CONFIG_MAX_PCI_DEV], + ) { + if num_pci_devs == 0 { + return; + } + + info!("PCIe init!"); + + init_ecam_base(pci_config.ecam_base as _); + + for idx in 0..num_pci_devs { + info!( + "PCIe device assigned to zone {}: {:#x}", + self.id, alloc_pci_devs[idx] + ); + self.pciroot.alloc_devs.push(alloc_pci_devs[idx] as _); + #[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] + if alloc_pci_devs[idx] != 0 { + iommu_add_device(self.id, alloc_pci_devs[idx] as _); + } + } + + if self.id == 0 { + self.root_pci_init(pci_config); + } else { + self.virtual_pci_mmio_init(pci_config); + self.virtual_pci_device_init(pci_config); + } + } + + pub fn root_pci_init(&mut self, pci_config: &HvPciConfig) { + // Virtual ECAM + + self.mmio_region_register( + pci_config.ecam_base as _, + pci_config.ecam_size as _, + mmio_pci_handler, + pci_config.ecam_base as _, + ); + + if pci_config.io_size != 0 { + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + pci_config.io_base as GuestPhysAddr, + pci_config.io_base as _, + pci_config.io_size as _, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + } + + if pci_config.mem32_size != 0 { + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + pci_config.mem32_base as GuestPhysAddr, + pci_config.mem32_base as _, + pci_config.mem32_size as _, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + } + + if pci_config.mem64_size != 0 { + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + pci_config.mem64_base as GuestPhysAddr, + pci_config.mem64_base as _, + pci_config.mem64_size as _, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + } + } + + //probe pci mmio + pub fn virtual_pci_mmio_init(&mut self, pci_config: &HvPciConfig) { + self.mmio_region_register( + pci_config.ecam_base as _, + pci_config.ecam_size as _, + mmio_pci_handler, + pci_config.ecam_base as _, + ); + + if pci_config.io_size != 0 { + self.mmio_region_register( + pci_config.io_base as _, + pci_config.io_size as _, + mmio_pci_handler, + pci_config.io_base as _, + ); + } + + if pci_config.mem32_size != 0 { + self.mmio_region_register( + pci_config.mem32_base as _, + pci_config.mem32_size as _, + mmio_pci_handler, + pci_config.mem32_base as _, + ); + } + + if pci_config.mem64_size != 0 { + self.mmio_region_register( + pci_config.mem64_base as _, + pci_config.mem64_size as _, + mmio_pci_handler, + pci_config.mem64_base as _, + ); + } + } + + pub fn virtual_pci_device_init(&mut self, pci_config: &HvPciConfig) { + for bdf in self.pciroot.alloc_devs.clone() { + if bdf != 0 { + let base = cfg_base(bdf) + 0xe; + let header_val = unsafe { ptr::read_volatile(base as *mut u8) }; + match header_val & 0b1111111 { + 0b0 => self.pciroot.endpoints.push(EndpointConfig::new(bdf)), + 0b1 => self.pciroot.bridges.push(BridgeConfig::new(bdf)), + _ => error!("unsupported device type!"), + }; + } else { + // host bridge + } + } + + trace!("pciroot = {:?}", self.pciroot); + self.pciroot.bars_register(); + self.pci_bars_register(pci_config); + } + + fn pci_bars_register(&mut self, pci_config: &HvPciConfig) { + for region in self.pciroot.bar_regions.iter_mut() { + let (cpu_base, pci_base) = match region.bar_type { + BarType::IO => (pci_config.io_base as usize, pci_config.pci_io_base as usize), + BarType::Mem32 => ( + pci_config.mem32_base as usize, + pci_config.pci_mem32_base as usize, + ), + BarType::Mem64 => ( + pci_config.mem64_base as usize, + pci_config.pci_mem64_base as usize, + ), + _ => panic!("Unknown BAR type!"), + }; + + region.start = cpu_base + region.start - pci_base; + region.start = align_down(region.start); + + info!( + "pci bar region: type: {:?}, base: {:#x}, size:{:#x}", + region.bar_type, region.start, region.size + ); + + self.gpm + .insert(MemoryRegion::new_with_offset_mapper( + region.start as GuestPhysAddr, + region.start, + region.size, + MemFlags::READ | MemFlags::WRITE, + )) + .ok(); + } + } +} + +pub fn mmio_pci_handler(mmio: &mut MMIOAccess, base: usize) -> HvResult { + let reg_addr = mmio.address & 0xfff; + let bdf = mmio.address >> 12; + let function = bdf & 0x7; + let device = (bdf >> 3) & 0b11111; + let bus = bdf >> 8; + + let zone = this_zone(); + let mut binding = zone.write(); + let is_assigned = binding.pciroot.is_assigned_device(bdf); + + match is_assigned { + true => { + mmio_perform_access(base, mmio); + } + false => { + match reg_addr { + 0 => { + let header_addr = base + mmio.address; + let header_val = unsafe { ptr::read_volatile(header_addr as *mut u32) }; + if header_val == 0xffffffffu32 { + // empty device + mmio.value = 0xffffffffu32 as _; + } else { + // phantom device + let command_addr = CFG_CMD_OFF + header_addr; + let command = unsafe { ptr::read_volatile(command_addr as *mut u16) }; + binding + .pciroot + .phantom_devs + .push(PhantomCfg::new(bdf, command)); // add phantom devs, for accessing virtual command register + mmio.value = PHANTOM_DEV_HEADER as _; + } + } + CFG_CMD_OFF => { + if let Some(pdev) = binding + .pciroot + .phantom_devs + .iter_mut() + .find(|pdev| pdev.bdf == bdf) + { + if mmio.is_write { + pdev.set_cmd(mmio.value as _); + } else { + mmio.value = pdev.get_cmd() as _; + } + } + } + CFG_CLASS_CODE_OFF => { + if !mmio.is_write { + mmio.value = 0x1f000010; + } + } + CFG_CAP_PTR_OFF => { + // can't see any capabilities + mmio.value = 0x0; + } + _ => { + mmio_perform_access(base, mmio); + } + } + } + } + if mmio.is_write == true { + trace!( + "ecam write {} bytes, {:x}:{:x}:{:x} off:{:#x} -> {:#x}", + mmio.size, + bus, + device, + function, + reg_addr, + mmio.value + ); + } else { + trace!( + "ecam read {} bytes, {:x}:{:x}:{:x} off:{:#x} -> {:#x}", + mmio.size, + bus, + device, + function, + reg_addr, + mmio.value + ); + } + + Ok(()) +} diff --git a/src/pci/pcibar.rs b/src/pci/pcibar.rs new file mode 100644 index 00000000..ab8e1773 --- /dev/null +++ b/src/pci/pcibar.rs @@ -0,0 +1,92 @@ +#[derive(Debug, Default, Clone, Copy)] +pub struct PciBar { + val: u32, + bar_type: BarType, + size: usize, +} + +#[derive(Debug, Copy, Clone)] +pub struct BarRegion { + pub start: usize, + pub size: usize, + pub bar_type: BarType, +} + +#[derive(Default, Debug, Copy, Clone)] +pub enum BarType { + Mem32, + Mem64, + IO, + #[default] + Unknown, +} + +impl PciBar { + // origin_val: the register value written by vm + // val: write !0u64 to the BAR to get the size this BAR need + pub fn init(&mut self, origin_val: u32, val: u32) { + self.val = origin_val; + + if let Some(fix_bit) = (0..32).rev().find(|&off| val & (1 << off) == 0) { + if fix_bit != 31 { + self.size = 1 << (fix_bit + 1); + } else { + // fix_bit == 31, indicates that all the bits are read-only + self.size = 0; + } + } else { + // all the bits are rw, indicates this BAR's value is the upper 32 bits of a region's address + // so the size depends on the next BAR, set self.size to 1, or the value will overflow + self.size = 1; + } + + self.bar_type = match self.val & 0b1 { + 0b1 => BarType::IO, + _ => match self.val & 0b110 { + 0b000 => BarType::Mem32, + 0b100 => BarType::Mem64, + _ => BarType::Unknown, + }, + }; + } + + pub fn is_mutable(&self) -> bool { + match self.size { + 0 => false, + _ => true, + } + } + + pub fn mem_type_64(&self) -> bool { + match self.bar_type { + BarType::Mem64 => true, + _ => false, + } + } + + pub fn get_32b_region(&self) -> BarRegion { + BarRegion { + start: (self.val & 0xfffffff0) as _, + size: self.size, + bar_type: self.bar_type, + } + } + + pub fn get_upper_mem64_32b(&self) -> BarRegion { + BarRegion { + start: self.val as _, // upper 32bits are all mutable + size: self.size, + bar_type: self.bar_type, + } + } + + pub fn get_64b_region(&self, lower_region: BarRegion) -> BarRegion { + let higher_region = self.get_upper_mem64_32b(); + // info!("mm64, high: {:#x}, low: {:#x}", higher_region.start, lower_region.start); + BarRegion { + start: (higher_region.start << 32) + lower_region.start, + size: higher_region.size * lower_region.size, + bar_type: BarType::Mem64, + } + } +} diff --git a/src/pci/phantom_cfg.rs b/src/pci/phantom_cfg.rs new file mode 100644 index 00000000..f9e2d2ab --- /dev/null +++ b/src/pci/phantom_cfg.rs @@ -0,0 +1,22 @@ +#[derive(Debug)] +pub struct PhantomCfg { + pub bdf: usize, + command: u16, +} + +impl PhantomCfg { + pub fn new(bdf: usize, command: u16) -> Self { + Self { + bdf, + command: command & !0x400, // set disable-intx to 0, the origin state + } + } + + pub fn set_cmd(&mut self, command: u16) { + self.command = command; + } + + pub fn get_cmd(&self) -> u16 { + self.command + } +} diff --git a/src/percpu.rs b/src/percpu.rs index 54ce79cd..911473a0 100644 --- a/src/percpu.rs +++ b/src/percpu.rs @@ -1,16 +1,20 @@ use alloc::sync::Arc; +use alloc::vec::Vec; use spin::{Mutex, RwLock}; use crate::arch::cpu::{this_cpu_id, ArchCpu}; use crate::consts::{INVALID_ADDRESS, PER_CPU_ARRAY_PTR, PER_CPU_SIZE}; use crate::memory::addr::VirtAddr; use crate::zone::Zone; -use crate::ENTERED_CPUS; +use crate::{arch, ENTERED_CPUS}; use core::fmt::Debug; use core::sync::atomic::Ordering; // global_asm!(include_str!("./arch/aarch64/page_table.S"),); +#[cfg(test)] +mod tests; + #[repr(C)] pub struct PerCpu { pub id: usize, @@ -40,8 +44,11 @@ impl PerCpu { }; #[cfg(target_arch = "riscv64")] { - use crate::arch::csr::CSR_SSCRATCH; - write_csr!(CSR_SSCRATCH, &ret.arch_cpu as *const _ as usize); //arch cpu pointer + use crate::arch::csr::{write_csr, CSR_SSCRATCH}; + write_csr!( + CSR_SSCRATCH, + &ret.as_mut().unwrap().arch_cpu as *const _ as usize + ); //arch cpu pointer } unsafe { ret.as_mut().unwrap() } } diff --git a/src/percpu/tests.rs b/src/percpu/tests.rs new file mode 100644 index 00000000..d718bfeb --- /dev/null +++ b/src/percpu/tests.rs @@ -0,0 +1,23 @@ +use super::*; + +#[test_case] +fn test_cpuset() { + let mut cpuset = CpuSet::new(3, 0b1010); + assert_eq!(cpuset.contains_cpu(0), false); + assert_eq!(cpuset.contains_cpu(1), true); + assert_eq!(cpuset.contains_cpu(2), false); + assert_eq!(cpuset.contains_cpu(3), true); + cpuset.set_bit(0); + assert_eq!(cpuset.contains_cpu(0), true); + assert_eq!(cpuset.contains_cpu(1), true); + assert_eq!(cpuset.contains_cpu(2), false); + assert_eq!(cpuset.contains_cpu(3), true); + cpuset.clear_bit(1); + assert_eq!(cpuset.contains_cpu(0), true); + assert_eq!(cpuset.contains_cpu(1), false); + assert_eq!(cpuset.contains_cpu(2), false); + assert_eq!(cpuset.contains_cpu(3), true); + assert_eq!(cpuset.first_cpu(), Some(0)); + assert_eq!(cpuset.iter().collect::>(), vec![0, 3]); + assert_eq!(cpuset.iter_except(0).collect::>(), vec![3]); +} diff --git a/src/platform/imx8mp_aarch64.rs b/src/platform/imx8mp_aarch64.rs index ed93cdb2..30a8804e 100644 --- a/src/platform/imx8mp_aarch64.rs +++ b/src/platform/imx8mp_aarch64.rs @@ -5,7 +5,9 @@ pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0xa0400000; pub const ROOT_ZONE_ENTRY: u64 = 0xa0400000; pub const ROOT_ZONE_CPUS: u64 = (1 << 0) | (1 << 1); -pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 3] = [ +pub const ROOT_ZONE_NAME: &str = "root-linux"; + +pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 8] = [ HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, physical_start: 0x50000000, @@ -18,12 +20,43 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 3] = [ virtual_start: 0x30000000, size: 0x400000, }, // bus@30000000 + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x30c00000, + virtual_start: 0x30c00000, + size: 0x400000, + }, HvConfigMemoryRegion { mem_type: MEM_TYPE_IO, physical_start: 0x30800000, virtual_start: 0x30800000, size: 0x400000, - }, // bus@30800000 + }, + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x38000000, + virtual_start: 0x38000000, + size: 0x8000, + }, + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x38008000, + virtual_start: 0x38008000, + size: 0x8000, + }, + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x38500000, + virtual_start: 0x38500000, + size: 0x20000, + }, + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x32c00000, + virtual_start: 0x32c00000, + size: 0x400000, + }, // hdmi + // bus@30800000 // HvConfigMemoryRegion { // mem_type: MEM_TYPE_IO, // physical_start: 0x30890000, @@ -32,8 +65,9 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 3] = [ // }, // serial ]; -pub const ROOT_ZONE_IRQS: [u32; 19] = [ - 36, 52, 55, 59, 64, 67, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 150, 151, 152, +pub const ROOT_ZONE_IRQS: [u32; 28] = [ + 35, 36, 37, 38, 45, 52, 55, 56, 57, 59, 64, 67, 75, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 135, 150, 151, 152, 162, ]; pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { @@ -41,4 +75,15 @@ pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { gicd_size: 0x10000, gicr_base: 0x38880000, gicr_size: 0xc0000, + gicc_base: 0, + gicc_size: 0, + gicc_offset: 0x0, + gich_base: 0, + gich_size: 0, + gicv_base: 0, + gicv_size: 0, + gits_base: 0, + gits_size: 0, }; + +pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; diff --git a/src/platform/ls3a5000_loongarch64.rs b/src/platform/ls3a5000_loongarch64.rs new file mode 100644 index 00000000..801c78c0 --- /dev/null +++ b/src/platform/ls3a5000_loongarch64.rs @@ -0,0 +1,74 @@ +use crate::{arch::zone::HvArchZoneConfig, config::*}; + +pub const ROOT_ZONE_DTB_ADDR: u64 = 0x10000f000; +pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0x200000; +pub const ROOT_ZONE_ENTRY: u64 = 0x9000000000e71000; +pub const ROOT_ZONE_CPUS: u64 = 1 << 0; + +pub const ROOT_ZONE_NAME: &str = "root-linux-la64"; + +pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 7] = [ + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x00200000, + virtual_start: 0x00200000, + size: 0x0ee00000, + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x90000000, + virtual_start: 0x90000000, + size: 0x10000000, + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0xf0000000, + virtual_start: 0xf0000000, + size: 0x10000000, // 0xf0000000 - 0xffffffff, 256M + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0xc0000000, + virtual_start: 0xc0000000, + size: 0x30000000, // 0xc0000000 - 0xefffffff, 768M + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x1fe00000, + virtual_start: 0x1fe00000, + size: 0x2000, + }, // serial + // map special region + // 2024.4.12 + // linux's strscpy called gpa at 0x9000_0000_0000_0000 which is ldx x, 0x9000_0000_0000_0000(a1) + 0x0(a0) why ? + // __memcpy_fromio 0xf0000 why? + // (0x0, 0x10000, ZONE_MEM_FLAG_R | ZONE_MEM_FLAG_W | ZONE_MEM_FLAG_X) + // (0xf0000, 0x10000, ZONE_MEM_FLAG_R | ZONE_MEM_FLAG_W | ZONE_MEM_FLAG_X) + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x1000, + virtual_start: 0x0, + size: 0x10000, + }, // 0x0 + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0xf0000, + virtual_start: 0xf0000, + size: 0x10000, + }, // 0xf0000 + // HvConfigMemoryRegion { + // mem_type: MEM_TYPE_RAM, + // physical_start: 0x10000, + // virtual_start: 0x10000, + // size: 0x10000, + // }, // 0x10000 + // HvConfigMemoryRegion { + // mem_type: MEM_TYPE_RAM, + // physical_start: 0xf000000, + // virtual_start: 0xf000000, + // size: 0x1000, + // }, +]; + +pub const ROOT_ZONE_IRQS: [u32; 0] = []; +pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { dummy: 0 }; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 4e3732f9..d2a17cf2 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,6 +1,7 @@ use crate::{ config::{ - HvConfigMemoryRegion, HvZoneConfig, CONFIG_MAX_INTERRUPTS, CONFIG_MAX_MEMORY_REGIONS, + HvConfigMemoryRegion, HvIvcConfig, HvPciConfig, HvZoneConfig, CONFIG_MAX_INTERRUPTS, + CONFIG_MAX_IVC_CONGIGS, CONFIG_MAX_MEMORY_REGIONS, CONFIG_MAX_PCI_DEV, CONFIG_NAME_MAXLEN, }, consts::INVALID_ADDRESS, }; @@ -15,7 +16,7 @@ use qemu_riscv64::*; pub mod qemu_aarch64; #[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] -use qemu_aarch64::*; +pub use qemu_aarch64::*; #[cfg(all(feature = "platform_imx8mp", target_arch = "aarch64"))] pub mod imx8mp_aarch64; @@ -23,6 +24,18 @@ pub mod imx8mp_aarch64; #[cfg(all(feature = "platform_imx8mp", target_arch = "aarch64"))] use imx8mp_aarch64::*; +#[cfg(all(feature = "platform_zcu102", target_arch = "aarch64"))] +pub mod zcu102_aarch64; + +#[cfg(all(feature = "platform_zcu102", target_arch = "aarch64"))] +pub use zcu102_aarch64::*; + +#[cfg(target_arch = "loongarch64")] +pub mod ls3a5000_loongarch64; + +#[cfg(target_arch = "loongarch64")] +pub use ls3a5000_loongarch64::*; + pub fn platform_root_zone_config() -> HvZoneConfig { // fill zero for memory regions and interrupts @@ -35,9 +48,30 @@ pub fn platform_root_zone_config() -> HvZoneConfig { memory_regions[..ROOT_ZONE_MEMORY_REGIONS.len()].copy_from_slice(&ROOT_ZONE_MEMORY_REGIONS); + let mut ivc_configs = [HvIvcConfig::default(); CONFIG_MAX_IVC_CONGIGS]; + let mut num_ivc_configs = 0; + #[cfg(target_arch = "aarch64")] + { + num_ivc_configs = ROOT_ZONE_IVC_CONFIG.len() as _; + ivc_configs[..num_ivc_configs].copy_from_slice(&ROOT_ZONE_IVC_CONFIG); + } + let mut interrupts = [0; CONFIG_MAX_INTERRUPTS]; interrupts[..ROOT_ZONE_IRQS.len()].copy_from_slice(&ROOT_ZONE_IRQS); + let mut name = [0; CONFIG_NAME_MAXLEN]; + name[..ROOT_ZONE_NAME.len()].copy_from_slice(ROOT_ZONE_NAME.as_bytes()); + + let mut pci_devs = [0; CONFIG_MAX_PCI_DEV]; + let mut root_pci_cfg = HvPciConfig::new_empty(); + let mut num_pci_devs: u64 = 0; + #[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] + { + pci_devs[..ROOT_PCI_DEVS.len()].copy_from_slice(&ROOT_PCI_DEVS); + root_pci_cfg = ROOT_PCI_CONFIG; + num_pci_devs = ROOT_PCI_DEVS.len() as _; + } + HvZoneConfig::new( 0, ROOT_ZONE_CPUS, @@ -45,11 +79,17 @@ pub fn platform_root_zone_config() -> HvZoneConfig { memory_regions, ROOT_ZONE_IRQS.len() as u32, interrupts, + num_ivc_configs as _, + ivc_configs, ROOT_ZONE_ENTRY, ROOT_ZONE_KERNEL_ADDR, INVALID_ADDRESS as _, ROOT_ZONE_DTB_ADDR, INVALID_ADDRESS as _, + name, ROOT_ARCH_ZONE_CONFIG, + root_pci_cfg, + num_pci_devs, + pci_devs, ) } diff --git a/src/platform/qemu_aarch64.rs b/src/platform/qemu_aarch64.rs index 4897857b..b97c243f 100644 --- a/src/platform/qemu_aarch64.rs +++ b/src/platform/qemu_aarch64.rs @@ -5,12 +5,14 @@ pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0xa0400000; pub const ROOT_ZONE_ENTRY: u64 = 0xa0400000; pub const ROOT_ZONE_CPUS: u64 = (1 << 0) | (1 << 1); +pub const ROOT_ZONE_NAME: &str = "root-linux"; + pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 3] = [ HvConfigMemoryRegion { mem_type: MEM_TYPE_RAM, physical_start: 0x50000000, virtual_start: 0x50000000, - size: 0x80000000, + size: 0x70000000, }, // ram HvConfigMemoryRegion { mem_type: MEM_TYPE_IO, @@ -26,11 +28,40 @@ pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 3] = [ }, // virtio ]; -pub const ROOT_ZONE_IRQS: [u32; 4] = [33, 64, 77, 79]; +// 35 36 37 38 -> pcie intx# +// 65 -> ivc +pub const ROOT_ZONE_IRQS: [u32; 9] = [33, 64, 77, 79, 35, 36, 37, 38, 65]; pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { gicd_base: 0x8000000, gicd_size: 0x10000, gicr_base: 0x80a0000, gicr_size: 0xf60000, + gicc_base: 0x8010000, + gicc_size: 0x10000, + gicc_offset: 0x0, + gich_base: 0x8030000, + gich_size: 0x10000, + gicv_base: 0x8040000, + gicv_size: 0x10000, + gits_base: 0x8080000, + gits_size: 0x20000, +}; + +pub const ROOT_PCI_CONFIG: HvPciConfig = HvPciConfig { + ecam_base: 0x4010000000, + ecam_size: 0x10000000, + io_base: 0x3eff0000, + io_size: 0x10000, + pci_io_base: 0x0, + mem32_base: 0x10000000, + mem32_size: 0x2eff0000, + pci_mem32_base: 0x10000000, + mem64_base: 0x8000000000, + mem64_size: 0x8000000000, + pci_mem64_base: 0x8000000000, }; + +pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; + +pub const ROOT_PCI_DEVS: [u64; 2] = [0, 1 << 3]; diff --git a/src/platform/qemu_riscv64.rs b/src/platform/qemu_riscv64.rs index ef99c879..052c90be 100644 --- a/src/platform/qemu_riscv64.rs +++ b/src/platform/qemu_riscv64.rs @@ -1,5 +1,84 @@ +use crate::{arch::zone::HvArchZoneConfig, config::*}; + pub const PLIC_BASE: usize = 0xc000000; +pub const APLIC_BASE: usize = 0xc000000; pub const PLIC_MAX_IRQ: usize = 1024; pub const PLIC_GLOBAL_SIZE: usize = 0x200000; pub const PLIC_TOTAL_SIZE: usize = 0x400000; pub const PLIC_MAX_CONTEXT: usize = 64; +pub const PLIC_PRIORITY_BASE: usize = 0x0000; +pub const PLIC_PENDING_BASE: usize = 0x1000; +pub const PLIC_ENABLE_BASE: usize = 0x2000; + +pub const ROOT_ZONE_DTB_ADDR: u64 = 0x8f000000; +pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0x90000000; +pub const ROOT_ZONE_ENTRY: u64 = 0x90000000; +pub const ROOT_ZONE_CPUS: u64 = (1 << 0) | (1 << 1) | (1 << 2); + +pub const ROOT_ZONE_NAME: &str = "root-linux"; + +pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 9] = [ + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x83000000, + virtual_start: 0x83000000, + size: 0x1D000000, + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10000000, + virtual_start: 0x10000000, + size: 0x1000, + }, // serial + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x30000000, + virtual_start: 0x30000000, + size: 0x10000000, + }, // pci + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10001000, + virtual_start: 0x10001000, + size: 0x1000, + }, // virtio + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10002000, + virtual_start: 0x10002000, + size: 0x1000, + }, // virtio + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10003000, + virtual_start: 0x10003000, + size: 0x1000, + }, // virtio + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10004000, + virtual_start: 0x10004000, + size: 0x1000, + }, // virtio + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10005000, + virtual_start: 0x10005000, + size: 0x1000, + }, // virtio + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0x10008000, + virtual_start: 0x10008000, + size: 0x1000, + }, // virtio +]; + +pub const ROOT_ZONE_IRQS: [u32; 11] = [1, 2, 3, 4, 5, 8, 10, 33, 34, 35, 36]; // ARCH= riscv .It doesn't matter temporarily. + +pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { + plic_base: 0xc000000, + plic_size: 0x4000000, + aplic_base: 0xd000000, + aplic_size: 0x8000, +}; diff --git a/src/platform/zcu102_aarch64.rs b/src/platform/zcu102_aarch64.rs new file mode 100644 index 00000000..20c27d27 --- /dev/null +++ b/src/platform/zcu102_aarch64.rs @@ -0,0 +1,68 @@ +use crate::config::HvConfigMemoryRegion; +use crate::{arch::zone::HvArchZoneConfig, config::*}; + +pub const ROOT_ZONE_DTB_ADDR: u64 = 0x04000000; +pub const ROOT_ZONE_KERNEL_ADDR: u64 = 0x00200000; +pub const ROOT_ZONE_ENTRY: u64 = 0x00200000; +pub const ROOT_ZONE_CPUS: u64 = (1 << 0) | (1 << 1); + +pub const ROOT_ZONE_NAME: &str = "root-linux"; + +pub const ROOT_ZONE_MEMORY_REGIONS: [HvConfigMemoryRegion; 5] = [ + // HvConfigMemoryRegion { + // mem_type: MEM_TYPE_RAM, + // physical_start: 0x800000000, + // virtual_start: 0x800000000, + // size: 0x80000000, + // }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x00000000, + virtual_start: 0x00000000, + size: 0x40000000, + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0x50000000, + virtual_start: 0x50000000, + size: 0x25000000, + }, // ram + HvConfigMemoryRegion { + mem_type: MEM_TYPE_RAM, + physical_start: 0xfd070000, + virtual_start: 0xfd070000, + size: 0x30000, + }, // memory-controller + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xff000000, + virtual_start: 0xff000000, + size: 0x1000, + }, // serial + HvConfigMemoryRegion { + mem_type: MEM_TYPE_IO, + physical_start: 0xff170000, + virtual_start: 0xff170000, + size: 0x1000, + }, // mmc0 +]; + +pub const ROOT_ZONE_IRQS: [u32; 8] = [53, 81, 67, 175, 176, 177, 178, 64]; + +pub const ROOT_ARCH_ZONE_CONFIG: HvArchZoneConfig = HvArchZoneConfig { + gicd_base: 0xf9010000, + gicd_size: 0x10000, + gicr_base: 0x80a0000, + gicr_size: 0xf60000, + gits_base: 0x20000, + gits_size: 0x20000, + gicc_base: 0xf9020000, + gicc_size: 0x20000, + gicc_offset: 0xf000, + gich_base: 0xf9040000, + gich_size: 0x20000, + gicv_base: 0xf9060000, + gicv_size: 0x20000, +}; + +pub const ROOT_ZONE_IVC_CONFIG: [HvIvcConfig; 0] = []; diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 00000000..cfb0143d --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,67 @@ +/// this module is for unittests of hvisor +/// since this is a baremetal program +/// all unittests are performed when running hvisor on qemu +/// you will need to use `make test` to run the unittests +use core::ptr::write_volatile; +use qemu_exit::QEMUExit; + +#[test_case] +fn simple_test() { + assert_eq!(1, 1); +} + +/// base trait for hvisor unittests +pub trait HvUnitTest { + fn run(&self); +} + +impl HvUnitTest for T +where + T: Fn(), +{ + fn run(&self) { + // Get the test name + let test_name = core::any::type_name::(); + + // Print a clean start message with a header and test name + print!("\n--- Running test: {} ---\n", test_name); + + // Execute the test function + self(); + + // Print a success message after the test + println!("Result: PASSED"); + } +} + +/// The result of a unit test +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HvUnitTestResult { + Success, + Failed, +} + +pub fn quit_qemu(result: HvUnitTestResult) { + warn!("quitting qemu, result: {:?}", result); + #[cfg(target_arch = "aarch64")] + { + let qemu_exit_handle = qemu_exit::AArch64::new(); + match result { + HvUnitTestResult::Success => qemu_exit_handle.exit_success(), + HvUnitTestResult::Failed => qemu_exit_handle.exit_failure(), + } + } +} + +#[cfg(test)] +#[no_mangle] +pub fn test_main(tests: &[&dyn HvUnitTest]) { + info!("Running {} unit tests", tests.len()); + println!("\nTotal {} tests to run", tests.len()); + for test in tests { + test.run(); + } + println!("\nAll tests passed without panic which is good"); + quit_qemu(HvUnitTestResult::Success); + loop {} +} diff --git a/src/zone.rs b/src/zone.rs index 0f3f6e32..f2db2ee5 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -1,11 +1,13 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use psci::error::INVALID_ADDRESS; +// use psci::error::INVALID_ADDRESS; +use crate::consts::INVALID_ADDRESS; +use crate::pci::pci::PciRoot; use spin::RwLock; use crate::arch::mm::new_s2_memory_set; use crate::arch::s2pt::Stage2PageTable; -use crate::config::HvZoneConfig; +use crate::config::{HvZoneConfig, CONFIG_NAME_MAXLEN}; use crate::consts::MAX_CPU_NUM; use crate::error::HvResult; @@ -14,22 +16,29 @@ use crate::memory::{MMIOConfig, MMIOHandler, MMIORegion, MemorySet}; use crate::percpu::{get_cpu_data, this_zone, CpuSet}; use core::panic; +#[cfg(test)] +pub mod tests; + pub struct Zone { + pub name: [u8; CONFIG_NAME_MAXLEN], pub id: usize, pub mmio: Vec, pub cpu_set: CpuSet, pub irq_bitmap: [u32; 1024 / 32], pub gpm: MemorySet, + pub pciroot: PciRoot, } impl Zone { - pub fn new(zoneid: usize) -> Self { + pub fn new(zoneid: usize, name: &[u8]) -> Self { Self { + name: name.try_into().unwrap(), id: zoneid, gpm: new_s2_memory_set(), cpu_set: CpuSet::new(MAX_CPU_NUM as usize, 0), mmio: Vec::new(), irq_bitmap: [0; 1024 / 32], + pciroot: PciRoot::new(), } } @@ -143,23 +152,26 @@ pub fn find_zone(zone_id: usize) -> Option>> { .cloned() } +pub fn all_zones_info() -> Vec { + let zone_list = ZONE_LIST.read(); + + zone_list + .iter() + .map(|zone| { + let zone_lock = zone.read(); + ZoneInfo { + zone_id: zone_lock.id as u32, + cpus: zone_lock.cpu_set.bitmap, + name: zone_lock.name.clone(), + } + }) + .collect() +} + pub fn this_zone_id() -> usize { this_zone().read().id } -// #[repr(C)] -// #[derive(Debug, Clone)] -// pub struct ZoneConfig { -// pub zone_id: u32, -// pub cpus: u64, -// pub num_memory_regions: u32, -// pub memory_regions: [MemoryRegion; CONFIG_MAX_MEMORY_REGIONS], -// pub num_interrupts: u32, -// pub interrupts: [u32; CONFIG_MAX_INTERRUPTS], -// pub entry_point: u64, -// pub dtb_load_paddr: u64, -// } - pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { // we create the new zone here // TODO: create Zone with cpu_set @@ -169,21 +181,23 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { return hv_result_err!(EEXIST); } - let mut zone = Zone::new(zone_id); + let mut zone = Zone::new(zone_id, &config.name); zone.pt_init(config.memory_regions()).unwrap(); - zone.mmio_init(&config.arch); + zone.mmio_init(&config.arch_config); zone.irq_bitmap_init(config.interrupts()); + #[cfg(target_arch = "aarch64")] + zone.ivc_init(config.ivc_config()); + #[cfg(all(feature = "platform_qemu", target_arch = "aarch64"))] + zone.pci_init( + &config.pci_config, + config.num_pci_devs as _, + &config.alloc_pci_devs, + ); config.cpus().iter().for_each(|cpu_id| { zone.cpu_set.set_bit(*cpu_id as _); }); - // pub struct HvConfigMemoryRegion { - // pub mem_type: u32, - // pub physical_start: u64, - // pub virtual_start: u64, - // pub size: u64, - // } let mut dtb_ipa = INVALID_ADDRESS as u64; for region in config.memory_regions() { // region contains config.dtb_load_paddr? @@ -213,3 +227,11 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { Ok(new_zone_pointer) } + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ZoneInfo { + zone_id: u32, + cpus: u64, + name: [u8; CONFIG_NAME_MAXLEN], +} diff --git a/src/zone/tests.rs b/src/zone/tests.rs new file mode 100644 index 00000000..1c2f39ff --- /dev/null +++ b/src/zone/tests.rs @@ -0,0 +1,18 @@ +use super::*; +use alloc::sync::Arc; +use spin::RwLock; + +#[test_case] +fn test_add_and_remove_zone() { + let zone_count = 50; + let zone_count_before = ZONE_LIST.read().len(); + for i in 0..zone_count { + let u8name_array = [i as u8; CONFIG_NAME_MAXLEN]; + let zone = Zone::new(i, &u8name_array); + ZONE_LIST.write().push(Arc::new(RwLock::new(zone))); + } + for i in 0..zone_count { + remove_zone(i); + } + assert_eq!(ZONE_LIST.read().len(), zone_count_before); +} diff --git a/tools/cargo_test.sh b/tools/cargo_test.sh new file mode 100755 index 00000000..3f14a582 --- /dev/null +++ b/tools/cargo_test.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# for hvisor's unit test when running cargo test +# passed to .cargo/config +# runner = "___HVISOR_SRC___/_cargo_test.sh" +# ___HVISOR_SRC___ will be replaced dynamically by the Makefile and restored after the test +# wheatfox(wheatfox17@icloud.com) 2024.12 + +PWD=$(pwd) +THIS=$(basename $0) +CARGO_BUILD_INPUT_ARG0=$1 + +# capture env: FEATURES, ARCH + +ARCH=${ARCH} +FEATURES=${FEATURES} +BOARD=${BOARD} + +UBOOT_GICV3=images/aarch64/bootloader/u-boot-atf.bin +UBOOT_GICV2=images/aarch64/bootloader/u-boot-v2.bin +# UBOOT=u-boot.bin + +HVISOR_ELF=$CARGO_BUILD_INPUT_ARG0 +HVISOR_BIN_TMP=$HVISOR_ELF.bin.tmp +HVISOR_BIN=$HVISOR_ELF.bin + +OBJCOPY=rust-objcopy + +YELLOW='\033[1;33m' +END='\033[0m' + +info() { + # echo "${YELLOW}[INFO | $THIS] $1${END}" + echo "[INFO | $THIS] $1" +} + +info "Running cargo test with env: ARCH=$ARCH, FEATURES=$FEATURES, BOARD=$BOARD" + +info "Building hvisor with $CARGO_BUILD_INPUT_ARG0" +info "PWD=$PWD, running cargo test" +$OBJCOPY $HVISOR_ELF --strip-all -O binary $HVISOR_BIN_TMP + +if [ "$ARCH" == "aarch64" ]; then + mkimage -n hvisor_img -A arm64 -O linux -C none -T kernel -a 0x40400000 \ + -e 0x40400000 -d $HVISOR_BIN_TMP $HVISOR_BIN + + info "Running QEMU with $HVISOR_BIN" + + # if we have gicv2,gicv3 in FEATURES, we get the number from it + AARCH64_GIC_TEST_VERSION=3 + if [[ $FEATURES == *"gicv2"* ]]; then + AARCH64_GIC_TEST_VERSION=2 + fi + info "Using GIC version: $AARCH64_GIC_TEST_VERSION" + + UBOOT=$UBOOT_GICV3 + if [ $AARCH64_GIC_TEST_VERSION -eq 2 ]; then + UBOOT=$UBOOT_GICV2 + fi + info "Using U-Boot: $UBOOT" + + qemu-system-aarch64 \ + -machine virt,secure=on,gic-version=${AARCH64_GIC_TEST_VERSION},virtualization=on,iommu=smmuv3 \ + -global arm-smmuv3.stage=2 \ + -cpu cortex-a57 -smp 4 -m 3G -nographic \ + -semihosting \ + -bios $UBOOT \ + -drive if=pflash,format=raw,index=1,file=flash.img \ + -device loader,file=$HVISOR_BIN,addr=0x40400000,force-raw=on + + mv .cargo/config.bak .cargo/config + exit 0 +elif [ "$ARCH" == "riscv64" ]; then + info "riscv64 auto test is not supported yet" + mv .cargo/config.bak .cargo/config + exit 1 +else + info "Unsupported ARCH: $ARCH" + mv .cargo/config.bak .cargo/config + exit 1 +fi \ No newline at end of file diff --git a/tools/compress.sh b/tools/compress.sh new file mode 100755 index 00000000..524bd6c9 --- /dev/null +++ b/tools/compress.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# first find hvisor src dir by searching for README.md +# find in current dir and parent dir +HVISOR_SRC_DIR=. +while [ ! -f "$HVISOR_SRC_DIR/README.md" ]; do + if [ "$HVISOR_SRC_DIR" = "/" ]; then + echo "Error: Could not find hvisor source directory." + exit 1 + fi + HVISOR_SRC_DIR="$HVISOR_SRC_DIR/.." +done + +INPUT_FILE="$HVISOR_SRC_DIR/flash.img" + +if [ ! -f "$INPUT_FILE" ]; then + echo "Error: File $INPUT_FILE does not exist." + exit 1 +fi + +OUTPUT_FILE="$HVISOR_SRC_DIR/flash.img.partial" + +dd if="$INPUT_FILE" of="$OUTPUT_FILE" bs=1K count=1 status=none + +if [ $? -eq 0 ]; then + echo "Successfully truncated $INPUT_FILE to $OUTPUT_FILE." +else + echo "Error: Truncation failed." + exit 1 +fi \ No newline at end of file diff --git a/tools/extract.sh b/tools/extract.sh new file mode 100755 index 00000000..f0e5bab9 --- /dev/null +++ b/tools/extract.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# first find hvisor src dir by searching for README.md +# find in current dir and parent dir +HVISOR_SRC_DIR=. +while [ ! -f "$HVISOR_SRC_DIR/README.md" ]; do + if [ "$HVISOR_SRC_DIR" = "/" ]; then + echo "Error: Could not find hvisor source directory." + exit 1 + fi + HVISOR_SRC_DIR="$HVISOR_SRC_DIR/.." +done + +INPUT_FILE="$HVISOR_SRC_DIR/flash.img.partial" + +if [ ! -f "$INPUT_FILE" ]; then + echo "Error: File $INPUT_FILE does not exist." + exit 1 +fi + +OUTPUT_FILE="flash.img" +TARGET_SIZE=$((64 * 1024)) # 64MB is 64 * 1024KB + +CURRENT_SIZE=$(stat -c%s "$INPUT_FILE" 2>/dev/null || stat -f%z "$INPUT_FILE") +CURRENT_SIZE=$((CURRENT_SIZE / 1024)) # Convert bytes to KB + +echo "Current size of $INPUT_FILE: $CURRENT_SIZE KB." +echo "Target size: $TARGET_SIZE KB." + +if [ $CURRENT_SIZE -gt $TARGET_SIZE ]; then + echo "Error: $INPUT_FILE is larger than the target size of 64MB." + exit 1 +fi + +cp "$INPUT_FILE" "$OUTPUT_FILE" +dd if=/dev/zero bs=1K count=$((TARGET_SIZE - CURRENT_SIZE)) >> "$OUTPUT_FILE" + +if [ $? -eq 0 ]; then + echo "Successfully padded $INPUT_FILE to $OUTPUT_FILE with a size of 64MB." +else + echo "Error: Padding failed." + exit 1 +fi diff --git a/vendor/fdt/dtb/issue-3.dtb b/vendor/fdt/dtb/issue-3.dtb deleted file mode 100644 index 02c7fcaa..00000000 Binary files a/vendor/fdt/dtb/issue-3.dtb and /dev/null differ diff --git a/vendor/fdt/dtb/sifive.dtb b/vendor/fdt/dtb/sifive.dtb deleted file mode 100644 index f3fa7166..00000000 Binary files a/vendor/fdt/dtb/sifive.dtb and /dev/null differ diff --git a/vendor/fdt/dtb/test.dtb b/vendor/fdt/dtb/test.dtb deleted file mode 100644 index 27f6a9cb..00000000 Binary files a/vendor/fdt/dtb/test.dtb and /dev/null differ