├── .github └── workflows │ ├── ci.yml │ └── main.yml ├── .gitignore ├── .gitlab ├── issue_templates │ └── Bug.md └── merge_request_templates │ ├── BugFix.md │ ├── Docs.md │ ├── Minor.md │ ├── Newfeature.md │ └── Refactor.md ├── .gitmodules ├── Cargo.toml ├── LICENSE ├── README.md ├── figures ├── arch-dark.png ├── arch-isol-dark.png ├── arch-isol-light.png ├── arch-light.png ├── arch-plane-dark.png ├── arch-plane-light.png ├── logo-long-black.png ├── logo-long-dark-cropped.png ├── logo-long-dark.png ├── logo-long-light-cropped.png ├── logo-long-light.png ├── logo-long-white.png ├── overview-dark.png └── overview-light.png ├── scripts ├── build │ ├── build_duvisor.sh │ ├── build_guest_linux.sh │ ├── build_host_linux.sh │ ├── build_opensbi.sh │ ├── build_qemu.sh │ ├── build_rootfs.sh │ ├── copy_duvisor_to_vm.sh │ ├── docker_exec_wrapper.sh │ └── example_copy.sh ├── expect_wrapper.sh ├── local │ ├── boot.sh │ ├── duvisor_test.exp │ ├── duvisor_test.sh │ ├── duvisor_test_main.tcl │ ├── duvisor_test_multi_vcpu.sh │ ├── duvisor_test_network.exp │ ├── duvisor_test_network.sh │ ├── duvisor_test_standalone.exp │ ├── multi_vcpu │ │ ├── duvisor_test_main_multi_vcpu.tcl │ │ ├── duvisor_test_multi_vcpu_8.exp │ │ └── duvisor_test_multi_vcpu_8.sh │ └── run_tests.sh ├── opensource │ └── Dockerfile ├── quick_docker_build.sh ├── quick_native_build.sh └── run │ └── example_boot.sh ├── src ├── atomic_enum │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── data_model │ ├── Cargo.toml │ └── src │ │ ├── endian.rs │ │ ├── lib.rs │ │ └── volatile_memory.rs ├── devices │ ├── Cargo.toml │ └── src │ │ ├── bus.rs │ │ ├── i8042.rs │ │ ├── lib.rs │ │ ├── serial.rs │ │ └── virtio │ │ ├── block.rs │ │ ├── mmio.rs │ │ ├── mod.rs │ │ ├── net.rs │ │ └── queue.rs ├── duvisor │ ├── Cargo.toml │ ├── build.rs │ ├── preparefile.rs │ └── src │ │ ├── clap_config.yml │ │ ├── devices │ │ ├── mod.rs │ │ ├── plic.rs │ │ └── vplic.rs │ │ ├── guestentry │ │ ├── csr.h │ │ ├── enter_guest.S │ │ ├── guest_entry.h │ │ └── save_restore.S │ │ ├── init │ │ ├── cmdline.rs │ │ └── mod.rs │ │ ├── irq │ │ ├── delegation.rs │ │ ├── mod.rs │ │ ├── vipi.rs │ │ ├── virq.rs │ │ └── vtimer.S │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── mm │ │ ├── gparegion.rs │ │ ├── gstagemmu.rs │ │ ├── hpmallocator.rs │ │ ├── mmio.rs │ │ ├── mod.rs │ │ └── utils.rs │ │ ├── plat │ │ ├── kvm │ │ │ ├── mod.rs │ │ │ └── syscall.rs │ │ ├── mod.rs │ │ ├── opensbi │ │ │ ├── emulation.rs │ │ │ ├── mod.rs │ │ │ └── uart.c │ │ └── uhe │ │ │ ├── csr.rs │ │ │ ├── ioctl.rs │ │ │ └── mod.rs │ │ ├── test │ │ ├── mod.rs │ │ └── utils.rs │ │ ├── unit │ │ ├── fake_config │ │ └── unitest_kernel │ │ ├── vcpu │ │ ├── asm_offset.h │ │ ├── mod.rs │ │ ├── utils.rs │ │ ├── vcpucontext.rs │ │ └── virtualcpu.rs │ │ └── vm │ │ ├── dtb.rs │ │ ├── image.rs │ │ ├── mod.rs │ │ └── virtualmachine.rs ├── irq_util │ ├── Cargo.toml │ └── src │ │ ├── irqchip.rs │ │ └── lib.rs ├── net_sys │ ├── Cargo.toml │ └── src │ │ ├── if_tun.rs │ │ ├── iff.rs │ │ ├── inn.rs │ │ ├── lib.rs │ │ └── sockios.rs ├── net_util │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── tap.rs ├── sys_util │ ├── Cargo.toml │ ├── build.rs │ ├── sock_ctrl_msg.c │ └── src │ │ ├── errno.rs │ │ ├── eventfd.rs │ │ ├── fork.rs │ │ ├── guest_address.rs │ │ ├── guest_memory.rs │ │ ├── handle_eintr.rs │ │ ├── ioctl.rs │ │ ├── lib.rs │ │ ├── mmap.rs │ │ ├── passwd.rs │ │ ├── poll.rs │ │ ├── shm.rs │ │ ├── signal.rs │ │ ├── signalfd.rs │ │ ├── sock_ctrl_msg.rs │ │ ├── struct_util.rs │ │ ├── syslog.rs │ │ ├── tempdir.rs │ │ └── terminal.rs ├── syscall_defines │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── linux-aarch64 │ │ └── mod.rs │ │ ├── linux-arm │ │ └── mod.rs │ │ ├── linux-x86 │ │ └── mod.rs │ │ └── linux-x86_64 │ │ └── mod.rs └── virtio_sys │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── virtio_net.rs └── tests └── integration └── test_images ├── CMakeLists.txt ├── asm.h ├── build.sh ├── dtb_ld_data.S ├── ecall_emulation_remote_fence.S ├── ecall_emulation_unsupported.S ├── opensbi_getchar_count.S ├── opensbi_getchar_sum.S ├── opensbi_putchar.S ├── tty_load.S ├── tty_store.S ├── vcpu_add_all_gprs.S ├── vcpu_ecall_exit.S ├── vipi_send_to_null_vcpu.S ├── vipi_user_ipi_remote.S ├── vipi_user_ipi_remote_multi.S ├── vipi_virtual_ipi_accurate.S ├── vipi_virtual_ipi_local.S ├── vipi_virtual_ipi_remote_each.S ├── vipi_virtual_ipi_remote_not_running.S ├── vipi_virtual_ipi_remote_running.S ├── vm.ld ├── vmem_W_Ro.S ├── vmem_X_nonX.S ├── vmem_ld_data.S ├── vmem_ld_mapping.S ├── vmem_ld_sd_over_loop.S └── vmem_ld_sd_sum.S /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Daily Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | Test: 14 | 15 | #runs-on: ubuntu-latest 16 | runs-on: [self-hosted, Linux, X64] 17 | timeout-minutes: 10000000 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | submodules: recursive 23 | token: ${{ secrets.PAT_TOKEN }} 24 | clean: false 25 | - name: Build 26 | run: | 27 | git submodule update --init --recursive 28 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_qemu.sh 29 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_host_linux.sh 30 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_guest_linux.sh 31 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_opensbi.sh 32 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_duvisor.sh 33 | ./scripts/build/copy_duvisor_to_vm.sh 34 | - name: Unit Test 35 | run: | 36 | ./scripts/local/duvisor_test.sh 37 | killall qemu-system-riscv64 || echo "" 38 | - name: Single VM Test 39 | run: | 40 | killall qemu-system-riscv64 || echo "" 41 | sleep 2 42 | ./scripts/expect_wrapper.sh ./scripts/local/duvisor_test_standalone.exp 43 | sleep 2 44 | ./scripts/local/duvisor_test_network.sh 45 | screen -wipe || echo "" 46 | sleep 2 47 | ./scripts/local/duvisor_test_multi_vcpu.sh 48 | - name: Clean up 49 | if: ${{ always() }} 50 | run: | 51 | sudo rm -r mnt || echo "" 52 | sudo rm -r target || echo "" 53 | sudo rm -r tests/integration/test_images/build || echo "" 54 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [main, firesim, security-ae] 4 | paths: ['README.md'] 5 | 6 | jobs: 7 | build: 8 | #runs-on: ubuntu-latest 9 | runs-on: [self-hosted, Linux, X64] 10 | timeout-minutes: 5 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | clean: false 15 | - run: | 16 | curl https://raw.githubusercontent.com/ekalinin/github-markdown-toc/0.8.0/gh-md-toc -o gh-md-toc 17 | chmod a+x gh-md-toc 18 | ./gh-md-toc --insert --no-backup --hide-footer README.md 19 | rm gh-md-toc 20 | - uses: stefanzweifel/git-auto-commit-action@v4 21 | with: 22 | commit_message: Auto update markdown TOC 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .rusty-tags.vi 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | /prepare 6 | /.vscode 7 | /mnt 8 | /tests/integration/test_images/build/* 9 | /testsfiles/integration/test_images/build/* 10 | /tests/*/*.img 11 | /tests/*.img 12 | /tests/Image 13 | /src/duvisor/src/vcpu/asm_offset.h 14 | .gdbinit 15 | /.cargo 16 | /vendor 17 | vm.log 18 | linux-guest 19 | linux-*.tar.xz 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /.gitlab/issue_templates/Bug.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | ### Summary 9 | 10 | (Summarize the bug encountered concisely) 11 | 12 | ### Branch or commit number 13 | 14 | (Which branch or commit number causes the bug) 15 | 16 | ### Steps to reproduce 17 | 18 | (How one can reproduce the issue - this is very important) 19 | 20 | ### What is the expected *correct* behavior? 21 | 22 | (What you should see instead) 23 | 24 | ### What is the current *bug's* behavior? 25 | 26 | (What actually happens) 27 | 28 | ### Relevant logs and/or screenshots 29 | 30 | (Paste any relevant logs - please use code blocks (```) to format console output, 31 | logs, and code as it's tough to read otherwise.) 32 | 33 | ### Possible fixes 34 | 35 | (If any comes to your mind) 36 | 37 | /label ~Issue-bug 38 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/BugFix.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | /label ~"MR-bugfix" 10 | 11 | ## Bug Description 12 | 13 | 17 | 18 | ## Changes 19 | 20 | 23 | 24 | ## Limitations 25 | 26 | 29 | 30 | None. 31 | 32 | ## Related Jira Task URL 33 | 34 | 37 | 38 | Not related to a Jira Task. 39 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/Docs.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | /label ~"MR-docs" 10 | 11 | ## Description 12 | 13 | 16 | 17 | ## Related Jira Task URL 18 | 19 | 22 | 23 | Not related to a Jira Task. 24 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/Minor.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | /label ~"MR-minor" 10 | 11 | ## Description 12 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/Newfeature.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | /label ~"MR-newfeature" 10 | 11 | ## Rationale 12 | 13 | 16 | 17 | ## Changes 18 | 19 | 22 | 23 | ## Limitations 24 | 25 | 28 | 29 | None. 30 | 31 | ## Related Jira Task URL 32 | 33 | 36 | 37 | Not related to a Jira Task. 38 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/Refactor.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | /label ~"MR-refactor" 10 | 11 | ## Rationale 12 | 13 | 16 | 17 | ## Changes 18 | 19 | 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "qemu-duvisor"] 2 | path = qemu-duvisor 3 | url = https://github.com/IPADS-DuVisor/qemu-duvisor.git 4 | [submodule "linux-duvisor"] 5 | path = linux-duvisor 6 | url = https://github.com/IPADS-DuVisor/linux-duvisor.git 7 | [submodule "opensbi-duvisor"] 8 | path = opensbi-duvisor 9 | url = https://github.com/IPADS-DuVisor/opensbi-duvisor.git 10 | [submodule "test-files-duvisor"] 11 | path = test-files-duvisor 12 | url = https://github.com/IPADS-DuVisor/test-files-duvisor.git 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["src/duvisor"] 3 | -------------------------------------------------------------------------------- /figures/arch-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/arch-dark.png -------------------------------------------------------------------------------- /figures/arch-isol-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/arch-isol-dark.png -------------------------------------------------------------------------------- /figures/arch-isol-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/arch-isol-light.png -------------------------------------------------------------------------------- /figures/arch-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/arch-light.png -------------------------------------------------------------------------------- /figures/arch-plane-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/arch-plane-dark.png -------------------------------------------------------------------------------- /figures/arch-plane-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/arch-plane-light.png -------------------------------------------------------------------------------- /figures/logo-long-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/logo-long-black.png -------------------------------------------------------------------------------- /figures/logo-long-dark-cropped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/logo-long-dark-cropped.png -------------------------------------------------------------------------------- /figures/logo-long-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/logo-long-dark.png -------------------------------------------------------------------------------- /figures/logo-long-light-cropped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/logo-long-light-cropped.png -------------------------------------------------------------------------------- /figures/logo-long-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/logo-long-light.png -------------------------------------------------------------------------------- /figures/logo-long-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/logo-long-white.png -------------------------------------------------------------------------------- /figures/overview-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/overview-dark.png -------------------------------------------------------------------------------- /figures/overview-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/figures/overview-light.png -------------------------------------------------------------------------------- /scripts/build/build_duvisor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -e "$HOME/.cargo/config" ]; then 4 | mkdir -p ~/.cargo/ 5 | echo ' 6 | [source.crates-io] 7 | registry = "https://github.com/rust-lang/crates.io-index" 8 | 9 | [net] 10 | git-fetch-with-cli = true 11 | 12 | [target.riscv64gc-unknown-linux-gnu] 13 | linker = "riscv64-linux-gnu-gcc" 14 | ' >> ~/.cargo/config 15 | 16 | # If `~/.cargo/config` does not exist, it is likely to be in docker env. 17 | # In docker env, `rustup update` would encounter error. 18 | # According to https://github.com/rust-lang/rustup/issues/2729#issuecomment-1516103534, 19 | # we reinstall the rustup stable toolchain to keep it up-to-date. 20 | rustup toolchain uninstall stable && rustup toolchain install stable 21 | fi 22 | 23 | export PATH=/root/.cargo/bin:$PATH 24 | 25 | first_arg=${1:-release} 26 | 27 | if test ${first_arg} = release; then 28 | build_level="--release" 29 | build_path=release 30 | elif test ${first_arg} = debug; then 31 | build_level="" 32 | build_path=debug 33 | else 34 | echo "Wrong arg." 35 | exit 36 | fi 37 | 38 | #export CARGO_NET_OFFLINE=true 39 | 40 | echo `hostname` 41 | 42 | echo $build_level 43 | 44 | cargo clean 45 | cargo update 46 | rustup default stable 47 | rustup update 48 | rustup target add riscv64gc-unknown-linux-gnu 49 | RUSTFLAGS='-C target-feature=+crt-static' cargo build --target=riscv64gc-unknown-linux-gnu $build_level --features "qemu" 50 | 51 | 52 | # get duvisor all the binary names 53 | RUSTFLAGS='-C target-feature=+crt-static' cargo test --no-run --target=riscv64gc-unknown-linux-gnu $build_level --features "qemu" 54 | 55 | ## Build test images 56 | rm -r ./tests/integration/test_images/build 57 | ./tests/integration/test_images/build.sh ./tests/integration/test_images/build ./tests/integration/ 58 | -------------------------------------------------------------------------------- /scripts/build/build_guest_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd ./linux-guest 3 | 4 | export ARCH=riscv 5 | export CROSS_COMPILE=riscv64-linux-gnu- 6 | 7 | make defconfig 8 | make -j$(nproc) 9 | 10 | popd 11 | -------------------------------------------------------------------------------- /scripts/build/build_host_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd linux-duvisor 3 | 4 | cp .config-qemu .config 5 | export ARCH=riscv 6 | export CROSS_COMPILE=riscv64-linux-gnu- 7 | 8 | make -j$(nproc) 9 | 10 | if [ $? -ne 0 ]; then 11 | exit -1 12 | fi 13 | 14 | popd 15 | -------------------------------------------------------------------------------- /scripts/build/build_opensbi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd opensbi-duvisor 3 | 4 | export CROSS_COMPILE=riscv64-linux-gnu- 5 | make PLATFORM=generic 6 | if [ $? -ne 0 ]; then 7 | exit -1 8 | fi 9 | 10 | popd 11 | -------------------------------------------------------------------------------- /scripts/build/build_qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd ./qemu-duvisor 3 | 4 | if [ ! -e "./build/Makefile" ]; then 5 | ./configure --target-list=riscv64-softmmu 6 | fi 7 | 8 | make -j $(nproc) 9 | 10 | popd 11 | -------------------------------------------------------------------------------- /scripts/build/build_rootfs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reference: https://github.com/IPADS-DuVisor/kvm-tutorial. 4 | 5 | export ARCH=riscv 6 | export CROSS_COMPILE=riscv64-linux-gnu- 7 | 8 | CI_HOSTNAME=ip-172-31-63-27 9 | 10 | if [ $(hostname)1 == ${CI_HOSTNAME}1 ]; then 11 | # for CI environment 12 | PREPARE="/home/ubuntu/prepare" 13 | else 14 | PREPARE="./prepare" 15 | fi 16 | 17 | echo prepare dirctory is ${PREPARE} 18 | # We would user this to build rootfs of both guest and host. 19 | ROOTFS_DIR=${PREPARE}/rootfs 20 | if [ ! -e $ROOTFS_DIR ]; then 21 | echo "Root fs not found for $ROOTFS_DIR !" 22 | exit 0 23 | fi 24 | pushd $ROOTFS_DIR 25 | cp -f ../howto/configs/busybox-1.33.1_defconfig .config 26 | make oldconfig 27 | make install -j$(nproc) 28 | chmod 777 _install 29 | mkdir -p _install/etc/init.d 30 | mkdir -p _install/dev 31 | mkdir -p _install/proc 32 | mkdir -p _install/sys 33 | mkdir -p _install/apps 34 | ln -sf /sbin/init _install/init 35 | cp -f ../howto/configs/busybox/fstab _install/etc/fstab 36 | cp -f ../howto/configs/busybox/rcS _install/etc/init.d/rcS 37 | cp -f ../howto/configs/busybox/motd _install/etc/motd 38 | pushd _install 39 | find ./ | cpio -o -H newc > ../../rootfs-guest.img 40 | popd 41 | popd 42 | -------------------------------------------------------------------------------- /scripts/build/copy_duvisor_to_vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CI_HOSTNAME=ip-172-31-63-27 4 | 5 | if [ $(hostname)1 == ${CI_HOSTNAME}1 ]; then 6 | # for CI environment 7 | PREPARE="/home/ubuntu/prepare" 8 | else 9 | PREPARE="./prepare" 10 | fi 11 | 12 | echo prepare dirctory is ${PREPARE} 13 | 14 | if [ ! -e $PREPARE ]; then 15 | echo "Prepare dirctory not exist!" 16 | exit -1 17 | fi 18 | 19 | first_arg=${1:-release} 20 | 21 | if test ${first_arg} = release; then 22 | build_path=release 23 | elif test ${first_arg} = debug; then 24 | build_path=debug 25 | else 26 | echo "Wrong arg." 27 | exit 28 | fi 29 | 30 | duvisor_name=./target/riscv64gc-unknown-linux-gnu/${build_path}/duvisor 31 | 32 | deps=`ls ./target/riscv64gc-unknown-linux-gnu/${build_path}/deps/*` 33 | 34 | # Delete duvisor main binary name, so that we get duvisor tests binary names 35 | for i in $deps; do 36 | [[ ! `diff $i $duvisor_name` ]] && sudo rm $i 37 | done 38 | 39 | duvisor_names=`find ./target/riscv64gc-unknown-linux-gnu/${build_path}/deps/ -type f ! -name '*.*' ` 40 | 41 | duvisor_test_names=${duvisor_names/$duvisor_name} 42 | mkdir -p mnt 43 | sudo mount $PREPARE/ubuntu-vdisk.img ./mnt 44 | sudo rm -r ./mnt/duvisor 45 | sudo mkdir -p ./mnt/duvisor/tests_bin 46 | sudo cp scripts/local/run_tests.sh $duvisor_name ./mnt/duvisor 47 | sudo cp $duvisor_test_names ./mnt/duvisor/tests_bin/ 48 | sudo cp -r src ./mnt/duvisor/ 49 | sudo cp -r tests ./mnt/duvisor/ 50 | sudo cp -r test-files-duvisor ./mnt/duvisor/test-files-duvisor 51 | 52 | sudo umount ./mnt 53 | -------------------------------------------------------------------------------- /scripts/build/docker_exec_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CI_HOSTNAME=ip-172-31-63-27 3 | IMAGE=duvisor/build-env:v2 4 | 5 | if [ $(hostname)1 == ${CI_HOSTNAME}1 ]; then 6 | # for CI environment 7 | IT="" 8 | EXTRA_V=" -v /home/ubuntu/prepare:/home/ubuntu/prepare " 9 | else 10 | IT=" -it " 11 | fi 12 | 13 | docker run ${IT} --rm \ 14 | -v $(pwd):/home/$(id -u -n)/duvisor \ 15 | -w /home/$(id -u -n)/duvisor \ 16 | -u root \ 17 | --network=host \ 18 | -e PATH="/root/.cargo/bin:${PATH}" \ 19 | ${EXTRA_V} \ 20 | ${IMAGE} $1 21 | -------------------------------------------------------------------------------- /scripts/build/example_copy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PREPARE="./prepare" 4 | 5 | echo prepare dirctory is ${PREPARE} 6 | 7 | if [ ! -e $PREPARE ]; then 8 | echo "Prepare dirctory not exist!" 9 | exit -1 10 | fi 11 | 12 | first_arg=${1:-release} 13 | 14 | if test ${first_arg} = release; then 15 | build_path=release 16 | elif test ${first_arg} = debug; then 17 | build_path=debug 18 | else 19 | echo "Wrong arg." 20 | exit 21 | fi 22 | 23 | duvisor_name=./target/riscv64gc-unknown-linux-gnu/${build_path}/duvisor 24 | 25 | deps=`ls ./target/riscv64gc-unknown-linux-gnu/${build_path}/deps/*` 26 | 27 | # Delete duvisor main binary name, so that we get duvisor tests binary names 28 | for i in $deps; do 29 | [[ ! `diff $i $duvisor_name` ]] && sudo rm $i 30 | done 31 | 32 | duvisor_names=`find ./target/riscv64gc-unknown-linux-gnu/${build_path}/deps/ -type f ! -name '*.*' ` 33 | 34 | duvisor_test_names=${duvisor_names/$duvisor_name} 35 | ROOTFS_DIR=$PREPARE/rootfs 36 | rm -rf $ROOTFS_DIR/_install/duvisor 37 | mkdir -p $ROOTFS_DIR/_install/duvisor 38 | cp $duvisor_name $ROOTFS_DIR/_install/duvisor/ 39 | cp $PREPARE/rootfs-guest.img $ROOTFS_DIR/_install/duvisor 40 | cp ./linux-guest/arch/riscv/boot/Image $ROOTFS_DIR/_install/duvisor 41 | 42 | echo '#!/bin/bash 43 | ./duvisor \ 44 | --smp 2 \ 45 | --initrd \ 46 | ./rootfs-guest.img \ 47 | --kernel ./Image \ 48 | --memory 512 \ 49 | --machine duvisor_virt \ 50 | --append "root=/dev/ram console=ttyS0 earlycon=sbi" 51 | ' > $ROOTFS_DIR/_install/duvisor/boot.sh 52 | 53 | chmod +x $ROOTFS_DIR/_install/duvisor/boot.sh 54 | 55 | pushd $ROOTFS_DIR/_install 56 | find ./ | cpio -o -H newc > ../../rootfs-host.img 57 | popd 58 | -------------------------------------------------------------------------------- /scripts/expect_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Execute expect 4 | echo $1 5 | $1 | tr "\r" "\n" 6 | 7 | # Check Return value 8 | if [ ${PIPESTATUS[0]} -eq 0 ]; then 9 | exit 0 10 | else 11 | exit -1 12 | fi 13 | -------------------------------------------------------------------------------- /scripts/local/boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ${USER}1 == ubuntu1 ]; then 4 | # for CI environment 5 | PREPARE=${PREPARE:-"${HOME}/prepare"} 6 | else 7 | PREPARE=${PREPARE:-"./prepare"} 8 | fi 9 | 10 | if ! [ -e /sys/class/net/br0 ]; then 11 | echo -n "Please create br0 first" 12 | exit 1 13 | fi 14 | 15 | if ! [ -e /sys/class/net/tap0 ]; then 16 | echo -n "Creating tap0" 17 | sudo ip tuntap add tap0 mode tap user $(whoami) 18 | sudo ip link set tap0 master br0 19 | sudo ip link set dev br0 up 20 | sudo ip link set dev tap0 up 21 | fi 22 | 23 | MACADDR=66:22:33:44:55:11 24 | ROMFILE=./qemu-duvisor/pc-bios/efi-virtio.rom 25 | #ROMFILE=./qemu-duvisor/pc-bios/efi-e1000e.rom 26 | 27 | ./qemu-duvisor/build/riscv64-softmmu/qemu-system-riscv64 \ 28 | -snapshot \ 29 | -nographic \ 30 | -cpu rv64,x-h=true,x-z=true \ 31 | -smp 8 \ 32 | -m 16G \ 33 | -machine virt \ 34 | -bios ./opensbi-duvisor/build/platform/generic/firmware/fw_jump.elf \ 35 | -kernel ./linux-duvisor/arch/riscv/boot/Image \ 36 | -initrd $PREPARE/rootfs.img \ 37 | -append "root=/dev/ram rw console=ttyS0 earlycon=sbi" \ 38 | -device virtio-blk-pci,drive=vdisk \ 39 | -drive if=none,id=vdisk,file=$PREPARE/ubuntu-vdisk.img,format=raw \ 40 | -device virtio-net-pci,netdev=vnet,mac=$MACADDR,romfile=$ROMFILE \ 41 | -netdev tap,id=vnet,ifname=tap0,script=no $@ 42 | #-device e1000e,netdev=vnet,mac=$MACADDR,romfile=$ROMFILE \ 43 | #-netdev user,id=vnet,hostfwd=tcp::5555-:22 44 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | source [file join [file dirname $argv0] ./duvisor_test_main.tcl] 3 | 4 | set timeout 180 5 | set env(TERM) xterm-256color 6 | set env(SHELL) /bin/bash 7 | set env(HOME) /home/gitlab-runner 8 | set env(PATH) /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games 9 | set env(LOGNAME) gitlab-runner 10 | set env(SHLVL) 2 11 | set env(MAIL) /var/mail/gitlab-runner 12 | set env(SUDO_UID) 1000 13 | set env(SUDO_GID) 1000 14 | set env(SUDO_COMMAND) /bin/bash 15 | 16 | # set args_for_duvisor_test [lindex $argv 0]; 17 | 18 | spawn bash -c "killall qemu-system-riscv64" 19 | 20 | spawn bash -c "screen -r virt" 21 | 22 | send "./scripts/local/boot.sh \n" 23 | 24 | expect { 25 | "Please press Enter to activate this console" { 26 | send "\n ./chroot.sh \n 27 | mount -t proc proc /proc \n 28 | mount -t sysfs sysfs /sys \n 29 | mount -t devtmpfs devtmpfs /dev \n" 30 | # echo 5 > /proc/sys/kernel/printk \n" 31 | exp_continue 32 | } 33 | 34 | "root@(none)" { 35 | send "cd duvisor && RUST_TEST_THREADS=1 ./run_tests.sh $argv\n" 36 | expect { 37 | "test failed" { 38 | exit -1 39 | } 40 | "ALL TEST PASSED" { 41 | 42 | } 43 | timeout { 44 | exit -1 45 | } 46 | } 47 | } 48 | timeout { 49 | send "\n" 50 | expect { 51 | "root@(none)" { 52 | send "cd duvisor && RUST_TEST_THREADS=1 ./run_tests.sh $argv\n" 53 | expect { 54 | "test failed" { 55 | exit -1 56 | } 57 | "ALL TEST PASSED" { 58 | 59 | } 60 | timeout { 61 | exit -1 62 | } 63 | } 64 | } 65 | timeout { 66 | exit -1 67 | } 68 | } 69 | } 70 | } 71 | 72 | main_test 73 | 74 | puts "Test OK" 75 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # trap ctrl-c and call ctrl_c() 4 | trap ctrl_c INT 5 | 6 | export PREPARE= 7 | 8 | function ctrl_c() { 9 | pkill screen 10 | } 11 | 12 | 13 | screen -S virt -d -m & 14 | VIRT_PID=$! 15 | echo "virt screen session pid is ${VIRT_PID}" 16 | screen -S host -d -m & 17 | HOST_PID=$! 18 | echo "host screen session pid is ${HOST_PID}" 19 | sleep 1 20 | ./scripts/expect_wrapper.sh ./scripts/local/duvisor_test.exp 21 | ret=$? 22 | pkill screen 23 | exit $ret 24 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test_main.tcl: -------------------------------------------------------------------------------- 1 | proc main_test { } { 2 | # Test the binary 3 | expect ":/duvisor#" 4 | 5 | send "./duvisor --smp 4 --block /blk-dev.img\n" 6 | expect { 7 | "please set memory size by using --memory or config files." { 8 | } 9 | timeout { 10 | exit -1 11 | } 12 | } 13 | 14 | send "./duvisor --memory 128 --block /blk-dev.img\n" 15 | expect { 16 | "please set vcpu count by using --smp or config files." { 17 | } 18 | timeout { 19 | exit -1 20 | } 21 | } 22 | 23 | send "./duvisor --smp 4 --memory 128 --block /blk-dev.img\n" 24 | expect { 25 | "please set kernel image by using --kernel or config files." { 26 | } 27 | timeout { 28 | exit -1 29 | } 30 | } 31 | 32 | set timeout 300 33 | 34 | send "./duvisor --smp 1 --initrd ./test-files-duvisor/rootfs-net.img --dtb ./test-files-duvisor/vmlinux.dtb --kernel ./test-files-duvisor/GuestLinuxImage.ok --memory 1024 --machine duvisor_virt --block /blk-dev.img --vmtap vmtap0 --append 'console=ttyS0 root=/dev/vda rw console=sbi earlycon=sbi'\n" 35 | expect { 36 | "Busybox Rootfs" { 37 | send "\n ls \n" 38 | expect { 39 | "guest-net.sh" {} 40 | } 41 | } 42 | 43 | timeout { 44 | exit -1 45 | } 46 | } 47 | 48 | send "/guest-net.sh \n ip a \n" 49 | expect { 50 | "eth0: " { 51 | } 52 | 53 | timeout { 54 | exit -1 55 | } 56 | } 57 | 58 | send "sync \n" 59 | expect { 60 | "#" { 61 | } 62 | 63 | timeout { 64 | exit -1 65 | } 66 | } 67 | 68 | send "poweroff -f \n" 69 | expect { 70 | "root@(none)" { 71 | } 72 | 73 | timeout { 74 | exit -1 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test_multi_vcpu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./scripts/local/multi_vcpu/duvisor_test_multi_vcpu_8.sh 4 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test_network.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | set timeout 180 3 | 4 | 5 | set timeout 300 6 | set env(TERM) xterm-256color 7 | set env(SHELL) /bin/bash 8 | set env(HOME) /home/gitlab-runner 9 | set env(PATH) /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games 10 | set env(LOGNAME) gitlab-runner 11 | set env(SHLVL) 2 12 | set env(MAIL) /var/mail/gitlab-runner 13 | set env(SUDO_UID) 1000 14 | set env(SUDO_GID) 1000 15 | set env(SUDO_COMMAND) /bin/bash 16 | 17 | 18 | spawn bash -c "screen -r virt" 19 | 20 | expect "DuVisor" 21 | 22 | send "./scripts/local/boot.sh | tee ~/log-duvisor/network/`date +%Y-%m-%d-%T`\n" 23 | 24 | expect { 25 | "root@(none)" { 26 | 27 | } 28 | timeout { 29 | send "sleep 10\n" 30 | send "\n" 31 | expect { 32 | "root@(none)" { 33 | 34 | } 35 | timeout { 36 | exit -1 37 | } 38 | } 39 | } 40 | } 41 | 42 | # Use blk-dev-vm-1.img to isolated from the other tests of single_vm_test 43 | send "cd duvisor && ./duvisor --smp 1 --initrd ./test-files-duvisor/rootfs-net.img --dtb ./test-files-duvisor/vmlinux.dtb --kernel ./test-files-duvisor/GuestLinuxImage.ok --memory 1024 --machine duvisor_virt --block /blk-dev-vm-1.img --vmtap vmtap0 --append 'console=ttyS0 root=/dev/vda rw console=sbi earlycon=sbi'\n" 44 | expect { 45 | "Busybox Rootfs" { 46 | send "\n ls \n" 47 | expect { 48 | "guest-net.sh" {} 49 | } 50 | } 51 | 52 | timeout { 53 | exit -1 54 | } 55 | } 56 | 57 | send "./guest-net.sh\n ip a \n" 58 | expect { 59 | "eth0: " { 60 | } 61 | 62 | timeout { 63 | exit -1 64 | } 65 | } 66 | 67 | set timeout 500 68 | 69 | expect "#" 70 | send "mount /dev/vda /root && chroot root bash\n" 71 | 72 | 73 | expect "#" 74 | send "\n\n./apache_server.sh \n" 75 | 76 | 77 | expect { 78 | "directive globally to suppress this message" { 79 | } 80 | 81 | timeout { 82 | exit -1 83 | } 84 | } 85 | 86 | sleep 2 87 | 88 | send "\x01"; send "d" 89 | 90 | expect "DuVisor" 91 | 92 | spawn bash -c "screen -r host" 93 | 94 | expect "DuVisor" 95 | 96 | send "ab -c 1 -n 1 http://192.168.27.201/ \n" 97 | 98 | expect "Transfer rate" 99 | 100 | send "\x01"; send "d" 101 | 102 | spawn bash -c "screen -r virt" 103 | 104 | expect "#" 105 | 106 | send "./mount_dev.sh && ./apache_kill.sh && ./memcached_server.sh \n" 107 | 108 | expect "#" 109 | 110 | sleep 3 111 | 112 | send "\x01"; send "d" 113 | 114 | expect "DuVisor" 115 | 116 | spawn bash -c "screen -r host" 117 | 118 | expect "DuVisor" 119 | 120 | send "memcslap --concurrency=1 --servers=192.168.27.201:11211 \n" 121 | 122 | expect { 123 | "Failure" { 124 | puts "Test Failed" 125 | exit -1 126 | } 127 | 128 | "seconds to load data" { 129 | 130 | } 131 | 132 | } 133 | 134 | puts "Test OK" 135 | 136 | send "\x01x" 137 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test_network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # trap ctrl-c and call ctrl_c() 4 | trap ctrl_c INT 5 | 6 | function ctrl_c() { 7 | pkill screen 8 | } 9 | 10 | 11 | screen -S virt -d -m & 12 | VIRT_PID=$! 13 | echo "virt screen session pid is ${VIRT_PID}" 14 | screen -S host -d -m & 15 | HOST_PID=$! 16 | echo "host screen session pid is ${HOST_PID}" 17 | sleep 1 18 | ./scripts/expect_wrapper.sh ./scripts/local/duvisor_test_network.exp 19 | ret=$? 20 | pkill screen 21 | exit $ret 22 | -------------------------------------------------------------------------------- /scripts/local/duvisor_test_standalone.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | 3 | set timeout 300 4 | spawn bash -c "killall qemu-system-riscv64" 5 | 6 | spawn bash -c "./scripts/local/boot.sh | tee ~/log-duvisor/standalone/`date +%Y-%m-%d-%T`" 7 | 8 | expect { 9 | "root@(none)" { 10 | 11 | } 12 | timeout { 13 | send "sleep 10\n" 14 | send "\n" 15 | expect { 16 | "root@(none)" { 17 | 18 | } 19 | timeout { 20 | exit -1 21 | } 22 | } 23 | } 24 | } 25 | 26 | send "pwd \n" 27 | send "ls \n" 28 | send "ls duvisor \n" 29 | send "file duvisor/duvisor \n" 30 | 31 | # Use blk-dev-vm-0.img to isolated from the other tests of single_vm_test 32 | send "cd duvisor && ./duvisor --smp 1 --initrd ./test-files-duvisor/rootfs-net.img --dtb ./test-files-duvisor/vmlinux.dtb --kernel ./test-files-duvisor/GuestLinuxImage.ok --memory 1024 --machine duvisor_virt --block /blk-dev-vm-0.img --vmtap vmtap0 --append 'console=ttyS0 root=/dev/vda rw console=sbi earlycon=sbi'\n" 33 | expect { 34 | "Busybox Rootfs" { 35 | send "\n ls \n" 36 | expect { 37 | "guest-net.sh" {} 38 | } 39 | } 40 | 41 | timeout { 42 | exit -1 43 | } 44 | } 45 | 46 | send "./guest-net.sh\n ip a \n" 47 | expect { 48 | "eth0: " { 49 | send "/ping-test.sh 10 \n" 50 | expect { 51 | "Ping test OK" {} 52 | 53 | timeout { 54 | exit -1 55 | } 56 | } 57 | } 58 | 59 | timeout { 60 | exit -1 61 | } 62 | } 63 | 64 | set timeout 1000 65 | 66 | send "mount /dev/vda /root && chroot root \n" 67 | expect "#" 68 | 69 | send "./untar.sh \n" 70 | expect { 71 | "pagefaults" { 72 | } 73 | 74 | timeout { 75 | exit -1 76 | } 77 | } 78 | 79 | send "hackbench \n" 80 | expect { 81 | "Time:" { 82 | } 83 | 84 | timeout { 85 | exit -1 86 | } 87 | 88 | } 89 | 90 | set timeout 1000 91 | 92 | send "./lmbench.sh \n" 93 | expect { 94 | "Simple syscall" { 95 | 96 | } 97 | 98 | timeout { 99 | exit -1 100 | } 101 | } 102 | 103 | puts "\nTest OK\n" 104 | 105 | send "\x01x" 106 | 107 | -------------------------------------------------------------------------------- /scripts/local/multi_vcpu/duvisor_test_main_multi_vcpu.tcl: -------------------------------------------------------------------------------- 1 | proc main_test_multi_vcpu_8 { } { 2 | #set timeout -1 3 | expect { 4 | "root@(none):/#" { 5 | } 6 | 7 | timeout { 8 | exit -1 9 | } 10 | } 11 | send "pwd \n" 12 | send "ls duvisor \n" 13 | send "file duvisor/duvisor \n" 14 | send "echo 4 4 1 4 > /proc/sys/kernel/printk \n" 15 | 16 | # Use blk-dev-vm-2.img to isolated from the other tests of single_vm_test 17 | send "cd duvisor && ./duvisor --smp 8 --initrd ./test-files-duvisor/rootfs-net.img --kernel ./test-files-duvisor/GuestLinuxImage.ok --memory 8192 --machine duvisor_virt --block /blk-dev-vm-2.img --vmtap vmtap0 --append 'console=ttyS0 root=/dev/vda rw console=sbi earlycon=sbi'\n" 18 | expect { 19 | "Busybox Rootfs" { 20 | send "\n" 21 | send "echo 4 4 1 4 > /proc/sys/kernel/printk \n" 22 | send "\n ls \n" 23 | expect { 24 | "guest-net.sh" {} 25 | } 26 | } 27 | 28 | timeout { 29 | exit -1 30 | } 31 | } 32 | 33 | send "/guest-net.sh \n ip a \n" 34 | expect { 35 | "eth0: " { 36 | } 37 | 38 | timeout { 39 | exit -1 40 | } 41 | } 42 | 43 | #sleep 2 44 | set timeout 1000 45 | send "mount /dev/vda /root && chroot root \n" 46 | send "echo abb | tr a-z A-Z\n" 47 | expect "ABB" 48 | send "/etc/init.d/ssh start\n" 49 | 50 | send "hackbench \n" 51 | expect { 52 | "Time:" { 53 | } 54 | 55 | timeout { 56 | puts "Timeout by hackbench" 57 | exit -1 58 | } 59 | } 60 | #set timeout -1 61 | send "ls \n\n\n" 62 | send "echo abc | tr a-z A-Z\n" 63 | expect { 64 | "ABC" { 65 | } 66 | 67 | timeout { 68 | puts "Timeout by ls" 69 | exit -1 70 | } 71 | } 72 | #sleep 1 73 | set timeout 600 74 | send "./lmbench.sh \n" 75 | expect { 76 | "Simple syscall" { 77 | } 78 | 79 | timeout { 80 | puts "Timeout by lmbench" 81 | exit -1 82 | } 83 | } 84 | #set timeout 20 85 | #send "ls -a /root \n" 86 | #expect { 87 | # "bashrc" { 88 | # } 89 | 90 | # timeout { 91 | # puts "ls timeout. " 92 | # exit -1 93 | # } 94 | #} 95 | set timeout 700 96 | 97 | send "strace sync \n" 98 | send "echo def | tr a-z A-Z\n" 99 | expect { 100 | "DEF" { 101 | } 102 | 103 | timeout { 104 | exit -1 105 | } 106 | } 107 | #send "sleep 10\n" 108 | #send "strace poweroff -f \n" 109 | #expect { 110 | # "root@(none)" { 111 | # } 112 | # 113 | # timeout { 114 | # set timeout 700 115 | # send "strace sleep 2\n" 116 | # send "\n" 117 | # expect { 118 | # "root@(none)" { 119 | # 120 | # } 121 | # timeout { 122 | # exit -1 123 | # } 124 | # } 125 | # } 126 | #} 127 | } 128 | -------------------------------------------------------------------------------- /scripts/local/multi_vcpu/duvisor_test_multi_vcpu_8.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | source [file join [file dirname $argv0] ./duvisor_test_main_multi_vcpu.tcl] 3 | 4 | set timeout 180 5 | set env(TERM) xterm-256color 6 | set env(SHELL) /bin/bash 7 | set env(HOME) /home/gitlab-runner 8 | set env(PATH) /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games 9 | set env(LOGNAME) gitlab-runner 10 | set env(SHLVL) 2 11 | set env(MAIL) /var/mail/gitlab-runner 12 | set env(SUDO_UID) 1000 13 | set env(SUDO_GID) 1000 14 | set env(SUDO_COMMAND) /bin/bash 15 | 16 | spawn bash -c "killall qemu-system-riscv64" 17 | 18 | spawn bash -c "screen -r virt" 19 | 20 | send "echo test >> test.log \n" 21 | 22 | send "./scripts/local/boot.sh | tee ~/log-duvisor/multi-vcpu/`date +%Y-%m-%d-%T` \n" 23 | send "\n" 24 | expect { 25 | "root@(none)" { 26 | 27 | } 28 | timeout { 29 | send "sleep 10\n" 30 | send "\n" 31 | expect { 32 | "root@(none)" { 33 | 34 | } 35 | timeout { 36 | exit -1 37 | } 38 | } 39 | } 40 | } 41 | send "\n" 42 | 43 | main_test_multi_vcpu_8 44 | 45 | puts "Test OK" 46 | #spawn bash -c "screen -r host" 47 | spawn bash -c "date >> test.log" 48 | -------------------------------------------------------------------------------- /scripts/local/multi_vcpu/duvisor_test_multi_vcpu_8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # trap ctrl-c and call ctrl_c() 4 | trap ctrl_c INT 5 | 6 | function ctrl_c() { 7 | pkill screen 8 | } 9 | 10 | screen -S virt -d -m & 11 | VIRT_PID=$! 12 | echo "virt screen session pid is ${VIRT_PID}" 13 | screen -S host -d -m & 14 | HOST_PID=$! 15 | echo "host screen session pid is ${HOST_PID}" 16 | sleep 1 17 | ./scripts/expect_wrapper.sh ./scripts/local/multi_vcpu/duvisor_test_multi_vcpu_8.exp 18 | ret=$? 19 | pkill screen 20 | exit $ret 21 | -------------------------------------------------------------------------------- /scripts/local/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ret=0 3 | for file in ./tests_bin/*; do 4 | echo $file $@ 5 | ./$file $@ 6 | if [ $? -ne 0 ]; 7 | then 8 | ret=-1 9 | break 10 | fi 11 | echo $ret 12 | done 13 | 14 | if [ "$ret" -ne 0 ]; 15 | then 16 | echo "test failed"; 17 | else 18 | echo "ALL TEST PASSED" 19 | fi 20 | -------------------------------------------------------------------------------- /scripts/opensource/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | ARG DEBIAN_FRONTEND=noninteractive 3 | ENV TZ=Asia/Shanghai 4 | 5 | # Build essentials 6 | RUN apt-get update && apt-get install -y \ 7 | ca-certificates build-essential \ 8 | curl git libssl-dev \ 9 | pkg-config python3 wget \ 10 | qemu-system-misc opensbi u-boot-qemu qemu-utils \ 11 | gcc-riscv64-linux-gnu 12 | 13 | # Install Rust 14 | RUN mkdir -p /tmp 15 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rust_installer.sh 16 | RUN chmod +x /tmp/rust_installer.sh 17 | RUN /tmp/rust_installer.sh -y 18 | 19 | # Install packages for compiling qemu and linux 20 | RUN apt-get update && apt-get install -y ninja-build libglib2.0-dev libpixman-1-dev flex bison bc 21 | 22 | # Install cmake for cargo build 23 | RUN apt-get update && apt-get install -y cmake 24 | 25 | # Install cpio for rootfs build 26 | RUN apt-get update && apt-get install -y cpio 27 | -------------------------------------------------------------------------------- /scripts/quick_docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_qemu.sh 4 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_host_linux.sh 5 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_guest_linux.sh 6 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_opensbi.sh 7 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_rootfs.sh 8 | ./scripts/build/docker_exec_wrapper.sh ./scripts/build/build_duvisor.sh 9 | ./scripts/build/example_copy.sh 10 | 11 | -------------------------------------------------------------------------------- /scripts/quick_native_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./scripts/build/build_qemu.sh 4 | ./scripts/build/build_host_linux.sh 5 | ./scripts/build/build_guest_linux.sh 6 | ./scripts/build/build_opensbi.sh 7 | ./scripts/build/build_rootfs.sh 8 | ./scripts/build/build_duvisor.sh 9 | ./scripts/build/example_copy.sh 10 | -------------------------------------------------------------------------------- /scripts/run/example_boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./qemu-duvisor/build/riscv64-softmmu/qemu-system-riscv64 \ 4 | -snapshot \ 5 | -nographic \ 6 | -cpu rv64,x-h=true,x-z=true \ 7 | -smp 4 \ 8 | -m 8G \ 9 | -machine virt \ 10 | -bios ./opensbi-duvisor/build/platform/generic/firmware/fw_jump.elf \ 11 | -kernel ./linux-duvisor/arch/riscv/boot/Image \ 12 | -initrd ./prepare/rootfs-host.img \ 13 | -append "root=/dev/ram0 rw console=ttyS0 earlycon=sbi" 14 | -------------------------------------------------------------------------------- /src/atomic_enum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "atomic_enum" 3 | version = "0.1.1" 4 | authors = ["Thomas Bächler "] 5 | edition = "2018" 6 | 7 | description = "An attribute to create an atomic wrapper around a C-style enum" 8 | repository = "https://github.com/brain0/atomic_enum" 9 | keywords = ["atomic", "enum"] 10 | categories = ["concurrency"] 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "passively-maintained" } 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | syn = { version = "1.0.16", features = ["full"] } 21 | quote = "1.0.3" 22 | proc-macro2 = "1.0.9" 23 | -------------------------------------------------------------------------------- /src/data_model/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_model" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /src/data_model/src/endian.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Explicit endian types useful for embedding in structs or reinterpreting data. 6 | //! 7 | //! Each endian type is guarnteed to have the same size and alignment as a regular unsigned primiive 8 | //! of the equal size. 9 | //! 10 | //! # Examples 11 | //! 12 | //! ``` 13 | //! # use data_model::*; 14 | //! let b: Be32 = From::from(3); 15 | //! let l: Le32 = From::from(3); 16 | //! 17 | //! assert_eq!(b.to_native(), 3); 18 | //! assert_eq!(l.to_native(), 3); 19 | //! assert!(b == 3); 20 | //! assert!(l == 3); 21 | //! 22 | //! let b_trans: u32 = unsafe { std::mem::transmute(b) }; 23 | //! let l_trans: u32 = unsafe { std::mem::transmute(l) }; 24 | //! 25 | //! #[cfg(target_endian = "little")] 26 | //! assert_eq!(l_trans, 3); 27 | //! #[cfg(target_endian = "big")] 28 | //! assert_eq!(b_trans, 3); 29 | //! 30 | //! assert_ne!(b_trans, l_trans); 31 | //! ``` 32 | 33 | use DataInit; 34 | 35 | macro_rules! endian_type { 36 | ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => ( 37 | /// An unsigned integer type of with an explicit endianness. 38 | /// 39 | /// See module level documentation for examples. 40 | #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] 41 | pub struct $new_type($old_type); 42 | 43 | impl $new_type { 44 | /// Converts `self` to the native endianness. 45 | pub fn to_native(self) -> $old_type { 46 | $old_type::$from_new(self.0) 47 | } 48 | } 49 | 50 | unsafe impl DataInit for $new_type {} 51 | 52 | impl PartialEq<$old_type> for $new_type { 53 | fn eq(&self, other: &$old_type) -> bool { 54 | self.0 == $old_type::$to_new(*other) 55 | } 56 | } 57 | 58 | impl PartialEq<$new_type> for $old_type { 59 | fn eq(&self, other: &$new_type) -> bool { 60 | $old_type::$to_new(other.0) == *self 61 | } 62 | } 63 | 64 | impl Into<$old_type> for $new_type { 65 | fn into(self) -> $old_type { 66 | $old_type::$from_new(self.0) 67 | } 68 | } 69 | 70 | impl From<$old_type> for $new_type { 71 | fn from(v: $old_type) -> $new_type { 72 | $new_type($old_type::$to_new(v)) 73 | } 74 | } 75 | ) 76 | } 77 | 78 | endian_type!(u16, Le16, to_le, from_le); 79 | endian_type!(u32, Le32, to_le, from_le); 80 | endian_type!(u64, Le64, to_le, from_le); 81 | endian_type!(usize, LeSize, to_le, from_le); 82 | endian_type!(u16, Be16, to_be, from_be); 83 | endian_type!(u32, Be32, to_be, from_be); 84 | endian_type!(u64, Be64, to_be, from_be); 85 | endian_type!(usize, BeSize, to_be, from_be); 86 | 87 | #[cfg(test)] 88 | mod tests { 89 | use super::*; 90 | 91 | use std::convert::From; 92 | use std::mem::{size_of, align_of, transmute}; 93 | 94 | #[cfg(target_endian = "little")] 95 | const NATIVE_LITTLE: bool = true; 96 | #[cfg(target_endian = "big")] 97 | const NATIVE_LITTLE: bool = false; 98 | const NATIVE_BIG: bool = !NATIVE_LITTLE; 99 | 100 | macro_rules! endian_test { 101 | ($old_type:ty, $new_type:ty, $test_name:ident, $native:expr) => ( 102 | mod $test_name { 103 | use super::*; 104 | 105 | #[test] 106 | fn align() { 107 | assert_eq!(align_of::<$new_type>(), align_of::<$old_type>()); 108 | } 109 | 110 | #[test] 111 | fn size() { 112 | assert_eq!(size_of::<$new_type>(), size_of::<$old_type>()); 113 | } 114 | 115 | #[allow(overflowing_literals)] 116 | #[test] 117 | fn equality() { 118 | let v = 0x0123456789ABCDEF as $old_type; 119 | let endian_v: $new_type = From::from(v); 120 | let endian_into: $old_type = endian_v.into(); 121 | let endian_transmute: $old_type = unsafe { transmute(endian_v) }; 122 | 123 | if $native { 124 | assert_eq!(endian_v, endian_transmute); 125 | } else { 126 | assert_eq!(endian_v, endian_transmute.swap_bytes()); 127 | } 128 | 129 | assert_eq!(v, endian_into); 130 | assert!(v == endian_v); 131 | assert!(endian_v == v); 132 | } 133 | } 134 | ) 135 | } 136 | 137 | endian_test!(u16, Le16, test_le16, NATIVE_LITTLE); 138 | endian_test!(u32, Le32, test_le32, NATIVE_LITTLE); 139 | endian_test!(u64, Le64, test_le64, NATIVE_LITTLE); 140 | endian_test!(usize, LeSize, test_le_size, NATIVE_LITTLE); 141 | endian_test!(u16, Be16, test_be16, NATIVE_BIG); 142 | endian_test!(u32, Be32, test_be32, NATIVE_BIG); 143 | endian_test!(u64, Be64, test_be64, NATIVE_BIG); 144 | endian_test!(usize, BeSize, test_be_size, NATIVE_BIG); 145 | } 146 | -------------------------------------------------------------------------------- /src/data_model/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /// Types for which it is safe to initialize from raw data. 6 | /// 7 | /// A type `T` is `DataInit` if and only if it can be initialized by reading its contents from a 8 | /// byte array. This is generally true for all plain-old-data structs. It is notably not true for 9 | /// any type that includes a reference. 10 | /// 11 | /// Implementing this trait guarantees that it is safe to instantiate the struct with random data. 12 | pub unsafe trait DataInit: Copy + Send + Sync {} 13 | 14 | // All intrinsic types and arays of intrinsic types are DataInit. They are just numbers. 15 | macro_rules! array_data_init { 16 | ($T:ty, $($N:expr)+) => { 17 | $( 18 | unsafe impl DataInit for [$T; $N] {} 19 | )+ 20 | } 21 | } 22 | macro_rules! data_init_type { 23 | ($T:ty) => { 24 | unsafe impl DataInit for $T {} 25 | array_data_init! { 26 | $T, 27 | 0 1 2 3 4 5 6 7 8 9 28 | 10 11 12 13 14 15 16 17 18 19 29 | 20 21 22 23 24 25 26 27 28 29 30 | 30 31 32 31 | } 32 | } 33 | } 34 | data_init_type!(u8); 35 | data_init_type!(u16); 36 | data_init_type!(u32); 37 | data_init_type!(u64); 38 | data_init_type!(usize); 39 | data_init_type!(i8); 40 | data_init_type!(i16); 41 | data_init_type!(i32); 42 | data_init_type!(i64); 43 | data_init_type!(isize); 44 | 45 | pub mod endian; 46 | pub use endian::*; 47 | 48 | pub mod volatile_memory; 49 | pub use volatile_memory::*; 50 | -------------------------------------------------------------------------------- /src/devices/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "devices" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | 6 | [dependencies] 7 | 8 | byteorder = "1.2.1" 9 | libc = "0.2.32" 10 | 11 | net_util = { path = "../net_util" } 12 | net_sys = { path = "../net_sys" } 13 | sys_util = { path = "../sys_util" } 14 | virtio_sys = { path = "../virtio_sys" } 15 | irq_util = { path = "../irq_util" } 16 | -------------------------------------------------------------------------------- /src/devices/src/i8042.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use sys_util::EventFd; 6 | 7 | use BusDevice; 8 | 9 | /// A i8042 PS/2 controller that emulates just enough to shutdown the machine. 10 | pub struct I8042Device { 11 | reset_evt: EventFd, 12 | } 13 | 14 | impl I8042Device { 15 | /// Constructs a i8042 device that will signal the given event when the guest requests it. 16 | pub fn new(reset_evt: EventFd) -> I8042Device { 17 | I8042Device { 18 | reset_evt: reset_evt, 19 | } 20 | } 21 | } 22 | 23 | impl BusDevice for I8042Device { 24 | fn read(&mut self, offset: u64, data: &mut [u8]) { 25 | if data.len() == 1 && offset == 0 { 26 | data[0] = 0x0; 27 | } 28 | } 29 | 30 | fn write(&mut self, offset: u64, data: &[u8]) { 31 | if data.len() == 1 && data[0] == 0xfe && offset == 0 { 32 | if let Err(e) = self.reset_evt.write(1) { 33 | error!("failed to trigger i8042 reset event: {:?}", e); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/devices/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Emulates virtual and hardware devices. 6 | 7 | extern crate byteorder; 8 | extern crate libc; 9 | 10 | extern crate net_sys; 11 | extern crate net_util; 12 | #[macro_use] 13 | extern crate sys_util; 14 | extern crate virtio_sys; 15 | extern crate irq_util; 16 | 17 | mod bus; 18 | mod i8042; 19 | mod serial; 20 | 21 | pub mod virtio; 22 | 23 | pub use self::bus::{Bus, BusDevice}; 24 | pub use self::i8042::I8042Device; 25 | pub use self::serial::Serial; 26 | -------------------------------------------------------------------------------- /src/devices/src/virtio/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Implements virtio devices, queues, and transport mechanisms. 6 | 7 | mod block; 8 | mod mmio; 9 | mod net; 10 | mod queue; 11 | 12 | pub use self::block::*; 13 | pub use self::mmio::*; 14 | pub use self::net::*; 15 | pub use self::queue::*; 16 | 17 | const DEVICE_ACKNOWLEDGE: u32 = 0x01; 18 | const DEVICE_DRIVER: u32 = 0x02; 19 | const DEVICE_DRIVER_OK: u32 = 0x04; 20 | const DEVICE_FEATURES_OK: u32 = 0x08; 21 | const DEVICE_FAILED: u32 = 0x80; 22 | 23 | // Types taken from linux/virtio_ids.h 24 | const TYPE_NET: u32 = 1; 25 | const TYPE_BLOCK: u32 = 2; 26 | 27 | const INTERRUPT_STATUS_USED_RING: u32 = 0x1; 28 | 29 | /// Offset from the base MMIO address of a virtio device used by the guest to notify the device of 30 | /// queue events. 31 | pub const NOTIFY_REG_OFFSET: u32 = 0x50; 32 | -------------------------------------------------------------------------------- /src/duvisor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "duvisor" 3 | version = "0.1.0" 4 | authors = ["Zeyu Mi "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = {version = "2.33.3", features = ["yaml"]} 11 | colored = "2" 12 | libc = ">=0.2.39" 13 | rusty-fork = "0.3.0" 14 | elf = "0.0.10" 15 | dtb = "0.2.0" 16 | once_cell = "1.8.0" 17 | epoll = "2.1.0" 18 | scopeguard = "0.3.3" 19 | 20 | byteorder = "1.2.1" 21 | devices = { path = "../devices" } 22 | sys_util = { path = "../sys_util" } 23 | irq_util = { path = "../irq_util" } 24 | atomic_enum = { path = "../atomic_enum" } 25 | vm-fdt = "0.2.0" 26 | fdt-rs = "0.4" 27 | 28 | [build-dependencies] 29 | gcc = "0.3" 30 | cc = "1.0" 31 | 32 | [features] 33 | qemu = [] 34 | xilinx = [] 35 | -------------------------------------------------------------------------------- /src/duvisor/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | #[path = "preparefile.rs"] 15 | mod preparefile; 16 | 17 | use preparefile::*; 18 | 19 | extern crate cc; 20 | 21 | fn cc_build_filename(filename: &str) { 22 | let mut path: String = "../../tests/integration/test_images/".to_owned(); 23 | path.push_str(filename); 24 | path.push_str(".S"); 25 | cc::Build::new() 26 | .file(path) 27 | .define("__FILENAME__", Some(filename)) 28 | .compile(filename); 29 | } 30 | 31 | fn main() { 32 | /* Prepare guestentry/asm_offset.h */ 33 | prepare_asm_offset_header(); 34 | 35 | cc::Build::new() 36 | .file("src/guestentry/enter_guest.S") 37 | .compile("enter_guest"); 38 | 39 | cc::Build::new() 40 | .file("src/plat/opensbi/uart.c") 41 | .compile("uart"); 42 | 43 | cc::Build::new().file("src/irq/vtimer.S").compile("vtimer"); 44 | 45 | let filenames = [ 46 | "vcpu_add_all_gprs", 47 | "vcpu_ecall_exit", 48 | "vmem_ld_mapping", 49 | "vmem_ld_sd_over_loop", 50 | "vmem_W_Ro", 51 | "vmem_X_nonX", 52 | "vmem_ld_sd_sum", 53 | "vmem_ld_data", 54 | ]; 55 | for i in 0..filenames.len() { 56 | cc_build_filename(filenames[i]); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/duvisor/src/clap_config.yml: -------------------------------------------------------------------------------- 1 | name: DuVisor 2 | version: "1.0" 3 | about: A user-level hypervisor written in Rust for RISC-V 4 | args: 5 | - vm_config: 6 | long: config 7 | value_name: CONFIG 8 | help: Sets the path of a configuration file for this virtual machine, its contents will overwrite all other arguments 9 | - machine: 10 | long: machine 11 | value_name: MACHINE 12 | help: Sets the machine type of this virtual machine 13 | possible_values: [ duvisor_virt, test_type] 14 | conflicts_with: 15 | - vm_config 16 | - smp: 17 | long: smp 18 | value_name: SMP 19 | help: Sets the number of virtual CPUs 20 | conflicts_with: 21 | - vm_config 22 | - vmtap: 23 | long: vmtap 24 | value_name: VMTAP 25 | help: Sets the name of the vmtap device 26 | conflicts_with: 27 | - vm_config 28 | - block: 29 | long: block 30 | value_name: BLOCK 31 | help: Sets the path of the block device 32 | conflicts_with: 33 | - vm_config 34 | - console: 35 | long: console 36 | value_name: console 37 | help: Sets the console for virtual mcahine 38 | conflicts_with: 39 | - vm_config 40 | - memory: 41 | long: memory 42 | value_name: MEMORY 43 | help: Sets the memory size of this virtual machine (in MegaBytes) 44 | conflicts_with: 45 | - vm_config 46 | - kernel: 47 | long: kernel 48 | value_name: KERNEL_IMAGE_PATH 49 | help: Sets the path of a kernel image file 50 | conflicts_with: 51 | - vm_config 52 | - dtb: 53 | long: dtb 54 | value_name: DTB 55 | help: Sets the path of a dtb file 56 | conflicts_with: 57 | - vm_config 58 | - initrd: 59 | long: initrd 60 | value_name: INITRD 61 | help: Sets the path of an initrd file 62 | conflicts_with: 63 | - vm_config 64 | - append: 65 | long: append 66 | value_name: APPEND 67 | help: Sets the path of an initrd file 68 | conflicts_with: 69 | - vm_config 70 | -------------------------------------------------------------------------------- /src/duvisor/src/devices/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod plic; 15 | pub mod vplic; 16 | -------------------------------------------------------------------------------- /src/duvisor/src/devices/vplic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | use libc; 15 | use std::ffi::CString; 16 | 17 | pub const VPLIC_LENGTH: usize = 0x4000000; 18 | pub const VMODE_VPLIC_OFFSET: u64 = 0x1f00000; 19 | pub const VIRT_IRQ_OFFSET: u32 = 0x80; 20 | pub const NULLPTR: *mut libc::c_void = 0 as *mut libc::c_void; 21 | 22 | pub struct VPlic { 23 | pending_vector: u64, 24 | } 25 | 26 | impl VPlic { 27 | pub fn new() -> Self { 28 | let pending_vector = VPlic::acquire_vplic(); 29 | let ptr = pending_vector as *mut u32; 30 | unsafe { 31 | *ptr = 0; 32 | } 33 | Self { 34 | pending_vector: pending_vector as u64, 35 | } 36 | } 37 | 38 | pub fn acquire_vplic() -> *mut u32 { 39 | let file_path = CString::new("/dev/vplic_dev").unwrap(); 40 | let vplic_fd; 41 | unsafe { 42 | vplic_fd = (libc::open(file_path.as_ptr(), libc::O_RDWR)) as i32; 43 | let vplic_base_addr = libc::mmap( 44 | NULLPTR, 45 | VPLIC_LENGTH, 46 | libc::PROT_READ | libc::PROT_WRITE, 47 | libc::MAP_SHARED, 48 | vplic_fd, 49 | 0, 50 | ); 51 | assert_ne!(vplic_base_addr, libc::MAP_FAILED); 52 | let vmode_vplic_addr = 53 | (vplic_base_addr as u64 + VMODE_VPLIC_OFFSET) as *mut libc::c_void; 54 | let vplic_ptr = vmode_vplic_addr as *mut u32; 55 | return vplic_ptr; 56 | } 57 | } 58 | 59 | /* check irq is sent to virtual device. */ 60 | pub fn check_virt_irq(irq: u32) -> bool { 61 | irq >= VIRT_IRQ_OFFSET 62 | } 63 | 64 | pub fn send_posted_interrupt(&self, irq: u32) { 65 | if VPlic::check_virt_irq(irq) == false { 66 | println!("send_posted_interrupt ERROR. irq: 0x{:x}", irq); 67 | return; 68 | } 69 | /* offset will be minused in QEMU as long as DuVisor access plic with VMODE_VPLIC_OFFSET */ 70 | /* more detail explaination can be found in code of QEMU:hw/intc/sifive_plic.c:sifive_plic_write */ 71 | /* I know such kind of design can be ugly and confusing, but it's not convenient to 72 | * change it since previous developer has already designed it */ 73 | let real_irq = irq - VIRT_IRQ_OFFSET; 74 | let ptr = self.pending_vector as *mut u32; 75 | unsafe { 76 | *ptr = 1 << real_irq; 77 | } 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::*; 84 | use rusty_fork::rusty_fork_test; 85 | 86 | rusty_fork_test! { 87 | #[test] 88 | fn test_access_vplic() { 89 | unsafe { 90 | let vplic_ptr = VPlic::acquire_vplic(); 91 | let val = *vplic_ptr; 92 | println!("[debug-DuVisor] vplic value:0x{:x}", val); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/duvisor/src/guestentry/csr.h: -------------------------------------------------------------------------------- 1 | #define CSR_HUSTATUS 0x800 2 | #define CSR_HUEDELEG 0x802 3 | #define CSR_HUIDELEG 0x803 4 | #define CSR_HUIE 0x804 5 | #define CSR_HUCOUNTEREN 0x806 6 | #define CSR_HUTVAL 0x843 7 | #define CSR_HUVIP 0x845 8 | #define CSR_HUIP 0x844 9 | #define CSR_HUTINST 0x84A 10 | #define CSR_HUGATP 0x880 11 | #define CSR_HUTIMEDELTA 0x805 12 | #define CSR_HUTIMEDELTAH 0x815 13 | 14 | #define CSR_UTVEC 0x5 15 | #define CSR_USCRATCH 0x40 16 | #define CSR_UEPC 0x41 17 | #define CSR_UCAUSE 0x42 18 | #define CSR_UTVAL 0x43 19 | 20 | #define CSR_HUVSSTATUS 0x400 21 | #define CSR_HUVSIE 0x404 22 | #define CSR_HUVSTVEC 0x405 23 | #define CSR_HUVSSCRATCH 0x440 24 | #define CSR_HUVSEPC 0x441 25 | #define CSR_HUVSCAUSE 0x480 26 | #define CSR_HUVSTVAL 0x443 27 | #define CSR_HUVSIP 0x444 28 | #define CSR_HUVSATP 0x480 29 | 30 | #define INS_HUFENCE_GVMA 0xE2000073 -------------------------------------------------------------------------------- /src/duvisor/src/guestentry/enter_guest.S: -------------------------------------------------------------------------------- 1 | #include "csr.h" 2 | #include "../vcpu/asm_offset.h" 3 | #include "save_restore.S" 4 | 5 | .align 2 6 | 7 | .global enter_guest 8 | 9 | enter_guest: 10 | /* a0 point to vcpu_ctx */ 11 | 12 | /* save host gp with a0=ctx, except t0-t6 and zero-x0 */ 13 | SAVE_HOST_CTX a0 14 | 15 | /* save a0-vcpu-ctx in CSR_USCRATCH & save USCRATCH */ 16 | csrrw t3, CSR_USCRATCH, a0 17 | sd t3, HOST_HYP_USCRATCH(a0) 18 | 19 | ld t0, HOST_HYP_HUSTATUS(a0) 20 | csrw CSR_HUSTATUS, t0 21 | 22 | ld t0, HOST_HYP_HUCOUNTEREN(a0) 23 | csrw CSR_HUCOUNTEREN, t0 24 | 25 | ld t0, HOST_HYP_HUTVAL(a0) 26 | csrw CSR_HUTVAL, t0 27 | 28 | ld t0, HOST_HYP_HUCOUNTEREN(a0) 29 | csrw CSR_HUCOUNTEREN, t0 30 | 31 | /* Restore UEPC & UCAUSE & UTVAL for trap handler */ 32 | ld t0, HOST_HYP_UEPC(a0) 33 | csrw CSR_UEPC, t0 34 | 35 | ld t0, HOST_HYP_UCAUSE(a0) 36 | csrw CSR_UCAUSE, t0 37 | 38 | ld t0, HOST_HYP_UTVAL(a0) 39 | csrw CSR_UTVAL, t0 40 | 41 | /* restore guest GP except A0 & X0 */ 42 | RESTORE_GUEST_CTX a0 43 | 44 | /* restore guest A0 */ 45 | ld x10, GUEST_GP_X10(a0) 46 | 47 | /* huret */ 48 | uret 49 | 50 | .align 2 51 | .global exit_guest 52 | exit_guest: 53 | /* save guest-a0 in sscratch & get host-a0 */ 54 | csrrw a0, CSR_USCRATCH, a0 55 | 56 | /* save guest gp except A0 & X0 */ 57 | SAVE_GUEST_CTX a0 58 | 59 | /* save guest A0 with USCRATCH */ 60 | csrr t1, CSR_USCRATCH 61 | sd t1, GUEST_GP_X10(a0) 62 | 63 | csrr t0, CSR_HUSTATUS 64 | sd t0, HOST_HYP_HUSTATUS(a0) 65 | 66 | csrr t0, CSR_HUCOUNTEREN 67 | sd t0, HOST_HYP_HUCOUNTEREN(a0) 68 | 69 | csrr t0, CSR_HUTVAL 70 | sd t0, HOST_HYP_HUTVAL(a0) 71 | 72 | csrr t0, CSR_HUCOUNTEREN 73 | sd t0, HOST_HYP_HUCOUNTEREN(a0) 74 | 75 | /* Save UEPC & UCAUSE & UTVAL for trap handler */ 76 | csrr t0, CSR_UEPC 77 | sd t0, HOST_HYP_UEPC(a0) 78 | 79 | csrr t0, CSR_UCAUSE 80 | sd t0, HOST_HYP_UCAUSE(a0) 81 | 82 | csrr t0, CSR_UTVAL 83 | sd t0, HOST_HYP_UTVAL(a0) 84 | 85 | /* restore host gp with a0=ctx, except t0-t6 and zero-x0 */ 86 | RESTORE_HOST_CTX a0 87 | 88 | ret 89 | 90 | /* 91 | * Instruction encoding of hufence.gvma is: 92 | * HUFENCE.GVMA rs1, rs2 93 | * HUFENCE.GVMA zero, rs2 94 | * HUFENCE.GVMA rs1 95 | * HUFENCE.GVMA 96 | * 97 | * rs1!=zero and rs2!=zero ==> HUFENCE.GVMA rs1, rs2 98 | * rs1==zero and rs2!=zero ==> HUFENCE.GVMA zero, rs2 99 | * rs1!=zero and rs2==zero ==> HUFENCE.GVMA rs1 100 | * rs1==zero and rs2==zero ==> HUFENCE.GVMA 101 | * 102 | * Instruction encoding of HUFENCE.GVMA is: 103 | * 1110001 rs2(5) rs1(5) 000 00000 1110011 104 | */ 105 | .global hufence_gvma_vmid_gpa 106 | hufence_gvma_vmid_gpa: 107 | /* 108 | * rs1 = a0 (GPA) 109 | * rs2 = a1 (VMID) 110 | * HUFENCE.GVMA a0, a1 111 | * 1110001 01011 01010 000 00000 1110011 112 | */ 113 | .word 0xe2b50073 114 | ret 115 | 116 | .global hufence_gvma_vmid 117 | hufence_gvma_vmid: 118 | /* 119 | * rs1 = zero 120 | * rs2 = a0 (VMID) 121 | * HUFENCE.GVMA zero, a0 122 | * 1110001 01010 00000 000 00000 1110011 123 | */ 124 | .word 0xe2a00073 125 | ret 126 | 127 | .global hufence_gvma_gpa 128 | hufence_gvma_gpa: 129 | /* 130 | * rs1 = a0 (GPA) 131 | * rs2 = zero 132 | * HUFENCE.GVMA a0 133 | * 1110001 00000 01010 000 00000 1110011 134 | */ 135 | .word 0xe2050073 136 | ret 137 | 138 | .global hufence_gvma_all 139 | hufence_gvma_all: 140 | /* 141 | * rs1 = zero 142 | * rs2 = zero 143 | * HUFENCE.GVMA 144 | * 1110001 00000 00000 000 00000 1110011 145 | */ 146 | .word 0xe2000073 147 | ret -------------------------------------------------------------------------------- /src/duvisor/src/guestentry/guest_entry.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int enter_guest(struct VcpuCtx *ctx); 4 | -------------------------------------------------------------------------------- /src/duvisor/src/guestentry/save_restore.S: -------------------------------------------------------------------------------- 1 | .macro SAVE_HOST_CTX reg1 2 | addi \reg1, \reg1, HOST_GP 3 | SAVE_GP \reg1 4 | addi \reg1, \reg1, -HOST_GP 5 | .endm 6 | 7 | .macro RESTORE_HOST_CTX reg1 8 | addi \reg1, \reg1, HOST_GP 9 | RESTORE_GP \reg1 10 | addi \reg1, \reg1, -HOST_GP 11 | .endm 12 | 13 | .macro SAVE_GUEST_CTX reg1 14 | addi \reg1, \reg1, GUEST_GP 15 | SAVE_GP \reg1 16 | addi \reg1, \reg1, -GUEST_GP 17 | .endm 18 | 19 | .macro RESTORE_GUEST_CTX reg1 20 | addi \reg1, \reg1, GUEST_GP 21 | RESTORE_GP \reg1 22 | addi \reg1, \reg1, -GUEST_GP 23 | .endm 24 | 25 | .macro SAVE_GP reg1 26 | sd x1, GP_X1(\reg1) 27 | sd x2, GP_X2(\reg1) 28 | sd x3, GP_X3(\reg1) 29 | sd x4, GP_X4(\reg1) 30 | sd x5, GP_X5(\reg1) 31 | sd x6, GP_X6(\reg1) 32 | sd x7, GP_X7(\reg1) 33 | sd x8, GP_X8(\reg1) 34 | sd x9, GP_X9(\reg1) 35 | sd x10, GP_X10(\reg1) 36 | sd x11, GP_X11(\reg1) 37 | sd x12, GP_X12(\reg1) 38 | sd x13, GP_X13(\reg1) 39 | sd x14, GP_X14(\reg1) 40 | sd x15, GP_X15(\reg1) 41 | sd x16, GP_X16(\reg1) 42 | sd x17, GP_X17(\reg1) 43 | sd x18, GP_X18(\reg1) 44 | sd x19, GP_X19(\reg1) 45 | sd x20, GP_X20(\reg1) 46 | sd x21, GP_X21(\reg1) 47 | sd x22, GP_X22(\reg1) 48 | sd x23, GP_X23(\reg1) 49 | sd x24, GP_X24(\reg1) 50 | sd x25, GP_X25(\reg1) 51 | sd x26, GP_X26(\reg1) 52 | sd x27, GP_X27(\reg1) 53 | sd x28, GP_X28(\reg1) 54 | sd x29, GP_X29(\reg1) 55 | sd x30, GP_X30(\reg1) 56 | sd x31, GP_X31(\reg1) 57 | .endm 58 | 59 | .macro RESTORE_GP reg1 60 | ld x1, GP_X1(\reg1) 61 | ld x2, GP_X2(\reg1) 62 | ld x3, GP_X3(\reg1) 63 | ld x4, GP_X4(\reg1) 64 | ld x5, GP_X5(\reg1) 65 | ld x6, GP_X6(\reg1) 66 | ld x7, GP_X7(\reg1) 67 | ld x8, GP_X8(\reg1) 68 | ld x9, GP_X9(\reg1) 69 | ld x11, GP_X11(\reg1) 70 | ld x12, GP_X12(\reg1) 71 | ld x13, GP_X13(\reg1) 72 | ld x14, GP_X14(\reg1) 73 | ld x15, GP_X15(\reg1) 74 | ld x16, GP_X16(\reg1) 75 | ld x17, GP_X17(\reg1) 76 | ld x18, GP_X18(\reg1) 77 | ld x19, GP_X19(\reg1) 78 | ld x20, GP_X20(\reg1) 79 | ld x21, GP_X21(\reg1) 80 | ld x22, GP_X22(\reg1) 81 | ld x23, GP_X23(\reg1) 82 | ld x24, GP_X24(\reg1) 83 | ld x25, GP_X25(\reg1) 84 | ld x26, GP_X26(\reg1) 85 | ld x27, GP_X27(\reg1) 86 | ld x28, GP_X28(\reg1) 87 | ld x29, GP_X29(\reg1) 88 | ld x30, GP_X30(\reg1) 89 | ld x31, GP_X31(\reg1) 90 | .endm 91 | -------------------------------------------------------------------------------- /src/duvisor/src/init/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod cmdline; 15 | -------------------------------------------------------------------------------- /src/duvisor/src/irq/delegation.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | #[allow(unused)] 15 | pub mod delegation_constants { 16 | /* Exception delegation */ 17 | pub const EXC_VIRTUAL_SUPERVISOR_SYSCALL: u64 = 10; 18 | pub const EXC_INST_GUEST_PAGE_FAULT: u64 = 20; 19 | pub const EXC_LOAD_GUEST_PAGE_FAULT: u64 = 21; 20 | pub const EXC_VIRTUAL_INST_FAULT: u64 = 22; 21 | pub const EXC_STORE_GUEST_PAGE_FAULT: u64 = 23; 22 | pub const EXC_IRQ_MASK: u64 = 1 << 63; 23 | 24 | /* Interrupt delegation */ 25 | /* TODO: A general define for both FPGA and qemu */ 26 | pub const IRQ_U_SOFT: u64 = 0; 27 | pub const IRQ_VS_SOFT: u64 = 2; 28 | pub const IRQ_VS_TIMER: u64 = 6; 29 | pub const IRQ_VS_EXT: u64 = 10; 30 | pub const IRQ_U_TIMER: u64 = 4; 31 | } 32 | -------------------------------------------------------------------------------- /src/duvisor/src/irq/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod delegation; 15 | pub mod vipi; 16 | pub mod virq; 17 | -------------------------------------------------------------------------------- /src/duvisor/src/irq/virq.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | use crate::csrc; 15 | use crate::csrs; 16 | use crate::vcpu::utils::*; 17 | use core::arch::asm; 18 | use std::sync::atomic::{AtomicU16, Ordering}; 19 | 20 | #[allow(unused)] 21 | pub struct VirtualInterrupt { 22 | /* 23 | * UVTIMER (#16) is not controlled by vcpu.virq field 24 | * FIXME: use a bit array 25 | */ 26 | irq_pending: AtomicU16, 27 | } 28 | 29 | impl VirtualInterrupt { 30 | pub fn new() -> Self { 31 | VirtualInterrupt { 32 | irq_pending: AtomicU16::new(0), 33 | } 34 | } 35 | 36 | pub fn set_pending_irq(&self, irq: u64) { 37 | if irq >= 16 { 38 | panic!("set_pending_irq: irq {} out of range", irq); 39 | } 40 | self.irq_pending.fetch_or(1 << irq, Ordering::SeqCst); 41 | } 42 | 43 | pub fn unset_pending_irq(&self, irq: u64) { 44 | if irq >= 16 { 45 | panic!("set_pending_irq: irq {} out of range", irq); 46 | } 47 | self.irq_pending.fetch_and(!(1 << irq), Ordering::SeqCst); 48 | } 49 | 50 | pub fn flush_pending_irq(&self) { 51 | /* Leave IRQ_U_SOFT for hardware UIPI */ 52 | let pending = self.irq_pending.load(Ordering::SeqCst); 53 | for i in 1..9 { 54 | if (pending & (1 << i)) != 0 { 55 | unsafe { 56 | csrs!(HUVIP, 1 << i); 57 | } 58 | } else { 59 | unsafe { 60 | csrc!(HUVIP, 1 << i); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/duvisor/src/irq/vtimer.S: -------------------------------------------------------------------------------- 1 | .global rdvtimecmp 2 | rdvtimecmp: 3 | .word 0xe0102577 4 | ret 5 | 6 | .global wrvtimecmp 7 | wrvtimecmp: 8 | .word 0xe0a01077 9 | ret 10 | 11 | .global rdvtimectl 12 | rdvtimectl: 13 | .word 0xf0202577 14 | ret 15 | 16 | .global wrvtimectl 17 | wrvtimectl: 18 | .word 0xf0a01077 19 | ret -------------------------------------------------------------------------------- /src/duvisor/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | #[allow(unused_imports)] 15 | #[macro_use] 16 | extern crate clap; 17 | 18 | pub mod devices; 19 | pub mod irq; 20 | pub mod mm; 21 | pub mod plat; 22 | pub mod test; 23 | pub mod vcpu; 24 | pub mod vm; 25 | use vm::virtualmachine::VirtualMachine; 26 | 27 | pub mod init; 28 | 29 | use init::cmdline; 30 | 31 | pub fn run(config: cmdline::VMConfig) { 32 | let mut vm = VirtualMachine::new(config); 33 | let ret = vm.vm_init(); 34 | 35 | if ret.len() == 0 { 36 | /* No kernel data has been loaded */ 37 | panic!("VM init failed"); 38 | } 39 | 40 | vm.vm_run(); 41 | 42 | vm.vm_destroy(); 43 | 44 | println!("Finish vm running..."); 45 | } 46 | -------------------------------------------------------------------------------- /src/duvisor/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | use colored::*; 15 | use duvisor::init::cmdline; 16 | use std::process; 17 | 18 | fn main() { 19 | let vm_config = cmdline::VMConfig::new().unwrap_or_else(|err| { 20 | eprintln!("{}: {}", "error".bright_red(), err); 21 | process::exit(1); 22 | }); 23 | 24 | if !cmdline::VMConfig::verify_args(&vm_config) { 25 | process::exit(1); 26 | } 27 | 28 | let hello_str = r#" 29 | 30 | ,------. ,--. ,--.,--. 31 | | .-. \ ,--.,--.\ `.' / `--' ,---. ,---. ,--.--. 32 | | | \ :| || | \ / ,--.( .-' | .-. || .--' 33 | | '--' /' '' ' \ / | |.-' `)' '-' '| | 34 | `-------' `----' `-' `--'`----' `---' `--' 35 | 36 | "#; 37 | println!("{}", hello_str); 38 | 39 | duvisor::run(vm_config); 40 | } 41 | -------------------------------------------------------------------------------- /src/duvisor/src/mm/gparegion.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub struct GpaRegion { 15 | gpa: u64, 16 | length: u64, 17 | } 18 | 19 | impl GpaRegion { 20 | pub fn new(gpa: u64, length: u64) -> Self { 21 | Self { gpa, length } 22 | } 23 | 24 | pub fn get_gpa(&self) -> u64 { 25 | self.gpa 26 | } 27 | 28 | pub fn get_length(&self) -> u64 { 29 | self.length 30 | } 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use super::*; 36 | 37 | #[test] 38 | fn test_gpa_region_new() { 39 | let gpa = 0x8000; 40 | let length = 0x2000; 41 | let gpa_region = GpaRegion::new(gpa, length); 42 | 43 | assert_eq!(gpa_region.gpa, gpa); 44 | assert_eq!(gpa_region.length, length); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/duvisor/src/mm/mmio.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | use crate::mm::gparegion; 15 | 16 | pub struct MmioManager { 17 | gpa_regions: Vec, 18 | } 19 | 20 | impl MmioManager { 21 | pub fn new(mmio_regions: Vec) -> Self { 22 | let mut gpa_regions: Vec = Vec::new(); 23 | 24 | for i in mmio_regions { 25 | gpa_regions.push(i); 26 | } 27 | 28 | Self { gpa_regions } 29 | } 30 | 31 | pub fn get_gpa_regions(&self) -> &Vec { 32 | &self.gpa_regions 33 | } 34 | 35 | pub fn mmio_add(&mut self, gpa: u64, length: u64) { 36 | let gpa_region = gparegion::GpaRegion::new(gpa, length); 37 | 38 | self.gpa_regions.push(gpa_region); 39 | } 40 | 41 | /* TODO: check mmio region list and reorder them */ 42 | pub fn check_valid(&self) -> bool { 43 | return true; 44 | } 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use super::*; 50 | 51 | /* Check new() of GpaBlock */ 52 | #[test] 53 | fn test_mmio_add() { 54 | let mut gpa: u64 = 0; 55 | let mut length: u64 = 0; 56 | let gpa_ans = 0x4000; 57 | let length_ans = 0x1000; 58 | let mmio_regions: Vec = Vec::new(); 59 | let mut mmio_manager = MmioManager::new(mmio_regions); 60 | 61 | mmio_manager.mmio_add(gpa_ans, length_ans); 62 | 63 | let len = mmio_manager.gpa_regions.len(); 64 | assert_eq!(len, 1); 65 | 66 | for i in mmio_manager.gpa_regions { 67 | gpa = i.get_gpa(); 68 | length = i.get_length(); 69 | } 70 | 71 | assert_eq!(gpa_ans, gpa); 72 | assert_eq!(length_ans, length); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/duvisor/src/mm/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod gparegion; 15 | pub mod gstagemmu; 16 | pub mod hpmallocator; 17 | pub mod mmio; 18 | pub mod utils; 19 | -------------------------------------------------------------------------------- /src/duvisor/src/mm/utils.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub const PAGE_SIZE_SHIFT: u64 = 12; 15 | pub const PAGE_TABLE_REGION_SIZE: u64 = 32 << MB_SHIFT; /* 32MB for now */ 16 | pub const PAGE_SIZE: u64 = 1u64 << PAGE_SIZE_SHIFT; 17 | pub const PAGE_SIZE_MASK: u64 = PAGE_SIZE - 1; 18 | pub const PAGE_SHIFT: u64 = 12; 19 | pub const PAGE_ORDER: u64 = 9; 20 | pub const KB_SHIFT: u64 = 10; 21 | pub const MB_SHIFT: u64 = 2 * KB_SHIFT; 22 | #[allow(unused)] 23 | pub const GB_SHIFT: u64 = 3 * KB_SHIFT; 24 | #[allow(unused)] 25 | pub const TB_SHIFT: u64 = 4 * KB_SHIFT; 26 | 27 | #[macro_export] 28 | macro_rules! dbgprintln { 29 | () => { 30 | #[cfg(test)] 31 | print!("\n"); 32 | }; 33 | ($fmt: expr) => { 34 | #[cfg(test)] 35 | print!(concat!($fmt, "\n")); 36 | }; 37 | ($fmt: expr, $($arg:tt)*) => { 38 | #[cfg(test)] 39 | print!(concat!($fmt, "\n"), $($arg)*); 40 | }; 41 | } 42 | 43 | #[macro_export] 44 | macro_rules! print_flush { 45 | ( $($t:tt)* ) => { 46 | { 47 | let mut h = io::stdout(); 48 | write!(h, $($t)* ).unwrap(); 49 | h.flush().unwrap(); 50 | } 51 | } 52 | } 53 | 54 | pub fn page_size_round_up(length: u64) -> u64 { 55 | if length & PAGE_SIZE_MASK == 0 { 56 | return length; 57 | } 58 | 59 | let result: u64 = (length & !PAGE_SIZE_MASK) + PAGE_SIZE; 60 | 61 | result 62 | } 63 | 64 | pub fn va_to_hpa_helper(va_base: u64, hpa_base: u64, va: u64, length: u64) -> Option { 65 | if va < va_base { 66 | return None; 67 | } 68 | 69 | let offset: u64 = va - va_base; 70 | 71 | if offset >= length { 72 | return None; 73 | } 74 | 75 | Some(offset + hpa_base) 76 | } 77 | 78 | pub fn hpa_to_va_helper(va_base: u64, hpa_base: u64, hpa: u64, length: u64) -> Option { 79 | if hpa < hpa_base { 80 | return None; 81 | } 82 | 83 | let offset = hpa - hpa_base; 84 | 85 | if offset >= length { 86 | return None; 87 | } 88 | 89 | Some(offset + va_base) 90 | } 91 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/kvm/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod syscall; 15 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/kvm/syscall.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | /* KVM syscall */ 15 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod kvm; 15 | pub mod opensbi; 16 | pub mod uhe; 17 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/opensbi/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod emulation; 15 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/opensbi/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int getchar_emulation() { 5 | char a; 6 | 7 | a = getchar(); 8 | 9 | return a; 10 | } -------------------------------------------------------------------------------- /src/duvisor/src/plat/uhe/csr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | /* HU CSR */ 15 | 16 | #[allow(unused)] 17 | pub mod csr_constants { 18 | /* HUSTATUS */ 19 | pub const HUSTATUS_UIE_SHIFT: u64 = 0; 20 | pub const HUSTATUS_UPIE_SHIFT: u64 = 4; 21 | pub const HUSTATUS_SPV_SHIFT: u64 = 7; 22 | pub const HUSTATUS_SPVP_SHIFT: u64 = 8; 23 | pub const HUSTATUS_VTW_SHIFT: u64 = 21; 24 | 25 | pub const VTIMECTL_ENABLE: u64 = 0; 26 | 27 | pub const HUGATP_MODE_SHIFT: u64 = 60; 28 | pub const HUGATP_MODE_SV39: u64 = 8 << HUGATP_MODE_SHIFT; 29 | pub const HUGATP_MODE_SV48: u64 = 9 << HUGATP_MODE_SHIFT; 30 | } 31 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/uhe/ioctl.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | /* User Hypervisor Extension (UHE) */ 15 | 16 | #[allow(unused)] 17 | pub mod ioctl_constants { 18 | /* Ioctl id */ 19 | pub const IOCTL_DUVISOR_GET_API_VERSION: u64 = 0x80086B01; 20 | pub const IOCTL_DUVISOR_REQUEST_DELEG: u64 = 0x40106B03; 21 | pub const IOCTL_DUVISOR_REGISTER_VCPU: u64 = 0x6B04; 22 | pub const IOCTL_DUVISOR_UNREGISTER_VCPU: u64 = 0x6B05; 23 | pub const IOCTL_DUVISOR_QUERY_PFN: u64 = 0xc0086b06; 24 | pub const IOCTL_DUVISOR_RELEASE_PFN: u64 = 0x40086b07; 25 | pub const IOCTL_REMOTE_FENCE: u64 = 0x80106b08; 26 | pub const IOCTL_DUVISOR_GET_VMID: u64 = 0x80086b09; 27 | pub const IOCTL_DUVISOR_GET_CPUID: u64 = 0x80086b0b; 28 | } 29 | -------------------------------------------------------------------------------- /src/duvisor/src/plat/uhe/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod csr; 15 | pub mod ioctl; 16 | -------------------------------------------------------------------------------- /src/duvisor/src/test/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod utils; 15 | -------------------------------------------------------------------------------- /src/duvisor/src/test/utils.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod configtest { 15 | use crate::init::cmdline::VMConfig; 16 | 17 | const ELF_IMG_PATH: &str = "./tests/integration/vcpu_add_all_gprs.img"; 18 | const DTB_PATH: &str = "./test-files-duvisor/hifive-unleashed-a00.dtb"; 19 | const DEFAULT_CONSOLE_TYPE: &str = "none"; 20 | const DEFAULT_VMTAP_NAME: &str = "vmtap0"; 21 | const DEFAULT_BLOCK_PATH: &str = "/blk-dev.img"; 22 | 23 | pub fn test_vm_config_create() -> VMConfig { 24 | let mut vm_config = VMConfig::gen_empty_config(); 25 | vm_config.set_vcpu_count(1); 26 | vm_config.set_mem_size(8192); 27 | vm_config.set_kernel_img_path(String::from(ELF_IMG_PATH)); 28 | vm_config.set_dtb_path(String::from(DTB_PATH)); 29 | vm_config.set_console_type(String::from(DEFAULT_CONSOLE_TYPE)); 30 | vm_config.set_vmtap_name(String::from(DEFAULT_VMTAP_NAME)); 31 | vm_config.set_block_path(String::from(DEFAULT_BLOCK_PATH)); 32 | vm_config 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/duvisor/src/unit/fake_config: -------------------------------------------------------------------------------- 1 | smp = 3 2 | memory = 32 3 | kernel = unitest 4 | initrd = init.file 5 | dtb = dtb.file 6 | machine = duvisor_virt 7 | -------------------------------------------------------------------------------- /src/duvisor/src/unit/unitest_kernel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IPADS-DuVisor/DuVisor/3cb1da5d94bda5f6a957326b6d8acfdb48617063/src/duvisor/src/unit/unitest_kernel -------------------------------------------------------------------------------- /src/duvisor/src/vcpu/asm_offset.h: -------------------------------------------------------------------------------- 1 | /* This file is generated by build.rs. Please do not modify it! */ 2 | 3 | #define HOST_GP_X0 0 4 | #define GUEST_GP_X0 408 5 | #define HOST_GP_X1 8 6 | #define GUEST_GP_X1 416 7 | #define HOST_GP_X2 16 8 | #define GUEST_GP_X2 424 9 | #define HOST_GP_X3 24 10 | #define GUEST_GP_X3 432 11 | #define HOST_GP_X4 32 12 | #define GUEST_GP_X4 440 13 | #define HOST_GP_X5 40 14 | #define GUEST_GP_X5 448 15 | #define HOST_GP_X6 48 16 | #define GUEST_GP_X6 456 17 | #define HOST_GP_X7 56 18 | #define GUEST_GP_X7 464 19 | #define HOST_GP_X8 64 20 | #define GUEST_GP_X8 472 21 | #define HOST_GP_X9 72 22 | #define GUEST_GP_X9 480 23 | #define HOST_GP_X10 80 24 | #define GUEST_GP_X10 488 25 | #define HOST_GP_X11 88 26 | #define GUEST_GP_X11 496 27 | #define HOST_GP_X12 96 28 | #define GUEST_GP_X12 504 29 | #define HOST_GP_X13 104 30 | #define GUEST_GP_X13 512 31 | #define HOST_GP_X14 112 32 | #define GUEST_GP_X14 520 33 | #define HOST_GP_X15 120 34 | #define GUEST_GP_X15 528 35 | #define HOST_GP_X16 128 36 | #define GUEST_GP_X16 536 37 | #define HOST_GP_X17 136 38 | #define GUEST_GP_X17 544 39 | #define HOST_GP_X18 144 40 | #define GUEST_GP_X18 552 41 | #define HOST_GP_X19 152 42 | #define GUEST_GP_X19 560 43 | #define HOST_GP_X20 160 44 | #define GUEST_GP_X20 568 45 | #define HOST_GP_X21 168 46 | #define GUEST_GP_X21 576 47 | #define HOST_GP_X22 176 48 | #define GUEST_GP_X22 584 49 | #define HOST_GP_X23 184 50 | #define GUEST_GP_X23 592 51 | #define HOST_GP_X24 192 52 | #define GUEST_GP_X24 600 53 | #define HOST_GP_X25 200 54 | #define GUEST_GP_X25 608 55 | #define HOST_GP_X26 208 56 | #define GUEST_GP_X26 616 57 | #define HOST_GP_X27 216 58 | #define GUEST_GP_X27 624 59 | #define HOST_GP_X28 224 60 | #define GUEST_GP_X28 632 61 | #define HOST_GP_X29 232 62 | #define GUEST_GP_X29 640 63 | #define HOST_GP_X30 240 64 | #define GUEST_GP_X30 648 65 | #define HOST_GP_X31 248 66 | #define GUEST_GP_X31 656 67 | #define HOST_HYP_HUSTATUS 256 68 | #define GUEST_HYP_HUSTATUS 736 69 | #define HOST_HYP_HUEDELEG 264 70 | #define GUEST_HYP_HUEDELEG 744 71 | #define HOST_HYP_HUIDELEG 272 72 | #define GUEST_HYP_HUIDELEG 752 73 | #define HOST_HYP_HUVIP 304 74 | #define GUEST_HYP_HUVIP 784 75 | #define HOST_HYP_HUIP 312 76 | #define GUEST_HYP_HUIP 792 77 | #define HOST_HYP_HUIE 280 78 | #define GUEST_HYP_HUIE 760 79 | #define HOST_HYP_HUGEIP 320 80 | #define GUEST_HYP_HUGEIP 800 81 | #define HOST_HYP_HUGEIE 328 82 | #define GUEST_HYP_HUGEIE 808 83 | #define HOST_HYP_HUCOUNTEREN 288 84 | #define GUEST_HYP_HUCOUNTEREN 768 85 | #define HOST_HYP_HUTIMEDELTA 336 86 | #define GUEST_HYP_HUTIMEDELTA 816 87 | #define HOST_HYP_HUTIMEDELTAH 344 88 | #define GUEST_HYP_HUTIMEDELTAH 824 89 | #define HOST_HYP_HUTVAL 296 90 | #define GUEST_HYP_HUTVAL 776 91 | #define HOST_HYP_HUTINST 352 92 | #define GUEST_HYP_HUTINST 832 93 | #define HOST_HYP_HUGATP 360 94 | #define GUEST_HYP_HUGATP 840 95 | #define HOST_HYP_UTVEC 368 96 | #define GUEST_HYP_UTVEC 848 97 | #define HOST_HYP_UEPC 376 98 | #define GUEST_HYP_UEPC 856 99 | #define HOST_HYP_USCRATCH 384 100 | #define GUEST_HYP_USCRATCH 864 101 | #define HOST_HYP_UTVAL 392 102 | #define GUEST_HYP_UTVAL 872 103 | #define HOST_HYP_UCAUSE 400 104 | #define GUEST_HYP_UCAUSE 880 105 | #define GUEST_SYS_HUVSSTATUS 664 106 | #define GUEST_SYS_HUVSIP 672 107 | #define GUEST_SYS_HUVSIE 680 108 | #define GUEST_SYS_HUVSTVEC 688 109 | #define GUEST_SYS_HUVSSCRATCH 696 110 | #define GUEST_SYS_HUVSEPC 704 111 | #define GUEST_SYS_HUVSCAUSE 712 112 | #define GUEST_SYS_HUVSTVAL 720 113 | #define GUEST_SYS_HUVSATP 728 114 | #define HOST_GP 0 115 | #define GUEST_GP 408 116 | #define GP_X0 0 117 | #define GP_X1 8 118 | #define GP_X2 16 119 | #define GP_X3 24 120 | #define GP_X4 32 121 | #define GP_X5 40 122 | #define GP_X6 48 123 | #define GP_X7 56 124 | #define GP_X8 64 125 | #define GP_X9 72 126 | #define GP_X10 80 127 | #define GP_X11 88 128 | #define GP_X12 96 129 | #define GP_X13 104 130 | #define GP_X14 112 131 | #define GP_X15 120 132 | #define GP_X16 128 133 | #define GP_X17 136 134 | #define GP_X18 144 135 | #define GP_X19 152 136 | #define GP_X20 160 137 | #define GP_X21 168 138 | #define GP_X22 176 139 | #define GP_X23 184 140 | #define GP_X24 192 141 | #define GP_X25 200 142 | #define GP_X26 208 143 | #define GP_X27 216 144 | #define GP_X28 224 145 | #define GP_X29 232 146 | #define GP_X30 240 147 | #define GP_X31 248 148 | #define SYS_HUVSSTATUS 0 149 | #define SYS_HUVSIP 8 150 | #define SYS_HUVSIE 16 151 | #define SYS_HUVSTVEC 24 152 | #define SYS_HUVSSCRATCH 32 153 | #define SYS_HUVSEPC 40 154 | #define SYS_HUVSCAUSE 48 155 | #define SYS_HUVSTVAL 56 156 | #define SYS_HUVSATP 64 157 | #define HYP_HUSTATUS 0 158 | #define HYP_HUEDELEG 8 159 | #define HYP_HUIDELEG 16 160 | #define HYP_HUVIP 48 161 | #define HYP_HUIP 56 162 | #define HYP_HUIE 24 163 | #define HYP_HUGEIP 64 164 | #define HYP_HUGEIE 72 165 | #define HYP_HUCOUNTEREN 32 166 | #define HYP_HUTIMEDELTA 80 167 | #define HYP_HUTIMEDELTAH 88 168 | #define HYP_HUTVAL 40 169 | #define HYP_HUTINST 96 170 | #define HYP_HUGATP 104 171 | #define HYP_UTVEC 112 172 | #define HYP_UEPC 120 173 | #define HYP_USCRATCH 128 174 | #define HYP_UTVAL 136 175 | #define HYP_UCAUSE 144 176 | -------------------------------------------------------------------------------- /src/duvisor/src/vcpu/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod utils; 15 | pub mod vcpucontext; 16 | pub mod virtualcpu; 17 | -------------------------------------------------------------------------------- /src/duvisor/src/vcpu/vcpucontext.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | #![allow(unused)] 15 | 16 | use super::utils::*; 17 | 18 | pub mod gp_reg_constants { 19 | pub const ZERO: usize = 0; 20 | pub const RA: usize = 1; 21 | pub const SP: usize = 2; 22 | pub const GP: usize = 3; 23 | pub const TP: usize = 4; 24 | pub const T0: usize = 5; 25 | pub const T1: usize = 6; 26 | pub const T2: usize = 7; 27 | pub const S0: usize = 8; 28 | pub const S1: usize = 9; 29 | pub const A0: usize = 10; 30 | pub const A1: usize = 11; 31 | pub const A2: usize = 12; 32 | pub const A3: usize = 13; 33 | pub const A4: usize = 14; 34 | pub const A5: usize = 15; 35 | pub const A6: usize = 16; 36 | pub const A7: usize = 17; 37 | pub const S2: usize = 18; 38 | pub const S3: usize = 19; 39 | pub const S4: usize = 20; 40 | pub const S5: usize = 21; 41 | pub const S6: usize = 22; 42 | pub const S7: usize = 23; 43 | pub const S8: usize = 24; 44 | pub const S9: usize = 25; 45 | pub const S10: usize = 26; 46 | pub const S11: usize = 27; 47 | pub const T3: usize = 28; 48 | pub const T4: usize = 29; 49 | pub const T5: usize = 30; 50 | pub const T6: usize = 31; 51 | } 52 | 53 | #[repr(C)] 54 | pub struct GpRegs { 55 | pub x_reg: [u64; 32], 56 | } 57 | 58 | impl GpRegs { 59 | pub fn new() -> Self { 60 | Self { x_reg: [0; 32] } 61 | } 62 | } 63 | 64 | /* SysReg for Guest */ 65 | #[repr(C)] 66 | pub struct SysRegs { 67 | pub huvsstatus: u64, 68 | pub huvsip: u64, 69 | pub huvsie: u64, 70 | pub huvstvec: u64, 71 | pub huvsscratch: u64, 72 | pub huvsepc: u64, 73 | pub huvscause: u64, 74 | pub huvstval: u64, 75 | pub huvsatp: u64, 76 | } 77 | 78 | impl SysRegs { 79 | pub fn new() -> Self { 80 | Self { 81 | huvsstatus: 0, 82 | huvsip: 0, 83 | huvsie: 0, 84 | huvstvec: 0, 85 | huvsscratch: 0, 86 | huvsepc: 0, 87 | huvscause: 0, 88 | huvstval: 0, 89 | huvsatp: 0, 90 | } 91 | } 92 | } 93 | 94 | #[repr(C)] 95 | pub struct HypRegs { 96 | pub hustatus: u64, 97 | pub huedeleg: u64, 98 | pub huideleg: u64, 99 | pub huie: u64, 100 | 101 | /* TODO: scounteren & hucounteren */ 102 | pub hucounteren: u64, 103 | pub hutval: u64, 104 | pub huvip: u64, 105 | pub huip: u64, 106 | /* TODO: hip & hie in doc */ 107 | 108 | /* TODO: In doc: Direct IRQ to VM, not needed in HU-mode? */ 109 | pub hugeip: u64, 110 | 111 | /* TODO: In doc: Direct IRQ to VM, not needed in HU-mode? */ 112 | pub hugeie: u64, 113 | 114 | pub hutimedelta: u64, 115 | pub hutimedeltah: u64, 116 | pub hutinst: u64, 117 | pub hugatp: u64, 118 | pub utvec: u64, 119 | pub uepc: u64, /* For sepc */ 120 | pub uscratch: u64, /* For sscratch */ 121 | pub utval: u64, /* For stval */ 122 | pub ucause: u64, /* For scause */ 123 | } 124 | 125 | impl HypRegs { 126 | pub fn new() -> Self { 127 | Self { 128 | hustatus: 0, 129 | huedeleg: 0, 130 | huideleg: 0, 131 | huvip: 0, 132 | huip: 0, 133 | huie: 0, 134 | hugeip: 0, 135 | hugeie: 0, 136 | hucounteren: 0, 137 | hutimedelta: 0, 138 | hutimedeltah: 0, 139 | hutval: 0, 140 | hutinst: 0, 141 | hugatp: 0, 142 | utvec: 0, 143 | uepc: 0, 144 | uscratch: 0, 145 | utval: 0, 146 | ucause: 0, 147 | } 148 | } 149 | } 150 | 151 | #[repr(C)] 152 | pub struct HostCtx { 153 | pub gp_regs: GpRegs, 154 | pub hyp_regs: HypRegs, 155 | } 156 | 157 | impl HostCtx { 158 | pub fn new() -> Self { 159 | let gp_regs = GpRegs::new(); 160 | let hyp_regs = HypRegs::new(); 161 | 162 | Self { gp_regs, hyp_regs } 163 | } 164 | } 165 | 166 | #[repr(C)] 167 | pub struct GuestCtx { 168 | pub gp_regs: GpRegs, 169 | pub sys_regs: SysRegs, 170 | pub hyp_regs: HypRegs, 171 | } 172 | 173 | impl GuestCtx { 174 | pub fn new() -> Self { 175 | let gp_regs = GpRegs::new(); 176 | let sys_regs = SysRegs::new(); 177 | let hyp_regs = HypRegs::new(); 178 | 179 | Self { 180 | gp_regs, 181 | sys_regs, 182 | hyp_regs, 183 | } 184 | } 185 | } 186 | 187 | /* Context for both ULH & VM */ 188 | #[repr(C)] 189 | pub struct VcpuCtx { 190 | pub host_ctx: HostCtx, 191 | pub guest_ctx: GuestCtx, 192 | } 193 | 194 | impl VcpuCtx { 195 | pub fn new() -> Self { 196 | let host_ctx = HostCtx::new(); 197 | let guest_ctx = GuestCtx::new(); 198 | 199 | Self { 200 | host_ctx, 201 | guest_ctx, 202 | } 203 | } 204 | 205 | pub fn set_guest_gpreg(&mut self, regid: usize, value: u64) { 206 | self.guest_ctx.gp_regs.x_reg[regid] = value 207 | } 208 | 209 | pub fn get_guest_gpreg(&self, regid: usize) -> u64 { 210 | self.guest_ctx.gp_regs.x_reg[regid] 211 | } 212 | 213 | pub fn set_host_gpreg(&mut self, regid: usize, value: u64) { 214 | self.host_ctx.gp_regs.x_reg[regid] = value 215 | } 216 | 217 | pub fn get_host_gpreg(&self, regid: usize) -> u64 { 218 | self.host_ctx.gp_regs.x_reg[regid] 219 | } 220 | 221 | pub fn get_guest_csr(&self, regid: u64) -> u64 { 222 | match regid { 223 | _ => { 224 | panic!("Unknown csr {}", regid); 225 | 0 226 | } 227 | } 228 | } 229 | 230 | pub fn set_guest_csr(&self, regid: u64, value: u64) { 231 | match regid { 232 | _ => { 233 | panic!("Unknown csr {}", regid); 234 | } 235 | } 236 | } 237 | 238 | pub fn get_host_csr(&self, regid: u64) -> u64 { 239 | match regid { 240 | UEPC => self.host_ctx.hyp_regs.uepc, 241 | UCAUSE => self.host_ctx.hyp_regs.ucause, 242 | UTVAL => self.host_ctx.hyp_regs.utval, 243 | HUTVAL => self.host_ctx.hyp_regs.hutval, 244 | HUTINST => self.host_ctx.hyp_regs.hutinst, 245 | _ => { 246 | panic!("Unknown csr {}", regid); 247 | 0 248 | } 249 | } 250 | } 251 | 252 | pub fn set_host_csr(&mut self, regid: u64, value: u64) { 253 | match regid { 254 | HUCOUNTEREN => self.host_ctx.hyp_regs.hucounteren = value, 255 | UEPC => self.host_ctx.hyp_regs.uepc = value, 256 | HUSTATUS => self.host_ctx.hyp_regs.hustatus = value, 257 | HUGATP => self.host_ctx.hyp_regs.hugatp = value, 258 | _ => { 259 | panic!("Unknown csr {}", regid) 260 | } 261 | } 262 | } 263 | 264 | pub fn increment_host_uepc(&mut self, value: u64) { 265 | self.host_ctx.hyp_regs.uepc += value; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/duvisor/src/vm/image.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | use std::path::PathBuf; 15 | 16 | /* File type for vm image */ 17 | pub const IMAGE_TYPE_ELF: u8 = 1; 18 | pub const IMAGE_TYPE_DATA: u8 = 2; 19 | 20 | /* Linux image const */ 21 | pub const RISCV_RAM_GPA_START: u64 = 0x80000000; 22 | pub const KERNEL_OFFSET: u64 = 0x200000; 23 | 24 | /* Read and parse the vm img file */ 25 | pub struct VmImage { 26 | elf_file: elf::File, 27 | file_data: Vec, 28 | file_type: u8, 29 | } 30 | 31 | impl VmImage { 32 | pub fn new(file_path: &str) -> Self { 33 | /* Parse ELF file */ 34 | let elf_file: elf::File; 35 | let elf_wrap = VmImage::elf_parse(file_path); 36 | let file_type: u8; 37 | 38 | if elf_wrap.is_none() { 39 | /* VM image is not ELF */ 40 | elf_file = elf::File::new(); 41 | file_type = IMAGE_TYPE_DATA; 42 | } else { 43 | elf_file = elf_wrap.unwrap(); 44 | file_type = IMAGE_TYPE_ELF; 45 | } 46 | 47 | let file_data = std::fs::read(file_path).unwrap_or_else(|_| { 48 | panic!("read file failed"); 49 | }); 50 | 51 | Self { 52 | elf_file, 53 | file_data, 54 | file_type, 55 | } 56 | } 57 | 58 | pub fn elf_parse(elf_path: &str) -> Option { 59 | let path = PathBuf::from(elf_path); 60 | let file = match elf::File::open_path(&path) { 61 | Ok(f) => Some(f), 62 | Err(_e) => None, 63 | }; 64 | 65 | file 66 | } 67 | 68 | /* Get Image file data */ 69 | pub fn get_file_data(&self) -> &Vec { 70 | &self.file_data 71 | } 72 | 73 | /* Get Image file type */ 74 | pub fn get_file_type(&self) -> u8 { 75 | self.file_type 76 | } 77 | 78 | /* Get ELF file structure (Metadata) */ 79 | pub fn get_elf_file(&self) -> &elf::File { 80 | &self.elf_file 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use crate::test::utils::configtest::test_vm_config_create; 87 | use crate::vm::*; 88 | use libc::c_void; 89 | use rusty_fork::rusty_fork_test; 90 | 91 | rusty_fork_test! { 92 | #[test] 93 | fn test_image_type_data() { 94 | let mut vm_config = test_vm_config_create(); 95 | vm_config.set_kernel_img_path( 96 | String::from("./test-files-duvisor/Image")); 97 | 98 | let vm = virtualmachine::VirtualMachine::new(vm_config); 99 | let kernel_type = vm.image_file_type(); 100 | 101 | assert_eq!(kernel_type, image::IMAGE_TYPE_DATA); 102 | } 103 | 104 | #[test] 105 | fn test_image_type_elf() { 106 | let mut vm_config = test_vm_config_create(); 107 | vm_config.set_kernel_img_path( 108 | String::from("./tests/integration/vcpu_add_all_gprs.img")); 109 | 110 | let vm = virtualmachine::VirtualMachine::new(vm_config); 111 | let kernel_type = vm.image_file_type(); 112 | 113 | assert_eq!(kernel_type, image::IMAGE_TYPE_ELF); 114 | } 115 | 116 | #[test] 117 | fn test_image_load() { 118 | let mut vm_config = test_vm_config_create(); 119 | let kernel_path: &str = "./test-files-duvisor/Image"; 120 | vm_config.set_kernel_img_path(String::from(kernel_path)); 121 | let mut vm = virtualmachine::VirtualMachine::new(vm_config); 122 | let kernel_hva: u64 = vm.vm_init()[0]; 123 | 124 | let ans_res = std::fs::read(kernel_path); 125 | if ans_res.is_err() { 126 | panic!("Ans kernel load failed"); 127 | } 128 | let ans_data = ans_res.unwrap(); 129 | 130 | let result: i32; 131 | unsafe { 132 | result = libc::memcmp(kernel_hva as *const c_void, 133 | ans_data.as_ptr() as *const c_void, 134 | ans_data.len()); 135 | } 136 | 137 | assert_eq!(result, 0); 138 | } 139 | 140 | #[test] 141 | fn test_image_zero_size() { 142 | let mut vm_config = test_vm_config_create(); 143 | vm_config.set_kernel_img_path( 144 | String::from("./test-files-duvisor/null-kernel-image.img")); 145 | let mut vm = virtualmachine::VirtualMachine::new(vm_config); 146 | let hva_list = vm.vm_init(); 147 | let length = hva_list.len(); 148 | 149 | /* There should not be any data loaded */ 150 | assert_eq!(length, 0); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/duvisor/src/vm/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023 The institute of parallel and distributed systems (IPADS) 3 | DuVisor is licensed under Mulan PSL v2. 4 | You can use this software according to the terms and conditions of the Mulan PSL v2. 5 | You may obtain a copy of Mulan PSL v2 at: 6 | http://license.coscl.org.cn/MulanPSL2 7 | 8 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 9 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 10 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 | See the Mulan PSL v2 for more details. 12 | */ 13 | 14 | pub mod dtb; 15 | pub mod image; 16 | pub mod virtualmachine; 17 | 18 | pub use virtualmachine::*; 19 | -------------------------------------------------------------------------------- /src/irq_util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "irq_util" 3 | version = "0.1.0" 4 | authors = ["lidj "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /src/irq_util/src/irqchip.rs: -------------------------------------------------------------------------------- 1 | pub trait IrqChip: Send + Sync { 2 | fn mmio_callback(&self, addr: u64, data: &mut u32, is_write: bool); 3 | 4 | fn trigger_level_irq(&self, irq: u32, level: bool); 5 | 6 | fn trigger_edge_irq(&self, irq: u32); 7 | 8 | /* TODO: Vcpu should find running vcpus via plic, remove it */ 9 | fn trigger_virtual_irq(&self, vcpu_id: u32) -> bool; 10 | } 11 | -------------------------------------------------------------------------------- /src/irq_util/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod irqchip; 2 | pub use irqchip::*; 3 | -------------------------------------------------------------------------------- /src/net_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "net_sys" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | 6 | [dependencies] 7 | sys_util = { path = "../sys_util" } 8 | -------------------------------------------------------------------------------- /src/net_sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright TUNTAP, 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #![allow(non_upper_case_globals)] 6 | #![allow(non_camel_case_types)] 7 | #![allow(non_snake_case)] 8 | #![allow(deref_nullptr)] 9 | 10 | #[macro_use] 11 | extern crate sys_util; 12 | 13 | // generated with bindgen /usr/include/linux/if.h --no-unstable-rust 14 | // --constified-enum '*' --with-derive-default -- -D __UAPI_DEF_IF_IFNAMSIZ -D 15 | // __UAPI_DEF_IF_NET_DEVICE_FLAGS -D __UAPI_DEF_IF_IFREQ -D __UAPI_DEF_IF_IFMAP 16 | // Name is "iff" to avoid conflicting with "if" keyword. 17 | // Generated against Linux 4.11 to include fix "uapi: fix linux/if.h userspace 18 | // compilation errors". 19 | // Manual fixup of ifrn_name to be of type c_uchar instead of c_char. 20 | pub mod iff; 21 | // generated with bindgen /usr/include/linux/if_tun.h --no-unstable-rust 22 | // --constified-enum '*' --with-derive-default 23 | pub mod if_tun; 24 | // generated with bindgen /usr/include/linux/in.h --no-unstable-rust 25 | // --constified-enum '*' --with-derive-default 26 | // Name is "inn" to avoid conflicting with "in" keyword. 27 | pub mod inn; 28 | // generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust 29 | // --constified-enum '*' --with-derive-default 30 | pub mod sockios; 31 | pub use iff::*; 32 | pub use if_tun::*; 33 | pub use inn::*; 34 | pub use sockios::*; 35 | 36 | pub const TUNTAP: ::std::os::raw::c_uint = 84; 37 | 38 | ioctl_iow_nr!(TUNSETNOCSUM, TUNTAP, 200, ::std::os::raw::c_int); 39 | ioctl_iow_nr!(TUNSETDEBUG, TUNTAP, 201, ::std::os::raw::c_int); 40 | ioctl_iow_nr!(TUNSETIFF, TUNTAP, 202, ::std::os::raw::c_int); 41 | ioctl_iow_nr!(TUNSETPERSIST, TUNTAP, 203, ::std::os::raw::c_int); 42 | ioctl_iow_nr!(TUNSETOWNER, TUNTAP, 204, ::std::os::raw::c_int); 43 | ioctl_iow_nr!(TUNSETLINK, TUNTAP, 205, ::std::os::raw::c_int); 44 | ioctl_iow_nr!(TUNSETGROUP, TUNTAP, 206, ::std::os::raw::c_int); 45 | ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 207, ::std::os::raw::c_uint); 46 | ioctl_iow_nr!(TUNSETOFFLOAD, TUNTAP, 208, ::std::os::raw::c_uint); 47 | ioctl_iow_nr!(TUNSETTXFILTER, TUNTAP, 209, ::std::os::raw::c_uint); 48 | ioctl_ior_nr!(TUNGETIFF, TUNTAP, 210, ::std::os::raw::c_uint); 49 | ioctl_ior_nr!(TUNGETSNDBUF, TUNTAP, 211, ::std::os::raw::c_int); 50 | ioctl_iow_nr!(TUNSETSNDBUF, TUNTAP, 212, ::std::os::raw::c_int); 51 | ioctl_iow_nr!(TUNATTACHFILTER, TUNTAP, 213, sock_fprog); 52 | ioctl_iow_nr!(TUNDETACHFILTER, TUNTAP, 214, sock_fprog); 53 | ioctl_ior_nr!(TUNGETVNETHDRSZ, TUNTAP, 215, ::std::os::raw::c_int); 54 | ioctl_iow_nr!(TUNSETVNETHDRSZ, TUNTAP, 216, ::std::os::raw::c_int); 55 | ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 217, ::std::os::raw::c_int); 56 | ioctl_iow_nr!(TUNSETIFINDEX, TUNTAP, 218, ::std::os::raw::c_uint); 57 | ioctl_ior_nr!(TUNGETFILTER, TUNTAP, 219, sock_fprog); 58 | ioctl_iow_nr!(TUNSETVNETLE, TUNTAP, 220, ::std::os::raw::c_int); 59 | ioctl_ior_nr!(TUNGETVNETLE, TUNTAP, 221, ::std::os::raw::c_int); 60 | ioctl_iow_nr!(TUNSETVNETBE, TUNTAP, 222, ::std::os::raw::c_int); 61 | ioctl_ior_nr!(TUNGETVNETBE, TUNTAP, 223, ::std::os::raw::c_int); 62 | -------------------------------------------------------------------------------- /src/net_sys/src/sockios.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen */ 2 | 3 | pub const FIOSETOWN: ::std::os::raw::c_uint = 35073; 4 | pub const SIOCSPGRP: ::std::os::raw::c_uint = 35074; 5 | pub const FIOGETOWN: ::std::os::raw::c_uint = 35075; 6 | pub const SIOCGPGRP: ::std::os::raw::c_uint = 35076; 7 | pub const SIOCATMARK: ::std::os::raw::c_uint = 35077; 8 | pub const SIOCGSTAMP: ::std::os::raw::c_uint = 35078; 9 | pub const SIOCGSTAMPNS: ::std::os::raw::c_uint = 35079; 10 | pub const SOCK_IOC_TYPE: ::std::os::raw::c_uint = 137; 11 | pub const SIOCADDRT: ::std::os::raw::c_uint = 35083; 12 | pub const SIOCDELRT: ::std::os::raw::c_uint = 35084; 13 | pub const SIOCRTMSG: ::std::os::raw::c_uint = 35085; 14 | pub const SIOCGIFNAME: ::std::os::raw::c_uint = 35088; 15 | pub const SIOCSIFLINK: ::std::os::raw::c_uint = 35089; 16 | pub const SIOCGIFCONF: ::std::os::raw::c_uint = 35090; 17 | pub const SIOCGIFFLAGS: ::std::os::raw::c_uint = 35091; 18 | pub const SIOCSIFFLAGS: ::std::os::raw::c_uint = 35092; 19 | pub const SIOCGIFADDR: ::std::os::raw::c_uint = 35093; 20 | pub const SIOCSIFADDR: ::std::os::raw::c_uint = 35094; 21 | pub const SIOCGIFDSTADDR: ::std::os::raw::c_uint = 35095; 22 | pub const SIOCSIFDSTADDR: ::std::os::raw::c_uint = 35096; 23 | pub const SIOCGIFBRDADDR: ::std::os::raw::c_uint = 35097; 24 | pub const SIOCSIFBRDADDR: ::std::os::raw::c_uint = 35098; 25 | pub const SIOCGIFNETMASK: ::std::os::raw::c_uint = 35099; 26 | pub const SIOCSIFNETMASK: ::std::os::raw::c_uint = 35100; 27 | pub const SIOCGIFMETRIC: ::std::os::raw::c_uint = 35101; 28 | pub const SIOCSIFMETRIC: ::std::os::raw::c_uint = 35102; 29 | pub const SIOCGIFMEM: ::std::os::raw::c_uint = 35103; 30 | pub const SIOCSIFMEM: ::std::os::raw::c_uint = 35104; 31 | pub const SIOCGIFMTU: ::std::os::raw::c_uint = 35105; 32 | pub const SIOCSIFMTU: ::std::os::raw::c_uint = 35106; 33 | pub const SIOCSIFNAME: ::std::os::raw::c_uint = 35107; 34 | pub const SIOCSIFHWADDR: ::std::os::raw::c_uint = 35108; 35 | pub const SIOCGIFENCAP: ::std::os::raw::c_uint = 35109; 36 | pub const SIOCSIFENCAP: ::std::os::raw::c_uint = 35110; 37 | pub const SIOCGIFHWADDR: ::std::os::raw::c_uint = 35111; 38 | pub const SIOCGIFSLAVE: ::std::os::raw::c_uint = 35113; 39 | pub const SIOCSIFSLAVE: ::std::os::raw::c_uint = 35120; 40 | pub const SIOCADDMULTI: ::std::os::raw::c_uint = 35121; 41 | pub const SIOCDELMULTI: ::std::os::raw::c_uint = 35122; 42 | pub const SIOCGIFINDEX: ::std::os::raw::c_uint = 35123; 43 | pub const SIOGIFINDEX: ::std::os::raw::c_uint = 35123; 44 | pub const SIOCSIFPFLAGS: ::std::os::raw::c_uint = 35124; 45 | pub const SIOCGIFPFLAGS: ::std::os::raw::c_uint = 35125; 46 | pub const SIOCDIFADDR: ::std::os::raw::c_uint = 35126; 47 | pub const SIOCSIFHWBROADCAST: ::std::os::raw::c_uint = 35127; 48 | pub const SIOCGIFCOUNT: ::std::os::raw::c_uint = 35128; 49 | pub const SIOCGIFBR: ::std::os::raw::c_uint = 35136; 50 | pub const SIOCSIFBR: ::std::os::raw::c_uint = 35137; 51 | pub const SIOCGIFTXQLEN: ::std::os::raw::c_uint = 35138; 52 | pub const SIOCSIFTXQLEN: ::std::os::raw::c_uint = 35139; 53 | pub const SIOCETHTOOL: ::std::os::raw::c_uint = 35142; 54 | pub const SIOCGMIIPHY: ::std::os::raw::c_uint = 35143; 55 | pub const SIOCGMIIREG: ::std::os::raw::c_uint = 35144; 56 | pub const SIOCSMIIREG: ::std::os::raw::c_uint = 35145; 57 | pub const SIOCWANDEV: ::std::os::raw::c_uint = 35146; 58 | pub const SIOCOUTQNSD: ::std::os::raw::c_uint = 35147; 59 | pub const SIOCGSKNS: ::std::os::raw::c_uint = 35148; 60 | pub const SIOCDARP: ::std::os::raw::c_uint = 35155; 61 | pub const SIOCGARP: ::std::os::raw::c_uint = 35156; 62 | pub const SIOCSARP: ::std::os::raw::c_uint = 35157; 63 | pub const SIOCDRARP: ::std::os::raw::c_uint = 35168; 64 | pub const SIOCGRARP: ::std::os::raw::c_uint = 35169; 65 | pub const SIOCSRARP: ::std::os::raw::c_uint = 35170; 66 | pub const SIOCGIFMAP: ::std::os::raw::c_uint = 35184; 67 | pub const SIOCSIFMAP: ::std::os::raw::c_uint = 35185; 68 | pub const SIOCADDDLCI: ::std::os::raw::c_uint = 35200; 69 | pub const SIOCDELDLCI: ::std::os::raw::c_uint = 35201; 70 | pub const SIOCGIFVLAN: ::std::os::raw::c_uint = 35202; 71 | pub const SIOCSIFVLAN: ::std::os::raw::c_uint = 35203; 72 | pub const SIOCBONDENSLAVE: ::std::os::raw::c_uint = 35216; 73 | pub const SIOCBONDRELEASE: ::std::os::raw::c_uint = 35217; 74 | pub const SIOCBONDSETHWADDR: ::std::os::raw::c_uint = 35218; 75 | pub const SIOCBONDSLAVEINFOQUERY: ::std::os::raw::c_uint = 35219; 76 | pub const SIOCBONDINFOQUERY: ::std::os::raw::c_uint = 35220; 77 | pub const SIOCBONDCHANGEACTIVE: ::std::os::raw::c_uint = 35221; 78 | pub const SIOCBRADDBR: ::std::os::raw::c_uint = 35232; 79 | pub const SIOCBRDELBR: ::std::os::raw::c_uint = 35233; 80 | pub const SIOCBRADDIF: ::std::os::raw::c_uint = 35234; 81 | pub const SIOCBRDELIF: ::std::os::raw::c_uint = 35235; 82 | pub const SIOCSHWTSTAMP: ::std::os::raw::c_uint = 35248; 83 | pub const SIOCGHWTSTAMP: ::std::os::raw::c_uint = 35249; 84 | pub const SIOCDEVPRIVATE: ::std::os::raw::c_uint = 35312; 85 | pub const SIOCPROTOPRIVATE: ::std::os::raw::c_uint = 35296; 86 | -------------------------------------------------------------------------------- /src/net_util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "net_util" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | 6 | [dependencies] 7 | libc = "0.2.32" 8 | net_sys = { path = "../net_sys" } 9 | sys_util = { path = "../sys_util" } 10 | -------------------------------------------------------------------------------- /src/net_util/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | extern crate libc; 6 | extern crate net_sys; 7 | extern crate sys_util; 8 | 9 | mod tap; 10 | 11 | use std::io::{Error as IoError}; 12 | use std::mem; 13 | use std::net; 14 | use std::os::unix::io::FromRawFd; 15 | 16 | pub use tap::Tap; 17 | 18 | #[derive(Debug)] 19 | pub enum Error { 20 | /// Failed to create a socket. 21 | CreateSocket(IoError), 22 | /// Couldn't open /dev/net/tun. 23 | OpenTun(IoError), 24 | /// Unable to create tap interface. 25 | CreateTap(IoError), 26 | /// ioctl failed. 27 | IoctlError(IoError), 28 | } 29 | 30 | pub type Result = std::result::Result; 31 | 32 | /// Create a sockaddr_in from an IPv4 address, and expose it as 33 | /// an opaque sockaddr suitable for usage by socket ioctls. 34 | fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_sys::sockaddr { 35 | // IPv4 addresses big-endian (network order), but Ipv4Addr will give us 36 | // a view of those bytes directly so we can avoid any endian trickiness. 37 | let addr_in = net_sys::sockaddr_in { 38 | sin_family: net_sys::AF_INET as u16, 39 | sin_port: 0, 40 | sin_addr: unsafe { mem::transmute(ip_addr.octets()) }, 41 | __pad: [0; 8usize], 42 | }; 43 | 44 | unsafe { mem::transmute(addr_in) } 45 | } 46 | 47 | fn create_socket() -> Result { 48 | // This is safe since we check the return value. 49 | let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; 50 | if sock < 0 { 51 | return Err(Error::CreateSocket(IoError::last_os_error())); 52 | } 53 | 54 | // This is safe; nothing else will use or hold onto the raw sock fd. 55 | Ok(unsafe { net::UdpSocket::from_raw_fd(sock) }) 56 | } 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/sys_util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sys_util" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | build = "build.rs" 6 | 7 | [dependencies] 8 | data_model = { path = "../data_model" } 9 | libc = "*" 10 | syscall_defines = { path = "../syscall_defines" } 11 | 12 | [build-dependencies] 13 | gcc = "=0.3.54" 14 | cc = "1.0" 15 | -------------------------------------------------------------------------------- /src/sys_util/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | extern crate cc; 6 | 7 | fn main() { 8 | cc::Build::new().file("sock_ctrl_msg.c").compile( 9 | "sock_ctrl_msg", 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/sys_util/sock_ctrl_msg.c: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include // memcpy 7 | #include // close 8 | #include 9 | #include // CMSG_* 10 | 11 | /* 12 | * Returns the number of bytes the `cmsg_buffer` must be for the functions that take a cmsg_buffer 13 | * in this module. 14 | * Arguments: 15 | * * `fd_count` - Maximum number of file descriptors to be sent or received via the cmsg. 16 | */ 17 | size_t scm_cmsg_buffer_len(size_t fd_count) 18 | { 19 | return CMSG_SPACE(sizeof(int) * fd_count); 20 | } 21 | 22 | /* 23 | * Convenience wrapper around `sendmsg` that builds up the `msghdr` structure for you given the 24 | * array of fds. 25 | * Arguments: 26 | * * `fd` - Unix domain socket to `sendmsg` on. 27 | * * `outv` - Array of `outv_count` length `iovec`s that contain the data to send. 28 | * * `outv_count` - Number of elements in `outv` array. 29 | * * `cmsg_buffer` - A buffer that must be at least `scm_cmsg_buffer_len(fd_count)` bytes long. 30 | * * `fds` - Array of `fd_count` file descriptors to send along with data. 31 | * * `fd_count` - Number of elements in `fds` array. 32 | * Returns: 33 | * A non-negative number indicating how many bytes were sent on success or a negative errno on 34 | * failure. 35 | */ 36 | ssize_t scm_sendmsg(int fd, const struct iovec *outv, size_t outv_count, uint8_t *cmsg_buffer, 37 | const int *fds, size_t fd_count) 38 | { 39 | if (fd < 0 || ((!cmsg_buffer || !fds) && fd_count > 0)) 40 | return -EINVAL; 41 | 42 | struct msghdr msg; 43 | memset(&msg, 0, sizeof(msg)); 44 | msg.msg_iov = (struct iovec *)outv; // discard const, sendmsg won't mutate it 45 | msg.msg_iovlen = outv_count; 46 | 47 | if (fd_count) { 48 | msg.msg_control = cmsg_buffer; 49 | msg.msg_controllen = scm_cmsg_buffer_len(fd_count); 50 | 51 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 52 | cmsg->cmsg_level = SOL_SOCKET; 53 | cmsg->cmsg_type = SCM_RIGHTS; 54 | cmsg->cmsg_len = CMSG_LEN(fd_count * sizeof(int)); 55 | memcpy(CMSG_DATA(cmsg), fds, fd_count * sizeof(int)); 56 | 57 | msg.msg_controllen = cmsg->cmsg_len; 58 | } 59 | 60 | ssize_t bytes_sent = sendmsg(fd, &msg, MSG_NOSIGNAL); 61 | if (bytes_sent == -1) 62 | return -errno; 63 | 64 | return bytes_sent; 65 | } 66 | 67 | /* 68 | * Convenience wrapper around `recvmsg` that builds up the `msghdr` structure and returns up to 69 | * `*fd_count` file descriptors in the given `fds` array. 70 | * Arguments: 71 | * * `fd` - Unix domain socket to `recvmsg` on. 72 | * * `outv` - Array of `outv_count` length `iovec`s that will contain the received data. 73 | * * `outv_count` - Number of elements in `outv` array. 74 | * * `cmsg_buffer` - A buffer that must be at least `scm_cmsg_buffer_len(*fd_count)` bytes long. 75 | * * `fds` - Array of `fd_count` file descriptors to receive along with data. 76 | * * `fd_count` - Number of elements in `fds` array. 77 | * Returns: 78 | * A non-negative number indicating how many bytes were received on success or a negative errno on 79 | * failure. 80 | */ 81 | ssize_t scm_recvmsg(int fd, struct iovec *outv, size_t outv_count, uint8_t *cmsg_buffer, int *fds, 82 | size_t *fd_count) 83 | { 84 | if (fd < 0 || !cmsg_buffer || !fds || !fd_count) 85 | return -EINVAL; 86 | 87 | struct msghdr msg; 88 | memset(&msg, 0, sizeof(msg)); 89 | msg.msg_iov = outv; 90 | msg.msg_iovlen = outv_count; 91 | msg.msg_control = cmsg_buffer; 92 | msg.msg_controllen = scm_cmsg_buffer_len(*fd_count); 93 | 94 | ssize_t total_read = recvmsg(fd, &msg, 0); 95 | if (total_read == -1) 96 | return -errno; 97 | 98 | if (total_read == 0 && CMSG_FIRSTHDR(&msg) == NULL) { 99 | *fd_count = 0; 100 | return 0; 101 | } 102 | 103 | size_t fd_idx = 0; 104 | struct cmsghdr *cmsg; 105 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 106 | if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) 107 | continue; 108 | 109 | size_t cmsg_fd_count = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); 110 | 111 | int *cmsg_fds = (int *)CMSG_DATA(cmsg); 112 | size_t cmsg_fd_idx; 113 | for (cmsg_fd_idx = 0; cmsg_fd_idx < cmsg_fd_count; cmsg_fd_idx++) { 114 | if (fd_idx < *fd_count) { 115 | fds[fd_idx] = cmsg_fds[cmsg_fd_idx]; 116 | fd_idx++; 117 | } else { 118 | close(cmsg_fds[cmsg_fd_idx]); 119 | } 120 | } 121 | } 122 | 123 | *fd_count = fd_idx; 124 | 125 | return total_read; 126 | } 127 | -------------------------------------------------------------------------------- /src/sys_util/src/errno.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::result; 6 | use std::io; 7 | 8 | use libc::__errno_location; 9 | 10 | /// An error number, retrieved from errno (man 3 errno), set by a libc 11 | /// function that returned an error. 12 | #[derive(Clone, Copy, Debug, PartialEq)] 13 | pub struct Error(i32); 14 | pub type Result = result::Result; 15 | 16 | impl Error { 17 | /// Constructs a new error with the given errno. 18 | pub fn new(e: i32) -> Error { 19 | Error(e) 20 | } 21 | 22 | /// Constructs an error from the current errno. 23 | /// 24 | /// The result of this only has any meaning just after a libc call that returned a value 25 | /// indicating errno was set. 26 | pub fn last() -> Error { 27 | Error(unsafe { *__errno_location() }) 28 | } 29 | 30 | /// Gets the errno for this error 31 | pub fn errno(&self) -> i32 { 32 | self.0 33 | } 34 | } 35 | 36 | impl From for Error { 37 | fn from(e: io::Error) -> Self { 38 | Error::new(e.raw_os_error().unwrap_or_default()) 39 | } 40 | } 41 | 42 | /// Returns the last errno as a Result that is always an error. 43 | pub fn errno_result() -> Result { 44 | Err(Error::last()) 45 | } 46 | -------------------------------------------------------------------------------- /src/sys_util/src/eventfd.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::mem; 6 | use std::fs::File; 7 | use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; 8 | 9 | use libc::{read, write, eventfd, dup, c_void}; 10 | 11 | use {Result, errno_result}; 12 | 13 | /// A safe wrapper around a Linux eventfd (man 2 eventfd). 14 | /// 15 | /// An eventfd is useful because it is sendable across processes and can be used for signaling in 16 | /// and out of the KVM API. They can also be polled like any other file descriptor. 17 | pub struct EventFd { 18 | eventfd: File, 19 | } 20 | 21 | impl EventFd { 22 | /// Creates a new blocking EventFd with an initial value of 0. 23 | pub fn new() -> Result { 24 | // This is safe because eventfd merely allocated an eventfd for our process and we handle 25 | // the error case. 26 | let ret = unsafe { eventfd(0, 0) }; 27 | if ret < 0 { 28 | return errno_result(); 29 | } 30 | // This is safe because we checked ret for success and know the kernel gave us an fd that we 31 | // own. 32 | Ok(EventFd { eventfd: unsafe { File::from_raw_fd(ret) } }) 33 | } 34 | 35 | /// Adds `v` to the eventfd's count, blocking until this won't overflow the count. 36 | pub fn write(&self, v: u64) -> Result<()> { 37 | // This is safe because we made this fd and the pointer we pass can not overflow because we 38 | // give the syscall's size parameter properly. 39 | let ret = unsafe { 40 | write( 41 | self.as_raw_fd(), 42 | &v as *const u64 as *const c_void, 43 | mem::size_of::(), 44 | ) 45 | }; 46 | if ret <= 0 { 47 | return errno_result(); 48 | } 49 | Ok(()) 50 | } 51 | 52 | /// Blocks until the the eventfd's count is non-zero, then resets the count to zero. 53 | pub fn read(&self) -> Result { 54 | let mut buf: u64 = 0; 55 | let ret = unsafe { 56 | // This is safe because we made this fd and the pointer we pass can not overflow because 57 | // we give the syscall's size parameter properly. 58 | read( 59 | self.as_raw_fd(), 60 | &mut buf as *mut u64 as *mut c_void, 61 | mem::size_of::(), 62 | ) 63 | }; 64 | if ret <= 0 { 65 | return errno_result(); 66 | } 67 | Ok(buf) 68 | } 69 | 70 | /// Clones this EventFd, internally creating a new file descriptor. The new EventFd will share 71 | /// the same underlying count within the kernel. 72 | pub fn try_clone(&self) -> Result { 73 | // This is safe because we made this fd and properly check that it returns without error. 74 | let ret = unsafe { dup(self.as_raw_fd()) }; 75 | if ret < 0 { 76 | return errno_result(); 77 | } 78 | // This is safe because we checked ret for success and know the kernel gave us an fd that we 79 | // own. 80 | Ok(EventFd { eventfd: unsafe { File::from_raw_fd(ret) } }) 81 | } 82 | } 83 | 84 | impl AsRawFd for EventFd { 85 | fn as_raw_fd(&self) -> RawFd { 86 | self.eventfd.as_raw_fd() 87 | } 88 | } 89 | 90 | unsafe impl ::Pollable for EventFd { 91 | fn pollable_fd(&self) -> RawFd { 92 | self.eventfd.as_raw_fd() 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | 100 | #[test] 101 | fn new() { 102 | EventFd::new().unwrap(); 103 | } 104 | 105 | #[test] 106 | fn read_write() { 107 | let evt = EventFd::new().unwrap(); 108 | evt.write(55).unwrap(); 109 | assert_eq!(evt.read(), Ok(55)); 110 | } 111 | 112 | #[test] 113 | fn clone() { 114 | let evt = EventFd::new().unwrap(); 115 | let evt_clone = evt.try_clone().unwrap(); 116 | evt.write(923).unwrap(); 117 | assert_eq!(evt_clone.read(), Ok(923)); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/sys_util/src/fork.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::fs; 6 | use std::io; 7 | use std::path::Path; 8 | use std::process; 9 | use std::result; 10 | 11 | use errno_result; 12 | 13 | use libc::{syscall, SIGCHLD, CLONE_NEWUSER, CLONE_NEWPID, c_long, pid_t}; 14 | 15 | use syscall_defines::linux::LinuxSyscall::SYS_clone; 16 | 17 | /// Controls what namespace `clone_process` will have. See NAMESPACES(7). 18 | #[repr(u32)] 19 | pub enum CloneNamespace { 20 | /// The new process will inherit the namespace from the old process. 21 | Inherit = 0, 22 | /// The new process with be in a new user and PID namespace. 23 | NewUserPid = CLONE_NEWUSER as u32 | CLONE_NEWPID as u32, 24 | } 25 | 26 | #[derive(Debug)] 27 | pub enum CloneError { 28 | /// There was an error trying to iterate this process's threads. 29 | IterateTasks(io::Error), 30 | /// There are multiple threads running. The `usize` indicates how many threads. 31 | Multithreaded(usize), 32 | /// There was an error while cloning. 33 | Sys(::Error), 34 | } 35 | 36 | unsafe fn do_clone(flags: i32) -> ::Result { 37 | // Forking is unsafe, this function must be unsafe as there is no way to guarantee safety 38 | // without more context about the state of the program. 39 | let pid = syscall(SYS_clone as c_long, flags | SIGCHLD as i32, 0); 40 | if pid < 0 { 41 | errno_result() 42 | } else { 43 | Ok(pid as pid_t) 44 | } 45 | } 46 | 47 | fn count_dir_entries>(path: P) -> io::Result { 48 | Ok(fs::read_dir(path)?.count()) 49 | } 50 | 51 | /// Clones this process and calls a closure in the new process. 52 | /// 53 | /// After `post_clone_cb` returns or panics, the new process exits. Similar to how a `fork` syscall 54 | /// works, the new process is the same as the current process with the exception of the namespace 55 | /// controlled with the `ns` argument. 56 | /// 57 | /// # Arguments 58 | /// * `ns` - What namespace the new process will have (see NAMESPACES(7)). 59 | /// * `post_clone_cb` - Callback to run in the new process 60 | pub fn clone_process(ns: CloneNamespace, post_clone_cb: F) -> result::Result 61 | where 62 | F: FnOnce(), 63 | { 64 | match count_dir_entries("/proc/self/task") { 65 | Ok(1) => {} 66 | Ok(thread_count) => { 67 | // Test cfg gets a free pass on this because tests generally have multiple independent 68 | // test threads going. 69 | let _ = thread_count; 70 | #[cfg(not(test))] return Err(CloneError::Multithreaded(thread_count)); 71 | } 72 | Err(e) => return Err(CloneError::IterateTasks(e)), 73 | } 74 | // Forking is considered unsafe in mutlithreaded programs, but we just checked for other threads 75 | // in this process. We also only allow valid flags from CloneNamespace and check the return 76 | // result for errors. We also never let the cloned process return from this function. 77 | let ret = unsafe { do_clone(ns as i32) }.map_err(CloneError::Sys)?; 78 | if ret == 0 { 79 | struct ExitGuard; 80 | impl Drop for ExitGuard { 81 | fn drop(&mut self) { 82 | process::exit(101); 83 | } 84 | } 85 | // Prevents a panic in post_clone_cb from bypassing the process::exit. 86 | #[allow(unused_variables)] 87 | let exit_guard = ExitGuard {}; 88 | post_clone_cb(); 89 | // ! Never returns 90 | process::exit(0); 91 | } 92 | 93 | Ok(ret) 94 | } 95 | -------------------------------------------------------------------------------- /src/sys_util/src/guest_address.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Represents an address in the guest's memory space. 6 | 7 | use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; 8 | use std::ops::{BitAnd, BitOr}; 9 | 10 | /// Represents an Address in the guest's memory. 11 | #[derive(Clone, Copy, Debug)] 12 | pub struct GuestAddress(pub usize); 13 | 14 | impl GuestAddress { 15 | /// Returns the offset from this address to the given base address. 16 | /// 17 | /// # Examples 18 | /// 19 | /// ``` 20 | /// # use sys_util::GuestAddress; 21 | /// let base = GuestAddress(0x100); 22 | /// let addr = GuestAddress(0x150); 23 | /// assert_eq!(addr.offset_from(base), 0x50usize); 24 | /// ``` 25 | pub fn offset_from(&self, base: GuestAddress) -> usize { 26 | self.0 - base.0 27 | } 28 | 29 | /// Returns the address as a usize offset from 0x0. 30 | /// Use this when a raw number is needed to pass to the kernel. 31 | pub fn offset(&self) -> usize { 32 | self.0 33 | } 34 | 35 | /// Returns the result of the add or None if there is overflow. 36 | pub fn checked_add(&self, other: usize) -> Option { 37 | self.0.checked_add(other).map(GuestAddress) 38 | } 39 | 40 | /// Returns the result of the base address + the size. 41 | /// Only use this when `offset` is guaranteed not to overflow. 42 | pub fn unchecked_add(&self, offset: usize) -> GuestAddress { 43 | GuestAddress(self.0 + offset) 44 | } 45 | 46 | /// Returns the result of the subtraction of None if there is underflow. 47 | pub fn checked_sub(&self, other: usize) -> Option { 48 | self.0.checked_sub(other).map(GuestAddress) 49 | } 50 | 51 | /// Returns the bitwise and of the address with the given mask. 52 | pub fn mask(&self, mask: u64) -> GuestAddress { 53 | GuestAddress(self.0 & mask as usize) 54 | } 55 | } 56 | 57 | impl BitAnd for GuestAddress { 58 | type Output = GuestAddress; 59 | 60 | fn bitand(self, other: u64) -> GuestAddress { 61 | GuestAddress(self.0 & other as usize) 62 | } 63 | } 64 | 65 | impl BitOr for GuestAddress { 66 | type Output = GuestAddress; 67 | 68 | fn bitor(self, other: u64) -> GuestAddress { 69 | GuestAddress(self.0 | other as usize) 70 | } 71 | } 72 | 73 | impl PartialEq for GuestAddress { 74 | fn eq(&self, other: &GuestAddress) -> bool { 75 | self.0 == other.0 76 | } 77 | } 78 | impl Eq for GuestAddress {} 79 | 80 | impl Ord for GuestAddress { 81 | fn cmp(&self, other: &GuestAddress) -> Ordering { 82 | self.0.cmp(&other.0) 83 | } 84 | } 85 | 86 | impl PartialOrd for GuestAddress { 87 | fn partial_cmp(&self, other: &GuestAddress) -> Option { 88 | Some(self.cmp(other)) 89 | } 90 | } 91 | 92 | #[cfg(test)] 93 | mod tests { 94 | use super::*; 95 | 96 | #[test] 97 | fn equals() { 98 | let a = GuestAddress(0x300); 99 | let b = GuestAddress(0x300); 100 | let c = GuestAddress(0x301); 101 | assert_eq!(a, b); 102 | assert_eq!(b, a); 103 | assert_ne!(a, c); 104 | assert_ne!(c, a); 105 | } 106 | 107 | #[test] 108 | fn cmp() { 109 | let a = GuestAddress(0x300); 110 | let b = GuestAddress(0x301); 111 | assert!(a < b); 112 | assert!(b > a); 113 | assert!(!(a < a)); 114 | } 115 | 116 | #[test] 117 | fn mask() { 118 | let a = GuestAddress(0x5050); 119 | assert_eq!(GuestAddress(0x5000), a & 0xff00u64); 120 | assert_eq!(GuestAddress(0x5055), a | 0x0005u64); 121 | } 122 | 123 | #[test] 124 | fn add_sub() { 125 | let a = GuestAddress(0x50); 126 | let b = GuestAddress(0x60); 127 | assert_eq!(Some(GuestAddress(0xb0)), a.checked_add(0x60)); 128 | assert_eq!(0x10, b.offset_from(a)); 129 | } 130 | 131 | #[test] 132 | fn checked_add_overflow() { 133 | let a = GuestAddress(0xffffffffffffff55); 134 | assert_eq!(Some(GuestAddress(0xffffffffffffff57)), a.checked_add(2)); 135 | assert!(a.checked_add(0xf0).is_none()); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/sys_util/src/handle_eintr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Macro and helper trait for handling interrupted routines. 6 | 7 | use std::io; 8 | 9 | use libc::EINTR; 10 | 11 | /// Trait for determining if a result indicates the operation was interrupted. 12 | pub trait InterruptibleResult { 13 | /// Returns `true` if this result indicates the operation was interrupted and should be retried, 14 | /// and `false` in all other cases. 15 | fn is_interrupted(&self) -> bool; 16 | } 17 | 18 | impl InterruptibleResult for i32 { 19 | fn is_interrupted(&self) -> bool { 20 | *self == -EINTR 21 | } 22 | } 23 | 24 | impl InterruptibleResult for ::Result { 25 | fn is_interrupted(&self) -> bool { 26 | match self { 27 | &Err(e) if e.errno() == -EINTR => true, 28 | _ => false, 29 | } 30 | } 31 | } 32 | 33 | impl InterruptibleResult for io::Result { 34 | fn is_interrupted(&self) -> bool { 35 | match self { 36 | &Err(ref e) if e.kind() == io::ErrorKind::Interrupted => true, 37 | _ => false, 38 | } 39 | } 40 | } 41 | 42 | /// Macro that retries the given expression every time its result indicates it was interrupted (i.e. 43 | /// returned `-EINTR`). This is useful for operations that are prone to being interrupted by 44 | /// signals, such as blocking syscalls. 45 | /// 46 | /// The given expression `$x` can return 47 | /// 48 | /// * `i32` in which case the expression is retried if equal to `-EINTR`. 49 | /// * `sys_util::Result` in which case the expression is retried if the `Error::errno()` is 50 | /// `-EINTR`. 51 | /// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is 52 | /// `ErrorKind::Interrupted`. 53 | /// 54 | /// In all cases where the result does not indicate that the expression was interrupted, the result 55 | /// is returned verbatim to the caller of this macro. 56 | /// 57 | /// See the section titled _Interruption of system calls and library functions by signal handlers_ 58 | /// on the man page for `signal(7)` to see more information about interruptible syscalls. 59 | /// 60 | /// To summarize, routines that use one of these syscalls _might_ need to handle `EINTR`: 61 | /// 62 | /// * `accept(2)` 63 | /// * `clock_nanosleep(2)` 64 | /// * `connect(2)` 65 | /// * `epoll_pwait(2)` 66 | /// * `epoll_wait(2)` 67 | /// * `fcntl(2)` 68 | /// * `fifo(7)` 69 | /// * `flock(2)` 70 | /// * `futex(2)` 71 | /// * `getrandom(2)` 72 | /// * `inotify(7)` 73 | /// * `io_getevents(2)` 74 | /// * `ioctl(2)` 75 | /// * `mq_receive(3)` 76 | /// * `mq_send(3)` 77 | /// * `mq_timedreceive(3)` 78 | /// * `mq_timedsend(3)` 79 | /// * `msgrcv(2)` 80 | /// * `msgsnd(2)` 81 | /// * `nanosleep(2)` 82 | /// * `open(2)` 83 | /// * `pause(2)` 84 | /// * `poll(2)` 85 | /// * `ppoll(2)` 86 | /// * `pselect(2)` 87 | /// * `pthread_cond_wait(3)` 88 | /// * `pthread_mutex_lock(3)` 89 | /// * `read(2)` 90 | /// * `readv(2)` 91 | /// * `recv(2)` 92 | /// * `recvfrom(2)` 93 | /// * `recvmmsg(2)` 94 | /// * `recvmsg(2)` 95 | /// * `select(2)` 96 | /// * `sem_timedwait(3)` 97 | /// * `sem_wait(3)` 98 | /// * `semop(2)` 99 | /// * `semtimedop(2)` 100 | /// * `send(2)` 101 | /// * `sendmsg(2)` 102 | /// * `sendto(2)` 103 | /// * `setsockopt(2)` 104 | /// * `sigsuspend(2)` 105 | /// * `sigtimedwait(2)` 106 | /// * `sigwaitinfo(2)` 107 | /// * `sleep(3)` 108 | /// * `usleep(3)` 109 | /// * `wait(2)` 110 | /// * `wait3(2)` 111 | /// * `wait4(2)` 112 | /// * `waitid(2)` 113 | /// * `waitpid(2)` 114 | /// * `write(2)` 115 | /// * `writev(2)` 116 | /// 117 | /// # Examples 118 | /// 119 | /// ``` 120 | /// # #[macro_use] extern crate sys_util; 121 | /// # use std::io::stdin; 122 | /// # fn main() { 123 | /// let mut line = String::new(); 124 | /// let res = handle_eintr!(stdin().read_line(&mut line)); 125 | /// # } 126 | /// ``` 127 | #[macro_export] 128 | macro_rules! handle_eintr { 129 | ($x:expr) => ( 130 | { 131 | use $crate::handle_eintr::InterruptibleResult; 132 | let res; 133 | loop { 134 | match $x { 135 | ref v if v.is_interrupted() => continue, 136 | v => { 137 | res = v; 138 | break; 139 | } 140 | } 141 | } 142 | res 143 | } 144 | ) 145 | } 146 | 147 | 148 | #[cfg(test)] 149 | mod tests { 150 | use super::*; 151 | 152 | use Error as SysError; 153 | 154 | #[test] 155 | fn i32_eintr() { 156 | let mut count = 3; 157 | { 158 | let mut dummy = || { 159 | count -= 1; 160 | if count > 0 { -EINTR } else { 56 } 161 | }; 162 | let res = handle_eintr!(dummy()); 163 | assert_eq!(res, 56); 164 | } 165 | assert_eq!(count, 0); 166 | } 167 | 168 | #[test] 169 | fn sys_eintr() { 170 | let mut count = 7; 171 | { 172 | let mut dummy = || { 173 | count -= 1; 174 | if count > 1 { 175 | Err(SysError::new(-EINTR)) 176 | } else { 177 | Ok(101) 178 | } 179 | }; 180 | let res = handle_eintr!(dummy()); 181 | assert_eq!(res, Ok(101)); 182 | } 183 | assert_eq!(count, 1); 184 | } 185 | 186 | #[test] 187 | fn io_eintr() { 188 | let mut count = 108; 189 | { 190 | let mut dummy = || { 191 | count -= 1; 192 | if count > 99 { 193 | Err(io::Error::new( 194 | io::ErrorKind::Interrupted, 195 | "interrupted again :(", 196 | )) 197 | } else { 198 | Ok(32) 199 | } 200 | }; 201 | let res = handle_eintr!(dummy()); 202 | assert_eq!(res.unwrap(), 32); 203 | } 204 | assert_eq!(count, 99); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/sys_util/src/ioctl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Macros and wrapper functions for dealing with ioctls. 6 | 7 | use std::os::raw::*; 8 | use std::os::unix::io::AsRawFd; 9 | 10 | use libc; 11 | 12 | /// Raw macro to declare a function that returns an ioctl number. 13 | #[macro_export] 14 | macro_rules! ioctl_ioc_nr { 15 | ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => ( 16 | #[allow(non_snake_case)] 17 | pub fn $name() -> ::std::os::raw::c_ulong { 18 | (($dir << $crate::ioctl::_IOC_DIRSHIFT) | 19 | ($ty << $crate::ioctl::_IOC_TYPESHIFT) | 20 | ($nr<< $crate::ioctl::_IOC_NRSHIFT) | 21 | ($size << $crate::ioctl::_IOC_SIZESHIFT)) as ::std::os::raw::c_ulong 22 | } 23 | ) 24 | } 25 | 26 | /// Declare an ioctl that transfers no data. 27 | #[macro_export] 28 | macro_rules! ioctl_io_nr { 29 | ($name:ident, $ty:expr, $nr:expr) => ( 30 | ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0); 31 | ) 32 | } 33 | 34 | /// Declare an ioctl that reads data. 35 | #[macro_export] 36 | macro_rules! ioctl_ior_nr { 37 | ($name:ident, $ty:expr, $nr:expr, $size:ty) => ( 38 | ioctl_ioc_nr!( 39 | $name, $crate::ioctl::_IOC_READ, $ty, $nr, ::std::mem::size_of::<$size>() as u32); 40 | ) 41 | } 42 | 43 | /// Declare an ioctl that writes data. 44 | #[macro_export] 45 | macro_rules! ioctl_iow_nr { 46 | ($name:ident, $ty:expr, $nr:expr, $size:ty) => ( 47 | ioctl_ioc_nr!( 48 | $name, $crate::ioctl::_IOC_WRITE, $ty, $nr, ::std::mem::size_of::<$size>() as u32); 49 | ) 50 | } 51 | 52 | /// Declare an ioctl that reads and writes data. 53 | #[macro_export] 54 | macro_rules! ioctl_iowr_nr { 55 | ($name:ident, $ty:expr, $nr:expr, $size:ty) => ( 56 | ioctl_ioc_nr!( 57 | $name, $crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE, $ty, $nr, 58 | ::std::mem::size_of::<$size>() as u32); 59 | ) 60 | } 61 | 62 | pub const _IOC_NRBITS: c_uint = 8; 63 | pub const _IOC_TYPEBITS: c_uint = 8; 64 | pub const _IOC_SIZEBITS: c_uint = 14; 65 | pub const _IOC_DIRBITS: c_uint = 2; 66 | pub const _IOC_NRMASK: c_uint = 255; 67 | pub const _IOC_TYPEMASK: c_uint = 255; 68 | pub const _IOC_SIZEMASK: c_uint = 16383; 69 | pub const _IOC_DIRMASK: c_uint = 3; 70 | pub const _IOC_NRSHIFT: c_uint = 0; 71 | pub const _IOC_TYPESHIFT: c_uint = 8; 72 | pub const _IOC_SIZESHIFT: c_uint = 16; 73 | pub const _IOC_DIRSHIFT: c_uint = 30; 74 | pub const _IOC_NONE: c_uint = 0; 75 | pub const _IOC_WRITE: c_uint = 1; 76 | pub const _IOC_READ: c_uint = 2; 77 | pub const IOC_IN: c_uint = 1073741824; 78 | pub const IOC_OUT: c_uint = 2147483648; 79 | pub const IOC_INOUT: c_uint = 3221225472; 80 | pub const IOCSIZE_MASK: c_uint = 1073676288; 81 | pub const IOCSIZE_SHIFT: c_uint = 16; 82 | 83 | /// Run an ioctl with no arguments. 84 | pub unsafe fn ioctl(fd: &F, nr: c_ulong) -> c_int { 85 | libc::ioctl(fd.as_raw_fd(), nr, 0) 86 | } 87 | 88 | /// Run an ioctl with a single value argument. 89 | pub unsafe fn ioctl_with_val(fd: &F, nr: c_ulong, arg: c_ulong) -> c_int { 90 | libc::ioctl(fd.as_raw_fd(), nr, arg) 91 | } 92 | 93 | /// Run an ioctl with an immutable reference. 94 | pub unsafe fn ioctl_with_ref(fd: &F, nr: c_ulong, arg: &T) -> c_int { 95 | libc::ioctl(fd.as_raw_fd(), nr, arg as *const T as *const c_void) 96 | } 97 | 98 | /// Run an ioctl with a mutable reference. 99 | pub unsafe fn ioctl_with_mut_ref(fd: &F, nr: c_ulong, arg: &mut T) -> c_int { 100 | libc::ioctl(fd.as_raw_fd(), nr, arg as *mut T as *mut c_void) 101 | } 102 | 103 | /// Run an ioctl with a raw pointer. 104 | pub unsafe fn ioctl_with_ptr(fd: &F, nr: c_ulong, arg: *const T) -> c_int { 105 | libc::ioctl(fd.as_raw_fd(), nr, arg as *const c_void) 106 | } 107 | 108 | /// Run an ioctl with a mutable raw pointer. 109 | pub unsafe fn ioctl_with_mut_ptr(fd: &F, nr: c_ulong, arg: *mut T) -> c_int { 110 | libc::ioctl(fd.as_raw_fd(), nr, arg as *mut c_void) 111 | } 112 | 113 | #[cfg(test)] 114 | mod tests { 115 | const TUNTAP: ::std::os::raw::c_uint = 0x54; 116 | const VHOST: ::std::os::raw::c_uint = 0xaf; 117 | 118 | ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01); 119 | ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint); 120 | ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int); 121 | ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, ::std::os::raw::c_int); 122 | 123 | #[test] 124 | fn ioctl_macros() { 125 | assert_eq!(0x0000af01, VHOST_SET_OWNER()); 126 | assert_eq!(0x800454cf, TUNGETFEATURES()); 127 | assert_eq!(0x400454d9, TUNSETQUEUE()); 128 | assert_eq!(0xc004af12, VHOST_GET_VRING_BASE()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/sys_util/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Small system utility modules for usage by other modules. 6 | 7 | extern crate data_model; 8 | extern crate libc; 9 | extern crate syscall_defines; 10 | 11 | #[macro_use] 12 | pub mod handle_eintr; 13 | #[macro_use] 14 | pub mod ioctl; 15 | #[macro_use] 16 | pub mod syslog; 17 | mod mmap; 18 | mod shm; 19 | mod eventfd; 20 | mod errno; 21 | mod guest_address; 22 | mod guest_memory; 23 | mod poll; 24 | mod struct_util; 25 | mod tempdir; 26 | mod terminal; 27 | mod signal; 28 | mod fork; 29 | mod signalfd; 30 | mod sock_ctrl_msg; 31 | mod passwd; 32 | 33 | pub use mmap::*; 34 | pub use shm::*; 35 | pub use eventfd::*; 36 | pub use errno::{Error, Result}; 37 | pub use errno::errno_result; 38 | pub use guest_address::*; 39 | pub use guest_memory::*; 40 | pub use poll::*; 41 | pub use struct_util::*; 42 | pub use tempdir::*; 43 | pub use terminal::*; 44 | pub use signal::*; 45 | pub use fork::*; 46 | pub use signalfd::*; 47 | pub use ioctl::*; 48 | pub use sock_ctrl_msg::*; 49 | pub use passwd::*; 50 | 51 | pub use mmap::Error as MmapError; 52 | pub use guest_memory::Error as GuestMemoryError; 53 | pub use signalfd::Error as SignalFdError; 54 | 55 | use std::ffi::CStr; 56 | use std::ptr; 57 | 58 | use libc::{kill, syscall, waitpid, c_long, pid_t, uid_t, gid_t, SIGKILL, WNOHANG}; 59 | 60 | use syscall_defines::linux::LinuxSyscall::SYS_getpid; 61 | 62 | /// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used 63 | /// elsewhere. 64 | #[inline(always)] 65 | pub fn getpid() -> pid_t { 66 | // Safe because this syscall can never fail and we give it a valid syscall number. 67 | unsafe { syscall(SYS_getpid as c_long) as pid_t } 68 | } 69 | 70 | /// Safe wrapper for `geteuid(2)`. 71 | #[inline(always)] 72 | pub fn geteuid() -> uid_t { 73 | // trivially safe 74 | unsafe { libc::geteuid() } 75 | } 76 | 77 | /// Safe wrapper for `getegid(2)`. 78 | #[inline(always)] 79 | pub fn getegid() -> gid_t { 80 | // trivially safe 81 | unsafe { libc::getegid() } 82 | } 83 | 84 | /// Safe wrapper for chown(2). 85 | #[inline(always)] 86 | pub fn chown(path: &CStr, uid: uid_t, gid: gid_t) -> Result<()> { 87 | // Safe since we pass in a valid string pointer and check the return value. 88 | let ret = unsafe { libc::chown(path.as_ptr(), uid, gid) }; 89 | 90 | if ret < 0 { errno_result() } else { Ok(()) } 91 | } 92 | 93 | /// Reaps a child process that has terminated. 94 | /// 95 | /// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children 96 | /// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap. 97 | /// 98 | /// # Examples 99 | /// 100 | /// Reaps all child processes until there are no terminated children to reap. 101 | /// 102 | /// ``` 103 | /// # extern crate libc; 104 | /// # extern crate sys_util; 105 | /// fn reap_children() { 106 | /// loop { 107 | /// match sys_util::reap_child() { 108 | /// Ok(0) => println!("no children ready to reap"), 109 | /// Ok(pid) => { 110 | /// println!("reaped {}", pid); 111 | /// continue 112 | /// }, 113 | /// Err(e) if e.errno() == libc::ECHILD => println!("no children left"), 114 | /// Err(e) => println!("error reaping children: {:?}", e), 115 | /// } 116 | /// break 117 | /// } 118 | /// } 119 | /// ``` 120 | pub fn reap_child() -> Result { 121 | // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error. 122 | let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) }; 123 | if ret == -1 { errno_result() } else { Ok(ret) } 124 | } 125 | 126 | /// Kill all processes in the current process group. 127 | /// 128 | /// On success, this kills all processes in the current process group, including the current 129 | /// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`. 130 | pub fn kill_process_group() -> Result<()> { 131 | let ret = unsafe { kill(0, SIGKILL) }; 132 | if ret == -1 { 133 | errno_result() 134 | } else { 135 | // Kill succeeded, so this process never reaches here. 136 | unreachable!(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/sys_util/src/passwd.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Wrappers for passwd and group file access. 6 | 7 | use std::ffi::CStr; 8 | use std::mem; 9 | use std::ptr; 10 | 11 | use libc; 12 | use libc::{c_char, gid_t, uid_t, getgrnam_r, getpwnam_r}; 13 | 14 | use {Result, errno_result}; 15 | 16 | /// Safe wrapper for getting a uid from a user name with `getpwnam_r(3)`. 17 | #[inline(always)] 18 | pub fn get_user_id(user_name: &CStr) -> Result { 19 | // libc::passwd is a C struct and can be safely initialized with zeroed memory. 20 | let mut passwd: libc::passwd = unsafe { mem::zeroed() }; 21 | let mut passwd_result: *mut libc::passwd = ptr::null_mut(); 22 | let mut buf = [0 as c_char; 256]; 23 | 24 | // For thread-safety, use the reentrant version of this function. This allows us to give it a 25 | // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return 26 | // value of this doesn't really need to be checked, since the extra result pointer that is 27 | // passed in indicates whether or not the function succeeded. 28 | // 29 | // This call is safe as long as it behaves as described in the man page. We pass in valid 30 | // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct. 31 | unsafe { 32 | handle_eintr!(getpwnam_r( 33 | user_name.as_ptr(), 34 | &mut passwd, 35 | buf.as_mut_ptr(), 36 | buf.len(), 37 | &mut passwd_result, 38 | )) 39 | }; 40 | 41 | if passwd_result.is_null() { 42 | errno_result() 43 | } else { 44 | Ok(passwd.pw_uid) 45 | } 46 | } 47 | 48 | /// Safe wrapper for getting a gid from a group name with `getgrnam_r(3)`. 49 | #[inline(always)] 50 | pub fn get_group_id(group_name: &CStr) -> Result { 51 | // libc::group is a C struct and can be safely initialized with zeroed memory. 52 | let mut group: libc::group = unsafe { mem::zeroed() }; 53 | let mut group_result: *mut libc::group = ptr::null_mut(); 54 | let mut buf = [0 as c_char; 256]; 55 | 56 | // For thread-safety, use the reentrant version of this function. This allows us to give it a 57 | // buffer on the stack (instead of a global buffer). Unlike most libc functions, the return 58 | // value of this doesn't really need to be checked, since the extra result pointer that is 59 | // passed in indicates whether or not the function succeeded. 60 | // 61 | // This call is safe as long as it behaves as described in the man page. We pass in valid 62 | // pointers to stack-allocated buffers, and the length check for the scratch buffer is correct. 63 | unsafe { 64 | handle_eintr!(getgrnam_r( 65 | group_name.as_ptr(), 66 | &mut group, 67 | buf.as_mut_ptr(), 68 | buf.len(), 69 | &mut group_result, 70 | )) 71 | }; 72 | 73 | if group_result.is_null() { 74 | errno_result() 75 | } else { 76 | Ok(group.gr_gid) 77 | } 78 | } 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use super::*; 83 | 84 | // The build environment is configured as statically linking. 85 | // Tests below would panic. Ignore them. 86 | 87 | #[ignore] 88 | #[test] 89 | fn get_good_uid() { 90 | let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap(); 91 | 92 | // root's uid should always exist, and should be 0. 93 | let root_uid = get_user_id(root_name).unwrap(); 94 | assert_eq!(root_uid, 0); 95 | } 96 | 97 | #[ignore] 98 | #[test] 99 | fn get_bad_uid() { 100 | let bad_name = CStr::from_bytes_with_nul(b"this better not be a user\0").unwrap(); 101 | 102 | // This user should give us an error. As a cruel joke, the getpwnam(3) man page allows 103 | // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a user isn't found. So 104 | // instead of checking which error we got, just see that we did get one. 105 | let bad_uid_result = get_user_id(bad_name); 106 | assert!(bad_uid_result.is_err()); 107 | } 108 | 109 | #[ignore] 110 | #[test] 111 | fn get_good_gid() { 112 | let root_name = CStr::from_bytes_with_nul(b"root\0").unwrap(); 113 | 114 | // root's gid should always exist, and should be 0. 115 | let root_gid = get_group_id(root_name).unwrap(); 116 | assert_eq!(root_gid, 0); 117 | } 118 | 119 | #[ignore] 120 | #[test] 121 | fn get_bad_gid() { 122 | let bad_name = CStr::from_bytes_with_nul(b"this better not be a group\0").unwrap(); 123 | 124 | // This group should give us an error. As a cruel joke, the getgrnam(3) man page allows 125 | // ENOENT, ESRCH, EBADF, EPERM, or even 0 to be set in errno if a group isn't found. So 126 | // instead of checking which error we got, just see that we did get one. 127 | let bad_gid_result = get_group_id(bad_name); 128 | assert!(bad_gid_result.is_err()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/sys_util/src/poll.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::os::unix::io::{AsRawFd, RawFd}; 6 | use std::os::unix::net::{UnixDatagram, UnixStream}; 7 | 8 | use libc::{nfds_t, pollfd, poll, POLLIN}; 9 | 10 | use {Result, errno_result}; 11 | 12 | /// Trait for file descriptors that can be polled for input. 13 | /// 14 | /// This is marked unsafe because the implementation must promise that the returned RawFd is valid 15 | /// for polling purposes and that the lifetime of the returned fd is at least that of the trait 16 | /// object. 17 | pub unsafe trait Pollable { 18 | /// Gets the file descriptor that can be polled for input. 19 | fn pollable_fd(&self) -> RawFd; 20 | } 21 | 22 | unsafe impl Pollable for UnixStream { 23 | fn pollable_fd(&self) -> RawFd { 24 | self.as_raw_fd() 25 | } 26 | } 27 | 28 | unsafe impl Pollable for UnixDatagram { 29 | fn pollable_fd(&self) -> RawFd { 30 | self.as_raw_fd() 31 | } 32 | } 33 | 34 | /// Used to poll multiple `Pollable` objects at once. 35 | /// 36 | /// # Example 37 | /// 38 | /// ``` 39 | /// # use sys_util::{Result, EventFd, Poller, Pollable}; 40 | /// # fn test() -> Result<()> { 41 | /// let evt1 = EventFd::new()?; 42 | /// let evt2 = EventFd::new()?; 43 | /// evt2.write(1)?; 44 | /// 45 | /// let pollables: Vec<(u32, &Pollable)> = vec![(1, &evt1), (2, &evt2)]; 46 | /// 47 | /// let mut poller = Poller::new(2); 48 | /// assert_eq!(poller.poll(&pollables[..]), Ok([2].as_ref())); 49 | /// # Ok(()) 50 | /// # } 51 | /// ``` 52 | pub struct Poller { 53 | pollfds: Vec, 54 | tokens: Vec, 55 | } 56 | 57 | impl Poller { 58 | /// Constructs a new poller object with the given `capacity` of Pollable objects pre-allocated. 59 | pub fn new(capacity: usize) -> Poller { 60 | Poller { 61 | pollfds: Vec::with_capacity(capacity), 62 | tokens: Vec::with_capacity(capacity), 63 | } 64 | } 65 | 66 | /// Waits for any of the given slice of `token`-`Pollable` tuples to be readable without 67 | /// blocking and returns the `token` of each that is readable. 68 | /// 69 | /// This is guaranteed to not allocate if `pollables.len()` is less than the `capacity` given in 70 | /// `Poller::new`. 71 | pub fn poll(&mut self, pollables: &[(u32, &dyn Pollable)]) -> Result<&[u32]> { 72 | self.pollfds.clear(); 73 | for pollable in pollables.iter() { 74 | self.pollfds.push(pollfd { 75 | fd: pollable.1.pollable_fd(), 76 | events: POLLIN, 77 | revents: 0, 78 | }); 79 | } 80 | 81 | // Safe because poll is given the correct length of properly initialized pollfds, and we 82 | // check the return result. 83 | let ret = unsafe { 84 | handle_eintr!(poll( 85 | self.pollfds.as_mut_ptr(), 86 | self.pollfds.len() as nfds_t, 87 | -1, 88 | )) 89 | }; 90 | if ret < 0 { 91 | return errno_result(); 92 | } 93 | 94 | self.tokens.clear(); 95 | for (pollfd, pollable) in self.pollfds.iter().zip(pollables.iter()) { 96 | if (pollfd.revents & POLLIN) != 0 { 97 | self.tokens.push(pollable.0); 98 | } 99 | } 100 | 101 | Ok(&self.tokens) 102 | } 103 | } 104 | 105 | #[cfg(test)] 106 | mod tests { 107 | use super::*; 108 | use EventFd; 109 | 110 | #[test] 111 | fn poller() { 112 | let evt1 = EventFd::new().unwrap(); 113 | let evt2 = EventFd::new().unwrap(); 114 | evt2.write(1).unwrap(); 115 | 116 | let pollables: Vec<(u32, &dyn Pollable)> = vec![(1, &evt1), (2, &evt2)]; 117 | 118 | let mut poller = Poller::new(2); 119 | assert_eq!(poller.poll(&pollables[..]), Ok([2].as_ref())); 120 | } 121 | 122 | #[test] 123 | fn poller_multi() { 124 | let evt1 = EventFd::new().unwrap(); 125 | let evt2 = EventFd::new().unwrap(); 126 | evt1.write(1).unwrap(); 127 | evt2.write(1).unwrap(); 128 | 129 | let pollables: Vec<(u32, &dyn Pollable)> = vec![(1, &evt1), (2, &evt2)]; 130 | 131 | let mut poller = Poller::new(2); 132 | assert_eq!(poller.poll(&pollables[..]), Ok([1, 2].as_ref())); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/sys_util/src/shm.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::ffi::CStr; 6 | use std::fs::File; 7 | use std::io::{Seek, SeekFrom}; 8 | use std::os::unix::io::{AsRawFd, IntoRawFd, FromRawFd, RawFd}; 9 | 10 | use libc::{off64_t, c_long, c_int, c_uint, c_char, syscall, ftruncate64}; 11 | 12 | use syscall_defines::linux::LinuxSyscall::SYS_memfd_create; 13 | 14 | use {Result, errno_result}; 15 | 16 | /// A shared memory file descriptor and its size. 17 | pub struct SharedMemory { 18 | fd: File, 19 | size: u64, 20 | } 21 | 22 | // from 23 | const MFD_CLOEXEC: c_uint = 0x0001; 24 | 25 | unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int { 26 | syscall(SYS_memfd_create as c_long, name as i64, flags as i64) as c_int 27 | } 28 | 29 | impl SharedMemory { 30 | /// Creates a new shared memory file descriptor with zero size. 31 | /// 32 | /// If a name is given, it will appear in `/proc/self/fd/` for the purposes of 33 | /// debugging. The name does not need to be unique. 34 | /// 35 | /// The file descriptor is opened with the close on exec flag. 36 | pub fn new(name: Option<&CStr>) -> Result { 37 | let shm_name = name.map(|n| n.as_ptr()).unwrap_or( 38 | b"/crosvm_shm\0".as_ptr() as 39 | *const c_char, 40 | ); 41 | // The following are safe because we give a valid C string and check the 42 | // results of the memfd_create call. 43 | let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC) }; 44 | if fd < 0 { 45 | return errno_result(); 46 | } 47 | 48 | let file = unsafe { File::from_raw_fd(fd) }; 49 | 50 | Ok(SharedMemory { fd: file, size: 0 }) 51 | } 52 | 53 | /// Constructs a `SharedMemory` instance from a file descriptor that represents shared memory. 54 | /// 55 | /// The size of the resulting shared memory will be determined using `File::seek`. If the given 56 | /// file's size can not be determined this way, this will return an error. 57 | pub fn from_raw_fd(fd: T) -> Result { 58 | // Safe because the IntoRawFd trait indicates fd has unique ownership. 59 | let mut file = unsafe { File::from_raw_fd(fd.into_raw_fd()) }; 60 | let file_size = file.seek(SeekFrom::End(0))?; 61 | Ok(SharedMemory { 62 | fd: file, 63 | size: file_size as u64, 64 | }) 65 | } 66 | 67 | /// Gets the size in bytes of the shared memory. 68 | /// 69 | /// The size returned here does not reflect changes by other interfaces or users of the shared 70 | /// memory file descriptor.. 71 | pub fn size(&self) -> u64 { 72 | self.size 73 | } 74 | 75 | /// Sets the size in bytes of the shared memory. 76 | /// 77 | /// Note that if some process has already mapped this shared memory and the new size is smaller, 78 | /// that process may get signaled with SIGBUS if they access any page past the new size. 79 | pub fn set_size(&mut self, size: u64) -> Result<()> { 80 | let ret = unsafe { ftruncate64(self.fd.as_raw_fd(), size as off64_t) }; 81 | if ret < 0 { 82 | return errno_result(); 83 | } 84 | self.size = size; 85 | Ok(()) 86 | } 87 | } 88 | 89 | impl AsRawFd for SharedMemory { 90 | fn as_raw_fd(&self) -> RawFd { 91 | self.fd.as_raw_fd() 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod tests { 97 | use super::*; 98 | 99 | use std::ffi::CString; 100 | use std::fs::read_link; 101 | use std::io::repeat; 102 | 103 | use data_model::VolatileMemory; 104 | 105 | use MemoryMapping; 106 | 107 | #[test] 108 | fn new() { 109 | let shm = SharedMemory::new(None).expect("failed to create shared memory"); 110 | assert_eq!(shm.size(), 0); 111 | } 112 | 113 | #[test] 114 | fn new_sized() { 115 | let mut shm = SharedMemory::new(None).expect("failed to create shared memory"); 116 | shm.set_size(1024).expect( 117 | "failed to set shared memory size", 118 | ); 119 | assert_eq!(shm.size(), 1024); 120 | } 121 | 122 | #[test] 123 | fn new_huge() { 124 | let mut shm = SharedMemory::new(None).expect("failed to create shared memory"); 125 | shm.set_size(0x7fff_ffff_ffff_ffff).expect( 126 | "failed to set shared memory size", 127 | ); 128 | assert_eq!(shm.size(), 0x7fff_ffff_ffff_ffff); 129 | } 130 | 131 | #[test] 132 | fn new_too_huge() { 133 | let mut shm = SharedMemory::new(None).expect("failed to create shared memory"); 134 | shm.set_size(0x8000_0000_0000_0000).unwrap_err(); 135 | assert_eq!(shm.size(), 0); 136 | } 137 | 138 | #[test] 139 | fn new_named() { 140 | let name = "very unique name"; 141 | let cname = CString::new(name).unwrap(); 142 | let shm = SharedMemory::new(Some(&cname)).expect("failed to create shared memory"); 143 | let fd_path = format!("/proc/self/fd/{}", shm.as_raw_fd()); 144 | let link_name = 145 | read_link(fd_path).expect("failed to read link of shared memory /proc/self/fd entry"); 146 | assert!(link_name.to_str().unwrap().contains(name)); 147 | } 148 | 149 | #[test] 150 | fn mmap_page() { 151 | let mut shm = SharedMemory::new(None).expect("failed to create shared memory"); 152 | shm.set_size(4096).expect( 153 | "failed to set shared memory size", 154 | ); 155 | 156 | let mmap1 = 157 | MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory"); 158 | let mmap2 = 159 | MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory"); 160 | 161 | assert_ne!( 162 | mmap1.get_slice(0, 1).unwrap().as_ptr(), 163 | mmap2.get_slice(0, 1).unwrap().as_ptr() 164 | ); 165 | 166 | mmap1 167 | .get_slice(0, 4096) 168 | .expect("failed to get mmap slice") 169 | .read_from(&mut repeat(0x45)) 170 | .expect("failed to fill mmap slice"); 171 | 172 | for i in 0..4096 { 173 | assert_eq!(mmap2.get_ref::(i).unwrap().load(), 0x45u8); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/sys_util/src/signal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use libc::{c_int, pthread_t, signal, pthread_kill, SIG_ERR, EINVAL}; 6 | 7 | use std::thread::JoinHandle; 8 | use std::os::unix::thread::JoinHandleExt; 9 | 10 | use {Error, Result, errno_result}; 11 | 12 | #[link(name = "c")] 13 | extern "C" { 14 | fn __libc_current_sigrtmin() -> c_int; 15 | fn __libc_current_sigrtmax() -> c_int; 16 | } 17 | 18 | /// Returns the minimum (inclusive) real-time signal number. 19 | #[allow(non_snake_case)] 20 | pub fn SIGRTMIN() -> c_int { 21 | unsafe { __libc_current_sigrtmin() } 22 | } 23 | 24 | /// Returns the maximum (inclusive) real-time signal number. 25 | #[allow(non_snake_case)] 26 | pub fn SIGRTMAX() -> c_int { 27 | unsafe { __libc_current_sigrtmax() } 28 | } 29 | 30 | fn valid_signal_num(num: u8) -> bool { 31 | (num as c_int) + SIGRTMIN() <= SIGRTMAX() 32 | } 33 | 34 | /// Registers `handler` as the signal handler of signum `num + SIGRTMIN`. 35 | /// 36 | /// The value of `num + SIGRTMIN` must not exceed `SIGRTMAX`. 37 | /// 38 | /// This is considered unsafe because the given handler will be called asynchronously, interrupting 39 | /// whatever the thread was doing and therefore must only do async-signal-safe operations. 40 | pub unsafe fn register_signal_handler(num: u8, handler: extern "C" fn() -> ()) -> Result<()> { 41 | if !valid_signal_num(num) { 42 | return Err(Error::new(EINVAL)); 43 | } 44 | let ret = signal((num as i32) + SIGRTMIN(), handler as *const () as usize); 45 | if ret == SIG_ERR { 46 | return errno_result(); 47 | } 48 | 49 | Ok(()) 50 | } 51 | 52 | /// Trait for threads that can be signalled via `pthread_kill`. 53 | /// 54 | /// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are 55 | /// guaranteed to not be used by the C runtime. 56 | /// 57 | /// This is marked unsafe because the implementation of this trait must guarantee that the returned 58 | /// pthread_t is valid and has a lifetime at least that of the trait object. 59 | pub unsafe trait Killable { 60 | fn pthread_handle(&self) -> pthread_t; 61 | 62 | /// Sends the signal `num + SIGRTMIN` to this killable thread. 63 | /// 64 | /// The value of `num + SIGRTMIN` must not exceed `SIGRTMAX`. 65 | fn kill(&self, num: u8) -> Result<()> { 66 | if !valid_signal_num(num) { 67 | return Err(Error::new(EINVAL)); 68 | } 69 | 70 | // Safe because we ensure we are using a valid pthread handle, a valid signal number, and 71 | // check the return result. 72 | let ret = unsafe { pthread_kill(self.pthread_handle(), (num as i32) + SIGRTMIN()) }; 73 | if ret < 0 { 74 | return errno_result(); 75 | } 76 | Ok(()) 77 | } 78 | } 79 | 80 | // Safe because we fulfill our contract of returning a genuine pthread handle. 81 | unsafe impl Killable for JoinHandle { 82 | fn pthread_handle(&self) -> pthread_t { 83 | self.as_pthread_t() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/sys_util/src/struct_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std; 6 | use std::io::Read; 7 | use std::mem; 8 | 9 | #[derive(Debug)] 10 | pub enum Error { 11 | ReadStruct, 12 | } 13 | pub type Result = std::result::Result; 14 | 15 | /// Reads a struct from an input buffer. 16 | /// This is unsafe because the struct is initialized to unverified data read from the input. 17 | /// `read_struct` should only be called to fill plain old data structs. It is not endian safe. 18 | /// 19 | /// # Arguments 20 | /// 21 | /// * `f` - The input to read from. Often this is a file. 22 | /// * `out` - The struct to fill with data read from `f`. 23 | pub unsafe fn read_struct(f: &mut F, out: &mut T) -> Result<()> { 24 | let out_slice = std::slice::from_raw_parts_mut(out as *mut T as *mut u8, mem::size_of::()); 25 | f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?; 26 | Ok(()) 27 | } 28 | 29 | /// Reads an array of structs from an input buffer. Returns a Vec of structs initialized with data 30 | /// from the specified input. 31 | /// This is unsafe because the structs are initialized to unverified data read from the input. 32 | /// `read_struct_slice` should only be called for plain old data structs. It is not endian safe. 33 | /// 34 | /// # Arguments 35 | /// 36 | /// * `f` - The input to read from. Often this is a file. 37 | /// * `len` - The number of structs to fill with data read from `f`. 38 | pub unsafe fn read_struct_slice(f: &mut F, len: usize) -> Result> { 39 | let mut out: Vec = Vec::with_capacity(len); 40 | out.set_len(len); 41 | let out_slice = std::slice::from_raw_parts_mut( 42 | out.as_ptr() as *mut T as *mut u8, 43 | mem::size_of::() * len, 44 | ); 45 | f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?; 46 | Ok(out) 47 | } 48 | 49 | #[cfg(test)] 50 | mod test { 51 | use std::io::Cursor; 52 | use std::mem; 53 | use super::*; 54 | 55 | #[derive(Clone, Copy, Debug, Default, PartialEq)] 56 | struct TestRead { 57 | a: u64, 58 | b: u8, 59 | c: u8, 60 | d: u8, 61 | e: u8, 62 | } 63 | 64 | #[test] 65 | fn struct_basic_read() { 66 | let orig = TestRead { 67 | a: 0x7766554433221100, 68 | b: 0x88, 69 | c: 0x99, 70 | d: 0xaa, 71 | e: 0xbb, 72 | }; 73 | let source = unsafe { 74 | // Don't worry it's a test 75 | std::slice::from_raw_parts( 76 | &orig as *const _ as *const u8, 77 | std::mem::size_of::(), 78 | ) 79 | }; 80 | assert_eq!(mem::size_of::(), mem::size_of_val(&source)); 81 | let mut tr: TestRead = Default::default(); 82 | unsafe { 83 | read_struct(&mut Cursor::new(source), &mut tr).unwrap(); 84 | } 85 | assert_eq!(orig, tr); 86 | } 87 | 88 | #[test] 89 | fn struct_read_past_end() { 90 | let orig = TestRead { 91 | a: 0x7766554433221100, 92 | b: 0x88, 93 | c: 0x99, 94 | d: 0xaa, 95 | e: 0xbb, 96 | }; 97 | let source = unsafe { 98 | // Don't worry it's a test 99 | std::slice::from_raw_parts( 100 | &orig as *const _ as *const u8, 101 | std::mem::size_of::() - 1, 102 | ) 103 | }; 104 | let mut tr: TestRead = Default::default(); 105 | unsafe { 106 | assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err()); 107 | } 108 | } 109 | 110 | #[test] 111 | fn struct_slice_read() { 112 | let orig = vec![ 113 | TestRead { 114 | a: 0x7766554433221100, 115 | b: 0x88, 116 | c: 0x99, 117 | d: 0xaa, 118 | e: 0xbb, 119 | }, 120 | TestRead { 121 | a: 0x7867564534231201, 122 | b: 0x02, 123 | c: 0x13, 124 | d: 0x24, 125 | e: 0x35, 126 | }, 127 | TestRead { 128 | a: 0x7a69584736251403, 129 | b: 0x04, 130 | c: 0x15, 131 | d: 0x26, 132 | e: 0x37, 133 | }, 134 | ]; 135 | let source = unsafe { 136 | // Don't worry it's a test 137 | std::slice::from_raw_parts( 138 | orig.as_ptr() as *const u8, 139 | std::mem::size_of::() * 3, 140 | ) 141 | }; 142 | 143 | let tr: Vec = unsafe { read_struct_slice(&mut Cursor::new(source), 3).unwrap() }; 144 | assert_eq!(orig, tr); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/sys_util/src/tempdir.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::ffi::CString; 6 | use std::ffi::OsStr; 7 | use std::ffi::OsString; 8 | use std::fs; 9 | use std::os::unix::ffi::OsStringExt; 10 | use std::path::Path; 11 | use std::path::PathBuf; 12 | 13 | use libc; 14 | 15 | use {Result, errno_result}; 16 | 17 | /// Create and remove a temporary directory. The directory will be maintained for the lifetime of 18 | /// the `TempDir` object. 19 | pub struct TempDir { 20 | path: Option, 21 | } 22 | 23 | impl TempDir { 24 | /// Creates a new tempory directory. 25 | /// The directory will be removed when the object goes out of scope. 26 | /// 27 | /// # Examples 28 | /// 29 | /// ``` 30 | /// # use std::path::Path; 31 | /// # use std::path::PathBuf; 32 | /// # use sys_util::TempDir; 33 | /// # fn test_create_temp_dir() -> Result<(), ()> { 34 | /// let t = TempDir::new("/tmp/testdir").map_err(|_| ())?; 35 | /// assert!(t.as_path().unwrap().exists()); 36 | /// # Ok(()) 37 | /// # } 38 | /// ``` 39 | pub fn new>(prefix: P) -> Result { 40 | let mut dir_string = prefix.as_ref().to_os_string(); 41 | dir_string.push("XXXXXX"); 42 | // unwrap this result as the internal bytes can't have a null with a valid path. 43 | let dir_name = CString::new(dir_string.into_vec()).unwrap(); 44 | let mut dir_bytes = dir_name.into_bytes_with_nul(); 45 | let ret = unsafe { 46 | // Creating the directory isn't unsafe. The fact that it modifies the guts of the path 47 | // is also OK because it only overwrites the last 6 Xs added above. 48 | libc::mkdtemp(dir_bytes.as_mut_ptr() as *mut libc::c_char) 49 | }; 50 | if ret.is_null() { 51 | return errno_result(); 52 | } 53 | dir_bytes.pop(); // Remove the null becasue from_vec can't handle it. 54 | Ok(TempDir { 55 | path: Some(PathBuf::from(OsString::from_vec(dir_bytes))), 56 | }) 57 | } 58 | 59 | /// Removes the temporary directory. Calling this is optional as dropping a `TempDir` object 60 | /// will also remove the directory. Calling remove explicitly allows for better error handling. 61 | pub fn remove(mut self) -> Result<()> { 62 | let path = self.path.take(); 63 | path.map_or(Ok(()), |ref p| fs::remove_dir_all(p))?; 64 | Ok(()) 65 | } 66 | 67 | /// Returns the path to the tempdir if it is currently valid 68 | pub fn as_path(&self) -> Option<&Path> { 69 | self.path.as_ref().map(|ref p| p.as_path()) 70 | } 71 | } 72 | 73 | impl Drop for TempDir { 74 | fn drop(&mut self) { 75 | if let Some(ref p) = self.path { 76 | // Nothing can be done here if this returns an error. 77 | let _ = fs::remove_dir_all(p); 78 | } 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use super::*; 85 | 86 | #[test] 87 | fn create_dir() { 88 | let t = TempDir::new("/tmp/asdf").unwrap(); 89 | let path = t.as_path().unwrap(); 90 | assert!(path.exists()); 91 | assert!(path.is_dir()); 92 | assert!(path.starts_with("/tmp/")); 93 | } 94 | 95 | #[test] 96 | fn remove_dir() { 97 | let t = TempDir::new("/tmp/asdf").unwrap(); 98 | let path = t.as_path().unwrap().to_owned(); 99 | assert!(t.remove().is_ok()); 100 | assert!(!path.exists()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/sys_util/src/terminal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::mem::zeroed; 6 | use std::io::StdinLock; 7 | use std::os::unix::io::RawFd; 8 | 9 | use libc::{fcntl, tcgetattr, tcsetattr, isatty, read, c_int, termios, STDIN_FILENO, TCSANOW, 10 | ICANON, ECHO, ISIG, O_NONBLOCK, F_GETFL, F_SETFL}; 11 | 12 | use {Result, errno_result}; 13 | 14 | fn modify_mode(fd: RawFd, f: F) -> Result<()> { 15 | // Safe because we check the return value of isatty. 16 | if unsafe { isatty(fd) } != 1 { 17 | return Ok(()); 18 | } 19 | 20 | // The following pair are safe because termios gets totally overwritten by tcgetattr and we 21 | // check the return result. 22 | let mut termios: termios = unsafe { zeroed() }; 23 | let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) }; 24 | if ret < 0 { 25 | return errno_result(); 26 | } 27 | let mut new_termios = termios; 28 | f(&mut new_termios); 29 | // Safe because the syscall will only read the extent of termios and we check the return result. 30 | let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) }; 31 | if ret < 0 { 32 | return errno_result(); 33 | } 34 | 35 | Ok(()) 36 | } 37 | 38 | fn get_flags(fd: RawFd) -> Result { 39 | // Safe because no third parameter is expected and we check the return result. 40 | let ret = unsafe { fcntl(fd, F_GETFL) }; 41 | if ret < 0 { 42 | return errno_result(); 43 | } 44 | Ok(ret) 45 | } 46 | 47 | 48 | fn set_flags(fd: RawFd, flags: c_int) -> Result<()> { 49 | // Safe because we supply the third parameter and we check the return result. 50 | let ret = unsafe { fcntl(fd, F_SETFL, flags) }; 51 | if ret < 0 { 52 | return errno_result(); 53 | } 54 | Ok(()) 55 | } 56 | 57 | /// Trait for file descriptors that are TTYs, according to `isatty(3)`. 58 | /// 59 | /// This is marked unsafe because the implementation must promise that the returned RawFd is a valid 60 | /// fd and that the lifetime of the returned fd is at least that of the trait object. 61 | pub unsafe trait Terminal { 62 | /// Gets the file descriptor of the TTY. 63 | fn tty_fd(&self) -> RawFd; 64 | 65 | /// Set this terminal's mode to canonical mode (`ICANON | ECHO | ISIG`). 66 | fn set_canon_mode(&self) -> Result<()> { 67 | modify_mode(self.tty_fd(), |t| t.c_lflag |= ICANON | ECHO | ISIG) 68 | } 69 | 70 | /// Set this terminal's mode to raw mode (`!(ICANON | ECHO | ISIG)`). 71 | fn set_raw_mode(&self) -> Result<()> { 72 | modify_mode(self.tty_fd(), |t| t.c_lflag &= !(ICANON | ECHO | ISIG)) 73 | } 74 | 75 | /// Sets the non-blocking mode of this terminal's file descriptor. 76 | /// 77 | /// If `non_block` is `true`, then `read_raw` will not block. If `non_block` is `false`, then 78 | /// `read_raw` may block if there is nothing to read. 79 | fn set_non_block(&self, non_block: bool) -> Result<()> { 80 | let old_flags = get_flags(self.tty_fd())?; 81 | let new_flags = if non_block { 82 | old_flags | O_NONBLOCK 83 | } else { 84 | old_flags & !O_NONBLOCK 85 | }; 86 | if new_flags != old_flags { 87 | set_flags(self.tty_fd(), new_flags)? 88 | } 89 | Ok(()) 90 | } 91 | 92 | /// Reads up to `out.len()` bytes from this terminal without any buffering. 93 | /// 94 | /// This may block, depending on if non-blocking was enabled with `set_non_block` or if there 95 | /// are any bytes to read. If there is at least one byte that is readable, this will not block. 96 | fn read_raw(&self, out: &mut [u8]) -> Result { 97 | // Safe because read will only modify the pointer up to the length we give it and we check 98 | // the return result. 99 | let ret = unsafe { read(self.tty_fd(), out.as_mut_ptr() as *mut _, out.len()) }; 100 | if ret < 0 { 101 | return errno_result(); 102 | } 103 | 104 | Ok(ret as usize) 105 | } 106 | } 107 | 108 | // Safe because we return a genuine terminal fd that never changes and shares our lifetime. 109 | unsafe impl<'a> Terminal for StdinLock<'a> { 110 | fn tty_fd(&self) -> RawFd { 111 | STDIN_FILENO 112 | } 113 | } 114 | 115 | // Safe because we return a genuine pollable fd that never changes and shares our lifetime. 116 | unsafe impl ::Pollable for T { 117 | fn pollable_fd(&self) -> RawFd { 118 | self.tty_fd() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/syscall_defines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "syscall_defines" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /src/syscall_defines/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #[cfg(target_arch = "x86_64")] 6 | #[path = "linux-x86_64/mod.rs"] 7 | pub mod linux; 8 | 9 | #[cfg(target_arch = "x86")] 10 | #[path = "linux-x86/mod.rs"] 11 | pub mod linux; 12 | 13 | #[cfg(target_arch = "aarch64")] 14 | #[path = "linux-aarch64/mod.rs"] 15 | pub mod linux; 16 | 17 | #[cfg(target_arch = "arm")] 18 | #[path = "linux-arm/mod.rs"] 19 | pub mod linux; 20 | 21 | #[path = "linux-aarch64/mod.rs"] 22 | pub mod linux; 23 | -------------------------------------------------------------------------------- /src/virtio_sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio_sys" 3 | version = "0.1.0" 4 | authors = ["The Chromium OS Authors"] 5 | 6 | [dependencies] 7 | sys_util = { path = "../sys_util" } 8 | -------------------------------------------------------------------------------- /src/virtio_sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | pub mod virtio_net; 6 | -------------------------------------------------------------------------------- /src/virtio_sys/src/virtio_net.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen */ 2 | 3 | //actually, only kept the things currently borrowed by firecracker 4 | 5 | pub const VIRTIO_F_VERSION_1: ::std::os::raw::c_uint = 32; 6 | 7 | pub const VIRTIO_NET_F_CSUM: ::std::os::raw::c_uint = 0; 8 | pub const VIRTIO_NET_F_GUEST_CSUM: ::std::os::raw::c_uint = 1; 9 | pub const VIRTIO_NET_F_GUEST_TSO4: ::std::os::raw::c_uint = 7; 10 | pub const VIRTIO_NET_F_GUEST_UFO: ::std::os::raw::c_uint = 10; 11 | pub const VIRTIO_NET_F_HOST_TSO4: ::std::os::raw::c_uint = 11; 12 | pub const VIRTIO_NET_F_HOST_UFO: ::std::os::raw::c_uint = 14; 13 | 14 | pub type U8 = ::std::os::raw::c_uchar; 15 | pub type U16 = ::std::os::raw::c_ushort; 16 | pub type Virtio16 = U16; 17 | 18 | impl Clone for virtio_net_hdr_v1 { 19 | fn clone(&self) -> Self { 20 | *self 21 | } 22 | } 23 | 24 | #[repr(C)] 25 | #[derive(Debug, Copy)] 26 | pub struct virtio_net_hdr_v1 { 27 | pub flags: U8, 28 | pub gso_type: U8, 29 | pub hdr_len: Virtio16, 30 | pub gso_size: Virtio16, 31 | pub csum_start: Virtio16, 32 | pub csum_offset: Virtio16, 33 | pub num_buffers: Virtio16, 34 | } 35 | -------------------------------------------------------------------------------- /tests/integration/test_images/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(CMAKE_VERBOSE_MAKEFILE on) 4 | 5 | project(virt-test-img C ASM) 6 | 7 | set(CROSS_COMPILE "riscv64-linux-gnu-") 8 | set(CMAKE_C_COMPILER "${CROSS_COMPILE}gcc") 9 | set(CMAKE_ASM_COMPILER "${CROSS_COMPILE}gcc") 10 | 11 | add_compile_options(-Wall -fPIC -nostdlib -nostartfiles -ffreestanding -Werror -nostdinc) 12 | add_compile_definitions($<$:__ASM__>) 13 | add_link_options(-T ${CMAKE_CURRENT_LIST_DIR}/vm.ld) 14 | 15 | include_directories(..) 16 | 17 | macro(add_vm_test_img IMG_NAME CODE_ENTRY) 18 | add_executable(${IMG_NAME}.img ${ARGN}) 19 | target_link_options(${IMG_NAME}.img PUBLIC -e ${CODE_ENTRY}) 20 | target_compile_definitions(${IMG_NAME}.img PUBLIC __FILENAME__=${IMG_NAME}) 21 | endmacro(add_vm_test_img) 22 | 23 | FILE(GLOB image_files "*.S") 24 | foreach(image_file ${image_files}) 25 | get_filename_component(image ${image_file} NAME_WE) 26 | add_vm_test_img(${image} ${image} ${image_file}) 27 | endforeach() 28 | -------------------------------------------------------------------------------- /tests/integration/test_images/asm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU) 3 | * ChCore is licensed under the Mulan PSL v1. 4 | * You can use this software according to the terms and conditions of the Mulan PSL v1. 5 | * You may obtain a copy of Mulan PSL v1 at: 6 | * http://license.coscl.org.cn/MulanPSL 7 | * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR 8 | * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR 9 | * PURPOSE. 10 | * See the Mulan PSL v1 for more details. 11 | */ 12 | 13 | #pragma once 14 | 15 | #define ECALL_VM_TEST_END (0xFF) 16 | 17 | // Opensbi EXT ID 18 | #define SBI_EXT_0_1_SET_TIMER (0x0) 19 | #define SBI_EXT_0_1_CONSOLE_PUTCHAR (0x1) 20 | #define SBI_EXT_0_1_CONSOLE_GETCHAR (0x2) 21 | #define SBI_EXT_0_1_CLEAR_IPI (0x3) 22 | #define SBI_EXT_0_1_SEND_IPI (0x4) 23 | #define SBI_EXT_0_1_REMOTE_FENCE_I (0x5) 24 | #define SBI_EXT_0_1_REMOTE_SFENCE_VMA (0x6) 25 | #define SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID (0x7) 26 | #define SBI_EXT_0_1_SHUTDOWN (0x8) 27 | 28 | /* ULH EXT ID */ 29 | #define SBI_TEST_HU_USER_IPI (0xC000000) 30 | #define SBI_TEST_HU_VIRTUAL_IPI (0xC000001) 31 | #define SBI_TEST_GET_VCPU_ID (0xC000002) 32 | #define SBI_TEST_SYNC_WAIT (0xC000003) 33 | #define SBI_TEST_SYNC_SET (0xC000004) 34 | #define SBI_TEST_TIME_START (0xC000005) 35 | #define SBI_TEST_TIME_END (0xC000006) 36 | #define SBI_TEST_SUCCESS (0xC000007) 37 | #define SBI_TEST_FAILED (0xC000008) 38 | #define SBI_TEST_HU_LOOP (0xC100000) 39 | 40 | #define BEGIN_FUNC(_name) \ 41 | .global _name; \ 42 | .type _name, % function; \ 43 | _name: 44 | 45 | #define END_FUNC(_name) .size _name, .- _name 46 | 47 | #define __FILE_NAME_NAME_END(filename) filename ## _ ## end 48 | #define _FILE_NAME_END(filename) __FILE_NAME_NAME_END(filename) 49 | #define FILE_NAME_END _FILE_NAME_END( __FILENAME__ ) 50 | 51 | 52 | #define BEGIN_FUNC_FILE_NAME() \ 53 | .global __FILENAME__; \ 54 | .type __FILENAME__, % function; \ 55 | .align 12; \ 56 | __FILENAME__: 57 | 58 | 59 | #define END_FUNC_FILE_NAME() \ 60 | .global FILE_NAME_END; \ 61 | FILE_NAME_END: \ 62 | .size __FILENAME__, .- __FILENAME__ 63 | -------------------------------------------------------------------------------- /tests/integration/test_images/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit immediately if a command exits with a non-zero status. 4 | set -e 5 | 6 | main() { 7 | SCRIPT_DIR=`dirname "$0"` 8 | BUILD_DIR_TMP=$1 9 | BUILD_DIR=${BUILD_DIR_TMP:-build} 10 | TARGET_DIR=$2 11 | CLEAN_FLAG=$3 12 | if [ ${CLEAN_FLAG}1 == clean1 ]; then 13 | echo "clean all test images" 14 | rm -rf $BUILD_DIR 15 | rm ./$TARGET_DIR/*.img 16 | exit 0 17 | fi 18 | 19 | if [ -d $BUILD_DIR ]; then 20 | cd $BUILD_DIR 21 | echo "compiling vm test images..." 22 | ninja 23 | echo "vm test images compile succeed!" 24 | exit 0 25 | else 26 | mkdir -p $BUILD_DIR 27 | 28 | cd $BUILD_DIR 29 | echo "compiling vm test images..." 30 | 31 | cmake \ 32 | -DCMAKE_LINKER=riscv64-linux-gnu-ld \ 33 | -DCMAKE_C_LINK_EXECUTABLE=" -o " \ 34 | -DCMAKE_ASM_LINK_EXECUTABLE=" -o " \ 35 | ${SCRIPT_DIR} -G Ninja .. 36 | 37 | ninja 38 | cd - 39 | echo "vm test images compile succeed!" 40 | mv -f ./$BUILD_DIR/*.img ./$TARGET_DIR/ 41 | exit 0 42 | fi 43 | } 44 | 45 | main $@ 46 | -------------------------------------------------------------------------------- /tests/integration/test_images/dtb_ld_data.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t0, 0 5 | li t1, 0x82200000 6 | 7 | ld t0, (t1) 8 | 9 | // test end 10 | li a7, ECALL_VM_TEST_END 11 | ecall 12 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/ecall_emulation_remote_fence.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* all the results will be stored on 0x3000(GPA) */ 5 | li t0, 0x3000 6 | 7 | /* save return values of SHUTDOWN */ 8 | li a7, SBI_EXT_0_1_REMOTE_FENCE_I 9 | ecall 10 | sd a0, (t0) 11 | add t0, t0, 8 12 | sd a1, (t0) 13 | add t0, t0, 8 14 | 15 | li a7, ECALL_VM_TEST_END 16 | ecall 17 | END_FUNC_FILE_NAME() 18 | -------------------------------------------------------------------------------- /tests/integration/test_images/ecall_emulation_unsupported.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* all the results will be stored on 0x3000(GPA) */ 5 | li t0, 0x3000 6 | 7 | /* save return values of CLEAR_IPI */ 8 | li a7, SBI_EXT_0_1_CLEAR_IPI 9 | ecall 10 | sd a0, (t0) 11 | add t0, t0, 8 12 | sd a1, (t0) 13 | 14 | li a7, ECALL_VM_TEST_END 15 | ecall 16 | END_FUNC_FILE_NAME() 17 | -------------------------------------------------------------------------------- /tests/integration/test_images/opensbi_getchar_count.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | // Output "Hello Ecall\n" via opensbi 4 | BEGIN_FUNC_FILE_NAME() 5 | // target char 'd' 6 | li a6, 10 7 | 8 | // Count the input 9 | li t1, 0 10 | 11 | // Output "getchar succeed" from console_getchar() 12 | continue: 13 | li a7, SBI_EXT_0_1_CONSOLE_GETCHAR 14 | ecall 15 | mv t0, a0 16 | add t1, t1, 1 17 | li a7, SBI_EXT_0_1_CONSOLE_PUTCHAR 18 | ecall 19 | bne t0, a6, continue 20 | 21 | li a0, 10 // \n 22 | ecall 23 | 24 | // test end 25 | li a7, ECALL_VM_TEST_END 26 | ecall 27 | END_FUNC_FILE_NAME() 28 | -------------------------------------------------------------------------------- /tests/integration/test_images/opensbi_getchar_sum.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | // Output "Hello Ecall\n" via opensbi 4 | BEGIN_FUNC_FILE_NAME() 5 | // target char 'd' 6 | li a6, 10 7 | 8 | // Sum up the input 9 | li t1, 0 10 | 11 | // Output "getchar succeed" from console_getchar() 12 | continue: 13 | li a7, SBI_EXT_0_1_CONSOLE_GETCHAR 14 | ecall 15 | mv t0, a0 16 | add t1, t1, a0 17 | li a7, SBI_EXT_0_1_CONSOLE_PUTCHAR 18 | ecall 19 | bne t0, a6, continue 20 | 21 | li a0, 10 // \n 22 | ecall 23 | 24 | // test end 25 | li a7, ECALL_VM_TEST_END 26 | ecall 27 | END_FUNC_FILE_NAME() 28 | -------------------------------------------------------------------------------- /tests/integration/test_images/opensbi_putchar.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | // Output "Hello Ecall\n" via opensbi 4 | BEGIN_FUNC_FILE_NAME() 5 | li t0, 0 6 | li t1, 0 7 | li a7, SBI_EXT_0_1_CONSOLE_PUTCHAR 8 | 9 | li a0, 72 // H 10 | add t0, t0, a0 11 | ecall 12 | add t1, t1, a0 13 | 14 | li a0, 101 // e 15 | add t0, t0, a0 16 | ecall 17 | add t1, t1, a0 18 | 19 | li a0, 108 // l 20 | add t0, t0, a0 21 | ecall 22 | add t1, t1, a0 23 | 24 | li a0, 108 // l 25 | add t0, t0, a0 26 | ecall 27 | add t1, t1, a0 28 | 29 | li a0, 111 // o 30 | add t0, t0, a0 31 | ecall 32 | add t1, t1, a0 33 | 34 | li a0, 32 // blank 35 | add t0, t0, a0 36 | ecall 37 | add t1, t1, a0 38 | 39 | li a0, 69 // E 40 | add t0, t0, a0 41 | ecall 42 | add t1, t1, a0 43 | 44 | li a0, 99 // c 45 | add t0, t0, a0 46 | ecall 47 | add t1, t1, a0 48 | 49 | li a0, 97 // a 50 | add t0, t0, a0 51 | ecall 52 | add t1, t1, a0 53 | 54 | li a0, 108 // l 55 | add t0, t0, a0 56 | ecall 57 | add t1, t1, a0 58 | 59 | li a0, 108 // l 60 | add t0, t0, a0 61 | ecall 62 | add t1, t1, a0 63 | 64 | li a0, 10 // \n 65 | add t0, t0, a0 66 | ecall 67 | add t1, t1, a0 68 | 69 | // test end 70 | li a7, ECALL_VM_TEST_END 71 | ecall 72 | END_FUNC_FILE_NAME() 73 | -------------------------------------------------------------------------------- /tests/integration/test_images/tty_load.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t0, 0x3000 5 | li t1, 0x3f8 6 | 7 | lb a0, 0x0(t1) 8 | sb a0, 0x0(t0) 9 | 10 | lb a0, 0x1(t1) 11 | sb a0, 0x1(t0) 12 | 13 | lb a0, 0x2(t1) 14 | sb a0, 0x2(t0) 15 | 16 | lb a0, 0x3(t1) 17 | sb a0, 0x3(t0) 18 | 19 | lb a0, 0x4(t1) 20 | sb a0, 0x4(t0) 21 | 22 | lb a0, 0x5(t1) 23 | sb a0, 0x5(t0) 24 | 25 | lb a0, 0x6(t1) 26 | sb a0, 0x6(t0) 27 | 28 | lb a0, 0x7(t1) 29 | sb a0, 0x7(t0) 30 | 31 | // test end 32 | li a7, ECALL_VM_TEST_END 33 | ecall 34 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/tty_store.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* UART_LCR = 0x80 with UART_LCR_DLAB = 1 */ 5 | li t0, 0x3fb 6 | li a2, 0x80 7 | sb a2, 0x0(t0) 8 | 9 | /* UART_DLM = 0x0 */ 10 | li t0, 0x3f9 11 | li a2, 0x0 12 | sb a2, 0x0(t0) 13 | 14 | /* UART_DLL = 0xc */ 15 | li t0, 0x3f8 16 | li a2, 0xc 17 | sb a2, 0x0(t0) 18 | 19 | /* UART_LCR = 0x0 with UART_LCR_DLAB = 0 */ 20 | li t0, 0x3fb 21 | li a2, 0x0 22 | sb a2, 0x0(t0) 23 | 24 | /* UART_IER = 0xff & 0xf */ 25 | li t0, 0x3f9 26 | li a2, 0xff 27 | sb a2, 0x0(t0) 28 | 29 | /* UART_FCR = UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR */ 30 | li t0, 0x3fa 31 | li a2, 0x6 32 | sb a2, 0x0(t0) 33 | 34 | /* UART_MCR = UART_MCR_OUT2 */ 35 | li t0, 0x3fc 36 | li a2, 0x08 37 | sb a2, 0x0(t0) 38 | 39 | /* UART_SCR = 0x0 */ 40 | li t0, 0x3ff 41 | li a2, 0x0 42 | sb a2, 0x0(t0) 43 | 44 | li a7, ECALL_VM_TEST_END 45 | ecall 46 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/vcpu_add_all_gprs.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | // x0 is hardwired to 0 5 | li x1, 10 6 | add x1, x1, x2 7 | add x1, x1, x3 8 | add x1, x1, x4 9 | add x1, x1, x5 10 | add x1, x1, x6 11 | add x1, x1, x7 12 | add x1, x1, x8 13 | add x1, x1, x9 14 | add x1, x1, x10 15 | add x1, x1, x11 16 | add x1, x1, x12 17 | add x1, x1, x13 18 | add x1, x1, x14 19 | add x1, x1, x15 20 | add x1, x1, x16 21 | add x1, x1, x17 22 | add x1, x1, x18 23 | add x1, x1, x19 24 | add x1, x1, x20 25 | add x1, x1, x21 26 | add x1, x1, x22 27 | add x1, x1, x23 28 | add x1, x1, x24 29 | add x1, x1, x25 30 | add x1, x1, x26 31 | add x1, x1, x27 32 | add x1, x1, x28 33 | add x1, x1, x29 34 | add x1, x1, x30 35 | add x1, x1, x31 36 | mv a0, x1 37 | li a7, ECALL_VM_TEST_END 38 | ecall 39 | END_FUNC_FILE_NAME() 40 | -------------------------------------------------------------------------------- /tests/integration/test_images/vcpu_ecall_exit.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li a7, ECALL_VM_TEST_END 5 | ecall 6 | END_FUNC_FILE_NAME() 7 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_send_to_null_vcpu.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Set up irq_handler */ 5 | la a6, irq_handler 6 | csrw stvec, a6 7 | 8 | /* Set up sstatus.SIE */ 9 | csrs sstatus, 0x2 10 | 11 | /* Enable VS_SOFT via SSIE bit */ 12 | li t1, 0x2 13 | csrs sie, t1 14 | 15 | /* Address for sync */ 16 | li t0, 0x3000 17 | 18 | li t1, 0 19 | li t2, 1 20 | 21 | beq a0, t1, vcpu_0 22 | beq a0, t2, vcpu_1 23 | j test_failed 24 | 25 | /* vcpu 0 */ 26 | vcpu_0: 27 | /* Wait for vcpu 1 util sync = 1 */ 28 | ld t5, 0x0(t0) 29 | li t6, 0x1 30 | bne t5, t6, vcpu_0 31 | 32 | /* 33 | * Set up hart mask to aim at vcpu [1,2,3,4,5,6,7] 34 | * And vcpu [2,3,4,5,6,7] is invalid. 35 | */ 36 | li a0, 0x3100 37 | li a2, 0xfe 38 | sd a2, 0x0(a0) 39 | 40 | /* Send virtual ipi */ 41 | li a7, SBI_EXT_0_1_SEND_IPI 42 | ecall 43 | 44 | /* Vcpu 0 should finish the SEND_IPI ecall */ 45 | j test_success 46 | 47 | /* vcpu 1 */ 48 | vcpu_1: 49 | /* Set sync data = 1 */ 50 | sd t2, 0x0(t0) 51 | 52 | wait_for_ipi_1: 53 | wfi 54 | /* Wait for vipi of vcpu 0 */ 55 | j wait_for_ipi_1 56 | 57 | /* Vcpu 1 should never reach here */ 58 | j test_failed 59 | 60 | test_success: 61 | li a7, SBI_TEST_SUCCESS 62 | ecall 63 | li a7, ECALL_VM_TEST_END 64 | ecall 65 | 66 | test_failed: 67 | li a7, SBI_TEST_FAILED 68 | ecall 69 | li a7, ECALL_VM_TEST_END 70 | ecall 71 | END_FUNC_FILE_NAME() 72 | 73 | /* 74 | * Irq handler for vcpu 0, it will end this 75 | * test case. 76 | */ 77 | .align 4 78 | irq_handler: 79 | li a7, ECALL_VM_TEST_END 80 | ecall 81 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_user_ipi_remote.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Address for sync */ 5 | li t0, 0x3000 6 | li t1, 0 7 | li t2, 1 8 | 9 | /* Set sync data */ 10 | sd t2, 0x0(t0) 11 | 12 | wait_for_uipi: 13 | ld t3, 0x0(t0) 14 | li t4, 0x2 15 | wfi 16 | /* 17 | * If *0x3000 == 2, the sending thread must have finished 18 | * the setting of VIPI CSR. And vcpu will get user ipi before 19 | * it reach test_success. 20 | */ 21 | bne t4, t3, wait_for_uipi 22 | 23 | test_success: 24 | li a7, ECALL_VM_TEST_END 25 | ecall 26 | 27 | test_failed: 28 | li a7, SBI_TEST_FAILED 29 | ecall 30 | 31 | END_FUNC_FILE_NAME() 32 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_user_ipi_remote_multi.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Address for sync */ 5 | li t0, 0x3000 6 | 7 | li t1, 0 8 | li t2, 1 9 | 10 | beq a0, t1, vcpu_0 11 | beq a0, t2, vcpu_1 12 | j test_failed 13 | 14 | vcpu_0: 15 | ld t5, 0x0(t0) 16 | li t6, 0x2 17 | /* 18 | * If *0x3000 == 2, the sending thread must have finished 19 | * the setting of VIPI CSR via the vipi struct of vcpu 0. 20 | * Then vcpu 0 can exit. 21 | */ 22 | bne t5, t6, vcpu_0 23 | j test_success 24 | 25 | vcpu_1: 26 | /* Set sync data */ 27 | sd t2, 0x0(t0) 28 | 29 | wait_for_uipi: 30 | ld t3, 0x0(t0) 31 | li t4, 0x2 32 | wfi 33 | /* 34 | * If *0x3000 == 2, the sending thread must have finished 35 | * the setting of VIPI CSR. And vcpu will get user ipi before 36 | * it reach test_success. 37 | */ 38 | bne t4, t3, wait_for_uipi 39 | 40 | test_success: 41 | li a7, ECALL_VM_TEST_END 42 | ecall 43 | 44 | test_failed: 45 | li a7, SBI_TEST_FAILED 46 | ecall 47 | 48 | END_FUNC_FILE_NAME() 49 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_virtual_ipi_accurate.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Set up sstatus.SIE */ 5 | csrs sstatus, 0x2 6 | 7 | /* Enable VS_SOFT via SSIE bit */ 8 | li t1, 0x2 9 | csrs sie, t1 10 | 11 | /* Address for sync */ 12 | li t0, 0x3000 13 | 14 | li t1, 0 15 | li t2, 1 16 | li t3, 2 17 | 18 | beq a0, t1, vcpu_0 19 | beq a0, t2, vcpu_1 20 | beq a0, t3, vcpu_2 21 | j test_failed 22 | 23 | /* vcpu 0 */ 24 | vcpu_0: 25 | /* Set up irq_handler */ 26 | la a6, irq_handler_0 27 | csrw stvec, a6 28 | 29 | /* Wait for vcpu 1 util sync = 1 */ 30 | ld t5, 0x0(t0) 31 | li t6, 0x1 32 | bne t5, t6, vcpu_0 33 | 34 | /* Set up hart mask to aim at vcpu 1 */ 35 | li a0, 0x3100 36 | li a2, 2 37 | sd a2, 0x0(a0) 38 | 39 | /* Send virtual ipi */ 40 | li a7, SBI_EXT_0_1_SEND_IPI 41 | ecall 42 | 43 | /* Set sync data = 2 */ 44 | li t2, 2 45 | sd t2, 0x0(t0) 46 | 47 | /* Vcpu 0 ends successfully */ 48 | j test_success 49 | 50 | /* vcpu 1 */ 51 | vcpu_1: 52 | /* Set up irq_handler */ 53 | la a6, irq_handler_1 54 | csrw stvec, a6 55 | 56 | /* Set sync data = 1 */ 57 | sd t2, 0x0(t0) 58 | 59 | wait_for_ipi_1: 60 | wfi 61 | /* Wait for vipi of vcpu 0 */ 62 | j wait_for_ipi_1 63 | 64 | /* Vcpu 1 should never reach here */ 65 | j test_failed 66 | 67 | /* vcpu 2 */ 68 | vcpu_2: 69 | /* Set up irq_handler */ 70 | la a6, irq_handler_2 71 | csrw stvec, a6 72 | 73 | wait_for_ipi_2: 74 | wfi 75 | /* Wait for vcpu 0 util sync = 2 */ 76 | ld t5, 0x0(t0) 77 | li t6, 0x2 78 | bne t5, t6, wait_for_ipi_2 79 | 80 | /* Vcpu 2 ends successfully */ 81 | j test_success 82 | 83 | /* Test end */ 84 | test_success: 85 | li a7, SBI_TEST_SUCCESS 86 | ecall 87 | li a7, ECALL_VM_TEST_END 88 | ecall 89 | 90 | test_failed: 91 | li a7, SBI_TEST_FAILED 92 | ecall 93 | li a7, ECALL_VM_TEST_END 94 | ecall 95 | END_FUNC_FILE_NAME() 96 | 97 | /* 98 | * Irq handler for vcpu 0, it will fail this 99 | * test case. 100 | */ 101 | .align 4 102 | irq_handler_0: 103 | j test_failed 104 | 105 | /* 106 | * Irq handler for vcpu 1, it will end this 107 | * test case successfully. 108 | */ 109 | .align 4 110 | irq_handler_1: 111 | j test_success 112 | 113 | /* 114 | * Irq handler for vcpu 2, it will fail this 115 | * test case. 116 | */ 117 | .align 4 118 | irq_handler_2: 119 | j test_failed -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_virtual_ipi_local.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Set up irq_handler */ 5 | la t1, irq_handler 6 | csrw stvec, t1 7 | csrs sstatus, 0x2 8 | 9 | /* Enable VS_SOFT via SSIE bit */ 10 | li t1, 0x2 11 | csrs sie, t1 12 | 13 | /* Notice local vcpu thread to insert virtual ipi */ 14 | li a7, SBI_TEST_HU_VIRTUAL_IPI 15 | ecall 16 | 17 | loop: 18 | wfi 19 | j loop 20 | 21 | 22 | END_FUNC_FILE_NAME() 23 | 24 | .align 4 25 | irq_handler: 26 | li a7, ECALL_VM_TEST_END 27 | ecall 28 | 29 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_virtual_ipi_remote_each.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Set up sstatus.SIE */ 5 | csrs sstatus, 0x2 6 | 7 | /* Enable VS_SOFT via SSIE bit */ 8 | li t1, 0x2 9 | csrs sie, t1 10 | 11 | /* Address for sync */ 12 | li t0, 0x3000 13 | 14 | li t1, 0 15 | li t2, 1 16 | 17 | beq a0, t1, vcpu_0 18 | beq a0, t2, vcpu_1 19 | j test_failed 20 | 21 | /* vcpu 0 */ 22 | vcpu_0: 23 | /* Set up irq_handler */ 24 | la a6, irq_handler_0 25 | csrw stvec, a6 26 | 27 | /* Wait for vcpu 1 util sync = 1 */ 28 | ld t5, 0x0(t0) 29 | li t6, 0x1 30 | bne t5, t6, vcpu_0 31 | 32 | /* Set up hart mask to aim at vcpu 1 */ 33 | li a0, 0x3100 34 | li a2, 2 35 | sd a2, 0x0(a0) 36 | 37 | /* Send virtual ipi */ 38 | li a7, SBI_EXT_0_1_SEND_IPI 39 | ecall 40 | 41 | wait_for_ipi_0: 42 | wfi 43 | /* Wait for vipi of vcpu 1 */ 44 | j wait_for_ipi_0 45 | 46 | /* Vcpu 0 should never reach here */ 47 | j test_failed 48 | 49 | /* vcpu 1 */ 50 | vcpu_1: 51 | /* Set up irq_handler */ 52 | la a6, irq_handler_1 53 | csrw stvec, a6 54 | 55 | /* Set sync data = 1 */ 56 | sd t2, 0x0(t0) 57 | 58 | wait_for_ipi_1: 59 | wfi 60 | /* Wait for vipi of vcpu 0 */ 61 | j wait_for_ipi_1 62 | 63 | /* Vcpu 1 should never reach here */ 64 | j test_failed 65 | 66 | test_success: 67 | li a7, ECALL_VM_TEST_END 68 | ecall 69 | 70 | test_failed: 71 | li a7, SBI_TEST_FAILED 72 | ecall 73 | 74 | END_FUNC_FILE_NAME() 75 | 76 | /* 77 | * Irq handler for vcpu 0, it will end this 78 | * test case. 79 | */ 80 | .align 4 81 | irq_handler_0: 82 | li a7, SBI_TEST_SUCCESS 83 | ecall 84 | li a7, ECALL_VM_TEST_END 85 | ecall 86 | 87 | /* 88 | * Irq handler for vcpu 1, it will send vipi 89 | * to vcpu 0. 90 | */ 91 | .align 4 92 | irq_handler_1: 93 | /* Set up hart mask to aim at vcpu 0 */ 94 | li a0, 0x3200 95 | li a2, 1 96 | sd a2, 0x0(a0) 97 | 98 | /* Send virtual ipi */ 99 | li a7, SBI_EXT_0_1_SEND_IPI 100 | ecall 101 | 102 | /* Vcpu 1 exit */ 103 | li a7, ECALL_VM_TEST_END 104 | ecall 105 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_virtual_ipi_remote_not_running.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Set up irq_handler */ 5 | la t1, irq_handler 6 | csrw stvec, t1 7 | csrs sstatus, 0x2 8 | 9 | /* Enable VS_SOFT via SSIE bit */ 10 | li t1, 0x2 11 | csrs sie, t1 12 | 13 | /* Address for sync */ 14 | li t0, 0x3000 15 | 16 | li t1, 0 17 | li t2, 1 18 | 19 | beq a0, t1, vcpu_0 20 | beq a0, t2, vcpu_1 21 | j test_failed 22 | 23 | /* vcpu 0 */ 24 | vcpu_0: 25 | /* Wait for vcpu 1 util sync = 1 */ 26 | ld t5, 0x0(t0) 27 | li t6, 0x1 28 | bne t5, t6, vcpu_0 29 | 30 | /* Set up hart mask */ 31 | li a0, 0x3100 32 | li a2, 2 33 | sd a2, 0x0(a0) 34 | 35 | /* Send virtual ipi */ 36 | li a7, SBI_EXT_0_1_SEND_IPI 37 | ecall 38 | 39 | /* Update sync to 2 */ 40 | li t6, 2 41 | sd t6, 0x0(t0) 42 | 43 | j test_success 44 | 45 | 46 | /* vcpu 1 */ 47 | vcpu_1: 48 | /* 49 | * Ecall into HU-mode 50 | * a1 = hva of 0x3000(gpa) 51 | * a2 = start signal = 1 52 | * a3 = end signal = 2 53 | */ 54 | li a7, SBI_TEST_HU_LOOP 55 | li a2, 1 56 | li a3, 2 57 | ecall 58 | 59 | /* Vcpu 1 should exit from irq_handler */ 60 | j test_failed 61 | 62 | test_success: 63 | li a7, ECALL_VM_TEST_END 64 | ecall 65 | 66 | test_failed: 67 | li a7, SBI_TEST_FAILED 68 | ecall 69 | 70 | END_FUNC_FILE_NAME() 71 | 72 | .align 4 73 | irq_handler: 74 | li a7, SBI_TEST_SUCCESS 75 | ecall 76 | li a7, ECALL_VM_TEST_END 77 | ecall 78 | -------------------------------------------------------------------------------- /tests/integration/test_images/vipi_virtual_ipi_remote_running.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | /* Set up irq_handler */ 5 | la t1, irq_handler 6 | csrw stvec, t1 7 | csrs sstatus, 0x2 8 | 9 | /* Enable VS_SOFT via SSIE bit */ 10 | li t1, 0x2 11 | csrs sie, t1 12 | 13 | /* Address for sync */ 14 | li t0, 0x3000 15 | 16 | li t1, 0 17 | li t2, 1 18 | 19 | beq a0, t1, vcpu_0 20 | beq a0, t2, vcpu_1 21 | j test_failed 22 | 23 | vcpu_0: 24 | /* Wait for vcpu 1 util sync = 1 */ 25 | ld t5, 0x0(t0) 26 | li t6, 0x1 27 | bne t5, t6, vcpu_0 28 | 29 | /* Set up hart mask */ 30 | li a0, 0x3100 31 | li a2, 2 32 | sd a2, 0x0(a0) 33 | 34 | /* Send virtual ipi */ 35 | li a7, SBI_EXT_0_1_SEND_IPI 36 | ecall 37 | 38 | /* Update sync to 2 */ 39 | li t6, 2 40 | sd t6, 0x0(t0) 41 | 42 | j test_success 43 | 44 | vcpu_1: 45 | /* Set sync data = 1 */ 46 | sd t2, 0x0(t0) 47 | 48 | wait_for_ipi: 49 | /* Wait for vcpu 0 util sync = 2 */ 50 | ld t3, 0x0(t0) 51 | li t4, 0x2 52 | wfi 53 | bne t4, t3, wait_for_ipi 54 | 55 | /* Vcpu 1 should exit from irq_handler */ 56 | j test_failed 57 | 58 | test_success: 59 | li a7, ECALL_VM_TEST_END 60 | ecall 61 | 62 | test_failed: 63 | li a7, SBI_TEST_FAILED 64 | ecall 65 | 66 | END_FUNC_FILE_NAME() 67 | 68 | .align 4 69 | irq_handler: 70 | li a7, SBI_TEST_SUCCESS 71 | ecall 72 | li a7, ECALL_VM_TEST_END 73 | ecall 74 | -------------------------------------------------------------------------------- /tests/integration/test_images/vm.ld: -------------------------------------------------------------------------------- 1 | SECTIONS { 2 | . = 0x1000; 3 | 4 | .text : { 5 | *(.text*) 6 | } 7 | 8 | .data : { 9 | *(.data*) 10 | } 11 | 12 | .rodata : { 13 | *(.rodata*) 14 | } 15 | 16 | .bss : { 17 | *(.bss*) 18 | } 19 | } -------------------------------------------------------------------------------- /tests/integration/test_images/vmem_W_Ro.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t1, 0x3000 5 | ld t1, (t1) 6 | li t2, 0x3000 7 | li t3, 0x4321 8 | sd t3, (t2) 9 | li a7, ECALL_VM_TEST_END 10 | ecall 11 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/vmem_X_nonX.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t0, 0x3000 5 | ld t1, (t0) 6 | add t2, t1, 1 7 | sd t2, (t0) 8 | jalr t0 9 | li a7, ECALL_VM_TEST_END 10 | ecall 11 | END_FUNC_FILE_NAME() 12 | -------------------------------------------------------------------------------- /tests/integration/test_images/vmem_ld_data.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t0, 0 5 | li t1, 0x3000 6 | 7 | ld t0, (t1) 8 | 9 | // test end 10 | li a7, ECALL_VM_TEST_END 11 | ecall 12 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/vmem_ld_mapping.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t1, 0x3000 5 | ld t1, (t1) 6 | li a7, ECALL_VM_TEST_END 7 | ecall 8 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/vmem_ld_sd_over_loop.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t0, 0 5 | li t1, 100 6 | li t2, 0x3000 7 | li t4, 0 8 | 9 | continue: 10 | ld t3, (t2) 11 | add t4, t4, t3 12 | addi t2, t2, 0xf8 13 | ld t3, (t2) 14 | add t4, t4, t3 15 | addi t0, t0, 1 16 | li t5, 0x1008 17 | add t2, t2, t5 18 | bne t0, t1, continue 19 | 20 | li a7, ECALL_VM_TEST_END 21 | ecall 22 | END_FUNC_FILE_NAME() -------------------------------------------------------------------------------- /tests/integration/test_images/vmem_ld_sd_sum.S: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | BEGIN_FUNC_FILE_NAME() 4 | li t0, 0 5 | li t1, 100 6 | li t2, 0x3000 7 | 8 | /* sum */ 9 | li t4, 0 10 | 11 | continue: 12 | sd t0, (t2) 13 | ld t3, (t2) 14 | 15 | /* sum += i from 0..100 */ 16 | add t4, t4, t3 17 | 18 | addi t2, t2, 0xf8 19 | sd t0, (t2) 20 | ld t3, (t2) 21 | 22 | /* sum += i from 0..100 */ 23 | add t4, t4, t3 24 | 25 | addi t0, t0, 1 26 | li t5, 0x1008 27 | add t2, t2, t5 28 | bne t0, t1, continue 29 | 30 | li a7, ECALL_VM_TEST_END 31 | ecall 32 | END_FUNC_FILE_NAME() --------------------------------------------------------------------------------