├── .github ├── dependabot.yml └── workflows │ ├── build-pdf.yml │ └── pre-commit.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.adoc ├── dependencies ├── Gemfile ├── README.md ├── apt_packages.txt └── package.json ├── iommu_ref_model ├── LICENSE ├── Makefile ├── README.md ├── libiommu │ ├── Makefile │ ├── include │ │ ├── iommu.h │ │ ├── iommu_atc.h │ │ ├── iommu_ats.h │ │ ├── iommu_command_queue.h │ │ ├── iommu_data_structures.h │ │ ├── iommu_fault.h │ │ ├── iommu_hpm.h │ │ ├── iommu_interrupt.h │ │ ├── iommu_ref_api.h │ │ ├── iommu_registers.h │ │ ├── iommu_req_rsp.h │ │ ├── iommu_translate.h │ │ └── iommu_utils.h │ └── src │ │ ├── iommu_atc.c │ │ ├── iommu_ats.c │ │ ├── iommu_command_queue.c │ │ ├── iommu_device_context.c │ │ ├── iommu_faults.c │ │ ├── iommu_hpm.c │ │ ├── iommu_interrupt.c │ │ ├── iommu_msi_trans.c │ │ ├── iommu_process_context.c │ │ ├── iommu_reg.c │ │ ├── iommu_second_stage_trans.c │ │ ├── iommu_translate.c │ │ ├── iommu_two_stage_trans.c │ │ └── iommu_utils.c ├── libtables │ ├── Makefile │ ├── include │ │ └── tables_api.h │ └── src │ │ ├── build_ddt.c │ │ ├── build_g_stage_pt.c │ │ ├── build_pdt.c │ │ ├── build_s_stage_pt.c │ │ ├── build_vs_stage_pt.c │ │ └── translate_gpa.c ├── runtests └── test │ ├── Makefile │ ├── tbapi.c │ ├── test_app.c │ ├── test_app.h │ └── test_utils.c └── src ├── bibliography.adoc ├── contributors.adoc ├── images ├── ddt-base.svg ├── ddt-ext.svg ├── guest-OS.svg ├── hypervisor.svg ├── interfaces.svg ├── msi-imsic.svg ├── non-virt-OS.svg ├── pdt.svg └── placement.svg ├── index.adoc ├── iommu.bib ├── iommu_data_structures.adoc ├── iommu_debug.adoc ├── iommu_extensions.adoc ├── iommu_hw_guidelines.adoc ├── iommu_in_memory_queues.adoc ├── iommu_intro.adoc ├── iommu_preface.adoc ├── iommu_registers.adoc ├── iommu_sw_guidelines.adoc └── riscv-iommu.adoc /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem 3 | version: 2 4 | updates: 5 | - package-ecosystem: gitsubmodule 6 | directory: / 7 | schedule: 8 | interval: daily 9 | -------------------------------------------------------------------------------- /.github/workflows/build-pdf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Create Specification Document 3 | 4 | # The workflow is triggered by pull request, push to main, and manual dispatch. 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | revision_mark: 9 | description: "Set revision mark as Draft, Release or Stable:" 10 | required: true 11 | type: choice 12 | options: 13 | - Development 14 | - Stable 15 | - Frozen 16 | - Ratified 17 | default: Development 18 | prerelease: 19 | description: Tag as a pre-release? 20 | required: false 21 | type: boolean 22 | default: true 23 | draft: 24 | description: Create release as a draft? 25 | required: false 26 | type: boolean 27 | default: false 28 | pull_request: 29 | push: 30 | branches: 31 | - main 32 | 33 | jobs: 34 | build: 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | # Checkout the repository 39 | - name: Checkout repository 40 | uses: actions/checkout@v4 41 | with: 42 | submodules: recursive 43 | 44 | - name: Get next version 45 | uses: reecetech/version-increment@2024.4.4 46 | id: version 47 | with: 48 | scheme: semver 49 | increment: patch 50 | 51 | # Pull the latest RISC-V Docs container image 52 | - name: Pull Container 53 | run: docker pull riscvintl/riscv-docs-base-container-image:latest 54 | 55 | # Override VERSION and REVMARK for manual workflow dispatch 56 | - name: Update environment variables 57 | run: | 58 | echo "VERSION=v${{ steps.version.outputs.version }}" >> "$GITHUB_ENV" 59 | echo "REVMARK=${{ github.event.inputs.revision_mark }}" >> "$GITHUB_ENV" 60 | if: github.event_name == 'workflow_dispatch' 61 | 62 | # Build Files 63 | - name: Build Files 64 | run: make 65 | 66 | # Upload the built PDF files as a single artifact 67 | - name: Upload Build Artifacts 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: Build Artifacts 71 | path: ${{ github.workspace }}/build/*.pdf 72 | retention-days: 30 73 | 74 | # Create Release 75 | - name: Create Release 76 | uses: softprops/action-gh-release@v1 77 | with: 78 | files: ${{ github.workspace }}/build/*.pdf 79 | tag_name: v${{ steps.version.outputs.version }} 80 | name: Release ${{ steps.version.outputs.version }} 81 | draft: ${{ github.event.inputs.draft }} 82 | prerelease: ${{ github.event.inputs.prerelease }} 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GHTOKEN }} 85 | if: github.event_name == 'workflow_dispatch' 86 | # This condition ensures this step only runs for workflow_dispatch events. 87 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: pre-commit 3 | 4 | on: 5 | pull_request: 6 | push: 7 | branches: [main] 8 | 9 | jobs: 10 | pre-commit: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v5 15 | - uses: pre-commit/action@v3.0.0 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | /images/* 3 | .vscode 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs-resources"] 2 | path = docs-resources 3 | url = https://github.com/riscv/docs-resources.git 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.6.0 5 | hooks: 6 | - id: check-json 7 | - id: check-symlinks 8 | - id: check-yaml 9 | - id: end-of-file-fixer 10 | exclude: \.svg$ 11 | - id: trailing-whitespace 12 | args: [--markdown-linebreak-ext=md] 13 | exclude: \.svg$ 14 | 15 | - repo: local 16 | hooks: 17 | - id: forbidden-file-extensions 18 | name: forbidden-file-extensions 19 | entry: disallow these file extensions 20 | language: fail 21 | # Disallow other asciidoc extensions except .adoc 22 | files: .*\.(asciidoc|asc)$ 23 | 24 | - repo: https://github.com/rbubley/mirrors-prettier 25 | rev: v3.3.0 26 | hooks: 27 | - id: prettier 28 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # RISC-V Code of Conduct 2 | 3 | All RISC-V International projects are governed by the RISC-V Code of Conduct found at https://riscv.org/community/community-code-of-conduct/. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | As an open-source project, we appreciate and encourage community members to submit patches directly to the project. To maintain a well-organized development environment, we have established standards and methods for submitting changes. This document outlines the process for submitting patches to the project, ensuring that your contribution is swiftly incorporated into the codebase. 4 | 5 | # Licensing 6 | 7 | Licensing is crucial for open-source projects, as it guarantees that the software remains available under the conditions specified by the author. 8 | 9 | This project employs the Creative Commons Attribution 4.0 International license, which can be found in the LICENSE file within the project's repository. 10 | 11 | Licensing defines the rights granted to you as an author by the copyright holder. It is essential for contributors to fully understand and accept these licensing rights. In some cases, the copyright holder may not be the contributor, such as when the contributor is working on behalf of a company. 12 | 13 | # Developer Certificate of Origin (DCO) 14 | 15 | To uphold licensing criteria and demonstrate good faith, this project mandates adherence to the Developer Certificate of Origin (DCO) process. 16 | 17 | The DCO is an attestation appended to every contribution from each author. In the commit message of the contribution (explained in greater detail later in this document), the author adds a Signed-off-by statement, thereby accepting the DCO. 18 | 19 | When an author submits a patch, they affirm that they possess the right to submit the patch under the designated license. The DCO agreement is displayed below and at https://developercertificate.org. 20 | 21 | Developer's Certificate of Origin 1.1 22 | 23 | By making a contribution to this project, I certify that: 24 | 25 | (a) The contribution was created in whole or in part by me and I 26 | have the right to submit it under the open source license 27 | indicated in the file; or 28 | 29 | (b) The contribution is based upon previous work that, to the best 30 | of my knowledge, is covered under an appropriate open source 31 | license and I have the right under that license to submit that 32 | work with modifications, whether created in whole or in part 33 | by me, under the same open source license (unless I am 34 | permitted to submit under a different license), as indicated 35 | in the file; or 36 | 37 | (c) The contribution was provided directly to me by some other 38 | person who certified (a), (b), or (c), and I have not modified 39 | it. 40 | 41 | (d) I understand and agree that this project and the contribution 42 | are public and that a record of the contribution (including all 43 | personal information I submit with it, including my sign-off) is 44 | maintained indefinitely and may be redistributed consistent with 45 | this project or the open source license(s) involved. 46 | 47 | # DCO Sign-Off Methods 48 | 49 | The DCO necessitates the inclusion of a sign-off message in the following format for each commit within the pull request: 50 | 51 | Signed-off-by: Stephano Cetola 52 | 53 | Please use your real name in the sign-off message. 54 | 55 | You can manually add the DCO text to your commit body or include either -s or --signoff in your standard Git commit commands. If you forget to incorporate the sign-off, you can also amend a previous commit with the sign-off by executing git commit --amend -s. If you have already pushed your changes to GitHub, you will need to force push your branch afterward using git push -f. 56 | 57 | Note: 58 | 59 | Ensure that the name and email address associated with your GitHub account match the name and email address in the Signed-off-by line of your commit message. 60 | -------------------------------------------------------------------------------- /GOVERNANCE.md: -------------------------------------------------------------------------------- 1 | # Governance 2 | 3 | This project for the template specification is governed by the Documentation SIG. 4 | 5 | The group can be joined by RISC-V members at: https://lists.riscv.org/g/sig-documentation. 6 | 7 | Mailing list archives are available at: https://lists.riscv.org/g/sig-documentation/topics. 8 | 9 | **_NOTE:_** PROJECTS BUILT USING THE TEMPLATE SHOULD UPDATE THE ABOVE TEXT AS-NEEDED. 10 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | This project is maintained by the following people: 4 | 5 | - FIXME 6 | 7 | **_NOTE:_** PROJECTS BUILT USING THE TEMPLATE SHOULD UPDATE THE ABOVE TEXT AS-NEEDED. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for RISC-V Doc Template 2 | # 3 | # This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 4 | # International License. To view a copy of this license, visit 5 | # http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to 6 | # Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 7 | # 8 | # SPDX-License-Identifier: CC-BY-SA-4.0 9 | # 10 | # Description: 11 | # 12 | # This Makefile is designed to automate the process of building and packaging 13 | # the Doc Template for RISC-V Extensions. 14 | 15 | DOCS := \ 16 | riscv-iommu.adoc 17 | 18 | DATE ?= $(shell date +%Y-%m-%d) 19 | VERSION ?= v1.0 20 | REVMARK ?= 'This document is Ratified. See http://riscv.org/spec-state for details.' 21 | DOCKER_IMG := riscvintl/riscv-docs-base-container-image:latest 22 | ifneq ($(SKIP_DOCKER),true) 23 | DOCKER_CMD := docker run --rm -v ${PWD}:/build -w /build \ 24 | ${DOCKER_IMG} \ 25 | /bin/sh -c 26 | DOCKER_QUOTE := " 27 | endif 28 | 29 | SRC_DIR := src 30 | BUILD_DIR := build 31 | 32 | DOCS_PDF := $(DOCS:%.adoc=%.pdf) 33 | DOCS_HTML := $(DOCS:%.adoc=%.html) 34 | 35 | XTRA_ADOC_OPTS := 36 | ASCIIDOCTOR_PDF := asciidoctor-pdf 37 | ASCIIDOCTOR_HTML := asciidoctor 38 | OPTIONS := --trace \ 39 | -a compress \ 40 | -a mathematical-format=svg \ 41 | -a revnumber=${VERSION} \ 42 | -a revremark=${REVMARK} \ 43 | -a revdate=${DATE} \ 44 | -a pdf-fontsdir=docs-resources/fonts \ 45 | -a pdf-theme=docs-resources/themes/riscv-pdf.yml \ 46 | $(XTRA_ADOC_OPTS) \ 47 | -D build \ 48 | --failure-level=ERROR 49 | REQUIRES := --require=asciidoctor-bibtex \ 50 | --require=asciidoctor-diagram \ 51 | --require=asciidoctor-lists \ 52 | --require=asciidoctor-mathematical 53 | 54 | .PHONY: all build clean build-container build-no-container build-docs 55 | 56 | all: build 57 | 58 | build-docs: $(DOCS_PDF) $(DOCS_HTML) 59 | 60 | vpath %.adoc $(SRC_DIR) 61 | 62 | %.pdf: %.adoc 63 | $(DOCKER_CMD) $(DOCKER_QUOTE) $(ASCIIDOCTOR_PDF) $(OPTIONS) $(REQUIRES) $< $(DOCKER_QUOTE) 64 | 65 | %.html: %.adoc 66 | $(DOCKER_CMD) $(DOCKER_QUOTE) $(ASCIIDOCTOR_HTML) $(OPTIONS) $(REQUIRES) $< $(DOCKER_QUOTE) 67 | 68 | build: 69 | @echo "Checking if Docker is available..." 70 | @if command -v docker >/dev/null 2>&1 ; then \ 71 | echo "Docker is available, building inside Docker container..."; \ 72 | $(MAKE) build-container; \ 73 | else \ 74 | echo "Docker is not available, building without Docker..."; \ 75 | $(MAKE) build-no-container; \ 76 | fi 77 | 78 | build-container: 79 | @echo "Starting build inside Docker container..." 80 | $(MAKE) build-docs 81 | @echo "Build completed successfully inside Docker container." 82 | 83 | build-no-container: 84 | @echo "Starting build..." 85 | $(MAKE) SKIP_DOCKER=true build-docs 86 | @echo "Build completed successfully." 87 | 88 | # Update docker image to latest 89 | docker-pull-latest: 90 | docker pull ${DOCKER_IMG} 91 | 92 | clean: 93 | @echo "Cleaning up generated files..." 94 | rm -rf $(BUILD_DIR) 95 | @echo "Cleanup completed." 96 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = RISC-V IOMMU specification 2 | 3 | The specification is Ratified. 4 | 5 | == License 6 | 7 | This work is licensed under a Creative Commons Attribution 4.0 International License (CC-BY-4.0). For details, see the link:LICENSE[LICENSE] file. 8 | 9 | == Maintainers 10 | 11 | The list of maintainers of this specification is maintained in the link:MAINTAINERS.md[MAINTAINERS] file. 12 | 13 | == Contributors 14 | 15 | The list of contributors to this specification is maintained in the link:src/contributors.adoc[contributors] file. 16 | 17 | For guidelines on how to contribute, refer to the link:CONTRIBUTING.md[CONTRIBUTING] file. 18 | 19 | == Governance 20 | 21 | The governance for this project is defined in the link:GOVERNANCE.md[GOVERNANCE] file. 22 | 23 | Community information, including meeting (if held) and mailing lists are detailed in this file. 24 | 25 | == Building the Document 26 | 27 | === Directory Structure 28 | 29 | The following directories are used to organize the contents of this repo: 30 | 31 | * `dependencies/`: software dependencies needed to build the specification 32 | * `docs-resources/`: resources for all specifications sourced from link:.gitmodules[git submodule] 33 | * `src/`: source files for the specification 34 | * `build/`: default directory where the build artifacts are generated 35 | 36 | === Prerequisites 37 | 38 | To build the document, you'll need the following tools installed on your system: 39 | 40 | * Make 41 | * asciiDoctor-pdf, asciidoctor-bibtex, asciidoctor-diagram, and asciidoctor-mathematical 42 | * Docker 43 | 44 | === Cloning the Repository 45 | 46 | ```shell 47 | git clone --recurse-submodules https://github.com/riscv-non-isa/riscv-iommu.git 48 | ``` 49 | 50 | All in one single line: 51 | 52 | ```shell 53 | git clone --recurse-submodules https://github.com/riscv-non-isa/riscv-iommu.git && cd riscv-iommu && git submodule update --init --recursive 54 | 55 | ``` 56 | 57 | === Building the Documentation 58 | 59 | To start the build process, run: 60 | 61 | ```shell 62 | cd ./riscv-iommu && make build 63 | ``` 64 | 65 | The link:Makefile[] script will check the availability of Docker on your system: 66 | 67 | * If Docker is available, the documentation will be built inside a Docker container using the image riscvintl/riscv-docs-base-container-image:latest. This ensures a consistent build environment across different systems. 68 | * If Docker is not available, the documentation will be built directly on your system using the installed tools. 69 | 70 | The documentation is generated from the AsciiDoctor source files in your project. The primary source file is specified by the `HEADER_SOURCE` variable in the Makefile. 71 | 72 | The build process utilizes several options, including theming and font settings, and generates a PDF document as output. 73 | 74 | === Cleaning up 75 | 76 | To clean up the generated files, run: 77 | 78 | ```shell 79 | make clean 80 | ``` 81 | 82 | == Enabling pre-commit checks locally 83 | 84 | The repository has some basic commit checks set up with https://pre-commit.com/[pre-commit] that will be enforced by the GitHub CI. 85 | To ensure these checks are also run in the local repository while making changes the following can be done: 86 | 87 | .Installing pre-commit tool 88 | [source,shell] 89 | ---- 90 | # Do once on your system 91 | pip3 install pre-commit 92 | ---- 93 | 94 | .Installing pre-commit git hook in repo 95 | [source,shell] 96 | ---- 97 | # Do once in local repo 98 | pre-commit install 99 | ---- 100 | 101 | Rather than doing the above `pre-commit install` in every repo that uses it, you can do it https://pre-commit.com/#automatically-enabling-pre-commit-on-repositories[once on your system.] 102 | 103 | When enabling additional checks https://pre-commit.com/#plugins[by editing .pre-commit-config.yaml], it is recommended running the newly added check on all files in the repository. This can be done with the following command: 104 | 105 | .Running all pre-commit hooks on all files 106 | [source,shell] 107 | ---- 108 | pre-commit run --all-files 109 | ---- 110 | -------------------------------------------------------------------------------- /dependencies/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'asciidoctor' 3 | gem 'asciidoctor-bibtex' 4 | gem 'asciidoctor-diagram' 5 | gem 'asciidoctor-mathematical' 6 | gem 'asciidoctor-pdf' 7 | gem 'citeproc-ruby' 8 | gem 'coderay' 9 | gem 'csl-styles' 10 | gem 'json' 11 | gem 'pygments.rb' 12 | gem 'rghost' 13 | gem 'rouge' 14 | gem 'ruby_dev' 15 | -------------------------------------------------------------------------------- /dependencies/README.md: -------------------------------------------------------------------------------- 1 | Dependencies for the build environment for various package managers. Used in 2 | `.github/workflows/`. 3 | -------------------------------------------------------------------------------- /dependencies/apt_packages.txt: -------------------------------------------------------------------------------- 1 | bison 2 | build-essential 3 | cmake 4 | curl 5 | flex 6 | fonts-lyx 7 | git 8 | graphviz 9 | # For wavedrom 10 | default-jre 11 | libcairo2-dev 12 | libffi-dev 13 | libgdk-pixbuf2.0-dev 14 | libpango1.0-dev 15 | libxml2-dev 16 | libwebp-dev 17 | libzstd-dev 18 | make 19 | pkg-config 20 | ruby 21 | ruby-dev 22 | -------------------------------------------------------------------------------- /dependencies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "bytefield-svg": "^1.8.0", 4 | "wavedrom-cli": "^2.6.8" 5 | }, 6 | "name": "local", 7 | "version": "0.0.1" 8 | } 9 | -------------------------------------------------------------------------------- /iommu_ref_model/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /iommu_ref_model/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = libiommu libtables test 2 | .PHONY: run 3 | all: 4 | @for i in $(SUBDIRS); do \ 5 | echo "Building $$i";\ 6 | (cd $$i; $(MAKE) -s --no-print-directory clean);\ 7 | (cd $$i; $(MAKE) -s --no-print-directory); done 8 | clean: 9 | @for i in $(SUBDIRS); do \ 10 | (cd $$i; $(MAKE) -s --no-print-directory clean); done 11 | 12 | run: 13 | ./runtests 14 | -------------------------------------------------------------------------------- /iommu_ref_model/README.md: -------------------------------------------------------------------------------- 1 | # RISC-V IOMMU reference model 2 | 3 | This code implements the RISC-V IOMMU specification - https://github.com/riscv-non-isa/riscv-iommu - and 4 | is intended to be a behavioural reference model for the specification. 5 | 6 | # Files organization 7 | 8 | - libiommu - Files implementing the specification 9 | - libtables - a support library to build page and directory tables 10 | - test - a sample test application illustrating how to invoke and use libiommu and libtables 11 | 12 | # Building and Running tests 13 | 14 | The project builds two libraries libiommu.a and libtables.a. The test application links to both 15 | the libraries and uses the API provided by libtables to build the memory resident tables. 16 | 17 | The test application may be run by invoking `make run`. The test application should print the status 18 | of the tests and a coverage report on the reference model. The coverage report may be used to guide 19 | generation of further tests. 20 | 21 | The reference model, may be used in conjuction with a IOMMU to verify the behavior of the IOMMU. For 22 | example, the tests may provide identical stimulus to the IOMMU reference model and the IOMMU and 23 | compare the results. The debug interface of the IOMMU may be used to provide stimulus when its simpler 24 | not to use real IO devices to generate the stimulus. 25 | 26 | The stock test application generates the following output. 27 | 28 | ```bash 29 | Running IOMMU test suite 30 | Test 01 : All inbound transactions disallowed : PASS 31 | Test 02 : Bare mode tests : PASS 32 | Test 03 : Too wide device_id : PASS 33 | Test 04 : Non-leaf DDTE invalid : PASS 34 | : 35 | : 36 | Test 27 : Illegal commands and CQ mem faults : PASS 37 | Test 28 : Sv32 mode : PASS 38 | Test 29 : Misc. Register Access tests : PASS 39 | 40 | Coverage report 41 | File 'src/iommu_atc.c' :100.00% 42 | File 'src/iommu_ats.c' :100.00% 43 | File 'src/iommu_command_queue.c' :100.00% 44 | : 45 | : 46 | File 'src/iommu_reg.c' :100.00% 47 | File 'src/iommu_s_vs_stage_trans.c' :100.00% 48 | File 'src/iommu_translate.c' :100.00% 49 | File 'src/iommu_utils.c' :100.00% 50 | 51 | ``` 52 | 53 | # Reference model test bench functions 54 | 55 | To drive the reference mode, the model exposes the following APIs that must be 56 | implemented by the test bench: 57 | 58 | 1. **`uint8_t read_memory(uint64_t address, uint8_t size, char *data)`** 59 | 60 | This function is used by the reference model to read memory. The bit 0 of the 61 | return value may be set to 1 to indicate an access violation and the bit 1 set 62 | to 1 to indicate that the data returned is corrupted and thus must cause a data 63 | corruption fault. If neither bits are set then the read is assumed to be 64 | successful and the data bytes placed in the buffer identified by the data 65 | parameter. 66 | 67 | 2. **`uint8_t read_memory_for_AMO(uint64_t address, uint8_t size, char *data)`** 68 | 69 | This API works is used by the reference model to read memory for an atomic memory 70 | operation but is otherwise identical to the read_memory API. 71 | 72 | 3. **`uint8_t write_memory(char *data, uint64_t address, uint32_t size)`** 73 | 74 | This function is used by the reference model to write memory. The bit 0 of the 75 | return value may be set to 1 to indicate an access violation. If the return value is 76 | 0 then the write is assumed to be successful and the data bytes in the buffer 77 | identified by the data parameter are written to the memory identified by the address 78 | parameter. 79 | 80 | 4. **`void iommu_to_hb_do_global_observability_sync(uint8_t PR, uint8_t PW)`** 81 | 82 | This function is invoked by the IOMMU to request the test bench to perform the global observability 83 | actions for previous reads if PR is 1 and for previous writes if PW is 1. When the function returns 84 | the IOMMU model assumes that all previous read and/or write are globally observed as requested. 85 | 86 | 5. **`void send_msg_iommu_to_hb(ats_msg_t *prgr)`** 87 | 88 | This function is invoked by the IOMMU to send a ATS message - Invalidation request or a page request 89 | group response - to the test bench. 90 | 91 | # Reference model functions 92 | 93 | These functions are provided by the reference model to the test bench to input stimulii and 94 | obtain responses from the model. 95 | 96 | 1. **`uint64_t read_register(uint16_t offset, uint8_t num_bytes)`** 97 | 98 | This function is provided by the reference model to read a memory mapped IOMMU 99 | register. The register is identified by the offset parameter and the number of bytes 100 | read is identified by the num_bytes parameter. The register data is returned by the 101 | function. If the access is invalid then the function returns all 1's i.e. an abort 102 | response. 103 | 104 | 2. **`void write_register(uint16_t offset, uint8_t num_bytes, uint64_t data)`** 105 | 106 | This function is provided by the reference model to write a memory mapped IOMMU 107 | register. The register is identified by the offset parameter and the number of bytes 108 | written is identified by the num_bytes parameter. The data to be written is provided 109 | in the data parameter. If the access is invalid then the function drops the write i.e. 110 | an abort response. 111 | 112 | 3. **`int reset_iommu(uint8_t num_hpm, uint8_t hpmctr_bits, uint16_t eventID_mask, uint8_t num_vec_bits, uint8_t reset_iommu_mode, capabilities_t capabilities, fctrl_t fctrl)`** 113 | 114 | This function is provided by the reference model to establish the resset default state. 115 | The num_hpm indicates the number of hardware performace monitoring counters to be 116 | implemented by the model and the hpmctr_bits indicates the width of the counters in bits. 117 | The eventID_mask indicates the width of the eventID field in the hardware performance 118 | monitoring event registers that should be implemented by the reference model. The 119 | num_vec_bits indicates the width in bits of the vector field of the ICVEC register. The 120 | default IOMMU mode - Off or Bare - is selected by the reset_iommu_mode parameter. The 121 | capabilities of the IOMMU that should be implemented are specified by the capabilities 122 | parameter. The default value of the feature control register is provided by the fctrl 123 | parameter. The function returns 0 if the reference model could be successfully initialized 124 | with the provided parameters. 125 | 126 | 4. **`void iommu_translate_iova(hb_to_iommu_req_t *req, iommu_to_hb_rsp_t *rsp_msg)`** 127 | 128 | This function is used by the test bench to invoke the translation request interface in the 129 | IOMMU. The translation response is returned in the buffer pointed to by rsp_msg. 130 | 131 | 5. **`void handle_page_request(ats_msg_t *pr)`** 132 | 133 | This function is used by the test bench to send a page request message to the IOMMU. 134 | 135 | 6. **`uint8_t handle_invalidation_completion(ats_msg_t *inv_cc)`** 136 | 137 | This function is used by the test bench to send a invalidation completion message to the IOMMU. 138 | 139 | 7. **`void do_ats_timer_expiry(uint32_t itag_vector)`** 140 | 141 | This function is used by the test bench to signal a timeout for one or more ATS invalidation 142 | requests sent by the IOMMU. 143 | 144 | 8. **`void process_commands(void)`** 145 | 146 | This function when invoked causes the IOMMU to process a command from the command queue. This 147 | function acts like a "clock" and in each invocation processes one command. If multiple command 148 | processing is required then the function should be invoked for each command. 149 | 150 | 9. **`void get_attribs_from_req(hb_to_iommu_req_t *req, uint8_t *read, uint8_t *write, uint8_t *exec, uint8_t *priv)`** 151 | 152 | This function is used to extract the read, write, execute, and privilege 153 | attributes from the the request. 154 | 155 | # Libtables functions 156 | 157 | The following functions are provided by the libtables to build memory resident data structures. 158 | The functions do not implement extensive error checking and providing bad inputs may lead to bad 159 | tables being created. 160 | 161 | 1. **`uint64_t add_dev_context(device_context_t *DC, uint32_t device_id)`** 162 | 163 | This function is used to build the device directory table by adding non-leaf entries when needed 164 | and inserting the device context in the leaf entry. The function returns the address, in test memory 165 | space, where the leaf entry was inserted. 166 | 167 | 2. **`uint64_t add_process_context(device_context_t *DC, process_context_t *PC, uint32_t process_id)`** 168 | 169 | This function is used to build the process directory table by adding non-leaf entries when needed 170 | and inserting the process context in the leaf entry. The function returns the address, in test memory 171 | space, where the leaf entry was inserted. 172 | 173 | 3. **`uint64_t add_g_stage_pte(iohgatp_t iohgatp, uint64_t gpa, gpte_t gpte, uint8_t add_level)`** 174 | 175 | This function is used to build the G-stage page table by adding non-leaf entries when needed 176 | and inserting the leaf entry at the requested level. The level value of 0 indicates that the entry 177 | should be added at the last level possible for the G-stage page table i.e. a 4K or a NAPOT 64K entry. 178 | Larger mappings may be created, as appropriate for the mode in iohgatp, by specifying higher levels. 179 | The function may invoke the get_ppn function to request pages to build the page table. The function 180 | returns the address, in test memory space, where the leaf entry was inserted. 181 | 182 | 4. **`uint64_t add_s_stage_pte(iosatp_t satp, uint64_t va, pte_t pte, uint8_t add_level)`** 183 | 184 | This function is used to build the S-stage page table by adding non-leaf entries when needed 185 | and inserting the leaf entry at the requested level. The level value of 0 indicates that the entry 186 | should be added at the last level possible for the S-stage page table i.e. a 4K or a NAPOT 64K entry. 187 | Larger mappings may be created, as appropriate for the mode in satp, by specifying higher levels. 188 | The function may invoke the get_ppn function to request pages to build the page table. The function 189 | returns the address, in test memory space, where the leaf entry was inserted. 190 | 191 | 5. **`uint64_t add_vs_stage_pte(iosatp_t satp, uint64_t va, pte_t pte, uint8_t add_level, iohgatp_t iohgatp)`** 192 | 193 | This function is used to build the VS-stage page table by adding non-leaf entries when needed 194 | and inserting the leaf entry at the requested level. The level value of 0 indicates that the entry 195 | should be added at the last level possible for the VS-stage page table i.e. a 4K or a NAPOT 64K entry. 196 | Larger mappings may be created, as appropriate for the mode in satp, by specifying higher levels. 197 | The function may invoke the get_gppn function to request pages to build the page table. The function 198 | maps the obtained gppn into the G-stage page table determined by iohgatp. The function returns the address, 199 | in test memory space, where the leaf entry was inserted. 200 | 201 | 6. **`uint64_t translate_gpa (iohgatp_t iohgatp, uint64_t gpa, uint64_t *spa)`** 202 | 203 | This function is used to translate a gpa to a spa. The function also returns the G-stage pte that provides 204 | the translation as an address in test memory space. 205 | 206 | # Libtables test bench functions 207 | 208 | These functions are invoked by the libtables functions to allocate memory for the table structures. These 209 | are provided by the test bench that invokes the libtables. 210 | 211 | 1. **`uint64_t get_free_ppn(uint64_t num_ppn)`** 212 | 213 | This function is used to allocate a set of pages in the test memory space. The function should provide 214 | a range of pages with the base page aligned to num_ppn. 215 | 216 | 2. **`uint64_t get_free_gppn(uint64_t num_gppn, iohgatp_t iohgatp)`** 217 | 218 | This function is used to allocate a set of pages in the memory space of the guest associated with iohgatp. 219 | The function should provide a range of pages with the base page aligned to num_gppn. 220 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -g -Wall -Werror -Iinclude -fprofile-arcs -ftest-coverage 2 | CC := gcc 3 | NAME := iommu 4 | SRCS = src/iommu_reg.c src/iommu_translate.c src/iommu_faults.c src/iommu_interrupt.c src/iommu_two_stage_trans.c src/iommu_second_stage_trans.c src/iommu_msi_trans.c src/iommu_device_context.c src/iommu_command_queue.c src/iommu_utils.c src/iommu_atc.c src/iommu_process_context.c src/iommu_ats.c src/iommu_hpm.c 5 | OBJS = $(SRCS:.c=.o) 6 | 7 | lib: lib$(NAME).a 8 | 9 | lib$(NAME).a: $(OBJS) 10 | ar rcD $@ $^ > /dev/null 2>&1 11 | ranlib $@ 12 | 13 | clean: 14 | $(RM) src/*.o *.a* tags log src/*.gc* src/*.gcov *.gcov 15 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_H__ 6 | #define __IOMMU_H__ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "iommu_registers.h" 13 | #include "iommu_data_structures.h" 14 | #include "iommu_req_rsp.h" 15 | #include "iommu_fault.h" 16 | #include "iommu_translate.h" 17 | #include "iommu_utils.h" 18 | #include "iommu_interrupt.h" 19 | #include "iommu_command_queue.h" 20 | #include "iommu_ats.h" 21 | #include "iommu_atc.h" 22 | #include "iommu_hpm.h" 23 | #include "iommu_ref_api.h" 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_atc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_ATC_H__ 6 | #define __IOMMU_ATC_H__ 7 | // Contents of this file are not architectural 8 | // IOMMU TLB 9 | typedef struct { 10 | // Tags 11 | uint64_t vpn; 12 | uint8_t GV; 13 | uint8_t PSCV; 14 | uint32_t GSCID; 15 | uint32_t PSCID; 16 | // Attributes from VS stage page tables 17 | uint8_t VS_R; 18 | uint8_t VS_W; 19 | uint8_t VS_X; 20 | uint8_t PBMT; 21 | uint8_t G; 22 | uint8_t U; 23 | uint8_t VS_D; 24 | // Attributes from G stage page tables 25 | uint8_t G_R; 26 | uint8_t G_W; 27 | uint8_t G_X; 28 | uint8_t G_D; 29 | // PPN and size 30 | uint64_t PPN; 31 | uint8_t S; 32 | uint32_t lru; 33 | uint8_t valid; 34 | // Whether is an MSI translation 35 | uint8_t IS_MSI; 36 | } tlb_t; 37 | // Device directory cache 38 | typedef struct { 39 | device_context_t DC; 40 | uint32_t DID; 41 | uint32_t lru; 42 | uint8_t valid; 43 | } ddt_cache_t; 44 | // Process directory cache 45 | typedef struct { 46 | process_context_t PC; 47 | uint32_t DID; 48 | uint32_t PID; 49 | uint32_t lru; 50 | uint8_t valid; 51 | } pdt_cache_t; 52 | 53 | #define DDT_CACHE_SIZE 2 54 | #define PDT_CACHE_SIZE 2 55 | #define TLB_SIZE 2 56 | 57 | #define IOATC_MISS 0 58 | #define IOATC_HIT 1 59 | #define IOATC_FAULT 2 60 | 61 | extern ddt_cache_t ddt_cache[DDT_CACHE_SIZE]; 62 | extern pdt_cache_t pdt_cache[PDT_CACHE_SIZE]; 63 | extern tlb_t tlb[TLB_SIZE]; 64 | extern void 65 | cache_ioatc_iotlb( 66 | uint64_t vpn, uint8_t GV, uint8_t PSCV, uint32_t GSCID, uint32_t PSCID, 67 | pte_t *vs_pte, gpte_t *g_pte, uint64_t PPN, uint8_t S, uint8_t is_msi); 68 | 69 | extern uint8_t 70 | lookup_ioatc_iotlb( 71 | uint64_t iova, uint8_t check_access_perms, 72 | uint8_t priv, uint8_t is_read, uint8_t is_write, uint8_t is_exec, 73 | uint8_t SUM, uint8_t PSCV, uint32_t PSCID, uint8_t GV, uint16_t GSCID, 74 | uint32_t *cause, uint64_t *resp_pa, uint64_t *page_sz, 75 | pte_t *vs_pte, gpte_t *g_pteu, uint8_t *is_msi); 76 | 77 | extern uint8_t 78 | lookup_ioatc_dc(uint32_t device_id, device_context_t *DC); 79 | 80 | extern void 81 | cache_ioatc_dc(uint32_t device_id, device_context_t *DC); 82 | 83 | extern uint8_t 84 | lookup_ioatc_pc(uint32_t device_id, uint32_t process_id, process_context_t *PC); 85 | 86 | extern void 87 | cache_ioatc_pc(uint32_t device_id, uint32_t process_id, process_context_t *PC); 88 | 89 | #endif // __IOMMU_ATC_H__ 90 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_ats.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_ATS_H__ 6 | #define __IOMMU_ATS_H__ 7 | typedef union { 8 | struct { 9 | uint64_t reserved0:12; 10 | uint64_t PID:20; 11 | uint64_t PV:1; 12 | uint64_t PRIV:1; 13 | uint64_t EXEC:1; 14 | uint64_t reserved1:5; 15 | uint64_t DID:24; 16 | uint64_t PAYLOAD; 17 | }; 18 | uint64_t raw[2]; 19 | } page_rec_t; 20 | 21 | #define PQ_ENTRY_SZ sizeof(page_rec_t) 22 | 23 | // IOMMU generated notifications (invalidation requests and 24 | // page group responses) 25 | // IOMMU response to requests from the IO bridge 26 | // Message Code Routing r[2:0] Type Description 27 | // 00000001 010 MsgD Invalidate Request Message, see § Section 10.3.1 28 | // 00000010 010 Msg Invalidate Completion Message, see § Section 10.3.2 29 | // 00000100 000 Msg Page Request Message, see § Section 10.4.1 30 | // 00000101 010 Msg PRG Response Message, see § Section 10.4.2 31 | #define INVAL_REQ_MSG_CODE 0x01 32 | #define INVAL_COMPL_MSG_CODE 0x02 33 | #define PAGE_REQ_MSG_CODE 0x04 34 | #define PRGR_MSG_CODE 0x05 35 | typedef struct { 36 | uint8_t MSGCODE; 37 | uint8_t TAG; 38 | uint32_t RID; 39 | uint8_t PV; 40 | uint32_t PID; 41 | uint8_t PRIV; 42 | uint8_t EXEC_REQ; 43 | uint8_t DSV; 44 | uint8_t DSEG; 45 | uint64_t PAYLOAD; 46 | } ats_msg_t; 47 | 48 | // Page Request Group Response - response field encoding 49 | //Value |Status |Meaning 50 | //-----------+---------+-------------------------------------------------------------------- 51 | //0000b |Success |All pages within the associated PRG were successfully made resident. 52 | //-----------+---------+-------------------------------------------------------------------- 53 | //0001b |Invalid |One or more pages within the associated PRG do not exist or requests 54 | // |Request |access privilege(s) that cannot be granted. Unless the page mapping 55 | // | |associated with the Function is altered, re-issuance of the associated 56 | // | |request will never result in success. 57 | //-----------+---------+-------------------------------------------------------------------- 58 | //1110b:0010b|Unused |Unused Response Code values. A Function receiving such a message shall 59 | // | |process it as if the message contained a Response Code of 60 | // | |Response Failure. 61 | //-----------+---------+-------------------------------------------------------------------- 62 | //1111b | Response|One or more pages within the associated request group have 63 | // | Failure |encountered/caused a catastrophic error. This response disables the 64 | // | |Page Request Interface at the Function. Any pending page requests for 65 | // | |other PRGs will be satisfied at the convenience of the host. The 66 | // | |Function shall ignore any subsequent PRG Response Messages, pending 67 | // | |re-enablement of the Page Request Interface. 68 | //-----------+---------+-------------------------------------------------------------------- 69 | #define PRGR_SUCCESS 0x0UL 70 | #define PRGR_INVALID_REQUEST 0x1UL 71 | #define PRGR_RESPONSE_FAILURE 0xFUL 72 | 73 | typedef struct { 74 | uint8_t busy; 75 | uint8_t DSV; 76 | uint8_t DSEG; 77 | uint16_t RID; 78 | uint8_t num_rsp_rcvd; 79 | } itag_tracker_t; 80 | 81 | #define MAX_ITAGS 2 82 | extern uint8_t allocate_itag(uint8_t DSV, uint8_t DSEG, uint16_t RID, uint8_t *itag); 83 | extern void send_msg_iommu_to_hb(ats_msg_t *msg); 84 | extern uint8_t any_ats_invalidation_requests_pending(void); 85 | #endif //__IOMMU_ATS_H__ 86 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_command_queue.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_COMMAND_QUEUE_H__ 6 | #define __IOMMU_COMMAND_QUEUE_H__ 7 | #define IOTINVAL 1 8 | #define IOFENCE 2 9 | #define IODIR 3 10 | #define ATS 4 11 | 12 | #define VMA 0 13 | #define GVMA 1 14 | 15 | #define INVAL_DDT 0 16 | #define INVAL_PDT 1 17 | 18 | #define IOFENCE_C 0 19 | 20 | #define INVAL 0 21 | #define PRGR 1 22 | typedef union { 23 | struct { 24 | uint64_t opcode:7; 25 | uint64_t func3:3; 26 | uint64_t av:1; 27 | uint64_t rsvd:1; 28 | uint64_t pscid:20; 29 | 30 | uint64_t pscv:1; 31 | uint64_t gv:1; 32 | uint64_t rsvd1:10; 33 | uint64_t gscid:16; 34 | uint64_t rsvd2:4; 35 | 36 | uint64_t rsvd3:10; 37 | uint64_t addr_63_12:52; 38 | uint64_t rsvd4:2; 39 | } iotinval; 40 | struct { 41 | uint64_t opcode:7; 42 | uint64_t func3:3; 43 | uint64_t av:1; 44 | uint64_t wsi:1; 45 | uint64_t pr:1; 46 | uint64_t pw:1; 47 | uint64_t reserved:18; 48 | 49 | uint64_t data:32; 50 | 51 | uint64_t addr_63_2:62; 52 | uint64_t reserved1:2; 53 | } iofence; 54 | struct { 55 | uint64_t opcode:7; 56 | uint64_t func3:3; 57 | uint64_t rsvd:2; 58 | uint64_t pid:20; 59 | 60 | uint64_t rsvd1:1; 61 | uint64_t dv:1; 62 | uint64_t rsvd2:6; 63 | uint64_t did:24; 64 | 65 | uint64_t rsvd3; 66 | } iodir; 67 | struct { 68 | uint64_t opcode:7; 69 | uint64_t func3:3; 70 | uint64_t rsvd:2; 71 | uint64_t pid:20; 72 | 73 | uint64_t pv:1; 74 | uint64_t dsv:1; 75 | uint64_t rsvd1:6; 76 | uint64_t rid:16; 77 | uint64_t dseg:8; 78 | 79 | uint64_t payload; 80 | } ats; 81 | struct { 82 | uint64_t opcode:7; 83 | uint64_t func3:3; 84 | uint64_t low:54; 85 | uint64_t high:64; 86 | } any; 87 | struct { 88 | uint64_t low; 89 | uint64_t high; 90 | }; 91 | } command_t; 92 | 93 | #define CQ_ENTRY_SZ sizeof(command_t) 94 | 95 | void do_inval_ddt(uint8_t DV, uint32_t DID); 96 | void do_inval_pdt(uint32_t DID, uint32_t PID); 97 | void do_iotinval_vma(uint8_t GV, uint8_t AV, uint8_t PSCV, uint32_t GSCID, uint32_t PSCID, uint64_t ADDR); 98 | void do_iotinval_gvma(uint8_t GV, uint8_t AV, uint32_t GSCID, uint64_t ADDR); 99 | void do_ats_msg( uint8_t MSGCODE, uint8_t TAG, uint8_t DSV, uint8_t DSEG, uint16_t RID, 100 | uint8_t PV, uint32_t PID, uint64_t PAYLOAD); 101 | uint8_t do_iofence_c(uint8_t PR, uint8_t PW, uint8_t AV, uint8_t WIS_BIT, uint64_t ADDR, uint32_t DATA); 102 | void do_pending_iofence(); 103 | void queue_any_blocked_ats_inval_req(); 104 | extern uint8_t g_ats_inv_req_timeout; 105 | #endif // __IOMMU_COMMAND_QUEUE_H__ 106 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_fault.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_FAULT_H__ 6 | #define __IOMMU_FAULT_H__ 7 | 8 | // The TTYP field reports inbound transaction type 9 | // Fault record `TTYP` field encodings 10 | // |TTYP | Description 11 | // |0 | None. Fault not caused by an inbound transaction. 12 | // |1 | Untranslated read for execute transaction 13 | // |2 | Untranslated read transaction 14 | // |3 | Untranslated write/AMO transaction 15 | // |4 | Reserved 16 | // |5 | Translated read for execute transaction 17 | // |6 | Translated read transaction 18 | // |7 | Translated write/AMO transaction 19 | // |8 | PCIe ATS Translation Request 20 | // |9 | Message Request 21 | // |10 - 31| Reserved 22 | // |31 - 63| Reserved for custom use 23 | #define TTYPE_NONE 0 24 | #define UNTRANSLATED_READ_FOR_EXECUTE_TRANSACTION 1 25 | #define UNTRANSLATED_READ_TRANSACTION 2 26 | #define UNTRANSLATED_WRITE_AMO_TRANSACTION 3 27 | #define TRANSLATED_READ_FOR_EXECUTE_TRANSACTION 5 28 | #define TRANSLATED_READ_TRANSACTION 6 29 | #define TRANSLATED_WRITE_AMO_TRANSACTION 7 30 | #define PCIE_ATS_TRANSLATION_REQUEST 8 31 | #define PCIE_MESSAGE_REQUEST 9 32 | 33 | 34 | // Fault-queue record 35 | // bits: 11:0: 'CAUSE' 36 | // bits: 31:12: 'PID' 37 | // bits: 32: 'PV' 38 | // bits: 33: 'PRIV' 39 | // bits: 39:34: 'TTYP' 40 | // bits: 63:40: 'DID' 41 | // bits: 95:64: 'for custom use' 42 | // bits: 127:96: 'reserved' 43 | // bits: 191:128: 'iotval' 44 | // bits: 255:192: 'iotval2' 45 | typedef union { 46 | struct { 47 | uint64_t CAUSE:12; 48 | uint64_t PID:20; 49 | uint64_t PV:1; 50 | uint64_t PRIV:1; 51 | uint64_t TTYP:6; 52 | uint64_t DID:24; 53 | uint32_t custom; 54 | uint32_t reserved; 55 | uint64_t iotval; 56 | uint64_t iotval2; 57 | }; 58 | uint64_t raw[4]; 59 | } fault_rec_t; 60 | #define ACCESS_FAULT 0x01 61 | #define DATA_CORRUPTION 0x02 62 | 63 | #define GST_PAGE_FAULT 0x21 64 | #define GST_ACCESS_FAULT 0x22 65 | #define GST_DATA_CORRUPTION 0x23 66 | 67 | #define FQ_ENTRY_SZ sizeof(fault_rec_t) 68 | 69 | extern void report_fault(uint16_t cause, uint64_t iotval, uint64_t iotval2, uint8_t TTYP, uint8_t dtf, 70 | uint32_t device_id, uint8_t pid_valid, uint32_t process_id, uint8_t priv_req); 71 | #endif // __IOMMU_FAULT_H__ 72 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_hpm.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_PMU_H__ 6 | #define __IOMMU_PMU_H__ 7 | // The following table lists the standard events that can be counted: 8 | // | *eventID* | *Event counted* | *IDT settings supported* 9 | // | 0 | Do not count | 10 | // | 1 | Untranslated requests | 0 11 | // | 2 | Translated requests | 0 12 | // | 3 | ATS Translation requests | 0 13 | // | 4 | TLB miss | 0/1 14 | // | 5 | Device Directory Walks | 0 15 | // | 6 | Process Directory Walks | 0 16 | // | 7 | S/VS-stage Page Table Walks | 0/1 17 | // | 8 | G-stage Page Table Walks | 0/1 18 | // | 9 - 16383 | reserved for future standard | - 19 | #define NO_EVENT 0 20 | #define UNTRANSLATED_REQUEST 1 21 | #define TRANSLATED_REQUEST 2 22 | #define TRANSLATION_REQUEST 3 23 | #define IOATC_TLB_MISS 4 24 | #define DDT_WALKS 5 25 | #define PDT_WALKS 6 26 | #define S_VS_PT_WALKS 7 27 | #define G_PT_WALKS 8 28 | 29 | void count_events(uint8_t PV, uint32_t PID, uint8_t PSCV, uint32_t PSCID, 30 | uint32_t DID, uint8_t GSCV, uint32_t GSCID, uint16_t eventID); 31 | #endif // __IOMMU_PMU_H__ 32 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_interrupt.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_INTERRUPT_H__ 6 | #define __IOMMU_INTERRUPT_H__ 7 | 8 | #define COMMAND_QUEUE 0 9 | #define FAULT_QUEUE 1 10 | #define HPM 2 11 | #define PAGE_QUEUE 3 12 | 13 | #define MSI_VEC_CTRL_MASK_BIT 1 14 | 15 | extern void generate_interrupt(uint8_t unit); 16 | extern void release_pending_interrupt(uint8_t vec); 17 | #endif // __IOMMU_INTERRUPT_H__ 18 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_ref_api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_REF_API_H__ 6 | #define __IOMMU_REF_API_H__ 7 | 8 | extern uint8_t read_memory(uint64_t addr, uint8_t size, char *data, 9 | uint32_t rcid, uint32_t mcid); 10 | extern uint8_t read_memory_for_AMO(uint64_t address, uint8_t size, char *data, 11 | uint32_t rcid, uint32_t mcid); 12 | extern uint8_t write_memory(char *data, uint64_t address, uint32_t size, 13 | uint32_t rcid, uint32_t mcid); 14 | extern uint8_t read_memory_test(uint64_t addr, uint8_t size, char *data); 15 | extern uint8_t write_memory_test(char *data, uint64_t address, uint32_t size); 16 | 17 | extern uint64_t read_register(uint16_t offset, uint8_t num_bytes); 18 | extern void write_register(uint16_t offset, uint8_t num_bytes, uint64_t data); 19 | 20 | #define FILL_IOATC_ATS_T2GPA 0x01 21 | #define FILL_IOATC_ATS_ALWAYS 0x02 22 | extern int reset_iommu(uint8_t num_hpm, uint8_t hpmctr_bits, uint16_t eventID_mask, 23 | uint8_t num_vec_bits, uint8_t reset_iommu_mode, 24 | uint8_t max_iommu_mode, uint32_t max_devid_mask, 25 | uint8_t gxl_writeable, uint8_t fctl_be_writeable, 26 | uint8_t fill_ats_trans_in_ioatc, capabilities_t capabilities, 27 | fctl_t fctl, uint64_t sv57_bare_pg_sz, uint64_t sv48_bare_pg_sz, 28 | uint64_t sv39_bare_pg_sz, uint64_t sv32_bare_pg_sz); 29 | extern void iommu_translate_iova(hb_to_iommu_req_t *req, iommu_to_hb_rsp_t *rsp_msg); 30 | extern void handle_page_request(ats_msg_t *pr); 31 | extern uint8_t handle_invalidation_completion(ats_msg_t *inv_cc); 32 | extern void do_ats_timer_expiry(uint32_t itag_vector); 33 | extern void process_commands(void); 34 | 35 | extern void iommu_to_hb_do_global_observability_sync(uint8_t PR, uint8_t PW); 36 | extern void send_msg_iommu_to_hb(ats_msg_t *prgr); 37 | extern void get_attribs_from_req(hb_to_iommu_req_t *req, uint8_t *read, 38 | uint8_t *write, uint8_t *exec, uint8_t *priv); 39 | 40 | #endif // __IOMMU_REF_API_H__ 41 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_req_rsp.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_REQ_RSP_H__ 6 | #define __IOMMU_REQ_RSP_H__ 7 | 8 | typedef enum { 9 | // 00 - Untranslated - IOMMU may treat the address as either virtual or physical. 10 | // 01 - Trans. Req. - The IOMMU will return the translation of the address 11 | // contained in the address field of the request as a read 12 | // completion. 13 | // 10 - Translated - The address in the transaction has been translated by an IOMMU. 14 | // If the Function associated with the device_id is allowed to 15 | // present physical addresses to the system memory, then the IOMMU 16 | // might not translate this address. If the Function is not allowed 17 | // to present physical addresses, then the TA may treat this as an UR. 18 | ADDR_TYPE_UNTRANSLATED = 0, 19 | ADDR_TYPE_PCIE_ATS_TRANSLATION_REQUEST = 1, 20 | ADDR_TYPE_TRANSLATED = 2 21 | } addr_type_t; 22 | #define READ 0 23 | #define WRITE 1 24 | typedef struct { 25 | addr_type_t at; 26 | uint64_t iova; 27 | uint32_t length; 28 | uint8_t read_writeAMO; 29 | } iommu_trans_req_t; 30 | 31 | // Request to IOMMU from the host bridge 32 | typedef struct { 33 | // Device ID input 34 | uint32_t device_id; 35 | // Process ID input (e.g. PASID present) 36 | uint8_t pid_valid; 37 | uint32_t process_id; 38 | uint8_t no_write; 39 | uint8_t exec_req; 40 | uint8_t priv_req; 41 | uint8_t is_cxl_dev; 42 | // Translation request 43 | iommu_trans_req_t tr; 44 | } hb_to_iommu_req_t; 45 | 46 | // Translation completion status 47 | typedef enum { 48 | // This Completion Status has a nominal meaning of “success”. 49 | SUCCESS = 0, 50 | 51 | // A status that applies to a posted or non-posted Request 52 | // that specifies some action or access to some space that 53 | // is not supported by the Completer. 54 | // OR 55 | // A status indication returned with a Completion for a 56 | // non-posted Request that suffered an Unsupported Request 57 | // at the Completer. 58 | UNSUPPORTED_REQUEST = 1, 59 | 60 | // A status that applies to a posted or non-posted Request 61 | // that the Completer is permanently unable to complete 62 | // successfully, due to a violation of the Completer’s 63 | // programming model or to an unrecoverable error associated 64 | // with the Completer. 65 | // OR 66 | // A status indication returned with a Completion for a 67 | // non-posted Request that suffered a Completer Abort at the 68 | // Completer. 69 | COMPLETER_ABORT = 4 70 | } status_t; 71 | 72 | // Translation response from iommu to host bridge 73 | typedef struct { 74 | uint64_t PPN; 75 | uint8_t S; 76 | uint8_t N; 77 | uint8_t CXL_IO; 78 | uint8_t Global; 79 | uint8_t Priv; 80 | uint8_t U; 81 | uint8_t R; 82 | uint8_t W; 83 | uint8_t Exe; 84 | uint8_t AMA; 85 | uint8_t PBMT; 86 | uint8_t is_msi; 87 | uint8_t is_mrif; 88 | uint64_t dest_mrif_addr; 89 | uint32_t mrif_nid; 90 | } iommu_trans_rsp_t; 91 | 92 | // IOMMU response to requests from the IO bridge 93 | typedef struct { 94 | status_t status; 95 | iommu_trans_rsp_t trsp; 96 | } iommu_to_hb_rsp_t; 97 | 98 | #endif // __IOMMU_REQ_RSP_H__ 99 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_translate.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_TRANSLATE_H__ 6 | #define __IOMMU_TRANSLATE_H__ 7 | 8 | #define PAGESIZE 4096UL 9 | #define U_MODE 0 10 | #define S_MODE 1 11 | #define PMA 0 12 | #define NC 1 13 | #define IO 2 14 | typedef union { 15 | struct { 16 | uint64_t V:1; 17 | uint64_t R:1; 18 | uint64_t W:1; 19 | uint64_t X:1; 20 | uint64_t U:1; 21 | uint64_t G:1; 22 | uint64_t A:1; 23 | uint64_t D:1; 24 | uint64_t RSW:2; 25 | uint64_t PPN:44; 26 | uint64_t reserved:7; 27 | uint64_t PBMT:2; 28 | uint64_t N:1; 29 | }; 30 | uint64_t raw; 31 | } pte_t; 32 | typedef union { 33 | struct { 34 | uint64_t V:1; 35 | uint64_t R:1; 36 | uint64_t W:1; 37 | uint64_t X:1; 38 | uint64_t U:1; 39 | uint64_t G:1; 40 | uint64_t A:1; 41 | uint64_t D:1; 42 | uint64_t RSW:2; 43 | uint64_t PPN:44; 44 | uint64_t reserved:7; 45 | uint64_t PBMT:2; 46 | uint64_t N:1; 47 | }; 48 | uint64_t raw; 49 | } gpte_t; 50 | typedef union { 51 | struct { 52 | uint64_t V:1; 53 | uint64_t M:2; 54 | uint64_t other:60; 55 | uint64_t C:1; 56 | uint64_t upperQW:64; 57 | }; 58 | struct { 59 | uint64_t V:1; 60 | uint64_t M:2; 61 | uint64_t reserved0:7; 62 | uint64_t PPN:44; 63 | uint64_t reserved:9; 64 | uint64_t C:1; 65 | uint64_t ignored; 66 | } translate_rw; 67 | struct { 68 | uint64_t V:1; 69 | uint64_t M:2; 70 | uint64_t reserved1:4; 71 | uint64_t MRIF_ADDR_55_9:47; 72 | uint64_t reserved2:9; 73 | uint64_t C:1; 74 | uint64_t N90:10; 75 | uint64_t NPPN:44; 76 | uint64_t reserved3:6; 77 | uint64_t N10:1; 78 | uint64_t reserved4:3; 79 | } mrif; 80 | uint64_t raw[2]; 81 | } msipte_t; 82 | 83 | 84 | extern uint8_t 85 | locate_device_context(device_context_t *DC, uint32_t device_id, uint8_t pid_valid, 86 | uint32_t process_id, uint32_t *cause); 87 | 88 | extern uint8_t 89 | locate_process_context(process_context_t *PC, device_context_t *DC, 90 | uint32_t device_id, uint32_t process_id, uint32_t *cause, 91 | uint64_t *iotval2, uint8_t TTYP); 92 | 93 | extern uint8_t 94 | two_stage_address_translation( 95 | uint64_t iova, uint8_t TTYP, uint32_t DID, uint8_t is_read, 96 | uint8_t is_write, uint8_t is_exec, 97 | uint8_t PV, uint32_t PID, uint8_t PSCV, uint32_t PSCID, 98 | iosatp_t iosatp, uint8_t priv, uint8_t SUM, uint8_t SADE, 99 | uint8_t GV, uint32_t GSCID, iohgatp_t iohgatp, uint8_t GADE, uint8_t SXL, 100 | uint32_t *cause, uint64_t *iotval2, uint64_t *pa, 101 | uint64_t *page_sz, pte_t *vs_pte, uint32_t rcid, uint32_t mcid); 102 | 103 | extern uint8_t 104 | second_stage_address_translation( 105 | uint64_t gpa, uint8_t check_access_perms, uint32_t DID, 106 | uint8_t is_read, uint8_t is_write, uint8_t is_exec, uint8_t is_implicit, 107 | uint8_t PV, uint32_t PID, uint8_t PSCV, uint32_t PSCID, 108 | uint8_t GV, uint32_t GSCID, iohgatp_t iohgatp, uint8_t GADE, uint8_t SADE, uint8_t SXL, 109 | uint64_t *pa, uint64_t *gst_page_sz, gpte_t *gpte, uint32_t rcid, uint32_t mcid); 110 | 111 | extern uint8_t 112 | msi_address_translation( 113 | uint64_t gpa, uint8_t is_exec, device_context_t *DC, 114 | uint8_t *is_msi, uint8_t *is_mrif, uint32_t *mrif_nid, uint64_t *dest_mrif_addr, 115 | uint32_t *cause, uint64_t *iotval2, uint64_t *pa, 116 | uint64_t *page_sz, gpte_t *g_pte, uint8_t check_access_perms, uint32_t rcid, 117 | uint32_t mcid); 118 | 119 | #endif // __IOMMU_TRANSLATE_H__ 120 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/include/iommu_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #ifndef __IOMMU_UTILS_H__ 6 | #define __IOMMU_UTILS_H__ 7 | #define get_bits(__MS_BIT, __LS_BIT, __FIELD)\ 8 | ((__FIELD >> __LS_BIT) & (((uint64_t)1 << (((__MS_BIT - __LS_BIT) + 1))) - 1)) 9 | extern uint8_t match_address_range( uint64_t ADDR, uint64_t PPN, uint8_t S); 10 | #endif // __IOMMU_UTILS_H__ 11 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_atc.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | ddt_cache_t ddt_cache[DDT_CACHE_SIZE]; 7 | pdt_cache_t pdt_cache[2]; 8 | tlb_t tlb[2]; 9 | uint32_t dc_lru_time = 0; 10 | uint32_t pc_lru_time = 0; 11 | uint32_t tlb_lru_time = 0; 12 | 13 | // Cache a device context 14 | void 15 | cache_ioatc_dc( 16 | uint32_t device_id, device_context_t *DC) { 17 | uint8_t i, replace; 18 | uint32_t lru = 0xFFFFFFFF; 19 | 20 | for ( i = 0; i < DDT_CACHE_SIZE; i++ ) { 21 | if ( ddt_cache[i].valid == 0 ) { 22 | replace = i; 23 | break; 24 | } 25 | } 26 | if ( i == DDT_CACHE_SIZE ) { 27 | for ( i = 0; i < DDT_CACHE_SIZE; i++ ) { 28 | if ( ddt_cache[i].lru < lru ) { 29 | replace = i; 30 | lru = ddt_cache[i].lru; 31 | } 32 | } 33 | } 34 | ddt_cache[replace].DC = *DC; 35 | ddt_cache[replace].DID = device_id; 36 | ddt_cache[replace].valid = 1; 37 | return; 38 | } 39 | 40 | // Lookup IOATC for a device context 41 | uint8_t 42 | lookup_ioatc_dc( 43 | uint32_t device_id, device_context_t *DC) { 44 | uint8_t i; 45 | for ( i = 0; i < DDT_CACHE_SIZE; i++ ) { 46 | if ( ddt_cache[i].valid == 1 && ddt_cache[i].DID == device_id ) { 47 | *DC = ddt_cache[i].DC; 48 | ddt_cache[i].lru = dc_lru_time++; 49 | return IOATC_HIT; 50 | } 51 | } 52 | return IOATC_MISS; 53 | } 54 | // Cache a process context 55 | void 56 | cache_ioatc_pc( 57 | uint32_t device_id, uint32_t process_id, process_context_t *PC) { 58 | uint8_t i, replace = 0; 59 | uint32_t lru = 0xFFFFFFFF; 60 | 61 | for ( i = 0; i < PDT_CACHE_SIZE; i++ ) { 62 | if ( pdt_cache[i].valid == 0 ) { 63 | replace = i; 64 | break; 65 | } 66 | } 67 | if ( i == PDT_CACHE_SIZE ) { 68 | for ( i = 0; i < PDT_CACHE_SIZE; i++ ) { 69 | if ( pdt_cache[i].lru < lru ) { 70 | replace = i; 71 | lru = pdt_cache[i].lru; 72 | } 73 | } 74 | } 75 | pdt_cache[replace].PC = *PC; 76 | pdt_cache[replace].DID = device_id; 77 | pdt_cache[replace].PID = process_id; 78 | pdt_cache[replace].valid = 1; 79 | return; 80 | } 81 | // Lookup IOATC for a process context 82 | uint8_t 83 | lookup_ioatc_pc( 84 | uint32_t device_id, uint32_t process_id, process_context_t *PC) { 85 | uint8_t i; 86 | for ( i = 0; i < PDT_CACHE_SIZE; i++ ) { 87 | if ( pdt_cache[i].valid == 1 && 88 | pdt_cache[i].DID == device_id && 89 | pdt_cache[i].PID == process_id ) { 90 | *PC = pdt_cache[i].PC; 91 | pdt_cache[i].lru = pc_lru_time++; 92 | return 1; 93 | } 94 | } 95 | return 0; 96 | } 97 | // Cache a translation in the IOATC 98 | void 99 | cache_ioatc_iotlb( 100 | uint64_t vpn, uint8_t GV, uint8_t PSCV, uint32_t GSCID, uint32_t PSCID, 101 | pte_t *vs_pte, gpte_t *g_pte, uint64_t PPN, uint8_t S, uint8_t IS_MSI) { 102 | 103 | uint8_t i, replace = 0; 104 | uint32_t lru = 0xFFFFFFFF; 105 | 106 | for ( i = 0; i < TLB_SIZE; i++ ) { 107 | if ( tlb[i].valid == 0 ) { 108 | replace = i; 109 | break; 110 | } 111 | } 112 | if ( i == TLB_SIZE ) { 113 | for ( i = 0; i < TLB_SIZE; i++ ) { 114 | if ( tlb[i].lru < lru ) { 115 | replace = i; 116 | lru = tlb[i].lru; 117 | } 118 | } 119 | } 120 | 121 | // Fill the tags 122 | tlb[replace].vpn = vpn; 123 | tlb[replace].GV = GV; 124 | tlb[replace].PSCV = PSCV; 125 | tlb[replace].GSCID = GSCID; 126 | tlb[replace].PSCID = PSCID; 127 | // Fill VS stage attributes 128 | tlb[replace].VS_R = vs_pte->R; 129 | tlb[replace].VS_W = vs_pte->W; 130 | tlb[replace].VS_X = vs_pte->X; 131 | tlb[replace].VS_D = vs_pte->D; 132 | tlb[replace].U = vs_pte->U; 133 | tlb[replace].G = vs_pte->G; 134 | tlb[replace].PBMT = vs_pte->PBMT; 135 | // Fill G stage attributes 136 | tlb[replace].G_R = g_pte->R; 137 | tlb[replace].G_W = g_pte->W; 138 | tlb[replace].G_X = g_pte->X; 139 | tlb[replace].G_D = g_pte->D; 140 | // PPN and size 141 | tlb[replace].PPN = PPN; 142 | tlb[replace].S = S; 143 | // Whether MSI 144 | tlb[replace].IS_MSI = IS_MSI; 145 | 146 | tlb[replace].valid = 1; 147 | return; 148 | } 149 | 150 | // Lookup a translation in the IOATC 151 | uint8_t 152 | lookup_ioatc_iotlb( 153 | uint64_t iova, uint8_t check_access_perms, 154 | uint8_t priv, uint8_t is_read, uint8_t is_write, uint8_t is_exec, 155 | uint8_t SUM, uint8_t PSCV, uint32_t PSCID, uint8_t GV, uint16_t GSCID, 156 | uint32_t *cause, uint64_t *resp_pa, uint64_t *page_sz, 157 | pte_t *vs_pte, gpte_t *g_pte, uint8_t *is_msi) { 158 | 159 | uint8_t i, hit; 160 | uint64_t vpn = iova / PAGESIZE; 161 | 162 | hit = 0xFF; 163 | for ( i = 0; i < TLB_SIZE; i++ ) { 164 | if ( tlb[i].valid == 1 && 165 | tlb[i].GV == GV && tlb[i].GSCID == GSCID && 166 | tlb[i].PSCV == PSCV && tlb[i].PSCID == PSCID && 167 | match_address_range(vpn, tlb[i].vpn, tlb[i].S) ) { 168 | hit = i; 169 | break; 170 | } 171 | } 172 | if ( hit == 0xFF ) return IOATC_MISS; 173 | 174 | // Age the entries 175 | tlb[i].lru = tlb_lru_time++; 176 | 177 | // Check S/VS stage permissions 178 | if ( check_access_perms == 1 ) { 179 | if ( is_exec && (tlb[hit].VS_X == 0) ) goto page_fault; 180 | if ( is_read && (tlb[hit].VS_R == 0) ) goto page_fault; 181 | if ( is_write && (tlb[hit].VS_W == 0) ) goto page_fault; 182 | } 183 | if ( (priv == U_MODE) && (tlb[hit].U == 0) ) goto page_fault; 184 | if ( is_exec && (priv == S_MODE) && (tlb[hit].U == 1) ) goto page_fault; 185 | if ( (priv == S_MODE) && !is_exec && SUM == 0 && tlb[hit].U == 1 ) goto page_fault; 186 | 187 | // Check G stage permissions 188 | if ( (is_exec && (tlb[hit].G_X == 0)) || 189 | (is_read && (tlb[hit].G_R == 0)) || 190 | (is_write && (tlb[hit].G_W == 0)) ) { 191 | // More commonly, implementations contain address-translation caches that 192 | // map guest virtual addresses directly to supervisor physical addresses, 193 | // removing a level of indirection. 194 | // If a G-stage permission fault is detected then such caches may not have 195 | // GPA to report in the iotval2. A common technique is to treat it as a 196 | // TLB miss and trigger a page walk such that the GPA can be reported if 197 | // the fault is actually detected again by the G-stage page tables 198 | tlb[hit].valid = 0; 199 | return IOATC_MISS; 200 | } 201 | // If memory access is a store and VS/S or G stage D bit is 0 then mark 202 | // TLB entry as invalid so it returns a miss to trigger a page walk 203 | if ( (tlb[hit].VS_D == 0 || tlb[hit].G_D == 0) && is_write == 1 ) { 204 | tlb[hit].valid = 0; 205 | return IOATC_MISS; 206 | } 207 | *page_sz = ((tlb[hit].S == 0) ? 1 : ((tlb[hit].PPN ^ (tlb[hit].PPN + 1)) + 1)); 208 | *page_sz = *page_sz * PAGESIZE; 209 | *resp_pa = ((tlb[hit].PPN * PAGESIZE) & ~(*page_sz - 1)) | (iova & (*page_sz - 1)); 210 | g_pte->R = tlb[hit].G_R; 211 | g_pte->W = tlb[hit].G_W; 212 | g_pte->X = tlb[hit].G_X; 213 | 214 | vs_pte->R = tlb[hit].VS_R; 215 | vs_pte->W = tlb[hit].VS_W; 216 | vs_pte->X = tlb[hit].VS_X; 217 | vs_pte->G = tlb[hit].G; 218 | vs_pte->U = tlb[hit].U; 219 | vs_pte->PBMT = tlb[hit].PBMT; 220 | 221 | *is_msi = tlb[hit].IS_MSI; 222 | return IOATC_HIT; 223 | 224 | page_fault: 225 | if ( is_exec ) *cause = 12; // Instruction page fault 226 | else if ( is_read ) *cause = 13; // Read page fault 227 | else *cause = 15; // Write/AMO page fault 228 | return IOATC_FAULT; 229 | } 230 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_faults.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | 6 | #include "iommu.h" 7 | 8 | void 9 | report_fault(uint16_t cause, uint64_t iotval, uint64_t iotval2, uint8_t TTYP, uint8_t dtf, 10 | uint32_t device_id, uint8_t pid_valid, uint32_t process_id, uint8_t priv_req) { 11 | fault_rec_t frec; 12 | uint32_t fqh; 13 | uint32_t fqt; 14 | uint64_t fqb; 15 | uint64_t frec_addr; 16 | uint8_t status; 17 | 18 | // The fault-queue enable bit enables the fault-queue when set to 1. 19 | // The fault-queue is active if fqon reads 1. 20 | if ( g_reg_file.fqcsr.fqon == 0 || g_reg_file.fqcsr.fqen == 0 ) 21 | return; 22 | 23 | // The fqmf bit is set to 1 if the IOMMU encounters an access fault 24 | // when storing a fault record to the fault queue. The fault-record that 25 | // was attempted to be written is discarded and no more fault records 26 | // are generated until software clears fqmf bit by writing 1 to the bit. 27 | // An interrupt is generated if enabled and not already pending (i.e. 28 | // ipsr.fip == 1) and not masked (i.e. fqsr.fie == 0). 29 | if ( g_reg_file.fqcsr.fqmf == 1 ) 30 | return; 31 | 32 | // The fault-queue-overflow bit is set to 1 if the IOMMU needs to 33 | // queue a fault record but the fault-queue is full (i.e., fqt == fqh - 1) 34 | // The fault-record is discarded and no more fault records are 35 | // generated till software clears fqof by writing 1 to the bit. An 36 | // interrupt is generated if not already pending (i.e. ipsr.fip == 1) 37 | // and not masked (i.e. fqsr.fie == 0) 38 | if ( g_reg_file.fqcsr.fqof == 1 ) 39 | return; 40 | 41 | // Setting the disable-translation-fault - DTF - bit to 1 disables reporting of 42 | // faults encountered in the address translation process. Setting DTF to 1 does not 43 | // disable error responses from being generated to the device in response to faulting 44 | // transactions. Setting DTF to 1 does not disable reporting of faults from the IOMMU 45 | // that are not related to the address translation process. The faults that are not 46 | // reported when DTF is 1 are listed in Table 8 47 | // |CAUSE | Description | Reported if `DTF` is 1? 48 | // |0 | Instruction address misaligned | No 49 | // |1 | Instruction access fault | No 50 | // |4 | Read address misaligned | No 51 | // |5 | Read access fault | No 52 | // |6 | Write/AMO address misaligned | No 53 | // |7 | Write/AMO access fault | No 54 | // |12 | Instruction page fault | No 55 | // |13 | Read page fault | No 56 | // |15 | Write/AMO page fault | No 57 | // |20 | Instruction guest page fault | No 58 | // |21 | Read guest-page fault | No 59 | // |23 | Write/AMO guest-page fault | No 60 | // |256 | All inbound transactions disallowed | Yes 61 | // |257 | DDT entry load access fault | Yes 62 | // |258 | DDT entry not valid | Yes 63 | // |259 | DDT entry misconfigured | Yes 64 | // |260 | Transaction type disallowed | No 65 | // |261 | MSI PTE load access fault | No 66 | // |262 | MSI PTE not valid | No 67 | // |263 | MSI PTE misconfigured | No 68 | // |264 | MRIF access fault | No 69 | // |265 | PDT entry load access fault | No 70 | // |266 | PDT entry not valid | No 71 | // |267 | PDT entry misconfigured | No 72 | // |268 | DDT data corruption | Yes 73 | // |269 | PDT data corruption | No 74 | // |270 | MSI PT data corruption | No 75 | // |271 | MSI MRIF data corruption | No 76 | // |272 | Internal datapath error | Yes 77 | // |273 | IOMMU MSI write access fault | Yes 78 | // |274 | S/VS/G-stage PT data corruption | No 79 | // The `CAUSE` encodings 274 through 2047 are reserved for future standard use and 80 | // the encodings 2048 through 4095 are designated for custom use. 81 | if ( (dtf == 1) && (cause != 256) && (cause != 257) && 82 | (cause != 258) && (cause != 259) && (cause != 268) && 83 | (cause != 272) && (cause != 273) ) { 84 | return; 85 | } 86 | 87 | // DID holds the device_id of the transaction. 88 | // If PV is 0, then PID and PRIV are 0. If PV is 1, the PID 89 | // holds a process_id of the transaction and if the privilege 90 | // of the transaction was Supervisor then PRIV bit is 1 else its 0. 91 | frec.DID = device_id; 92 | if ( pid_valid ) { 93 | frec.PID = process_id; 94 | frec.PV = pid_valid; 95 | frec.PRIV = priv_req; 96 | } else { 97 | frec.PID = 0; 98 | frec.PV = 0; 99 | frec.PRIV = 0; 100 | } 101 | frec.reserved = 0; 102 | frec.custom = 0; 103 | frec.iotval = iotval; 104 | frec.iotval2 = iotval2; 105 | frec.TTYP = TTYP; 106 | frec.CAUSE = cause; 107 | 108 | // Fault/Event queue is an in-memory queue data structure used to report events 109 | // and faults raised when processing transactions. Each fault record is 32 bytes. 110 | // The PPN of the base of this in-memory queue and the size of the queue is 111 | // configured into a memorymapped register called fault-queue base (fqb). 112 | // The tail of the fault-queue resides in a IOMMU controlled read-only 113 | // memory-mapped register called fqt. The fqt is an index into the next fault 114 | // record that IOMMU will write in the fault-queue. 115 | // Subsequent to writing the record, the IOMMU advances the fqt by 1. The head of 116 | // the fault-queue resides in a read/write memory-mapped software controlled 117 | // register called fqh. The fqh is an index into the next fault record that SW 118 | // should process next. Subsequent to processing fault record(s) software advances 119 | // the fqh by the count of the number of fault records processed. If fqh == fqt, the 120 | // fault-queue is empty. If fqt == (fqh - 1) the fault-queue is full. 121 | fqh = g_reg_file.fqh.index; 122 | fqt = g_reg_file.fqt.index; 123 | fqb = g_reg_file.fqb.ppn; 124 | if ( ((fqt + 1) & ((1UL << (g_reg_file.fqb.log2szm1 + 1)) - 1)) == fqh ) { 125 | g_reg_file.fqcsr.fqof = 1; 126 | generate_interrupt(FAULT_QUEUE); 127 | return; 128 | } 129 | // The IOMMU may be unable to report faults through the fault-queue due to error 130 | // conditions such as the fault-queue being full or the IOMMU encountering access 131 | // faults when attempting to access the queue memory. A memory-mapped fault control 132 | // and status register (fqcsr) holds information about such faults. If the fault-queue 133 | // full condition is detected the IOMMU sets a fault-queue overflow (fqof) 134 | // bit in fqcsr. If the IOMMU encounters a fault in accessing the fault-queue memory, 135 | // the IOMMU sets a fault-queue memory access fault (fqmf) bit in fqcsr. While either 136 | // error bits are set in fqcsr, the IOMMU discards the record that led to the fault 137 | // and all further fault records. When an error bit is in the fqcsr changes state 138 | // from 0 to 1 or when a new fault record is produced in the fault-queue, fault 139 | // interrupt pending (fip) bit is set in the fqcsr. 140 | frec_addr = ((fqb * PAGESIZE) | (fqt * FQ_ENTRY_SZ)); 141 | status = write_memory((char *)&frec, frec_addr, 32, 142 | g_reg_file.iommu_qosid.rcid, g_reg_file.iommu_qosid.mcid); 143 | if ( (status & ACCESS_FAULT) || (status & DATA_CORRUPTION) ) { 144 | g_reg_file.fqcsr.fqmf = 1; 145 | } else { 146 | fqt = (fqt + 1) & ((1UL << (g_reg_file.fqb.log2szm1 + 1)) - 1); 147 | g_reg_file.fqt.index = fqt; 148 | } 149 | generate_interrupt(FAULT_QUEUE); 150 | return; 151 | } 152 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_hpm.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | void 7 | count_events( 8 | uint8_t PV, uint32_t PID, uint8_t PSCV, uint32_t PSCID, 9 | uint32_t DID, uint8_t GSCV, uint32_t GSCID, uint16_t eventID) { 10 | uint8_t i; 11 | uint32_t mask; 12 | uint64_t count; 13 | 14 | // IOMMU implements a performance-monitoring unit 15 | // if capabilities.hpm == 1 16 | if ( g_reg_file.capabilities.hpm == 0 ) return; 17 | 18 | for ( i = 0; i < g_num_hpm; i++ ) { 19 | // The performance-monitoring counter inhibits is a 32-bits WARL 20 | // register where that contains bits to inhibit the corresponding 21 | // counters from counting. Bit X when set inhibits counting in 22 | // iohpmctrX and bit 0 inhibits counting in iohpmcycles. 23 | if ( g_reg_file.iocountinh.raw & (1UL << i) ) continue; 24 | 25 | // Counter is not inhibited check if it matches 26 | // These performance-monitoring event registers are 64-bit RW 27 | // registers. When a transaction processed by the IOMMU causes an 28 | // event that is programmed to count in a counter then the counter is 29 | // incremented. In addition to matching events the event selector may 30 | // be programmed with additional filters based on device_id, process_id, 31 | // GSCID, and PSCID such that the counter is incremented conditionally 32 | // based on the transaction matching these additional filters. When such 33 | // device_id based filtering is used, the match may be configured to be 34 | // a precise match or a partial match. A partial match allows a 35 | // transactions with a range of IDs to be counted by the counter. 36 | if ( g_reg_file.iohpmevt[i].eventID != eventID ) continue; 37 | 38 | // When filtering by device_id or GSCID is selected and the event supports 39 | // ID based filtering, the DMASK field can be used to configure a partial 40 | // match. When DMASK is set to 1, partial matching of the DID_GSCID is 41 | // performed for the transaction. The lower bits of the DID_GSCID all the 42 | // way to the first low order 0 bit (including the 0 bit position itself) 43 | // are masked. 44 | // The following example illustrates the use of DMASK and filtering by `device_id`. 45 | // | *DMASK* | *DID_GSCID* | *Comment* 46 | // | 0 | yyyyyyyy yyyyyyyy yyyyyyyy | One specific seg:bus:dev:func 47 | // | 1 | yyyyyyyy yyyyyyyy yyyyy011 | seg:bus:dev - any func 48 | // | 1 | yyyyyyyy yyyyyyyy 01111111 | seg:bus - any dev:func 49 | // | 1 | yyyyyyyy 01111111 11111111 | seg - any bus:dev:func 50 | mask = g_reg_file.iohpmevt[i].did_gscid + 1; 51 | mask = mask ^ g_reg_file.iohpmevt[i].did_gscid; 52 | mask = ~mask; 53 | // If DMASK is 0, then all 24-bits must match 54 | mask = (g_reg_file.iohpmevt[i].dmask == 1) ? mask : 0xFFFFFF; 55 | 56 | // IDT - Filter ID Type: This field indicates the type of ID to 57 | // filter on. 58 | if ( g_reg_file.iohpmevt[i].idt == 0 ) { 59 | // When 0, the DID_GSCID field holds a device_id and the 60 | // PID_PSCID field holds a process_id. 61 | if ( g_reg_file.iohpmevt[i].pv_pscv == 1 ) { 62 | if ( PV == 0 ) continue; 63 | if ( g_reg_file.iohpmevt[i].pid_pscid != PID ) continue; 64 | } 65 | if ( g_reg_file.iohpmevt[i].dv_gscv == 1 ) { 66 | if ( (g_reg_file.iohpmevt[i].did_gscid & mask) != (DID & mask) ) { 67 | continue; 68 | } 69 | } 70 | } else { 71 | // g_reg_file.iohpmevt[i].idt == 1 72 | // When 1, the DID_GSCID field holds a GSCID and PID_PSCID 73 | // field holds a PSCID. 74 | if ( g_reg_file.iohpmevt[i].pv_pscv == 1 ) { 75 | if ( PSCV == 0 ) continue; 76 | if ( g_reg_file.iohpmevt[i].pid_pscid != PSCID ) continue; 77 | } 78 | if ( g_reg_file.iohpmevt[i].dv_gscv == 1 ) { 79 | if ( GSCV == 0 ) continue; 80 | if ( (g_reg_file.iohpmevt[i].did_gscid & mask) != (GSCID & mask) ) continue; 81 | } 82 | } 83 | // Counter is not inhibited and all filters pass 84 | count = g_reg_file.iohpmctr[i].counter + 1; 85 | count = count & ((g_hpmctr_bits == 64) ? -1LL : ((1LL << g_hpmctr_bits) - 1)); 86 | if ( g_reg_file.iohpmctr[i].counter > count ) { 87 | g_reg_file.iohpmctr[i].counter = count; 88 | // The OF bit is set when the corresponding iohpmctr* overflows, 89 | // and remains set until cleared by software. Since iohpmctr* 90 | // values are unsigned values, overflow is defined as unsigned 91 | // overflow. Note that there is no loss of information after an 92 | // overflow since the counter wraps around and keeps counting 93 | // while the sticky OF bit remains set. If an iohpmctr* overflows 94 | // while the associated OF bit is zero, then a HPM Counter Overflow 95 | // interrupt is generated. If the OF bit is one, then no interrupt 96 | // request is generated. Consequently the OF bit also functions as 97 | // a count overflow interrupt disable for the associated iohpmctr*. 98 | // A pending HPM Counter Overflow interrupt (OR of all iohpmctr* 99 | // overflows) is and reported through ipsr register. 100 | if ( g_reg_file.iohpmevt[i].of == 0 ) { 101 | g_reg_file.iohpmevt[i].of = 1; 102 | generate_interrupt(HPM); 103 | } 104 | } else { 105 | g_reg_file.iohpmctr[i].counter = count; 106 | } 107 | } 108 | return; 109 | } 110 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_interrupt.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | 6 | #include "iommu.h" 7 | // MSI pending bit array. 8 | uint8_t msi_pending[16] = {0}; 9 | static void 10 | do_msi( 11 | uint32_t msi_data, uint64_t msi_addr) { 12 | uint8_t status; 13 | status = write_memory((char *)&msi_data, msi_addr, 4, 14 | g_reg_file.iommu_qosid.rcid, g_reg_file.iommu_qosid.mcid); 15 | if ( status & ACCESS_FAULT ) { 16 | // If an access fault is detected on a MSI write using msi_addr_x, 17 | // then the IOMMU reports a "IOMMU MSI write access fault" (cause 273) fault, 18 | // with TTYP set to 0 and iotval set to the value of msi_addr_x. 19 | report_fault(273, msi_addr, 0, TTYPE_NONE, 0, 0, 0, 0, 0); 20 | } 21 | return; 22 | } 23 | void 24 | generate_interrupt( 25 | uint8_t unit) { 26 | 27 | msi_addr_t msi_addr; 28 | uint32_t msi_data; 29 | msi_vec_ctrl_t msi_vec_ctrl; 30 | uint8_t vec; 31 | 32 | // Interrupt pending status register (ipsr) 33 | // This 32-bits register (RW1C) reports the pending interrupts 34 | // which require software service. Each interrupt-pending bit 35 | // in the register corresponds to a interrupt source in the IOMMU. When an 36 | // interrupt-pending bit in the register is set to 1 the IOMMU will not 37 | // signal another interrupt from that source till software clears that 38 | // interrupt-pending bit by writing 1 to clear it. 39 | // Interrupt-cause-to-vector register (icvec) 40 | // Interrupt-cause-to-vector register maps a cause to a vector. All causes 41 | // can be mapped to same vector or a cause can be given a unique vector. 42 | switch ( unit ) { 43 | case FAULT_QUEUE: 44 | // The fault-queue-interrupt-pending 45 | if ( g_reg_file.ipsr.fip == 1) 46 | return; 47 | if ( g_reg_file.fqcsr.fie == 0) 48 | return; 49 | vec = g_reg_file.icvec.fiv; 50 | g_reg_file.ipsr.fip = 1; 51 | break; 52 | case PAGE_QUEUE: 53 | if ( g_reg_file.ipsr.pip == 1) 54 | return; 55 | if ( g_reg_file.pqcsr.pie == 0) 56 | return; 57 | vec = g_reg_file.icvec.piv; 58 | g_reg_file.ipsr.pip = 1; 59 | break; 60 | case COMMAND_QUEUE: 61 | if ( g_reg_file.ipsr.cip == 1) 62 | return; 63 | if ( g_reg_file.cqcsr.cie == 0) 64 | return; 65 | vec = g_reg_file.icvec.civ; 66 | g_reg_file.ipsr.cip = 1; 67 | break; 68 | default: // HPM 69 | if ( g_reg_file.ipsr.pmip == 1) 70 | return; 71 | vec = g_reg_file.icvec.pmiv; 72 | g_reg_file.ipsr.pmip = 1; 73 | break; 74 | } 75 | // The vector is used: 76 | // 1. By an IOMMU that generates interrupts as MSI, to index into MSI 77 | // configuration table (msi_cfg_tbl) to determine the MSI to generate. An 78 | // IOMMU is capable of generating interrupts as a MSI if capabilities.IGS==MSI 79 | // or if capabilities.IGS==BOTH. When capabilities.IGS==BOTH the IOMMU may be 80 | // configured to generate interrupts as MSI by setting fctl.WSI to 0. 81 | // 2. By an IOMMU that generates wire based interrupts, to determine the wire 82 | // to signal the interrupt. An IOMMU is capable of generating wire based 83 | // interrupts if capabilities.IGS==WSI or if capabilities.IGS==BOTH. When 84 | // capabilities.IGS==BOTH the IOMMU may be configured to generate wire based 85 | // interrupts by setting fctl.WSI to 1. 86 | if ( g_reg_file.fctl.wsi == 0 ) { 87 | msi_addr.raw = g_reg_file.msi_cfg_tbl[vec].msi_addr.raw; 88 | msi_data = g_reg_file.msi_cfg_tbl[vec].msi_data; 89 | msi_vec_ctrl.raw = g_reg_file.msi_cfg_tbl[vec].msi_vec_ctrl.raw; 90 | // When the mask bit M is 1, the corresponding interrupt vector is 91 | // masked and the IOMMU is prohibited from sending the associated 92 | // message. 93 | if ( msi_vec_ctrl.m == 1 ) { 94 | // Pending messages for that vector are later generated if the 95 | // corresponding mask bit is cleared to 0. 96 | msi_pending[vec] = 1; 97 | return; 98 | } 99 | do_msi(msi_data, msi_addr.raw); 100 | } 101 | return; 102 | } 103 | void 104 | release_pending_interrupt( 105 | uint8_t vec) { 106 | msi_addr_t msi_addr; 107 | uint32_t msi_data; 108 | 109 | if ( msi_pending[vec] == 1 ) { 110 | msi_addr.raw = g_reg_file.msi_cfg_tbl[vec].msi_addr.raw; 111 | msi_data = g_reg_file.msi_cfg_tbl[vec].msi_data; 112 | do_msi(msi_data, msi_addr.raw); 113 | msi_pending[vec] = 0; 114 | } 115 | return; 116 | } 117 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_msi_trans.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | 6 | #include "iommu.h" 7 | uint64_t 8 | extract(uint64_t data, uint64_t mask) { 9 | uint32_t i, j = 0; 10 | uint64_t I = 0; 11 | for ( i = 0; i < 64; i++ ) { 12 | if ( mask & (1UL << i) ) { 13 | I |= (((data >> i) & 0x01) << j); 14 | j++; 15 | } 16 | } 17 | return I; 18 | } 19 | uint8_t 20 | msi_address_translation( 21 | uint64_t gpa, uint8_t is_exec, device_context_t *DC, 22 | uint8_t *is_msi, uint8_t *is_mrif, uint32_t *mrif_nid, uint64_t *dest_mrif_addr, 23 | uint32_t *cause, uint64_t *iotval2, uint64_t *pa, 24 | uint64_t *page_sz, gpte_t *g_pte, uint8_t check_access_perms, 25 | uint32_t rcid, uint32_t mcid) { 26 | 27 | uint64_t A, m, I; 28 | uint8_t status; 29 | msipte_t msipte; 30 | 31 | *iotval2 = 0; 32 | *is_msi = 0; 33 | 34 | if ( DC->msiptp.MODE == MSIPTP_Off ) 35 | return 0; 36 | 37 | // 1. Let `A` be the GPA 38 | A = gpa; 39 | 40 | // 2. Let `DC` be the device-context located using the `device_id` of the device 41 | // using the process outlined in <>. 42 | // 3. Determine if the address `A` is an access to a virtual interrupt file as 43 | // specified in <> 44 | *is_msi = (((A >> 12) & ~DC->msi_addr_mask.mask) == 45 | ((DC->msi_addr_pattern.pattern & ~DC->msi_addr_mask.mask))); 46 | 47 | // 4. If the address is not determined to be that of a virtual interrupt file then 48 | // stop this process and instead use the regular translation data structures to 49 | // do the address translation. 50 | if ( *is_msi == 0 ) 51 | return 0; 52 | 53 | // 5. Extract an interrupt file number `I` from `A` as 54 | // `I = extract(A >> 12, DC.msi_addr_mask)`. The bit extract function `extract(x, y)` 55 | // discards all bits from `x` whose matching bits in the same positions in the 56 | // mask `y` are zeros, and packs the remaining bits from `x` contiguously at the 57 | // least-significant end of the result, keeping the same bit order as `x` and 58 | // filling any other bits at the most-significant end of the result with zeros. 59 | // For example, if the bits of `x` and `y` are 60 | // ** `x = a b c d e f g h` 61 | // ** `y = 1 0 1 0 0 1 1 0` 62 | // ** then the value of `extract(x, y)` has bits `0 0 0 0 a c f g`. 63 | I = extract((A >> 12), DC->msi_addr_mask.mask); 64 | 65 | // 6. Let `m` be `(DC.msiptp.PPN x 2^12^)`. 66 | m = DC->msiptp.PPN * PAGESIZE; 67 | 68 | // 7. Let `msipte` be the value of sixteen bytes at address `(m | (I x 16))`. If 69 | // accessing `msipte` violates a PMA or PMP check, then stop and report 70 | // "MSI PTE load access fault" (cause = 261). 71 | status = read_memory((m + (I * 16)), 16, (char *)&msipte.raw, rcid, mcid); 72 | if ( status & ACCESS_FAULT ) { 73 | *cause = 261; // MSI PTE load access fault 74 | return 1; 75 | } 76 | 77 | // 8. If `msipte` access detects a data corruption (a.k.a. poisoned data), then 78 | // stop and report "MSI PT data corruption" (cause = 270). This fault is reported 79 | // if the IOMMU supports RAS (i.e., capabilities.RAS == 1) 80 | if ( (status & DATA_CORRUPTION) ) { 81 | *cause = 270; // MSI PTE load access fault 82 | return 1; 83 | } 84 | 85 | // 9. If `msipte.V == 0`, then stop and report "MSI PTE not valid" (cause = 262). 86 | if ( msipte.V == 0 ) { 87 | *cause = 262; // MSI PTE not valid 88 | return 1; 89 | } 90 | 91 | //10. If `msipte.C == 1`, then further process is to interpret the PTE is 92 | // implementation defined. No custom implementation in reference model 93 | // so this case causes "MSI PTE misconfigured" (cause = 263). 94 | if ( msipte.C == 1 ) { 95 | *cause = 263; 96 | return 1; 97 | } 98 | 99 | //11. If `msipte.C == 0` then the process is outlined in subsequent steps. 100 | //12. If `msipte.M == 0` or `msipte.M == 2`,then stop and report "MSI PTE 101 | // misconfigured" (cause = 263). 102 | if ( msipte.M == 0 || msipte.M == 2 ) { 103 | *cause = 263; 104 | return 1; 105 | } 106 | 107 | //13. If `msipte.M == 3` the PTE is translate R/W mode PTE and the translation 108 | // process is as follows: 109 | // a. If any bits or encoding that are reserved for future standard use are 110 | // set within msipte, stop and report "MSI PTE misconfigured" (cause = 263). 111 | // b. Compute the translated address as `msipte.PPN << 12 | A[11:0]`. 112 | if ( msipte.M == 3 ) { 113 | if ( msipte.translate_rw.reserved != 0 || msipte.translate_rw.reserved0 != 0 ) { 114 | *cause = 263; 115 | return 1; 116 | } 117 | *pa = ((msipte.translate_rw.PPN * PAGESIZE) | (A & 0xFFF)); 118 | g_pte->raw = 0; 119 | g_pte->PPN = gpa / PAGESIZE; 120 | g_pte->D = g_pte->A = g_pte->U = 1; 121 | g_pte->X = 0; 122 | g_pte->W = g_pte->R = g_pte->V = 1; 123 | g_pte->N = g_pte->G = 0; 124 | g_pte->PBMT = PMA; 125 | *page_sz = PAGESIZE; 126 | *is_mrif = 0; 127 | goto step_15; 128 | } 129 | 130 | //14. If `msipte.M == 1` the PTE is in MRIF mode and the translation process 131 | // is as follows: 132 | // a. If `capabilities.MSI_MRIF == 0`, stop and report "MSI PTE misconfigured" 133 | // (cause = 263). 134 | if ( g_reg_file.capabilities.msi_mrif == 0 ) { 135 | *cause = 263; 136 | return 1; 137 | } 138 | 139 | // b. If any bits or encoding that are reserved for future standard use are 140 | // set within `msipte`, stop and report "MSI PTE misconfigured" (cause = 263). 141 | if ( msipte.mrif.reserved1 != 0 || msipte.mrif.reserved2 != 0 || 142 | msipte.mrif.reserved3 != 0 || msipte.mrif.reserved4 != 0 ) { 143 | *cause = 263; 144 | return 1; 145 | } 146 | 147 | // d. The address of the destination MRIF is `msipte.MRIF_Address[55:9] * 512`. 148 | *dest_mrif_addr = msipte.mrif.MRIF_ADDR_55_9 << 9; 149 | 150 | // e. The destination address of the notice MSI is `msipte.NPPN << 12`. 151 | *pa = (msipte.mrif.NPPN * PAGESIZE); 152 | 153 | // f. Let `NID` be `(msipte.N10 << 10) | msipte.N[9:0]`. The data value for 154 | // notice MSI is the 11-bit `NID` value zero-extended to 32-bits. 155 | *mrif_nid = (msipte.mrif.N10 << 10) | msipte.mrif.N90; 156 | 157 | *is_mrif = 1; 158 | g_pte->raw = 0; 159 | g_pte->PPN = gpa / PAGESIZE; 160 | g_pte->D = g_pte->A = g_pte->U = 1; 161 | g_pte->X = 0; 162 | g_pte->W = g_pte->R = g_pte->V = 1; 163 | g_pte->N = g_pte->G = 0; 164 | g_pte->PBMT = PMA; 165 | *page_sz = PAGESIZE; 166 | 167 | step_15: 168 | //15. The access permissions associated with the translation determined through 169 | // this process are equivalent to that of a regular RISC-V second-stage PTE with 170 | // R=W=U=1 and X=0. Similar to a second-stage PTE, when checking the U bit, the 171 | // transaction is treated as not requesting supervisor privilege. 172 | // a. If the transaction is a Untranslated or Translated read-for-execute then stop 173 | // and report "Instruction acccess fault" (cause = 1). 174 | if ( is_exec == 1 && check_access_perms == 1 ) { 175 | *cause = 1; 176 | return 1; 177 | } 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_process_context.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | 6 | #include "iommu.h" 7 | uint8_t 8 | do_process_context_configuration_checks( 9 | device_context_t *DC, process_context_t *PC); 10 | 11 | uint8_t 12 | locate_process_context( 13 | process_context_t *PC, device_context_t *DC, uint32_t device_id, uint32_t process_id, 14 | uint32_t *cause, uint64_t *iotval2, uint8_t TTYP) { 15 | uint64_t a, gst_page_sz; 16 | uint8_t i, LEVELS, status, gst_fault; 17 | gpte_t g_pte; 18 | pdte_t pdte; 19 | uint16_t PDI[3]; 20 | uint8_t is_implicit, is_read, is_write, is_exec; 21 | 22 | // The device-context provides the PDT root page PPN (pdtp.ppn). 23 | // When DC.iohgatp.mode is not Bare, pdtp.PPN as well as pdte.PPN 24 | // are Guest Physical Addresses (GPA) which must be translated into 25 | // System Physical Addresses (SPA) using the G-stage page table 26 | // determined by DC.iohgatp. 27 | 28 | // The PDT may be configured to be a 1, 2, or 3 level radix table 29 | // depending on the maximum width of the process_id supported for 30 | // that device. The partitioning of the process_id to obtain the process 31 | // directory indices (PDI) to traverse the PDT radix-tree table are as follows: 32 | PDI[0] = get_bits(7, 0, process_id); 33 | PDI[1] = get_bits(16, 8, process_id); 34 | PDI[2] = get_bits(19, 17, process_id); 35 | 36 | // The following diagrams illustrate the PDT radix-tree. The root 37 | // process-directory page number is located using the process-directory-table 38 | // pointer (`pdtp`) field of the device-context. Each non-leaf (`NL`) entry 39 | // provides the PPN of the next level process-directory-table. The leaf 40 | // process-directory-table entry holds the process-context (`PC`). 41 | // .Three, two and single-level process directory 42 | // +-------+-------+-------+ +-------+-------+ +-------+ 43 | // |PDI[2] |PDI[1] |PDI[0] | |PDI[1] |PDI[0] | |PDI[0] | 44 | // +--+----+--+----+--+----+ +-+-----+-+-----+ +-+-----+ 45 | // | | | | | | 46 | // +-3-bit +-9-bit +-8-bit +-9-bit +-8-bit +-8-bit 47 | // | | | | | | 48 | // | +--+ | +--+ | +--+ | +--+ | +--+ | +--+ 49 | // | | | | | | | | | | | | | | | | | | 50 | // | | | | | | | +--+ | | | | +--+ | | | 51 | // | | | | | | +->|PC| | | | +->|PC| | | | 52 | // | | | | +--+ +--+ | | | +--+ | | | 53 | // | | | +->|NL+-+ | | | +--+ | | | | | 54 | // | | | +--+ | | | +->|NL+-+ | | | +--+ 55 | // +->+--+ | | | | | +--+ | | | +-->|PC| 56 | // |NL+-+ | | | | | | | | | | +--+ 57 | // +--+ | | | | | | | | | | | | | 58 | // | | | | | | | | | | | | | | | 59 | // pdtp--->+--+ +->+--+ +->+--+ pdtp--->+--+ +->+--+ pdtp--->+--+ 60 | 61 | // The process to locate the Process-context for a transaction 62 | // using its process_id is as follows: 63 | 64 | // Determine if there is a cached device context 65 | if ( lookup_ioatc_pc(device_id, process_id, PC) == IOATC_HIT ) 66 | return 0; 67 | 68 | // 1. Let a be pdtp.PPN x 2^12 and let i = LEVELS - 1. When pdtp.MODE 69 | // is PD20, LEVELS is three. When pdtp.MODE is PD17, LEVELS is two. 70 | // When pdtp.MODE is PD8, LEVELS is one. 71 | a = DC->fsc.pdtp.PPN * PAGESIZE; 72 | if ( DC->fsc.pdtp.MODE == PD20 ) LEVELS = 3; 73 | if ( DC->fsc.pdtp.MODE == PD17 ) LEVELS = 2; 74 | if ( DC->fsc.pdtp.MODE == PD8 ) LEVELS = 1; 75 | i = LEVELS - 1; 76 | 77 | step_2: 78 | a = a + ((i == 0) ? (PDI[i] * 16) : (PDI[i] * 8)); 79 | // 2. If `DC.iohgatp.mode != Bare`, then `a` is a GPA. Invoke the process 80 | // to translate `a` to a SPA as an implicit memory access. If faults 81 | // occur during G-stage address translation of `a` then stop and the fault 82 | // detected by the G-stage address translation process. The translated `a` 83 | // is used in subsequent steps. 84 | is_read = 1; 85 | is_write = is_exec = is_implicit = 0; 86 | if ( ( gst_fault = second_stage_address_translation(a, 1, device_id, is_read, is_write, 87 | is_exec, is_implicit, 1, process_id, 0, 0, 88 | ((DC->iohgatp.MODE == IOHGATP_Bare) ? 0 : 1), 89 | DC->iohgatp.GSCID, DC->iohgatp, DC->tc.GADE, DC->tc.SADE, 90 | DC->tc.SXL, &a, &gst_page_sz, &g_pte, DC->ta.rcid, 91 | DC->ta.mcid) ) ) { 92 | if ( gst_fault == GST_PAGE_FAULT ) { 93 | *cause = 21; // Read guest page fault 94 | *iotval2 = (a & ~0x3); 95 | *iotval2 |= 1; 96 | return 1; 97 | } 98 | if ( gst_fault == GST_ACCESS_FAULT ) { 99 | *cause = 265; // PDT entry load access fault 100 | return 1; 101 | } 102 | if ( gst_fault == GST_DATA_CORRUPTION ) { 103 | *cause = 274; // PDT entry load data corruption fault 104 | return 1; 105 | } 106 | } 107 | 108 | // 3. If `i == 0` go to step 9. 109 | if ( i == 0 ) goto step_9; 110 | 111 | // Count walks in PDT 112 | count_events(1, process_id, 0, 0, device_id, 0, 0, PDT_WALKS); 113 | 114 | // 4. Let `pdte` be value of eight bytes at address `a + PDI[i] x 8`. If 115 | // accessing `pdte` violates a PMA or PMP check, then stop and report 116 | // "PDT entry load access fault" (cause = 265). 117 | status = read_memory(a, 8, (char *)&pdte.raw, DC->ta.rcid, DC->ta.mcid); 118 | if ( status & ACCESS_FAULT ) { 119 | *cause = 265; // PDT entry load access fault 120 | return 1; 121 | } 122 | 123 | // 5. If `pdte` access detects a data corruption (a.k.a. poisoned data), then 124 | // stop and report "PDT data corruption" (cause = 269). 125 | if ( status & DATA_CORRUPTION ) { 126 | *cause = 269; // PDT data corruption 127 | return 1; 128 | } 129 | 130 | // 6. If `pdte.V == 0`, stop and report "PDT entry not valid" (cause = 266). 131 | if ( pdte.V == 0 ) { 132 | *cause = 266; // PDT entry not valid 133 | return 1; 134 | } 135 | 136 | // 7. If if any bits or encoding that are reserved for future standard use are 137 | // set within `pdte`, stop and report "PDT entry misconfigured" (cause = 267). 138 | if ( pdte.reserved0 != 0 || pdte.reserved1 != 0 ) { 139 | *cause = 267; // PDT entry misconfigured 140 | return 1; 141 | } 142 | 143 | // 8. Let `i = i - 1` and let `a = pdte.PPN x 2^12`. Go to step 2. 144 | i = i - 1; 145 | a = pdte.PPN * PAGESIZE; 146 | goto step_2; 147 | 148 | step_9: 149 | // Count walks in PDT 150 | count_events(1, process_id, 0, 0, device_id, 0, 0, PDT_WALKS); 151 | 152 | // 9. Let `PC` be value of 16-bytes at address `a + PDI[0] x 16`. If accessing `PC` 153 | // violates a PMA or PMP check, then stop and report "PDT entry load access 154 | // fault" (cause = 265).If `PC` access detects a data corruption 155 | // (a.k.a. poisoned data), then stop and report "PDT data corruption" 156 | // (cause = 269). 157 | status = read_memory(a, 16, (char *)PC, DC->ta.rcid, DC->ta.mcid); 158 | if ( status & ACCESS_FAULT ) { 159 | *cause = 265; // PDT entry load access fault 160 | return 1; 161 | } 162 | if ( status & DATA_CORRUPTION ) { 163 | *cause = 269; // PDT data corruption 164 | return 1; 165 | } 166 | //10. If `PC.ta.V == 0`, stop and report "PDT entry not valid" (cause = 266). 167 | if ( PC->ta.V == 0 ) { 168 | *cause = 266; // PDT entry not valid 169 | return 1; 170 | } 171 | 172 | //11. If the PC is misconfigured as determined by rules outlined in Section 2.2.4 173 | // then stop and report "PDT entry misconfigured" (cause = 267). 174 | if ( do_process_context_configuration_checks(DC, PC) ) { 175 | *cause = 267; // PDT entry misconfigured 176 | return 1; 177 | } 178 | 179 | //12. The Process-context has been successfully located. 180 | cache_ioatc_pc(device_id, process_id, PC); 181 | return 0; 182 | } 183 | uint8_t 184 | do_process_context_configuration_checks( 185 | device_context_t *DC, process_context_t *PC) { 186 | //1. If any bits or encoding that are reserved for future standard use are set 187 | if ( PC->ta.reserved0 != 0 || PC->ta.reserved1 != 0 || 188 | PC->fsc.iosatp.reserved != 0 ) { 189 | return 1; 190 | } 191 | // 2. PC.fsc.MODE encoding is not valid as determined by Table 3 192 | if ( (DC->tc.SXL == 0) && 193 | (PC->fsc.iosatp.MODE != IOSATP_Bare) && 194 | (PC->fsc.iosatp.MODE != IOSATP_Sv39) && 195 | (PC->fsc.iosatp.MODE != IOSATP_Sv48) && 196 | (PC->fsc.iosatp.MODE != IOSATP_Sv57) ) { 197 | return 1; 198 | } 199 | if ( (DC->tc.SXL == 1) && 200 | (PC->fsc.iosatp.MODE != IOSATP_Bare) && 201 | (PC->fsc.iosatp.MODE != IOSATP_Sv32) ) { 202 | return 1; 203 | } 204 | // 3. DC.tc.SXL is 0 and PC.fsc.MODE is not one of the supported modes 205 | // a. capabilities.Sv39 is 0 and PC.fsc.MODE is Sv39 206 | // b. capabilities.Sv48 is 0 and PC.fsc.MODE is Sv48 207 | // c. capabilities.Sv57 is 0 and PC.fsc.MODE is Sv57 208 | if ( (DC->tc.SXL == 0) && 209 | (((PC->fsc.iosatp.MODE == IOSATP_Sv39) && (g_reg_file.capabilities.Sv39 == 0)) || 210 | ((PC->fsc.iosatp.MODE == IOSATP_Sv48) && (g_reg_file.capabilities.Sv48 == 0)) || 211 | ((PC->fsc.iosatp.MODE == IOSATP_Sv57) && (g_reg_file.capabilities.Sv57 == 0))) ) { 212 | return 1; 213 | } 214 | //4. DC.tc.SXL is 1 and PC.fsc.MODE is not one of the supported modes 215 | // a. capabilities.Sv32 is 0 and PC.fsc.MODE is Sv32 216 | if ( (DC->tc.SXL == 1) && 217 | ((PC->fsc.iosatp.MODE == IOSATP_Sv32) && (g_reg_file.capabilities.Sv32 == 0)) ) { 218 | return 1; 219 | } 220 | return 0; 221 | } 222 | -------------------------------------------------------------------------------- /iommu_ref_model/libiommu/src/iommu_utils.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | // Match address to a NAPOT range 7 | uint8_t 8 | match_address_range( 9 | uint64_t VPN, uint64_t BASE_PN, uint8_t S) { 10 | 11 | uint64_t RANGE_MASK; 12 | 13 | RANGE_MASK = (S == 0) ? ~0UL : ~((BASE_PN) ^ (BASE_PN + 1)); 14 | return ((VPN & RANGE_MASK) == (BASE_PN & RANGE_MASK)) ? 1 : 0; 15 | } 16 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -O0 -g -Wall -Werror -I../libiommu/include/ -Iinclude 2 | CC := gcc 3 | NAME := tables 4 | SRCS = src/build_ddt.c src/build_pdt.c src/build_g_stage_pt.c src/build_s_stage_pt.c src/build_vs_stage_pt.c src/translate_gpa.c 5 | OBJS = $(SRCS:.c=.o) 6 | 7 | lib: lib$(NAME).a 8 | 9 | lib$(NAME).a: $(OBJS) 10 | ar rcD $@ $^ > /dev/null 2>&1 11 | ranlib $@ 12 | 13 | OBJ_APP = $(SRCS_APP:.c=.o) 14 | 15 | clean: 16 | $(RM) src/*.o *.a* tags log 17 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/include/tables_api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | #ifndef __TABLES_API_H__ 7 | #define __TABLES_API_H__ 8 | uint64_t add_dev_context(device_context_t *DC, uint32_t device_id); 9 | uint64_t add_process_context(device_context_t *DC, process_context_t *PC, uint32_t process_id); 10 | uint64_t add_g_stage_pte(iohgatp_t iohgatp, uint64_t gpa, gpte_t gpte, uint8_t add_level); 11 | uint64_t add_s_stage_pte(iosatp_t satp, uint64_t va, pte_t pte, uint8_t add_level, uint8_t SXL); 12 | uint64_t add_vs_stage_pte(iosatp_t satp, uint64_t va, pte_t pte, uint8_t add_level, iohgatp_t iohgatp, uint8_t SXL); 13 | uint64_t translate_gpa (iohgatp_t iohgatp, uint64_t gpa, uint64_t *spa); 14 | 15 | 16 | extern uint64_t get_free_ppn(uint64_t num_ppn); 17 | extern uint64_t get_free_gppn(uint64_t num_gppn, iohgatp_t iohgatp); 18 | 19 | #endif // __TABLES_API_H__ 20 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/src/build_ddt.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | #include "tables_api.h" 7 | 8 | uint64_t 9 | add_dev_context( 10 | device_context_t *DC, uint32_t device_id) { 11 | uint64_t a; 12 | uint8_t i, LEVELS, DC_SIZE; 13 | ddte_t ddte; 14 | uint16_t DDI[3]; 15 | // The DDT used to locate the DC may be configured to be a 1, 2, or 3 level 16 | // radix-table depending on the maximum width of the device_id supported. 17 | // The partitioning of the device_id to obtain the device directory indexes 18 | // (DDI) to traverse the DDT radix-tree table are as follows: 19 | if ( g_reg_file.capabilities.msi_flat == 0 ) { 20 | DDI[0] = get_bits(6, 0, device_id); 21 | DDI[1] = get_bits(15, 7, device_id); 22 | DDI[2] = get_bits(23, 16, device_id); 23 | DC_SIZE = BASE_FORMAT_DC_SIZE; 24 | } else { 25 | DDI[0] = get_bits(5, 0, device_id); 26 | DDI[1] = get_bits(14, 6, device_id); 27 | DDI[2] = get_bits(23, 15, device_id); 28 | DC_SIZE = EXT_FORMAT_DC_SIZE; 29 | } 30 | a = g_reg_file.ddtp.ppn * PAGESIZE; 31 | if ( g_reg_file.ddtp.iommu_mode == DDT_3LVL ) LEVELS = 3; 32 | if ( g_reg_file.ddtp.iommu_mode == DDT_2LVL ) LEVELS = 2; 33 | if ( g_reg_file.ddtp.iommu_mode == DDT_1LVL ) LEVELS = 1; 34 | i = LEVELS - 1; 35 | while ( i > 0 ) { 36 | ddte.raw = 0; 37 | if ( read_memory_test((a + (DDI[i] * 8)), 8, (char *)&ddte.raw) ) return -1; 38 | if ( ddte.V == 0 ) { 39 | ddte.V = 1; 40 | ddte.PPN = get_free_ppn(1); 41 | if ( write_memory_test((char *)&ddte.raw, (a + (DDI[i] * 8)), 8) ) return -1; 42 | } 43 | i = i - 1; 44 | a = ddte.PPN * PAGESIZE; 45 | } 46 | if ( write_memory_test((char *)DC, (a + (DDI[0] * DC_SIZE)), DC_SIZE) ) return -1; 47 | return (a + (DDI[0] * DC_SIZE)); 48 | } 49 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/src/build_g_stage_pt.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | #include "tables_api.h" 7 | uint64_t 8 | add_g_stage_pte ( 9 | iohgatp_t iohgatp, uint64_t gpa, gpte_t gpte, uint8_t add_level) { 10 | 11 | uint16_t vpn[5]; 12 | uint64_t a; 13 | uint8_t i, PTESIZE, LEVELS; 14 | gpte_t nl_gpte; 15 | 16 | PTESIZE = 8; 17 | if ( iohgatp.MODE == IOHGATP_Sv32x4 && g_reg_file.fctl.gxl == 1 ) { 18 | vpn[0] = get_bits(21, 12, gpa); 19 | vpn[1] = get_bits(34, 22, gpa); 20 | LEVELS = 2; 21 | PTESIZE = 4; 22 | } 23 | if ( iohgatp.MODE == IOHGATP_Sv39x4 && g_reg_file.fctl.gxl == 0) { 24 | vpn[0] = get_bits(20, 12, gpa); 25 | vpn[1] = get_bits(29, 21, gpa); 26 | vpn[2] = get_bits(40, 30, gpa); 27 | LEVELS = 3; 28 | } 29 | if ( iohgatp.MODE == IOHGATP_Sv48x4 ) { 30 | vpn[0] = get_bits(20, 12, gpa); 31 | vpn[1] = get_bits(29, 21, gpa); 32 | vpn[2] = get_bits(38, 30, gpa); 33 | vpn[3] = get_bits(49, 39, gpa); 34 | LEVELS = 4; 35 | } 36 | if ( iohgatp.MODE == IOHGATP_Sv57x4 ) { 37 | vpn[0] = get_bits(20, 12, gpa); 38 | vpn[1] = get_bits(29, 21, gpa); 39 | vpn[2] = get_bits(38, 30, gpa); 40 | vpn[3] = get_bits(47, 39, gpa); 41 | vpn[4] = get_bits(58, 48, gpa); 42 | LEVELS = 5; 43 | } 44 | i = LEVELS - 1; 45 | a = iohgatp.PPN * PAGESIZE; 46 | while ( i > add_level ) { 47 | nl_gpte.raw = 0; 48 | if ( read_memory_test((a | (vpn[i] * PTESIZE)), PTESIZE, (char *)&nl_gpte.raw) ) return -1; 49 | if ( nl_gpte.V == 0 ) { 50 | nl_gpte.V = 1; 51 | nl_gpte.PPN = get_free_ppn(1); 52 | if ( write_memory_test((char *)&nl_gpte.raw, (a | (vpn[i] * PTESIZE)), PTESIZE) ) return -1; 53 | } 54 | i = i - 1; 55 | if ( i < 0 ) return -1; 56 | a = nl_gpte.PPN * PAGESIZE; 57 | } 58 | if ( write_memory_test((char *)&gpte.raw, (a | (vpn[i] * PTESIZE)), PTESIZE) ) return -1; 59 | return (a | (vpn[i] * PTESIZE)); 60 | } 61 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/src/build_pdt.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | #include "tables_api.h" 7 | 8 | uint64_t 9 | add_process_context( 10 | device_context_t *DC, process_context_t *PC, uint32_t process_id) { 11 | uint64_t a; 12 | uint8_t i, LEVELS; 13 | pdte_t pdte; 14 | uint16_t PDI[3]; 15 | 16 | PDI[0] = get_bits(7, 0, process_id); 17 | PDI[1] = get_bits(16, 8, process_id); 18 | PDI[2] = get_bits(19, 17, process_id); 19 | 20 | if ( DC->fsc.pdtp.MODE == PD20 ) LEVELS = 3; 21 | if ( DC->fsc.pdtp.MODE == PD17 ) LEVELS = 2; 22 | if ( DC->fsc.pdtp.MODE == PD8 ) LEVELS = 1; 23 | 24 | a = DC->fsc.pdtp.PPN * PAGESIZE; 25 | i = LEVELS - 1; 26 | while ( i > 0 ) { 27 | if ( translate_gpa(DC->iohgatp, a, &a) == -1 ) return -1; 28 | pdte.raw = 0; 29 | if ( read_memory_test((a + (PDI[i] * 8)), 8, (char *)&pdte.raw) ) return -1; 30 | if ( pdte.V == 0 ) { 31 | pdte.V = 1; 32 | pdte.reserved0 = pdte.reserved1 = 0; 33 | if (DC->iohgatp.MODE != IOHGATP_Bare) { 34 | gpte_t gpte; 35 | 36 | pdte.PPN = get_free_gppn(1, DC->iohgatp); 37 | 38 | gpte.raw = 0; 39 | gpte.V = 1; 40 | gpte.R = 1; 41 | gpte.W = 0; 42 | gpte.X = 0; 43 | gpte.U = 1; 44 | gpte.G = 0; 45 | gpte.A = 0; 46 | gpte.D = 0; 47 | gpte.PBMT = PMA; 48 | gpte.PPN = get_free_ppn(1); 49 | 50 | if ( add_g_stage_pte(DC->iohgatp, (PAGESIZE * pdte.PPN), gpte, 0) == -1 ) return -1; 51 | } else { 52 | pdte.PPN = get_free_ppn(1); 53 | } 54 | if ( write_memory_test((char *)&pdte.raw, (a + (PDI[i] * 8)), 8) ) return -1; 55 | } 56 | i = i - 1; 57 | a = pdte.PPN * PAGESIZE; 58 | } 59 | if ( translate_gpa(DC->iohgatp, a, &a) == -1 ) return -1; 60 | if ( write_memory_test((char *)PC, (a + (PDI[0] * 16)), 16) ) return -1; 61 | return (a + (PDI[0] * 16)); 62 | } 63 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/src/build_s_stage_pt.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | #include "tables_api.h" 7 | uint64_t 8 | add_s_stage_pte ( 9 | iosatp_t satp, uint64_t va, pte_t pte, uint8_t add_level, uint8_t SXL) { 10 | 11 | uint16_t vpn[5]; 12 | uint64_t a; 13 | uint8_t i, PTESIZE, LEVELS; 14 | pte_t nl_pte; 15 | 16 | PTESIZE = 8; 17 | if ( satp.MODE == IOSATP_Sv32 && SXL == 1) { 18 | vpn[0] = get_bits(21, 12, va); 19 | vpn[1] = get_bits(31, 22, va); 20 | LEVELS = 2; 21 | PTESIZE = 4; 22 | } 23 | if ( satp.MODE == IOSATP_Sv39 && SXL == 0) { 24 | vpn[0] = get_bits(20, 12, va); 25 | vpn[1] = get_bits(29, 21, va); 26 | vpn[2] = get_bits(38, 30, va); 27 | LEVELS = 3; 28 | } 29 | if ( satp.MODE == IOSATP_Sv48 ) { 30 | vpn[0] = get_bits(20, 12, va); 31 | vpn[1] = get_bits(29, 21, va); 32 | vpn[2] = get_bits(38, 30, va); 33 | vpn[3] = get_bits(47, 39, va); 34 | LEVELS = 4; 35 | } 36 | if ( satp.MODE == IOSATP_Sv57 ) { 37 | vpn[0] = get_bits(20, 12, va); 38 | vpn[1] = get_bits(29, 21, va); 39 | vpn[2] = get_bits(38, 30, va); 40 | vpn[3] = get_bits(47, 39, va); 41 | vpn[4] = get_bits(56, 48, va); 42 | LEVELS = 5; 43 | } 44 | i = LEVELS - 1; 45 | a = satp.PPN * PAGESIZE; 46 | while ( i > add_level ) { 47 | nl_pte.raw = 0; 48 | if ( read_memory_test((a | (vpn[i] * PTESIZE)), PTESIZE, (char *)&nl_pte.raw)) return -1; 49 | if ( nl_pte.V == 0 ) { 50 | nl_pte.V = 1; 51 | nl_pte.PPN = get_free_ppn(1); 52 | if ( write_memory_test((char *)&nl_pte.raw, (a | (vpn[i] * PTESIZE)), PTESIZE) ) return -1; 53 | } 54 | i = i - 1; 55 | if ( i < 0 ) return -1; 56 | a = nl_pte.PPN * PAGESIZE; 57 | } 58 | if ( write_memory_test((char *)&pte.raw, (a | (vpn[i] * PTESIZE)), PTESIZE) ) return -1; 59 | return (a | (vpn[i] * PTESIZE)); 60 | } 61 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/src/build_vs_stage_pt.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | #include "tables_api.h" 7 | uint64_t 8 | add_vs_stage_pte ( 9 | iosatp_t satp, uint64_t va, pte_t pte, uint8_t add_level, 10 | iohgatp_t iohgatp, uint8_t SXL) { 11 | 12 | uint16_t vpn[5]; 13 | uint64_t a; 14 | uint8_t i, PTESIZE, LEVELS; 15 | pte_t nl_pte; 16 | 17 | PTESIZE = 8; 18 | if ( satp.MODE == IOSATP_Sv32 && SXL == 1) { 19 | vpn[0] = get_bits(21, 12, va); 20 | vpn[1] = get_bits(31, 22, va); 21 | LEVELS = 2; 22 | PTESIZE = 4; 23 | } 24 | if ( satp.MODE == IOSATP_Sv39 && SXL == 0) { 25 | vpn[0] = get_bits(20, 12, va); 26 | vpn[1] = get_bits(29, 21, va); 27 | vpn[2] = get_bits(38, 30, va); 28 | LEVELS = 3; 29 | } 30 | if ( satp.MODE == IOSATP_Sv48 ) { 31 | vpn[0] = get_bits(20, 12, va); 32 | vpn[1] = get_bits(29, 21, va); 33 | vpn[2] = get_bits(38, 30, va); 34 | vpn[3] = get_bits(47, 39, va); 35 | LEVELS = 4; 36 | } 37 | if ( satp.MODE == IOSATP_Sv57 ) { 38 | vpn[0] = get_bits(20, 12, va); 39 | vpn[1] = get_bits(29, 21, va); 40 | vpn[2] = get_bits(38, 30, va); 41 | vpn[3] = get_bits(47, 39, va); 42 | vpn[4] = get_bits(56, 48, va); 43 | LEVELS = 5; 44 | } 45 | i = LEVELS - 1; 46 | a = satp.PPN * PAGESIZE; 47 | while ( i > add_level ) { 48 | if ( translate_gpa(iohgatp, a, &a) == -1) return -1; 49 | nl_pte.raw = 0; 50 | if ( read_memory_test((a | (vpn[i] * PTESIZE)), PTESIZE, (char *)&nl_pte.raw)) return -1; 51 | if ( nl_pte.V == 0 ) { 52 | gpte_t gpte; 53 | 54 | nl_pte.V = 1; 55 | nl_pte.PPN = get_free_gppn(1, iohgatp); 56 | 57 | gpte.raw = 0; 58 | gpte.V = 1; 59 | gpte.R = 1; 60 | gpte.W = 1; 61 | gpte.X = 0; 62 | gpte.U = 1; 63 | gpte.G = 0; 64 | gpte.A = 0; 65 | gpte.D = 0; 66 | gpte.PBMT = PMA; 67 | gpte.PPN = get_free_ppn(1); 68 | 69 | if ( add_g_stage_pte(iohgatp, (PAGESIZE * nl_pte.PPN), gpte, 0) == -1) return -1; 70 | 71 | if ( write_memory_test((char *)&nl_pte.raw, (a | (vpn[i] * PTESIZE)), PTESIZE) ) return -1; 72 | } 73 | i = i - 1; 74 | if ( i < 0 ) return 1; 75 | a = nl_pte.PPN * PAGESIZE; 76 | } 77 | if ( translate_gpa(iohgatp, a, &a) == -1) return -1; 78 | if ( write_memory_test((char *)&pte.raw, (a | (vpn[i] * PTESIZE)), PTESIZE) ) return -1; 79 | return (a | (vpn[i] * PTESIZE)); 80 | } 81 | -------------------------------------------------------------------------------- /iommu_ref_model/libtables/src/translate_gpa.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | #include "iommu.h" 6 | uint64_t 7 | translate_gpa ( 8 | iohgatp_t iohgatp, uint64_t gpa, uint64_t *spa) { 9 | 10 | uint16_t vpn[5]; 11 | uint64_t a; 12 | uint8_t i, PTESIZE, LEVELS; 13 | gpte_t nl_gpte; 14 | uint64_t gst_page_sz; 15 | 16 | PTESIZE = 8; 17 | if ( iohgatp.MODE == IOHGATP_Bare ) { 18 | *spa = gpa; 19 | return -1; 20 | } 21 | if ( iohgatp.MODE == IOHGATP_Sv32x4 ) { 22 | vpn[0] = get_bits(21, 12, gpa); 23 | vpn[1] = get_bits(34, 22, gpa); 24 | LEVELS = 2; 25 | PTESIZE = 4; 26 | gst_page_sz = 4UL * 1024UL * 1024UL; 27 | } 28 | if ( iohgatp.MODE == IOHGATP_Sv39x4 ) { 29 | vpn[0] = get_bits(20, 12, gpa); 30 | vpn[1] = get_bits(29, 21, gpa); 31 | vpn[2] = get_bits(40, 30, gpa); 32 | gst_page_sz = 512UL * 512UL * PAGESIZE; 33 | LEVELS = 3; 34 | } 35 | if ( iohgatp.MODE == IOHGATP_Sv48x4 ) { 36 | vpn[0] = get_bits(20, 12, gpa); 37 | vpn[1] = get_bits(29, 21, gpa); 38 | vpn[2] = get_bits(38, 30, gpa); 39 | vpn[3] = get_bits(49, 39, gpa); 40 | gst_page_sz = 512UL * 512UL * 512UL * PAGESIZE; 41 | LEVELS = 4; 42 | } 43 | if ( iohgatp.MODE == IOHGATP_Sv57x4 ) { 44 | vpn[0] = get_bits(20, 12, gpa); 45 | vpn[1] = get_bits(29, 21, gpa); 46 | vpn[2] = get_bits(38, 30, gpa); 47 | vpn[3] = get_bits(47, 39, gpa); 48 | vpn[4] = get_bits(58, 48, gpa); 49 | gst_page_sz = 512UL * 512UL * 512UL * 512UL * PAGESIZE; 50 | LEVELS = 5; 51 | } 52 | i = LEVELS - 1; 53 | a = iohgatp.PPN * PAGESIZE; 54 | while ( 1 ) { 55 | nl_gpte.raw = 0; 56 | if ( read_memory_test((a | (vpn[i] * PTESIZE)), PTESIZE, (char *)&nl_gpte.raw) ) return -1; 57 | if ( nl_gpte.V == 0 ) return -1; 58 | if ( nl_gpte.R != 0 || nl_gpte.X != 0 ) { 59 | *spa = nl_gpte.PPN; 60 | *spa = *spa * PAGESIZE; 61 | *spa = *spa & ~(gst_page_sz - 1); 62 | *spa = *spa | (gpa & (gst_page_sz - 1)); 63 | return (a | (vpn[i] * PTESIZE)); 64 | } 65 | i = i - 1; 66 | if ( i < 0 ) return -1; 67 | gst_page_sz = ( i == 0 ) ? PAGESIZE : (gst_page_sz / 512); 68 | a = nl_gpte.PPN * PAGESIZE; 69 | } 70 | return -1; 71 | } 72 | -------------------------------------------------------------------------------- /iommu_ref_model/runtests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GREEN='\032' 3 | clear 4 | echo -e "\e[0m\e[4m \e[0m" 5 | echo -e "\e[32m\e[4m\e[1mRunning IOMMU test suite\e[0m" 6 | test/iommu 7 | echo -e "\e[0m\e[4m \e[0m" 8 | cd libiommu;gcovr -s;cd .. 9 | echo -e "\e[0m\e[4m \e[0m" 10 | -------------------------------------------------------------------------------- /iommu_ref_model/test/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -O0 -g -Wall -Werror -I../libiommu/include -I../libtables/include -lgcov --coverage 2 | CC := gcc 3 | SRCS_APP = test_app.c tbapi.c test_utils.c 4 | OBJ_APP = $(SRCS_APP:.c=.o) 5 | iommu: $(OBJ_APP) 6 | $(CC) -o $@ $^ $(CFLAGS) ../libiommu/libiommu.a ../libtables/libtables.a 7 | 8 | clean: 9 | $(RM) *.o *.a* tags log *.gc* src/*.gcov iommu 10 | -------------------------------------------------------------------------------- /iommu_ref_model/test/tbapi.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | 6 | #include 7 | #include 8 | #include "iommu.h" 9 | #include "tables_api.h" 10 | #include "test_app.h" 11 | 12 | uint8_t 13 | read_memory( 14 | uint64_t addr, uint8_t size, char *data, uint32_t rcid, uint32_t mcid){ 15 | if ( addr == access_viol_addr ) return ACCESS_FAULT; 16 | if ( addr == data_corruption_addr ) return DATA_CORRUPTION; 17 | memcpy(data, &memory[addr], size); 18 | return 0; 19 | } 20 | uint8_t 21 | read_memory_test( 22 | uint64_t addr, uint8_t size, char *data) { 23 | return read_memory(addr, size, data, 0, 0); 24 | } 25 | uint8_t 26 | read_memory_for_AMO( 27 | uint64_t addr, uint8_t size, char *data, uint32_t rcid, uint32_t mcid) { 28 | // Same for now 29 | return read_memory(addr, size, data, rcid, mcid); 30 | } 31 | 32 | uint8_t 33 | write_memory( 34 | char *data, uint64_t addr, uint32_t size, uint32_t rcid, uint32_t mcid) { 35 | if ( addr == access_viol_addr ) return ACCESS_FAULT; 36 | if ( addr == data_corruption_addr ) return DATA_CORRUPTION; 37 | memcpy(&memory[addr], data, size); 38 | return 0; 39 | } 40 | uint8_t 41 | write_memory_test( 42 | char *data, uint64_t addr, uint32_t size) { 43 | return write_memory(data, addr, size, 0, 0); 44 | } 45 | 46 | 47 | void 48 | iommu_to_hb_do_global_observability_sync( 49 | uint8_t PR, uint8_t PW){ 50 | pr_go_requested = PR; 51 | pw_go_requested = PW; 52 | return; 53 | } 54 | 55 | void 56 | send_msg_iommu_to_hb( 57 | ats_msg_t *msg) { 58 | if ( exp_msg.MSGCODE != msg->MSGCODE || 59 | exp_msg.TAG != msg->TAG || 60 | exp_msg.RID != msg->RID || 61 | exp_msg.PV != msg->PV || 62 | exp_msg.PID != msg->PID || 63 | exp_msg.PRIV != msg->PRIV || 64 | exp_msg.EXEC_REQ != msg->EXEC_REQ || 65 | exp_msg.DSV != msg->DSV || 66 | exp_msg.DSEG != msg->DSEG || 67 | exp_msg.PAYLOAD != msg->PAYLOAD ) 68 | exp_msg_received = 0; 69 | else 70 | exp_msg_received = 1; 71 | message_received = 1; 72 | memcpy(&rcvd_msg, msg, sizeof(ats_msg_t)); 73 | return; 74 | } 75 | 76 | void 77 | get_attribs_from_req( 78 | hb_to_iommu_req_t *req, uint8_t *read, uint8_t *write, uint8_t *exec, uint8_t *priv) { 79 | 80 | *read = ( req->tr.read_writeAMO == READ ) ? 1 : 0; 81 | *write = ( req->tr.read_writeAMO == WRITE ) ? 1 : 0; 82 | 83 | // The No Write flag, when Set, indicates that the Function is requesting read-only 84 | // access for this translation. 85 | // The TA (IOMMU) may ignore the No Write Flag, however, if the TA responds with a 86 | // translation marked as read-only then the Function must not issue Memory Write 87 | // transactions using that translation. In this case, the Function may issue another 88 | // translation request with the No Write flag Clear, which may result in a new 89 | // translation completion with or without the W (Write) bit Set. 90 | // Upon receiving a Translation Request with the NW flag Clear, TAs are permitted to 91 | // mark the associated pages dirty. Functions MUST not issue such Requests 92 | // unless they have been given explicit write permission. 93 | // Note ATS Translation requests are read - so read_writeAMO is READ for these requests 94 | *write = ( (req->tr.at == ADDR_TYPE_PCIE_ATS_TRANSLATION_REQUEST) && 95 | (req->no_write == 0) ) ? 1 : *write; 96 | 97 | // If a Translation Request has a PASID, the Untranslated Address Field is an address 98 | // within the process address space indicated by the PASID field. 99 | // If a Translation Request has a PASID with either the Privileged Mode Requested 100 | // or Execute Requested bit Set, these may be used in constructing the Translation 101 | // Completion Data Entry. The PASID Extended Capability indicates whether a Function 102 | // supports and is enabled to send and receive TLPs with the PASID. 103 | *exec = ( (*read && req->exec_req && 104 | (req->tr.at == ADDR_TYPE_UNTRANSLATED || req->pid_valid)) ) ? 1 : 0; 105 | *priv = ( req->pid_valid && req->priv_req ) ? S_MODE : U_MODE; 106 | return; 107 | } 108 | -------------------------------------------------------------------------------- /iommu_ref_model/test/test_app.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 by Rivos Inc. 2 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. 3 | // SPDX-License-Identifier: Apache-2.0 4 | // Author: ved@rivosinc.com 5 | 6 | // Global functions 7 | extern int8_t reset_system(uint8_t mem_gb, uint16_t num_vms); 8 | extern int8_t enable_cq(uint32_t nppn); 9 | extern int8_t enable_fq(uint32_t nppn); 10 | extern int8_t enable_disable_pq(uint32_t nppn, uint8_t enable_disable); 11 | extern int8_t enable_iommu(uint8_t iommu_mode); 12 | extern void iodir(uint8_t f3, uint8_t DV, uint32_t DID, uint32_t PID); 13 | extern void iotinval( uint8_t f3, uint8_t GV, uint8_t AV, uint8_t PSCV, uint32_t GSCID, uint32_t PSCID, uint64_t address); 14 | extern void iofence(uint8_t f3, uint8_t PR, uint8_t PW, uint8_t AV, uint8_t WSI_bit, uint64_t addr, uint32_t data); 15 | extern void ats_command( uint8_t f3, uint8_t DSV, uint8_t PV, uint32_t PID, uint8_t DSEG, uint16_t RID, uint64_t payload); 16 | extern void generic_any(command_t cmd); 17 | extern void send_translation_request(uint32_t did, uint8_t pid_valid, uint32_t pid, uint8_t no_write, 18 | uint8_t exec_req, uint8_t priv_req, uint8_t is_cxl_dev, addr_type_t at, uint64_t iova, 19 | uint32_t length, uint8_t read_writeAMO, 20 | hb_to_iommu_req_t *req, iommu_to_hb_rsp_t *rsp); 21 | extern int8_t check_rsp_and_faults(hb_to_iommu_req_t *req, iommu_to_hb_rsp_t *rsp, status_t status, 22 | uint16_t cause, uint64_t exp_iotval2); 23 | extern int8_t check_faults(uint16_t cause, uint8_t exp_PV, uint32_t exp_PID, 24 | uint8_t exp_PRIV, uint32_t exp_DID, uint64_t exp_iotval, uint8_t ttyp, uint64_t exp_iotval2); 25 | extern int8_t check_exp_pq_rec(uint32_t DID, uint32_t PID, uint8_t PV, uint8_t PRIV, uint8_t EXEC, 26 | uint16_t reserved0, uint8_t reserved1, uint64_t PAYLOAD); 27 | extern uint64_t get_free_gppn(uint64_t num_gppn, iohgatp_t iohgatp); 28 | extern uint64_t add_device(uint32_t device_id, uint32_t gscid, uint8_t en_ats, uint8_t en_pri, uint8_t t2gpa, 29 | uint8_t dtf, uint8_t prpr, 30 | uint8_t gade, uint8_t sade, uint8_t dpe, uint8_t sbe, uint8_t sxl, 31 | uint8_t iohgatp_mode, uint8_t iosatp_mode, uint8_t pdt_mode, 32 | uint8_t msiptp_mode, uint8_t msiptp_pages, uint64_t msi_addr_mask, 33 | uint64_t msi_addr_pattern); 34 | 35 | // Global variables 36 | extern ats_msg_t exp_msg; 37 | extern ats_msg_t rcvd_msg; 38 | extern uint8_t exp_msg_received; 39 | extern uint8_t message_received; 40 | extern int8_t *memory; 41 | extern uint64_t access_viol_addr; 42 | extern uint64_t data_corruption_addr; 43 | extern uint8_t pr_go_requested; 44 | extern uint8_t pw_go_requested; 45 | extern uint64_t next_free_page; 46 | extern uint64_t next_free_gpage[65536]; 47 | 48 | // Macros 49 | #define FOR_ALL_TRANSACTION_TYPES(at, pid_valid, exec_req, priv_req, no_write, code)\ 50 | for ( at = 0; at < 3; at++ ) {\ 51 | for ( pid_valid = 0; pid_valid < 2; pid_valid++ ) {\ 52 | for ( exec_req = 0; exec_req < 2; exec_req++ ) {\ 53 | for ( priv_req = 0; priv_req < 2; priv_req++ ) {\ 54 | for ( no_write = 0; no_write < 2; no_write++ ) {\ 55 | code\ 56 | }\ 57 | }\ 58 | }\ 59 | }\ 60 | } 61 | 62 | #define START_TEST(__STR__)\ 63 | test_num++;\ 64 | printf("Test %02d : %-40s : ", test_num, __STR__); 65 | #define fail_if(__COND__) if __COND__ {printf("\x1B[31mFAIL. Line %d\x1B[0m\n", __LINE__); return -1;} 66 | #define END_TEST() {printf("\x1B[32mPASS\x1B[0m\n");} 67 | -------------------------------------------------------------------------------- /src/bibliography.adoc: -------------------------------------------------------------------------------- 1 | [bibliography] 2 | == Bibliography 3 | 4 | bibliography::[] 5 | -------------------------------------------------------------------------------- /src/contributors.adoc: -------------------------------------------------------------------------------- 1 | == Contributors 2 | 3 | This RISC-V specification has been contributed to directly or indirectly by (in alphabetical order): 4 | 5 | [%hardbreaks] 6 | Aaron Durbin, Allen Baum, Anup Patel, Daniel Gracia Pérez, David Kruckemyer, Greg Favor, Ahmad Fawal, Guerney D Hunt, John Hauser, Josh Scheid, Matt Evans, Manuel Rodriguez, Nick Kossifidis, Paul Donahue, Paul Walmsley, Perrine Peresse, Philipp Tomsich, Rieul Ducousso, Scott Nelson, Siqi Zhao, Sunil V.L, Tomasz Jeznach, Vassilis Papaefstathiou, Vedvyas Shanbhogue 7 | -------------------------------------------------------------------------------- /src/images/ddt-base.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/images/ddt-ext.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/images/hypervisor.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/images/msi-imsic.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/index.adoc: -------------------------------------------------------------------------------- 1 | [index] 2 | == Index 3 | -------------------------------------------------------------------------------- /src/iommu.bib: -------------------------------------------------------------------------------- 1 | @electronic{PRIV, 2 | title = {RISC-V Instruction Set Manual, Volume II: Privileged Architecture}, 3 | url = {https://github.com/riscv/riscv-isa-manual}, 4 | year = {} 5 | } 6 | @electronic{PCI, 7 | title = {PCI Express® Base Specification Revision 6.0}, 8 | url = {https://pcisig.com/pci-express-6.0-specification}, 9 | year = {} 10 | } 11 | @electronic{PCI-CLS, 12 | title = {PCI Code and ID Assignment Specification Revision 1.1}, 13 | url = {https://pcisig.com/sites/default/files/files/PCI_Code-ID_r_1_11__v24_Jan_2019.pdf}, 14 | year = {} 15 | } 16 | @electronic{AIA, 17 | title = {RISC-V Advanced Interrupt Architecture}, 18 | url = {https://github.com/riscv/riscv-aia} 19 | } 20 | @electronic{CFI, 21 | title = {RISC-V Shadow Stacks and Landing Pads}, 22 | url = {https://github.com/riscv/riscv-cfi} 23 | } 24 | @electronic{PR243, 25 | title = {Clarification updates to IOMMU v1.0.0}, 26 | url = {https://github.com/riscv-non-isa/riscv-iommu/pull/243/commits} 27 | } 28 | @electronic{CBQRI, 29 | title = {RISC-V Capacity and Bandwidth QoS Register Interface}, 30 | url = {https://github.com/riscv-non-isa/riscv-cbqri} 31 | } 32 | @article{PTCAMP, 33 | author = {Du Bois, Kristof and Eyerman, Stijn and Eeckhout, Lieven}, 34 | title = {Per-Thread Cycle Accounting in Multicore Processors}, 35 | year = {2013}, 36 | issue_date = {January 2013}, 37 | publisher = {Association for Computing Machinery}, 38 | address = {New York, NY, USA}, 39 | volume = {9}, 40 | number = {4}, 41 | issn = {1544-3566}, 42 | url = {https://doi.org/10.1145/2400682.2400688}, 43 | doi = {10.1145/2400682.2400688}, 44 | journal = {ACM Trans. Archit. Code Optim.}, 45 | month = {jan}, 46 | articleno = {29}, 47 | numpages = {22}, 48 | } 49 | @inproceedings{HERACLES, 50 | author = {Lo, David and Cheng, Liqun and Govindaraju, Rama and Ranganathan, Parthasarathy and Kozyrakis, Christos}, 51 | title = {Heracles: Improving Resource Efficiency at Scale}, 52 | year = {2015}, 53 | isbn = {9781450334020}, 54 | publisher = {Association for Computing Machinery}, 55 | address = {New York, NY, USA}, 56 | url = {https://doi.org/10.1145/2749469.2749475}, 57 | doi = {10.1145/2749469.2749475}, 58 | booktitle = {Proceedings of the 42nd Annual International Symposium on Computer Architecture}, 59 | pages = {450–462}, 60 | numpages = {13}, 61 | location = {Portland, Oregon}, 62 | series = {ISCA '15} 63 | } 64 | @electronic{SSQOSID, 65 | title = {RISC-V Quality-of-Service (QoS) Identifiers}, 66 | url = {https://github.com/riscv/riscv-ssqosid} 67 | } 68 | -------------------------------------------------------------------------------- /src/iommu_debug.adoc: -------------------------------------------------------------------------------- 1 | [[debug]] 2 | 3 | == Debug support 4 | To support software debug, the IOMMU may provide an optional register interface 5 | that may be used by software to request IOMMU to perform an address translation. 6 | The IOMMU supports this capability when `capabilities.DBG` is 1. The interface 7 | consists of two set of registers; translation-request registers that are used by 8 | software to program an IOVA and other inputs needed by the process to 9 | translate an IOVA (<>) as an Untranslated Request. The result of the 10 | translation, if the process completes successfully, is reported through the 11 | translation-response registers. If the process stops due to faults then the 12 | faults are reported normally in the fault-queue and the translation-response 13 | registers updated with a failure indicator. If the IOVA is determined to be 14 | that of a virtual interrupt file (<>) and the corresponding MSI PTE is 15 | in MRIF mode, then the process stops and reports a "Transaction type disallowed" 16 | (cause = 260) fault. 17 | 18 | When the process to translate an IOVA is invoked for this purpose, the IOMMU 19 | may or may not cache first-stage PTEs, second-stage PTEs, DDT entries, PDT entries, or 20 | MSI PTEs accessed for the translation process in the IOATC. The IOMMU is allowed 21 | to use any PTEs or directory structure entries that may already be cached in the 22 | IOATC. The IOMMU may update the Accessed (A) and/or Dirty (D) bits in the PTEs 23 | used for the translation process if supported by the IOMMU. When the IOMMU 24 | implements a HPM, the HPM counters may be updated normally by the IOMMU. For the 25 | purpose of counting in the HPM, these requests are treated as Untranslated 26 | Requests. 27 | 28 | The translation-request interface consists of the following 64-bit WARL registers: 29 | 30 | * `tr_req_iova` (<>) 31 | * `tr_req_ctl` (<>) 32 | 33 | The translation-response interface consists of a single 64-bit RO register 34 | `tr_response` (<>) 35 | 36 | To request a translation, the `tr_req_iova` register is written first with 37 | the desired IOVA and the `tr_req_ctl` register is written next. The 'Go/Busy` 38 | bit is set in `tr_req_ctl` to indicate a valid request in the registers. The 39 | `Go/Busy` bit is a read-write-sticky (RWS) bit that once set cannot be cleared 40 | by writing the register. The `Go/Busy` bit will be cleared to 0 by the IOMMU 41 | when the process completes (successfully or due to encountering a fault). When 42 | the `Go/Busy` bit goes from 1 to 0, a response is valid in the `tr_response` 43 | register. 44 | 45 | When the `Go/Busy` bit is 1, the IOMMU behavior is `UNSPECIFIED` if: 46 | 47 | * The `tr_req_iova` or `tr_req_ctl` are modified. 48 | * IOMMU configurations, such as `ddtp.iommu_mode`, are modified. 49 | 50 | The time to complete a translation request through this debug interface is 51 | `UNSPECIFIED` but is required to be finite. If the IOMMU is serving translation 52 | requests from the IO bridge when a request is made through this register 53 | interface then the time to complete the request may be longer than when the 54 | IOMMU is otherwise idle. 55 | 56 | [NOTE] 57 | ==== 58 | The debug interface is optional but recommended to be implemented to aid 59 | software debug and to implement architectural compliance tests. 60 | ==== 61 | -------------------------------------------------------------------------------- /src/iommu_extensions.adoc: -------------------------------------------------------------------------------- 1 | [[extensions]] 2 | 3 | == IOMMU Extensions 4 | 5 | This chapter specifies the following standard extensions to the IOMMU Base 6 | Architecture: 7 | 8 | [%autowidth,float="center",align="center",cols="^,^,^",options="header",] 9 | |=== 10 | | Specification |Version |Status 11 | | <> 12 | |*1.0* 13 | |*Ratified* 14 | |=== 15 | 16 | [[QOSID]] 17 | === Quality-of-Service (QoS) Identifiers Extension, Version 1.0 18 | 19 | Quality of Service (QoS) is defined as the minimal end-to-end performance 20 | guaranteed in advance by a service level agreement (SLA) to a workload. 21 | Performance metrics might include measures such as instructions per cycle (IPC), 22 | latency of service, etc. 23 | 24 | When multiple workloads execute concurrently on modern processors -- equipped 25 | with large core counts, multiple cache hierarchies, and multiple memory 26 | controllers -- the performance of any given workload becomes less 27 | deterministic, or even non-deterministic, due to shared resource contention 28 | cite:[PTCAMP]. 29 | 30 | To manage performance variability, system software needs resource allocation 31 | and monitoring capabilities. These capabilities allow for the reservation of 32 | resources like cache and bandwidth, thus meeting individual performance targets 33 | while minimizing interference cite:[HERACLES]. For resource management, hardware 34 | should provide monitoring features that allow system software to profile 35 | workload resource consumption and allocate resources accordingly. 36 | 37 | To facilitate this, the QoS Identifiers ISA extension (Ssqosid) cite:[SSQOSID] 38 | introduces the `srmcfg` register, which configures a hart with two identifiers: 39 | a Resource Control ID (`RCID`) and a Monitoring Counter ID (`MCID`). These 40 | identifiers accompany each request issued by the hart to shared resource 41 | controllers. 42 | 43 | These identifiers are crucial for the RISC-V Capacity and Bandwidth Controller 44 | QoS Register Interface cite:[CBQRI], which provides methods for setting resource 45 | usage limits and monitoring resource consumption. The `RCID` controls resource 46 | allocations, while the `MCID` is used for tracking resource usage. 47 | 48 | The IOMMU QoS ID extension provides a method to associate QoS IDs with requests 49 | to access resources by the IOMMU, as well as with devices governed by it. This 50 | complements the Ssqosid extension that provides a method to associate QoS IDs 51 | with requests originated by the RISC-V harts. Assocating QoS IDs with device 52 | and IOMMU originated requests is required for effective monitoring and 53 | allocation of shared resources. 54 | 55 | The IOMMU `capabilities` register (<>) is extended with a `QOSID` field 56 | which enumerates support for associating QoS IDs with requests made through the 57 | IOMMU. When `capabilities.QOSID` is 1, the memory-mapped register layout is 58 | extended to add a register named `iommu_qosid` (<>). This register is 59 | used to configure the Quality of Service (QoS) IDs associated with 60 | IOMMU-originated requests. The `ta` field of the device context (<>) is 61 | extended with two fields, `RCID` and `MCID`, to configure the QoS IDs to 62 | associate with requests originated by the devices. 63 | 64 | ==== Reset Behavior 65 | 66 | If the reset value for `ddtp.iommu_mode` field is `Bare`, then the 67 | `iommu_qosid.RCID` field must have a reset value of 0. 68 | 69 | [NOTE] 70 | ==== 71 | At reset, it is required that the `RCID` field of `iommu_qosid` is set to 0 if 72 | the IOMMU is in `Bare` mode, as typically the resource controllers in the 73 | SoC default to a reset behavior of associating all capacity or bandwidth to the 74 | `RCID` value of 0. When the reset value of the `ddtp.iommu_mode` is not `Bare`, 75 | the `iommu_qosid` register should be initialized by software before changing 76 | the mode to allow DMA. 77 | ==== 78 | 79 | ==== Sizing QoS Identifiers 80 | 81 | The size (or width) of `RCID` and `MCID`, as fields in registers or in data 82 | structures, supported by the IOMMU must be at least as large as that supported 83 | by any RISC-V application processor hart in the system. 84 | 85 | ==== IOMMU ATC Capacity Allocation and Monitoring 86 | 87 | Some IOMMUs might support capacity allocation and usage monitoring in the IOMMU 88 | address translation cache (IOATC) by implementing the capacity controller 89 | register interface. 90 | 91 | Additionally, some IOMMUs might support multiple IOATCs, each potentially having 92 | different capacities. In scenarios where multiple IOATCs are implemented, such 93 | as an IOATC for each supported page size, the IOMMU can implement a capacity 94 | controller register interface for each IOATC to facilitate individual capacity 95 | allocation. 96 | -------------------------------------------------------------------------------- /src/iommu_hw_guidelines.adoc: -------------------------------------------------------------------------------- 1 | [[hw_guidelines]] 2 | 3 | == Hardware guidelines 4 | This section provides guidelines to the system/hardware integrator of the 5 | IOMMU in the platform. 6 | 7 | === Integrating an IOMMU as a PCIe device 8 | The IOMMU may be constructed as a PCIe device itself and be discoverable 9 | as a dedicated PCIe function with PCIe defined Base Class 08h, Sub-Class 06h, 10 | and Programming Interface 00h cite:[PCI-CLS]. 11 | 12 | Such IOMMU must map the IOMMU registers defined in this specification as PCIe 13 | BAR mapped registers. 14 | 15 | The IOMMU may support MSI or MSI-X or both. When MSI-X is supported, the MSI-X 16 | capability block must point to the `msi_cfg_tbl` in BAR mapped registers such that 17 | system software can configure MSI address and data pairs for each message 18 | supported by the IOMMU. The MSI-X PBA may be located in the same BAR or 19 | another BAR of the IOMMU. The IOMMU is recommended to support MSI-X capability. 20 | 21 | === Faults from PMA and PMP 22 | The IO bridge may invoke a PMA and/or a PMP checker on memory accesses from 23 | IO devices or those generated by the IOMMU implicitly to access the in-memory 24 | data structures. When a memory access violates a PMA check or violates a PMP 25 | check, the IO bridge may abort the memory access as specified in 26 | <>. 27 | 28 | [[IOBR_FAULT_RESP]] 29 | === Aborting transactions 30 | If the aborted transaction is an IOMMU-initiated implicit memory access then the 31 | IO bridge signals such access faults to the IOMMU itself. The details of such 32 | signaling is implementation defined. 33 | 34 | If the aborted transaction is a write then the IO bridge may discard the write; 35 | the details of how the write is discarded are implementation defined. If the IO 36 | protocol requires a response for write transactions (e.g., AXI) then a response 37 | as defined by the IO protocol may be generated by the IO bridge (e.g., SLVERR on 38 | BRESP - Write Response channel). For PCIe, for example, write transactions are 39 | posted and no response is returned when a write transaction is discarded. 40 | 41 | If the faulting transaction is a read then the device expects a completion. The 42 | IO bridge may provide a completion to the device. The data, if returned, in such 43 | completion is implementation defined; usually it is a fixed value such as all 0 44 | or all 1. A status code may be returned to the device in the completion to 45 | indicate this condition. For AXI, for example, the completion status is provided 46 | by SLVERR on RRESP (Read Data channel). For PCIe, for example, the completion 47 | status field may be set to "Unsupported Request" (UR) or "Completer Abort" (CA). 48 | 49 | [[RAS]] 50 | === Reliability, Availability, and Serviceability (RAS) 51 | The IOMMU may support a RAS architecture that specifies the methods for 52 | enabling error detection, logging the detected errors (including their severity, 53 | nature, and location), and configuring means to report the error to an error 54 | handler. 55 | 56 | <<< 57 | 58 | Some errors, such as those in the IOATC, may be correctable by reloading the 59 | cached in-memory data structures when the error is detected. Such errors are not 60 | expected to affect the functioning of the IOMMU. 61 | 62 | Some errors may corrupt critical internal state of the IOMMU and such errors may 63 | lead the IOMMU to a failed state. Examples of such state may include registers 64 | such as the `ddtp`, `cqb`, etc. On entering such a failed state, the IOMMU may 65 | request the IO bridge to abort all incoming transactions. 66 | 67 | Some errors, such as corruptions that occur within the internal data paths of 68 | the IOMMU, may not be correctable but the effects of such errors may be contained 69 | to the transaction being processed by the IOMMU. 70 | 71 | As part of processing a transaction, the IOMMU may need to read data from 72 | in-memory data structures such as the DDT, PDT, or first/second-stage page tables. 73 | The provider (a memory controller or a cache) of the data may detect that the 74 | data requested has an uncorrectable error and signal that the data is corrupted 75 | and defer the error to the IOMMU. Such technique to defer the handling of the 76 | corrupted data to the consumer of the data is also commonly known as data 77 | poisoning. The effects of such errors may be contained to the transaction that 78 | caused the corrupted data to be accessed. 79 | 80 | In the cases where the error affects the transaction being processed but 81 | otherwise allows the IOMMU to continue providing service, the IOMMU may abort 82 | (see <>) the transaction and report the the fault by queuing 83 | a fault record in the `FQ`. For PCIe, for example, a "Completer Abort (CA)" 84 | response is appropriate to abort the transaction. The following cause codes are 85 | used to report such faulting transactions: 86 | 87 | * DDT data corruption (cause = 268) 88 | * PDT data corruption (cause = 269) 89 | * MSI PT data corruption (cause = 270) 90 | * MSI MRIF data corruption (cause = 271) 91 | * Internal data-path error (cause = 272) 92 | * First/second-stage PT data corruption (cause = 274) 93 | 94 | If the IO bridge is not capable of signaling such deferred errors uniquely 95 | from other errors that prevent the IOMMU from accessing in-memory data 96 | structures then the IOMMU may report such errors as access faults instead 97 | of using the differentiated data corruption cause codes. 98 | -------------------------------------------------------------------------------- /src/iommu_preface.adoc: -------------------------------------------------------------------------------- 1 | == Preface 2 | 3 | [.big]*_Preface to Version 20240901_* 4 | 5 | Chapters 2 through 8 of this document form the RISC-V IOMMU Base Architecture 6 | Specification. Chapter 9 includes the standard extensions to the base 7 | architecture. This release, version 20240901, contains the following versions 8 | of the RISC-V IOMMU Base Architecture specification and standard extensions: 9 | 10 | [%autowidth,float="center",align="center",cols="^,^,^",options="header",] 11 | |=== 12 | | Specification |Version |Status 13 | |*RISC-V IOMMU Base Architecture specification* + 14 | *Quality-of-Service (QoS) Identifiers Extension* 15 | |*1.0* + 16 | *1.0* 17 | |*Ratified* + 18 | *Ratified* 19 | |=== 20 | 21 | The following backward-compatible changes, comprising a set of clarifications 22 | and corrections, have been made since version 1.0.0: 23 | 24 | * A set of typographic errors and editorial updates were made. 25 | * Translations cached, if any, in `Bare` mode do not require invalidation. 26 | * Clarified that memory faults encountered by commands also set the `cqmf` flag. 27 | * Values tested by algorithms in SW Guidelines are before modifications made by 28 | the algorithms. 29 | * Included SW guidelines for modifying non-leaf PDT entries. 30 | * Clarified the behavior for in-flight transactions observed at the time of `ddtp` 31 | write operations. 32 | * Clarified the behavior when `IOTINVAL` is invoked with an invalid address. 33 | * Stated that faults leading to UR/CA ATS responses are reported in the Fault Queue. 34 | * Added a detailed description of the `capabilities.PAS` field. 35 | * SW guidelines for changing IOMMU modes and programming `tr_req_ctl` and HPM 36 | counters. 37 | * PCIe ATS Translation Resp. grants execute permission only if requested. 38 | * Clarified the handling of hardware implementations that internally split 39 | 8-byte transactions. 40 | * Shadow stack encodings introduced by Zicfiss are reserved for IOMMU use. 41 | * Listed the fault codes reported for faults detected by Page Request. 42 | * Updated Fig 31 to remove the unused Destination ID field for ATS.PRGR 43 | * Included a software guideline for IOMMU emulation. 44 | 45 | These changes were made through PR#243 cite:[PR243]. 46 | 47 | [.big]*_Preface to Version 1.0.0_* 48 | 49 | * Ratified version of the RISC-V IOMMU Architecture Specification. 50 | -------------------------------------------------------------------------------- /src/riscv-iommu.adoc: -------------------------------------------------------------------------------- 1 | = RISC-V IOMMU Architecture Specification 2 | include::../docs-resources/global-config.adoc[] 3 | :docgroup: IOMMU Task Group 4 | :description: RISC-V IOMMU Architecture Specification 5 | :revdate: 06/2023 6 | :revnumber: 1.0 7 | :revremark: This document is Ratified. See http://riscv.org/spec-state for details. 8 | :preface-title: Preamble 9 | :colophon: 10 | :appendix-caption: Appendix 11 | :title-logo-image: image:../docs-resources/images/risc-v_logo.png["RISC-V International Logo",pdfwidth=3.25in,align=center] 12 | // Settings: 13 | :experimental: 14 | :reproducible: 15 | :imagesoutdir: images 16 | :bibtex-file: src/iommu.bib 17 | :bibtex-order: appearance 18 | :bibtex-style: ieee 19 | :icons: font 20 | :lang: en 21 | :listing-caption: Listing 22 | :sectnums: 23 | :sectnumlevels: 5 24 | :toc: left 25 | :toclevels: 5 26 | :source-highlighter: pygments 27 | ifdef::backend-pdf[] 28 | :source-highlighter: coderay 29 | endif::[] 30 | :data-uri: 31 | :hide-uri-scheme: 32 | :stem: latexmath 33 | :footnote: 34 | :xrefstyle: short 35 | 36 | // Preamble - Begin 37 | //[preface] 38 | //== List of figures 39 | //list-of::image[hide_empty_section=true, enhanced_rendering=true] 40 | 41 | //[preface] 42 | //== List of tables 43 | //list-of::table[hide_empty_section=true, enhanced_rendering=true] 44 | 45 | //[preface] 46 | //== List of listings 47 | //list-of::listing[hide_empty_section=true, enhanced_rendering=true] 48 | 49 | [WARNING] 50 | .This document is link:http://riscv.org/spec-state[Ratified]. 51 | ==== 52 | No changes are allowed. Any desired or needed changes can be the subject of a 53 | follow-on new extension. Ratified extensions are never revised. 54 | ==== 55 | 56 | [preface] 57 | == Copyright and license information 58 | This specification is licensed under the Creative Commons 59 | Attribution 4.0 International License (CC-BY 4.0). The full 60 | license text is available at 61 | https://creativecommons.org/licenses/by/4.0/. 62 | 63 | Copyright 2023 by RISC-V International. 64 | 65 | [preface] 66 | include::contributors.adoc[] 67 | // Preamble - End 68 | 69 | include::iommu_preface.adoc[] 70 | include::iommu_intro.adoc[] 71 | include::iommu_data_structures.adoc[] 72 | include::iommu_in_memory_queues.adoc[] 73 | include::iommu_debug.adoc[] 74 | include::iommu_registers.adoc[] 75 | include::iommu_sw_guidelines.adoc[] 76 | include::iommu_hw_guidelines.adoc[] 77 | include::iommu_extensions.adoc[] 78 | include::bibliography.adoc[] 79 | --------------------------------------------------------------------------------