├── .github └── workflows │ ├── ci.yaml │ ├── pr.yaml │ └── push.yaml ├── .gitignore ├── .gitmodules ├── .reuse └── dep5 ├── LICENSE.md ├── LICENSES ├── BSD-2-Clause.txt ├── BSD-3-Clause.txt ├── CC-BY-SA-4.0.txt ├── GPL-2.0-or-later.txt └── MIT.txt ├── README.md ├── build.zig ├── build.zig.zon ├── ci ├── README.md ├── buildroot_login.exp └── examples.sh ├── docs ├── INTERFACE.md ├── MANUAL.md ├── README.md ├── assets │ ├── passthrough.svg │ └── virtio_console_example.svg └── shell.nix ├── examples ├── rust │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── aarch64-microkit-minimal.json │ ├── build.rs │ ├── images │ │ ├── README.md │ │ ├── buildroot_config │ │ ├── linux.dts │ │ └── linux_config │ ├── rust-toolchain.toml │ ├── rust_vmm.system │ └── src │ │ └── vmm.rs ├── simple │ ├── Makefile │ ├── README.md │ ├── board │ │ ├── maaxboard │ │ │ ├── README.md │ │ │ ├── buildroot_config │ │ │ ├── linux.dts │ │ │ ├── linux_config │ │ │ ├── overlay.dts │ │ │ └── simple.system │ │ ├── odroidc4 │ │ │ ├── README.md │ │ │ ├── linux.dts │ │ │ ├── linux_config │ │ │ ├── overlay.dts │ │ │ └── simple.system │ │ └── qemu_virt_aarch64 │ │ │ ├── README.md │ │ │ ├── buildroot_config │ │ │ ├── linux.dts │ │ │ ├── linux_config │ │ │ ├── overlay.dts │ │ │ └── simple.system │ ├── build.zig │ ├── build.zig.zon │ ├── simple.mk │ └── vmm.c ├── virtio-snd │ ├── Makefile │ ├── README.md │ ├── board │ │ ├── odroidc4 │ │ │ ├── client_vm │ │ │ │ ├── buildroot_config │ │ │ │ ├── dts │ │ │ │ │ ├── linux.dts │ │ │ │ │ └── overlays │ │ │ │ │ │ ├── init.dts │ │ │ │ │ │ └── io.dts │ │ │ │ └── linux_config │ │ │ ├── snd_driver_vm │ │ │ │ ├── buildroot_config │ │ │ │ ├── dts │ │ │ │ │ ├── linux.dts │ │ │ │ │ └── overlays │ │ │ │ │ │ ├── init.dts │ │ │ │ │ │ └── io.dts │ │ │ │ └── linux_config │ │ │ └── virtio-snd.system │ │ └── qemu_virt_aarch64 │ │ │ ├── client_vm │ │ │ ├── README.md │ │ │ ├── buildroot_config │ │ │ ├── dts │ │ │ │ ├── linux.dts │ │ │ │ └── overlays │ │ │ │ │ ├── init.dts │ │ │ │ │ └── io.dts │ │ │ └── linux_config │ │ │ ├── snd_driver_vm │ │ │ ├── README.md │ │ │ ├── buildroot_config │ │ │ ├── dts │ │ │ │ ├── linux.dts │ │ │ │ └── overlays │ │ │ │ │ ├── init.dts │ │ │ │ │ └── io.dts │ │ │ └── linux_config │ │ │ └── virtio-snd.system │ ├── client_vmm.c │ ├── docs │ │ ├── SOUND.md │ │ ├── sound-structure.svg │ │ └── virtio-snd.svg │ ├── include │ │ └── serial_config.h │ ├── shell.nix │ ├── snd_driver_vmm.c │ ├── userlevel │ │ ├── control.c │ │ ├── feedback.c │ │ ├── pcm.c │ │ ├── pcm_min.c │ │ └── record.c │ └── virtio_snd.mk ├── virtio │ ├── Makefile │ ├── README.md │ ├── client_vm │ │ ├── README.md │ │ ├── buildroot_config │ │ ├── gic_v2_overlay.dts │ │ ├── gic_v3_overlay.dts │ │ ├── linux.dts │ │ └── linux_config │ ├── client_vmm.c │ ├── meta.py │ └── virtio.mk └── zig │ ├── README.md │ ├── build.zig │ ├── build.zig.zon │ ├── images │ ├── README.md │ ├── buildroot_config │ ├── linux.dts │ └── linux_config │ ├── src │ ├── extern.c │ └── vmm.zig │ └── zig_vmm.system ├── flake.lock ├── flake.nix ├── include └── libvmm │ ├── arch │ └── aarch64 │ │ ├── fault.h │ │ ├── hsr.h │ │ ├── linux.h │ │ ├── psci.h │ │ ├── smc.h │ │ └── vgic │ │ ├── vdist.h │ │ ├── vgic.h │ │ ├── vgic_v2.h │ │ ├── vgic_v3.h │ │ └── virq.h │ ├── config.h │ ├── dtb.h │ ├── guest.h │ ├── tcb.h │ ├── util │ ├── atomic.h │ ├── printf.h │ ├── queue.h │ └── util.h │ ├── vcpu.h │ ├── virq.h │ └── virtio │ ├── block.h │ ├── config.h │ ├── console.h │ ├── mmio.h │ ├── net.h │ ├── sound.h │ ├── virtio.h │ └── virtq.h ├── scripts └── package_image.sh ├── src ├── arch │ └── aarch64 │ │ ├── fault.c │ │ ├── linux.c │ │ ├── psci.c │ │ ├── smc.c │ │ ├── tcb.c │ │ ├── vcpu.c │ │ ├── vgic │ │ ├── vgic.c │ │ ├── vgic_v2.c │ │ └── vgic_v3.c │ │ └── virq.c ├── guest.c ├── util │ ├── printf.c │ └── util.c └── virtio │ ├── block.c │ ├── console.c │ ├── mmio.c │ ├── net.c │ └── sound.c ├── tools ├── dtscat ├── linux │ ├── blk │ │ ├── blk_client_init │ │ ├── blk_init.mk │ │ └── board │ │ │ ├── odroidc4 │ │ │ └── blk_driver_init │ │ │ └── qemu_virt_aarch64 │ │ │ └── blk_driver_init │ ├── include │ │ └── uio │ │ │ ├── blk.h │ │ │ ├── libuio.h │ │ │ └── sound.h │ ├── snd │ │ ├── board │ │ │ ├── odroidc4 │ │ │ │ └── asound.conf │ │ │ └── qemu_virt_aarch64 │ │ │ │ └── asound.conf │ │ ├── snd_driver_init │ │ └── sound_init.mk │ ├── uio │ │ ├── libuio.c │ │ └── uio.mk │ └── uio_drivers │ │ ├── blk │ │ ├── blk.c │ │ └── uio_blk.mk │ │ └── snd │ │ ├── convert.c │ │ ├── convert.h │ │ ├── log.h │ │ ├── main.c │ │ ├── queue.c │ │ ├── queue.h │ │ ├── stream.c │ │ ├── stream.h │ │ └── uio_snd.mk ├── mkvirtdisk ├── package_guest_images.S └── packrootfs └── vmm.mk /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | name: CI 6 | 7 | on: 8 | pull_request: 9 | push: 10 | branches: [ "main" ] 11 | 12 | env: 13 | MICROKIT_VERSION: 2.0.1 14 | MICROKIT_URL: https://github.com/seL4/microkit/releases/download/2.0.1/microkit-sdk-2.0.1 15 | SDFGEN_VERSION: 0.23.1 16 | 17 | jobs: 18 | build_manual: 19 | name: Build manual 20 | runs-on: ubuntu-24.04 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | - name: Install Nix 25 | uses: cachix/install-nix-action@v25 26 | with: 27 | nix_path: nixpkgs=channel:nixos-unstable 28 | - name: Create nix-shell and build PDF 29 | run: | 30 | cd docs 31 | nix-shell --run "pandoc MANUAL.md -o MANUAL.pdf" 32 | - name: Upload manual PDF 33 | uses: actions/upload-artifact@v4 34 | with: 35 | name: MANUAL 36 | path: docs/MANUAL.pdf 37 | build_linux_x86_64: 38 | name: Build and run examples (Linux x86-64) 39 | runs-on: ubuntu-24.04 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v4 43 | with: 44 | submodules: 'true' 45 | - name: Download Microkit SDK 46 | run: | 47 | wget ${{ env.MICROKIT_URL }}-linux-x86-64.tar.gz 48 | tar xf microkit-sdk-${{ env.MICROKIT_VERSION }}-linux-x86-64.tar.gz 49 | - name: Install dependencies (via apt) 50 | # 'expect' is only a dependency for CI testing 51 | run: sudo apt update && sudo apt install -y make clang lld llvm qemu-system-arm device-tree-compiler expect gcc-aarch64-linux-gnu 52 | - name: Install Zig 53 | uses: mlugg/setup-zig@v2.0.0 54 | with: 55 | version: 0.14.0 56 | - name: Setup pyenv 57 | run: | 58 | python3 -m venv venv 59 | ./venv/bin/pip install --upgrade sdfgen==${{ env.SDFGEN_VERSION }} 60 | - name: Build and run examples 61 | run: ./ci/examples.sh ${PWD}/microkit-sdk-${{ env.MICROKIT_VERSION }} 62 | shell: bash 63 | env: 64 | PYTHON: ${{ github.workspace }}/venv/bin/python 65 | build_macos_arm64: 66 | name: Build and run examples (macOS ARM64) 67 | runs-on: macos-14 68 | steps: 69 | - name: Checkout repository 70 | uses: actions/checkout@v4 71 | with: 72 | submodules: 'true' 73 | - name: Download Microkit SDK 74 | run: | 75 | wget ${{ env.MICROKIT_URL }}-macos-aarch64.tar.gz 76 | tar xf microkit-sdk-${{ env.MICROKIT_VERSION }}-macos-aarch64.tar.gz 77 | - name: Install dependencies (via Homebrew) 78 | # 'expect' is only a dependency for CI testing 79 | run: | 80 | brew install llvm lld qemu dtc make dosfstools expect 81 | echo "/opt/homebrew/opt/llvm/bin:$PATH" >> $GITHUB_PATH 82 | - name: Install Zig 83 | uses: mlugg/setup-zig@v2.0.0 84 | with: 85 | version: 0.14.0 86 | - name: Setup pyenv 87 | run: | 88 | python3 -m venv venv 89 | ./venv/bin/pip install --upgrade sdfgen==${{ env.SDFGEN_VERSION }} 90 | - name: Build and run examples 91 | run: ./ci/examples.sh ${PWD}/microkit-sdk-${{ env.MICROKIT_VERSION }} 92 | shell: bash 93 | env: 94 | PYTHON: ${{ github.workspace }}/venv/bin/python 95 | build_linux_x86_64_nix: 96 | name: Build and run examples (Linux x86-64 via Nix) 97 | runs-on: ubuntu-24.04 98 | steps: 99 | - name: Checkout repository 100 | uses: actions/checkout@v4 101 | with: 102 | submodules: 'true' 103 | - name: Install Nix 104 | uses: cachix/install-nix-action@v25 105 | with: 106 | nix_path: nixpkgs=channel:nixos-unstable 107 | - name: Build and run examples 108 | run: nix develop --ignore-environment -c bash -c './ci/examples.sh $MICROKIT_SDK' 109 | -------------------------------------------------------------------------------- /.github/workflows/pr.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | # Actions to *only* run on GitHub pull requests 6 | 7 | name: PR 8 | 9 | on: [pull_request] 10 | 11 | jobs: 12 | whitespace: 13 | name: 'Trailing Whitespace' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: seL4/ci-actions/git-diff-check@master 17 | 18 | style: 19 | name: Style 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: seL4/ci-actions/style@master 23 | -------------------------------------------------------------------------------- /.github/workflows/push.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | # Actions to run on Push and Pull Request 6 | name: CI 7 | 8 | on: 9 | push: 10 | branches: 11 | - main 12 | pull_request: 13 | 14 | jobs: 15 | check: 16 | name: License Check 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: seL4/ci-actions/license-check@master 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # SPDX-License-Identifier: BSD-2-Clause 3 | 4 | build/ 5 | ci_build/ 6 | zig-out/ 7 | .zig-cache/ 8 | *.pdf 9 | *.dtb 10 | rust/target/ 11 | .cache 12 | compile_commands.json 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sddf"] 2 | path = dep/sddf 3 | url = https://github.com/au-ts/sddf 4 | branch = main 5 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: libvmm 3 | Source: https://github.com/au-ts/libvmm 4 | 5 | Files: 6 | .gitmodules 7 | flake.lock 8 | build.zig.zon 9 | examples/simple/build.zig.zon 10 | examples/zig/build.zig.zon 11 | examples/rust/Cargo.lock 12 | examples/rust/.cargo/config 13 | examples/rust/aarch64-microkit-minimal.json 14 | Copyright: UNSW 15 | License: BSD-2-Clause 16 | 17 | Files: 18 | docs/assets/passthrough.svg 19 | docs/assets/virtio_console_example.svg 20 | examples/virtio-snd/docs/sound-structure.svg 21 | examples/virtio-snd/docs/virtio-snd.svg 22 | Copyright: UNSW 23 | License: CC-BY-SA-4.0 24 | 25 | Files: 26 | */linux_config 27 | */linux.dts 28 | examples/*/images/linux 29 | examples/*/board/*/linux 30 | examples/virtio/client_vm/linux 31 | examples/virtio/client_vm/rootfs.cpio.gz 32 | examples/framebuffer/board/odroidc4/linux_vu7c.dts 33 | examples/framebuffer/board/odroidc4/linux_with_serial.dts 34 | Copyright: Linux 35 | License: GPL-2.0-or-later 36 | 37 | Files: 38 | */buildroot_config 39 | examples/*/images/rootfs.cpio.gz 40 | examples/*/board/*/rootfs.cpio.gz 41 | Copyright: Buildroot 42 | License: GPL-2.0-or-later 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # License 7 | 8 | The files in the libvmm project are released under standard 9 | open source licenses, identified by [SPDX license tags][1]. See the individual file 10 | headers for details, or use one of the publicly available SPDX tools to generate a bill 11 | of materials. The directory [`LICENSES`][2] contains the text for all licenses 12 | that are mentioned by files in this project. 13 | 14 | Code contributions to this project should be licensed under the [BSD-2-Clause] 15 | license, and documentation under the [CC-BY-SA-4.0] license. 16 | 17 | [1]: https://spdx.org 18 | [2]: LICENSES/ 19 | [BSD-2-CLAUSE]: LICENSES/BSD-2-Clause.txt 20 | [CC-BY-SA-4.0]: LICENSES/CC-BY-SA-4.0.txt 21 | -------------------------------------------------------------------------------- /LICENSES/BSD-2-Clause.txt: -------------------------------------------------------------------------------- 1 | Copyright 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /LICENSES/BSD-3-Clause.txt: -------------------------------------------------------------------------------- 1 | Copyright 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | Copyright 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # libvmm 7 | 8 | The purpose of this project is to make it easy to run virtual machines on top of the seL4 microkernel. 9 | 10 | This project contains three parts: 11 | 12 | * `src/`: The source code of libvmm, a library for virtual-machine-monitors (VMM) to create and manage virtual machines on seL4. 13 | * `examples/`: Examples for using libvmm. 14 | * `tools/`: Tools that are useful when developing systems using virtual machines, but are not 15 | necessary for using the library. 16 | 17 | This project is currently in-development and is frequently changing. It is not ready for 18 | production use. The project also depends on the [seL4 Microkit](https://github.com/seL4/microkit) 19 | SDK and expects to be used in a Microkit environment, in the future this may change such that libvmm 20 | itself is environment agnostic. 21 | 22 | For information on the project and how to use it, please see the [manual](docs/MANUAL.md). 23 | 24 | ## Getting started 25 | 26 | To quickly show off the project, we will run the `simple` example. This example is 27 | intended to simply boot a Linux guest that has serial input and output. 28 | 29 | ### Dependencies 30 | 31 | * GNU Make 32 | * Device Tree Compiler 33 | * Clang/LLVM tools 34 | * QEMU 35 | * Microkit SDK (version 2.0.1) 36 | 37 | For the Microkit SDK, you can download it [here](https://github.com/seL4/microkit/releases/2.0.1). 38 | 39 | For all other dependencies, see the below instructions depending on your machine. 40 | 41 | #### Ubuntu/Debian (apt): 42 | 43 | ```sh 44 | sudo apt install -y make clang lld llvm qemu-system-arm device-tree-compiler 45 | ``` 46 | 47 | #### macOS (Homebrew): 48 | 49 | If you do not have Homebrew installed, you can install it [here](https://brew.sh/). 50 | 51 | It should be noted that while the examples in libvmm can be built 52 | on macOS, if you need to do anything such as compile a custom Linux kernel image 53 | or a guest root file system for developing your own system, you will probably have 54 | less friction on a Linux machine. 55 | 56 | ```sh 57 | # Note that you should make sure that the LLVM tools are in your path after running 58 | # the install command. Homebrew does not do it automatically but does print out a 59 | # message on how to do it. 60 | brew install make qemu dtc llvm 61 | ``` 62 | 63 | #### Nix 64 | 65 | There is a Nix flake available in the repository, so you can get a development shell via: 66 | ```sh 67 | nix develop 68 | ``` 69 | 70 | Note that this will set the `MICROKIT_SDK` environment variable to the SDK path, you do not 71 | need to download the Microkit SDK manually. 72 | 73 | ### Building and running 74 | 75 | First, initialize the repository: 76 | 77 | ```sh 78 | git clone https://github.com/au-ts/libvmm 79 | cd libvmm 80 | git submodule update --init 81 | ``` 82 | 83 | Finally, we can simulate a basic system with a single Linux guest with the 84 | following commands: 85 | ```sh 86 | cd examples/simple 87 | make MICROKIT_BOARD=qemu_virt_aarch64 MICROKIT_SDK=/path/to/sdk qemu 88 | ``` 89 | 90 | You should see Linux booting and be greeted with the buildroot prompt: 91 | ``` 92 | ... 93 | [ 0.410421] Run /init as init process 94 | [ 0.410522] with arguments: 95 | [ 0.410580] /init 96 | [ 0.410627] with environment: 97 | [ 0.410682] HOME=/ 98 | [ 0.410743] TERM=linux 99 | [ 0.410788] earlyprintk=serial 100 | Starting syslogd: OK 101 | Starting klogd: OK 102 | Running sysctl: OK 103 | Saving random seed: [ 3.051374] random: crng init done 104 | OK 105 | Starting network: OK 106 | 107 | Welcome to Buildroot 108 | buildroot login: 109 | ``` 110 | 111 | The username to login is `root`. There is no password required. 112 | 113 | ## Next steps 114 | 115 | Other examples are under `examples/`. Each example has its own documentation for 116 | how to build and use it. 117 | 118 | For more information, have a look at the [manual](docs/MANUAL.md). 119 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | // Copyright 2024, UNSW 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | const std = @import("std"); 5 | const LazyPath = std.Build.LazyPath; 6 | 7 | const src = [_][]const u8{ 8 | "src/guest.c", 9 | "src/util/util.c", 10 | "src/util/printf.c", 11 | "src/virtio/mmio.c", 12 | "src/virtio/block.c", 13 | "src/virtio/console.c", 14 | "src/virtio/net.c", 15 | "src/virtio/sound.c", 16 | }; 17 | 18 | const src_aarch64_vgic_v2 = [_][]const u8{ 19 | "src/arch/aarch64/vgic/vgic_v2.c", 20 | }; 21 | 22 | const src_aarch64_vgic_v3 = [_][]const u8{ 23 | "src/arch/aarch64/vgic/vgic_v3.c", 24 | }; 25 | 26 | const src_aarch64 = [_][]const u8{ 27 | "src/arch/aarch64/vgic/vgic.c", 28 | "src/arch/aarch64/fault.c", 29 | "src/arch/aarch64/psci.c", 30 | "src/arch/aarch64/smc.c", 31 | "src/arch/aarch64/virq.c", 32 | "src/arch/aarch64/linux.c", 33 | "src/arch/aarch64/tcb.c", 34 | "src/arch/aarch64/vcpu.c", 35 | }; 36 | 37 | pub fn build(b: *std.Build) void { 38 | const optimize = b.standardOptimizeOption(.{}); 39 | const target = b.standardTargetOptions(.{}); 40 | 41 | const libmicrokit_opt = b.option([]const u8, "libmicrokit", "Path to libmicrokit.a") orelse null; 42 | const libmicrokit_include_opt = b.option([]const u8, "libmicrokit_include", "Path to the libmicrokit include directory") orelse null; 43 | const libmicrokit_linker_script_opt = b.option([]const u8, "libmicrokit_linker_script", "Path to the libmicrokit linker script") orelse null; 44 | 45 | // Default to vGIC version 2 46 | const arm_vgic_version = b.option(usize, "arm_vgic_version", "ARM vGIC version to emulate") orelse null; 47 | 48 | const libvmm = b.addStaticLibrary(.{ 49 | .name = "vmm", 50 | .target = target, 51 | .optimize = optimize, 52 | }); 53 | 54 | const sddf = b.dependency("sddf", .{ 55 | .target = target, 56 | .optimize = optimize, 57 | .libmicrokit = @as([]const u8, libmicrokit_opt.?), 58 | .libmicrokit_include = @as([]const u8, libmicrokit_include_opt.?), 59 | .libmicrokit_linker_script = @as([]const u8, libmicrokit_linker_script_opt.?), 60 | }); 61 | 62 | const src_arch = switch (target.result.cpu.arch) { 63 | .aarch64 => blk: { 64 | const vgic_src = switch (arm_vgic_version.?) { 65 | 2 => src_aarch64_vgic_v2, 66 | 3 => src_aarch64_vgic_v3, 67 | else => @panic("Unsupported vGIC version given"), 68 | }; 69 | 70 | break :blk src_aarch64 ++ vgic_src; 71 | }, 72 | else => { 73 | std.log.err("Unsupported libvmm architecture given '{s}'", .{ @tagName(target.result.cpu.arch) }); 74 | std.posix.exit(1); 75 | } 76 | }; 77 | libvmm.addCSourceFiles(.{ 78 | .files = &(src ++ src_arch), 79 | .flags = &.{ 80 | "-Wall", 81 | "-Werror", 82 | "-Wno-unused-function", 83 | "-mstrict-align", 84 | "-fno-sanitize=undefined", // @ivanv: ideally we wouldn't have to turn off UBSAN 85 | } 86 | }); 87 | 88 | libvmm.addIncludePath(b.path("include")); 89 | libvmm.addIncludePath(sddf.path("include")); 90 | libvmm.addIncludePath(sddf.path("include/microkit")); 91 | libvmm.addIncludePath(.{ .cwd_relative = libmicrokit_include_opt.? }); 92 | 93 | libvmm.installHeadersDirectory(b.path("include/libvmm"), "libvmm", .{}); 94 | libvmm.installHeadersDirectory(sddf.path("include/sddf"), "sddf", .{}); 95 | 96 | libvmm.linkLibrary(sddf.artifact("util")); 97 | libvmm.linkLibrary(sddf.artifact("util_putchar_debug")); 98 | 99 | b.installArtifact(libvmm); 100 | } 101 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .libvmm, 3 | .version = "0.0.0", 4 | 5 | .dependencies = .{ 6 | .sddf = .{ 7 | .path = "dep/sddf" 8 | }, 9 | }, 10 | .paths = .{ 11 | "LICENSE", 12 | "README.md", 13 | "build.zig", 14 | "build.zig.zon", 15 | "src", 16 | "tools", 17 | }, 18 | .fingerprint = 0x78d558db37c63bd1, 19 | } 20 | -------------------------------------------------------------------------------- /ci/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # CI for the VMM 7 | 8 | This basic CI attempts to build and run systems using the VMM in order to give 9 | the minimal assurance that at least the code is not completely broken. 10 | 11 | ## Running the CI locally 12 | 13 | If you are making changes to libvmm itself, it is a good idea to 14 | first test the changes locally before upstreaming the changes. 15 | 16 | Make sure you have the dependencies listed in the top-level README, you will 17 | also need the `expect` program installed. 18 | 19 | On Ubuntu/Debian: 20 | ```sh 21 | sudo apt install expect 22 | ``` 23 | 24 | On macOS: 25 | ```sh 26 | brew install expect 27 | ``` 28 | 29 | If you are using the Nix shell then you will already have it installed. 30 | 31 | You can then run the CI like so: 32 | ```sh 33 | $ ./ci/examples.sh /path/to/sdk 34 | ``` 35 | 36 | You should then get the `Passed all VMM tests` message. 37 | -------------------------------------------------------------------------------- /ci/buildroot_login.exp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S expect -f 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | set IMAGE [lindex $argv 0] 8 | 9 | # Wait enough (forever) until a long-time boot 10 | set timeout 30 11 | 12 | # Start QEMU 13 | spawn qemu-system-aarch64 \ 14 | -machine virt,virtualization=on,highmem=off,secure=off \ 15 | -cpu cortex-a53 \ 16 | -serial mon:stdio \ 17 | -device loader,file=$IMAGE,addr=0x70000000,cpu-num=0 \ 18 | -m size=2G \ 19 | -nographic 20 | 21 | # Login 22 | expect "buildroot login: " 23 | send "root\n" 24 | expect "# " 25 | 26 | # Attempt to reboot the guest, make sure we can login again 27 | # send "reboot\n" 28 | # expect "buildroot login: " 29 | # send "root\n" 30 | # expect "# " 31 | 32 | send "\x21"; send "x" 33 | -------------------------------------------------------------------------------- /docs/INTERFACE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Virtual machine Library 7 | The virtual machine library provides the following interfaces: 8 | 9 | ` uintptr_t linux_setup_images(uintptr_t` *ram\_start*, 10 | uintptr_t *kernel*, 11 | size_t *kernel\_size*, 12 | uintptr_t *dtb\_src*, 13 | uintptr_t *dtb\_dest*, 14 | size_t *dtb\_size*, 15 | uintptr_t *initrd\_src*, 16 | uintptr_t *initrd\_dest*, 17 | size_t *initrd\_size*)` 18 | 19 | 20 | 21 | `linux_setup_images` is used to copy Linux kernel, initial RAM disk 22 | and device tree into guest RAM ready for execution. The first 23 | argument *ram\_start* is the virtual address of the guest RAM. 24 | 25 | The memory pointed to by *kernel* is checked to see that it is a Linux 26 | kernel `vmlinux`-style 27 | image before it is copied into an appropriate place in the guest RAM. 28 | 29 | 30 | *initrd\_dest* and *dtb\_dest* should match what is in the Device 31 | Tree. 32 | 33 | `linux_setup_images()` returns the kernel entry point if successful, 34 | otherwise -1. 35 | 36 | 37 | `bool virq_controller_init(size_t cpu_id)` 38 | Call to initialise the interrupt controller for a guest. This must be 39 | done before calling `virq_register()` 40 | 41 | `bool virq_register(size_t vcpu_id, size_t virq_num, 42 | virq_ack_fn_t ack_fn, 43 | void *ack_data);` 44 | 45 | Register an interrupt with the virtual interrupt controller. 46 | The arguments are: 47 | 48 | `vpcu_id` the virtual CPU number to devlvier interrupts to 49 | 50 | `virq_num` the IRQ number to deliver 51 | 52 | `ack_fn` a function to be called when the guest acknowledges the 53 | interrupt 54 | 55 | `ack_data` a cookie to be passed to the `ack_fn` when called. 56 | 57 | 58 | `bool virq_inject(size_t vcpu_id, int irq)` 59 | 60 | Inject interrupt `irq` into the virtual interrupt controller on virtual 61 | cpu `vcpu_id` 62 | 63 | `bool virq_register_passthrough(size_t vcpu_id, size_t irq, 64 | microkit_channel irq_ch);` 65 | 66 | Tell the system that interrupt request `irq` is a hardware interrupt 67 | that will be passed through to the guest. `irq_ch` is the channel in 68 | the System Description File that maps to that interrupt. 69 | 70 | `bool virq_handle_passthrough(microkit_channel irq_ch)` 71 | Perform an interrupt injection for the interrupt registered with 72 | `virq_register_passthrough()` for channel `irq_ch` 73 | 74 | `bool guest_start(size_t boot_vcpu_id, uintptr_t kernel_pc, uintptr_t 75 | dtb, uintptr_t initrd);` 76 | Start a guest Linux system, by passing control to the kernel entry 77 | point. 78 | 79 | `void guest_stop(size_t boot_vcpu_id);` 80 | Stop executing the guest. 81 | 82 | `bool guest_restart(size_t boot_vcpu_id, uintptr_t guest_ram_vaddr, 83 | size_t guest_ram_size);` 84 | Restart the guest, possibly with a different image. 85 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Manual 7 | 8 | ## Dependencies 9 | 10 | ### Ubuntu/Debian 11 | 12 | ```sh 13 | sudo apt install texlive-latex-base texlive-fonts-recommended texlive-formats-extra pandoc librsvg2-bin 14 | ``` 15 | 16 | ## Building 17 | 18 | To build the manual as a PDF, run the following command: 19 | ```sh 20 | pandoc MANUAL.md -o MANUAL.pdf 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /docs/shell.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | let 6 | pkgs = import {}; 7 | in 8 | pkgs.mkShell { 9 | buildInputs = with pkgs.buildPackages; [ 10 | texlive.combined.scheme-full 11 | pandoc 12 | librsvg 13 | ]; 14 | } 15 | -------------------------------------------------------------------------------- /examples/rust/.cargo/config: -------------------------------------------------------------------------------- 1 | [unstable] 2 | unstable-options = true 3 | -------------------------------------------------------------------------------- /examples/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | [package] 6 | name = "vmm" 7 | version = "0.1.0" 8 | edition = "2021" 9 | authors = ["Ivan Velickovic "] 10 | license = "BSD-2-Clause" 11 | 12 | [[bin]] 13 | name = "vmm" 14 | path = "src/vmm.rs" 15 | 16 | [dependencies.sel4-microkit] 17 | # Temporarily use fork of rust-seL4 until all the changes 18 | # we need are upstreamed 19 | git = "https://github.com/Ivan-Velickovic/rust-seL4" 20 | branch = "dev" 21 | -------------------------------------------------------------------------------- /examples/rust/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021, Breakaway Consulting Pty. Ltd. 3 | # Copyright 2025, UNSW (ABN 57 195 873 179) 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | # 7 | 8 | ifeq ($(strip $(MICROKIT_SDK)),) 9 | $(error MICROKIT_SDK must be specified) 10 | endif 11 | 12 | # Default build directory, pass BUILD_DIR= to override 13 | BUILD_DIR ?= build 14 | # Default config is a debug build, pass CONFIG= to override 15 | CONFIG ?= debug 16 | # Default board supported is QEMU virt AArch64 17 | BOARD = qemu_virt_aarch64 18 | 19 | # Download the Linux kernel and initrd from Trustworthy Systems website 20 | LINUX ?= 85000f3f42a882e4476e57003d53f2bbec8262b0-linux 21 | INITRD ?= 6dcd1debf64e6d69b178cd0f46b8c4ae7cebe2a5-rootfs.cpio.gz 22 | 23 | # Specify that we use bash for all shell commands 24 | SHELL=/bin/bash 25 | # All dependencies needed to compile the VMM 26 | QEMU := qemu-system-aarch64 27 | DTC := dtc 28 | CC := clang 29 | LD := ld.lld 30 | AR := llvm-ar 31 | MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit 32 | 33 | BOARD_DIR := $(MICROKIT_SDK)/board/$(BOARD)/$(CONFIG) 34 | LIBVMM := $(abspath ../../) 35 | SYSTEM_DESCRIPTION := rust_vmm.system 36 | 37 | IMAGE_DIR := images 38 | DTS := $(IMAGE_DIR)/linux.dts 39 | DTB := $(BUILD_DIR)/linux.dtb 40 | 41 | ELFS := vmm.elf 42 | IMAGE_FILE = $(BUILD_DIR)/loader.img 43 | REPORT_FILE = $(BUILD_DIR)/report.txt 44 | 45 | LIBVMM_OBJS := printf.o \ 46 | virq.o \ 47 | linux.o \ 48 | guest.o \ 49 | psci.o \ 50 | smc.o \ 51 | fault.o \ 52 | util.o \ 53 | vgic.o \ 54 | vgic_v2.o \ 55 | tcb.o \ 56 | vcpu.o 57 | 58 | # Toolchain flags 59 | # FIXME: For optimisation we should consider providing the flag -mcpu. 60 | # FIXME: We should also consider whether -mgeneral-regs-only should be 61 | # used to avoid the use of the FPU and therefore seL4 does not have to 62 | # context switch the FPU. 63 | # Note we only need -Wno-unused-command-line-argument because in Nix 64 | # passes an extra `--gcc-toolchain` flag which we do not need. 65 | CFLAGS := -mstrict-align \ 66 | -g3 \ 67 | -O3 \ 68 | -ffreestanding \ 69 | -nostdlib \ 70 | -Wno-unused-command-line-argument \ 71 | -Wall -Wno-unused-function -Werror \ 72 | -I$(LIBVMM)/include -I$(BOARD_DIR)/include \ 73 | -DBOARD_$(BOARD) \ 74 | -DCONFIG_$(CONFIG) \ 75 | -target aarch64-none-elf 76 | 77 | LDFLAGS := -L$(BOARD_DIR)/lib 78 | LIBS := -lmicrokit -Tmicrokit.ld 79 | 80 | RUST_TARGET_PATH := . 81 | RUST_MICROKIT_TARGET := aarch64-microkit-minimal 82 | TARGET_DIR := $(BUILD_DIR)/target 83 | 84 | RUST_ENV = \ 85 | RUST_TARGET_PATH=$(abspath $(RUST_TARGET_PATH)) \ 86 | SEL4_INCLUDE_DIRS=$(abspath $(BOARD_DIR)/include) \ 87 | MICROKIT_BOARD_DIR=$(abspath $(BOARD_DIR)) \ 88 | BUILD_DIR=$(abspath $(BUILD_DIR)) \ 89 | LINUX_PATH=$(abspath $(LINUX)) \ 90 | INITRD_PATH=$(abspath $(INITRD)) 91 | 92 | RUST_OPTIONS := \ 93 | -Z unstable-options \ 94 | -Z build-std=core,alloc,compiler_builtins \ 95 | -Z build-std-features=compiler-builtins-mem \ 96 | -Z bindeps \ 97 | --release \ 98 | --target $(RUST_MICROKIT_TARGET) \ 99 | --target-dir $(abspath $(TARGET_DIR)) \ 100 | --out-dir $(abspath $(BUILD_DIR)) \ 101 | 102 | all: directories $(IMAGE_FILE) 103 | 104 | qemu: all 105 | $(QEMU) -machine virt,virtualization=on,highmem=off,secure=off \ 106 | -cpu cortex-a53 \ 107 | -serial mon:stdio \ 108 | -device loader,file=$(IMAGE_FILE),addr=0x70000000,cpu-num=0 \ 109 | -m size=2G \ 110 | -nographic 111 | 112 | directories: 113 | mkdir -p $(BUILD_DIR) 114 | 115 | ${LINUX}: 116 | curl -L https://trustworthy.systems/Downloads/libvmm/images/${LINUX}.tar.gz -o $(BUILD_DIR)/$@.tar.gz 117 | mkdir -p $(BUILD_DIR)/linux_download_dir 118 | tar -xf $(BUILD_DIR)/$@.tar.gz -C $(BUILD_DIR)/linux_download_dir 119 | # This eval step is to propagate the image's absolute path into RUST_OPTIONS 120 | # so that src/vmm.rs can include_bytes! the correct file. 121 | $(eval LINUX=$(BUILD_DIR)/linux_download_dir/${LINUX}/linux) 122 | 123 | ${INITRD}: 124 | curl -L https://trustworthy.systems/Downloads/libvmm/images/${INITRD}.tar.gz -o $(BUILD_DIR)/$@.tar.gz 125 | mkdir -p $(BUILD_DIR)/initrd_download_dir 126 | tar xf $(BUILD_DIR)/$@.tar.gz -C $(BUILD_DIR)/initrd_download_dir 127 | $(eval INITRD=$(BUILD_DIR)/initrd_download_dir/${INITRD}/rootfs.cpio.gz) 128 | 129 | $(DTB): $(DTS) 130 | $(DTC) -q -I dts -O dtb $< > $@ 131 | 132 | $(BUILD_DIR)/%.o: $(LIBVMM)/src/%.c Makefile 133 | $(CC) -c $(CFLAGS) $< -o $@ 134 | 135 | $(BUILD_DIR)/%.o: $(LIBVMM)/src/util/%.c Makefile 136 | $(CC) -c $(CFLAGS) $< -o $@ 137 | 138 | $(BUILD_DIR)/%.o: $(LIBVMM)/src/arch/aarch64/%.c Makefile 139 | $(CC) -c $(CFLAGS) $< -o $@ 140 | 141 | $(BUILD_DIR)/%.o: $(LIBVMM)/src/arch/aarch64/vgic/%.c Makefile 142 | $(CC) -c $(CFLAGS) $< -o $@ 143 | 144 | $(BUILD_DIR)/libvmm.a: $(addprefix $(BUILD_DIR)/, $(LIBVMM_OBJS)) 145 | $(AR) -crv $@ $^ 146 | 147 | $(BUILD_DIR)/vmm.elf: src/vmm.rs $(BUILD_DIR)/libvmm.a ${LINUX} ${INITRD} $(DTB) 148 | $(RUST_ENV) \ 149 | cargo build $(RUST_OPTIONS) 150 | 151 | $(IMAGE_FILE) $(REPORT_FILE): $(addprefix $(BUILD_DIR)/, $(ELFS)) $(SYSTEM_DESCRIPTION) 152 | $(MICROKIT_TOOL) $(SYSTEM_DESCRIPTION) --search-path $(BUILD_DIR) --board $(BOARD) --config $(CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) 153 | -------------------------------------------------------------------------------- /examples/rust/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Rust VMM example 7 | 8 | This example shows how to use the 9 | [Rust programming language](https://www.rust-lang.org/) with libvmm. 10 | This example is similar to the [simple example](../simple) in that it boots a 11 | simple Linux guest with a basic command line over serial. 12 | 13 | The Rust VMM only supports the QEMU virt AArch64 platform as the goal of this 14 | example is to simply show how to use the libvmm library (despite being written in 15 | C) and call it from a Rust program. This specific example uses manual FFI 16 | bindings to call into libvmm, but you could use [bindgen](https://github.com/rust-lang/rust-bindgen) 17 | or make your own Rust-like bindings. However for a minimal VMM as we have in this 18 | example, manual bindings are sufficient. 19 | 20 | It should also be noted that this example makes use of the 21 | [rust-seL4](https://github.com/seL4/rust-seL4/) project's support for Microkit. 22 | 23 | ## Building the example 24 | 25 | You will first need to have Rust installed, the instructions for this are [here](https://www.rust-lang.org/tools/install). 26 | 27 | From here, you build the example with: 28 | ```sh 29 | make MICROKIT_SDK=/path/to/sdk 30 | ``` 31 | 32 | By default the build system fetches the Linux kernel and initrd images from 33 | Trustworthy Systems' website on-demand. To override this anduse your own images, 34 | specify `LINUX` and/or `INITRD`. For example: 35 | 36 | ```sh 37 | make MICROKIT_SDK=/path/to/sdk LINUX=/path/to/linux INITRD=/path/to/initrd 38 | ``` 39 | 40 | ## Running the example 41 | 42 | You can build and run the example in a single command with: 43 | ```sh 44 | make MICROKIT_SDK=/path/to/sdk qemu 45 | ``` 46 | -------------------------------------------------------------------------------- /examples/rust/aarch64-microkit-minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "aarch64", 3 | "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", 4 | "disable-redzone": true, 5 | "eh-frame-header": false, 6 | "env": "sel4", 7 | "exe-suffix": ".elf", 8 | "features": "+v8a,+strict-align", 9 | "link-script": "__sel4_ipc_buffer_obj = (_end + 4096 - 1) & ~(4096 - 1);", 10 | "linker": "rust-lld", 11 | "linker-flavor": "ld.lld", 12 | "llvm-target": "aarch64-none-elf", 13 | "max-atomic-width": 128, 14 | "panic-strategy": "abort", 15 | "pre-link-args": { 16 | "ld.lld": [ 17 | "-z", 18 | "max-page-size=4096" 19 | ] 20 | }, 21 | "relocation-model": "static", 22 | "supported-sanitizers": [ 23 | "kcfi", 24 | "kernel-address" 25 | ], 26 | "target-pointer-width": "64" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /examples/rust/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024, UNSW 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | fn main() { 5 | // Our VMM needs to link with libmicrokit, this is how we tell Cargo where to look for it. 6 | match std::env::var("MICROKIT_BOARD_DIR") { 7 | Ok(microkit_board_dir) => println!("cargo:rustc-link-search={microkit_board_dir}/lib"), 8 | Err(e) => println!("Could not get environment variable 'MICROKIT_BOARD_DIR': {e}"), 9 | } 10 | 11 | match std::env::var("BUILD_DIR") { 12 | Ok(build_dir) => println!("cargo:rustc-link-search={build_dir}"), 13 | Err(e) => println!("Could not get environment variable 'BUILD_DIR': {e}"), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/rust/images/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # QEMU virt AArch64 images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v5.18 (commit hash: `4b0986a3613c92f4ec1bdc7f60ec66fea135991f`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | ``` 23 | git clone --depth 1 --branch v5.18 https://github.com/torvalds/linux.git 24 | cp linux_config linux/.config 25 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 26 | ``` 27 | 28 | The path to the image is: `linux/arch/arm64/boot/Image`. 29 | 30 | ## Buildroot RootFS image 31 | 32 | ### Details 33 | * Image name: `rootfs.cpio.gz` 34 | * Config name: `buildroot_config` 35 | * Version: 2022.08-rc2 36 | 37 | ### Instructions for reproducing 38 | 39 | ``` 40 | wget https://buildroot.org/downloads/buildroot-2022.08-rc2.tar.xz 41 | tar xvf buildroot-2022.08-rc2.tar.xz 42 | cp buildroot_config buildroot-2022.08-rc2/.config 43 | make -C buildroot-2022.08-rc2 44 | ``` 45 | 46 | The root filesystem will be located at: `buildroot-2022.08-rc2/output/images/rootfs.cpio.gz` along 47 | with the other buildroot artefacts. 48 | -------------------------------------------------------------------------------- /examples/rust/images/linux.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | interrupt-parent = <0x8001>; 5 | #size-cells = <0x02>; 6 | #address-cells = <0x02>; 7 | compatible = "linux,dummy-virt"; 8 | 9 | aliases { 10 | serial0 = "/pl011@9000000"; 11 | }; 12 | 13 | psci { 14 | migrate = <0xc4000005>; 15 | cpu_on = <0xc4000003>; 16 | cpu_off = <0x84000002>; 17 | cpu_suspend = <0xc4000001>; 18 | method = "smc"; 19 | compatible = "arm,psci-0.2\0arm,psci"; 20 | }; 21 | 22 | memory@40000000 { 23 | reg = <0x00 0x40000000 0x00 0x10000000>; 24 | device_type = "memory"; 25 | }; 26 | 27 | platform@c000000 { 28 | interrupt-parent = <0x8001>; 29 | ranges = <0x00 0x00 0xc000000 0x2000000>; 30 | #address-cells = <0x01>; 31 | #size-cells = <0x01>; 32 | compatible = "qemu,platform\0simple-bus"; 33 | }; 34 | 35 | pl011@9000000 { 36 | clock-names = "uartclk\0apb_pclk"; 37 | clocks = <0x8000 0x8000>; 38 | interrupts = <0x00 0x01 0x04>; 39 | reg = <0x00 0x9000000 0x00 0x1000>; 40 | compatible = "arm,pl011\0arm,primecell"; 41 | }; 42 | 43 | pmu { 44 | interrupts = <0x01 0x07 0x104>; 45 | compatible = "arm,armv8-pmuv3"; 46 | }; 47 | 48 | intc@8000000 { 49 | phandle = <0x8001>; 50 | interrupts = <0x01 0x09 0x04>; 51 | reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>; 52 | compatible = "arm,cortex-a15-gic"; 53 | ranges; 54 | #size-cells = <0x02>; 55 | #address-cells = <0x02>; 56 | interrupt-controller; 57 | #interrupt-cells = <0x03>; 58 | }; 59 | 60 | flash@0 { 61 | bank-width = <0x04>; 62 | reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; 63 | compatible = "cfi-flash"; 64 | status = "disabled"; 65 | }; 66 | 67 | cpus { 68 | #size-cells = <0x00>; 69 | #address-cells = <0x01>; 70 | 71 | cpu@0 { 72 | reg = <0x00>; 73 | compatible = "arm,cortex-a53"; 74 | device_type = "cpu"; 75 | }; 76 | }; 77 | 78 | timer { 79 | interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>; 80 | always-on; 81 | compatible = "arm,armv8-timer\0arm,armv7-timer"; 82 | }; 83 | 84 | apb-pclk { 85 | phandle = <0x8000>; 86 | clock-output-names = "clk24mhz"; 87 | clock-frequency = <0x16e3600>; 88 | #clock-cells = <0x00>; 89 | compatible = "fixed-clock"; 90 | }; 91 | 92 | chosen { 93 | stdout-path = "/pl011@9000000"; 94 | bootargs = "earlycon=pl011,0x9000000 earlyprintk=serial debug loglevel=8"; 95 | linux,stdout-path = "/pl011@9000000"; 96 | linux,initrd-start = <0x4d700000>; 97 | linux,initrd-end = <0x4d800000>; 98 | }; 99 | }; 100 | -------------------------------------------------------------------------------- /examples/rust/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | [toolchain] 6 | channel = "nightly-2024-01-06" 7 | components = [ "rustfmt", "rust-src", "rustc-dev", "llvm-tools-preview" ] 8 | 9 | -------------------------------------------------------------------------------- /examples/rust/rust_vmm.system: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/simple/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | 7 | BUILD_DIR ?= build 8 | export MICROKIT_CONFIG ?= debug 9 | 10 | ifeq ($(strip $(MICROKIT_SDK)),) 11 | $(error MICROKIT_SDK must be specified) 12 | endif 13 | export override MICROKIT_SDK:=$(abspath $(MICROKIT_SDK)) 14 | 15 | ifeq ($(strip $(MICROKIT_BOARD)),) 16 | $(error MICROKIT_BOARD must be specified) 17 | endif 18 | 19 | export BUILD_DIR:=$(abspath $(BUILD_DIR)) 20 | export MICROKIT_SDK:=$(abspath $(MICROKIT_SDK)) 21 | export EXAMPLE_DIR:=$(abspath .) 22 | 23 | export TARGET := aarch64-none-elf 24 | export CC := clang 25 | export LD := ld.lld 26 | export AS := llvm-as 27 | export AR := llvm-ar 28 | export DTC := dtc 29 | export RANLIB := llvm-ranlib 30 | export MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit 31 | export SDDF=$(abspath ../../dep/sddf) 32 | export LIBVMM=$(abspath ../../) 33 | 34 | IMAGE_FILE := $(BUILD_DIR)/loader.img 35 | REPORT_FILE := $(BUILD_DIR)/report.txt 36 | 37 | all: $(IMAGE_FILE) 38 | 39 | qemu $(IMAGE_FILE) $(REPORT_FILE) clean clobber: $(BUILD_DIR)/Makefile FORCE 40 | $(MAKE) -C $(BUILD_DIR) MICROKIT_SDK=$(MICROKIT_SDK) $(notdir $@) 41 | 42 | $(BUILD_DIR)/Makefile: simple.mk 43 | mkdir -p $(BUILD_DIR) 44 | cp simple.mk $@ 45 | 46 | FORCE: 47 | -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # A simple VMM for running Linux guests 7 | 8 | This example is a minimal VMM that supports Linux guests and a basic 9 | buildroot/BusyBox root file system. This gives a basic command-line with some 10 | common Linux utilities. 11 | 12 | The example currently works on the following platforms: 13 | * QEMU virt AArch64 14 | * HardKernel Odroid-C4 15 | * Avnet MaaXBoard 16 | 17 | ## Building with Make 18 | 19 | ```sh 20 | make MICROKIT_BOARD= MICROKIT_SDK=/path/to/sdk 21 | ``` 22 | 23 | Where `` is one of: 24 | * `qemu_virt_aarch64` 25 | * `odroidc4` 26 | * `maaxboard` 27 | 28 | Other configuration options can be passed to the Makefile such as `MICROKIT_CONFIG` 29 | and `BUILD_DIR`, see the Makefile for details. 30 | 31 | If you would like to simulate the QEMU board you can run the following command: 32 | ```sh 33 | make MICROKIT_BOARD=qemu_virt_aarch64 MICROKIT_SDK=/path/to/sdk qemu 34 | ``` 35 | 36 | This will build the example code as well as run the QEMU command to simulate a 37 | system running the whole system. 38 | 39 | By default the build system fetches the Linux kernel and initrd images from 40 | Trustworthy Systems' website on-demand. To override this anduse your own images, 41 | specify `LINUX` and/or `INITRD`. For example: 42 | 43 | ```sh 44 | make MICROKIT_BOARD=qemu_virt_aarch64 MICROKIT_SDK=/path/to/sdk LINUX=/path/to/linux INITRD=/path/to/initrd qemu 45 | ``` 46 | 47 | ## Building with Zig 48 | 49 | For educational purposes, you can also build and run this example using the 50 | [Zig](https://ziglang.org/) build system. 51 | 52 | This example expects to be built with Zig 0.13.*. 53 | 54 | You can download Zig [here](https://ziglang.org/download/). 55 | 56 | ```sh 57 | zig build -Dsdk=/path/to/sdk -Dboard= 58 | ``` 59 | 60 | Where `` is one of: 61 | * `qemu_virt_aarch64` 62 | * `odroidc4` 63 | * `maaxboard` 64 | 65 | If you are building for QEMU then you can also run QEMU by doing: 66 | ```sh 67 | zig build -Dsdk=/path/to/sdk -Dboard=qemu_virt_aarch64 qemu 68 | ``` 69 | 70 | Similar with Make, the Zig build system will fetch Linux kernel and initrd images 71 | Trustworthy Systems' website. To use your own images, specify `-Dlinux` and/or 72 | `-Dinitrd`. For example: 73 | 74 | ```sh 75 | zig build -Dsdk=/path/to/sdk -Dboard=qemu_virt_aarch64 -Dlinux=/path/to/linux -Dinitrd=/path/to/initrd qemu 76 | ``` 77 | 78 | You can view other options by doing: 79 | ```sh 80 | zig build -Dsdk=/path/to/sdk -Dboard= -h 81 | ``` 82 | 83 | -------------------------------------------------------------------------------- /examples/simple/board/maaxboard/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # MaaXBoard images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v6.1 (commit hash: `830b3c68c1fb1e9176028d02ef86f3cf76aa2476`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | ``` 23 | git clone --depth 1 --branch v6.1 https://github.com/torvalds/linux.git 24 | cp linux_config linux/.config 25 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 26 | ``` 27 | 28 | The path to the image is: `linux/arch/arm64/boot/Image`. 29 | 30 | ## Buildroot RootFS image 31 | 32 | ### Details 33 | * Image name: `rootfs.cpio.gz` 34 | * Config name: `buildroot_config` 35 | * Version: 2023.08.1 36 | 37 | ### Instructions for reproducing 38 | 39 | ``` 40 | wget https://buildroot.org/downloads/buildroot-2023.08.1.tar.xz 41 | tar xvf buildroot-2023.08.1.tar.xz 42 | cp buildroot_config buildroot-2023.08.1/.config 43 | make -C buildroot-2023.08.1 44 | ``` 45 | 46 | The root filesystem will be located at: `buildroot-2023.08.1/output/images/rootfs.cpio.gz` along 47 | with the other buildroot artefacts. 48 | -------------------------------------------------------------------------------- /examples/simple/board/maaxboard/overlay.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | memory@40000000 { 8 | device_type = "memory"; 9 | reg = <0x00 0x40000000 0x00 0x10000000>; 10 | }; 11 | 12 | soc@0 { 13 | bus@30800000 { 14 | spi@30820000 { 15 | status = "disabled"; 16 | }; 17 | 18 | spi@30830000 { 19 | status = "disabled"; 20 | }; 21 | 22 | spi@30840000 { 23 | status = "disabled"; 24 | }; 25 | 26 | ethernet@30be0000 { 27 | status = "disabled"; 28 | }; 29 | 30 | mailbox@30aa0000 { 31 | status = "disabled"; 32 | }; 33 | 34 | mmc@30b40000 { 35 | status = "disabled"; 36 | }; 37 | 38 | mmc@30b50000 { 39 | status = "disabled"; 40 | }; 41 | 42 | spi@30bb0000 { 43 | status = "disabled"; 44 | }; 45 | 46 | i2c@30a20000 { 47 | status = "disabled"; 48 | }; 49 | }; 50 | 51 | bus@30000000 { 52 | gpio@30210000 { 53 | status = "disabled"; 54 | }; 55 | 56 | gpio@30220000 { 57 | status = "disabled"; 58 | }; 59 | 60 | gpio@30230000 { 61 | status = "disabled"; 62 | }; 63 | 64 | gpio@30240000 { 65 | status = "disabled"; 66 | }; 67 | 68 | watchdog@30280000 { 69 | status = "disabled"; 70 | }; 71 | 72 | efuse@30350000 { 73 | status = "disabled"; 74 | }; 75 | }; 76 | 77 | usb@38200000 { 78 | status = "disabled"; 79 | }; 80 | 81 | usb-phy@382f0040 { 82 | status = "disabled"; 83 | }; 84 | }; 85 | 86 | chosen { 87 | stdout-path = "/soc@0/bus@30800000/serial@30860000"; 88 | linux,initrd-start = <0x00000000 0x4c000000>; 89 | linux,initrd-end = <0x00000000 0x4e000000>; 90 | bootargs = "console=ttymxc0,115200 earlycon=ec_imx6q,0x30860000,115200 nosmp rw debug ignore_loglevel initcall_blacklist=imx8_soc_init pci=nomsi earlyprintk=serial maxcpus=1"; 91 | }; 92 | }; 93 | -------------------------------------------------------------------------------- /examples/simple/board/maaxboard/simple.system: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/simple/board/odroidc4/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Odroid-C4 images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v6.1 (commit hash: `830b3c68c1fb1e9176028d02ef86f3cf76aa2476`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | 23 | ``` 24 | git clone --depth 1 --branch v6.1 https://github.com/torvalds/linux.git 25 | cp linux_config linux/.config 26 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- linux_config all -j$(nproc) 27 | ``` 28 | 29 | The path to the image will be: `linux/arm64/boot/Image`. 30 | 31 | ## Buildroot RootFS image 32 | 33 | Note that buildoot currently does not list a configuration for OdroidC4 so we just 34 | use the OdroidC2 configuration. 35 | -------------------------------------------------------------------------------- /examples/simple/board/odroidc4/overlay.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | memory@0 { 8 | device_type = "memory"; 9 | reg = <0x00 0x20000000 0x00 0x10000000>; 10 | }; 11 | 12 | /* Give Linux a smaller amount of memory for DMA than normal since we only have 256MB of RAM */ 13 | reserved-memory { 14 | linux,cma { 15 | compatible = "shared-dma-pool"; 16 | reusable; 17 | size = <0x00 0x1000000>; 18 | alignment = <0x00 0x400000>; 19 | alloc-ranges = <0x00 0x8000000 0x10000000>; 20 | linux,cma-default; 21 | }; 22 | }; 23 | 24 | secure-monitor { 25 | status = "disabled"; 26 | }; 27 | 28 | soc { 29 | ethernet@ff3f0000 { 30 | status = "disabled"; 31 | }; 32 | 33 | bus@ff800000 { 34 | ao-secure@140 { 35 | status = "disabled"; 36 | }; 37 | 38 | adc@9000 { 39 | status = "disabled"; 40 | }; 41 | }; 42 | 43 | vpu@ff900000 { 44 | status = "disabled"; 45 | }; 46 | 47 | usb@ffe09000 { 48 | status = "disabled"; 49 | }; 50 | 51 | sd@ffe05000 { 52 | status = "disabled"; 53 | }; 54 | 55 | mmc@ffe07000 { 56 | status = "disabled"; 57 | }; 58 | 59 | gpu@ffe40000 { 60 | status = "disabled"; 61 | }; 62 | }; 63 | 64 | chosen { 65 | linux,stdout-path = "/soc/bus@ff800000/serial@3000"; 66 | stdout-path = "/soc/bus@ff800000/serial@3000"; 67 | linux,initrd-end = <0x00000000 0x2e000000>; 68 | linux,initrd-start = <0x00000000 0x2d700000>; 69 | bootargs = "console=ttyAML0,115200n8 root=/dev/ram0 nosmp rw debug loglevel=8 pci=nomsi earlyprintk=serial maxcpus=1"; 70 | }; 71 | }; 72 | -------------------------------------------------------------------------------- /examples/simple/board/odroidc4/simple.system: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/simple/board/qemu_virt_aarch64/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # QEMU virt AArch64 images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v5.18 (commit hash: `4b0986a3613c92f4ec1bdc7f60ec66fea135991f`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | ``` 23 | git clone --depth 1 --branch v5.18 https://github.com/torvalds/linux.git 24 | cp linux_config linux/.config 25 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 26 | ``` 27 | 28 | The path to the image is: `linux/arch/arm64/boot/Image`. 29 | 30 | ## Buildroot RootFS image 31 | 32 | ### Details 33 | * Image name: `rootfs.cpio.gz` 34 | * Config name: `buildroot_config` 35 | * Version: 2022.08-rc2 36 | 37 | ### Instructions for reproducing 38 | 39 | ``` 40 | wget https://buildroot.org/downloads/buildroot-2022.08-rc2.tar.xz 41 | tar xvf buildroot-2022.08-rc2.tar.xz 42 | cp buildroot_config buildroot-2022.08-rc2/.config 43 | make -C buildroot-2022.08-rc2 44 | ``` 45 | 46 | The root filesystem will be located at: `buildroot-2022.08-rc2/output/images/rootfs.cpio.gz` along 47 | with the other buildroot artefacts. 48 | -------------------------------------------------------------------------------- /examples/simple/board/qemu_virt_aarch64/linux.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | interrupt-parent = <0x8001>; 5 | #size-cells = <0x02>; 6 | #address-cells = <0x02>; 7 | compatible = "linux,dummy-virt"; 8 | 9 | psci { 10 | migrate = <0xc4000005>; 11 | cpu_on = <0xc4000003>; 12 | cpu_off = <0x84000002>; 13 | cpu_suspend = <0xc4000001>; 14 | method = "smc"; 15 | compatible = "arm,psci-0.2\0arm,psci"; 16 | }; 17 | 18 | memory@40000000 { 19 | reg = <0x00 0x40000000 0x00 0x80000000>; 20 | device_type = "memory"; 21 | }; 22 | 23 | platform@c000000 { 24 | interrupt-parent = <0x8001>; 25 | ranges = <0x00 0x00 0xc000000 0x2000000>; 26 | #address-cells = <0x01>; 27 | #size-cells = <0x01>; 28 | compatible = "qemu,platform\0simple-bus"; 29 | }; 30 | 31 | pl011@9000000 { 32 | clock-names = "uartclk\0apb_pclk"; 33 | clocks = <0x8000 0x8000>; 34 | interrupts = <0x00 0x01 0x04>; 35 | reg = <0x00 0x9000000 0x00 0x1000>; 36 | compatible = "arm,pl011\0arm,primecell"; 37 | }; 38 | 39 | pmu { 40 | interrupts = <0x01 0x07 0x104>; 41 | compatible = "arm,armv8-pmuv3"; 42 | }; 43 | 44 | intc@8000000 { 45 | phandle = <0x8001>; 46 | interrupts = <0x01 0x09 0x04>; 47 | reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>; 48 | compatible = "arm,cortex-a15-gic"; 49 | ranges; 50 | #size-cells = <0x02>; 51 | #address-cells = <0x02>; 52 | interrupt-controller; 53 | #interrupt-cells = <0x03>; 54 | }; 55 | 56 | flash@0 { 57 | bank-width = <0x04>; 58 | reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; 59 | compatible = "cfi-flash"; 60 | }; 61 | 62 | cpus { 63 | #size-cells = <0x00>; 64 | #address-cells = <0x01>; 65 | 66 | cpu@0 { 67 | reg = <0x00>; 68 | compatible = "arm,cortex-a53"; 69 | device_type = "cpu"; 70 | }; 71 | }; 72 | 73 | timer { 74 | interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>; 75 | always-on; 76 | compatible = "arm,armv8-timer\0arm,armv7-timer"; 77 | }; 78 | 79 | apb-pclk { 80 | phandle = <0x8000>; 81 | clock-output-names = "clk24mhz"; 82 | clock-frequency = <0x16e3600>; 83 | #clock-cells = <0x00>; 84 | compatible = "fixed-clock"; 85 | }; 86 | 87 | chosen { 88 | stdout-path = "/pl011@9000000"; 89 | rng-seed = <0x8f0ee46 0xecbe7263 0xc724a577 0x271cf683 0xdd190daf 0x103bff62 0x5f2496fb 0xee0cf760>; 90 | kaslr-seed = <0x198a65b3 0x8b3cef37>; 91 | }; 92 | }; 93 | -------------------------------------------------------------------------------- /examples/simple/board/qemu_virt_aarch64/overlay.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | /* Need to specify how much RAM the guest will have */ 8 | memory@40000000 { 9 | device_type = "memory"; 10 | reg = <0x00 0x40000000 0x00 0x10000000>; 11 | }; 12 | 13 | flash@0 { 14 | status = "disabled"; 15 | }; 16 | 17 | chosen { 18 | stdout-path = "/pl011@9000000"; 19 | bootargs = "earlycon=pl011,0x9000000 earlyprintk=serial debug loglevel=8"; 20 | linux,stdout-path = "/pl011@9000000"; 21 | linux,initrd-start = <0x4d700000>; 22 | linux,initrd-end = <0x4d800000>; 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /examples/simple/board/qemu_virt_aarch64/simple.system: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | 40 | 41 | 45 | 46 | 52 | 53 | 59 | 60 | 61 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/simple/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .simple, 3 | .version = "0.0.0", 4 | 5 | .dependencies = .{ 6 | .libvmm = .{ 7 | .path = "../../" 8 | }, 9 | .linux = .{ 10 | .url = "https://trustworthy.systems/Downloads/libvmm/images/85000f3f42a882e4476e57003d53f2bbec8262b0-linux.tar.gz", 11 | .hash = "linux-0.0.0-AAAAAACKHwKLhs9sy_2Ma-_f1lh-vvTVxNfPElIDod2h", 12 | .lazy = true 13 | }, 14 | .qemu_virt_aarch64_initrd = .{ 15 | .url = "https://trustworthy.systems/Downloads/libvmm/images/6dcd1debf64e6d69b178cd0f46b8c4ae7cebe2a5-rootfs.cpio.gz.tar.gz", 16 | .hash = "rootfs_cpio_gz-0.0.0-AAAAAFuyCwDpUITQ72Vk5neE9sZHvBPQTXdJFAvR05wM", 17 | .lazy = true 18 | }, 19 | .odroidc4_initrd = .{ 20 | .url = "https://trustworthy.systems/Downloads/libvmm/images/ec78fdfd660bc9358e4d7dcb73b55d88339ba19d-rootfs.cpio.gz.tar.gz", 21 | .hash = "rootfs_cpio_gz-0.0.0-AAAAAIteFwDCe2qFpuICyNBmCl0bQcKNNMxKXFRIus2P", 22 | .lazy = true 23 | }, 24 | .maaxboard_initrd = .{ 25 | .url = "https://trustworthy.systems/Downloads/libvmm/images/ce255a92feb25d09b5a0336b798523f35c2f8fe0-rootfs.cpio.gz.tar.gz", 26 | .hash = "rootfs_cpio_gz-0.0.0-AAAAAOBNFwDKwj6sAPDBtdu7X77Jg5Nl3lj_wRViitVr", 27 | .lazy = true 28 | }, 29 | }, 30 | .paths = .{ 31 | "build.zig", 32 | "build.zig.zon", 33 | }, 34 | .fingerprint = 0xc17b3d0243e9416e, 35 | } 36 | -------------------------------------------------------------------------------- /examples/simple/simple.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | QEMU := qemu-system-aarch64 7 | 8 | MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit 9 | 10 | BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) 11 | SYSTEM_DIR := $(EXAMPLE_DIR)/board/$(MICROKIT_BOARD) 12 | SYSTEM_FILE := $(SYSTEM_DIR)/simple.system 13 | IMAGE_FILE := loader.img 14 | REPORT_FILE := report.txt 15 | 16 | vpath %.c $(LIBVMM) $(EXAMPLE_DIR) 17 | 18 | IMAGES := vmm.elf 19 | 20 | LINUX ?= 85000f3f42a882e4476e57003d53f2bbec8262b0-linux 21 | ifeq ($(strip $(MICROKIT_BOARD)), qemu_virt_aarch64) 22 | INITRD ?= 6dcd1debf64e6d69b178cd0f46b8c4ae7cebe2a5-rootfs.cpio.gz 23 | else ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) 24 | INITRD ?= ec78fdfd660bc9358e4d7dcb73b55d88339ba19d-rootfs.cpio.gz 25 | else ifeq ($(strip $(MICROKIT_BOARD)), maaxboard) 26 | INITRD ?= ce255a92feb25d09b5a0336b798523f35c2f8fe0-rootfs.cpio.gz 27 | else 28 | $(error Unsupported MICROKIT_BOARD given) 29 | endif 30 | 31 | CFLAGS := \ 32 | -mstrict-align \ 33 | -ffreestanding \ 34 | -g3 -O3 -Wall \ 35 | -Wno-unused-function \ 36 | -DMICROKIT_CONFIG_$(MICROKIT_CONFIG) \ 37 | -DBOARD_$(MICROKIT_BOARD) \ 38 | -I$(BOARD_DIR)/include \ 39 | -I$(LIBVMM)/include \ 40 | -I$(SDDF)/include \ 41 | -I$(SDDF)/include/microkit \ 42 | -MD \ 43 | -MP \ 44 | -target $(TARGET) 45 | 46 | LDFLAGS := -L$(BOARD_DIR)/lib 47 | LIBS := --start-group -lmicrokit -Tmicrokit.ld libvmm.a --end-group 48 | 49 | CHECK_FLAGS_BOARD_MD5:=.board_cflags-$(shell echo -- $(CFLAGS) $(BOARD) $(MICROKIT_CONFIG) | shasum | sed 's/ *-//') 50 | 51 | $(CHECK_FLAGS_BOARD_MD5): 52 | -rm -f .board_cflags-* 53 | touch $@ 54 | 55 | vmm.elf: vmm.o images.o 56 | $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ 57 | 58 | all: loader.img 59 | 60 | -include vmm.d 61 | 62 | $(IMAGES): libvmm.a 63 | 64 | $(IMAGE_FILE) $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) 65 | $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) 66 | 67 | ${LINUX}: 68 | curl -L https://trustworthy.systems/Downloads/libvmm/images/${LINUX}.tar.gz -o $@.tar.gz 69 | mkdir -p linux_download_dir 70 | tar -xf $@.tar.gz -C linux_download_dir 71 | cp linux_download_dir/${LINUX}/linux ${LINUX} 72 | 73 | ${INITRD}: 74 | curl -L https://trustworthy.systems/Downloads/libvmm/images/${INITRD}.tar.gz -o $@.tar.gz 75 | mkdir -p initrd_download_dir 76 | tar xf $@.tar.gz -C initrd_download_dir 77 | cp initrd_download_dir/${INITRD}/rootfs.cpio.gz ${INITRD} 78 | 79 | vm.dts: $(SYSTEM_DIR)/linux.dts $(SYSTEM_DIR)/overlay.dts 80 | $(LIBVMM)/tools/dtscat $^ > $@ 81 | 82 | vm.dtb: vm.dts 83 | $(DTC) -q -I dts -O dtb $< > $@ 84 | 85 | vmm.o: $(EXAMPLE_DIR)/vmm.c $(CHECK_FLAGS_BOARD_MD5) 86 | $(CC) $(CFLAGS) -c -o $@ $< 87 | 88 | images.o: $(LIBVMM)/tools/package_guest_images.S $(LINUX) $(INITRD) vm.dtb 89 | $(CC) -c -g3 -x assembler-with-cpp \ 90 | -DGUEST_KERNEL_IMAGE_PATH=\"${LINUX}\" \ 91 | -DGUEST_DTB_IMAGE_PATH=\"vm.dtb\" \ 92 | -DGUEST_INITRD_IMAGE_PATH=\"${INITRD}\" \ 93 | -target $(TARGET) \ 94 | $(LIBVMM)/tools/package_guest_images.S -o $@ 95 | 96 | include $(LIBVMM)/vmm.mk 97 | 98 | qemu: $(IMAGE_FILE) 99 | if ! command -v $(QEMU) > /dev/null 2>&1; then echo "Could not find dependency: qemu-system-aarch64"; exit 1; fi 100 | $(QEMU) -machine virt,virtualization=on,highmem=off,secure=off \ 101 | -cpu cortex-a53 \ 102 | -serial mon:stdio \ 103 | -device loader,file=$(IMAGE_FILE),addr=0x70000000,cpu-num=0 \ 104 | -m size=2G \ 105 | -nographic 106 | 107 | clean:: 108 | $(RM) -f *.elf .depend* $ 109 | find . -name \*.[do] |xargs --no-run-if-empty rm 110 | 111 | clobber:: clean 112 | rm -f *.a 113 | rm -f $(IMAGE_FILE) $(REPORT_FILE) 114 | -------------------------------------------------------------------------------- /examples/virtio-snd/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | 7 | BUILD_DIR ?= build 8 | export MICROKIT_CONFIG ?= debug 9 | 10 | ifeq ($(strip $(MICROKIT_SDK)),) 11 | $(error MICROKIT_SDK must be specified) 12 | endif 13 | export override MICROKIT_SDK:=$(abspath $(MICROKIT_SDK)) 14 | 15 | ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) 16 | export UART_DRIVER := meson 17 | export CPU := cortex-a55 18 | else ifeq ($(strip $(MICROKIT_BOARD)), qemu_virt_aarch64) 19 | export UART_DRIVER := arm 20 | export CPU := cortex-a53 21 | QEMU := qemu-system-aarch64 22 | else 23 | $(error Unsupported MICROKIT_BOARD given) 24 | endif 25 | 26 | export BUILD_DIR:=$(abspath $(BUILD_DIR)) 27 | export MICROKIT_SDK:=$(abspath $(MICROKIT_SDK)) 28 | export VIRTIO_EXAMPLE:=$(abspath .) 29 | 30 | export TARGET := aarch64-none-elf 31 | export CC := clang 32 | export CC_USERLEVEL := zig cc 33 | export LD := ld.lld 34 | export AS := llvm-as 35 | export AR := llvm-ar 36 | export DTC := dtc 37 | export RANLIB := llvm-ranlib 38 | export MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit 39 | export SDDF=$(abspath ../../dep/sddf) 40 | export LIBVMM=$(abspath ../../) 41 | 42 | # If on macOS, override backend to `coreaudio` 43 | export QEMU_SND_BACKEND := alsa 44 | export QEMU_SND_FRONTEND := hda 45 | 46 | IMAGE_FILE := $(BUILD_DIR)/loader.img 47 | REPORT_FILE := $(BUILD_DIR)/report.txt 48 | 49 | all: $(IMAGE_FILE) 50 | 51 | qemu $(IMAGE_FILE) $(REPORT_FILE) clean clobber: $(BUILD_DIR)/Makefile FORCE 52 | $(MAKE) -C $(BUILD_DIR) MICROKIT_SDK=$(MICROKIT_SDK) $(notdir $@) 53 | 54 | $(BUILD_DIR)/Makefile: virtio_snd.mk 55 | mkdir -p $(BUILD_DIR) 56 | cp virtio_snd.mk $@ 57 | 58 | FORCE: 59 | -------------------------------------------------------------------------------- /examples/virtio-snd/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # virtIO sound example 7 | 8 | This example shows off the virtIO sound support that libvmm provides using the 9 | [seL4 Device Driver Framework](https://github.com/au-ts/sddf) to virtualise 10 | the hardware. 11 | 12 | We do not have a native driver for sound, but instead leverage a Linux VM that 13 | contains a driver and have a user-level program to convert sDDF requests/responses 14 | into Linux calls into the ALSA library. 15 | 16 | Currently this example is not integrated with the [virtIO example](../virtio) 17 | due to issues with using multiple driver VMs in the same system. Therefore, temporarily, 18 | we have a separate virtIO example for the emulating the sound device. You can find more 19 | information [in this GitHub issue](https://github.com/au-ts/libvmm/issues/60). 20 | 21 | The example currently works on the following platforms: 22 | * QEMU virt AArch64 23 | * HardKernel Odroid-C4 24 | 25 | For details on the internals of the example and instructions on building and running it, 26 | see [docs/SOUND.md](docs/SOUND.md). 27 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/odroidc4/client_vm/dts/overlays/init.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | / { 8 | memory@0 { 9 | reg = <0x00 0x20000000 0x00 0x8000000>; 10 | device_type = "memory"; 11 | }; 12 | 13 | chosen { 14 | bootargs = "console=hvc0 earlycon=hvc0 earlyprintk=serial debug loglevel=8"; 15 | linux,stdout-path = "/virtio-console@0130000"; 16 | stdout-path = "/virtio-console@0130000"; 17 | linux,initrd-start = <0x26000000>; 18 | linux,initrd-end = <0x27000000>; 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/odroidc4/client_vm/dts/overlays/io.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | virtio-console@0130000 { 8 | compatible = "virtio,mmio"; 9 | reg = <0x00 0x130000 0x00 0x200>; 10 | interrupts = <0x00 42 0x04>; 11 | }; 12 | 13 | virtio-snd@0170000 { 14 | compatible = "virtio,mmio"; 15 | reg = <0x00 0x170000 0x00 0x200>; 16 | interrupts = <0x00 44 0x04>; 17 | }; 18 | }; -------------------------------------------------------------------------------- /examples/virtio-snd/board/odroidc4/snd_driver_vm/dts/overlays/init.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | / { 8 | memory@0 { 9 | reg = <0x00 0x20000000 0x00 0x8000000>; 10 | device_type = "memory"; 11 | }; 12 | 13 | chosen { 14 | #address-cells = <0x02>; 15 | #size-cells = <0x02>; 16 | ranges; 17 | linux,stdout-path = "/virtio-console@0130000"; 18 | stdout-path = "/virtio-console@0130000"; 19 | linux,initrd-start = <0x26000000>; 20 | linux,initrd-end = <0x27000000>; 21 | bootargs = "console=hvc0 earlycon=hvc0 pci=nomsi earlyprintk=serial debug loglevel=8 uio_pdrv_genirq.of_id=generic-uio"; 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/odroidc4/snd_driver_vm/dts/overlays/io.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | virtio-console@0130000 { 8 | compatible = "virtio,mmio"; 9 | reg = <0x00 0x130000 0x00 0x200>; 10 | interrupts = <0x00 42 0x04>; 11 | }; 12 | 13 | uio { 14 | compatible = "generic-uio\0uio"; 15 | reg = < 16 | 0x00 0x6f00000 0x00 0x1000 17 | 0x00 0x7000000 0x00 0xC00000 18 | 0x00 0x9400000 0x00 0x200000 19 | >; 20 | interrupts = <0x00 0x12 0x04>; 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/client_vm/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # QEMU virt AArch64 images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v5.18 (commit hash: `4b0986a3613c92f4ec1bdc7f60ec66fea135991f`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | ``` 23 | git clone --depth 1 --branch v5.18 https://github.com/torvalds/linux.git 24 | cp linux_config linux/.config 25 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 26 | ``` 27 | 28 | The path to the image is: `linux/arch/arm64/boot/Image`. 29 | 30 | ## Buildroot RootFS image 31 | 32 | ### Details 33 | * Image name: `rootfs.cpio.gz` 34 | * Config name: `buildroot_config` 35 | * Version: 2022.08-rc2 36 | 37 | ### Instructions for reproducing 38 | 39 | ``` 40 | wget https://buildroot.org/downloads/buildroot-2022.08-rc2.tar.xz 41 | tar xvf buildroot-2022.08-rc2.tar.xz 42 | cp buildroot_config buildroot-2022.08-rc2/.config 43 | make -C buildroot-2022.08-rc2 44 | ``` 45 | 46 | The root filesystem will be located at: `buildroot-2022.08-rc2/output/images/rootfs.cpio.gz` along 47 | with the other buildroot artefacts. 48 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/client_vm/dts/linux.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | interrupt-parent = <0x8002>; 5 | model = "linux,dummy-virt"; 6 | #size-cells = <0x02>; 7 | #address-cells = <0x02>; 8 | compatible = "linux,dummy-virt"; 9 | 10 | aliases { 11 | serial0 = "/pl011@9000000"; 12 | }; 13 | 14 | psci { 15 | migrate = <0xc4000005>; 16 | cpu_on = <0xc4000003>; 17 | cpu_off = <0x84000002>; 18 | cpu_suspend = <0xc4000001>; 19 | method = "smc"; 20 | compatible = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; 21 | }; 22 | 23 | platform-bus@c000000 { 24 | interrupt-parent = <0x8002>; 25 | ranges = <0x00 0x00 0xc000000 0x2000000>; 26 | #address-cells = <0x01>; 27 | #size-cells = <0x01>; 28 | compatible = "qemu,platform\0simple-bus"; 29 | }; 30 | 31 | pl011@9000000 { 32 | clock-names = "uartclk\0apb_pclk"; 33 | clocks = <0x8000 0x8000>; 34 | interrupts = <0x00 0x01 0x04>; 35 | reg = <0x00 0x9000000 0x00 0x1000>; 36 | compatible = "arm,pl011\0arm,primecell"; 37 | status = "disabled"; 38 | }; 39 | 40 | pmu { 41 | interrupts = <0x01 0x07 0x104>; 42 | compatible = "arm,armv8-pmuv3"; 43 | }; 44 | 45 | intc@8000000 { 46 | phandle = <0x8002>; 47 | reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>; 48 | compatible = "arm,cortex-a15-gic"; 49 | ranges; 50 | #size-cells = <0x02>; 51 | #address-cells = <0x02>; 52 | interrupt-controller; 53 | #interrupt-cells = <0x03>; 54 | 55 | // v2m@8020000 { 56 | // phandle = <0x8003>; 57 | // reg = <0x00 0x8020000 0x00 0x1000>; 58 | // msi-controller; 59 | // compatible = "arm,gic-v2m-frame"; 60 | // }; 61 | }; 62 | 63 | flash@0 { 64 | bank-width = <0x04>; 65 | reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; 66 | compatible = "cfi-flash"; 67 | status = "disabled"; 68 | }; 69 | 70 | cpus { 71 | #size-cells = <0x00>; 72 | #address-cells = <0x01>; 73 | 74 | cpu@0 { 75 | phandle = <0x8001>; 76 | reg = <0x00>; 77 | compatible = "arm,cortex-a53"; 78 | device_type = "cpu"; 79 | }; 80 | }; 81 | 82 | timer { 83 | interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>; 84 | always-on; 85 | compatible = "arm,armv8-timer\0arm,armv7-timer"; 86 | }; 87 | 88 | apb-pclk { 89 | phandle = <0x8000>; 90 | clock-output-names = "clk24mhz"; 91 | clock-frequency = <0x16e3600>; 92 | #clock-cells = <0x00>; 93 | compatible = "fixed-clock"; 94 | }; 95 | }; 96 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/client_vm/dts/overlays/init.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | / { 8 | memory@40000000 { 9 | reg = <0x00 0x40000000 0x00 0x8000000>; 10 | device_type = "memory"; 11 | }; 12 | 13 | chosen { 14 | stdout-path = "/pl011@9000000"; 15 | bootargs = "console=hvc0 earlycon=hvc0 earlyprintk=serial debug loglevel=8"; 16 | linux,stdout-path = "/pl011@9000000"; 17 | // linux,initrd-start = <0x45000000>; 18 | linux,initrd-start = <0x46000000>; 19 | linux,initrd-end = <0x47000000>; 20 | }; 21 | }; -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/client_vm/dts/overlays/io.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | virtio-console@0130000 { 8 | compatible = "virtio,mmio"; 9 | reg = <0x00 0x130000 0x00 0x200>; 10 | interrupts = <0x00 42 0x04>; 11 | }; 12 | 13 | virtio-snd@0170000 { 14 | compatible = "virtio,mmio"; 15 | reg = <0x00 0x170000 0x00 0x200>; 16 | interrupts = <0x00 44 0x04>; 17 | }; 18 | }; -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/snd_driver_vm/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # QEMU virt AArch64 images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v5.18 (commit hash: `4b0986a3613c92f4ec1bdc7f60ec66fea135991f`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | ``` 23 | git clone --depth 1 --branch v5.18 https://github.com/torvalds/linux.git 24 | cp linux_config linux/.config 25 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 26 | ``` 27 | 28 | The path to the image is: `linux/arch/arm64/boot/Image`. 29 | 30 | ## Buildroot RootFS image 31 | 32 | ### Details 33 | * Image name: `rootfs.cpio.gz` 34 | * Config name: `buildroot_config` 35 | * Version: 2022.08-rc2 36 | 37 | ### Instructions for reproducing 38 | 39 | ``` 40 | wget https://buildroot.org/downloads/buildroot-2022.08-rc2.tar.xz 41 | tar xvf buildroot-2022.08-rc2.tar.xz 42 | cp buildroot_config buildroot-2022.08-rc2/.config 43 | make -C buildroot-2022.08-rc2 44 | ``` 45 | 46 | The root filesystem will be located at: `buildroot-2022.08-rc2/output/images/rootfs.cpio.gz` along 47 | with the other buildroot artefacts. 48 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/snd_driver_vm/dts/linux.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | interrupt-parent = <0x8002>; 5 | model = "linux,dummy-virt"; 6 | #size-cells = <0x02>; 7 | #address-cells = <0x02>; 8 | compatible = "linux,dummy-virt"; 9 | 10 | aliases { 11 | serial0 = "/pl011@9000000"; 12 | }; 13 | 14 | psci { 15 | migrate = <0xc4000005>; 16 | cpu_on = <0xc4000003>; 17 | cpu_off = <0x84000002>; 18 | cpu_suspend = <0xc4000001>; 19 | method = "smc"; 20 | compatible = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; 21 | }; 22 | 23 | platform-bus@c000000 { 24 | interrupt-parent = <0x8002>; 25 | ranges = <0x00 0x00 0xc000000 0x2000000>; 26 | #address-cells = <0x01>; 27 | #size-cells = <0x01>; 28 | compatible = "qemu,platform\0simple-bus"; 29 | }; 30 | 31 | pl011@9000000 { 32 | clock-names = "uartclk\0apb_pclk"; 33 | clocks = <0x8000 0x8000>; 34 | interrupts = <0x00 0x01 0x04>; 35 | reg = <0x00 0x9000000 0x00 0x1000>; 36 | compatible = "arm,pl011\0arm,primecell"; 37 | status = "disabled"; 38 | }; 39 | 40 | pmu { 41 | interrupts = <0x01 0x07 0x104>; 42 | compatible = "arm,armv8-pmuv3"; 43 | }; 44 | 45 | intc@8000000 { 46 | phandle = <0x8002>; 47 | reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>; 48 | compatible = "arm,cortex-a15-gic"; 49 | ranges; 50 | #size-cells = <0x02>; 51 | #address-cells = <0x02>; 52 | interrupt-controller; 53 | #interrupt-cells = <0x03>; 54 | }; 55 | 56 | flash@0 { 57 | bank-width = <0x04>; 58 | reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; 59 | compatible = "cfi-flash"; 60 | status = "disabled"; 61 | }; 62 | 63 | cpus { 64 | #size-cells = <0x00>; 65 | #address-cells = <0x01>; 66 | 67 | cpu@0 { 68 | phandle = <0x8001>; 69 | reg = <0x00>; 70 | compatible = "arm,cortex-a53"; 71 | device_type = "cpu"; 72 | }; 73 | }; 74 | 75 | timer { 76 | interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>; 77 | always-on; 78 | compatible = "arm,armv8-timer\0arm,armv7-timer"; 79 | }; 80 | 81 | apb-pclk { 82 | phandle = <0x8000>; 83 | clock-output-names = "clk24mhz"; 84 | clock-frequency = <0x16e3600>; 85 | #clock-cells = <0x00>; 86 | compatible = "fixed-clock"; 87 | }; 88 | 89 | pcie@10000000 { 90 | interrupt-map-mask = <0x1800 0x00 0x00 0x07>; 91 | interrupt-map = <0x00 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x05 0x04>; 92 | #interrupt-cells = <0x01>; 93 | // BUS_ADDRESS CPU BUS ADDRESS Size of segment 94 | ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 95 | 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 96 | 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; 97 | reg = <0x40 0x10000000 0x00 0x10000000>; 98 | msi-map = <0x00 0x8003 0x00 0x10000>; 99 | dma-coherent; 100 | bus-range = <0x00 0xff>; 101 | linux,pci-domain = <0x00>; 102 | #size-cells = <0x02>; 103 | #address-cells = <0x03>; 104 | device_type = "pci"; 105 | compatible = "pci-host-ecam-generic"; 106 | }; 107 | }; 108 | -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/snd_driver_vm/dts/overlays/init.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | / { 8 | memory@40000000 { 9 | reg = <0x00 0x40000000 0x00 0x8000000>; 10 | device_type = "memory"; 11 | }; 12 | 13 | chosen { 14 | stdout-path = "/pl011@9000000"; 15 | bootargs = "console=hvc0 earlycon=hvc0 pci=nomsi earlyprintk=serial debug loglevel=8 uio_pdrv_genirq.of_id=generic-uio"; 16 | linux,stdout-path = "/pl011@9000000"; 17 | linux,initrd-start = <0x46000000>; 18 | linux,initrd-end = <0x47000000>; 19 | }; 20 | }; -------------------------------------------------------------------------------- /examples/virtio-snd/board/qemu_virt_aarch64/snd_driver_vm/dts/overlays/io.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | / { 7 | virtio-console@0130000 { 8 | compatible = "virtio,mmio"; 9 | reg = <0x00 0x130000 0x00 0x200>; 10 | interrupts = <0x00 42 0x04>; 11 | }; 12 | 13 | uio { 14 | compatible = "generic-uio\0uio"; 15 | reg = < 16 | 0x00 0x6f00000 0x00 0x1000 17 | 0x00 0x7000000 0x00 0xC00000 18 | 0x00 0x9400000 0x00 0x200000 19 | >; 20 | interrupts = <0x00 0x12 0x04>; 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /examples/virtio-snd/shell.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | let 6 | pkgs = import {}; 7 | in 8 | pkgs.mkShell { 9 | nativeBuildInputs = 10 | let 11 | crossInputs = with pkgs.pkgsCross.aarch64-multiplatform; [ 12 | alsa-lib 13 | ]; 14 | nativeInputs = with pkgs; [ 15 | llvm 16 | dtc 17 | qemu 18 | patchelf 19 | clang 20 | lld 21 | gzip 22 | fakeroot 23 | cpio 24 | zig 25 | ]; 26 | in crossInputs ++ nativeInputs; 27 | hardeningDisable = [ "all" ]; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/virtio-snd/userlevel/control.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int main(void) 12 | { 13 | int idx, dev, err; 14 | snd_ctl_t *handle; 15 | snd_ctl_card_info_t *info; 16 | snd_pcm_info_t *pcminfo; 17 | snd_rawmidi_info_t *rawmidiinfo; 18 | char str[128]; 19 | 20 | snd_ctl_card_info_alloca(&info); 21 | snd_pcm_info_alloca(&pcminfo); 22 | snd_rawmidi_info_alloca(&rawmidiinfo); 23 | 24 | idx = -1; 25 | while (1) { 26 | if ((err = snd_card_next(&idx)) < 0) { 27 | printf("Card next error: %s\n", snd_strerror(err)); 28 | break; 29 | } 30 | if (idx < 0) 31 | break; 32 | sprintf(str, "hw:CARD=%i", idx); 33 | if ((err = snd_ctl_open(&handle, str, 0)) < 0) { 34 | printf("Open error: %s\n", snd_strerror(err)); 35 | continue; 36 | } 37 | if ((err = snd_ctl_card_info(handle, info)) < 0) { 38 | printf("HW info error: %s\n", snd_strerror(err)); 39 | continue; 40 | } 41 | printf("Soundcard #%i:\n", idx + 1); 42 | printf(" card - %i\n", snd_ctl_card_info_get_card(info)); 43 | printf(" id - '%s'\n", snd_ctl_card_info_get_id(info)); 44 | printf(" driver - '%s'\n", snd_ctl_card_info_get_driver(info)); 45 | printf(" name - '%s'\n", snd_ctl_card_info_get_name(info)); 46 | printf(" longname - '%s'\n", snd_ctl_card_info_get_longname(info)); 47 | printf(" mixername - '%s'\n", snd_ctl_card_info_get_mixername(info)); 48 | printf(" components - '%s'\n", snd_ctl_card_info_get_components(info)); 49 | dev = -1; 50 | while (1) { 51 | snd_pcm_sync_id_t sync; 52 | if ((err = snd_ctl_pcm_next_device(handle, &dev)) < 0) { 53 | printf(" PCM next device error: %s\n", snd_strerror(err)); 54 | break; 55 | } 56 | if (dev < 0) 57 | break; 58 | snd_pcm_info_set_device(pcminfo, dev); 59 | snd_pcm_info_set_subdevice(pcminfo, 0); 60 | snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK); 61 | if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { 62 | printf(" PCM info error: %s\n", snd_strerror(err)); 63 | continue; 64 | } 65 | printf("PCM info, device #%i:\n", dev); 66 | printf(" device - %i\n", snd_pcm_info_get_device(pcminfo)); 67 | printf(" subdevice - %i\n", snd_pcm_info_get_subdevice(pcminfo)); 68 | printf(" stream - %i\n", snd_pcm_info_get_stream(pcminfo)); 69 | printf(" card - %i\n", snd_pcm_info_get_card(pcminfo)); 70 | printf(" id - '%s'\n", snd_pcm_info_get_id(pcminfo)); 71 | printf(" name - '%s'\n", snd_pcm_info_get_name(pcminfo)); 72 | printf(" subdevice name - '%s'\n", snd_pcm_info_get_subdevice_name(pcminfo)); 73 | printf(" class - 0x%x\n", snd_pcm_info_get_class(pcminfo)); 74 | printf(" subclass - 0x%x\n", snd_pcm_info_get_subclass(pcminfo)); 75 | printf(" subdevices count - %i\n", snd_pcm_info_get_subdevices_count(pcminfo)); 76 | printf(" subdevices avail - %i\n", snd_pcm_info_get_subdevices_avail(pcminfo)); 77 | sync = snd_pcm_info_get_sync(pcminfo); 78 | printf(" sync - 0x%x,0x%x,0x%x,0x%x\n", sync.id32[0], sync.id32[1], sync.id32[2], sync.id32[3]); 79 | } 80 | dev = -1; 81 | while (1) { 82 | if ((err = snd_ctl_rawmidi_next_device(handle, &dev)) < 0) { 83 | printf(" RAWMIDI next device error: %s\n", snd_strerror(err)); 84 | break; 85 | } 86 | if (dev < 0) 87 | break; 88 | snd_rawmidi_info_set_device(rawmidiinfo, dev); 89 | snd_rawmidi_info_set_subdevice(rawmidiinfo, 0); 90 | snd_rawmidi_info_set_stream(rawmidiinfo, SND_RAWMIDI_STREAM_OUTPUT); 91 | if ((err = snd_ctl_rawmidi_info(handle, rawmidiinfo)) < 0) { 92 | printf(" RAWMIDI info error: %s\n", snd_strerror(err)); 93 | continue; 94 | } 95 | printf("RAWMIDI info, device #%i:\n", dev); 96 | printf(" device - %i\n", snd_rawmidi_info_get_device(rawmidiinfo)); 97 | printf(" subdevice - %i\n", snd_rawmidi_info_get_subdevice(rawmidiinfo)); 98 | printf(" stream - %i\n", snd_rawmidi_info_get_stream(rawmidiinfo)); 99 | printf(" card - %i\n", snd_rawmidi_info_get_card(rawmidiinfo)); 100 | printf(" flags - 0x%x\n", snd_rawmidi_info_get_flags(rawmidiinfo)); 101 | printf(" id - '%s'\n", snd_rawmidi_info_get_id(rawmidiinfo)); 102 | printf(" name - '%s'\n", snd_rawmidi_info_get_name(rawmidiinfo)); 103 | printf(" subname - '%s'\n", snd_rawmidi_info_get_subdevice_name(rawmidiinfo)); 104 | printf(" subdevices count - %i\n", snd_rawmidi_info_get_subdevices_count(rawmidiinfo)); 105 | printf(" subdevices avail - %i\n", snd_rawmidi_info_get_subdevices_avail(rawmidiinfo)); 106 | } 107 | snd_ctl_close(handle); 108 | } 109 | 110 | snd_config_update_free_global(); 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /examples/virtio-snd/userlevel/feedback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | unsigned char buffer[1024]; 14 | 15 | #define NUM_CHANNELS 1 16 | #define SAMPLE_RATE 48000 17 | #define FORMAT SND_PCM_FORMAT_S16 18 | #define BITS_PER_BYTE 8 19 | 20 | static bool running = true; 21 | 22 | static snd_pcm_t *open_stream(snd_pcm_stream_t direction, const char *name, const char *device) 23 | { 24 | int err; 25 | snd_pcm_t *handle; 26 | if ((err = snd_pcm_open(&handle, device, direction, 0)) < 0) { 27 | printf("%s open error: %s\n", name, snd_strerror(err)); 28 | exit(EXIT_FAILURE); 29 | } 30 | if ((err = snd_pcm_set_params(handle, 31 | FORMAT, 32 | SND_PCM_ACCESS_RW_INTERLEAVED, 33 | 1, 34 | SAMPLE_RATE, 35 | 1, 36 | 500000)) < 0) { /* 0.5sec */ 37 | printf("Capture open error: %s\n", snd_strerror(err)); 38 | exit(EXIT_FAILURE); 39 | } 40 | printf("Opened %s\n", name); 41 | 42 | return handle; 43 | } 44 | 45 | void signal_handler(int signum) 46 | { 47 | printf("\nStopping\n"); 48 | running = false; 49 | } 50 | 51 | int main(int argc, char **argv) 52 | { 53 | signal(SIGINT, signal_handler); 54 | 55 | snd_pcm_sframes_t frame_size = (snd_pcm_format_physical_width(FORMAT) / BITS_PER_BYTE) * NUM_CHANNELS; 56 | 57 | char *capture_device = argc > 1 ? argv[1] : "hw:0,1"; 58 | 59 | snd_pcm_t *playback = open_stream(SND_PCM_STREAM_PLAYBACK, "Playback", "default"); 60 | snd_pcm_t *capture = open_stream(SND_PCM_STREAM_CAPTURE, "Capture", capture_device); 61 | 62 | int err = snd_pcm_start(capture); 63 | if (err) { 64 | printf("Failed to start capture: %s\n", snd_strerror(err)); 65 | return 1; 66 | } 67 | 68 | while (running) { 69 | snd_pcm_sframes_t read = snd_pcm_readi(capture, buffer, sizeof(buffer) / frame_size); 70 | if (read < 0) { 71 | printf("Recovering capture\n"); 72 | read = snd_pcm_recover(capture, read, 0); 73 | } 74 | if (read < 0) { 75 | printf("snd_pcm_readi failed: %s\n", snd_strerror(read)); 76 | break; 77 | } 78 | if (read > 0 && read < (long)sizeof(buffer) / frame_size) 79 | printf("Short read (expected %li, wrote %li)\n", (long)sizeof(buffer), read); 80 | 81 | snd_pcm_sframes_t written = snd_pcm_writei(playback, buffer, read); 82 | if (written < 0){ 83 | printf("Recovering playback\n"); 84 | written = snd_pcm_recover(playback, written, 0); 85 | } 86 | if (written < 0) { 87 | printf("snd_pcm_writteni failed: %s\n", snd_strerror(written)); 88 | break; 89 | } 90 | if (written > 0 && written < (long)sizeof(buffer) / frame_size) 91 | printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), written); 92 | } 93 | 94 | /* pass the remaining samples, otherwise they're dropped in close */ 95 | err = snd_pcm_drain(capture); 96 | if (err < 0) 97 | printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); 98 | snd_pcm_close(capture); 99 | 100 | err = snd_pcm_drain(playback); 101 | if (err < 0) 102 | printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); 103 | snd_pcm_close(playback); 104 | 105 | return 0; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /examples/virtio-snd/userlevel/pcm_min.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | static char *device = "default"; /* playback device */ 11 | unsigned char buffer[16*1024]; /* some random data */ 12 | 13 | int main(void) 14 | { 15 | int err; 16 | unsigned int i; 17 | snd_pcm_t *handle; 18 | snd_pcm_sframes_t frames; 19 | 20 | for (i = 0; i < sizeof(buffer); i++) 21 | buffer[i] = random() & 0xff; 22 | 23 | if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 24 | printf("Playback open error: %s\n", snd_strerror(err)); 25 | exit(EXIT_FAILURE); 26 | } 27 | if ((err = snd_pcm_set_params(handle, 28 | SND_PCM_FORMAT_U8, 29 | SND_PCM_ACCESS_RW_INTERLEAVED, 30 | 1, 31 | 48000, 32 | 1, 33 | 500000)) < 0) { /* 0.5sec */ 34 | printf("Playback open error: %s\n", snd_strerror(err)); 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | for (i = 0; i < 16; i++) { 39 | frames = snd_pcm_writei(handle, buffer, sizeof(buffer)); 40 | if (frames < 0) 41 | frames = snd_pcm_recover(handle, frames, 0); 42 | if (frames < 0) { 43 | printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); 44 | break; 45 | } 46 | if (frames > 0 && frames < (long)sizeof(buffer)) 47 | printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames); 48 | } 49 | 50 | /* pass the remaining samples, otherwise they're dropped in close */ 51 | err = snd_pcm_drain(handle); 52 | if (err < 0) 53 | printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); 54 | snd_pcm_close(handle); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /examples/virtio-snd/userlevel/record.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | unsigned char buffer[16*1024]; 11 | 12 | typedef struct wavfile_header_s 13 | { 14 | char ChunkID[4]; 15 | int32_t ChunkSize; 16 | char Format[4]; 17 | 18 | char Subchunk1ID[4]; 19 | int32_t Subchunk1Size; 20 | int16_t AudioFormat; 21 | int16_t NumChannels; 22 | int32_t SampleRate; 23 | int32_t ByteRate; 24 | int16_t BlockAlign; 25 | int16_t BitsPerSample; 26 | 27 | char Subchunk2ID[4]; 28 | int32_t Subchunk2Size; 29 | } wavfile_header_t; 30 | 31 | #define NUM_CHANNELS 1 32 | #define BITS_PER_SAMPLE 16 33 | #define SUBCHUNK1SIZE 16 34 | 35 | #define SAMPLE_RATE 48000 36 | 37 | int write_header(FILE *file_p, 38 | int32_t SampleRate, 39 | int32_t FrameCount) 40 | { 41 | int ret; 42 | 43 | wavfile_header_t wav_header; 44 | int32_t subchunk2_size; 45 | int32_t chunk_size; 46 | 47 | size_t write_count; 48 | 49 | subchunk2_size = FrameCount * NUM_CHANNELS * BITS_PER_SAMPLE / 8; 50 | chunk_size = 4 + (8 + SUBCHUNK1SIZE) + (8 + subchunk2_size); 51 | 52 | wav_header.ChunkID[0] = 'R'; 53 | wav_header.ChunkID[1] = 'I'; 54 | wav_header.ChunkID[2] = 'F'; 55 | wav_header.ChunkID[3] = 'F'; 56 | 57 | wav_header.ChunkSize = chunk_size; 58 | 59 | wav_header.Format[0] = 'W'; 60 | wav_header.Format[1] = 'A'; 61 | wav_header.Format[2] = 'V'; 62 | wav_header.Format[3] = 'E'; 63 | 64 | wav_header.Subchunk1ID[0] = 'f'; 65 | wav_header.Subchunk1ID[1] = 'm'; 66 | wav_header.Subchunk1ID[2] = 't'; 67 | wav_header.Subchunk1ID[3] = ' '; 68 | 69 | wav_header.Subchunk1Size = SUBCHUNK1SIZE; 70 | wav_header.AudioFormat = 1; 71 | wav_header.NumChannels = NUM_CHANNELS; 72 | wav_header.SampleRate = SampleRate; 73 | wav_header.ByteRate = SampleRate * NUM_CHANNELS * BITS_PER_SAMPLE / 8; 74 | wav_header.BlockAlign = NUM_CHANNELS * BITS_PER_SAMPLE / 8; 75 | wav_header.BitsPerSample = BITS_PER_SAMPLE; 76 | 77 | wav_header.Subchunk2ID[0] = 'd'; 78 | wav_header.Subchunk2ID[1] = 'a'; 79 | wav_header.Subchunk2ID[2] = 't'; 80 | wav_header.Subchunk2ID[3] = 'a'; 81 | wav_header.Subchunk2Size = subchunk2_size; 82 | 83 | write_count = fwrite(&wav_header, 84 | sizeof(wavfile_header_t), 1, 85 | file_p); 86 | 87 | ret = (1 != write_count)? -1 : 0; 88 | 89 | return ret; 90 | } 91 | 92 | 93 | int main(int argc, char **argv) 94 | { 95 | int err; 96 | unsigned int i; 97 | snd_pcm_t *handle; 98 | snd_pcm_sframes_t frames; 99 | 100 | char *device; 101 | if (argc > 1) { 102 | device = argv[1]; 103 | } else { 104 | device = "default"; 105 | } 106 | 107 | if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { 108 | printf("Capture open error: %s\n", snd_strerror(err)); 109 | exit(EXIT_FAILURE); 110 | } 111 | if ((err = snd_pcm_set_params(handle, 112 | SND_PCM_FORMAT_S16, 113 | SND_PCM_ACCESS_RW_INTERLEAVED, 114 | 1, 115 | SAMPLE_RATE, 116 | 1, 117 | 500000)) < 0) { /* 0.5sec */ 118 | printf("Capture open error: %s\n", snd_strerror(err)); 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | FILE *file = fopen("rec.wav", "wb"); 123 | if (!file) { 124 | printf("Failed to open rec.wav\n"); 125 | exit(EXIT_FAILURE); 126 | } 127 | 128 | const int buffer_count = 16; 129 | write_header(file, SAMPLE_RATE, sizeof(buffer) * buffer_count); 130 | 131 | const int frame_size = 2; 132 | 133 | for (i = 0; i < buffer_count; i++) { 134 | frames = snd_pcm_readi(handle, buffer, sizeof(buffer) / frame_size); 135 | if (frames < 0) 136 | frames = snd_pcm_recover(handle, frames, 0); 137 | if (frames < 0) { 138 | printf("snd_pcm_readi failed: %s\n", snd_strerror(frames)); 139 | break; 140 | } 141 | if (frames > 0 && frames < (long)sizeof(buffer) / frame_size) 142 | printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer) / frame_size, frames); 143 | 144 | fwrite(buffer, 1, sizeof(buffer), file); 145 | } 146 | 147 | /* pass the remaining samples, otherwise they're dropped in close */ 148 | err = snd_pcm_drain(handle); 149 | if (err < 0) 150 | printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); 151 | snd_pcm_close(handle); 152 | 153 | fclose(file); 154 | return 0; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /examples/virtio/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | 7 | BUILD_DIR ?= build 8 | export MICROKIT_CONFIG ?= debug 9 | 10 | ifeq ($(strip $(MICROKIT_SDK)),) 11 | $(error MICROKIT_SDK must be specified) 12 | endif 13 | export override MICROKIT_SDK:=$(abspath $(MICROKIT_SDK)) 14 | 15 | # Both QEMU and Maaxboard uses the same guest device tree as the guest does not 16 | # need to access any physical device. The only difference is that QEMU is GICv2 while 17 | # the Maaxboard is GICv3. We apply a GIC overlay for the correct version on the board 18 | # at build time. 19 | # TODO: we should probably determine this better if we have lots of supported platforms. 20 | ifeq ($(strip $(MICROKIT_BOARD)), maaxboard) 21 | export UART_DRIVER := imx 22 | export TIMER_DRIVER := imx 23 | export BLK_DRIVER := mmc/imx 24 | export ETH_DRIVER := imx 25 | export CPU := cortex-a55 26 | export GIC_DT_OVERLAY := gic_v3_overlay.dts 27 | else ifeq ($(strip $(MICROKIT_BOARD)), qemu_virt_aarch64) 28 | export UART_DRIVER := arm 29 | export TIMER_DRIVER := arm 30 | export BLK_DRIVER := virtio 31 | export ETH_DRIVER := virtio 32 | export CPU := cortex-a53 33 | export GIC_DT_OVERLAY := gic_v2_overlay.dts 34 | QEMU := qemu-system-aarch64 35 | 36 | export BLK_NUM_PART = 1 37 | export BLK_SIZE = 512 38 | # 16MiB of disk space 39 | export BLK_MEM ?= 16777216 40 | else 41 | $(error Unsupported MICROKIT_BOARD given) 42 | endif 43 | 44 | # Allow to user to specify a custom partition 45 | PARTITION := 46 | ifdef PARTITION 47 | export PARTITION_ARG := --partition $(PARTITION) 48 | endif 49 | 50 | # All platforms use the same Linux and initrd images. 51 | export LINUX := a3f4bf9e2eb24fa8fc0d3d8cd02e4d8097062e8b-linux 52 | export INITRD := b6a276df6a0e39f76bc8950e975daa2888ad83df-rootfs.cpio.gz 53 | 54 | export BUILD_DIR:=$(abspath $(BUILD_DIR)) 55 | export MICROKIT_SDK:=$(abspath $(MICROKIT_SDK)) 56 | export VIRTIO_EXAMPLE:=$(abspath .) 57 | 58 | export OBJCOPY := llvm-objcopy 59 | export TARGET := aarch64-none-elf 60 | export CC := clang 61 | export CC_USERLEVEL := zig cc 62 | export LD := ld.lld 63 | export AS := llvm-as 64 | export AR := llvm-ar 65 | export DTC := dtc 66 | export RANLIB := llvm-ranlib 67 | export MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit 68 | export SDDF=$(abspath ../../dep/sddf) 69 | export LIBVMM=$(abspath ../../) 70 | 71 | IMAGE_FILE := $(BUILD_DIR)/loader.img 72 | REPORT_FILE := $(BUILD_DIR)/report.txt 73 | 74 | all: $(IMAGE_FILE) 75 | 76 | qemu $(IMAGE_FILE) $(REPORT_FILE) clean clobber: $(BUILD_DIR)/Makefile FORCE 77 | $(MAKE) -C $(BUILD_DIR) MICROKIT_SDK=$(MICROKIT_SDK) $(notdir $@) 78 | 79 | $(BUILD_DIR)/Makefile: virtio.mk 80 | mkdir -p $(BUILD_DIR) 81 | cp virtio.mk $@ 82 | 83 | FORCE: 84 | -------------------------------------------------------------------------------- /examples/virtio/client_vm/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Linux kernel image 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v6.13 (commit hash: `ffd294d346d185b70e28b1a28abe367bbfe53c04`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. This is a kernel minimally configured with a 20 | IPv4 network stack + DNS + DHCP and virtIO device drivers. 21 | 22 | ### Instructions for reproducing 23 | ``` 24 | git clone --depth 1 --branch v6.13 https://github.com/torvalds/linux.git 25 | cp config linux/.config 26 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 27 | ``` 28 | 29 | The path to the image is: `linux/arch/arm64/boot/Image`. 30 | 31 | ## Buildroot RootFS image 32 | 33 | ### Details 34 | * Image name: `rootfs.cpio.gz` 35 | * Config name: `buildroot_config` 36 | * Version: 2022.08-rc2 37 | 38 | ### Instructions for reproducing 39 | 40 | ``` 41 | wget https://buildroot.org/downloads/buildroot-2022.08-rc2.tar.xz 42 | tar xvf buildroot-2022.08-rc2.tar.xz 43 | cp buildroot_config buildroot-2022.08-rc2/.config 44 | make -C buildroot-2022.08-rc2 45 | ``` 46 | 47 | The root filesystem will be located at: `buildroot-2022.08-rc2/output/images/rootfs.cpio.gz` along 48 | with the other buildroot artefacts. -------------------------------------------------------------------------------- /examples/virtio/client_vm/gic_v2_overlay.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | / { 8 | intc@8000000 { 9 | phandle = <0x8001>; 10 | interrupts = <0x01 0x09 0x04>; 11 | reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>; 12 | compatible = "arm,cortex-a15-gic"; 13 | ranges; 14 | #size-cells = <0x02>; 15 | #address-cells = <0x02>; 16 | interrupt-controller; 17 | #interrupt-cells = <0x03>; 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /examples/virtio/client_vm/gic_v3_overlay.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | / { 8 | interrupt-controller@38800000 { 9 | compatible = "arm,gic-v3"; 10 | reg = <0x0 0x38800000 0x0 0x10000 0x0 0x38880000 0x0 0xc0000 0x0 0x31000000 0x0 0x2000 0x0 0x31010000 0x0 0x2000 0x0 0x31020000 0x0 0x2000>; 11 | #interrupt-cells = <0x03>; 12 | interrupt-controller; 13 | interrupts = <0x01 0x09 0x04>; 14 | interrupt-parent = <0x07>; 15 | phandle = <0x8001>; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /examples/virtio/client_vm/linux.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | /dts-v1/; 8 | 9 | / { 10 | interrupt-parent = <0x8001>; 11 | #size-cells = <0x02>; 12 | #address-cells = <0x02>; 13 | compatible = "linux,dummy-virt"; 14 | 15 | psci { 16 | migrate = <0xc4000005>; 17 | cpu_on = <0xc4000003>; 18 | cpu_off = <0x84000002>; 19 | cpu_suspend = <0xc4000001>; 20 | method = "smc"; 21 | compatible = "arm,psci-0.2\0arm,psci"; 22 | }; 23 | 24 | cpus { 25 | #size-cells = <0x00>; 26 | #address-cells = <0x01>; 27 | 28 | cpu@0 { 29 | reg = <0x00>; 30 | compatible = "arm,cortex-a53"; 31 | device_type = "cpu"; 32 | }; 33 | }; 34 | 35 | timer { 36 | interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>; 37 | always-on; 38 | compatible = "arm,armv8-timer\0arm,armv7-timer"; 39 | }; 40 | 41 | memory@40000000 { 42 | reg = <0x00 0x40000000 0x00 0x8000000>; 43 | device_type = "memory"; 44 | }; 45 | 46 | chosen { 47 | bootargs = "console=hvc0 earlycon=hvc0 earlyprintk=serial debug loglevel=8"; 48 | linux,initrd-start = <0x47000000>; 49 | linux,initrd-end = <0x47f00000>; 50 | }; 51 | 52 | virtio-console@130000 { 53 | compatible = "virtio,mmio"; 54 | reg = <0x00 0x130000 0x00 0x200>; 55 | interrupts = <0x00 42 0x04>; 56 | }; 57 | 58 | virtio-blk@150000 { 59 | compatible = "virtio,mmio"; 60 | reg = <0x00 0x150000 0x00 0x200>; 61 | interrupts = <0x00 43 0x04>; 62 | }; 63 | 64 | virtio-net@160000 { 65 | compatible = "virtio,mmio"; 66 | reg = <0x00 0x160000 0x00 0x200>; 67 | interrupts = <0x00 44 0x04>; 68 | }; 69 | }; 70 | -------------------------------------------------------------------------------- /examples/zig/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Zig VMM example 7 | 8 | This example aims to show using [Zig](https://ziglang.org/) to interact with 9 | libvmm. Although Zig is most-often thought of as a programming language, the 10 | project actually provides three things: 11 | 1. The Zig programming language 12 | 2. The Zig build system 13 | 3. A portable drop-in C/C++ cross-compiler 14 | 15 | This example makes use of all three. The VMM code itself is written in the 16 | Zig programming langauge, it calls into libvmm which has been compiled using 17 | the Zig C compiler. Building the example is done via the Zig build system. 18 | 19 | ## Building the example 20 | 21 | This example expects to be built with Zig 0.14.*. 22 | 23 | You can download Zig [here](https://ziglang.org/download/). 24 | 25 | ```sh 26 | zig build -Dsdk=/path/to/sdk 27 | ``` 28 | 29 | The build system fetches the Linux kernel and initrd images on-demand from 30 | Trustworthy Systems' website. To override this and use your own images, you can 31 | specify the paths with the `-Dlinux` and/or `-Dinitrd` options. For example: 32 | ```sh 33 | zig build -Dsdk=/path/to/sdk -Dlinux=/path/to/linux -Dinitrd=/path/to/initrd 34 | ``` 35 | 36 | To view other options available when building the example (such as optimisation 37 | level or the target Microkit configuration), run the following command: 38 | ```sh 39 | zig build -Dsdk=/path/to/sdk -h 40 | ``` 41 | 42 | ## Running the example 43 | 44 | To also run the example via QEMU, run the following command: 45 | ```sh 46 | zig build -Dsdk=/path/to/sdk qemu 47 | ``` 48 | 49 | You should see the following output after a couple of seconds: 50 | ``` 51 | Welcome to Buildroot 52 | buildroot login: 53 | ``` 54 | 55 | The login is `root`. There is no password necessary. After logging in, you will 56 | see the console prompt where you can input commands: 57 | ``` 58 | Welcome to Buildroot 59 | buildroot login: root 60 | # 61 | ``` 62 | -------------------------------------------------------------------------------- /examples/zig/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .zig, 3 | .version = "0.0.0", 4 | 5 | .dependencies = .{ 6 | .libvmm = .{ 7 | .path = "../../" 8 | }, 9 | .linux = .{ 10 | .url = "https://trustworthy.systems/Downloads/libvmm/images/85000f3f42a882e4476e57003d53f2bbec8262b0-linux.tar.gz", 11 | .hash = "linux-0.0.0-AAAAAACKHwKLhs9sy_2Ma-_f1lh-vvTVxNfPElIDod2h", 12 | .lazy = true, 13 | }, 14 | .initrd = .{ 15 | .url = "https://trustworthy.systems/Downloads/libvmm/images/6dcd1debf64e6d69b178cd0f46b8c4ae7cebe2a5-rootfs.cpio.gz.tar.gz", 16 | .hash = "rootfs_cpio_gz-0.0.0-AAAAAFuyCwDpUITQ72Vk5neE9sZHvBPQTXdJFAvR05wM", 17 | .lazy = true, 18 | }, 19 | }, 20 | .paths = .{ 21 | "build.zig", 22 | "build.zig.zon", 23 | }, 24 | .fingerprint = 0xc1ce1081cd2432c3, 25 | } 26 | -------------------------------------------------------------------------------- /examples/zig/images/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # QEMU virt AArch64 images 7 | 8 | ## Linux kernel 9 | 10 | ### Details 11 | * Image name: `linux` 12 | * Config name: `linux_config` 13 | * Git remote: https://github.com/torvalds/linux.git 14 | * Tag: v5.18 (commit hash: `4b0986a3613c92f4ec1bdc7f60ec66fea135991f`) 15 | * Toolchain: `aarch64-none-elf` 16 | * Version: GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16)) 10.2.1 20201103 17 | 18 | You can also get the Linux config used after booting by running the following 19 | command in userspace: `zcat /proc/config.gz`. 20 | 21 | ### Instructions for reproducing 22 | ``` 23 | git clone --depth 1 --branch v5.18 https://github.com/torvalds/linux.git 24 | cp linux_config linux/.config 25 | make -C linux ARCH=arm64 CROSS_COMPILE=aarch64-none-elf- all -j$(nproc) 26 | ``` 27 | 28 | The path to the image is: `linux/arch/arm64/boot/Image`. 29 | 30 | ## Buildroot RootFS image 31 | 32 | ### Details 33 | * Image name: `rootfs.cpio.gz` 34 | * Config name: `buildroot_config` 35 | * Version: 2022.08-rc2 36 | 37 | ### Instructions for reproducing 38 | 39 | ``` 40 | wget https://buildroot.org/downloads/buildroot-2022.08-rc2.tar.xz 41 | tar xvf buildroot-2022.08-rc2.tar.xz 42 | cp buildroot_config buildroot-2022.08-rc2/.config 43 | make -C buildroot-2022.08-rc2 44 | ``` 45 | 46 | The root filesystem will be located at: `buildroot-2022.08-rc2/output/images/rootfs.cpio.gz` along 47 | with the other buildroot artefacts. 48 | -------------------------------------------------------------------------------- /examples/zig/images/linux.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | interrupt-parent = <0x8001>; 5 | #size-cells = <0x02>; 6 | #address-cells = <0x02>; 7 | compatible = "linux,dummy-virt"; 8 | 9 | aliases { 10 | serial0 = "/pl011@9000000"; 11 | }; 12 | 13 | psci { 14 | migrate = <0xc4000005>; 15 | cpu_on = <0xc4000003>; 16 | cpu_off = <0x84000002>; 17 | cpu_suspend = <0xc4000001>; 18 | method = "smc"; 19 | compatible = "arm,psci-0.2\0arm,psci"; 20 | }; 21 | 22 | memory@40000000 { 23 | reg = <0x00 0x40000000 0x00 0x10000000>; 24 | device_type = "memory"; 25 | }; 26 | 27 | platform@c000000 { 28 | interrupt-parent = <0x8001>; 29 | ranges = <0x00 0x00 0xc000000 0x2000000>; 30 | #address-cells = <0x01>; 31 | #size-cells = <0x01>; 32 | compatible = "qemu,platform\0simple-bus"; 33 | }; 34 | 35 | pl011@9000000 { 36 | clock-names = "uartclk\0apb_pclk"; 37 | clocks = <0x8000 0x8000>; 38 | interrupts = <0x00 0x01 0x04>; 39 | reg = <0x00 0x9000000 0x00 0x1000>; 40 | compatible = "arm,pl011\0arm,primecell"; 41 | }; 42 | 43 | pmu { 44 | interrupts = <0x01 0x07 0x104>; 45 | compatible = "arm,armv8-pmuv3"; 46 | }; 47 | 48 | intc@8000000 { 49 | phandle = <0x8001>; 50 | interrupts = <0x01 0x09 0x04>; 51 | reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>; 52 | compatible = "arm,cortex-a15-gic"; 53 | ranges; 54 | #size-cells = <0x02>; 55 | #address-cells = <0x02>; 56 | interrupt-controller; 57 | #interrupt-cells = <0x03>; 58 | }; 59 | 60 | flash@0 { 61 | bank-width = <0x04>; 62 | reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; 63 | compatible = "cfi-flash"; 64 | status = "disabled"; 65 | }; 66 | 67 | cpus { 68 | #size-cells = <0x00>; 69 | #address-cells = <0x01>; 70 | 71 | cpu@0 { 72 | reg = <0x00>; 73 | compatible = "arm,cortex-a53"; 74 | device_type = "cpu"; 75 | }; 76 | }; 77 | 78 | timer { 79 | interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>; 80 | always-on; 81 | compatible = "arm,armv8-timer\0arm,armv7-timer"; 82 | }; 83 | 84 | apb-pclk { 85 | phandle = <0x8000>; 86 | clock-output-names = "clk24mhz"; 87 | clock-frequency = <0x16e3600>; 88 | #clock-cells = <0x00>; 89 | compatible = "fixed-clock"; 90 | }; 91 | 92 | chosen { 93 | stdout-path = "/pl011@9000000"; 94 | bootargs = "earlycon=pl011,0x9000000 earlyprintk=serial debug loglevel=8"; 95 | linux,stdout-path = "/pl011@9000000"; 96 | linux,initrd-start = <0x4d700000>; 97 | linux,initrd-end = <0x4d800000>; 98 | }; 99 | }; 100 | -------------------------------------------------------------------------------- /examples/zig/src/extern.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | /* Zig will automatically translate microkit.h when it is imported into vmm.zig. 8 | * This is great, but the C translation functionality has one limitation that involves 9 | * us doing some extra work. Zig does not auto-translate GCC inline assembly which is 10 | * used by libsel4. This means that we need to provide certain functions where the implementation 11 | * is not in the header. Our implementations are just copies of the AArch64 libsel4 ones. */ 12 | 13 | #define arm_sys_send arm_sys_send_header 14 | #define arm_sys_send_recv arm_sys_send_recv_header 15 | 16 | #include 17 | 18 | /* Ignore the implementations of the header so we do not get duplicate function errors. */ 19 | #undef arm_sys_send 20 | #undef arm_sys_send_recv 21 | 22 | void arm_sys_send(seL4_Word sys, seL4_Word dest, seL4_Word info_arg, seL4_Word mr0, seL4_Word mr1, 23 | seL4_Word mr2, seL4_Word mr3) 24 | { 25 | register seL4_Word destptr asm("x0") = dest; 26 | register seL4_Word info asm("x1") = info_arg; 27 | 28 | /* Load beginning of the message into registers. */ 29 | register seL4_Word msg0 asm("x2") = mr0; 30 | register seL4_Word msg1 asm("x3") = mr1; 31 | register seL4_Word msg2 asm("x4") = mr2; 32 | register seL4_Word msg3 asm("x5") = mr3; 33 | 34 | /* Perform the system call. */ 35 | register seL4_Word scno asm("x7") = sys; 36 | asm volatile( 37 | "svc #0" 38 | : "+r"(destptr), "+r"(msg0), "+r"(msg1), "+r"(msg2), 39 | "+r"(msg3), "+r"(info) 40 | : "r"(scno) 41 | ); 42 | } 43 | 44 | void arm_sys_send_recv(seL4_Word sys, seL4_Word dest, seL4_Word *out_badge, seL4_Word info_arg, 45 | seL4_Word *out_info, seL4_Word *in_out_mr0, seL4_Word *in_out_mr1, seL4_Word *in_out_mr2, seL4_Word *in_out_mr3, 46 | LIBSEL4_UNUSED seL4_Word reply) 47 | { 48 | register seL4_Word destptr asm("x0") = dest; 49 | register seL4_Word info asm("x1") = info_arg; 50 | 51 | /* Load beginning of the message into registers. */ 52 | register seL4_Word msg0 asm("x2") = *in_out_mr0; 53 | register seL4_Word msg1 asm("x3") = *in_out_mr1; 54 | register seL4_Word msg2 asm("x4") = *in_out_mr2; 55 | register seL4_Word msg3 asm("x5") = *in_out_mr3; 56 | MCS_PARAM_DECL("x6"); 57 | 58 | /* Perform the system call. */ 59 | register seL4_Word scno asm("x7") = sys; 60 | asm volatile( 61 | "svc #0" 62 | : "+r"(msg0), "+r"(msg1), "+r"(msg2), "+r"(msg3), 63 | "+r"(info), "+r"(destptr) 64 | : "r"(scno) MCS_PARAM 65 | : "memory" 66 | ); 67 | *out_info = info; 68 | *out_badge = destptr; 69 | *in_out_mr0 = msg0; 70 | *in_out_mr1 = msg1; 71 | *in_out_mr2 = msg2; 72 | *in_out_mr3 = msg3; 73 | } 74 | -------------------------------------------------------------------------------- /examples/zig/zig_vmm.system: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | 40 | 41 | 45 | 46 | 52 | 53 | 59 | 60 | 61 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | # 5 | { 6 | description = "A flake for building libvmm and its examples"; 7 | 8 | inputs = { 9 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 10 | zig-overlay.url = "github:mitchellh/zig-overlay"; 11 | zig-overlay.inputs.nixpkgs.follows = "nixpkgs"; 12 | sdfgen.url = "github:au-ts/microkit_sdf_gen/0.23.1"; 13 | sdfgen.inputs.nixpkgs.follows = "nixpkgs"; 14 | rust-overlay = { 15 | url = "github:oxalica/rust-overlay"; 16 | inputs.nixpkgs.follows = "nixpkgs"; 17 | }; 18 | }; 19 | 20 | outputs = { nixpkgs, zig-overlay, rust-overlay, sdfgen, ... }: 21 | let 22 | microkit-version = "2.0.1"; 23 | microkit-platforms = { 24 | aarch64-darwin = "macos-aarch64"; 25 | x86_64-darwin = "macos-x86-64"; 26 | x86_64-linux = "linux-x86-64"; 27 | aarch64-linux = "linux-aarch64"; 28 | }; 29 | overlays = [ (import rust-overlay) ]; 30 | 31 | forAllSystems = with nixpkgs.lib; genAttrs (builtins.attrNames microkit-platforms); 32 | in 33 | { 34 | devShells = forAllSystems 35 | (system: { 36 | default = 37 | let 38 | pkgs = import nixpkgs { 39 | inherit system overlays; 40 | }; 41 | 42 | llvm = pkgs.llvmPackages_18; 43 | zig = zig-overlay.packages.${system}."0.14.0"; 44 | rust = pkgs.rust-bin.fromRustupToolchainFile ./examples/rust/rust-toolchain.toml; 45 | 46 | pysdfgen = sdfgen.packages.${system}.pysdfgen.override { zig = zig; pythonPackages = pkgs.python312Packages; }; 47 | 48 | python = pkgs.python312.withPackages (ps: [ 49 | pysdfgen 50 | ]); 51 | in 52 | # mkShellNoCC, because we do not want the cc from stdenv to leak into this shell 53 | pkgs.mkShellNoCC rec { 54 | name = "libvmm-dev"; 55 | 56 | microkit-platform = microkit-platforms.${system} or (throw "Unsupported system: ${system}"); 57 | 58 | env.MICROKIT_SDK = pkgs.fetchzip { 59 | url = "https://github.com/seL4/microkit/releases/download/${microkit-version}/microkit-sdk-${microkit-version}-${microkit-platform}.tar.gz"; 60 | hash = { 61 | aarch64-darwin = "sha256-bFFyVBF2E3YDJ6CYbfCOID7KGREQXkIFDpTD4MzxfCE="; 62 | x86_64-darwin = "sha256-tQWrI5LRp05tLy/HIxgN+0KFJrlmOQ+dpws4Fre+6E0="; 63 | x86_64-linux = "sha256-YpgIAXWB8v4Njm/5Oo0jZpRt/t+e+rVTwFTJ8zr2Hn4="; 64 | aarch64-linux = "sha256-GwWDRJalJOpAYCP/qggFOHDh2e2J1LspWUsyjopECYA="; 65 | }.${system} or (throw "Unsupported system: ${system}"); 66 | }; 67 | 68 | nativeBuildInputs = with pkgs; [ 69 | rust 70 | git 71 | zig 72 | qemu 73 | gnumake 74 | dosfstools 75 | # for git-clang-format. 76 | llvm.libclang.python 77 | llvm.lld 78 | llvm.libllvm 79 | llvm.clang 80 | llvm.libclang 81 | dtc 82 | python 83 | util-linux 84 | # Needed for CI testing 85 | expect 86 | # for shasum 87 | perl 88 | curl 89 | which 90 | cpio 91 | ]; 92 | 93 | # To avoid Nix adding compiler flags that are not available on a freestanding 94 | # environment. 95 | hardeningDisable = [ "all" ]; 96 | # Necessary for Rust bindgen 97 | LIBCLANG_PATH = "${llvm.libclang.lib}/lib"; 98 | }; 99 | }); 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /include/libvmm/arch/aarch64/fault.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022, UNSW (ABN 57 195 873 179) 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* Fault-handling functions */ 15 | bool fault_handle(size_t vcpu_id, microkit_msginfo msginfo); 16 | 17 | bool fault_handle_vcpu_exception(size_t vcpu_id); 18 | bool fault_handle_vppi_event(size_t vcpu_id); 19 | bool fault_handle_user_exception(size_t vcpu_id); 20 | bool fault_handle_unknown_syscall(size_t vcpu_id); 21 | bool fault_handle_vm_exception(size_t vcpu_id); 22 | 23 | typedef bool (*vm_exception_handler_t)(size_t vcpu_id, size_t offset, size_t fsr, seL4_UserContext *regs, void *data); 24 | bool fault_register_vm_exception_handler(uintptr_t base, size_t size, vm_exception_handler_t callback, void *data); 25 | 26 | /* Helpers for emulating the fault and getting fault details */ 27 | bool fault_advance_vcpu(size_t vcpu_id, seL4_UserContext *regs); 28 | bool fault_advance(size_t vcpu_id, seL4_UserContext *regs, uint64_t addr, uint64_t fsr, uint64_t reg_val); 29 | uint64_t fault_get_data_mask(uint64_t addr, uint64_t fsr); 30 | uint64_t fault_get_data(seL4_UserContext *regs, uint64_t fsr); 31 | uint64_t fault_emulate(seL4_UserContext *regs, uint64_t reg, uint64_t addr, uint64_t fsr, uint64_t reg_val); 32 | void fault_emulate_write(seL4_UserContext *regs, size_t addr, size_t fsr, size_t reg_val); 33 | 34 | /* Take the fault label given by the kernel and convert it to a string. */ 35 | char *fault_to_string(seL4_Word fault_label); 36 | 37 | bool fault_is_write(uint64_t fsr); 38 | bool fault_is_read(uint64_t fsr); 39 | -------------------------------------------------------------------------------- /include/libvmm/arch/aarch64/hsr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | // @ivanv, where do these come from? 10 | #define HSR_EXCEPTION_CLASS_SHIFT (26) 11 | #define HSR_EXCEPTION_CLASS_MASK (((uint32_t)HSR_MAX_EXCEPTION) << HSR_EXCEPTION_CLASS_SHIFT) 12 | #define HSR_EXCEPTION_CLASS(hsr) (((hsr) & HSR_EXCEPTION_CLASS_MASK) >> HSR_EXCEPTION_CLASS_SHIFT) 13 | 14 | #define HSR_SYNDROME_VALID (1 << 24) 15 | #define HSR_IS_SYNDROME_VALID(hsr) ((hsr) & HSR_SYNDROME_VALID) 16 | #define HSR_SYNDROME_WIDTH(x) (((x) >> 22) & 0x3) 17 | #define HSR_SYNDROME_RT(x) (((x) >> 16) & 0x1f) 18 | 19 | /* HSR Exception Value */ 20 | #define HSR_UNKNOWN_EXCEPTION (0x0) 21 | #define HSR_WFx_EXCEPTION (0x1) 22 | #define HSR_CP15_32_EXCEPTION (0x3) 23 | #define HSR_CP15_64_EXCEPTION (0x4) 24 | #define HSR_CP14_32_EXCEPTION (0x5) 25 | #define HSR_CP14_LC_32_EXCEPTION (0x6) 26 | #define HSR_SIMD_EXCEPTION (0x7) 27 | #define HSR_CP10_EXCEPTION (0x8) 28 | #define HSR_CP14_EXCEPTION (0xC) 29 | #define HSR_ILLEGAL_EXCEPTION (0xE) 30 | #define HSR_SVC_32_EXCEPTION (0x11) 31 | #define HSR_HVC_32_EXCEPTION (0x12) 32 | #define HSR_SMC_32_EXCEPTION (0x13) 33 | #define HSR_SVC_64_EXCEPTION (0x15) 34 | #define HSR_HVC_64_EXCEPTION (0x16) 35 | #define HSR_SMC_64_EXCEPTION (0x17) 36 | #define HSR_SYSREG_64_EXCEPTION (0x18) 37 | #define HSR_IMPL_DEF_EXCEPTION (0x1f) 38 | #define HSR_IABT_LOW_EXCEPTION (0x20) 39 | #define HSR_IABT_CURR_EXCEPTION (0x21) 40 | #define HSR_PC_ALIGN_EXCEPTION (0x22) 41 | #define HSR_DABT_LOW_EXCEPTION (0x24) 42 | #define HSR_DABT_CUR_EXCEPTION (0x25) 43 | #define HSR_SP_ALIGN_EXCEPTION (0x26) 44 | #define HSR_FP32_EXCEPTION (0x28) 45 | #define HSR_FP64_EXCEPTION (0x2C) 46 | #define HSR_SERROR_EXCEPTION (0x2F) 47 | #define HSR_BRK_LOW_EXCEPTION (0x30) 48 | #define HSR_BRK_CUR_EXCEPTION (0x31) 49 | #define HSR_SWSTEP_LOW_EXCEPTION (0x32) 50 | #define HSR_SWSTEP_CUR_EXCEPTION (0x33) 51 | #define HSR_WATCHPT_LOW_EXCEPTION (0x34) 52 | #define HSR_WATCHPT_CUR_EXCEPTION (0x35) 53 | #define HSR_SWBRK_32_EXCEPTION (0x38) 54 | #define HSW_VECTOR_32_EXCEPTION (0x3a) 55 | #define HSR_SWBRK_64_EXCEPTION (0x3c) 56 | /* Remaining values (0x3d - 0x3f) are reserved */ 57 | #define HSR_MAX_EXCEPTION (0x3f) 58 | -------------------------------------------------------------------------------- /include/libvmm/arch/aarch64/linux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | /* 13 | * The structure of the kernel image header and the magic value comes from the 14 | * Linux kernel documentation that can be found in the Linux source code. The 15 | * path is Documentation/arm64/booting.rst or Documentation/arch/arm64/booting.rst 16 | * depending on the version of the source code. 17 | */ 18 | 19 | #define LINUX_IMAGE_MAGIC 0x644d5241 20 | 21 | struct linux_image_header { 22 | uint32_t code0; // Executable code 23 | uint32_t code1; // Executable code 24 | uint64_t text_offset; // Image load offset, little endian 25 | uint64_t image_size; // Effective Image size, little endian 26 | uint64_t flags; // kernel flags, little endian 27 | uint64_t res2; // reserved 28 | uint64_t res3; // reserved 29 | uint64_t res4; // reserved 30 | uint32_t magic; // Magic number, little endian, "ARM\x64" 31 | uint32_t res5; // reserved (used for PE COFF offset) 32 | }; 33 | 34 | // Note that this function assumes that the `kernel` parameter is aligned 35 | // to at least the alignment of `struct linux_image_header`. The `dtb_src` 36 | // paramter must be aligned to at least the alignment of `struct dtb_header`. 37 | uintptr_t linux_setup_images(uintptr_t ram_start, 38 | uintptr_t kernel, 39 | size_t kernel_size, 40 | uintptr_t dtb_src, 41 | uintptr_t dtb_dest, 42 | size_t dtb_size, 43 | uintptr_t initrd_src, 44 | uintptr_t initrd_dest, 45 | size_t initrd_size); 46 | -------------------------------------------------------------------------------- /include/libvmm/arch/aarch64/psci.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3 | * Copyright 2022, UNSW (ABN 57 195 873 179) 4 | * 5 | * SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | /* PSCI function IDs */ 15 | typedef enum psci { 16 | PSCI_VERSION = 0x0, 17 | PSCI_CPU_SUSPEND = 0x1, 18 | PSCI_CPU_OFF = 0x2, 19 | PSCI_CPU_ON = 0x3, 20 | PSCI_AFFINTY_INFO = 0x4, 21 | PSCI_MIGRATE = 0x5, 22 | PSCI_MIGRATE_INFO_TYPE = 0x6, 23 | PSCI_MIGRATE_INFO_UP_CPU = 0x7, 24 | PSCI_SYSTEM_OFF = 0x8, 25 | PSCI_SYSTEM_RESET = 0x9, 26 | PSCI_FEATURES = 0xa, 27 | PSCI_CPU_FREEZE = 0xb, 28 | PSCI_CPU_DEFAULT_SUSPEND = 0xc, 29 | PSCI_NODE_HW_STATE = 0xd, 30 | PSCI_SYSTEM_SUSPEND = 0xe, 31 | PSCI_SET_SUSPEND_MODE = 0xf, 32 | PSCI_STAT_RESIDENCY = 0x10, 33 | PSCI_STAT_COUNT = 0x11, 34 | PSCI_SYSTEM_RESET2 = 0x12, 35 | PSCI_MEM_PROTECT = 0x13, 36 | PSCI_MEM_PROTECT_CHECK_RANGE = 0x14, 37 | PSCI_MAX = 0x1f 38 | } psci_id_t; 39 | 40 | /* 41 | * The emulation of PSCI is done according to this manual: 42 | * ARM Power State Coordination Interface 43 | * Platform Design Document 44 | * Issue E (PSCI version 1.2) 45 | */ 46 | 47 | bool handle_psci(size_t vcpu_id, seL4_UserContext *regs, uint64_t fn_number, uint32_t hsr); 48 | -------------------------------------------------------------------------------- /include/libvmm/arch/aarch64/smc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3 | * Copyright 2022, UNSW (ABN 57 195 873 179) 4 | * 5 | * SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* SMC vCPU fault handler */ 16 | bool smc_handle(size_t vcpu_id, uint32_t hsr); 17 | 18 | /* 19 | * A custom handler for SMC SiP calls can be registered. 20 | * By default SiP calls are not handled. 21 | * Note only one handler can be registered at a given time. 22 | * smc_register_sip_handler returns false if a handler already exists. 23 | */ 24 | typedef bool (*smc_sip_handler_t)(size_t vcpu_id, seL4_UserContext *regs, size_t fn_number); 25 | bool smc_register_sip_handler(smc_sip_handler_t handler); 26 | 27 | #if defined(CONFIG_ALLOW_SMC_CALLS) 28 | /* 29 | * Handle SMC SiP calls simply by forwarding the arguments to seL4 to perform 30 | * and placing the response registers into the guest's TCB registers. 31 | */ 32 | bool smc_sip_forward(size_t vcpu, seL4_UserContext *regs, size_t fn_number); 33 | #endif 34 | 35 | /* Helper functions */ 36 | void smc_set_return_value(seL4_UserContext *u, uint64_t val); 37 | 38 | /* Gets the value of x1-x6 */ 39 | uint64_t smc_get_arg(seL4_UserContext *u, uint64_t arg); 40 | -------------------------------------------------------------------------------- /include/libvmm/arch/aarch64/vgic/vgic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 | * Copyright 2022, UNSW (ABN 57 195 873 179) 4 | * 5 | * SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_PLAT_QEMU_ARM_VIRT) 14 | #define GIC_V2 15 | #define GIC_DIST_PADDR 0x8000000 16 | #elif defined(CONFIG_PLAT_ODROIDC4) 17 | #define GIC_V2 18 | #define GIC_DIST_PADDR 0xffc01000 19 | #elif defined(CONFIG_PLAT_MAAXBOARD) 20 | #define GIC_V3 21 | #define GIC_DIST_PADDR 0x38800000 22 | #define GIC_REDIST_PADDR 0x38880000 23 | #elif defined(CONFIG_PLAT_ZYNQMP) 24 | #define GIC_V2 25 | #define GIC_DIST_PADDR 0xf9010000 26 | #else 27 | #error Need to define GIC addresses 28 | #endif 29 | 30 | #if defined(GIC_V2) 31 | #define GIC_DIST_SIZE 0x1000 32 | #elif defined(GIC_V3) 33 | #define GIC_DIST_SIZE 0x10000 34 | #define GIC_REDIST_SIZE 0xc0000 35 | #else 36 | #error Unknown GIC version 37 | #endif 38 | 39 | /* Uncomment these defines for more verbose logging in the GIC driver. */ 40 | // #define DEBUG_IRQ 41 | // #define DEBUG_DIST 42 | 43 | #if defined(DEBUG_IRQ) 44 | #define LOG_IRQ(...) do{ printf("VGIC|IRQ: "); printf(__VA_ARGS__); }while(0) 45 | #else 46 | #define LOG_IRQ(...) do{}while(0) 47 | #endif 48 | 49 | #if defined(DEBUG_DIST) 50 | #define LOG_DIST(...) do{ printf("VGIC|DIST: "); printf(__VA_ARGS__); }while(0) 51 | #else 52 | #define LOG_DIST(...) do{}while(0) 53 | #endif 54 | 55 | void vgic_init(); 56 | bool fault_handle_vgic_maintenance(size_t vcpu_id); 57 | bool handle_vgic_dist_fault(size_t vcpu_id, size_t offset, size_t fsr, seL4_UserContext *regs, void *data); 58 | bool handle_vgic_redist_fault(size_t vcpu_id, size_t offset, size_t fsr, seL4_UserContext *regs, void *data); 59 | bool vgic_register_irq(size_t vcpu_id, int virq_num, virq_ack_fn_t ack_fn, void *ack_data); 60 | bool vgic_inject_irq(size_t vcpu_id, int irq); 61 | -------------------------------------------------------------------------------- /include/libvmm/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | #include 7 | #include 8 | #include 9 | 10 | #define VMM_MAGIC_LEN 3 11 | static char VMM_MAGIC[VMM_MAGIC_LEN] = { 'v', 'm', 'm' }; 12 | 13 | #define VMM_MAX_IRQS 32 14 | #define VMM_MAX_VCPUS 32 15 | #define VMM_MAX_UIOS 16 16 | #define VMM_MAX_VIRTIO_MMIO_DEVICES 32 17 | 18 | typedef struct vmm_config_irq { 19 | uint8_t id; 20 | uint32_t irq; 21 | } vmm_config_irq_t; 22 | 23 | typedef struct vmm_config_virtio_mmio_device { 24 | uint8_t type; 25 | uint64_t base; 26 | uint32_t size; 27 | uint32_t irq; 28 | } vmm_config_virtio_mmio_device_t; 29 | 30 | typedef struct vmm_config_vcpu { 31 | uint8_t id; 32 | } vmm_config_vcpu_t; 33 | 34 | #define VMM_UIO_NAME_MAX_LEN 32 35 | typedef struct vmm_config_uio_region { 36 | char name[VMM_UIO_NAME_MAX_LEN]; 37 | uintptr_t guest_paddr; 38 | uintptr_t vmm_vaddr; 39 | uint64_t size; 40 | uint32_t irq; 41 | } vmm_config_uio_region_t; 42 | 43 | typedef struct vmm_config { 44 | char magic[VMM_MAGIC_LEN]; 45 | uint64_t ram; 46 | uint64_t ram_size; 47 | uint64_t dtb; 48 | uint64_t initrd; 49 | uint8_t num_irqs; 50 | vmm_config_irq_t irqs[VMM_MAX_IRQS]; 51 | uint8_t num_vcpus; 52 | vmm_config_vcpu_t vcpus[VMM_MAX_VCPUS]; 53 | uint8_t num_virtio_mmio_devices; 54 | vmm_config_virtio_mmio_device_t virtio_mmio_devices[VMM_MAX_VIRTIO_MMIO_DEVICES]; 55 | uint8_t num_uio_regions; 56 | vmm_config_uio_region_t uios[VMM_MAX_UIOS]; 57 | } vmm_config_t; 58 | 59 | int vmm_config_irq_from_id(vmm_config_t *config, uint8_t id) 60 | { 61 | for (int i = 0; i < config->num_irqs; i++) { 62 | if (config->irqs[i].id == id) { 63 | return config->irqs[i].irq; 64 | } 65 | } 66 | 67 | return -1; 68 | } 69 | 70 | static bool vmm_config_check_magic(void *config) 71 | { 72 | char *magic = (char *)config; 73 | for (int i = 0; i < VMM_MAGIC_LEN; i++) { 74 | if (magic[i] != VMM_MAGIC[i]) { 75 | return false; 76 | } 77 | } 78 | 79 | return true; 80 | } 81 | -------------------------------------------------------------------------------- /include/libvmm/dtb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | /* 13 | * The following definitions are from v0.3, Section 5.2 of the Devicetree 14 | * Specification. 15 | */ 16 | 17 | /* Note that this is the little-endian representation, the specification 18 | * mentions only the big-endian representation. */ 19 | #define DTB_MAGIC 0xEDFE0DD0 20 | 21 | struct dtb_header { 22 | uint32_t magic; 23 | uint32_t totalsize; 24 | uint32_t off_dt_struct; 25 | uint32_t off_dt_strings; 26 | uint32_t off_mem_rsvmap; 27 | uint32_t version; 28 | uint32_t last_comp_version; 29 | uint32_t boot_cpuid_phys; 30 | uint32_t size_dt_strings; 31 | uint32_t size_dt_struct; 32 | }; 33 | 34 | bool dtb_check_magic(struct dtb_header *h) { 35 | return h->magic == DTB_MAGIC; 36 | } 37 | -------------------------------------------------------------------------------- /include/libvmm/guest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | #include 7 | #include 8 | #include 9 | 10 | bool guest_start(size_t boot_vcpu_id, uintptr_t kernel_pc, uintptr_t dtb, uintptr_t initrd); 11 | void guest_stop(size_t boot_vcpu_id); 12 | bool guest_restart(size_t boot_vcpu_id, uintptr_t guest_ram_vaddr, size_t guest_ram_size); 13 | -------------------------------------------------------------------------------- /include/libvmm/tcb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | void tcb_print_regs(size_t vcpu_id); 12 | -------------------------------------------------------------------------------- /include/libvmm/util/atomic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | // Qemu faults if you try load or store with a strict memorder on aarch64. 10 | #ifdef BOARD_qemu_virt_aarch64 11 | #define VMM_NO_ATOMICS 12 | #endif 13 | 14 | #ifdef VMM_NO_ATOMICS 15 | #define ATOMIC_LOAD(ptr, memorder) __atomic_load_n(ptr, __ATOMIC_RELAXED) 16 | #define ATOMIC_STORE(ptr, val, memorder) __atomic_store_n(ptr, val, __ATOMIC_RELAXED) 17 | #else 18 | #define ATOMIC_LOAD(ptr, memorder) __atomic_load_n(ptr, memorder) 19 | #define ATOMIC_STORE(ptr, val, memorder) __atomic_store_n(ptr, val, memorder) 20 | #endif 21 | -------------------------------------------------------------------------------- /include/libvmm/util/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | /** 13 | * Generic fixed-size circular queue 14 | */ 15 | typedef struct queue { 16 | int head; 17 | int len; 18 | int capacity; 19 | int item_size; 20 | void *data; 21 | } queue_t; 22 | 23 | static void queue_init(queue_t *q, int item_size, void *data, int capacity) 24 | { 25 | q->head = 0; 26 | q->len = 0; 27 | q->capacity = capacity; 28 | q->item_size = item_size; 29 | q->data = data; 30 | } 31 | 32 | /** Enqueue a small item to the queue */ 33 | static bool queue_enqueue(queue_t *queue, const void *item) 34 | { 35 | if (queue->len == queue->capacity) { 36 | return false; 37 | } 38 | 39 | int tail = (queue->head + queue->len) % queue->capacity; 40 | memcpy(queue->data + (tail * queue->item_size), item, queue->item_size); 41 | queue->len++; 42 | 43 | return true; 44 | } 45 | 46 | /** Enqueue a large item to the queue */ 47 | static void *queue_enqueue_raw(queue_t *queue) 48 | { 49 | if (queue->len == queue->capacity) { 50 | return NULL; 51 | } 52 | 53 | int tail = (queue->head + queue->len) % queue->capacity; 54 | void *data = queue->data + (tail * queue->item_size); 55 | 56 | queue->len++; 57 | return data; 58 | } 59 | 60 | static inline void queue_clear(queue_t *queue) 61 | { 62 | queue->len = 0; 63 | queue->head = 0; 64 | } 65 | 66 | /** 67 | * Get first element in queue. 68 | * This can be used with `queue_dequeue` to avoid copying if items are large. 69 | */ 70 | static void *queue_front(queue_t *queue) 71 | { 72 | if (queue->len == 0) { 73 | return NULL; 74 | } 75 | return queue->data + queue->head * queue->item_size; 76 | } 77 | 78 | /** Remove first element from queue */ 79 | static bool queue_dequeue(queue_t *queue) 80 | { 81 | if (queue->len == 0) { 82 | return false; 83 | } 84 | 85 | queue->head = (queue->head + 1) % queue->capacity; 86 | queue->len--; 87 | return true; 88 | } 89 | 90 | /** Get oldest element in queue and remove it */ 91 | static bool queue_dequeue_front(queue_t *queue, void *dest) 92 | { 93 | if (queue->len == 0) { 94 | return false; 95 | } 96 | 97 | const void *src = queue->data + queue->head * queue->item_size; 98 | memcpy(dest, src, queue->item_size); 99 | 100 | queue->head = (queue->head + 1) % queue->capacity; 101 | queue->len--; 102 | return true; 103 | } 104 | 105 | /** Remove most recently added element from queue */ 106 | static bool queue_dequeue_back(queue_t *queue) 107 | { 108 | if (queue->len == 0) { 109 | return false; 110 | } 111 | 112 | queue->len--; 113 | return true; 114 | } 115 | 116 | static inline int queue_size(queue_t *queue) 117 | { 118 | return queue->len; 119 | } 120 | 121 | static inline bool queue_empty(queue_t *queue) 122 | { 123 | return queue->len == 0; 124 | } 125 | -------------------------------------------------------------------------------- /include/libvmm/util/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021, Breakaway Consulting Pty. Ltd. 3 | * Copyright 2022, UNSW (ABN 57 195 873 179) 4 | * 5 | * SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // @ivanv: these are here for convience, should not be here though 16 | #define GUEST_VCPU_ID 0 17 | #define GUEST_NUM_VCPUS 1 18 | 19 | // @ivanv: if we keep using this, make sure that we have a static assert 20 | // that sizeof seL4_UserContext is 0x24 21 | // Note that this is AArch64 specific 22 | #if defined(CONFIG_ARCH_AARCH64) 23 | #define SEL4_USER_CONTEXT_SIZE 0x24 24 | #endif 25 | 26 | #ifndef ARRAY_SIZE 27 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) 28 | #endif 29 | 30 | #define CTZ(x) __builtin_ctz(x) 31 | 32 | #if __STDC_VERSION__ >= 201112L && !defined(__cplusplus) 33 | #define static_assert _Static_assert 34 | #endif 35 | 36 | #define LOG_VMM(...) do{ printf("%s|INFO: ", microkit_name); printf(__VA_ARGS__); }while(0) 37 | #define LOG_VMM_ERR(...) do{ printf("%s|ERROR: ", microkit_name); printf(__VA_ARGS__); }while(0) 38 | 39 | void *memcpy(void *restrict dest, const void *restrict src, size_t n); 40 | void *memset(void *dest, int c, size_t n); 41 | 42 | static void assert_fail( 43 | const char *assertion, 44 | const char *file, 45 | unsigned int line, 46 | const char *function) 47 | { 48 | printf("Failed assertion '%s' at %s:%u in function %s\n", assertion, file, line, function); 49 | __builtin_trap(); 50 | } 51 | 52 | #define BIT_LOW(n) (1ul<<(n)) 53 | #define BIT_HIGH(n) (1ul<<(n - 32 )) 54 | 55 | #ifndef MIN 56 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 57 | #endif 58 | #ifndef MAX 59 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 60 | #endif 61 | 62 | /* Convenience function to print memory region in hex */ 63 | void print_mem_hex(uintptr_t addr, size_t size); 64 | 65 | #ifndef assert 66 | #ifndef CONFIG_DEBUG_BUILD 67 | 68 | #define _unused(x) ((void)(x)) 69 | #define assert(expr) _unused(expr) 70 | 71 | #else 72 | 73 | #define assert(expr) \ 74 | do { \ 75 | if (!(expr)) { \ 76 | assert_fail(#expr, __FILE__, __LINE__, __FUNCTION__); \ 77 | } \ 78 | } while(0) 79 | 80 | #endif 81 | #endif 82 | -------------------------------------------------------------------------------- /include/libvmm/vcpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | void vcpu_reset(size_t vcpu_id); 12 | void vcpu_print_regs(size_t vcpu_id); 13 | -------------------------------------------------------------------------------- /include/libvmm/virq.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef MAX_PASSTHROUGH_IRQ 12 | #define MAX_PASSTHROUGH_IRQ MICROKIT_MAX_CHANNELS 13 | #endif 14 | 15 | typedef void (*virq_ack_fn_t)(size_t vcpu_id, int irq, void *cookie); 16 | 17 | bool virq_controller_init(size_t boot_vcpu_id); 18 | bool virq_register(size_t vcpu_id, size_t virq_num, virq_ack_fn_t ack_fn, void *ack_data); 19 | bool virq_inject(size_t vcpu_id, int irq); 20 | 21 | /* 22 | * These two APIs are convenient for when you want to directly passthrough an IRQ from 23 | * the hardware to the guest as the same vIRQ. This is useful when the guest has direct 24 | * passthrough access to a particular device on the hardware. 25 | * After registering the passthrough IRQ, call `virq_handle_passthrough` when 26 | * the IRQ has come through from seL4. 27 | */ 28 | bool virq_register_passthrough(size_t vcpu_id, size_t irq, microkit_channel irq_ch); 29 | bool virq_handle_passthrough(microkit_channel irq_ch); 30 | -------------------------------------------------------------------------------- /include/libvmm/virtio/config.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause 2 | Copyright Linux */ 3 | 4 | #pragma once 5 | /* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so 6 | * anyone can use the definitions to implement compatible drivers/servers. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of IBM nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. */ 30 | 31 | /* Virtio devices use a standardized configuration space to define their 32 | * features and pass configuration information, but each implementation can 33 | * store and access that space differently. */ 34 | 35 | /* Status byte for guest to report progress, and synchronize features. */ 36 | /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ 37 | /* the guest OS triggers a device reset*/ 38 | #define VIRTIO_CONFIG_S_RESET 0 39 | /* the guest OS has noticed the device. */ 40 | #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 41 | /* the guest OS has found a driver for the device. */ 42 | #define VIRTIO_CONFIG_S_DRIVER 2 43 | /* Driver has used its parts of the config, and is happy */ 44 | #define VIRTIO_CONFIG_S_DRIVER_OK 4 45 | /* Driver has finished configuring features */ 46 | #define VIRTIO_CONFIG_S_FEATURES_OK 8 47 | /* Device entered invalid state, driver must reset it */ 48 | #define VIRTIO_CONFIG_S_NEEDS_RESET 0x40 49 | /* We've given up on this device. */ 50 | #define VIRTIO_CONFIG_S_FAILED 0x80 51 | 52 | /* 53 | * Virtio feature bits VIRTIO_TRANSPORT_F_START through 54 | * VIRTIO_TRANSPORT_F_END are reserved for the transport 55 | * being used (e.g. virtio_ring, virtio_pci etc.), the 56 | * rest are per-device feature bits. 57 | */ 58 | #define VIRTIO_TRANSPORT_F_START 28 59 | #define VIRTIO_TRANSPORT_F_END 41 60 | 61 | #ifndef VIRTIO_CONFIG_NO_LEGACY 62 | /* Do we get callbacks when the ring is completely used, even if we've 63 | * suppressed them? */ 64 | #define VIRTIO_F_NOTIFY_ON_EMPTY 24 65 | 66 | /* Can the device handle any descriptor layout? */ 67 | #define VIRTIO_F_ANY_LAYOUT 27 68 | #endif /* VIRTIO_CONFIG_NO_LEGACY */ 69 | 70 | /* v1.0 compliant. */ 71 | #define VIRTIO_F_VERSION_1 32 72 | 73 | /* 74 | * If clear - device has the platform DMA (e.g. IOMMU) bypass quirk feature. 75 | * If set - use platform DMA tools to access the memory. 76 | * 77 | * Note the reverse polarity (compared to most other features), 78 | * this is for compatibility with legacy systems. 79 | */ 80 | #define VIRTIO_F_ACCESS_PLATFORM 33 81 | /* Legacy name for VIRTIO_F_ACCESS_PLATFORM (for compatibility with old userspace) */ 82 | #define VIRTIO_F_IOMMU_PLATFORM VIRTIO_F_ACCESS_PLATFORM 83 | 84 | /* This feature indicates support for the packed virtqueue layout. */ 85 | #define VIRTIO_F_RING_PACKED 34 86 | 87 | /* 88 | * Inorder feature indicates that all buffers are used by the device 89 | * in the same order in which they have been made available. 90 | */ 91 | #define VIRTIO_F_IN_ORDER 35 92 | 93 | /* 94 | * This feature indicates that memory accesses by the driver and the 95 | * device are ordered in a way described by the platform. 96 | */ 97 | #define VIRTIO_F_ORDER_PLATFORM 36 98 | 99 | /* 100 | * Does the device support Single Root I/O Virtualization? 101 | */ 102 | #define VIRTIO_F_SR_IOV 37 103 | 104 | /* 105 | * This feature indicates that the driver can reset a queue individually. 106 | */ 107 | #define VIRTIO_F_RING_RESET 40 -------------------------------------------------------------------------------- /include/libvmm/virtio/console.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header is BSD licensed so 3 | * anyone can use the definitions to implement compatible drivers/servers: 4 | * 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * Copyright (C) Red Hat, Inc., 2009, 2010, 2011 30 | * Copyright (C) Amit Shah , 2009, 2010, 2011 31 | */ 32 | /* 33 | * SPDX-License-Identifier: BSD-3-Clause 34 | */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #define RX_QUEUE 0 43 | #define TX_QUEUE 1 44 | #define CTL_RX_QUEUE 2 45 | #define CTL_TX_QUEUE 3 46 | 47 | /* For the console device to function, we only need an RX queue and TX queue. */ 48 | #define VIRTIO_CONSOLE_NUM_VIRTQ 2 49 | 50 | /* Feature bits */ 51 | #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ 52 | #define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ 53 | #define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */ 54 | 55 | #define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) 56 | 57 | #define VIRTIO_CONSOLE_MAX_PORTS 4 /* Support max 4 ports right now... */ 58 | 59 | #define VIRTIO_CONSOLE_CFG_MAX_PORTS (VIRTIO_PCI_CONFIG_OFF(false) + offsetof(struct virtio_con_cfg, max_nr_ports)) 60 | 61 | struct virtio_console_config { 62 | /* colums of the screens */ 63 | uint16_t cols; 64 | /* rows of the screens */ 65 | uint16_t rows; 66 | /* max. number of ports this device can hold */ 67 | uint32_t max_nr_ports; 68 | /* emergency write register */ 69 | uint32_t emerg_wr; 70 | } __attribute__((packed)); 71 | 72 | /* 73 | * A message that's passed between the Host and the Guest for a 74 | * particular port. 75 | */ 76 | struct virtio_console_control { 77 | uint32_t id; /* Port number */ 78 | uint16_t event; /* The kind of control event (see below) */ 79 | uint16_t value; /* Extra information for the key */ 80 | } __attribute__((packed)); 81 | 82 | /* Some events for control messages */ 83 | #define VIRTIO_CONSOLE_DEVICE_READY 0 84 | #define VIRTIO_CONSOLE_PORT_ADD 1 85 | #define VIRTIO_CONSOLE_PORT_REMOVE 2 86 | #define VIRTIO_CONSOLE_PORT_READY 3 87 | #define VIRTIO_CONSOLE_CON_PORT 4 88 | #define VIRTIO_CONSOLE_RESIZE 5 89 | #define VIRTIO_CONSOLE_PORT_OPEN 6 90 | #define VIRTIO_CONSOLE_PORT_NAME 7 91 | 92 | struct virtio_console_device { 93 | struct virtio_device virtio_device; 94 | struct virtio_queue_handler vqs[VIRTIO_CONSOLE_NUM_VIRTQ]; 95 | serial_queue_handle_t *rxq; 96 | serial_queue_handle_t *txq; 97 | int tx_ch; 98 | }; 99 | 100 | bool virtio_mmio_console_init(struct virtio_console_device *console, 101 | uintptr_t region_base, 102 | uintptr_t region_size, 103 | size_t virq, 104 | serial_queue_handle_t *rxq, 105 | serial_queue_handle_t *txq, 106 | int tx_ch); 107 | 108 | bool virtio_console_handle_rx(struct virtio_console_device *console); 109 | -------------------------------------------------------------------------------- /include/libvmm/virtio/virtio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* 16 | * All terminology used and functionality of the virtIO device implementation 17 | * adheres with the following specification: 18 | * Virtual I/O Device (VIRTIO) Version 1.2 19 | */ 20 | -------------------------------------------------------------------------------- /scripts/package_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2025, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | set -e 8 | 9 | IMAGE_PATH=$1 10 | IMAGE_NAME=$2 11 | METADATA_PATH=$3 12 | 13 | [[ -z "$IMAGE_PATH" || -z "$IMAGE_NAME" ]] && echo "usage: package_image.sh image_path package_name [image_metadata_path]" && exit 1 14 | 15 | # Zig package manager expects a build.zig.zon alongside the artefact, we also need 16 | # to zip/tar it. 17 | # `image_metadata_path` is useful for when you need to package a config file with a linux kernel or rootfs image. 18 | 19 | ZON_PATH="build.zig.zon" 20 | 21 | SHASUM=$(shasum "$IMAGE_PATH" | cut -d' ' -f1) 22 | 23 | # Clean the image name if it contain dots, a common case is `rootfs.cpio.gz` 24 | # Because it can interfere with the Zig syntax 25 | SANITISED_IMG_NAME=$(echo "$IMAGE_NAME" | tr '.' '_') 26 | 27 | cat > $ZON_PATH < 8 | #include 9 | #include 10 | 11 | uintptr_t linux_setup_images(uintptr_t ram_start, 12 | uintptr_t kernel, 13 | size_t kernel_size, 14 | uintptr_t dtb_src, 15 | uintptr_t dtb_dest, 16 | size_t dtb_size, 17 | uintptr_t initrd_src, 18 | uintptr_t initrd_dest, 19 | size_t initrd_size) 20 | { 21 | // Before doing any copying, let's sanity check the addresses. 22 | // First we'll check that everything actually lives inside RAM. 23 | // @ivanv: TODO 24 | // Check that the kernel and DTB to do not overlap 25 | uintptr_t dtb_start = dtb_dest; 26 | uintptr_t dtb_end = dtb_start + dtb_size; 27 | uintptr_t kernel_start = kernel; 28 | uintptr_t kernel_end = kernel_start + kernel_size; 29 | if (!(dtb_start >= kernel_end || dtb_end <= kernel_start)) { 30 | LOG_VMM_ERR("Linux kernel image [0x%lx..0x%lx)" 31 | " overlaps with the destination of the DTB [0x%lx, 0x%lx)\n", 32 | kernel_start, kernel_end, dtb_start, dtb_end); 33 | return 0; 34 | } 35 | // Check that the kernel and initrd do not overlap 36 | uintptr_t initrd_start = initrd_dest; 37 | uintptr_t initrd_end = initrd_start + initrd_size; 38 | if (!(initrd_start >= kernel_end || initrd_end <= kernel_start)) { 39 | LOG_VMM_ERR("Linux kernel image [0x%lx..0x%lx) overlaps" 40 | " with the destination of the initial RAM disk [0x%lx, 0x%lx)\n", 41 | kernel_start, kernel_end, initrd_start, initrd_end); 42 | return 0; 43 | } 44 | // Check that the DTB and initrd do not overlap 45 | if (!(initrd_start >= dtb_end || initrd_end <= dtb_start)) { 46 | LOG_VMM_ERR("DTB [0x%lx..0x%lx) overlaps with the destination of the" 47 | " initial RAM disk [0x%lx, 0x%lx)\n", 48 | dtb_start, dtb_end, initrd_start, initrd_end); 49 | return 0; 50 | } 51 | // First we inspect the kernel image header to confirm it is a valid image 52 | // and to determine where in memory to place the image. 53 | struct linux_image_header *image_header = (struct linux_image_header *) kernel; 54 | assert(image_header->magic == LINUX_IMAGE_MAGIC); 55 | if (image_header->magic != LINUX_IMAGE_MAGIC) { 56 | LOG_VMM_ERR("Linux kernel image magic check failed\n"); 57 | return 0; 58 | } 59 | // Copy the guest kernel image into the right location 60 | uintptr_t kernel_dest = ram_start + image_header->text_offset; 61 | // This check is because the Linux kernel image requires to be placed at text_offset of 62 | // a 2MB aligned base address anywhere in usable system RAM and called there. 63 | // In this case, we place the image at the text_offset of the start of the guest's RAM, 64 | // so we need to make sure that the start of guest RAM is 2MiB aligned. 65 | assert((ram_start & ((1 << 20) - 1)) == 0); 66 | LOG_VMM("Copying guest kernel image to 0x%x (0x%x bytes)\n", kernel_dest, kernel_size); 67 | memcpy((char *)kernel_dest, (char *)kernel, kernel_size); 68 | // Copy the guest device tree blob into the right location 69 | // First check that the DTB given is actually a DTB! 70 | struct dtb_header *dtb_header = (struct dtb_header *) dtb_src; 71 | assert(dtb_check_magic(dtb_header)); 72 | if (!dtb_check_magic(dtb_header)) { 73 | LOG_VMM_ERR("Given DTB does not match DTB magic.\n"); 74 | return 0; 75 | } 76 | // Linux does not allow the DTB to be greater than 2 megabytes in size. 77 | assert(dtb_size <= (1 << 21)); 78 | if (dtb_size > (1 << 21)) { 79 | LOG_VMM_ERR("Linux expects size of DTB to be less than 2MB, DTB size is 0x%lx bytes\n", dtb_size); 80 | return 0; 81 | } 82 | // Linux expects the address of the DTB to be on an 8-byte boundary. 83 | assert(dtb_dest % 0x8 == 0); 84 | if (dtb_dest % 0x8) { 85 | LOG_VMM_ERR("Linux expects DTB address to be on an 8-byte boundary, DTB address is 0x%lx\n", dtb_dest); 86 | return 0; 87 | } 88 | LOG_VMM("Copying guest DTB to 0x%x (0x%x bytes)\n", dtb_dest, dtb_size); 89 | memcpy((char *)dtb_dest, (char *)dtb_src, dtb_size); 90 | // Copy the initial RAM disk into the right location 91 | // @ivanv: add checks for initrd according to Linux docs 92 | LOG_VMM("Copying guest initial RAM disk to 0x%x (0x%x bytes)\n", initrd_dest, initrd_size); 93 | memcpy((char *)initrd_dest, (char *)initrd_src, initrd_size); 94 | 95 | return kernel_dest; 96 | } 97 | -------------------------------------------------------------------------------- /src/arch/aarch64/psci.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3 | * Copyright 2022, UNSW (ABN 57 195 873 179) 4 | * 5 | * SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* 16 | * The PSCI version is represented by a 32-bit unsigned integer. 17 | * The upper 15 bits represent the major version. 18 | * The lower 16 bits represent the minor version. 19 | */ 20 | #define PSCI_MAJOR_VERSION(v) ((v) << 16) 21 | #define PSCI_MINOR_VERSION(v) ((v) & ((1 << 16) - 1)) 22 | 23 | /* PSCI return codes */ 24 | #define PSCI_SUCCESS 0 25 | #define PSCI_NOT_SUPPORTED -1 26 | #define PSCI_INVALID_PARAMETERS -2 27 | #define PSCI_DENIED -3 28 | #define PSCI_ALREADY_ON -4 29 | #define PSCI_ON_PENDING -5 30 | #define PSCI_INTERNAL_FAILURE -6 31 | #define PSCI_NOT_PRESENT -7 32 | #define PSCI_DISABLED -8 33 | #define PSCI_INVALID_ADDRESS -9 34 | 35 | bool handle_psci(size_t vcpu_id, seL4_UserContext *regs, uint64_t fn_number, uint32_t hsr) 36 | { 37 | // @ivanv: write a note about what convention we assume, should we be checking 38 | // the convention? 39 | switch (fn_number) { 40 | case PSCI_VERSION: { 41 | /* We support PSCI version 1.2 */ 42 | uint32_t version = PSCI_MAJOR_VERSION(1) | PSCI_MINOR_VERSION(2); 43 | smc_set_return_value(regs, version); 44 | break; 45 | } 46 | case PSCI_CPU_ON: { 47 | uintptr_t target_cpu = smc_get_arg(regs, 1); 48 | // Right now we only have one vCPU and so any fault for a target vCPU 49 | // that isn't the one that's already on we consider an error on the 50 | // guest's side. 51 | // @ivanv: adapt for starting other vCPUs 52 | if (target_cpu == vcpu_id) { 53 | smc_set_return_value(regs, PSCI_ALREADY_ON); 54 | } else { 55 | // The guest has requested to turn on a virtual CPU that does 56 | // not exist. 57 | smc_set_return_value(regs, PSCI_INVALID_PARAMETERS); 58 | } 59 | break; 60 | } 61 | case PSCI_MIGRATE_INFO_TYPE: 62 | /* 63 | * There are multiple possible return values for MIGRATE_INFO_TYPE. 64 | * In this case returning 2 will tell the guest that this is a 65 | * system that does not use a "Trusted OS" as the PSCI 66 | * specification says. 67 | */ 68 | smc_set_return_value(regs, 2); 69 | break; 70 | case PSCI_FEATURES: 71 | // @ivanv: seems weird that we just return nothing here. 72 | smc_set_return_value(regs, PSCI_NOT_SUPPORTED); 73 | break; 74 | case PSCI_SYSTEM_RESET: { 75 | // @refactor come back to 76 | // bool success = guest_restart(); 77 | // if (!success) { 78 | // LOG_VMM_ERR("Failed to restart guest\n"); 79 | // smc_set_return_value(regs, PSCI_INTERNAL_FAILURE); 80 | // } else { 81 | 82 | // * If we've successfully restarted the guest, all we want to do 83 | // * is reply to the fault that caused us to handle the PSCI call 84 | // * so that the guest can continue executing. We do not need to 85 | // * advance the vCPU program counter as we typically do when 86 | // * handling a fault since the correct PC has been set when we 87 | // * call guest_restart(). 88 | 89 | // return true; 90 | // } 91 | break; 92 | } 93 | case PSCI_SYSTEM_OFF: 94 | // @refactor, is it guaranteed that the CPU that does the vCPU request 95 | // is the boot vcpu? 96 | guest_stop(vcpu_id); 97 | return true; 98 | default: 99 | LOG_VMM_ERR("Unhandled PSCI function ID 0x%lx\n", fn_number); 100 | return false; 101 | } 102 | 103 | bool success = fault_advance_vcpu(vcpu_id, regs); 104 | assert(success); 105 | 106 | return success; 107 | } 108 | -------------------------------------------------------------------------------- /src/arch/aarch64/tcb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void tcb_print_regs(size_t vcpu_id) { 13 | /* 14 | * While we are potentially doing an extra system call in order to read the 15 | * TCB registers (as the VMM may have already read the TCB registers before 16 | * calling this function), this function should only be used in the context 17 | * of errors or debug logging and hence the cost of the system call should 18 | * not be an issue. 19 | */ 20 | seL4_UserContext regs; 21 | seL4_Error err = seL4_TCB_ReadRegisters(BASE_VM_TCB_CAP + vcpu_id, false, 0, SEL4_USER_CONTEXT_SIZE, ®s); 22 | assert(err == seL4_NoError); 23 | if (err != seL4_NoError) { 24 | LOG_VMM_ERR("Could not read TCB registers when trying to print TCB registers\n"); 25 | return; 26 | } 27 | /* Now dump the TCB registers. */ 28 | LOG_VMM("dumping TCB (ID 0x%lx) registers:\n", vcpu_id); 29 | /* Frame registers */ 30 | printf(" pc: 0x%016lx\n", regs.pc); 31 | printf(" sp: 0x%016lx\n", regs.sp); 32 | printf(" spsr: 0x%016lx\n", regs.spsr); 33 | printf(" x0: 0x%016lx\n", regs.x0); 34 | printf(" x1: 0x%016lx\n", regs.x1); 35 | printf(" x2: 0x%016lx\n", regs.x2); 36 | printf(" x3: 0x%016lx\n", regs.x3); 37 | printf(" x4: 0x%016lx\n", regs.x4); 38 | printf(" x5: 0x%016lx\n", regs.x5); 39 | printf(" x6: 0x%016lx\n", regs.x6); 40 | printf(" x7: 0x%016lx\n", regs.x7); 41 | printf(" x8: 0x%016lx\n", regs.x8); 42 | printf(" x16: 0x%016lx\n", regs.x16); 43 | printf(" x17: 0x%016lx\n", regs.x17); 44 | printf(" x18: 0x%016lx\n", regs.x18); 45 | printf(" x29: 0x%016lx\n", regs.x29); 46 | printf(" x30: 0x%016lx\n", regs.x30); 47 | /* Other integer registers */ 48 | printf(" x9: 0x%016lx\n", regs.x9); 49 | printf(" x10: 0x%016lx\n", regs.x10); 50 | printf(" x11: 0x%016lx\n", regs.x11); 51 | printf(" x12: 0x%016lx\n", regs.x12); 52 | printf(" x13: 0x%016lx\n", regs.x13); 53 | printf(" x14: 0x%016lx\n", regs.x14); 54 | printf(" x15: 0x%016lx\n", regs.x15); 55 | printf(" x19: 0x%016lx\n", regs.x19); 56 | printf(" x20: 0x%016lx\n", regs.x20); 57 | printf(" x21: 0x%016lx\n", regs.x21); 58 | printf(" x22: 0x%016lx\n", regs.x22); 59 | printf(" x23: 0x%016lx\n", regs.x23); 60 | printf(" x24: 0x%016lx\n", regs.x24); 61 | printf(" x25: 0x%016lx\n", regs.x25); 62 | printf(" x26: 0x%016lx\n", regs.x26); 63 | printf(" x27: 0x%016lx\n", regs.x27); 64 | printf(" x28: 0x%016lx\n", regs.x28); 65 | /* Thread ID registers */ 66 | printf(" tpidr_el0: 0x%016lx\n", regs.tpidr_el0); 67 | printf(" tpidrro_el0: 0x%016lx\n", regs.tpidrro_el0); 68 | } 69 | -------------------------------------------------------------------------------- /src/arch/aarch64/vgic/vgic.c: -------------------------------------------------------------------------------- 1 | /* @ivanv double check 2 | * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 | * Copyright 2022, UNSW (ABN 57 195 873 179) 4 | * 5 | * SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(GIC_V2) 14 | #include 15 | #elif defined(GIC_V3) 16 | #include 17 | #else 18 | #error "Unknown GIC version" 19 | #endif 20 | 21 | #include 22 | 23 | /* The driver expects the VGIC state to be initialised before calling any of the driver functionality. */ 24 | extern vgic_t vgic; 25 | 26 | bool fault_handle_vgic_maintenance(size_t vcpu_id) 27 | { 28 | // @ivanv: reivist, also inconsistency between int and bool 29 | bool success = true; 30 | int idx = microkit_mr_get(seL4_VGICMaintenance_IDX); 31 | /* Currently not handling spurious IRQs */ 32 | // @ivanv: wtf, this comment seems irrelevant to the code. 33 | assert(idx >= 0); 34 | 35 | // @ivanv: Revisit and make sure it's still correct. 36 | vgic_vcpu_t *vgic_vcpu = get_vgic_vcpu(&vgic, vcpu_id); 37 | assert(vgic_vcpu); 38 | assert((idx >= 0) && (idx < ARRAY_SIZE(vgic_vcpu->lr_shadow))); 39 | struct virq_handle *slot = &vgic_vcpu->lr_shadow[idx]; 40 | assert(slot->virq != VIRQ_INVALID); 41 | struct virq_handle lr_virq = *slot; 42 | slot->virq = VIRQ_INVALID; 43 | slot->ack_fn = NULL; 44 | slot->ack_data = NULL; 45 | /* Clear pending */ 46 | LOG_IRQ("Maintenance IRQ %d\n", lr_virq.virq); 47 | set_pending(vgic_get_dist(vgic.registers), lr_virq.virq, false, vcpu_id); 48 | virq_ack(vcpu_id, &lr_virq); 49 | /* Check the overflow list for pending IRQs */ 50 | struct virq_handle *virq = vgic_irq_dequeue(&vgic, vcpu_id); 51 | 52 | #if defined(GIC_V2) 53 | int group = 0; 54 | #elif defined(GIC_V3) 55 | int group = 1; 56 | #else 57 | #error "Unknown GIC version" 58 | #endif 59 | 60 | if (virq) { 61 | success = vgic_vcpu_load_list_reg(&vgic, vcpu_id, idx, group, virq); 62 | } 63 | 64 | if (!success) { 65 | printf("VGIC|ERROR: maintenance handler failed\n"); 66 | assert(0); 67 | } 68 | 69 | return success; 70 | } 71 | 72 | // @ivanv: maybe this shouldn't be here? 73 | bool vgic_register_irq(size_t vcpu_id, int virq_num, virq_ack_fn_t ack_fn, void *ack_data) { 74 | assert(virq_num >= 0 && virq_num != VIRQ_INVALID); 75 | struct virq_handle virq = { 76 | .virq = virq_num, 77 | .ack_fn = ack_fn, 78 | .ack_data = ack_data, 79 | }; 80 | 81 | return virq_add(vcpu_id, &vgic, &virq); 82 | } 83 | 84 | bool vgic_inject_irq(size_t vcpu_id, int irq) 85 | { 86 | LOG_IRQ("Injecting IRQ %d\n", irq); 87 | 88 | return vgic_dist_set_pending_irq(&vgic, vcpu_id, irq); 89 | 90 | // @ivanv: explain why we don't check error before checking this fault stuff 91 | // @ivanv: seperately, it seems weird to have this fault handling code here? 92 | // @ivanv: revist 93 | // if (!fault_handled(vcpu->vcpu_arch.fault) && fault_is_wfi(vcpu->vcpu_arch.fault)) { 94 | // // ignore_fault(vcpu->vcpu_arch.fault); 95 | // err = advance_vcpu_fault(regs); 96 | // } 97 | } 98 | 99 | // @ivanv: revisit this whole function 100 | bool handle_vgic_dist_fault(size_t vcpu_id, size_t offset, size_t fsr, seL4_UserContext *regs, void *data) 101 | { 102 | bool success = false; 103 | if (fault_is_read(fsr)) { 104 | // printf("VGIC|INFO: Read dist\n"); 105 | success = vgic_dist_reg_read(vcpu_id, &vgic, offset, fsr, regs); 106 | assert(success); 107 | } else { 108 | // printf("VGIC|INFO: Write dist\n"); 109 | success = vgic_dist_reg_write(vcpu_id, &vgic, offset, fsr, regs); 110 | assert(success); 111 | } 112 | 113 | return success; 114 | } -------------------------------------------------------------------------------- /src/arch/aarch64/virq.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* Maps Microkit channel numbers with registered vIRQ */ 14 | int virq_passthrough_map[MAX_PASSTHROUGH_IRQ] = {-1}; 15 | 16 | #define SGI_RESCHEDULE_IRQ 0 17 | #define SGI_FUNC_CALL 1 18 | #define PPI_VTIMER_IRQ 27 19 | 20 | static void vppi_event_ack(size_t vcpu_id, int irq, void *cookie) 21 | { 22 | microkit_vcpu_arm_ack_vppi(vcpu_id, irq); 23 | } 24 | 25 | static void sgi_ack(size_t vcpu_id, int irq, void *cookie) {} 26 | 27 | bool virq_controller_init(size_t boot_vcpu_id) { 28 | bool success; 29 | 30 | vgic_init(); 31 | #if defined(GIC_V2) 32 | LOG_VMM("initialised virtual GICv2 driver\n"); 33 | #elif defined(GIC_V3) 34 | LOG_VMM("initialised virtual GICv3 driver\n"); 35 | #else 36 | #error "Unsupported GIC version" 37 | #endif 38 | 39 | /* Register the fault handler */ 40 | success = fault_register_vm_exception_handler(GIC_DIST_PADDR, GIC_DIST_SIZE, handle_vgic_dist_fault, NULL); 41 | if (!success) { 42 | LOG_VMM_ERR("Failed to register fault handler for GIC distributor region\n"); 43 | return false; 44 | } 45 | #if defined(GIC_V3) 46 | success = fault_register_vm_exception_handler(GIC_REDIST_PADDR, GIC_REDIST_SIZE, handle_vgic_redist_fault, NULL); 47 | if (!success) { 48 | LOG_VMM_ERR("Failed to register fault handler for GIC redistributor region\n"); 49 | return false; 50 | } 51 | #endif 52 | 53 | success = vgic_register_irq(boot_vcpu_id, PPI_VTIMER_IRQ, &vppi_event_ack, NULL); 54 | if (!success) { 55 | LOG_VMM_ERR("Failed to register vCPU virtual timer IRQ: 0x%lx\n", PPI_VTIMER_IRQ); 56 | return false; 57 | } 58 | success = vgic_register_irq(boot_vcpu_id, SGI_RESCHEDULE_IRQ, &sgi_ack, NULL); 59 | if (!success) { 60 | LOG_VMM_ERR("Failed to register vCPU SGI 0 IRQ"); 61 | return false; 62 | } 63 | success = vgic_register_irq(boot_vcpu_id, SGI_FUNC_CALL, &sgi_ack, NULL); 64 | if (!success) { 65 | LOG_VMM_ERR("Failed to register vCPU SGI 1 IRQ"); 66 | return false; 67 | } 68 | 69 | return true; 70 | } 71 | 72 | bool virq_inject(size_t vcpu_id, int irq) { 73 | return vgic_inject_irq(vcpu_id, irq); 74 | } 75 | 76 | bool virq_register(size_t vcpu_id, size_t virq_num, virq_ack_fn_t ack_fn, void *ack_data) { 77 | return vgic_register_irq(vcpu_id, virq_num, ack_fn, ack_data); 78 | } 79 | 80 | static void virq_passthrough_ack(size_t vcpu_id, int irq, void *cookie) { 81 | /* We are down-casting to microkit_channel so must first cast to size_t */ 82 | microkit_irq_ack((microkit_channel)(size_t)cookie); 83 | } 84 | 85 | bool virq_register_passthrough(size_t vcpu_id, size_t irq, microkit_channel irq_ch) { 86 | assert(irq_ch < MICROKIT_MAX_CHANNELS); 87 | if (irq_ch >= MICROKIT_MAX_CHANNELS) { 88 | LOG_VMM_ERR("Invalid channel number given '0x%lx' for passthrough vIRQ 0x%lx\n", irq_ch, irq); 89 | return false; 90 | } 91 | 92 | LOG_VMM("Register passthrough vIRQ 0x%lx on vCPU 0x%lx (IRQ channel: 0x%lx)\n", irq, vcpu_id, irq_ch); 93 | virq_passthrough_map[irq_ch] = irq; 94 | 95 | bool success = virq_register(GUEST_VCPU_ID, irq, &virq_passthrough_ack, (void *)(size_t)irq_ch); 96 | assert(success); 97 | if (!success) { 98 | LOG_VMM_ERR("Failed to register passthrough vIRQ %d\n", irq); 99 | return false; 100 | } 101 | 102 | return true; 103 | } 104 | 105 | bool virq_handle_passthrough(microkit_channel irq_ch) { 106 | assert(virq_passthrough_map[irq_ch] >= 0); 107 | if (virq_passthrough_map[irq_ch] < 0) { 108 | LOG_VMM_ERR("attempted to handle invalid passthrough IRQ channel 0x%lx\n", irq_ch); 109 | return false; 110 | } 111 | 112 | bool success = vgic_inject_irq(GUEST_VCPU_ID, virq_passthrough_map[irq_ch]); 113 | if (!success) { 114 | LOG_VMM_ERR("could not inject passthrough vIRQ 0x%lx, dropped on vCPU 0x%lx\n", virq_passthrough_map[irq_ch], GUEST_VCPU_ID); 115 | return false; 116 | } 117 | 118 | return true; 119 | } 120 | -------------------------------------------------------------------------------- /src/guest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | bool guest_start(size_t boot_vcpu_id, uintptr_t kernel_pc, uintptr_t dtb, uintptr_t initrd) { 12 | /* 13 | * Set the TCB registers to what the virtual machine expects to be started with. 14 | * You will note that this is currently Linux specific as we currently do not support 15 | * any other kind of guest. However, even though the library is open to supporting other 16 | * guests, there is no point in prematurely generalising this code. 17 | */ 18 | seL4_UserContext regs = {0}; 19 | regs.x0 = dtb; 20 | regs.spsr = 5; // PMODE_EL1h 21 | regs.pc = kernel_pc; 22 | /* Write out all the TCB registers */ 23 | seL4_Word err = seL4_TCB_WriteRegisters( 24 | BASE_VM_TCB_CAP + boot_vcpu_id, 25 | false, // We'll explcitly start the guest below rather than in this call 26 | 0, // No flags 27 | 4, // Writing to x0, pc, and spsr. Due to the ordering of seL4_UserContext the count must be 4. 28 | ®s 29 | ); 30 | assert(err == seL4_NoError); 31 | if (err != seL4_NoError) { 32 | LOG_VMM_ERR("Failed to write registers to boot vCPU's TCB (id is 0x%lx), error is: 0x%lx\n", boot_vcpu_id, err); 33 | return false; 34 | } 35 | LOG_VMM("starting guest at 0x%lx, DTB at 0x%lx, initial RAM disk at 0x%lx\n", 36 | regs.pc, regs.x0, initrd); 37 | /* Restart the boot vCPU to the program counter of the TCB associated with it */ 38 | microkit_vcpu_restart(boot_vcpu_id, regs.pc); 39 | 40 | return true; 41 | } 42 | 43 | void guest_stop(size_t boot_vcpu_id) { 44 | LOG_VMM("Stopping guest\n"); 45 | microkit_vcpu_stop(boot_vcpu_id); 46 | LOG_VMM("Stopped guest\n"); 47 | } 48 | 49 | bool guest_restart(size_t boot_vcpu_id, uintptr_t guest_ram_vaddr, size_t guest_ram_size) { 50 | LOG_VMM("Attempting to restart guest\n"); 51 | // First, stop the guest 52 | microkit_vcpu_stop(boot_vcpu_id); 53 | LOG_VMM("Stopped guest\n"); 54 | // Then, we need to clear all of RAM 55 | LOG_VMM("Clearing guest RAM\n"); 56 | memset((char *)guest_ram_vaddr, 0, guest_ram_size); 57 | // Copy back the images into RAM 58 | // bool success = guest_init_images(); 59 | // if (!success) { 60 | // LOG_VMM_ERR("Failed to initialise guest images\n"); 61 | // return false; 62 | // } 63 | vcpu_reset(boot_vcpu_id); 64 | // Now we need to re-initialise all the VMM state 65 | // vmm_init(); 66 | // linux_start(GUEST_VCPU_ID, kernel_pc, GUEST_DTB_VADDR, GUEST_INIT_RAM_DISK_VADDR); 67 | LOG_VMM("Restarted guest\n"); 68 | return true; 69 | } 70 | -------------------------------------------------------------------------------- /src/util/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022, UNSW (ABN 57 195 873 179) 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | /* This is required to use the printf library we brought in, it is 11 | simply for convenience since there's a lot of logging/debug printing 12 | in the VMM. */ 13 | void _putchar(char character) 14 | { 15 | microkit_dbg_putc(character); 16 | } 17 | 18 | void *memset(void *dest, int c, size_t n) 19 | { 20 | unsigned char *s = dest; 21 | for (; n; n--, s++) { 22 | *s = c; 23 | } 24 | return dest; 25 | } 26 | 27 | void *memcpy(void *restrict dest, const void *restrict src, size_t n) 28 | { 29 | unsigned char *d = dest; 30 | const unsigned char *s = src; 31 | for (; n; n--) { 32 | *d++ = *s++; 33 | } 34 | return dest; 35 | } 36 | 37 | void print_mem_hex(uintptr_t addr, size_t size) 38 | { 39 | #ifdef CONFIG_DEBUG_BUILD 40 | for (size_t i = 0; i < size; i++) { 41 | printf("%02X ", *((uint8_t *)addr + i)); 42 | if ((i + 1) % 16 == 0) { 43 | printf("\n"); 44 | } 45 | } 46 | printf("\n"); 47 | #endif 48 | } 49 | -------------------------------------------------------------------------------- /tools/dtscat: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | # This is a basic script that concatenates the given Device Tree Sources. 8 | # Useful for when you have a single DTS 9 | # Author: tim-arney 10 | 11 | usage() { 12 | cmd=$(basename "$0") 13 | echo "usage: $cmd [ ... ]" >&2 14 | exit 1 15 | } 16 | 17 | if [ "$#" -lt 1 ] 18 | then 19 | usage 20 | fi 21 | 22 | BASE="$1" 23 | shift 24 | OVERLAYS="$@" 25 | 26 | if [ ! -f "$BASE" ] 27 | then 28 | echo "error: file not found: $BASE" >&2 29 | exit 1 30 | fi 31 | 32 | if ! grep '/dts-v1/;' "$BASE" > /dev/null 33 | then 34 | echo "error: '/dts-v1/;' tag not found in $BASE, are you sure this is a base file?" >&2 35 | exit 1 36 | fi 37 | 38 | for OVERLAY in $OVERLAYS 39 | do 40 | if [ ! -f "$OVERLAY" ] 41 | then 42 | echo "error: file not found: $OVERLAY" >&2 43 | exit 1 44 | fi 45 | 46 | if grep '/dts-v1/;' "$OVERLAY" > /dev/null 47 | then 48 | echo "error: '/dts-v1/;' tag found in $OVERLAY, should only be present in the base file" >&2 49 | exit 1 50 | fi 51 | done 52 | 53 | cat "$BASE" $OVERLAYS 54 | 55 | -------------------------------------------------------------------------------- /tools/linux/blk/blk_client_init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | mount /dev/vda /mnt -------------------------------------------------------------------------------- /tools/linux/blk/blk_init.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | # Generates blk_client_init and blk_driver_init scripts 7 | # 8 | 9 | ifeq ($(strip $(MICROKIT_BOARD)),) 10 | $(error MICROKIT_BOARD must be specified) 11 | endif 12 | 13 | LINUX_BLK_DIR := $(abs $(dir $(last ${MAKEFILES_LIST}))) 14 | LIBVMM ?= $(realpath ${LINUX_BLK_DIR}/../../../) 15 | 16 | blk_client_init: $(LIBVMM)/tools/linux/blk/blk_client_init 17 | cp $^ $@ 18 | 19 | blk_driver_init: $(LIBVMM)/tools/linux/blk/board/$(MICROKIT_BOARD)/blk_driver_init 20 | cp $^ $@ 21 | 22 | clobber:: 23 | rm -f blk_client_init blk_driver_init 24 | -------------------------------------------------------------------------------- /tools/linux/blk/board/odroidc4/blk_driver_init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | /root/uio_blk_driver /dev/mmcblk0 & 8 | -------------------------------------------------------------------------------- /tools/linux/blk/board/qemu_virt_aarch64/blk_driver_init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | /root/uio_blk_driver /dev/vda & -------------------------------------------------------------------------------- /tools/linux/include/uio/blk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | #pragma once 7 | 8 | #include 9 | 10 | int driver_init(void **maps, uintptr_t *maps_phys, int num_maps, int argc, char **argv); 11 | void driver_notified(); 12 | -------------------------------------------------------------------------------- /tools/linux/include/uio/libuio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | #pragma once 7 | 8 | /* Notify the VMM */ 9 | void uio_notify(); 10 | -------------------------------------------------------------------------------- /tools/linux/include/uio/sound.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #define UIO_SND_FAULT_ADDRESS 0x301000 12 | 13 | typedef struct vm_shared_state { 14 | sound_shared_state_t sound; 15 | uintptr_t data_paddr; 16 | } vm_shared_state_t; 17 | -------------------------------------------------------------------------------- /tools/linux/snd/board/odroidc4/asound.conf: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | # This assumes the playback device is card 1, and recording is card 0. 6 | # S95sound uses 'default' as playback device and 'hw:0,0' as recording. 7 | defaults.pcm.card 1 8 | defaults.pcm.device 0 9 | -------------------------------------------------------------------------------- /tools/linux/snd/board/qemu_virt_aarch64/asound.conf: -------------------------------------------------------------------------------- 1 | # Copyright 2024, UNSW 2 | # 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | 5 | # Defaults work fine for Qemu, no config necessary 6 | -------------------------------------------------------------------------------- /tools/linux/snd/snd_driver_init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | DAEMON="uio_snd_driver" 8 | PIDFILE="/var/run/$DAEMON.pid" 9 | LOGFILE="/var/log/user_sound" 10 | 11 | # shellcheck source=/dev/null 12 | [ -r "/etc/default/$DAEMON" ] && . "/etc/default/$DAEMON" 13 | 14 | # user_sound does not create a pidfile, so pass "-n" in the command line 15 | # and use "-m" to instruct start-stop-daemon to create one. 16 | start() { 17 | printf 'Starting %s: ' "$DAEMON" 18 | 19 | start-stop-daemon -b -m -S -q \ 20 | -p "$PIDFILE" \ 21 | --startas /bin/sh \ 22 | -- -c "exec '/root/$DAEMON' default hw:0,0 >> '$LOGFILE' 2>&1" 23 | status=$? 24 | if [ "$status" -eq 0 ]; then 25 | echo "OK" 26 | else 27 | echo "FAIL" 28 | fi 29 | return "$status" 30 | } 31 | 32 | stop() { 33 | printf 'Stopping %s: ' "$DAEMON" 34 | start-stop-daemon -K -q -p "$PIDFILE" 35 | status=$? 36 | if [ "$status" -eq 0 ]; then 37 | rm -f "$PIDFILE" 38 | echo "OK" 39 | else 40 | echo "FAIL" 41 | fi 42 | return "$status" 43 | } 44 | 45 | restart() { 46 | stop 47 | sleep 1 48 | start 49 | } 50 | 51 | case "$1" in 52 | start|stop|restart) 53 | "$1";; 54 | reload) 55 | # Restart, since there is no true "reload" feature. 56 | restart;; 57 | *) 58 | echo "Usage: $0 {start|stop|restart|reload}" 59 | exit 1 60 | esac 61 | -------------------------------------------------------------------------------- /tools/linux/snd/sound_init.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | # Generates asound.conf, snd_driver_init 7 | # 8 | 9 | ifeq ($(strip $(MICROKIT_BOARD)),) 10 | $(error MICROKIT_BOARD must be specified) 11 | endif 12 | 13 | LINUX_BLK_DIR := $(abs $(dir $(last ${MAKEFILES_LIST}))) 14 | LIBVMM ?= $(realpath ${LINUX_BLK_DIR}/../../../) 15 | 16 | asound.conf: $(LIBVMM)/tools/linux/snd/board/$(MICROKIT_BOARD)/asound.conf 17 | cp $^ $@ 18 | 19 | snd_driver_init: $(LIBVMM)/tools/linux/snd/snd_driver_init 20 | cp $^ $@ 21 | 22 | clobber:: 23 | rm -f asound.conf snd_driver_init 24 | -------------------------------------------------------------------------------- /tools/linux/uio/uio.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | # Generates library libuio.a to be linked against userlevel drivers 7 | # 8 | 9 | ifeq ($(strip $(SDDF)),) 10 | $(error SDDF must be specified) 11 | endif 12 | ifeq ($(strip $(CC_USERLEVEL)),) 13 | $(error CC_USERLEVEL must be specified) 14 | endif 15 | 16 | LINUX_UIO_DIR := $(abs $(dir $(last ${MAKEFILES_LIST}))) 17 | LIBVMM ?= $(realpath ${LINUX_UIO_DIR}/../../../) 18 | 19 | LIBUIO_IMAGES := libuio.a 20 | 21 | CFLAGS_libuio := -I$(SDDF)/include -I$(SDDF)/include/microkit -I$(LIBVMM)/tools/linux/include 22 | 23 | CHECK_LIBUIO_FLAGS_MD5:=.libuio_cflags-$(shell echo -- $(CFLAGS_USERLEVEL) $(CFLAGS_libuio) | shasum | sed 's/ *-//') 24 | 25 | $(CHECK_LIBUIO_FLAGS_MD5): 26 | -rm -f .libuio_cflags-* 27 | touch $@ 28 | 29 | 30 | libuio.a: libuio.o 31 | ar rv $@ $^ 32 | 33 | libuio.o: $(CHECK_LIBUIO_FLAGS_MD5) 34 | libuio.o: $(LIBVMM)/tools/linux/uio/libuio.c 35 | $(CC_USERLEVEL) $(CFLAGS_USERLEVEL) $(CFLAGS_libuio) -o $@ -c $< 36 | 37 | clean:: 38 | rm -f libuio.[od] .libuio_cflags-* 39 | 40 | clobber:: 41 | rm -f $(UIO_BLK_IMAGES) 42 | 43 | -include libuio.d 44 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/blk/uio_blk.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | # This Makefile snippet builds block UIO driver 7 | # 8 | 9 | ifeq ($(strip $(LIBVMM_TOOLS)),) 10 | $(error LIBVMM_TOOLS must be specified) 11 | endif 12 | ifeq ($(strip $(SDDF)),) 13 | $(error SDDF must be specified) 14 | endif 15 | ifeq ($(strip $(CC_USERLEVEL)),) 16 | $(error CC_USERLEVEL must be specified) 17 | endif 18 | 19 | UIO_BLK_IMAGES := uio_blk_driver 20 | 21 | CFLAGS_uio_blk_driver := -I$(SDDF)/include -I$(SDDF)/include/microkit -I$(LIBVMM_TOOLS)/linux/include 22 | 23 | CHECK_UIO_BLK_DRIVER_FLAGS_MD5:=.uio_blk_driver_cflags-$(shell echo -- $(CFLAGS_USERLEVEL) $(CFLAGS_uio_blk_driver) | shasum | sed 's/ *-//') 24 | 25 | $(CHECK_UIO_BLK_DRIVER_FLAGS_MD5): 26 | -rm -f .uio_blk_driver_cflags-* 27 | touch $@ 28 | 29 | uio_blk_driver: uio_blk_driver.o libuio.a 30 | $(CC_USERLEVEL) $(CFLAGS_USERLEVEL) $(CFLAGS_uio_blk_driver) $^ -o $@ 31 | 32 | uio_blk_driver.o: $(CHECK_UIO_BLK_DRIVER_FLAGS_MD5) 33 | uio_blk_driver.o: $(LIBVMM_TOOLS)/linux/uio_drivers/blk/blk.c 34 | $(CC_USERLEVEL) $(CFLAGS_USERLEVEL) $(CFLAGS_uio_blk_driver) -o $@ -c $< 35 | 36 | clean:: 37 | rm -f uio_blk_driver.[od] .uio_blk_driver_cflags-* 38 | 39 | clobber:: 40 | rm -f $(UIO_BLK_IMAGES) 41 | 42 | -include uio_blk_driver.d 43 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/convert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include "convert.h" 8 | 9 | #define RATE_COUNT 14 10 | #define FORMAT_COUNT 25 11 | 12 | static unsigned int alsa_rates[RATE_COUNT] = { 13 | 5512, 8000, 11025, 16000, 22050, 32000, 44100, 14 | 48000, 64000, 88200, 96000, 176400, 192000, 384000, 15 | }; 16 | 17 | static sound_pcm_rate_t sddf_rates[RATE_COUNT] = { 18 | SOUND_PCM_RATE_5512, SOUND_PCM_RATE_8000, SOUND_PCM_RATE_11025, 19 | SOUND_PCM_RATE_16000, SOUND_PCM_RATE_22050, SOUND_PCM_RATE_32000, 20 | SOUND_PCM_RATE_44100, SOUND_PCM_RATE_48000, SOUND_PCM_RATE_64000, 21 | SOUND_PCM_RATE_88200, SOUND_PCM_RATE_96000, SOUND_PCM_RATE_176400, 22 | SOUND_PCM_RATE_192000, SOUND_PCM_RATE_384000, 23 | }; 24 | 25 | static snd_pcm_format_t alsa_formats[FORMAT_COUNT] = { 26 | SND_PCM_FORMAT_IMA_ADPCM, 27 | SND_PCM_FORMAT_MU_LAW, 28 | SND_PCM_FORMAT_A_LAW, 29 | SND_PCM_FORMAT_S8, 30 | SND_PCM_FORMAT_U8, 31 | SND_PCM_FORMAT_S16, 32 | SND_PCM_FORMAT_U16, 33 | SND_PCM_FORMAT_S18_3LE, 34 | SND_PCM_FORMAT_U18_3LE, 35 | SND_PCM_FORMAT_S20_3LE, 36 | SND_PCM_FORMAT_U20_3LE, 37 | SND_PCM_FORMAT_S24_3LE, 38 | SND_PCM_FORMAT_U24_3LE, 39 | SND_PCM_FORMAT_S20, 40 | SND_PCM_FORMAT_U20, 41 | SND_PCM_FORMAT_S24, 42 | SND_PCM_FORMAT_U24, 43 | SND_PCM_FORMAT_S32, 44 | SND_PCM_FORMAT_U32, 45 | SND_PCM_FORMAT_FLOAT, 46 | SND_PCM_FORMAT_FLOAT64, 47 | SND_PCM_FORMAT_DSD_U8, 48 | SND_PCM_FORMAT_DSD_U16_LE, 49 | SND_PCM_FORMAT_DSD_U32_LE, 50 | SND_PCM_FORMAT_IEC958_SUBFRAME, 51 | }; 52 | 53 | static sound_pcm_fmt_t sddf_formats[FORMAT_COUNT] = { 54 | SOUND_PCM_FMT_IMA_ADPCM, 55 | SOUND_PCM_FMT_MU_LAW, 56 | SOUND_PCM_FMT_A_LAW, 57 | SOUND_PCM_FMT_S8, 58 | SOUND_PCM_FMT_U8, 59 | SOUND_PCM_FMT_S16, 60 | SOUND_PCM_FMT_U16, 61 | SOUND_PCM_FMT_S18_3, 62 | SOUND_PCM_FMT_U18_3, 63 | SOUND_PCM_FMT_S20_3, 64 | SOUND_PCM_FMT_U20_3, 65 | SOUND_PCM_FMT_S24_3, 66 | SOUND_PCM_FMT_U24_3, 67 | SOUND_PCM_FMT_S20, 68 | SOUND_PCM_FMT_U20, 69 | SOUND_PCM_FMT_S24, 70 | SOUND_PCM_FMT_U24, 71 | SOUND_PCM_FMT_S32, 72 | SOUND_PCM_FMT_U32, 73 | SOUND_PCM_FMT_FLOAT, 74 | SOUND_PCM_FMT_FLOAT64, 75 | SOUND_PCM_FMT_DSD_U8, 76 | SOUND_PCM_FMT_DSD_U16, 77 | SOUND_PCM_FMT_DSD_U32, 78 | SOUND_PCM_FMT_IEC958_SUBFRAME, 79 | }; 80 | 81 | uint64_t sddf_rates_from_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 82 | { 83 | uint64_t rates = 0; 84 | for (int i = 0; i < RATE_COUNT; i++) { 85 | if (snd_pcm_hw_params_test_rate(pcm, params, alsa_rates[i], 0) == 0) { 86 | rates |= (1 << sddf_rates[i]); 87 | } 88 | } 89 | return rates; 90 | } 91 | 92 | uint64_t sddf_formats_from_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 93 | { 94 | uint64_t formats = 0; 95 | for (int i = 0; i < FORMAT_COUNT; i++) { 96 | if (snd_pcm_hw_params_test_format(pcm, params, alsa_formats[i]) == 0) { 97 | formats |= (1 << sddf_formats[i]); 98 | } 99 | } 100 | return formats; 101 | } 102 | 103 | snd_pcm_format_t sddf_format_to_alsa(sound_pcm_fmt_t format) 104 | { 105 | unsigned idx = (unsigned)format; 106 | if (idx >= FORMAT_COUNT) 107 | return SND_PCM_FORMAT_UNKNOWN; 108 | 109 | return alsa_formats[idx]; 110 | } 111 | 112 | unsigned int sddf_rate_to_alsa(sound_pcm_rate_t rate) 113 | { 114 | unsigned idx = (unsigned)rate; 115 | if (idx >= RATE_COUNT) 116 | return INVALID_RATE; 117 | 118 | return alsa_rates[idx]; 119 | } 120 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/convert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #define INVALID_RATE ((unsigned)-1) 13 | 14 | uint64_t sddf_rates_from_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); 15 | 16 | uint64_t sddf_formats_from_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); 17 | 18 | snd_pcm_format_t sddf_format_to_alsa(sound_pcm_fmt_t format); 19 | 20 | unsigned int sddf_rate_to_alsa(sound_pcm_rate_t rate); 21 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #define DEBUG_SOUND 12 | 13 | #if defined(DEBUG_SOUND) 14 | #define LOG_SOUND(...) do{ fprintf(stderr, "UIO(SOUND): "); fprintf(stderr, __VA_ARGS__); }while(0) 15 | #else 16 | #define LOG_SOUND(...) do{}while(0) 17 | #endif 18 | 19 | #define LOG_SOUND_ERR(...) do{ fprintf(stderr, "UIO(SOUND)|ERROR: "); fprintf(stderr, __VA_ARGS__); }while(0) 20 | #define LOG_SOUND_WARN(...) do{ fprintf(stderr, "UIO(SOUND)|WARN: "); fprintf(stderr, __VA_ARGS__); }while(0) 21 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #include "queue.h" 8 | #include 9 | #include 10 | #include 11 | 12 | struct queue 13 | { 14 | int head; 15 | int len; 16 | int capacity; 17 | int item_size; 18 | void *data; 19 | }; 20 | 21 | queue_t *queue_create(int item_size, int initial_capacity) 22 | { 23 | void *data = calloc(initial_capacity, item_size); 24 | if (data == NULL) { 25 | return NULL; 26 | } 27 | 28 | queue_t *queue = malloc(sizeof(queue_t)); 29 | if (queue == NULL) { 30 | free(data); 31 | return NULL; 32 | } 33 | 34 | queue->head = 0; 35 | queue->len = 0; 36 | queue->capacity = initial_capacity; 37 | queue->item_size = item_size; 38 | queue->data = data; 39 | 40 | return queue; 41 | } 42 | 43 | static void pcm_queue_resize(queue_t *queue) 44 | { 45 | int tail = (queue->head + queue->len) % queue->capacity; 46 | 47 | queue->capacity *= 2; 48 | queue->data = realloc(queue->data, queue->item_size * queue->capacity); 49 | assert(queue->data); 50 | 51 | memcpy(queue->data + queue->item_size * queue->len, queue->data, tail * queue->item_size); 52 | } 53 | 54 | void queue_enqueue(queue_t *queue, const void *item) 55 | { 56 | if (queue->len == queue->capacity) { 57 | pcm_queue_resize(queue); 58 | } 59 | 60 | int tail = (queue->head + queue->len) % queue->capacity; 61 | memcpy(queue->data + (tail * queue->item_size), item, queue->item_size); 62 | queue->len++; 63 | } 64 | 65 | void queue_clear(queue_t *queue) 66 | { 67 | queue->len = 0; 68 | queue->head = 0; 69 | } 70 | 71 | void *queue_front(queue_t *queue) 72 | { 73 | if (queue->len == 0) { 74 | return NULL; 75 | } 76 | return queue->data + queue->head * queue->item_size; 77 | } 78 | 79 | bool queue_dequeue(queue_t *queue) 80 | { 81 | if (queue->len == 0) { 82 | return false; 83 | } 84 | 85 | queue->head = (queue->head + 1) % queue->capacity; 86 | queue->len--; 87 | return true; 88 | } 89 | 90 | int queue_size(queue_t *queue) 91 | { 92 | return queue->len; 93 | } 94 | 95 | bool queue_empty(queue_t *queue) 96 | { 97 | return queue->len == 0; 98 | } 99 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | typedef struct queue queue_t; 12 | 13 | queue_t *queue_create(int item_size, int initial_capacity); 14 | 15 | void queue_enqueue(queue_t *queue, const void *item); 16 | 17 | void *queue_front(queue_t *queue); 18 | 19 | bool queue_dequeue(queue_t *queue); 20 | 21 | void queue_clear(queue_t *queue); 22 | 23 | int queue_size(queue_t *queue); 24 | 25 | bool queue_empty(queue_t *queue); 26 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/stream.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct stream stream_t; 14 | 15 | stream_t *stream_open(sound_pcm_info_t *info, 16 | const char *device, 17 | snd_pcm_stream_t direction, 18 | ssize_t translate_offset, 19 | sound_cmd_queue_handle_t *cmd_res, 20 | sound_pcm_queue_handle_t *pcm_res); 21 | 22 | void stream_enqueue_command(stream_t *stream, sound_cmd_t *cmd); 23 | void stream_enqueue_pcm_req(stream_t *stream, sound_pcm_t *pcm); 24 | 25 | int stream_timer_fd(stream_t *stream); 26 | 27 | /* Returns true to signal client notify */ 28 | bool stream_update(stream_t *stream); 29 | 30 | snd_pcm_stream_t stream_direction(stream_t *stream); 31 | -------------------------------------------------------------------------------- /tools/linux/uio_drivers/snd/uio_snd.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | # This Makefile snippet builds sound UIO driver 7 | # 8 | 9 | ifeq ($(strip $(SDDF)),) 10 | $(error SDDF must be specified) 11 | endif 12 | ifeq ($(strip $(CC_USERLEVEL)),) 13 | $(error CC_USERLEVEL must be specified) 14 | endif 15 | 16 | LINUX_BLK_DIR := $(abs $(dir $(last ${MAKEFILES_LIST}))) 17 | LIBVMM ?= $(realpath ${LINUX_BLK_DIR}/../../../../) 18 | 19 | UIO_SND_IMAGES := uio_snd_driver 20 | 21 | CFLAGS_uio_snd_driver := -I$(SDDF)/include -I$(SDDF)/include/microkit -I$(LIBVMM)/tools/linux/include -I$(LIBVMM)/include -lasound -lm -MD 22 | 23 | CHECK_UIO_SND_DRIVER_FLAGS_HASH:=.uio_snd_driver_cflags-$(shell echo -- $(CFLAGS_USERLEVEL) $(CFLAGS_uio_snd_driver) | shasum | sed 's/ *-//') 24 | 25 | $(CHECK_UIO_SND_DRIVER_FLAGS_HASH): 26 | -rm -f .uio_snd_driver_cflags-* 27 | touch $@ 28 | 29 | UIO_SND_DRV_DIR := $(LIBVMM)/tools/linux/uio_drivers/snd 30 | 31 | CFILES_uio_snd_driver := main.c stream.c queue.c convert.c 32 | OBJECTS_uio_snd_driver := $(CFILES_uio_snd_driver:.c=.o) 33 | DEPENDS_uio_snd_driver := $(CFILES_uio_snd_driver:.c=.d) 34 | 35 | OBJECTS_uio_snd_driver := $(addprefix _uio_snd_driver/,$(OBJECTS_uio_snd_driver)) 36 | DEPENDS_uio_snd_driver := $(addprefix _uio_snd_driver/,$(DEPENDS_uio_snd_driver)) 37 | 38 | _uio_snd_driver: 39 | mkdir -p _uio_snd_driver 40 | 41 | uio_snd_driver: $(OBJECTS_uio_snd_driver) 42 | $(CC_USERLEVEL) $(CFLAGS_USERLEVEL) $(CFLAGS_uio_snd_driver) $(OBJECTS_uio_snd_driver) -o $@ 43 | 44 | $(OBJECTS_uio_snd_driver): |_uio_snd_driver 45 | 46 | _uio_snd_driver/%.o: $(UIO_SND_DRV_DIR)/%.c $(CHECK_UIO_SND_DRIVER_FLAGS_HASH) 47 | $(CC_USERLEVEL) $(CFLAGS_USERLEVEL) $(CFLAGS_uio_snd_driver) -o $@ -c $< 48 | 49 | clean:: 50 | rm -rf _uio_snd_driver .uio_snd_driver_cflags-* 51 | 52 | clobber:: clean 53 | rm -f $(UIO_SND_IMAGES) 54 | 55 | -include $(DEPENDS_uio_snd_driver) 56 | -------------------------------------------------------------------------------- /tools/mkvirtdisk: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | # This bash script creates a virtual disk image with an msdos partition table. 8 | # It aligns the partitions to both the sDDF transfer size and the device's logical size. 9 | # It creates a FAT 12 filesystem on each partition. 10 | 11 | set -e 12 | 13 | function cleanup { 14 | rm -f $BUILD_DIR/fat.img 15 | } 16 | 17 | trap cleanup EXIT 18 | 19 | # Usage instructions 20 | if [ $# -ne 4 ]; then 21 | echo "Usage: $0 " 22 | exit 1 23 | fi 24 | 25 | if ! command -v mkfs.fat &> /dev/null 26 | then 27 | echo "mkfs.fat could not be found, please install dosfstools" 28 | exit 1 29 | fi 30 | 31 | if [[ "$OSTYPE" != "linux"* ]] && [[ "$OSTYPE" != "darwin"* ]]; then 32 | echo "This script is not supported on your OS" 33 | exit 1 34 | fi 35 | 36 | DISK_IMAGE=$1 37 | NUM_PARTITIONS=$2 38 | LSIZE=$3 39 | MEMSIZE=$4 40 | 41 | SDDF_TRANSFER_SIZE=4096 42 | FDISK_LSIZE=512 43 | 44 | if [ $(( LSIZE & (LSIZE - 1) )) -ne 0 ] || [ $LSIZE -lt $FDISK_LSIZE ]; then 45 | echo "LSIZE must be greater than $FDISK_LSIZE and must be a power of 2" 46 | exit 1 47 | fi 48 | 49 | if [ $((MEMSIZE)) -lt 114688 ]; then 50 | echo "MEMSIZE must be greater than 114688(112 * 1024), sizes smaller than that break either fdisk or mkfs.fat" 51 | exit 1 52 | fi 53 | 54 | BUILD_DIR=$(dirname $DISK_IMAGE) 55 | 56 | # Since LSIZE is always a power of 2, the least common multiple of LSIZE and SDDF_TRANSFER_SIZE is simply the bigger one 57 | LCM=$(( SDDF_TRANSFER_SIZE > LSIZE ? SDDF_TRANSFER_SIZE : LSIZE )) 58 | 59 | # Since we are using fdisk, COUNT uses the logical size provided by fdisk which is always 512 bytes (FDISK_LSIZE) 60 | MULTIPLE=$(( LCM / FDISK_LSIZE )) 61 | COUNT=$(( MEMSIZE / FDISK_LSIZE )) 62 | 63 | # Determine starting partition offset 64 | if [ $COUNT -gt $(( 2048 * 4 )) ]; then 65 | POFFSET=2048 66 | else 67 | POFFSET=$MULTIPLE 68 | fi 69 | 70 | COUNT=$(( COUNT / MULTIPLE * MULTIPLE)) # Now ensure the real COUNT is a multiple of sDDF transfer size and logical size 71 | 72 | # Create a file to act as a virtual disk 73 | dd if=/dev/zero of=$DISK_IMAGE bs=$FDISK_LSIZE count=$COUNT 2> /dev/null 74 | 75 | FS_COUNT=$(( (COUNT - POFFSET - 1) / NUM_PARTITIONS )) 76 | FS_COUNT=$(( FS_COUNT / MULTIPLE * MULTIPLE )) # Ensures that both filesystems are a multiple of sDDF transfer size and logical size 77 | 78 | # Create MBR partition table 79 | if [[ "$OSTYPE" == "linux"* ]]; then 80 | PREV=$POFFSET 81 | { 82 | echo o # Create a new empty DOS partition table 83 | 84 | # Loop to create each partition 85 | for i in $(seq 1 $NUM_PARTITIONS) 86 | do 87 | echo n # Add a new partition 88 | echo p # Primary partition 89 | if [ $i != 4 ]; then 90 | echo $i # Partition number 91 | fi 92 | echo $PREV # First sector 93 | echo +$(( FS_COUNT - 1 )) # Last sector 94 | PREV=$(( PREV + FS_COUNT )) 95 | done 96 | 97 | echo w # Write changes 98 | } | fdisk $DISK_IMAGE 99 | fdisk -l $DISK_IMAGE # Print the partition table 100 | elif [[ "$OSTYPE" == "darwin"* ]]; then 101 | PREV=$POFFSET 102 | { 103 | echo y # Answer yes to create a new empty DOS partition table 104 | 105 | # Loop to create each partition 106 | for i in $(seq 1 $NUM_PARTITIONS) 107 | do 108 | echo edit $i 109 | echo 01 # Set partition type to FAT12 110 | echo n # No to editing in CHS mode 111 | echo $PREV # First sector 112 | echo $FS_COUNT # Number of sectors in partition 113 | PREV=$(( PREV + FS_COUNT )) 114 | done 115 | 116 | echo write # Write changes 117 | echo quit # Save and quit fdisk 118 | } | fdisk -e $DISK_IMAGE 119 | fdisk $DISK_IMAGE # Print the partition table 120 | else 121 | echo "This script is not supported on your OS" 122 | exit 1 123 | fi 124 | 125 | # Create the FAT filesystem 126 | rm -f $BUILD_DIR/fat.img 127 | mkfs.fat -C $BUILD_DIR/fat.img $(( (FS_COUNT * FDISK_LSIZE) / 1024 )) -F 12 -S $LSIZE 128 | 129 | # Copy the FAT filesystem to the virtual disk 130 | for i in $(seq 0 $(( NUM_PARTITIONS - 1 ))) 131 | do 132 | echo "Copying FAT filesystem to partition $i, seek=$((POFFSET + i * FS_COUNT)), count=$FS_COUNT, bs=$FDISK_LSIZE" 133 | dd if=$BUILD_DIR/fat.img of=$DISK_IMAGE bs=$FDISK_LSIZE seek="$((POFFSET + i * FS_COUNT))" count=$FS_COUNT 2> /dev/null 134 | done 135 | -------------------------------------------------------------------------------- /tools/package_guest_images.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023, UNSW 3 | * 4 | * SPDX-License-Identifier: BSD-2-Clause 5 | */ 6 | 7 | /* 8 | * What even is this file? 9 | * 10 | * The purpose of this file is to include binaries that the VMM needs to setup, 11 | * such as the Linux kernel image, the DTB, and the initial RAM disk. What 12 | * happens is that the assembler includes the binary data in an ELF section 13 | * that we specify. We can then have global variables that essentially point to 14 | * where the data has been included. 15 | * 16 | * For each ELF section, we define a name and also some configuration. The 17 | * attributes "aw" is to say that the section is allocatable and that it is 18 | * writeable. We also have "@progbits", this is to indicate that the section 19 | * contains data (rather than code or something else). 20 | * 21 | */ 22 | 23 | #if defined(GUEST_KERNEL_IMAGE_PATH) 24 | .section .guest_kernel_image, "aw", @progbits 25 | .global _guest_kernel_image, _guest_kernel_image_end 26 | _guest_kernel_image: 27 | .incbin GUEST_KERNEL_IMAGE_PATH 28 | _guest_kernel_image_end: 29 | #endif 30 | 31 | #if defined(GUEST_DTB_IMAGE_PATH) 32 | .section .guest_dtb_image, "aw", @progbits 33 | .global _guest_dtb_image, _guest_dtb_image_end 34 | _guest_dtb_image: 35 | .incbin GUEST_DTB_IMAGE_PATH 36 | _guest_dtb_image_end: 37 | #endif 38 | 39 | #if defined(GUEST_INITRD_IMAGE_PATH) 40 | .section .guest_initrd_image, "aw", @progbits 41 | .global _guest_initrd_image, _guest_initrd_image_end 42 | _guest_initrd_image: 43 | .incbin GUEST_INITRD_IMAGE_PATH 44 | _guest_initrd_image_end: 45 | #endif 46 | -------------------------------------------------------------------------------- /tools/packrootfs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2024, UNSW 4 | # 5 | # SPDX-License-Identifier: BSD-2-Clause 6 | 7 | set -e 8 | 9 | if [ $# -lt 2 ]; then 10 | echo "Usage: $0 [-o output_rootfs] [--startup files...] [--home files...] [--etc files...]" 11 | exit 1 12 | fi 13 | 14 | which cpio >/dev/null 15 | if [ "$?" != 0 ]; then 16 | echo "packrootfs: Please install cpio utility." 17 | exit 1 18 | fi 19 | 20 | rootfs=$1 21 | tmpdir=$2 22 | startup_files=() 23 | home_files=() 24 | etc_files=() 25 | output_rootfs="" 26 | 27 | shift 2 28 | while [[ $# -gt 0 ]]; do 29 | case $1 in 30 | --startup) 31 | shift 32 | while [[ $# -gt 0 ]] && ! [[ $1 == --* ]]; do 33 | startup_files+=("$1") 34 | shift 35 | done 36 | ;; 37 | --home) 38 | shift 39 | while [[ $# -gt 0 ]] && ! [[ $1 == --* ]]; do 40 | home_files+=("$1") 41 | shift 42 | done 43 | ;; 44 | --etc) 45 | shift 46 | while [[ $# -gt 0 ]] && ! [[ $1 == --* ]]; do 47 | etc_files+=("$1") 48 | shift 49 | done 50 | ;; 51 | -o) 52 | shift 53 | if [[ $# -gt 0 ]]; then 54 | output_rootfs=$1 55 | shift 56 | else 57 | echo "Error: -o option requires an argument." 58 | exit 1 59 | fi 60 | ;; 61 | *) 62 | echo "Unknown option $1" 63 | exit 1 64 | ;; 65 | esac 66 | done 67 | 68 | if [ -z "$output_rootfs" ]; then 69 | output_rootfs="rootfs_modified.cpio.gz" 70 | fi 71 | 72 | rm -rf "$tmpdir" 73 | mkdir -p "$tmpdir" 74 | mkdir -p "$tmpdir/root" 75 | mkdir -p "$tmpdir/etc/init.d" 76 | 77 | # Copy home files to /root 78 | for file in "${home_files[@]}"; do 79 | cp -- "$file" "$tmpdir/root/$(basename "$file")" 80 | chmod +x "$tmpdir/root/$(basename "$file")" 81 | done 82 | 83 | # Copy startup files to /etc/init.d and prefix with S99 84 | for file in "${startup_files[@]}"; do 85 | cp -- "$file" "$tmpdir/etc/init.d/S99$(basename "$file")" 86 | chmod +x "$tmpdir/etc/init.d/S99$(basename "$file")" 87 | done 88 | 89 | # Copy etc files to /etc 90 | for file in "${etc_files[@]}"; do 91 | cp -- "$file" "$tmpdir/etc/$(basename "$file")" 92 | done 93 | 94 | if [[ "$output_rootfs" == /* ]]; then 95 | final_output_path="$output_rootfs" 96 | else 97 | final_output_path="$(pwd)/$output_rootfs" 98 | fi 99 | 100 | # Create CPIO from tmpdir and concatenate it with given rootfs, gzip result. 101 | # This is required as we cannot unpack roofs on macOS. 102 | ({ 103 | gunzip -dc "$rootfs"; 104 | cd "$tmpdir" && find . | cpio -o -H newc 105 | } | gzip > "$final_output_path") 106 | -------------------------------------------------------------------------------- /vmm.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2024, UNSW (ABN 57 195 873 179) 3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause 5 | # 6 | # Snippet to build libvmm.a, to be included in a full-system Makefile. 7 | # 8 | 9 | LIBVMM_DIR := $(abspath $(dir $(lastword ${MAKEFILE_LIST}))) 10 | 11 | GIC_V3_BOARDS := imx8mm_evk maaxboard 12 | ifeq ($(filter ${MICROKIT_BOARD},${GIC_V3_BOARDS}),) 13 | VGIC := GIC_V2 14 | VGIC_FILES := src/arch/aarch64/vgic/vgic_v2.c 15 | else 16 | VGIC := GIC_V3 17 | VGIC_FILES := src/arch/aarch64/vgic/vgic_v3.c 18 | endif 19 | 20 | AARCH64_FILES := src/arch/aarch64/fault.c \ 21 | src/arch/aarch64/linux.c \ 22 | src/arch/aarch64/linux.c \ 23 | src/arch/aarch64/psci.c \ 24 | src/arch/aarch64/smc.c \ 25 | src/arch/aarch64/tcb.c \ 26 | src/arch/aarch64/vcpu.c \ 27 | src/arch/aarch64/virq.c \ 28 | src/arch/aarch64/vgic/vgic.c \ 29 | ${VGIC_FILES} 30 | 31 | # VIRTIO MMIO depends on sddf 32 | ifeq ($(strip $(SDDF)),) 33 | $(error libvmm needs the location of the SDDF to build virtIO components) 34 | endif 35 | 36 | # we need ${SDDF} for virtIO; we need ${LIBVMM} for all 37 | # the libvmm api interfaces 38 | ifeq ($(findstring ${SDDF}/include, ${CFLAGS}),) 39 | CFLAGS += -I${SDDF}/include -I${SDDF}/include/microkit 40 | endif 41 | ifeq ($(findstring ${LIBVMM_DIR}/include,${CFLAGS}),) 42 | CFLAGS += -I${LIBVMM_DIR}/include 43 | endif 44 | 45 | ARCH_INDEP_FILES := src/util/printf.c \ 46 | src/util/util.c \ 47 | src/virtio/block.c \ 48 | src/virtio/console.c \ 49 | src/virtio/mmio.c \ 50 | src/virtio/net.c \ 51 | src/virtio/sound.c \ 52 | src/guest.c 53 | 54 | CFILES := ${AARCH64_FILES} ${ARCH_INDEP_FILES} 55 | OBJECTS := $(subst src,libvmm,${CFILES:.c=.o}) 56 | 57 | 58 | # Generate dependencies automatically 59 | CFLAGS += -MD 60 | 61 | # Force rebuid if CFLAGS changes. 62 | # This will pick up (among other things} changes 63 | # to Microkit BOARD and CONFIG. 64 | CHECK_LIBVMM_CFLAGS:=.libvmm_cflags.$(shell echo ${CFLAGS} | shasum | sed 's/ *-$$//') 65 | .libvmm_cflags.%: 66 | rm -f .libvmm_cflags.* 67 | echo ${CFLAGS} > $@ 68 | 69 | # This is ugly, but needed to distinguish directories in the BUILD area 70 | # from directories in the source area. 71 | libvmm/arch/aarch64/vgic: 72 | mkdir -p libvmm/arch/aarch64/vgic/ 73 | mkdir -p libvmm/util 74 | mkdir -p libvmm/virtio 75 | 76 | libvmm.a: ${OBJECTS} 77 | ${AR} crv $@ $^ 78 | 79 | ${OBJECTS}: ${SDDF}/include 80 | ${OBJECTS}: ${CHECK_LIBVMM_CFLAGS} |libvmm/arch/aarch64/vgic 81 | 82 | libvmm/%.o: src/%.c 83 | ${CC} ${CFLAGS} -c -o $@ $< 84 | 85 | -include ${OBJECTS:.o=.d} 86 | 87 | clean:: 88 | rm -f ${OBJECTS} ${OBJECTS:.c=.d} 89 | 90 | clobber:: clean 91 | rmdir src/arch/aarch64/vgic 92 | rmdir src/util 93 | rmdir src/virtio 94 | --------------------------------------------------------------------------------