├── .cargo └── config.toml ├── .gitignore ├── .vscode └── settings.json ├── aarch64-esr-web ├── .gitignore ├── static │ ├── google2d2782625f584348.html │ ├── logo.png │ ├── logo-192.png │ ├── screenshot-desktop.png │ ├── screenshot-mobile.png │ ├── style.css │ ├── midr.html │ ├── index.html │ ├── smccc.html │ ├── app.webmanifest │ └── logo.svg ├── README.md ├── webpack.config.js ├── package.json ├── Cargo.toml ├── js │ └── index.js ├── src │ └── lib.rs └── Cargo.lock ├── .github ├── dependabot.yml └── workflows │ ├── web.yml │ ├── rust.yml │ └── package.yml ├── arm-sysregs-json ├── Cargo.toml ├── README.md ├── examples │ └── parse_json.rs └── src │ └── lib.rs ├── arm-sysregs-xml ├── Cargo.toml ├── README.md ├── examples │ └── generate_decoder.rs └── src │ └── lib.rs ├── Cargo.toml ├── src ├── smccc │ ├── tapp.rs │ ├── hyp.rs │ ├── common.rs │ ├── arm.rs │ ├── secure.rs │ └── ffa.rs ├── esr │ ├── common.rs │ ├── bti.rs │ ├── hvc.rs │ ├── ld64b.rs │ ├── sve.rs │ ├── pauth.rs │ ├── wf.rs │ ├── mcr.rs │ ├── ldc.rs │ ├── serror.rs │ ├── fp.rs │ ├── breakpoint.rs │ ├── mod.rs │ ├── abort.rs │ └── tests.rs ├── midr.rs ├── main.rs ├── smccc.rs └── lib.rs ├── CONTRIBUTING.md ├── CHANGELOG.md ├── README.md ├── Cargo.lock └── LICENSE /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } -------------------------------------------------------------------------------- /aarch64-esr-web/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | /target 4 | /pkg 5 | /wasm-pack.log 6 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/google2d2782625f584348.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google2d2782625f584348.html -------------------------------------------------------------------------------- /aarch64-esr-web/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/aarch64-esr-decoder/main/aarch64-esr-web/static/logo.png -------------------------------------------------------------------------------- /aarch64-esr-web/static/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/aarch64-esr-decoder/main/aarch64-esr-web/static/logo-192.png -------------------------------------------------------------------------------- /aarch64-esr-web/static/screenshot-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/aarch64-esr-decoder/main/aarch64-esr-web/static/screenshot-desktop.png -------------------------------------------------------------------------------- /aarch64-esr-web/static/screenshot-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/aarch64-esr-decoder/main/aarch64-esr-web/static/screenshot-mobile.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "cargo" 8 | directory: "/aarch64-esr-web" 9 | schedule: 10 | interval: "weekly" 11 | - package-ecosystem: "npm" 12 | directory: "/aarch64-esr-web" 13 | schedule: 14 | interval: "weekly" 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "weekly" 19 | -------------------------------------------------------------------------------- /arm-sysregs-json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arm-sysregs-json" 3 | version = "0.3.0" 4 | authors = ["Andrew Walbran "] 5 | edition = "2024" 6 | license = "Apache-2.0" 7 | description = "A library for parsing Arm system register JSON files." 8 | repository = "https://github.com/google/aarch64-esr-decoder/" 9 | keywords = ["aarch64"] 10 | categories = ["development-tools"] 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | serde = { version = "1.0.228", features = ["derive"] } 15 | 16 | [dev-dependencies] 17 | serde_json = "1.0.145" 18 | -------------------------------------------------------------------------------- /aarch64-esr-web/README.md: -------------------------------------------------------------------------------- 1 | # aarch64-esr-web 2 | 3 | A small webapp for decoding aarch64 ESR register values. 4 | 5 | [See it in action](https://google.github.io/aarch64-esr-decoder/). 6 | 7 | This is not an officially supported Google product. 8 | 9 | ## Usage 10 | 11 | Build with `wasm-pack build`. 12 | 13 | ## License 14 | 15 | Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). 16 | 17 | ## Contributing 18 | 19 | If you want to contribute to the project, see details of 20 | [how we accept contributions](../CONTRIBUTING.md). 21 | -------------------------------------------------------------------------------- /arm-sysregs-xml/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arm-sysregs-xml" 3 | version = "0.1.0" 4 | authors = ["Andrew Walbran "] 5 | edition = "2024" 6 | license = "Apache-2.0" 7 | description = "A library for parsing Arm system register XML files." 8 | repository = "https://github.com/google/aarch64-esr-decoder/" 9 | keywords = ["aarch64"] 10 | categories = ["development-tools"] 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | serde = { version = "1.0.228", features = ["derive"] } 15 | 16 | [dev-dependencies] 17 | quick-xml = { version = "0.38.4", features = ["serialize"] } 18 | -------------------------------------------------------------------------------- /arm-sysregs-xml/README.md: -------------------------------------------------------------------------------- 1 | # arm-sysregs-xml 2 | 3 | [![crates.io page](https://img.shields.io/crates/v/arm-sysregs-xml.svg)](https://crates.io/crates/arm-sysregs-xml) 4 | [![docs.rs page](https://docs.rs/arm-sysregs-xml/badge.svg)](https://docs.rs/arm-sysregs-xml) 5 | 6 | A library for parsing Arm system register XML files. 7 | 8 | This is not an officially supported Google product. 9 | 10 | ## License 11 | 12 | Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). 13 | 14 | ## Contributing 15 | 16 | If you want to contribute to the project, see details of 17 | [how we accept contributions](CONTRIBUTING.md). 18 | -------------------------------------------------------------------------------- /arm-sysregs-json/README.md: -------------------------------------------------------------------------------- 1 | # arm-sysregs-json 2 | 3 | [![crates.io page](https://img.shields.io/crates/v/arm-sysregs-json.svg)](https://crates.io/crates/arm-sysregs-json) 4 | [![docs.rs page](https://docs.rs/arm-sysregs-json/badge.svg)](https://docs.rs/arm-sysregs-json) 5 | 6 | A library for parsing Arm system register JSON files. 7 | 8 | This is not an officially supported Google product. 9 | 10 | ## License 11 | 12 | Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). 13 | 14 | ## Contributing 15 | 16 | If you want to contribute to the project, see details of 17 | [how we accept contributions](CONTRIBUTING.md). 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["arm-sysregs-json", "arm-sysregs-xml"] 3 | 4 | [package] 5 | name = "aarch64-esr-decoder" 6 | version = "0.2.4" 7 | authors = ["Andrew Walbran "] 8 | edition = "2024" 9 | license = "Apache-2.0" 10 | description = "A library and command-line utility for decoding aarch64 Exception Syndrome Register values." 11 | repository = "https://github.com/google/aarch64-esr-decoder/" 12 | keywords = ["aarch64", "esr"] 13 | categories = ["development-tools"] 14 | exclude = [".github/"] 15 | readme = "README.md" 16 | 17 | [dependencies] 18 | bit_field = "0.10.3" 19 | thiserror = "2.0.17" 20 | 21 | [package.metadata.deb] 22 | section = "devel" 23 | copyright = "Google LLC" 24 | extended-description = "" 25 | -------------------------------------------------------------------------------- /aarch64-esr-web/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const CopyPlugin = require("copy-webpack-plugin"); 3 | const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin"); 4 | 5 | const dist = path.resolve(__dirname, "dist"); 6 | 7 | module.exports = { 8 | mode: "production", 9 | entry: { 10 | index: "./js/index.js", 11 | }, 12 | output: { 13 | path: dist, 14 | filename: "[name].js", 15 | }, 16 | devtool: "source-map", 17 | optimization: { 18 | minimize: true, 19 | usedExports: true, 20 | }, 21 | experiments: { 22 | asyncWebAssembly: true, 23 | }, 24 | plugins: [ 25 | new CopyPlugin({ patterns: [path.resolve(__dirname, "static")] }), 26 | 27 | new WasmPackPlugin({ 28 | crateDirectory: __dirname, 29 | }), 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /aarch64-esr-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Andrew Walbran ", 3 | "name": "aarch64-esr-web-www", 4 | "version": "0.1.0", 5 | "description": "Webapp to decode aarch64 ESR values", 6 | "scripts": { 7 | "build": "rimraf dist pkg && webpack", 8 | "start": "rimraf dist pkg && webpack-dev-server --open", 9 | "test": "cargo test && wasm-pack test --node" 10 | }, 11 | "devDependencies": { 12 | "@wasm-tool/wasm-pack-plugin": "^1.7.0", 13 | "@webassemblyjs/ast": "^1.14.1", 14 | "@webassemblyjs/wasm-edit": "^1.14.1", 15 | "@webassemblyjs/wasm-parser": "^1.14.1", 16 | "copy-webpack-plugin": "^13.0.1", 17 | "rimraf": "^6.1.2", 18 | "webpack": "^5.103.0", 19 | "webpack-cli": "^6.0.1", 20 | "webpack-dev-server": "^5.2.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/smccc/tapp.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{DecodeError, FieldInfo}; 16 | 17 | pub fn decode_tapp_service(smccc: u64, _conv: u64) -> Result { 18 | Ok(FieldInfo::get(smccc, "Function Number", None, 0, 16)) 19 | } 20 | -------------------------------------------------------------------------------- /src/esr/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Description functions shared between multiple modules. 16 | 17 | pub fn describe_cv(cv: bool) -> &'static str { 18 | if cv { 19 | "COND is valid" 20 | } else { 21 | "COND is not valid" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /arm-sysregs-json/examples/parse_json.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use arm_sysregs_json::RegisterEntry; 16 | use std::io::stdin; 17 | 18 | fn main() { 19 | let registers: Vec = serde_json::from_reader(stdin().lock()).unwrap(); 20 | println!("{registers:#?}"); 21 | } 22 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/style.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | } 4 | td { 5 | border: 1px solid black; 6 | } 7 | tr.value td { 8 | font-family: monospace; 9 | } 10 | tr.description td:not(:empty) { 11 | background-color: #ffffdd; 12 | } 13 | tr.name td:not(:empty) { 14 | background-color: #ddffdd; 15 | font-family: monospace; 16 | } 17 | p#error { 18 | color: red; 19 | } 20 | ul.tabbar { 21 | margin: 0; 22 | padding-top: 5px; 23 | padding-bottom: 0; 24 | padding-left: 0; 25 | padding-right: 0; 26 | width: 100%; 27 | list-style: none; 28 | border-bottom: 1px solid #ddffdd; 29 | } 30 | .tabbar li { 31 | margin-left: 5px; 32 | display: inline-block; 33 | } 34 | .tabbar li.current { 35 | background-color: #ddffdd; 36 | padding: 10px; 37 | } 38 | .tabbar li a { 39 | background-color: #ffffdd; 40 | display: block; 41 | padding: 10px; 42 | } 43 | .tabbar li a:hover { 44 | background-color: #cceecc; 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/web.yml: -------------------------------------------------------------------------------- 1 | name: Web 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | build: 13 | permissions: 14 | contents: write 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v6 18 | - name: Install wasm-pack 19 | uses: jetli/wasm-pack-action@v0.4.0 20 | with: 21 | version: "v0.13.1" 22 | - name: Build with wasm-pack 23 | working-directory: aarch64-esr-web 24 | run: wasm-pack build 25 | - name: NPM install and build 26 | working-directory: aarch64-esr-web 27 | run: | 28 | npm install 29 | npm run build 30 | - name: Deploy to GitHub Pages 31 | if: ${{ github.event_name == 'push' }} 32 | uses: JamesIves/github-pages-deploy-action@v4.7.6 33 | with: 34 | branch: gh-pages 35 | folder: aarch64-esr-web/dist 36 | -------------------------------------------------------------------------------- /src/esr/bti.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for a Branch Target Exception. 18 | pub fn decode_iss_bti(iss: u64) -> Result, DecodeError> { 19 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 2, 25).check_res0()?; 20 | let btype = FieldInfo::get(iss, "BTYPE", Some("PSTATE.BTYPE value"), 0, 2); 21 | 22 | Ok(vec![res0, btype]) 23 | } 24 | -------------------------------------------------------------------------------- /src/esr/hvc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for an HVC or SVC exception. 18 | pub fn decode_iss_hvc(iss: u64) -> Result, DecodeError> { 19 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 16, 25).check_res0()?; 20 | let imm16 = FieldInfo::get(iss, "imm16", Some("Value of the immediate field"), 0, 16); 21 | 22 | Ok(vec![res0, imm16]) 23 | } 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement (CLA). You (or your employer) retain the copyright to your 10 | contribution; this simply gives us permission to use and redistribute your 11 | contributions as part of the project. Head over to 12 | to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code Reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult 23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 24 | information on using pull requests. 25 | 26 | ## Community Guidelines 27 | 28 | This project follows 29 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 30 | -------------------------------------------------------------------------------- /aarch64-esr-web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "aarch64-esr-web" 5 | version = "0.1.0" 6 | authors = ["Andrew Walbran "] 7 | edition = "2018" 8 | license = "Apache-2.0" 9 | description = "A webapp for decoding aarch64 Exception Syndrome Register values." 10 | repository = "https://github.com/google/aarch64-esr-decoder/" 11 | keywords = ["aarch64", "esr", "wasm"] 12 | categories = ["development-tools"] 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [features] 18 | default = ["console_error_panic_hook"] 19 | 20 | [profile.release] 21 | lto = true 22 | # Optimize for small code size. 23 | opt-level = "s" 24 | 25 | [dependencies] 26 | wasm-bindgen = "0.2.101" 27 | aarch64-esr-decoder = { path = ".." } 28 | web-sys = { version = "0.3.76", features = [ "Document", "Element", "HtmlElement", "Node", "Window" ] } 29 | 30 | # The `console_error_panic_hook` crate provides better debugging of panics by 31 | # logging them with `console.error`. This is great for development, but requires 32 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 33 | # code size when deploying. 34 | console_error_panic_hook = { version = "0.1.7", optional = true } 35 | 36 | [dev-dependencies] 37 | wasm-bindgen-test = "0.3.49" 38 | -------------------------------------------------------------------------------- /src/esr/ld64b.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for a trapped LD64B or ST64B* instruction. 18 | pub fn decode_iss_ld64b(iss: u64) -> Result, DecodeError> { 19 | let iss = FieldInfo::get(iss, "ISS", None, 0, 25).describe(describe_iss_ld64b)?; 20 | Ok(vec![iss]) 21 | } 22 | 23 | fn describe_iss_ld64b(iss: u64) -> Result<&'static str, DecodeError> { 24 | match iss { 25 | 0b00 => Ok("ST64BV trapped"), 26 | 0b01 => Ok("ST64BV0 trapped"), 27 | 0b10 => Ok("LD64B or ST64B trapped"), 28 | _ => Err(DecodeError::InvalidLd64bIss { iss }), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/smccc/hyp.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{DecodeError, FieldInfo, describe_general32_queries}; 16 | 17 | pub fn decode_hyp_service(smccc: u64, conv: u64) -> Result { 18 | if conv == 0 { 19 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_general32_queries) 20 | } else { 21 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_hyp64_service) 22 | } 23 | } 24 | 25 | fn describe_hyp64_service(service: u64) -> Result<&'static str, DecodeError> { 26 | Ok(match service { 27 | 0x20..=0x3F => "PV Time 64-bit calls", 28 | _ => "", 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/esr/sve.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::common::describe_cv; 16 | use crate::{DecodeError, FieldInfo}; 17 | 18 | /// Decodes the ISS value for a trapped SVE, Advanced SIMD or FP instruction. 19 | pub fn decode_iss_sve(iss: u64) -> Result, DecodeError> { 20 | let cv = 21 | FieldInfo::get_bit(iss, "CV", Some("Condition code valid"), 24).describe_bit(describe_cv); 22 | let cond = FieldInfo::get( 23 | iss, 24 | "COND", 25 | Some("Condition code of the trapped instruction"), 26 | 20, 27 | 24, 28 | ); 29 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 0, 20).check_res0()?; 30 | 31 | Ok(vec![cv, cond, res0]) 32 | } 33 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/midr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AArch64 MIDR decoder 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

AArch64 register decoder

16 | 21 |
22 |

Decimal or hexadecimal input supported. Use 0x for hexadecimal.

23 |

24 | 25 | 26 |

27 |
28 |
29 |

30 |

Source and command-line version

31 | 32 | 33 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AArch64 ESR decoder 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

AArch64 register decoder

16 | 21 |
22 |

Decimal or hexadecimal input supported. Use 0x for hexadecimal.

23 |

24 | 25 | 26 |

27 |
28 |
29 |

30 |

Source and command-line version

31 | 32 | 33 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/smccc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Arm SMCCC decoder 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

AArch64 register decoder

16 |
    17 |
  • ESR
  • 18 |
  • MIDR
  • 19 |
  • SMCCC
  • 20 |
21 |
22 |

Decimal or hexadecimal input supported. Use 0x for hexadecimal.

23 |

24 | 25 | 26 |

27 |
28 |
29 |

30 |

Source and command-line version

31 | 32 | 33 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/app.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AArch64 ESR decoder", 3 | "short_name": "AArch64 ESR", 4 | "description": "Decoder for aarch64 Exception Syndrome Register values. Also supports MIDR values and SMCCC function IDs.", 5 | "icons": [ 6 | { 7 | "src": "logo.png", 8 | "type": "image/png", 9 | "sizes": "512x512" 10 | }, 11 | { 12 | "src": "logo-192.png", 13 | "type": "image/png", 14 | "sizes": "192x192" 15 | }, 16 | { 17 | "src": "logo.svg", 18 | "type": "image/svg+xml", 19 | "sizes": "any" 20 | } 21 | ], 22 | "screenshots": [ 23 | { 24 | "src": "screenshot-desktop.png", 25 | "type": "image/png", 26 | "sizes": "1115x773", 27 | "form_factor": "wide" 28 | }, 29 | { 30 | "src": "screenshot-mobile.png", 31 | "type": "image/png", 32 | "sizes": "1080x2400", 33 | "form_factor": "narrow" 34 | } 35 | ], 36 | "background_color": "#ffffff", 37 | "start_url": "/", 38 | "id": "/", 39 | "display": "standalone", 40 | "categories": ["devtools", "utilities"], 41 | "lang": "en", 42 | "shortcuts": [ 43 | { "name": "ESR decoder", "short_name": "ESR", "url": "/" }, 44 | { "name": "MIDR decoder", "short_name": "MIDR", "url": "midr.html" }, 45 | { "name": "SMCCC decoder", "short_name": "SMCCC", "url": "smccc.html" } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | build: 13 | permissions: 14 | checks: write 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v6 18 | - name: Build 19 | run: cargo build --all 20 | - name: Run tests 21 | run: cargo test --all 22 | - name: Run clippy 23 | uses: actions-rs/clippy-check@v1 24 | with: 25 | token: ${{ secrets.GITHUB_TOKEN }} 26 | args: --all --all-features 27 | 28 | format: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v6 32 | - name: Format Rust code 33 | run: cargo fmt --all -- --check 34 | 35 | coverage: 36 | runs-on: ubuntu-latest 37 | env: 38 | RUSTC_BOOTSTRAP: 1 39 | steps: 40 | - uses: actions/checkout@v6 41 | - name: Install cargo-llvm-cov 42 | uses: taiki-e/install-action@v2 43 | with: 44 | tool: cargo-llvm-cov 45 | - name: Run tests with coverage 46 | run: cargo llvm-cov test --all --all-features --codecov --output-path codecov-report.json 47 | - name: Upload coverage to codecov.io 48 | uses: codecov/codecov-action@v5 49 | with: 50 | fail_ci_if_error: true 51 | files: codecov-report.json 52 | token: ${{ secrets.CODECOV_TOKEN }} 53 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.4 4 | 5 | No new features or bugfixes, only dependency updates. 6 | 7 | ## 0.2.3 8 | 9 | ### New features 10 | 11 | - Added support for ISS decoding with RME extension. 12 | 13 | ## 0.2.2 14 | 15 | ### New features 16 | 17 | - Added support for decoding SMCCC function IDs. 18 | - Added support for MIDR and SMCCC to command-line tool. 19 | 20 | ## 0.2.1 21 | 22 | ### Bugfixes 23 | 24 | - Print long name for multi-bit fields in command-line app (they were only being printed for single 25 | bit fields). 26 | 27 | ### New features 28 | 29 | - Added support for decoding MIDR values too. 30 | - Added HVC and SMC ECs for `ESR_EL2`. 31 | 32 | ## 0.2.0 33 | 34 | ### Breaking changes 35 | 36 | - Removed `Decoded` struct, inlined its fields into `FieldInfo`. 37 | - `DecodeError` variants changed. 38 | - Added `long_name` to `FieldInfo` struct. 39 | 40 | ### Bugfixes 41 | 42 | - Added CRn field which was missing from MCR or MRC accesses. 43 | 44 | ### New features 45 | 46 | - Added ISS decoding for SVE exceptions. 47 | - Added ISS decoding for LD64B/ST64B\* exceptions. 48 | - Added ISS decoding for Branch Target Exception. 49 | - Added ISS decoding for HVC and SVC exceptions. 50 | - Added ISS decoding for MRS and MSR exceptions, including system register names. 51 | - Added ISS decoding for Pointer Authentication failures. 52 | - Added ISS decoding for floating-point exceptions. 53 | - Added ISS decoding for SError interrupts. 54 | - Added ISS decoding for Breakpoint, Watchpoint, Software Step and Vector Catch exceptions. 55 | 56 | ## 0.1.0 57 | 58 | Initial release. 59 | -------------------------------------------------------------------------------- /src/esr/pauth.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for a Pointer Authentication failure. 18 | pub fn decode_iss_pauth(iss: u64) -> Result, DecodeError> { 19 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 2, 25).check_res0()?; 20 | let instruction_or_data = 21 | FieldInfo::get_bit(iss, "IorD", Some("Instruction key or Data key"), 1) 22 | .describe_bit(describe_instruction_or_data); 23 | let a_or_b = 24 | FieldInfo::get_bit(iss, "AorB", Some("A key or B key"), 0).describe_bit(describe_a_or_b); 25 | 26 | Ok(vec![res0, instruction_or_data, a_or_b]) 27 | } 28 | 29 | fn describe_instruction_or_data(instruction_or_data: bool) -> &'static str { 30 | if instruction_or_data { 31 | "Data Key" 32 | } else { 33 | "Instruction Key" 34 | } 35 | } 36 | 37 | fn describe_a_or_b(a_or_b: bool) -> &'static str { 38 | if a_or_b { "B Key" } else { "A Key" } 39 | } 40 | -------------------------------------------------------------------------------- /src/smccc/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{DecodeError, FieldInfo}; 16 | 17 | pub fn decode_common_service(smccc: u64, conv: u64) -> Result { 18 | if conv == 0 { 19 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_general32_queries) 20 | } else { 21 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_general64_queries) 22 | } 23 | } 24 | 25 | pub fn reserved_fids(service: u64) -> &'static str { 26 | match service { 27 | 0xFF00..=0xFFFF => "Reserved for future expansion", 28 | _ => "", 29 | } 30 | } 31 | 32 | pub fn smccc_general32_queries(service: u64) -> &'static str { 33 | match service { 34 | 0xFF00 => "Call Count Query, deprecated from SMCCCv1.2", 35 | 0xFF01 => "Call UUID Query", 36 | 0xFF03 => "Revision Query", 37 | _ => reserved_fids(service), 38 | } 39 | } 40 | pub fn describe_general32_queries(service: u64) -> Result<&'static str, DecodeError> { 41 | Ok(smccc_general32_queries(service)) 42 | } 43 | pub fn describe_general64_queries(service: u64) -> Result<&'static str, DecodeError> { 44 | Ok(reserved_fids(service)) 45 | } 46 | -------------------------------------------------------------------------------- /src/smccc/arm.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{DecodeError, FieldInfo, reserved_fids}; 16 | 17 | pub fn decode_arm_service(smccc: u64, conv: u64) -> Result { 18 | if conv == 0 { 19 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_arm32_service) 20 | } else { 21 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_arm64_service) 22 | } 23 | } 24 | 25 | fn describe_arm32_service(service: u64) -> Result<&'static str, DecodeError> { 26 | Ok(match service { 27 | 0x0000 => "SMCCC_VERSION", 28 | 0x0001 => "SMCCC_ARCH_FEATURES", 29 | 0x0002 => "SMCCC_ARCH_SOC_ID", 30 | 0x3FFF => "SMCCC_ARCH_WORKAROUND_3", 31 | 0x7FFF => "SMCCC_ARCH_WORKAROUND_2", 32 | 0x8000 => "SMCCC_ARCH_WORKAROUND_1", 33 | 0xFF00 => "Call Count Query, deprecated from SMCCCv1.2", 34 | 0xFF01 => "Call UUID Query, deprecated from SMCCCv1.2", 35 | 0xFF03 => "Revision Query, deprecated from SMCCCv1.2", 36 | _ => reserved_fids(service), 37 | }) 38 | } 39 | fn describe_arm64_service(service: u64) -> Result<&'static str, DecodeError> { 40 | Ok(reserved_fids(service)) 41 | } 42 | -------------------------------------------------------------------------------- /aarch64-esr-web/js/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import("../pkg").then((wasm) => { 16 | wasm.init(); 17 | 18 | const esr = document.getElementById("esr"); 19 | if (esr != null) { 20 | esr.oninput = () => { 21 | if (esr.value.length > 0) { 22 | wasm.decode_esr(esr.value); 23 | } 24 | window.location.hash = esr.value; 25 | }; 26 | 27 | if (window.location.hash) { 28 | esr.value = window.location.hash.substring(1); 29 | wasm.decode_esr(esr.value); 30 | } 31 | } 32 | 33 | const midr = document.getElementById("midr"); 34 | if (midr != null) { 35 | midr.oninput = () => { 36 | if (midr.value.length > 0) { 37 | wasm.decode_midr(midr.value); 38 | } 39 | window.location.hash = midr.value; 40 | }; 41 | 42 | if (window.location.hash) { 43 | midr.value = window.location.hash.substring(1); 44 | wasm.decode_midr(midr.value); 45 | } 46 | } 47 | 48 | const smccc = document.getElementById("smccc"); 49 | if (smccc != null) { 50 | smccc.oninput = () => { 51 | if (smccc.value.length > 0) { 52 | wasm.decode_smccc(smccc.value); 53 | } 54 | window.location.hash = smccc.value; 55 | }; 56 | 57 | if (window.location.hash) { 58 | smccc.value = window.location.hash.substring(1); 59 | wasm.decode_smccc(smccc.value); 60 | } 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aarch64-esr-decoder 2 | 3 | [![crates.io page](https://img.shields.io/crates/v/aarch64-esr-decoder.svg)](https://crates.io/crates/aarch64-esr-decoder) 4 | [![docs.rs page](https://docs.rs/aarch64-esr-decoder/badge.svg)](https://docs.rs/aarch64-esr-decoder) 5 | 6 | A small utility for decoding aarch64 ESR register values. 7 | 8 | This is not an officially supported Google product. 9 | 10 | ## Usage 11 | 12 | Install a Debian package from the 13 | [latest release](https://github.com/google/aarch64-esr-decoder/releases/tag/0.2.0), install with 14 | `cargo install aarch64-esr-decoder`, or 15 | [try the web version](https://google.github.io/aarch64-esr-decoder/). 16 | 17 | For the command-line version, just pass the ESR value you're interested in as a parameter, in 18 | decimal or hexadecimal: 19 | 20 | ``` 21 | $ aarch64-esr-decoder 0x96000050 22 | ESR 0x00000000000000000000000096000050: 23 | # Data Abort taken without a change in Exception level 24 | 37..63 RES0: 0x0000000 0b000000000000000000000000000 25 | 32..36 ISS2: 0x00 0b00000 26 | 26..31 EC: 0x25 0b100101 27 | # Data Abort taken without a change in Exception level 28 | 25 IL: true 29 | # 32-bit instruction trapped 30 | 00..24 ISS: 0x0000050 0b0000000000000000001010000 31 | 24 ISV: false 32 | # No valid instruction syndrome 33 | 14..23 RES0: 0x000 0b0000000000 34 | 13 VNCR: false 35 | 11..12 SET: 0x0 0b00 36 | # Recoverable state (UER) 37 | 10 FnV: false 38 | # FAR is valid 39 | 09 EA: false 40 | 08 CM: false 41 | 07 S1PTW: false 42 | 06 WnR: true 43 | # Abort caused by writing to memory 44 | 00..05 DFSC: 0x10 0b010000 45 | # Synchronous External abort, not on translation table walk or hardware update of translation table. 46 | ``` 47 | 48 | For long field names, add `-v`. 49 | 50 | ## License 51 | 52 | Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). 53 | 54 | ## Contributing 55 | 56 | If you want to contribute to the project, see details of 57 | [how we accept contributions](CONTRIBUTING.md). 58 | -------------------------------------------------------------------------------- /src/esr/wf.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::common::describe_cv; 16 | use crate::{DecodeError, FieldInfo}; 17 | 18 | /// Decodes the ISS value for a trapped WF* instruction. 19 | pub fn decode_iss_wf(iss: u64) -> Result, DecodeError> { 20 | let cv = 21 | FieldInfo::get_bit(iss, "CV", Some("Condition code valid"), 24).describe_bit(describe_cv); 22 | let cond = FieldInfo::get( 23 | iss, 24 | "COND", 25 | Some("Condition code of the trapped instruction"), 26 | 20, 27 | 24, 28 | ); 29 | let res0a = FieldInfo::get(iss, "RES0", Some("Reserved"), 10, 20).check_res0()?; 30 | let rn = FieldInfo::get(iss, "RN", Some("Register Number"), 5, 10); 31 | let res0b = FieldInfo::get(iss, "RES0", Some("Reserved"), 3, 5).check_res0()?; 32 | let rv = FieldInfo::get_bit(iss, "RV", Some("Register Valid"), 2).describe_bit(describe_rv); 33 | let ti = FieldInfo::get(iss, "TI", Some("Trapped Instruction"), 0, 2).describe(describe_ti)?; 34 | 35 | Ok(vec![cv, cond, res0a, rn, res0b, rv, ti]) 36 | } 37 | 38 | fn describe_rv(rv: bool) -> &'static str { 39 | if rv { "RN is valid" } else { "RN is not valid" } 40 | } 41 | 42 | fn describe_ti(ti: u64) -> Result<&'static str, DecodeError> { 43 | Ok(match ti { 44 | 0b00 => "WFI trapped", 45 | 0b01 => "WFE trapped", 46 | 0b10 => "WFIT trapped", 47 | 0b11 => "WFET trapped", 48 | _ => unreachable!(), 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /src/smccc/secure.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{ 16 | DecodeError, FieldInfo, 17 | ffa::{ffa_32_function_id, ffa_64_function_id}, 18 | smccc_general32_queries, 19 | }; 20 | 21 | pub fn decode_secure_service(smccc: u64, conv: u64) -> Result { 22 | let info = if conv == 0 { 23 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_secure32_service)? 24 | } else { 25 | FieldInfo::get(smccc, "Function Number", None, 0, 16).describe(describe_secure64_service)? 26 | }; 27 | Ok(info) 28 | } 29 | 30 | fn secure_service(service: u64) -> &'static str { 31 | match service { 32 | 0x000..=0x01F => "PSCI Call (Power Secure Control Interface)", 33 | 0x020..=0x03F => "SDEI Call (Software Delegated Exception Interface)", 34 | 0x040..=0x04F => "MM Call (Management Mode)", 35 | 0x050..=0x05F => "TRNG Call", 36 | 0x060..=0x0EF => "Unknown FF-A Call", 37 | 0x0F0..=0x10F => "Errata Call", 38 | 0x150..=0x1CF => "CCA Call", 39 | _ => "", 40 | } 41 | } 42 | 43 | fn describe_secure32_service(service: u64) -> Result<&'static str, DecodeError> { 44 | if let Some(ffa_call) = ffa_32_function_id(service) { 45 | return Ok(ffa_call); 46 | } 47 | 48 | Ok(match service { 49 | 0x000..=0x1CF => secure_service(service), 50 | _ => smccc_general32_queries(service), 51 | }) 52 | } 53 | fn describe_secure64_service(service: u64) -> Result<&'static str, DecodeError> { 54 | if let Some(ffa_call) = ffa_64_function_id(service) { 55 | return Ok(ffa_call); 56 | } 57 | Ok(secure_service(service)) 58 | } 59 | -------------------------------------------------------------------------------- /src/midr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the given Main ID Register value, or returns an error if it is not valid. 18 | pub fn decode_midr(midr: u64) -> Result, DecodeError> { 19 | let res0 = FieldInfo::get(midr, "RES0", Some("Reserved"), 32, 64).check_res0()?; 20 | let implementer = 21 | FieldInfo::get(midr, "Implementer", None, 24, 32).describe(describe_implementer)?; 22 | let variant = FieldInfo::get(midr, "Variant", None, 20, 24); 23 | let architecture = 24 | FieldInfo::get(midr, "Architecture", None, 16, 20).describe(describe_architecture)?; 25 | let part_num = FieldInfo::get(midr, "PartNum", Some("Part number"), 4, 16); 26 | let revision = FieldInfo::get(midr, "Revision", None, 0, 4); 27 | 28 | Ok(vec![ 29 | res0, 30 | implementer, 31 | variant, 32 | architecture, 33 | part_num, 34 | revision, 35 | ]) 36 | } 37 | 38 | fn describe_implementer(implementer: u64) -> Result<&'static str, DecodeError> { 39 | Ok(match implementer { 40 | 0x00 => "Reserved for software use", 41 | 0xC0 => "Ampere Computing", 42 | 0x41 => "Arm Limited", 43 | 0x42 => "Broadcom Corporation", 44 | 0x43 => "Cavium Inc.", 45 | 0x44 => "Digital Equipment Corporation", 46 | 0x46 => "Fujitsu Ltd.", 47 | 0x49 => "Infineon Technologies AG", 48 | 0x4D => "Motorola or Freescale Semiconductor Inc.", 49 | 0x4E => "NVIDIA Corporation", 50 | 0x50 => "Applied Micro Circuits Corporation", 51 | 0x51 => "Qualcomm Inc.", 52 | 0x56 => "Marvell International Ltd.", 53 | 0x69 => "Intel Corporation", 54 | _ => "Unknown", 55 | }) 56 | } 57 | 58 | fn describe_architecture(architecture: u64) -> Result<&'static str, DecodeError> { 59 | Ok(match architecture { 60 | 0b0001 => "Armv4", 61 | 0b0010 => "Armv4T", 62 | 0b0011 => "Armv5", 63 | 0b0100 => "Armv5T", 64 | 0b0101 => "Armv5TE", 65 | 0b0110 => "Armv5TEJ", 66 | 0b0111 => "Armv6", 67 | 0b1111 => "Architectural features are individually identified", 68 | _ => "Reserved", 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /src/esr/mcr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::common::describe_cv; 16 | use crate::{DecodeError, FieldInfo}; 17 | 18 | /// Decodes the ISS value for an MCR or MRC access. 19 | pub fn decode_iss_mcr(iss: u64) -> Result, DecodeError> { 20 | let cv = 21 | FieldInfo::get_bit(iss, "CV", Some("Condition code valid"), 24).describe_bit(describe_cv); 22 | let cond = FieldInfo::get( 23 | iss, 24 | "COND", 25 | Some("Condition code of the trapped instruction"), 26 | 20, 27 | 24, 28 | ); 29 | let opc2 = FieldInfo::get(iss, "Opc2", None, 17, 20); 30 | let opc1 = FieldInfo::get(iss, "Opc1", None, 14, 17); 31 | let crn = FieldInfo::get(iss, "CRn", None, 10, 14); 32 | let rt = FieldInfo::get(iss, "Rt", None, 5, 10); 33 | let crm = FieldInfo::get(iss, "CRm", None, 1, 5); 34 | let direction = FieldInfo::get_bit( 35 | iss, 36 | "Direction", 37 | Some("Direction of the trapped instruction"), 38 | 0, 39 | ) 40 | .describe_bit(describe_direction); 41 | 42 | Ok(vec![cv, cond, opc2, opc1, crn, rt, crm, direction]) 43 | } 44 | 45 | /// Decodes the ISS value for an MCRR or MRRC access. 46 | pub fn decode_iss_mcrr(iss: u64) -> Result, DecodeError> { 47 | let cv = 48 | FieldInfo::get_bit(iss, "CV", Some("Condition code valid"), 24).describe_bit(describe_cv); 49 | let cond = FieldInfo::get( 50 | iss, 51 | "COND", 52 | Some("Condition code of the trapped instruction"), 53 | 20, 54 | 24, 55 | ); 56 | let opc1 = FieldInfo::get(iss, "Opc2", None, 16, 20); 57 | let res0 = FieldInfo::get_bit(iss, "RES0", Some("Reserved"), 15).check_res0()?; 58 | let rt2 = FieldInfo::get(iss, "Rt2", None, 10, 15); 59 | let rt = FieldInfo::get(iss, "Rt", None, 5, 10); 60 | let crm = FieldInfo::get(iss, "CRm", None, 1, 5); 61 | let direction = FieldInfo::get_bit( 62 | iss, 63 | "Direction", 64 | Some("Direction of the trapped instruction"), 65 | 0, 66 | ) 67 | .describe_bit(describe_direction); 68 | 69 | Ok(vec![cv, cond, opc1, res0, rt2, rt, crm, direction]) 70 | } 71 | 72 | fn describe_direction(direction: bool) -> &'static str { 73 | if direction { 74 | "Read from system register (MRC or VMRS)" 75 | } else { 76 | "Write to system register (MCR)" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/esr/ldc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::common::describe_cv; 16 | use crate::{DecodeError, FieldInfo}; 17 | 18 | /// Decodes the ISS value for a trapped LDC or STC instruction. 19 | pub fn decode_iss_ldc(iss: u64) -> Result, DecodeError> { 20 | let cv = 21 | FieldInfo::get_bit(iss, "CV", Some("Condition code valid"), 24).describe_bit(describe_cv); 22 | let cond = FieldInfo::get( 23 | iss, 24 | "COND", 25 | Some("Condition code of the trapped instruction"), 26 | 20, 27 | 24, 28 | ); 29 | let imm8 = FieldInfo::get( 30 | iss, 31 | "imm8", 32 | Some("Immediate value of the trapped instruction"), 33 | 12, 34 | 20, 35 | ); 36 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 10, 12).check_res0()?; 37 | let rn = FieldInfo::get( 38 | iss, 39 | "Rn", 40 | Some("General-purpose register number of the trapped instruction"), 41 | 5, 42 | 10, 43 | ); 44 | let offset = FieldInfo::get_bit( 45 | iss, 46 | "Offset", 47 | Some("Whether the offset is added or subtracted"), 48 | 4, 49 | ) 50 | .describe_bit(describe_offset); 51 | let am = FieldInfo::get(iss, "AM", Some("Addressing Mode"), 1, 4).describe(describe_am)?; 52 | let direction = FieldInfo::get_bit( 53 | iss, 54 | "Direction", 55 | Some("Direction of the trapped instruction"), 56 | 0, 57 | ) 58 | .describe_bit(describe_direction); 59 | 60 | Ok(vec![cv, cond, imm8, res0, rn, offset, am, direction]) 61 | } 62 | 63 | fn describe_offset(offset: bool) -> &'static str { 64 | if offset { 65 | "Add offset" 66 | } else { 67 | "Subtract offset" 68 | } 69 | } 70 | 71 | fn describe_am(am: u64) -> Result<&'static str, DecodeError> { 72 | match am { 73 | 0b000 => Ok("Immediate unindexed"), 74 | 0b001 => Ok("Immediate post-indexed"), 75 | 0b010 => Ok("Immediate offset"), 76 | 0b011 => Ok("Immediate pre-indexed"), 77 | 0b100 => Ok("Reserved for trapped STR or T32 LDC"), 78 | 0b110 => Ok("Reserved for trapped STC"), 79 | _ => Err(DecodeError::InvalidAm { am }), 80 | } 81 | } 82 | 83 | fn describe_direction(direction: bool) -> &'static str { 84 | if direction { 85 | "Read from memory (LDC)" 86 | } else { 87 | "Write to memory (STC)" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /arm-sysregs-xml/examples/generate_decoder.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use arm_sysregs_xml::{EncName, Encoding, ExecutionState, RegisterPage}; 16 | use quick_xml::de; 17 | use std::{ 18 | collections::BTreeMap, 19 | fs::{File, read_dir}, 20 | io::BufReader, 21 | }; 22 | 23 | fn main() { 24 | let mut encodings = BTreeMap::new(); 25 | for entry in read_dir("SysReg_xml_A_profile-2025-06/SysReg_xml_A_profile-2025-06").unwrap() { 26 | let entry = entry.unwrap(); 27 | let filename = entry.file_name().into_string().unwrap(); 28 | if filename.ends_with(".xml") 29 | && !filename.ends_with("index.xml") 30 | && ![ 31 | "amu.xml", 32 | "architecture_info.xml", 33 | "instructions.xml", 34 | "notice.xml", 35 | "pmu.xml", 36 | ] 37 | .contains(&filename.as_str()) 38 | { 39 | let register_page = de::from_reader::<_, RegisterPage>(BufReader::new( 40 | File::open(entry.path()).unwrap(), 41 | )) 42 | .unwrap(); 43 | let register = ®ister_page.registers.register; 44 | if register.execution_state != Some(ExecutionState::AArch64) { 45 | continue; 46 | } 47 | for mechanism in ®ister.access_mechanisms.access_mechanism { 48 | if let Some(encoding) = &mechanism.encoding 49 | && encoding.access_instruction.starts_with("MRS , ") 50 | { 51 | let reg_name = &encoding.access_instruction[10..]; 52 | if let (Some(op0), Some(op1), Some(crn), Some(crm), Some(op2)) = ( 53 | enc_value(encoding, EncName::Op0), 54 | enc_value(encoding, EncName::Op1), 55 | enc_value(encoding, EncName::CRn), 56 | enc_value(encoding, EncName::CRm), 57 | enc_value(encoding, EncName::Op2), 58 | ) { 59 | encodings.insert( 60 | reg_name.to_owned(), 61 | format!("({op0}, {crn}, {op1}, {crm}, {op2})"), 62 | ); 63 | } else { 64 | println!("// {reg_name}"); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | for (name, encoding) in &encodings { 71 | println!("{encoding} => \"{name}\","); 72 | } 73 | } 74 | 75 | fn enc_value(encoding: &Encoding, name: EncName) -> Option { 76 | encoding.enc.iter().find(|enc| enc.n == name)?.parse_value() 77 | } 78 | -------------------------------------------------------------------------------- /src/smccc/ffa.rs: -------------------------------------------------------------------------------- 1 | const FFA_ERROR: u64 = 0x60; 2 | const FFA_SUCCESS: u64 = 0x61; 3 | const FFA_INTERRUPT: u64 = 0x62; 4 | const FFA_VERSION: u64 = 0x63; 5 | const FFA_FEATURES: u64 = 0x64; 6 | const FFA_RX_RELEASE: u64 = 0x65; 7 | const FFA_RXTX_MAP: u64 = 0x66; 8 | const FFA_RXTX_UNMAP: u64 = 0x67; 9 | const FFA_PARTITION_INFO_GET: u64 = 0x68; 10 | const FFA_ID_GET: u64 = 0x69; 11 | const FFA_MSG_POLL: u64 = 0x6A; 12 | const FFA_MSG_WAIT: u64 = 0x6B; 13 | const FFA_YIELD: u64 = 0x6C; 14 | const FFA_RUN: u64 = 0x6D; 15 | const FFA_MSG_SEND: u64 = 0x6E; 16 | const FFA_MSG_SEND_DIRECT_REQ: u64 = 0x6F; 17 | const FFA_MSG_SEND_DIRECT_RESP: u64 = 0x70; 18 | const FFA_MEM_DONATE: u64 = 0x71; 19 | const FFA_MEM_LEND: u64 = 0x72; 20 | const FFA_MEM_SHARE: u64 = 0x73; 21 | const FFA_MEM_RETRIEVE_REQ: u64 = 0x74; 22 | const FFA_MEM_RETRIEVE_RESP: u64 = 0x75; 23 | const FFA_MEM_RELINQUISH: u64 = 0x76; 24 | const FFA_MEM_RECLAIM: u64 = 0x77; 25 | const FFA_MEM_OP_PAUSE: u64 = 0x78; 26 | const FFA_MEM_OP_RESUME: u64 = 0x79; 27 | const FFA_MEM_FRAG_RX: u64 = 0x7A; 28 | const FFA_MEM_FRAG_TX: u64 = 0x7B; 29 | const FFA_NORMAL_WORLD_RESUME: u64 = 0x7C; 30 | 31 | pub fn ffa_32_function_id(function: u64) -> Option<&'static str> { 32 | match function { 33 | FFA_ERROR => Some("FFA_ERROR_32"), 34 | FFA_SUCCESS => Some("FFA_SUCCESS_32"), 35 | FFA_INTERRUPT => Some("FFA_INTERRUPT_32"), 36 | FFA_VERSION => Some("FFA_VERSION_32"), 37 | FFA_FEATURES => Some("FFA_FEATURES_32"), 38 | FFA_RX_RELEASE => Some("FFA_RX_RELEASE_32"), 39 | FFA_RXTX_MAP => Some("FFA_RXTX_MAP_32"), 40 | FFA_RXTX_UNMAP => Some("FFA_RXTX_UNMAP_32"), 41 | FFA_PARTITION_INFO_GET => Some("FFA_PARTITION_INFO_GET_32"), 42 | FFA_ID_GET => Some("FFA_ID_GET_32"), 43 | FFA_MSG_POLL => Some("FFA_MSG_POLL_32"), 44 | FFA_MSG_WAIT => Some("FFA_MSG_WAIT_32"), 45 | FFA_YIELD => Some("FFA_YIELD_32"), 46 | FFA_RUN => Some("FFA_RUN_32"), 47 | FFA_MSG_SEND => Some("FFA_MSG_SEND_32"), 48 | FFA_MSG_SEND_DIRECT_REQ => Some("FFA_MSG_SEND_DIRECT_REQ_32"), 49 | FFA_MSG_SEND_DIRECT_RESP => Some("FFA_MSG_SEND_DIRECT_RESP_32"), 50 | FFA_MEM_DONATE => Some("FFA_MEM_DONATE_32"), 51 | FFA_MEM_LEND => Some("FFA_MEM_LEND_32"), 52 | FFA_MEM_SHARE => Some("FFA_MEM_SHARE_32"), 53 | FFA_MEM_RETRIEVE_REQ => Some("FFA_MEM_RETRIEVE_REQ_32"), 54 | FFA_MEM_RETRIEVE_RESP => Some("FFA_MEM_RETRIEVE_RESP_32"), 55 | FFA_MEM_RELINQUISH => Some("FFA_MEM_RELINQUISH_32"), 56 | FFA_MEM_RECLAIM => Some("FFA_MEM_RECLAIM_32"), 57 | FFA_MEM_OP_PAUSE => Some("FFA_MEM_OP_PAUSE"), 58 | FFA_MEM_OP_RESUME => Some("FFA_MEM_OP_RESUME"), 59 | FFA_MEM_FRAG_RX => Some("FFA_MEM_FRAG_RX_32"), 60 | FFA_MEM_FRAG_TX => Some("FFA_MEM_FRAG_TX_32"), 61 | FFA_NORMAL_WORLD_RESUME => Some("FFA_NORMAL_WORLD_RESUME"), 62 | _ => None, 63 | } 64 | } 65 | 66 | pub fn ffa_64_function_id(function: u64) -> Option<&'static str> { 67 | match function { 68 | FFA_RXTX_MAP => Some("FFA_RXTX_MAP_64"), 69 | FFA_MSG_SEND_DIRECT_REQ => Some("FFA_MSG_SEND_DIRECT_REQ_64"), 70 | FFA_MSG_SEND_DIRECT_RESP => Some("FFA_MSG_SEND_DIRECT_RESP_64"), 71 | FFA_MEM_DONATE => Some("FFA_MEM_DONATE_64"), 72 | FFA_MEM_LEND => Some("FFA_MEM_LEND_64"), 73 | FFA_MEM_SHARE => Some("FFA_MEM_SHARE_64"), 74 | FFA_MEM_RETRIEVE_REQ => Some("FFA_MEM_RETRIEVE_REQ_64"), 75 | _ => None, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/esr/serror.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for an SError interrupt. 18 | pub fn decode_iss_serror(iss: u64) -> Result, DecodeError> { 19 | let ids = FieldInfo::get_bit(iss, "IDS", Some("Implementation Defined Syndrome"), 24) 20 | .describe_bit(describe_ids); 21 | let platform_fields = if ids.as_bit() { 22 | let impdef = FieldInfo::get(iss, "IMPDEF", Some("Implementation defined"), 0, 24); 23 | vec![impdef] 24 | } else { 25 | let dfsc = FieldInfo::get(iss, "DFSC", Some("Data Fault Status Code"), 0, 6) 26 | .describe(describe_dfsc)?; 27 | 28 | let res0a = FieldInfo::get(iss, "RES0", Some("Reserved"), 14, 24).check_res0()?; 29 | let iesb = if dfsc.value == 0b010001 { 30 | FieldInfo::get_bit( 31 | iss, 32 | "IESB", 33 | Some("Implicit Error Synchronisation event"), 34 | 13, 35 | ) 36 | .describe_bit(describe_iesb) 37 | } else { 38 | FieldInfo::get_bit(iss, "RES0", Some("Reserved for this DFSC value"), 13) 39 | .check_res0()? 40 | }; 41 | let aet = FieldInfo::get(iss, "AET", Some("Asynchronous Error Type"), 10, 13) 42 | .describe(describe_aet)?; 43 | let ea = FieldInfo::get_bit(iss, "EA", Some("External Abort type"), 9); 44 | let res0b = FieldInfo::get(iss, "RES0", Some("Reserved"), 6, 9).check_res0()?; 45 | vec![res0a, iesb, aet, ea, res0b, dfsc] 46 | }; 47 | 48 | let mut fields = vec![ids]; 49 | fields.extend(platform_fields); 50 | Ok(fields) 51 | } 52 | 53 | fn describe_ids(ids: bool) -> &'static str { 54 | if ids { 55 | "The rest of the ISS is encoded in an implementation-defined format" 56 | } else { 57 | "The rest of the ISS is encoded according to the platform" 58 | } 59 | } 60 | 61 | fn describe_iesb(iesb: bool) -> &'static str { 62 | if iesb { 63 | "The SError interrupt was synchronized by the implicit error synchronization event and taken immediately." 64 | } else { 65 | "The SError interrupt was not synchronized by the implicit error synchronization event or not taken immediately." 66 | } 67 | } 68 | 69 | fn describe_aet(aet: u64) -> Result<&'static str, DecodeError> { 70 | match aet { 71 | 0b000 => Ok("Uncontainable (UC)"), 72 | 0b001 => Ok("Unrecoverable state (UEU)"), 73 | 0b010 => Ok("Restartable state (UEO)"), 74 | 0b011 => Ok("Recoverable state (UER)"), 75 | 0x110 => Ok("Corrected (CE)"), 76 | _ => Err(DecodeError::InvalidAet { aet }), 77 | } 78 | } 79 | 80 | fn describe_dfsc(dfsc: u64) -> Result<&'static str, DecodeError> { 81 | match dfsc { 82 | 0b000000 => Ok("Uncategorized error"), 83 | 0b010001 => Ok("Asynchronous SError interrupt"), 84 | _ => Err(DecodeError::InvalidFsc { fsc: dfsc }), 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/esr/fp.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for a floating-point exception. 18 | pub fn decode_iss_fp(iss: u64) -> Result, DecodeError> { 19 | let res0a = FieldInfo::get_bit(iss, "RES0", Some("Reserved"), 24).check_res0()?; 20 | let tfv = 21 | FieldInfo::get_bit(iss, "TFV", Some("Trapped Fault Valid"), 23).describe_bit(describe_tfv); 22 | let res0b = FieldInfo::get(iss, "RES0", Some("Reserved"), 11, 23).check_res0()?; 23 | let vecitr = FieldInfo::get(iss, "VECITR", Some("RES1 or UNKNOWN"), 8, 11); 24 | let idf = FieldInfo::get_bit(iss, "IDF", Some("Input Denormal"), 7).describe_bit(describe_idf); 25 | let res0c = FieldInfo::get(iss, "RES0", Some("Reserved"), 5, 7).check_res0()?; 26 | let ixf = FieldInfo::get_bit(iss, "IXF", Some("Inexact"), 4).describe_bit(describe_ixf); 27 | let uff = FieldInfo::get_bit(iss, "UFF", Some("Underflow"), 3).describe_bit(describe_uff); 28 | let off = FieldInfo::get_bit(iss, "OFF", Some("Overflow"), 2).describe_bit(describe_off); 29 | let dzf = FieldInfo::get_bit(iss, "DZF", Some("Divide by Zero"), 1).describe_bit(describe_dzf); 30 | let iof = 31 | FieldInfo::get_bit(iss, "IOF", Some("Invalid Operation"), 0).describe_bit(describe_iof); 32 | 33 | Ok(vec![ 34 | res0a, tfv, res0b, vecitr, idf, res0c, ixf, uff, off, dzf, iof, 35 | ]) 36 | } 37 | 38 | fn describe_tfv(tfv: bool) -> &'static str { 39 | if tfv { 40 | "One or more floating-point exceptions occurred; IDF, IXF, UFF, OFF, DZF and IOF hold information about what." 41 | } else { 42 | "IDF, IXF, UFF, OFF, DZF and IOF do not hold valid information." 43 | } 44 | } 45 | 46 | fn describe_idf(idf: bool) -> &'static str { 47 | if idf { 48 | "Input denormal floating-point exception occurred." 49 | } else { 50 | "Input denormal floating-point exception did not occur." 51 | } 52 | } 53 | 54 | fn describe_ixf(ixf: bool) -> &'static str { 55 | if ixf { 56 | "Inexact floating-point exception occurred." 57 | } else { 58 | "Inexact floating-point exception did not occur." 59 | } 60 | } 61 | 62 | fn describe_uff(uff: bool) -> &'static str { 63 | if uff { 64 | "Underflow floating-point exception occurred." 65 | } else { 66 | "Underflow floating-point exception did not occur." 67 | } 68 | } 69 | 70 | fn describe_off(off: bool) -> &'static str { 71 | if off { 72 | "Overflow floating-point exception occurred." 73 | } else { 74 | "Overflow floating-point exception did not occur." 75 | } 76 | } 77 | 78 | fn describe_dzf(dzf: bool) -> &'static str { 79 | if dzf { 80 | "Divide by Zero floating-point exception occurred." 81 | } else { 82 | "Divide by Zero floating-point exception did not occur." 83 | } 84 | } 85 | 86 | fn describe_iof(iof: bool) -> &'static str { 87 | if iof { 88 | "Invalid Operation floating-point exception occurred." 89 | } else { 90 | "Invalid Operation floating-point exception did not occur." 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: Package 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | tags: ["*"] 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | cargo-deb-version: 3.3.0 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | target: 18 | - arm-unknown-linux-gnueabi 19 | - armv7-unknown-linux-gnueabihf 20 | - aarch64-unknown-linux-gnu 21 | - x86_64-unknown-linux-gnu 22 | include: 23 | - target: arm-unknown-linux-gnueabi 24 | binutils: binutils-arm-linux-gnueabi 25 | - target: armv7-unknown-linux-gnueabihf 26 | binutils: binutils-arm-linux-gnueabihf 27 | - target: aarch64-unknown-linux-gnu 28 | binutils: binutils-aarch64-linux-gnu 29 | 30 | steps: 31 | - uses: actions/checkout@v6 32 | - name: Install dependencies 33 | run: sudo apt-get update && sudo apt-get install ${{ matrix.binutils }} 34 | - name: Toolchain 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: stable 38 | target: ${{ matrix.target }} 39 | override: true 40 | 41 | - name: Cache .cargo and target 42 | uses: actions/cache@v5 43 | with: 44 | path: | 45 | ~/.cargo/git 46 | ~/.cargo/registry 47 | target 48 | key: ${{ runner.os }}-${{ matrix.target }}-package-cargo-${{ hashFiles('**/Cargo.lock') }} 49 | - name: Cache cargo-deb and cross 50 | uses: actions/cache@v5 51 | with: 52 | path: | 53 | ~/.cargo/bin/cargo-deb 54 | ~/.cargo/bin/cross 55 | ~/.cargo/.crates.toml 56 | ~/.cargo/.crates2.json 57 | key: ${{ runner.os }}-cargo-bin-${{ env.cargo-deb-version }} 58 | 59 | - name: Install cargo-deb 60 | run: cargo install cargo-deb --version ${{ env.cargo-deb-version }} --locked 61 | 62 | - name: Cross build 63 | uses: actions-rs/cargo@v1 64 | with: 65 | use-cross: true 66 | command: build 67 | args: --release --target ${{ matrix.target }} 68 | - name: Package 69 | run: cargo deb --target ${{ matrix.target }} --no-build 70 | 71 | - name: Upload package 72 | uses: actions/upload-artifact@v6 73 | with: 74 | name: debian-package-${{ matrix.target }} 75 | path: target/${{ matrix.target }}/debian/ 76 | 77 | release: 78 | name: Draft release 79 | permissions: 80 | contents: write 81 | runs-on: ubuntu-latest 82 | needs: build 83 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') 84 | steps: 85 | - name: Download packages 86 | uses: actions/download-artifact@v7 87 | - name: Parse tag for version 88 | id: parse_tag 89 | uses: actions-ecosystem/action-regex-match@v2 90 | with: 91 | text: ${{ github.ref }} 92 | regex: "^refs/tags/(.+)$" 93 | - name: Create draft release 94 | id: create_release 95 | uses: actions/create-release@v1 96 | env: 97 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions. 98 | with: 99 | tag_name: ${{ github.ref }} 100 | release_name: ${{ steps.parse_tag.outputs.group1 }} 101 | draft: true 102 | prerelease: false 103 | - name: Upload packages to release 104 | uses: shogo82148/actions-upload-release-asset@v1 105 | with: 106 | upload_url: ${{ steps.create_release.outputs.upload_url }} 107 | asset_path: "debian-package-*/aarch64-esr-decoder_*.deb" 108 | asset_content_type: application/vnd.debian.binary-package 109 | -------------------------------------------------------------------------------- /src/esr/breakpoint.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | 17 | /// Decodes the ISS value for a Breakpoint or Vector Catch debug exception. 18 | pub fn decode_iss_breakpoint_vector_catch(iss: u64) -> Result, DecodeError> { 19 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 6, 25).check_res0()?; 20 | let ifsc = FieldInfo::get(iss, "IFSC", Some("Instruction Fault Status Code"), 0, 6) 21 | .describe(describe_fsc)?; 22 | 23 | Ok(vec![res0, ifsc]) 24 | } 25 | 26 | /// Decodes the ISS value for a Software Step exception. 27 | pub fn decode_iss_software_step(iss: u64) -> Result, DecodeError> { 28 | let isv = FieldInfo::get_bit(iss, "ISV", Some("Instruction Syndrome Valid"), 24) 29 | .describe_bit(describe_isv); 30 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 7, 24).check_res0()?; 31 | let ex = if isv.as_bit() { 32 | FieldInfo::get_bit(iss, "EX", Some("Exclusive operation"), 6).describe_bit(describe_ex) 33 | } else { 34 | FieldInfo::get_bit(iss, "RES0", Some("Reserved because ISV is false"), 6).check_res0()? 35 | }; 36 | let ifsc = FieldInfo::get(iss, "IFSC", Some("Instruction Fault Status Code"), 0, 6) 37 | .describe(describe_fsc)?; 38 | 39 | Ok(vec![isv, res0, ex, ifsc]) 40 | } 41 | 42 | /// Decodes the ISS value for a Watchpoint exception. 43 | pub fn decode_iss_watchpoint(iss: u64) -> Result, DecodeError> { 44 | let res0a = FieldInfo::get(iss, "RES0", Some("Reserved"), 15, 25).check_res0()?; 45 | let res0b = FieldInfo::get_bit(iss, "RES0", Some("Reserved"), 14).check_res0()?; 46 | let vncr = FieldInfo::get_bit(iss, "VNCR", None, 13); 47 | let res0c = FieldInfo::get(iss, "RES0", Some("Reserved"), 9, 13).check_res0()?; 48 | let cm = FieldInfo::get_bit(iss, "CM", Some("Cache Maintenance"), 8); 49 | let res0d = FieldInfo::get_bit(iss, "RES0", Some("Reserved"), 7).check_res0()?; 50 | let wnr = FieldInfo::get_bit(iss, "WnR", Some("Write not Read"), 6).describe_bit(describe_wnr); 51 | let dfsc = 52 | FieldInfo::get(iss, "DFSC", Some("Data Fault Status Code"), 0, 6).describe(describe_fsc)?; 53 | 54 | Ok(vec![res0a, res0b, vncr, res0c, cm, res0d, wnr, dfsc]) 55 | } 56 | 57 | /// Decodes the ISS value for a Breakpoint instruction. 58 | pub fn decode_iss_breakpoint(iss: u64) -> Result, DecodeError> { 59 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 16, 25).check_res0()?; 60 | let comment = FieldInfo::get( 61 | iss, 62 | "Comment", 63 | Some("Instruction comment field or immediate field"), 64 | 0, 65 | 16, 66 | ); 67 | 68 | Ok(vec![res0, comment]) 69 | } 70 | 71 | fn describe_fsc(fsc: u64) -> Result<&'static str, DecodeError> { 72 | match fsc { 73 | 0b100010 => Ok("Debug exception"), 74 | _ => Err(DecodeError::InvalidFsc { fsc }), 75 | } 76 | } 77 | 78 | fn describe_isv(isv: bool) -> &'static str { 79 | if isv { 80 | "EX bit is valid" 81 | } else { 82 | "EX bit is RES0" 83 | } 84 | } 85 | 86 | fn describe_ex(ex: bool) -> &'static str { 87 | if ex { 88 | "A Load-Exclusive instruction was stepped" 89 | } else { 90 | "Some instruction other than a Load-Exclusive was stepped" 91 | } 92 | } 93 | 94 | fn describe_wnr(wnr: bool) -> &'static str { 95 | if wnr { 96 | "Watchpoint caused by writing to memory" 97 | } else { 98 | "Watchpoint caused by reading from memory" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use aarch64_esr_decoder::{FieldInfo, decode, decode_midr, decode_smccc, parse_number}; 16 | use std::env; 17 | use std::ops::Deref; 18 | use std::process::exit; 19 | 20 | fn main() { 21 | let args = match parse_args() { 22 | Ok(args) => args, 23 | Err(error_code) => exit(error_code), 24 | }; 25 | 26 | let value = parse_number(&args.value).unwrap(); 27 | let decoded = match args.mode { 28 | Mode::Esr => { 29 | println!("ESR {value:#034x}:"); 30 | decode(value).unwrap() 31 | } 32 | Mode::Midr => { 33 | println!("MIDR {value:#034x}:"); 34 | decode_midr(value).unwrap() 35 | } 36 | Mode::Smccc => { 37 | println!("SMC ID {value:#018x}:"); 38 | decode_smccc(value).unwrap() 39 | } 40 | }; 41 | print_decoded(&decoded, args.verbose, 0); 42 | } 43 | 44 | fn print_decoded(fields: &[FieldInfo], verbose: bool, level: usize) { 45 | let indentation = " ".repeat(level * 2); 46 | for field in fields { 47 | let verbose_name = match field.long_name { 48 | Some(long_name) if verbose => format!(" ({long_name})"), 49 | _ => "".to_string(), 50 | }; 51 | if field.width == 1 { 52 | println!( 53 | "{}{:02} {}{}", 54 | indentation, field.start, field, verbose_name 55 | ); 56 | } else { 57 | println!( 58 | "{}{:02}..{:02} {}{}", 59 | indentation, 60 | field.start, 61 | field.start + field.width - 1, 62 | field, 63 | verbose_name, 64 | ); 65 | } 66 | if let Some(description) = &field.description { 67 | println!("{indentation} # {description}"); 68 | } 69 | 70 | print_decoded(&field.subfields, verbose, level + 1); 71 | } 72 | } 73 | 74 | /// Parse and return command-line arguments, or an error code to return. 75 | fn parse_args() -> Result { 76 | let args: Vec = env::args().collect(); 77 | let args: Vec<&str> = args.iter().map(Deref::deref).collect(); 78 | match args.as_slice() { 79 | [_, esr] => Ok(Args { 80 | verbose: false, 81 | mode: Mode::Esr, 82 | value: esr.to_string(), 83 | }), 84 | [_, "-v", esr] => Ok(Args { 85 | verbose: true, 86 | mode: Mode::Esr, 87 | value: esr.to_string(), 88 | }), 89 | [_, "midr", midr] => Ok(Args { 90 | verbose: false, 91 | mode: Mode::Midr, 92 | value: midr.to_string(), 93 | }), 94 | [_, "-v", "midr", midr] => Ok(Args { 95 | verbose: true, 96 | mode: Mode::Midr, 97 | value: midr.to_string(), 98 | }), 99 | [_, "smccc", smccc] => Ok(Args { 100 | verbose: false, 101 | mode: Mode::Smccc, 102 | value: smccc.to_string(), 103 | }), 104 | [_, "-v", "smccc", smccc] => Ok(Args { 105 | verbose: true, 106 | mode: Mode::Smccc, 107 | value: smccc.to_string(), 108 | }), 109 | _ => { 110 | eprintln!("Usage:"); 111 | eprintln!(" {} [-v] ", args[0]); 112 | eprintln!(" {} [-v] midr ", args[0]); 113 | eprintln!(" {} [-v] smccc ", args[0]); 114 | Err(1) 115 | } 116 | } 117 | } 118 | 119 | /// Command-line arguments. 120 | #[derive(Clone, Debug, Eq, PartialEq)] 121 | struct Args { 122 | verbose: bool, 123 | mode: Mode, 124 | value: String, 125 | } 126 | 127 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 128 | enum Mode { 129 | Esr, 130 | Midr, 131 | Smccc, 132 | } 133 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aarch64-esr-decoder" 7 | version = "0.2.4" 8 | dependencies = [ 9 | "bit_field", 10 | "thiserror", 11 | ] 12 | 13 | [[package]] 14 | name = "arm-sysregs-json" 15 | version = "0.3.0" 16 | dependencies = [ 17 | "serde", 18 | "serde_json", 19 | ] 20 | 21 | [[package]] 22 | name = "arm-sysregs-xml" 23 | version = "0.1.0" 24 | dependencies = [ 25 | "quick-xml", 26 | "serde", 27 | ] 28 | 29 | [[package]] 30 | name = "bit_field" 31 | version = "0.10.3" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" 34 | 35 | [[package]] 36 | name = "itoa" 37 | version = "1.0.15" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 40 | 41 | [[package]] 42 | name = "memchr" 43 | version = "2.7.5" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 46 | 47 | [[package]] 48 | name = "proc-macro2" 49 | version = "1.0.95" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 52 | dependencies = [ 53 | "unicode-ident", 54 | ] 55 | 56 | [[package]] 57 | name = "quick-xml" 58 | version = "0.38.4" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" 61 | dependencies = [ 62 | "memchr", 63 | "serde", 64 | ] 65 | 66 | [[package]] 67 | name = "quote" 68 | version = "1.0.40" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 71 | dependencies = [ 72 | "proc-macro2", 73 | ] 74 | 75 | [[package]] 76 | name = "ryu" 77 | version = "1.0.20" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 80 | 81 | [[package]] 82 | name = "serde" 83 | version = "1.0.228" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 86 | dependencies = [ 87 | "serde_core", 88 | "serde_derive", 89 | ] 90 | 91 | [[package]] 92 | name = "serde_core" 93 | version = "1.0.228" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 96 | dependencies = [ 97 | "serde_derive", 98 | ] 99 | 100 | [[package]] 101 | name = "serde_derive" 102 | version = "1.0.228" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 105 | dependencies = [ 106 | "proc-macro2", 107 | "quote", 108 | "syn", 109 | ] 110 | 111 | [[package]] 112 | name = "serde_json" 113 | version = "1.0.145" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 116 | dependencies = [ 117 | "itoa", 118 | "memchr", 119 | "ryu", 120 | "serde", 121 | "serde_core", 122 | ] 123 | 124 | [[package]] 125 | name = "syn" 126 | version = "2.0.104" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 129 | dependencies = [ 130 | "proc-macro2", 131 | "quote", 132 | "unicode-ident", 133 | ] 134 | 135 | [[package]] 136 | name = "thiserror" 137 | version = "2.0.17" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 140 | dependencies = [ 141 | "thiserror-impl", 142 | ] 143 | 144 | [[package]] 145 | name = "thiserror-impl" 146 | version = "2.0.17" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 149 | dependencies = [ 150 | "proc-macro2", 151 | "quote", 152 | "syn", 153 | ] 154 | 155 | [[package]] 156 | name = "unicode-ident" 157 | version = "1.0.18" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 160 | -------------------------------------------------------------------------------- /src/smccc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Decoder for SMC Calling Convention ARM DEN 0028E v1.4 16 | 17 | mod arm; 18 | mod common; 19 | mod ffa; 20 | mod hyp; 21 | mod secure; 22 | mod tapp; 23 | 24 | use arm::decode_arm_service; 25 | use common::decode_common_service; 26 | use common::describe_general32_queries; 27 | use common::reserved_fids; 28 | use common::smccc_general32_queries; 29 | use hyp::decode_hyp_service; 30 | use secure::decode_secure_service; 31 | use tapp::decode_tapp_service; 32 | 33 | use super::{DecodeError, FieldInfo}; 34 | 35 | /// Decodes the function ID of an SMCCC (ARM DEN 0028E v1.4) call, or returns an error if it is not valid. 36 | pub fn decode_smccc(smccc: u64) -> Result, DecodeError> { 37 | let call_type = FieldInfo::get(smccc, "Call Type", None, 31, 32).describe(describe_call)?; 38 | 39 | let result = if call_type.value == 1 { 40 | parse_fastcall(smccc)? 41 | } else { 42 | parse_yieldcall(smccc)? 43 | }; 44 | 45 | Ok([vec![call_type], result].concat()) 46 | } 47 | 48 | pub fn parse_fastcall(smccc: u64) -> Result, DecodeError> { 49 | let call_convention = 50 | FieldInfo::get(smccc, "Call Convention", None, 30, 31).describe(describe_convention)?; 51 | let service_call = 52 | FieldInfo::get(smccc, "Service Call", None, 24, 30).describe(describe_service)?; 53 | 54 | let mbz = FieldInfo::get( 55 | smccc, 56 | "MBZ", 57 | Some("Some legacy Armv7 set this to 1"), 58 | 17, 59 | 24, 60 | ); 61 | let sve = FieldInfo::get( 62 | smccc, 63 | "SVE live state", 64 | Some("No live state[1] From SMCCCv1.3, before SMCCCv1.3 MBZ"), 65 | 16, 66 | 17, 67 | ); 68 | 69 | let function_number = match service_call.value { 70 | 0x00 => decode_arm_service(smccc, call_convention.value)?, 71 | 0x04 => decode_secure_service(smccc, call_convention.value)?, 72 | 0x05 => decode_hyp_service(smccc, call_convention.value)?, 73 | 0x30..=0x31 => decode_tapp_service(smccc, call_convention.value)?, 74 | _ => decode_common_service(smccc, call_convention.value)?, 75 | }; 76 | 77 | Ok(vec![ 78 | call_convention, 79 | service_call, 80 | mbz, 81 | sve, 82 | function_number, 83 | ]) 84 | } 85 | pub fn parse_yieldcall(smccc: u64) -> Result, DecodeError> { 86 | let yield_type = 87 | FieldInfo::get(smccc, "Service Type", None, 0, 31).describe(describe_yield_service)?; 88 | Ok(vec![yield_type]) 89 | } 90 | 91 | fn describe_yield_service(service: u64) -> Result<&'static str, DecodeError> { 92 | Ok(match service { 93 | 0x00000000..=0x0100FFFF => { 94 | "Reserved for existing APIs (in use by the existing Armv7 devices)" 95 | } 96 | 0x02000000..=0x1FFFFFFF => "Trusted OS Yielding Calls", 97 | 0x20000000..=0x7FFFFFFF => "Reserved for future expansion of Trusted OS Yielding Calls", 98 | _ => "Unknown", 99 | }) 100 | } 101 | 102 | fn describe_call(call: u64) -> Result<&'static str, DecodeError> { 103 | Ok(match call { 104 | 0x00 => "Yielding Call", 105 | 0x01 => "Fast Call", 106 | _ => "Unknown", 107 | }) 108 | } 109 | fn describe_convention(conv: u64) -> Result<&'static str, DecodeError> { 110 | Ok(match conv { 111 | 0x00 => "SMC32/HVC32", 112 | 0x01 => "SMC64/HVC64", 113 | _ => "Unknown", 114 | }) 115 | } 116 | fn describe_service(service: u64) -> Result<&'static str, DecodeError> { 117 | Ok(match service { 118 | 0x00 => "Arm Architecture Call", 119 | 0x01 => "CPU Service Call", 120 | 0x02 => "SiP Service Call", 121 | 0x03 => "OEM Service Call", 122 | 0x04 => "Standard Secure Service Call", 123 | 0x05 => "Standard Hypervisor Service Call", 124 | 0x06 => "Vendor Specific Hypervisor Service Call", 125 | 0x07..=0x2F => "Reserved for future use", 126 | 0x30..=0x31 => "Trusted Application Call", 127 | 0x32..=0x3F => "Trusted OS Call", 128 | _ => "Unknown", 129 | }) 130 | } 131 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Library for decoding aarch64 Exception Syndrome Register and Main ID Register values. 16 | 17 | mod esr; 18 | mod midr; 19 | mod smccc; 20 | 21 | use bit_field::BitField; 22 | pub use esr::decode; 23 | pub use midr::decode_midr; 24 | pub use smccc::decode_smccc; 25 | use std::fmt::{self, Debug, Display, Formatter}; 26 | use std::num::ParseIntError; 27 | use thiserror::Error; 28 | 29 | /// Information about a particular field. 30 | #[derive(Clone, Debug, Eq, PartialEq)] 31 | pub struct FieldInfo { 32 | /// The short name of the field, e.g. "ISS". 33 | pub name: &'static str, 34 | /// The long name of the field, e.g. "Instruction Specific Syndrome". 35 | pub long_name: Option<&'static str>, 36 | /// The index of the lowest bit of the field. 37 | pub start: usize, 38 | /// The number of bits in the field. 39 | pub width: usize, 40 | /// The value of the field. 41 | pub value: u64, 42 | /// A description explaining the field value, if available. 43 | pub description: Option, 44 | /// Any sub-fields. 45 | pub subfields: Vec, 46 | } 47 | 48 | impl FieldInfo { 49 | fn get( 50 | register: u64, 51 | name: &'static str, 52 | long_name: Option<&'static str>, 53 | start: usize, 54 | end: usize, 55 | ) -> Self { 56 | let value = register.get_bits(start..end); 57 | Self { 58 | name, 59 | long_name, 60 | start, 61 | width: end - start, 62 | value, 63 | description: None, 64 | subfields: vec![], 65 | } 66 | } 67 | 68 | fn get_bit( 69 | register: u64, 70 | name: &'static str, 71 | long_name: Option<&'static str>, 72 | bit: usize, 73 | ) -> Self { 74 | Self::get(register, name, long_name, bit, bit + 1) 75 | } 76 | 77 | fn with_description(self, description: String) -> Self { 78 | Self { 79 | description: Some(description), 80 | ..self 81 | } 82 | } 83 | 84 | fn as_bit(&self) -> bool { 85 | assert!(self.width == 1); 86 | self.value == 1 87 | } 88 | 89 | /// Assuming this field has a width of exactly 1, describe it with the given function. 90 | /// 91 | /// Panics if `self.width != 1`. 92 | fn describe_bit(self, describer: F) -> Self 93 | where 94 | F: FnOnce(bool) -> &'static str, 95 | { 96 | let bit = self.as_bit(); 97 | let description = describer(bit).to_string(); 98 | self.with_description(description) 99 | } 100 | 101 | fn describe(self, describer: F) -> Result 102 | where 103 | F: FnOnce(u64) -> Result<&'static str, DecodeError>, 104 | { 105 | let description = describer(self.value)?.to_string(); 106 | Ok(self.with_description(description)) 107 | } 108 | 109 | fn check_res0(self) -> Result { 110 | if self.value != 0 { 111 | Err(DecodeError::InvalidRes0 { res0: self.value }) 112 | } else { 113 | Ok(self) 114 | } 115 | } 116 | 117 | /// Returns the value as a hexadecimal string, or "true" or "false" if it is a single bit. 118 | pub fn value_string(&self) -> String { 119 | if self.width == 1 { 120 | if self.value == 1 { "true" } else { "false" }.to_string() 121 | } else { 122 | format!("{:#01$x}", self.value, self.width.div_ceil(4) + 2,) 123 | } 124 | } 125 | 126 | /// Returns the value as a binary strings. 127 | pub fn value_binary_string(&self) -> String { 128 | format!("{:#01$b}", self.value, self.width + 2) 129 | } 130 | } 131 | 132 | impl Display for FieldInfo { 133 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 134 | if self.width == 1 { 135 | write!( 136 | f, 137 | "{}: {}", 138 | self.name, 139 | if self.value == 1 { "true" } else { "false" } 140 | ) 141 | } else { 142 | write!( 143 | f, 144 | "{}: {} {}", 145 | self.name, 146 | self.value_string(), 147 | self.value_binary_string(), 148 | ) 149 | } 150 | } 151 | } 152 | 153 | /// An error decoding a register value. 154 | #[derive(Debug, Error)] 155 | pub enum DecodeError { 156 | /// A RES0 field was not 0. 157 | #[error("Invalid ESR, res0 is {res0:#x}")] 158 | InvalidRes0 { res0: u64 }, 159 | /// The EC field had an invalid value. 160 | #[error("Invalid EC {ec:#x}")] 161 | InvalidEc { ec: u64 }, 162 | /// The DFSC or IFSC field had an invalid value. 163 | #[error("Invalid DFSC or IFSC {fsc:#x}")] 164 | InvalidFsc { fsc: u64 }, 165 | /// The SET field had an invalid value. 166 | #[error("Invalid SET {set:#x}")] 167 | InvalidSet { set: u64 }, 168 | /// The AET field had an invalid value. 169 | #[error("Invalid AET {aet:#x}")] 170 | InvalidAet { aet: u64 }, 171 | /// The AM field had an invalid value. 172 | #[error("Invalid AM {am:#x}")] 173 | InvalidAm { am: u64 }, 174 | /// The ISS field has an invalid value for a trapped LD64B or ST64B* exception. 175 | #[error("Invalid ISS {iss:#x} for trapped LD64B or ST64B*")] 176 | InvalidLd64bIss { iss: u64 }, 177 | } 178 | 179 | /// Parses a decimal or hexadecimal number from a string. 180 | /// 181 | /// If the string starts with `"0x"` then it will be parsed as hexadecimal, otherwise it will be 182 | /// assumed to be decimal. 183 | pub fn parse_number(s: &str) -> Result { 184 | if let Some(hex) = s.strip_prefix("0x") { 185 | u64::from_str_radix(hex, 16) 186 | } else { 187 | s.parse() 188 | } 189 | } 190 | 191 | #[cfg(test)] 192 | mod tests { 193 | use super::*; 194 | 195 | #[test] 196 | fn parse_decimal() { 197 | assert_eq!(parse_number("12345"), Ok(12345)); 198 | } 199 | 200 | #[test] 201 | fn parse_hex() { 202 | assert_eq!(parse_number("0x123abc"), Ok(0x123abc)); 203 | } 204 | 205 | #[test] 206 | fn parse_invalid() { 207 | assert!(parse_number("123abc").is_err()); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /aarch64-esr-web/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use aarch64_esr_decoder::{decode, parse_number, DecodeError, FieldInfo}; 16 | use std::convert::TryFrom; 17 | use std::ops::Deref; 18 | use wasm_bindgen::prelude::*; 19 | use web_sys::{Document, Element}; 20 | 21 | #[wasm_bindgen] 22 | pub fn init() { 23 | #[cfg(feature = "console_error_panic_hook")] 24 | console_error_panic_hook::set_once(); 25 | } 26 | 27 | #[wasm_bindgen] 28 | pub fn decode_esr(esr: &str) -> Result<(), JsValue> { 29 | match parse_number(esr) { 30 | Ok(esr) => { 31 | let decoded = decode(esr); 32 | show_decoded(esr, decoded, u64::BITS)?; 33 | } 34 | Err(_) => show_error("ESR not valid hex or decimal number"), 35 | } 36 | Ok(()) 37 | } 38 | 39 | #[wasm_bindgen] 40 | pub fn decode_midr(midr: &str) -> Result<(), JsValue> { 41 | match parse_number(midr) { 42 | Ok(midr) => { 43 | let decoded = aarch64_esr_decoder::decode_midr(midr); 44 | show_decoded(midr, decoded, u64::BITS)?; 45 | } 46 | Err(_) => show_error("MIDR not valid hex or decimal number"), 47 | } 48 | Ok(()) 49 | } 50 | 51 | #[wasm_bindgen] 52 | pub fn decode_smccc(fn_id: &str) -> Result<(), JsValue> { 53 | match parse_number(fn_id) { 54 | Ok(fn_id) => { 55 | let decoded = aarch64_esr_decoder::decode_smccc(fn_id); 56 | show_decoded(fn_id, decoded, u32::BITS)?; 57 | } 58 | Err(_) => show_error("SMCCC Function ID not valid hex or decimal number"), 59 | } 60 | Ok(()) 61 | } 62 | 63 | fn show_error(error: &str) { 64 | let document = web_sys::window() 65 | .expect("Couldn't find window") 66 | .document() 67 | .expect("Couldn't find document"); 68 | let error_element = document 69 | .get_element_by_id("error") 70 | .expect("Couldn't find error element"); 71 | let table = document 72 | .get_element_by_id("result_table") 73 | .expect("Couldn't find result table"); 74 | // Clear output table. 75 | table.set_inner_html(""); 76 | error_element.set_text_content(Some(error)); 77 | } 78 | 79 | fn show_decoded( 80 | esr: u64, 81 | decoded: Result, DecodeError>, 82 | nr_bit: u32, 83 | ) -> Result<(), JsValue> { 84 | assert!(nr_bit <= u64::BITS); 85 | let nr_bit = usize::try_from(nr_bit).unwrap(); 86 | let document = web_sys::window() 87 | .expect("Couldn't find window") 88 | .document() 89 | .expect("Couldn't find document"); 90 | let error_element = document 91 | .get_element_by_id("error") 92 | .expect("Couldn't find error element"); 93 | let table = document 94 | .get_element_by_id("result_table") 95 | .expect("Couldn't find result table"); 96 | // Remove existing contents. 97 | error_element.set_inner_html(""); 98 | table.set_inner_html(""); 99 | 100 | // ESR in hexadecimal 101 | let row = document.create_element("tr")?; 102 | row.set_attribute("class", "value")?; 103 | let esr_hex = format!("{:016x}", esr); 104 | // Loop over number of chars of interest only 105 | let nr_nib = (nr_bit + 3) / 4; 106 | for digit in esr_hex.chars().skip(16 - nr_nib) { 107 | let cell = make_cell(&document, Some(&digit.to_string()), None, 4)?; 108 | row.append_child(&cell)?; 109 | } 110 | table.append_child(&row)?; 111 | 112 | // ESR in binary 113 | let row = document.create_element("tr")?; 114 | row.set_attribute("class", "value")?; 115 | for i in (0..nr_bit).rev() { 116 | let bit = esr & (1 << i) != 0; 117 | let cell = make_cell(&document, Some(if bit { "1" } else { "0" }), None, 1)?; 118 | row.append_child(&cell)?; 119 | } 120 | table.append_child(&row)?; 121 | 122 | match decoded { 123 | Ok(fields) => { 124 | // Top-level field names and values 125 | let row = document.create_element("tr")?; 126 | row.set_attribute("class", "name")?; 127 | let mut last = nr_bit; 128 | add_field_cells( 129 | &document, 130 | &row, 131 | &fields, 132 | &mut last, 133 | |field| Some(field.to_string()), 134 | |field| field.long_name, 135 | )?; 136 | table.append_child(&row)?; 137 | 138 | // Top-level field descriptions 139 | let row = document.create_element("tr")?; 140 | row.set_attribute("class", "description")?; 141 | let mut last = nr_bit; 142 | add_field_cells( 143 | &document, 144 | &row, 145 | &fields, 146 | &mut last, 147 | |field| field.description.clone(), 148 | |_| None, 149 | )?; 150 | table.append_child(&row)?; 151 | 152 | // Second level field names and values 153 | let row = document.create_element("tr")?; 154 | row.set_attribute("class", "name")?; 155 | let mut last = nr_bit; 156 | for field in &fields { 157 | add_field_cells( 158 | &document, 159 | &row, 160 | &field.subfields, 161 | &mut last, 162 | |field| Some(field.to_string()), 163 | |field| field.long_name, 164 | )?; 165 | } 166 | table.append_child(&row)?; 167 | 168 | // Second level field descriptions 169 | let row = document.create_element("tr")?; 170 | row.set_attribute("class", "description")?; 171 | let mut last = nr_bit; 172 | for field in &fields { 173 | add_field_cells( 174 | &document, 175 | &row, 176 | &field.subfields, 177 | &mut last, 178 | |field| field.description.clone(), 179 | |_| None, 180 | )?; 181 | } 182 | table.append_child(&row)?; 183 | } 184 | Err(e) => error_element.set_text_content(Some(&e.to_string())), 185 | } 186 | 187 | Ok(()) 188 | } 189 | 190 | fn make_cell( 191 | document: &Document, 192 | contents: Option<&str>, 193 | hover_title: Option<&str>, 194 | colspan: usize, 195 | ) -> Result { 196 | let cell = document.create_element("td")?; 197 | cell.set_attribute("colspan", &colspan.to_string())?; 198 | if let Some(title) = hover_title { 199 | let abbr = document.create_element("abbr")?; 200 | abbr.set_attribute("title", title)?; 201 | abbr.set_text_content(contents); 202 | cell.append_child(&abbr)?; 203 | } else { 204 | cell.set_text_content(contents); 205 | } 206 | Ok(cell) 207 | } 208 | 209 | fn add_field_cells( 210 | document: &Document, 211 | row: &Element, 212 | fields: &[FieldInfo], 213 | last: &mut usize, 214 | get_contents: F, 215 | get_hover_title: G, 216 | ) -> Result<(), JsValue> 217 | where 218 | F: Fn(&FieldInfo) -> Option, 219 | G: Fn(&FieldInfo) -> Option<&str>, 220 | S: Deref, 221 | { 222 | for field in fields { 223 | if field.start + field.width != *last { 224 | // Add a filler 225 | let cell = make_cell(document, None, None, *last - field.start - field.width)?; 226 | row.append_child(&cell)?; 227 | } 228 | let cell = make_cell( 229 | document, 230 | get_contents(field).as_deref(), 231 | get_hover_title(field).as_deref(), 232 | field.width, 233 | )?; 234 | row.append_child(&cell)?; 235 | *last = field.start; 236 | } 237 | Ok(()) 238 | } 239 | -------------------------------------------------------------------------------- /src/esr/mod.rs: -------------------------------------------------------------------------------- 1 | mod abort; 2 | mod breakpoint; 3 | mod bti; 4 | mod common; 5 | mod fp; 6 | mod hvc; 7 | mod ld64b; 8 | mod ldc; 9 | mod mcr; 10 | mod msr; 11 | mod pauth; 12 | mod serror; 13 | mod sve; 14 | #[cfg(test)] 15 | mod tests; 16 | mod wf; 17 | 18 | use super::{DecodeError, FieldInfo}; 19 | use abort::{decode_iss_data_abort, decode_iss_instruction_abort}; 20 | use breakpoint::{ 21 | decode_iss_breakpoint, decode_iss_breakpoint_vector_catch, decode_iss_software_step, 22 | decode_iss_watchpoint, 23 | }; 24 | use bti::decode_iss_bti; 25 | use fp::decode_iss_fp; 26 | use hvc::decode_iss_hvc; 27 | use ld64b::decode_iss_ld64b; 28 | use ldc::decode_iss_ldc; 29 | use mcr::{decode_iss_mcr, decode_iss_mcrr}; 30 | use msr::decode_iss_msr; 31 | use pauth::decode_iss_pauth; 32 | use serror::decode_iss_serror; 33 | use sve::decode_iss_sve; 34 | use wf::decode_iss_wf; 35 | 36 | fn decode_iss_res0(iss: u64) -> Result, DecodeError> { 37 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 0, 25) 38 | .check_res0()? 39 | .with_description("ISS is RES0".to_string()); 40 | Ok(vec![res0]) 41 | } 42 | 43 | /// Decodes the given Exception Syndrome Register value, or returns an error if it is not valid. 44 | pub fn decode(esr: u64) -> Result, DecodeError> { 45 | let res0 = FieldInfo::get(esr, "RES0", Some("Reserved"), 37, 64).check_res0()?; 46 | let iss2 = FieldInfo::get(esr, "ISS2", None, 32, 37); 47 | let ec = FieldInfo::get(esr, "EC", Some("Exception Class"), 26, 32); 48 | let il = 49 | FieldInfo::get_bit(esr, "IL", Some("Instruction Length"), 25).describe_bit(describe_il); 50 | let iss = FieldInfo::get(esr, "ISS", Some("Instruction Specific Syndrome"), 0, 25); 51 | let (class, iss_subfields, iss_description) = match ec.value { 52 | 0b000000 => ("Unknown reason", decode_iss_res0(iss.value)?, None), 53 | 0b000001 => ( 54 | "Wrapped WF* instruction execution", 55 | decode_iss_wf(iss.value)?, 56 | None, 57 | ), 58 | 0b000011 => ( 59 | "Trapped MCR or MRC access with coproc=0b1111", 60 | decode_iss_mcr(iss.value)?, 61 | None, 62 | ), 63 | 0b000100 => ( 64 | "Trapped MCRR or MRRC access with coproc=0b1111", 65 | decode_iss_mcrr(iss.value)?, 66 | None, 67 | ), 68 | 0b000101 => ( 69 | "Trapped MCR or MRC access with coproc=0b1110", 70 | decode_iss_mcr(iss.value)?, 71 | None, 72 | ), 73 | 0b000110 => ( 74 | "Trapped LDC or STC access", 75 | decode_iss_ldc(iss.value)?, 76 | None, 77 | ), 78 | 0b000111 => ( 79 | "Trapped access to SVE, Advanced SIMD or floating point", 80 | decode_iss_sve(iss.value)?, 81 | None, 82 | ), 83 | 0b001010 => ( 84 | "Trapped execution of an LD64B, ST64B, ST64BV, or ST64BV0 instruction", 85 | decode_iss_ld64b(iss.value)?, 86 | None, 87 | ), 88 | 0b001100 => ( 89 | "Trapped MRRC access with (coproc==0b1110)", 90 | decode_iss_mcrr(iss.value)?, 91 | None, 92 | ), 93 | 0b001101 => ("Branch Target Exception", decode_iss_bti(iss.value)?, None), 94 | 0b001110 => ("Illegal Execution state", decode_iss_res0(iss.value)?, None), 95 | 0b010001 => ( 96 | "SVC instruction execution in AArch32 state", 97 | decode_iss_hvc(iss.value)?, 98 | None, 99 | ), 100 | 0b010101 => ( 101 | "SVC instruction execution in AArch64 state", 102 | decode_iss_hvc(iss.value)?, 103 | None, 104 | ), 105 | 0b010110 => ( 106 | "HVC instruction execution in AArch64 state", 107 | decode_iss_hvc(iss.value)?, 108 | None, 109 | ), 110 | 0b010111 => ( 111 | "SMC instruction execution in AArch64 state", 112 | decode_iss_hvc(iss.value)?, 113 | None, 114 | ), 115 | 0b011000 => { 116 | let (subfields, description) = decode_iss_msr(iss.value)?; 117 | ( 118 | "Trapped MSR, MRS or System instruction execution in AArch64 state", 119 | subfields, 120 | description, 121 | ) 122 | } 123 | 0b011001 => ( 124 | "Access to SVE functionality trapped as a result of CPACR_EL1.ZEN, CPTR_EL2.ZEN, \ 125 | CPTR_EL2.TZ, or CPTR_EL3.EZ", 126 | decode_iss_res0(iss.value)?, 127 | None, 128 | ), 129 | 0b011100 => ( 130 | "Exception from a Pointer Authentication instruction authentication failure", 131 | decode_iss_pauth(iss.value)?, 132 | None, 133 | ), 134 | 0b100000 => ( 135 | "Instruction Abort from a lower Exception level", 136 | decode_iss_instruction_abort(iss.value)?, 137 | None, 138 | ), 139 | 0b100001 => ( 140 | "Instruction Abort taken without a change in Exception level", 141 | decode_iss_instruction_abort(iss.value)?, 142 | None, 143 | ), 144 | 0b100010 => ( 145 | "PC alignment fault exception", 146 | decode_iss_res0(iss.value)?, 147 | None, 148 | ), 149 | 0b100100 => ( 150 | "Data Abort from a lower Exception level", 151 | decode_iss_data_abort(iss.value)?, 152 | None, 153 | ), 154 | 0b100101 => ( 155 | "Data Abort taken without a change in Exception level", 156 | decode_iss_data_abort(iss.value)?, 157 | None, 158 | ), 159 | 0b100110 => ( 160 | "SP alignment fault exception", 161 | decode_iss_res0(iss.value)?, 162 | None, 163 | ), 164 | 0b101000 => ( 165 | "Trapped floating-point exception taken from AArch32 state", 166 | decode_iss_fp(iss.value)?, 167 | None, 168 | ), 169 | 0b101100 => ( 170 | "Trapped floating-point exception taken from AArch64 state", 171 | decode_iss_fp(iss.value)?, 172 | None, 173 | ), 174 | 0b101111 => ("SError interrupt", decode_iss_serror(iss.value)?, None), 175 | 0b110000 => ( 176 | "Breakpoint exception from a lower Exception level", 177 | decode_iss_breakpoint_vector_catch(iss.value)?, 178 | None, 179 | ), 180 | 0b110001 => ( 181 | "Breakpoint exception taken without a change in Exception level", 182 | decode_iss_breakpoint_vector_catch(iss.value)?, 183 | None, 184 | ), 185 | 0b110010 => ( 186 | "Software Step exception from a lower Exception level", 187 | decode_iss_software_step(iss.value)?, 188 | None, 189 | ), 190 | 0b110011 => ( 191 | "Software Step exception taken without a change in Exception level", 192 | decode_iss_software_step(iss.value)?, 193 | None, 194 | ), 195 | 0b110100 => ( 196 | "Watchpoint exception from a lower Exception level", 197 | decode_iss_watchpoint(iss.value)?, 198 | None, 199 | ), 200 | 0b110101 => ( 201 | "Watchpoint exception taken without a change in Exception level", 202 | decode_iss_watchpoint(iss.value)?, 203 | None, 204 | ), 205 | 0b111000 => ( 206 | "BKPT instruction execution in AArch32 state", 207 | decode_iss_breakpoint(iss.value)?, 208 | None, 209 | ), 210 | 0b111100 => ( 211 | "BRK instruction execution in AArch64 state", 212 | decode_iss_breakpoint(iss.value)?, 213 | None, 214 | ), 215 | _ => return Err(DecodeError::InvalidEc { ec: ec.value }), 216 | }; 217 | let iss = FieldInfo { 218 | description: iss_description, 219 | subfields: iss_subfields, 220 | ..iss 221 | }; 222 | let ec = ec.with_description(class.to_string()); 223 | Ok(vec![res0, iss2, ec, il, iss]) 224 | } 225 | 226 | fn describe_il(il: bool) -> &'static str { 227 | if il { 228 | "32-bit instruction trapped" 229 | } else { 230 | "16-bit instruction trapped" 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /aarch64-esr-web/static/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 39 | 54 | 55 | 57 | 60 | 64 | 65 | 66 | 70 | 77 | 81 | 85 | 89 | 93 | 97 | 101 | 105 | 109 | 113 | 117 | 121 | 125 | 129 | 133 | 137 | 141 | 145 | 149 | 154 | 159 | 164 | 169 | 174 | 179 | 184 | 189 | 194 | 199 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/esr/abort.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{DecodeError, FieldInfo}; 16 | use std::fmt::{self, Debug, Display, Formatter}; 17 | 18 | /// Decodes the ISS value for an Instruction Abort. 19 | pub fn decode_iss_instruction_abort(iss: u64) -> Result, DecodeError> { 20 | let res0a = FieldInfo::get(iss, "RES0", Some("Reserved"), 13, 25).check_res0()?; 21 | let fnv = FieldInfo::get_bit(iss, "FnV", Some("FAR not Valid"), 10).describe_bit(describe_fnv); 22 | let ea = FieldInfo::get_bit(iss, "EA", Some("External abort type"), 9); 23 | let res0b = FieldInfo::get_bit(iss, "RES0", Some("Reserved"), 8).check_res0()?; 24 | let s1ptw = FieldInfo::get_bit(iss, "S1PTW", Some("Stage-1 translation table walk"), 7); 25 | let res0c = FieldInfo::get_bit(iss, "RES0", Some("Reserved"), 6).check_res0()?; 26 | let ifsc = FieldInfo::get(iss, "IFSC", Some("Instruction Fault Status Code"), 0, 6) 27 | .describe(describe_fsc)?; 28 | 29 | let set = if ifsc.value == 0b010000 { 30 | FieldInfo::get(iss, "SET", Some("Synchronous Error Type"), 11, 13).describe(describe_set)? 31 | } else { 32 | FieldInfo::get(iss, "RES0", Some("Reserved"), 11, 13) 33 | }; 34 | 35 | Ok(vec![res0a, set, fnv, ea, res0b, s1ptw, res0c, ifsc]) 36 | } 37 | 38 | /// Decodes the ISS value for a Data Abort. 39 | pub fn decode_iss_data_abort(iss: u64) -> Result, DecodeError> { 40 | let isv = FieldInfo::get_bit(iss, "ISV", Some("Instruction Syndrome Valid"), 24) 41 | .describe_bit(describe_isv); 42 | 43 | let intruction_syndrome_fields = if isv.as_bit() { 44 | // These fields are part of the instruction syndrome, and are only valid if ISV is true. 45 | let sas = FieldInfo::get(iss, "SAS", Some("Syndrome Access Size"), 22, 24); 46 | let sas_value = match sas.value { 47 | 0b00 => SyndromeAccessSize::Byte, 48 | 0b01 => SyndromeAccessSize::Halfword, 49 | 0b10 => SyndromeAccessSize::Word, 50 | 0b11 => SyndromeAccessSize::Doubleword, 51 | _ => unreachable!(), 52 | }; 53 | let sas = sas.with_description(sas_value.to_string()); 54 | let sse = FieldInfo::get_bit(iss, "SSE", Some("Syndrome Sign Extend"), 21); 55 | let srt = FieldInfo::get(iss, "SRT", Some("Syndrome Register Transfer"), 16, 21); 56 | let sf = FieldInfo::get_bit(iss, "SF", Some("Sixty-Four"), 15).describe_bit(describe_sf); 57 | let ar = 58 | FieldInfo::get_bit(iss, "AR", Some("Acquire/Release"), 14).describe_bit(describe_ar); 59 | vec![sas, sse, srt, sf, ar] 60 | } else { 61 | let res0 = FieldInfo::get(iss, "RES0", Some("Reserved"), 14, 24).check_res0()?; 62 | vec![res0] 63 | }; 64 | 65 | let vncr = FieldInfo::get_bit(iss, "VNCR", None, 13); 66 | let fnv = FieldInfo::get_bit(iss, "FnV", Some("FAR not Valid"), 10).describe_bit(describe_fnv); 67 | let ea = FieldInfo::get_bit(iss, "EA", Some("External abort type"), 9); 68 | let cm = FieldInfo::get_bit(iss, "CM", Some("Cache Maintenance"), 8); 69 | let s1ptw = FieldInfo::get_bit(iss, "S1PTW", Some("Stage-1 translation table walk"), 7); 70 | let wnr = FieldInfo::get_bit(iss, "WnR", Some("Write not Read"), 6).describe_bit(describe_wnr); 71 | let dfsc = 72 | FieldInfo::get(iss, "DFSC", Some("Data Fault Status Code"), 0, 6).describe(describe_fsc)?; 73 | let set = if dfsc.value == 0b010000 { 74 | FieldInfo::get(iss, "SET", Some("Synchronous Error Type"), 11, 13).describe(describe_set)? 75 | } else { 76 | FieldInfo::get(iss, "RES0", Some("Reserved"), 11, 13) 77 | }; 78 | 79 | let mut fields = vec![isv]; 80 | fields.extend(intruction_syndrome_fields); 81 | fields.extend(vec![vncr, set, fnv, ea, cm, s1ptw, wnr, dfsc]); 82 | Ok(fields) 83 | } 84 | 85 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 86 | enum SyndromeAccessSize { 87 | Byte = 0b00, 88 | Halfword = 0b01, 89 | Word = 0b10, 90 | Doubleword = 0b11, 91 | } 92 | 93 | impl Display for SyndromeAccessSize { 94 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 95 | let s = match self { 96 | Self::Byte => "byte", 97 | Self::Halfword => "halfword", 98 | Self::Word => "word", 99 | Self::Doubleword => "doubleword", 100 | }; 101 | write!(f, "{s}") 102 | } 103 | } 104 | 105 | fn describe_isv(isv: bool) -> &'static str { 106 | if isv { 107 | "Valid instruction syndrome" 108 | } else { 109 | "No valid instruction syndrome" 110 | } 111 | } 112 | 113 | fn describe_sf(sf: bool) -> &'static str { 114 | if sf { 115 | "64-bit wide register" 116 | } else { 117 | "32-bit wide register" 118 | } 119 | } 120 | 121 | fn describe_ar(ar: bool) -> &'static str { 122 | if ar { 123 | "Acquire/release semantics" 124 | } else { 125 | "No acquire/release semantics" 126 | } 127 | } 128 | 129 | fn describe_fnv(fnv: bool) -> &'static str { 130 | if fnv { 131 | "FAR is not valid, it holds an unknown value" 132 | } else { 133 | "FAR is valid" 134 | } 135 | } 136 | 137 | fn describe_wnr(wnr: bool) -> &'static str { 138 | if wnr { 139 | "Abort caused by writing to memory" 140 | } else { 141 | "Abort caused by reading from memory" 142 | } 143 | } 144 | 145 | fn describe_fsc(fsc: u64) -> Result<&'static str, DecodeError> { 146 | let description = match fsc { 147 | 0b000000 => { 148 | "Address size fault, level 0 of translation or translation table base register." 149 | } 150 | 0b000001 => "Address size fault, level 1.", 151 | 0b000010 => "Address size fault, level 2.", 152 | 0b000011 => "Address size fault, level 3.", 153 | 0b000100 => "Translation fault, level 0.", 154 | 0b000101 => "Translation fault, level 1.", 155 | 0b000110 => "Translation fault, level 2.", 156 | 0b000111 => "Translation fault, level 3.", 157 | 0b001001 => "Access flag fault, level 1.", 158 | 0b001010 => "Access flag fault, level 2.", 159 | 0b001011 => "Access flag fault, level 3.", 160 | 0b001000 => "Access flag fault, level 0.", 161 | 0b001100 => "Permission fault, level 0.", 162 | 0b001101 => "Permission fault, level 1.", 163 | 0b001110 => "Permission fault, level 2.", 164 | 0b001111 => "Permission fault, level 3.", 165 | 0b010000 => { 166 | "Synchronous External abort, not on translation table walk or hardware update of \ 167 | translation table." 168 | } 169 | 0b010001 => "Synchronous Tag Check Fault.", 170 | 0b010011 => { 171 | "Synchronous External abort on translation table walk or hardware update of \ 172 | translation table, level -1." 173 | } 174 | 0b010100 => { 175 | "Synchronous External abort on translation table walk or hardware update of \ 176 | translation table, level 0." 177 | } 178 | 0b010101 => { 179 | "Synchronous External abort on translation table walk or hardware update of \ 180 | translation table, level 1." 181 | } 182 | 0b010110 => { 183 | "Synchronous External abort on translation table walk or hardware update of \ 184 | translation table, level 2." 185 | } 186 | 0b010111 => { 187 | "Synchronous External abort on translation table walk or hardware update of \ 188 | translation table, level 3." 189 | } 190 | 0b011000 => { 191 | "Synchronous parity or ECC error on memory access, not on translation table walk." 192 | } 193 | 0b011011 => { 194 | "Synchronous parity or ECC error on memory access on translation table walk or \ 195 | hardware update of translation table, level -1." 196 | } 197 | 0b011100 => { 198 | "Synchronous parity or ECC error on memory access on translation table walk or \ 199 | hardware update of translation table, level 0." 200 | } 201 | 0b011101 => { 202 | "Synchronous parity or ECC error on memory access on translation table walk or \ 203 | hardware update of translation table, level 1." 204 | } 205 | 0b011110 => { 206 | "Synchronous parity or ECC error on memory access on translation table walk or \ 207 | hardware update of translation table, level 2." 208 | } 209 | 0b011111 => { 210 | "Synchronous parity or ECC error on memory access on translation table walk or \ 211 | hardware update of translation table, level 3." 212 | } 213 | 0b100001 => "Alignment fault.", 214 | 0b100011 => { 215 | "Granule Protection Fault on translation table walk or hardware update of \ 216 | translation table, level -1." 217 | } 218 | 0b100100 => { 219 | "Granule Protection Fault on translation table walk or hardware update of \ 220 | translation table, level 0." 221 | } 222 | 0b100101 => { 223 | "Granule Protection Fault on translation table walk or hardware update of \ 224 | translation table, level 1." 225 | } 226 | 0b100110 => { 227 | "Granule Protection Fault on translation table walk or hardware update of \ 228 | translation table, level 2." 229 | } 230 | 0b100111 => { 231 | "Granule Protection Fault on translation table walk or hardware update of \ 232 | translation table, level 3." 233 | } 234 | 0b101000 => { 235 | "Granule Protection Fault, not on translation table walk or hardware update of \ 236 | translation table." 237 | } 238 | 0b101001 => "Address size fault, level -1.", 239 | 0b101011 => "Translation fault, level -1.", 240 | 0b110000 => "TLB conflict abort.", 241 | 0b110001 => "Unsupported atomic hardware update fault.", 242 | 0b110100 => "IMPLEMENTATION DEFINED fault (Lockdown).", 243 | 0b110101 => "IMPLEMENTATION DEFINED fault (Unsupported Exclusive or Atomic access).", 244 | _ => return Err(DecodeError::InvalidFsc { fsc }), 245 | }; 246 | Ok(description) 247 | } 248 | 249 | fn describe_set(set: u64) -> Result<&'static str, DecodeError> { 250 | Ok(match set { 251 | 0b00 => "Recoverable state (UER)", 252 | 0b10 => "Uncontainable (UC)", 253 | 0b11 => "Restartable state (UEO)", 254 | _ => return Err(DecodeError::InvalidSet { set }), 255 | }) 256 | } 257 | -------------------------------------------------------------------------------- /aarch64-esr-web/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aarch64-esr-decoder" 7 | version = "0.2.4" 8 | dependencies = [ 9 | "bit_field", 10 | "thiserror", 11 | ] 12 | 13 | [[package]] 14 | name = "aarch64-esr-web" 15 | version = "0.1.0" 16 | dependencies = [ 17 | "aarch64-esr-decoder", 18 | "console_error_panic_hook", 19 | "wasm-bindgen", 20 | "wasm-bindgen-test", 21 | "web-sys", 22 | ] 23 | 24 | [[package]] 25 | name = "bit_field" 26 | version = "0.10.3" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" 29 | 30 | [[package]] 31 | name = "bumpalo" 32 | version = "3.19.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 35 | 36 | [[package]] 37 | name = "cc" 38 | version = "1.2.27" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" 41 | dependencies = [ 42 | "shlex", 43 | ] 44 | 45 | [[package]] 46 | name = "cfg-if" 47 | version = "1.0.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 50 | 51 | [[package]] 52 | name = "console_error_panic_hook" 53 | version = "0.1.7" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 56 | dependencies = [ 57 | "cfg-if", 58 | "wasm-bindgen", 59 | ] 60 | 61 | [[package]] 62 | name = "js-sys" 63 | version = "0.3.78" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" 66 | dependencies = [ 67 | "once_cell", 68 | "wasm-bindgen", 69 | ] 70 | 71 | [[package]] 72 | name = "log" 73 | version = "0.4.27" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 76 | 77 | [[package]] 78 | name = "minicov" 79 | version = "0.3.7" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" 82 | dependencies = [ 83 | "cc", 84 | "walkdir", 85 | ] 86 | 87 | [[package]] 88 | name = "once_cell" 89 | version = "1.21.3" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 92 | 93 | [[package]] 94 | name = "proc-macro2" 95 | version = "1.0.95" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 98 | dependencies = [ 99 | "unicode-ident", 100 | ] 101 | 102 | [[package]] 103 | name = "quote" 104 | version = "1.0.40" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 107 | dependencies = [ 108 | "proc-macro2", 109 | ] 110 | 111 | [[package]] 112 | name = "rustversion" 113 | version = "1.0.21" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 116 | 117 | [[package]] 118 | name = "same-file" 119 | version = "1.0.6" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 122 | dependencies = [ 123 | "winapi-util", 124 | ] 125 | 126 | [[package]] 127 | name = "shlex" 128 | version = "1.3.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 131 | 132 | [[package]] 133 | name = "syn" 134 | version = "2.0.104" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 137 | dependencies = [ 138 | "proc-macro2", 139 | "quote", 140 | "unicode-ident", 141 | ] 142 | 143 | [[package]] 144 | name = "thiserror" 145 | version = "2.0.17" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 148 | dependencies = [ 149 | "thiserror-impl", 150 | ] 151 | 152 | [[package]] 153 | name = "thiserror-impl" 154 | version = "2.0.17" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 157 | dependencies = [ 158 | "proc-macro2", 159 | "quote", 160 | "syn", 161 | ] 162 | 163 | [[package]] 164 | name = "unicode-ident" 165 | version = "1.0.18" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 168 | 169 | [[package]] 170 | name = "walkdir" 171 | version = "2.5.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 174 | dependencies = [ 175 | "same-file", 176 | "winapi-util", 177 | ] 178 | 179 | [[package]] 180 | name = "wasm-bindgen" 181 | version = "0.2.101" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" 184 | dependencies = [ 185 | "cfg-if", 186 | "once_cell", 187 | "rustversion", 188 | "wasm-bindgen-macro", 189 | "wasm-bindgen-shared", 190 | ] 191 | 192 | [[package]] 193 | name = "wasm-bindgen-backend" 194 | version = "0.2.101" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" 197 | dependencies = [ 198 | "bumpalo", 199 | "log", 200 | "proc-macro2", 201 | "quote", 202 | "syn", 203 | "wasm-bindgen-shared", 204 | ] 205 | 206 | [[package]] 207 | name = "wasm-bindgen-futures" 208 | version = "0.4.51" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" 211 | dependencies = [ 212 | "cfg-if", 213 | "js-sys", 214 | "once_cell", 215 | "wasm-bindgen", 216 | "web-sys", 217 | ] 218 | 219 | [[package]] 220 | name = "wasm-bindgen-macro" 221 | version = "0.2.101" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" 224 | dependencies = [ 225 | "quote", 226 | "wasm-bindgen-macro-support", 227 | ] 228 | 229 | [[package]] 230 | name = "wasm-bindgen-macro-support" 231 | version = "0.2.101" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" 234 | dependencies = [ 235 | "proc-macro2", 236 | "quote", 237 | "syn", 238 | "wasm-bindgen-backend", 239 | "wasm-bindgen-shared", 240 | ] 241 | 242 | [[package]] 243 | name = "wasm-bindgen-shared" 244 | version = "0.2.101" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" 247 | dependencies = [ 248 | "unicode-ident", 249 | ] 250 | 251 | [[package]] 252 | name = "wasm-bindgen-test" 253 | version = "0.3.51" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "80cc7f8a4114fdaa0c58383caf973fc126cf004eba25c9dc639bccd3880d55ad" 256 | dependencies = [ 257 | "js-sys", 258 | "minicov", 259 | "wasm-bindgen", 260 | "wasm-bindgen-futures", 261 | "wasm-bindgen-test-macro", 262 | ] 263 | 264 | [[package]] 265 | name = "wasm-bindgen-test-macro" 266 | version = "0.3.51" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "c5ada2ab788d46d4bda04c9d567702a79c8ced14f51f221646a16ed39d0e6a5d" 269 | dependencies = [ 270 | "proc-macro2", 271 | "quote", 272 | "syn", 273 | ] 274 | 275 | [[package]] 276 | name = "web-sys" 277 | version = "0.3.78" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" 280 | dependencies = [ 281 | "js-sys", 282 | "wasm-bindgen", 283 | ] 284 | 285 | [[package]] 286 | name = "winapi-util" 287 | version = "0.1.9" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 290 | dependencies = [ 291 | "windows-sys", 292 | ] 293 | 294 | [[package]] 295 | name = "windows-sys" 296 | version = "0.59.0" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 299 | dependencies = [ 300 | "windows-targets", 301 | ] 302 | 303 | [[package]] 304 | name = "windows-targets" 305 | version = "0.52.6" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 308 | dependencies = [ 309 | "windows_aarch64_gnullvm", 310 | "windows_aarch64_msvc", 311 | "windows_i686_gnu", 312 | "windows_i686_gnullvm", 313 | "windows_i686_msvc", 314 | "windows_x86_64_gnu", 315 | "windows_x86_64_gnullvm", 316 | "windows_x86_64_msvc", 317 | ] 318 | 319 | [[package]] 320 | name = "windows_aarch64_gnullvm" 321 | version = "0.52.6" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 324 | 325 | [[package]] 326 | name = "windows_aarch64_msvc" 327 | version = "0.52.6" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 330 | 331 | [[package]] 332 | name = "windows_i686_gnu" 333 | version = "0.52.6" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 336 | 337 | [[package]] 338 | name = "windows_i686_gnullvm" 339 | version = "0.52.6" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 342 | 343 | [[package]] 344 | name = "windows_i686_msvc" 345 | version = "0.52.6" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 348 | 349 | [[package]] 350 | name = "windows_x86_64_gnu" 351 | version = "0.52.6" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 354 | 355 | [[package]] 356 | name = "windows_x86_64_gnullvm" 357 | version = "0.52.6" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 360 | 361 | [[package]] 362 | name = "windows_x86_64_msvc" 363 | version = "0.52.6" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 366 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /arm-sysregs-json/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Types to parse the Arm system register JSON format. 16 | 17 | use serde::{Deserialize, Serialize}; 18 | use std::collections::BTreeMap; 19 | 20 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 21 | #[serde(tag = "_type")] 22 | pub enum RegisterEntry { 23 | Register(Register), 24 | RegisterArray(RegisterArray), 25 | RegisterBlock(RegisterBlock), 26 | } 27 | 28 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 29 | pub struct Register { 30 | #[serde(rename = "_meta")] 31 | pub meta: Option, 32 | pub access_text: Option, 33 | pub accessors: Vec, 34 | pub condition: Condition, 35 | pub configuration: Option, 36 | pub fieldsets: Vec
, 37 | pub groups: Option<()>, 38 | pub instances: Instances, 39 | pub mapset: Vec<()>, 40 | pub name: String, 41 | pub purpose: Option, 42 | #[serde(skip_serializing_if = "Option::is_none")] 43 | pub state: Option, 44 | pub title: Option, 45 | } 46 | 47 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 48 | pub struct Meta { 49 | pub license: License, 50 | pub version: Version, 51 | } 52 | 53 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 54 | pub struct License { 55 | pub copyright: String, 56 | pub info: String, 57 | } 58 | 59 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 60 | pub struct Version { 61 | pub architecture: String, 62 | pub build: String, 63 | #[serde(rename = "ref")] 64 | pub ref_: String, 65 | pub schema: String, 66 | pub timestamp: String, 67 | } 68 | 69 | #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] 70 | pub enum ExecutionState { 71 | AArch32, 72 | AArch64, 73 | #[serde(rename = "ext")] 74 | External, 75 | } 76 | 77 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 78 | #[serde(tag = "_type")] 79 | pub enum Accessor { 80 | #[serde(rename = "Accessors.SystemAccessor")] 81 | SystemAccessor(SystemAccessor), 82 | #[serde(rename = "Accessors.SystemAccessorArray")] 83 | SystemAccessorArray(SystemAccessorArray), 84 | #[serde(rename = "Accessors.BlockAccess")] 85 | BlockAccess(BlockAccess), 86 | #[serde(rename = "Accessors.BlockAccessArray")] 87 | BlockAccessArray(BlockAccessArray), 88 | #[serde(rename = "Accessors.ExternalDebug")] 89 | ExternalDebug(ExternalDebug), 90 | #[serde(rename = "Accessors.MemoryMapped")] 91 | MemoryMapped(MemoryMapped), 92 | } 93 | 94 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 95 | pub struct SystemAccessor { 96 | pub access: Option, 97 | pub condition: Condition, 98 | pub encoding: Vec, 99 | pub name: String, 100 | } 101 | 102 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 103 | pub struct SystemAccessorArray { 104 | pub access: Option, 105 | pub condition: Condition, 106 | pub encoding: Vec, 107 | pub index_variable: String, 108 | pub indexes: Vec, 109 | pub name: String, 110 | } 111 | 112 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 113 | pub struct BlockAccess { 114 | pub access: MemoryAccess, 115 | pub condition: Condition, 116 | pub offset: Vec, 117 | pub references: References, 118 | } 119 | 120 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 121 | pub struct BlockAccessArray { 122 | pub access: MemoryAccess, 123 | pub condition: Condition, 124 | pub index_variable: String, 125 | pub indexes: Vec, 126 | pub offset: Vec, 127 | pub references: References, 128 | } 129 | 130 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 131 | pub struct ExternalDebug { 132 | pub access: MemoryAccess, 133 | pub component: String, 134 | pub condition: Condition, 135 | pub instance: Option, 136 | pub offset: Offset, 137 | pub power_domain: Option, 138 | pub range: Option, 139 | } 140 | 141 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 142 | pub struct MemoryMapped { 143 | pub access: MemoryAccess, 144 | pub component: String, 145 | pub condition: Condition, 146 | pub frame: Option, 147 | pub instance: Option, 148 | pub offset: Offset, 149 | pub power_domain: Option, 150 | pub range: Option, 151 | } 152 | 153 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 154 | pub struct MemoryAccess {} 155 | 156 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 157 | pub struct Offset {} 158 | 159 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 160 | pub struct SystemAccess { 161 | pub access: Vec, 162 | pub condition: Condition, 163 | } 164 | 165 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 166 | pub struct Access {} 167 | 168 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 169 | pub struct Encoding { 170 | pub asmvalue: Option, 171 | pub encodings: BTreeMap, 172 | } 173 | 174 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 175 | pub struct Condition {} 176 | 177 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 178 | pub struct Fieldset { 179 | pub condition: Condition, 180 | pub description: Description, 181 | pub display: Option, 182 | pub name: Option, 183 | pub values: Vec, 184 | pub width: u32, 185 | } 186 | 187 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 188 | #[serde(tag = "_type")] 189 | pub enum FieldEntry { 190 | #[serde(rename = "Fields.Field")] 191 | Field(Field), 192 | #[serde(rename = "Fields.Reserved")] 193 | Reserved(ReservedField), 194 | #[serde(rename = "Fields.ImplementationDefined")] 195 | ImplementationDefined(ImplementationDefinedField), 196 | #[serde(rename = "Fields.Array")] 197 | Array(ArrayField), 198 | #[serde(rename = "Fields.Vector")] 199 | Vector(VectorField), 200 | #[serde(rename = "Fields.ConditionalField")] 201 | ConditionalField(ConditionalField), 202 | #[serde(rename = "Fields.ConstantField")] 203 | ConstantField(ConstantField), 204 | #[serde(rename = "Fields.Dynamic")] 205 | Dynamic(DynamicField), 206 | } 207 | 208 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 209 | pub struct Field { 210 | pub description: Description, 211 | pub name: Option, 212 | pub rangeset: Vec, 213 | pub resets: Option, 214 | pub values: Option, 215 | pub volatile: Option, 216 | } 217 | 218 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 219 | pub struct ReservedField { 220 | pub description: Description, 221 | pub rangeset: Vec, 222 | pub value: String, 223 | } 224 | 225 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 226 | pub struct ImplementationDefinedField { 227 | pub constraints: Option>, 228 | pub description: Description, 229 | pub display: Option, 230 | pub name: Option, 231 | pub rangeset: Vec, 232 | pub resets: Option, 233 | pub volatile: Option, 234 | } 235 | 236 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 237 | pub struct ArrayField { 238 | pub access: Option<()>, 239 | pub description: Description, 240 | pub display: Option, 241 | pub index_variable: String, 242 | pub indexes: Vec, 243 | pub name: Option, 244 | pub rangeset: Vec, 245 | pub resets: Option, 246 | pub values: Option, 247 | pub volatile: Option, 248 | } 249 | 250 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 251 | pub struct VectorField { 252 | pub access: Option<()>, 253 | pub description: Description, 254 | pub display: Option, 255 | pub index_variable: String, 256 | pub indexes: Vec, 257 | pub name: Option, 258 | pub rangeset: Vec, 259 | pub reserved_type: Option, 260 | pub resets: Option, 261 | pub size: Vec, 262 | pub values: Option, 263 | pub volatile: Option, 264 | } 265 | 266 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 267 | pub struct ConditionValue { 268 | pub condition: Condition, 269 | pub value: AstValue, 270 | } 271 | 272 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 273 | pub struct AstValue {} 274 | 275 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 276 | pub struct ConditionalField { 277 | pub description: Description, 278 | pub display: Option, 279 | pub fields: Vec, 280 | pub name: Option, 281 | pub rangeset: Vec, 282 | pub reservedtype: String, 283 | pub resets: Option, 284 | pub volatile: Option, 285 | } 286 | 287 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 288 | pub struct FieldCondition { 289 | pub condition: Condition, 290 | pub field: FieldEntry, 291 | } 292 | 293 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 294 | pub struct ConstantField { 295 | pub access: Option<()>, 296 | pub description: Description, 297 | pub name: Option, 298 | pub rangeset: Vec, 299 | pub resets: Option, 300 | pub value: ValueEntry, 301 | } 302 | 303 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 304 | pub struct DynamicField { 305 | pub description: Description, 306 | pub display: Option, 307 | pub instances: Vec
, 308 | pub name: Option, 309 | pub rangeset: Vec, 310 | pub resets: Option, 311 | pub volatile: Option, 312 | } 313 | 314 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 315 | pub struct Description { 316 | pub after: Option, 317 | pub before: Option, 318 | } 319 | 320 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 321 | pub struct Range { 322 | pub start: u32, 323 | pub width: u32, 324 | } 325 | 326 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 327 | pub struct FieldResets {} 328 | 329 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 330 | pub struct Values { 331 | pub values: Vec, 332 | } 333 | 334 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 335 | #[serde(tag = "_type")] 336 | pub enum ValueEntry { 337 | #[serde(rename = "Values.ConditionalValue")] 338 | ConditionalValue(ConditionalValue), 339 | #[serde(rename = "Values.EquationValue")] 340 | EquationValue(EquationValue), 341 | #[serde(rename = "Values.Group")] 342 | Group(Group), 343 | #[serde(rename = "Values.ImplementationDefined")] 344 | ImplementationDefined(ImplementationDefinedValue), 345 | #[serde(rename = "Values.Link")] 346 | Link(Link), 347 | #[serde(rename = "Values.NamedValue")] 348 | NamedValue(NamedValue), 349 | #[serde(rename = "Values.Value")] 350 | Value(Value), 351 | #[serde(rename = "Values.ValueRange")] 352 | ValueRange(ValueRange), 353 | } 354 | 355 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 356 | pub struct ConditionalValue { 357 | pub condition: Condition, 358 | pub meaning: Option, 359 | pub values: Values, 360 | } 361 | 362 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 363 | pub struct EquationValue { 364 | pub meaning: Option, 365 | pub slice: Vec, 366 | pub value: String, 367 | } 368 | 369 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 370 | pub struct Group { 371 | pub meaning: Option, 372 | pub value: String, 373 | pub values: Values, 374 | } 375 | 376 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 377 | pub struct Link { 378 | pub links: BTreeMap, 379 | pub meaning: Option, 380 | pub value: String, 381 | } 382 | 383 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 384 | pub struct NamedValue { 385 | pub meaning: Option, 386 | pub name: String, 387 | pub value: String, 388 | } 389 | 390 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 391 | pub struct Value { 392 | pub meaning: Option, 393 | pub value: String, 394 | } 395 | 396 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 397 | pub struct ImplementationDefinedValue { 398 | pub constraints: Option, 399 | pub meaning: Option, 400 | } 401 | 402 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 403 | pub struct ValueRange { 404 | pub end: Value, 405 | pub meaning: Option, 406 | pub start: Value, 407 | } 408 | 409 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 410 | pub struct RegisterArray { 411 | #[serde(rename = "_meta")] 412 | pub meta: Option, 413 | pub access_text: Option, 414 | pub accessors: Vec, 415 | pub condition: Condition, 416 | pub configuration: Option, 417 | pub fieldsets: Vec
, 418 | pub groups: Option<()>, 419 | pub index_variable: String, 420 | pub indexes: Vec, 421 | pub instances: Instances, 422 | pub mapset: Vec<()>, 423 | pub name: String, 424 | pub purpose: Option, 425 | #[serde(skip_serializing_if = "Option::is_none")] 426 | pub state: Option, 427 | pub title: Option, 428 | } 429 | 430 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 431 | #[serde(untagged)] 432 | pub enum Instances { 433 | InstanceSet(InstanceSet), 434 | Bool(bool), 435 | } 436 | 437 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 438 | pub struct InstanceSet { 439 | pub values: Vec, 440 | } 441 | 442 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 443 | pub struct Instance { 444 | pub condition: Condition, 445 | pub instance: String, 446 | } 447 | 448 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 449 | pub struct RegisterBlock { 450 | #[serde(rename = "_meta")] 451 | pub meta: Meta, 452 | pub accessors: Vec, 453 | pub blocks: Vec, 454 | pub condition: Condition, 455 | pub default_access: ReadWriteAccess, 456 | pub mapset: Vec<()>, 457 | pub name: String, 458 | pub purpose: Option, 459 | pub references: References, 460 | pub size: String, 461 | pub title: Option, 462 | } 463 | 464 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 465 | pub struct ReadWriteAccess { 466 | pub read: String, 467 | pub write: String, 468 | } 469 | 470 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 471 | pub struct References {} 472 | -------------------------------------------------------------------------------- /arm-sysregs-xml/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Types to parse the Arm system register XML format. 16 | 17 | use serde::{Deserialize, Deserializer, de::Unexpected}; 18 | 19 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 20 | pub struct RegisterPage { 21 | pub registers: Registers, 22 | pub timestamp: String, 23 | pub commit_id: String, 24 | } 25 | 26 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 27 | pub struct Registers { 28 | pub register: Register, 29 | } 30 | 31 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 32 | pub struct Register { 33 | #[serde(rename = "@execution_state")] 34 | pub execution_state: Option, 35 | #[serde(rename = "@is_register", deserialize_with = "titlecase_bool")] 36 | pub is_register: bool, 37 | #[serde(rename = "@is_internal", deserialize_with = "titlecase_bool")] 38 | pub is_internal: bool, 39 | #[serde(rename = "@is_stub_entry", deserialize_with = "titlecase_bool")] 40 | pub is_stub_entry: bool, 41 | pub reg_short_name: String, 42 | pub reg_long_name: String, 43 | pub reg_condition: Option, 44 | pub power_domain_text: Option, 45 | pub reg_reset_value: RegResetValue, 46 | pub reg_mappings: RegMappings, 47 | pub reg_purpose: RegPurpose, 48 | pub reg_groups: RegGroups, 49 | pub reg_configuration: Option, 50 | pub reg_attributes: RegAttributes, 51 | pub reg_fieldsets: RegFieldsets, 52 | pub access_mechanisms: AccessMechanisms, 53 | pub arch_variants: ArchVariants, 54 | #[serde(default)] 55 | pub reg_address: Vec, 56 | } 57 | 58 | #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] 59 | pub enum ExecutionState { 60 | AArch32, 61 | AArch64, 62 | External, 63 | } 64 | 65 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 66 | pub struct RegCondition { 67 | #[serde(rename = "@otherwise")] 68 | pub otherwise: Option, 69 | #[serde(rename = "$value", default)] 70 | pub condition: Vec, 71 | } 72 | 73 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 74 | pub struct RegResetValue { 75 | #[serde(default)] 76 | pub reg_reset_limited_to_el: Vec, 77 | pub reg_reset_special_text: Option, 78 | } 79 | 80 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 81 | pub struct RegResetSpecialText {} 82 | 83 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 84 | pub struct RegMappings { 85 | #[serde(default)] 86 | pub reg_mapping: Vec, 87 | } 88 | 89 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 90 | pub struct RegMapping { 91 | pub mapped_name: MappedName, 92 | pub mapped_type: String, 93 | pub mapped_execution_state: ExecutionState, 94 | pub mapped_from_startbit: Option, 95 | pub mapped_from_endbit: Option, 96 | pub mapped_to_startbit: Option, 97 | pub mapped_to_endbit: Option, 98 | pub mapped_from_rangeset: Option, 99 | } 100 | 101 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 102 | pub struct MappedName { 103 | #[serde(rename = "@filename")] 104 | pub filename: String, 105 | #[serde(rename = "$text")] 106 | pub name: String, 107 | } 108 | 109 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 110 | pub struct MappedFromRangeset { 111 | #[serde(rename = "@output")] 112 | pub output: String, 113 | pub range: Vec, 114 | } 115 | 116 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 117 | pub struct Range { 118 | pub msb: u8, 119 | pub lsb: u8, 120 | } 121 | 122 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 123 | pub struct RegPurpose { 124 | pub purpose_text: Vec, 125 | } 126 | 127 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 128 | pub struct RegGroups { 129 | pub reg_group: Vec, 130 | } 131 | 132 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 133 | pub struct RegConfiguration { 134 | #[serde(default)] 135 | pub configuration_text: Vec, 136 | } 137 | 138 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 139 | pub struct ConfigurationText {} 140 | 141 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 142 | pub struct RegAttributes { 143 | pub attributes_text: Vec, 144 | } 145 | 146 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 147 | pub struct RegFieldsets { 148 | #[serde(default)] 149 | pub fields: Vec, 150 | #[serde(default)] 151 | pub reg_fieldset: Vec, 152 | } 153 | 154 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 155 | pub struct Fields { 156 | #[serde(rename = "@id")] 157 | pub id: Option, 158 | #[serde(rename = "@length")] 159 | pub length: Option, 160 | pub fields_condition: Option, 161 | pub text_before_fields: Text, 162 | pub field: Vec, 163 | } 164 | 165 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 166 | pub struct Field { 167 | #[serde(rename = "@id")] 168 | pub id: String, 169 | #[serde(rename = "@has_partial_fieldset", deserialize_with = "titlecase_bool")] 170 | pub has_partial_fieldset: bool, 171 | #[serde( 172 | rename = "@is_linked_to_partial_fieldset", 173 | deserialize_with = "titlecase_bool" 174 | )] 175 | pub is_linked_to_partial_fieldset: bool, 176 | #[serde( 177 | rename = "@is_access_restriction_possible", 178 | deserialize_with = "titlecase_bool" 179 | )] 180 | pub is_access_restriction_possible: bool, 181 | #[serde(rename = "@is_variable_length", deserialize_with = "titlecase_bool")] 182 | pub is_variable_length: bool, 183 | #[serde(rename = "@is_constant_value", deserialize_with = "titlecase_bool")] 184 | pub is_constant_value: bool, 185 | #[serde(rename = "@is_partial_field", deserialize_with = "titlecase_bool")] 186 | pub is_partial_field: bool, 187 | #[serde( 188 | rename = "@is_conditional_field_name", 189 | deserialize_with = "titlecase_bool" 190 | )] 191 | pub is_conditional_field_name: bool, 192 | #[serde(rename = "@rwtype")] 193 | pub rwtype: Option, 194 | pub field_name: Option, 195 | pub field_msb: u8, 196 | pub field_lsb: u8, 197 | pub rel_range: String, 198 | pub field_description: Vec, 199 | } 200 | 201 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 202 | pub struct FieldDescription { 203 | #[serde(rename = "@order")] 204 | pub order: Order, 205 | #[serde(rename = "$value", default)] 206 | pub description: Vec, 207 | } 208 | 209 | #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] 210 | pub struct Text { 211 | #[serde(rename = "$value", default)] 212 | pub text: Vec, 213 | } 214 | 215 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 216 | #[serde(rename_all = "kebab-case")] 217 | pub enum TextEntry { 218 | #[serde(rename = "$text")] 219 | String(String), 220 | ArmDefinedWord(String), 221 | List(List), 222 | Note(Text), 223 | Para(Para), 224 | Table(Table), 225 | } 226 | 227 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 228 | pub struct Para { 229 | // TODO 230 | } 231 | 232 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 233 | pub struct List { 234 | pub listitem: Vec, 235 | } 236 | 237 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 238 | pub struct ListItem {} 239 | 240 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 241 | pub struct Table {} 242 | 243 | #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] 244 | #[serde(rename_all = "lowercase")] 245 | pub enum Order { 246 | After, 247 | Before, 248 | } 249 | 250 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 251 | pub struct RegFieldset { 252 | #[serde(rename = "@length")] 253 | pub length: u8, 254 | pub fields_condition: Option, 255 | pub fieldat: Vec, 256 | } 257 | 258 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 259 | pub struct FieldAt { 260 | #[serde(rename = "@id")] 261 | pub id: String, 262 | #[serde(rename = "@label")] 263 | pub label: Option, 264 | #[serde(rename = "@msb")] 265 | pub msb: u8, 266 | #[serde(rename = "@lsb")] 267 | pub lsb: u8, 268 | } 269 | 270 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 271 | pub struct AccessMechanisms { 272 | #[serde(default)] 273 | pub access_mechanism: Vec, 274 | } 275 | 276 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 277 | pub struct AccessMechanism { 278 | #[serde(rename = "@accessor")] 279 | pub accessor: Option, 280 | #[serde(rename = "@type")] 281 | pub type_: AccessMechanismType, 282 | #[serde(rename = "@table_id")] 283 | pub table_id: Option, 284 | pub encoding: Option, 285 | pub access_permission: Option, 286 | pub access_header: Option, 287 | } 288 | 289 | #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] 290 | pub enum AccessMechanismType { 291 | BlockAccessAbstract, 292 | SystemAccessor, 293 | } 294 | 295 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 296 | pub struct Encoding { 297 | pub access_instruction: String, 298 | pub enc: Vec, 299 | } 300 | 301 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 302 | pub struct Enc { 303 | #[serde(rename = "@n")] 304 | pub n: EncName, 305 | #[serde(rename = "@v")] 306 | pub v: String, 307 | } 308 | 309 | impl Enc { 310 | pub fn parse_value(&self) -> Option { 311 | let (prefix, rest) = self.v.split_at_checked(2)?; 312 | if prefix == "0b" { 313 | u8::from_str_radix(rest, 2).ok() 314 | } else { 315 | None 316 | } 317 | } 318 | } 319 | 320 | #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] 321 | #[serde(rename_all = "lowercase")] 322 | pub enum EncName { 323 | Coproc, 324 | #[serde(rename = "CRd")] 325 | CRd, 326 | #[serde(rename = "CRm")] 327 | CRm, 328 | #[serde(rename = "CRn")] 329 | CRn, 330 | #[serde(rename = "M")] 331 | M, 332 | #[serde(rename = "M1")] 333 | M1, 334 | Op0, 335 | Op1, 336 | Op2, 337 | Opc1, 338 | Opc2, 339 | #[serde(rename = "R")] 340 | R, 341 | Reg, 342 | } 343 | 344 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 345 | pub struct AccessPermission { 346 | pub ps: Ps, 347 | } 348 | 349 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 350 | pub struct AccessHeader {} 351 | 352 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 353 | pub struct Ps { 354 | #[serde(rename = "@name")] 355 | pub name: String, 356 | #[serde(rename = "@sections")] 357 | pub sections: usize, 358 | #[serde(rename = "@secttype")] 359 | pub secttype: String, 360 | pub pstext: String, 361 | } 362 | 363 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 364 | pub struct RegAddress { 365 | #[serde(rename = "@external_access", deserialize_with = "titlecase_bool")] 366 | pub external_access: bool, 367 | #[serde(rename = "@mem_map_access", deserialize_with = "titlecase_bool")] 368 | pub mem_map_access: bool, 369 | #[serde( 370 | rename = "@block_access", 371 | deserialize_with = "titlecase_bool_option", 372 | default 373 | )] 374 | pub block_access: Option, 375 | #[serde( 376 | rename = "@memory_access", 377 | deserialize_with = "titlecase_bool_option", 378 | default 379 | )] 380 | pub memory_access: Option, 381 | #[serde(rename = "@table_id")] 382 | pub table_id: Option, 383 | #[serde(rename = "@power_domain")] 384 | pub power_domain: Option, 385 | pub reg_component: Option, 386 | pub reg_frame: Option, 387 | pub reg_offset: RegOffset, 388 | pub reg_instance: Option, 389 | pub reg_access: RegAccess, 390 | } 391 | 392 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 393 | pub struct RegOffset { 394 | pub hexnumber: String, 395 | } 396 | 397 | impl RegOffset { 398 | pub fn parse_hex(&self) -> Option { 399 | let (prefix, rest) = self.hexnumber.split_at_checked(2)?; 400 | if prefix == "0x" { 401 | u64::from_str_radix(rest, 16).ok() 402 | } else { 403 | None 404 | } 405 | } 406 | } 407 | 408 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 409 | pub struct RegAccess { 410 | pub reg_access_state: Vec, 411 | } 412 | 413 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 414 | pub struct RegAccessState { 415 | pub reg_access_level: Option, 416 | pub reg_access_type: String, 417 | } 418 | 419 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 420 | pub struct ArchVariants { 421 | #[serde(default)] 422 | pub arch_variant: Vec, 423 | } 424 | 425 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] 426 | pub struct ArchVariant { 427 | #[serde(rename = "@name")] 428 | pub name: String, 429 | } 430 | 431 | fn titlecase_bool<'de, D: Deserializer<'de>>(deserializer: D) -> Result { 432 | match String::deserialize(deserializer)?.as_ref() { 433 | "True" => Ok(true), 434 | "False" => Ok(false), 435 | other => Err(serde::de::Error::invalid_value( 436 | Unexpected::Str(other), 437 | &"True or False", 438 | )), 439 | } 440 | } 441 | 442 | fn titlecase_bool_option<'de, D: Deserializer<'de>>( 443 | deserializer: D, 444 | ) -> Result, D::Error> { 445 | match >::deserialize(deserializer)?.as_deref() { 446 | Some("True") => Ok(Some(true)), 447 | Some("False") => Ok(Some(false)), 448 | None => Ok(None), 449 | Some(other) => Err(serde::de::Error::invalid_value( 450 | Unexpected::Str(other), 451 | &"True or False", 452 | )), 453 | } 454 | } 455 | 456 | #[cfg(test)] 457 | mod tests { 458 | use super::*; 459 | use quick_xml::de; 460 | use std::{ 461 | fs::{File, read_dir}, 462 | io::BufReader, 463 | }; 464 | 465 | #[test] 466 | fn parse_reg_purpose() { 467 | let reg_purpose: RegPurpose = de::from_str( 468 | "foo", 469 | ) 470 | .unwrap(); 471 | assert_eq!( 472 | reg_purpose, 473 | RegPurpose { 474 | purpose_text: vec![Text { 475 | text: vec![TextEntry::Para(Para {})] 476 | }] 477 | } 478 | ); 479 | } 480 | 481 | #[test] 482 | fn parse_reg_condition_string() { 483 | let reg_condition: RegCondition = 484 | de::from_str("foo").unwrap(); 485 | assert_eq!( 486 | reg_condition, 487 | RegCondition { 488 | otherwise: None, 489 | condition: vec![TextEntry::String("foo".to_string())] 490 | } 491 | ); 492 | } 493 | 494 | #[test] 495 | fn parse_reg_condition_para() { 496 | let reg_condition: RegCondition = 497 | de::from_str("foo").unwrap(); 498 | assert_eq!( 499 | reg_condition, 500 | RegCondition { 501 | otherwise: None, 502 | condition: vec![TextEntry::Para(Para {})] 503 | } 504 | ); 505 | } 506 | 507 | #[test] 508 | fn parse_field_description_empty() { 509 | let field_description: FieldDescription = 510 | de::from_str("").unwrap(); 511 | assert_eq!( 512 | field_description, 513 | FieldDescription { 514 | order: Order::Before, 515 | description: vec![], 516 | } 517 | ); 518 | } 519 | 520 | #[test] 521 | fn parse_field_description_para() { 522 | let field_description: FieldDescription = de::from_str( 523 | "foo", 524 | ) 525 | .unwrap(); 526 | assert_eq!( 527 | field_description, 528 | FieldDescription { 529 | order: Order::Before, 530 | description: vec![TextEntry::Para(Para {})], 531 | } 532 | ); 533 | } 534 | 535 | #[test] 536 | fn parse_reg_reset_value_empty() { 537 | let reg_reset_value: RegResetValue = 538 | de::from_str("").unwrap(); 539 | assert_eq!( 540 | reg_reset_value, 541 | RegResetValue { 542 | reg_reset_limited_to_el: vec![], 543 | reg_reset_special_text: None 544 | } 545 | ); 546 | } 547 | 548 | #[test] 549 | fn parse_reg_attributes() { 550 | let reg_attributes: RegAttributes = de::from_str( 551 | "foo", 552 | ) 553 | .unwrap(); 554 | assert_eq!( 555 | reg_attributes, 556 | RegAttributes { 557 | attributes_text: vec![Text { 558 | text: vec![TextEntry::Para(Para {})] 559 | }] 560 | } 561 | ); 562 | } 563 | 564 | #[test] 565 | fn parse_text_before_fields() { 566 | let text_before_fields: Text = 567 | de::from_str("foo").unwrap(); 568 | assert_eq!( 569 | text_before_fields, 570 | Text { 571 | text: vec![TextEntry::Para(Para {})] 572 | } 573 | ); 574 | } 575 | 576 | #[test] 577 | fn parse_hexnumber() { 578 | let reg_offset: RegOffset = 579 | de::from_str("0x18").unwrap(); 580 | assert_eq!(reg_offset.parse_hex(), Some(0x18)); 581 | } 582 | 583 | #[test] 584 | fn parse_enc() { 585 | let enc: Enc = de::from_str("").unwrap(); 586 | assert_eq!(enc.n, EncName::Coproc); 587 | assert_eq!(enc.parse_value(), Some(0b1101)); 588 | } 589 | 590 | #[test] 591 | #[ignore] 592 | fn parse_all() { 593 | let mut failed = 0; 594 | let mut succeeded = 0; 595 | for entry in read_dir("SysReg_xml_A_profile-2025-06/SysReg_xml_A_profile-2025-06").unwrap() 596 | { 597 | let entry = entry.unwrap(); 598 | let filename = entry.file_name().into_string().unwrap(); 599 | if filename.ends_with(".xml") 600 | && !filename.ends_with("index.xml") 601 | && ![ 602 | "amu.xml", 603 | "architecture_info.xml", 604 | "instructions.xml", 605 | "notice.xml", 606 | "pmu.xml", 607 | ] 608 | .contains(&filename.as_str()) 609 | { 610 | if let Err(e) = de::from_reader::<_, RegisterPage>(BufReader::new( 611 | File::open(entry.path()).unwrap(), 612 | )) { 613 | println!("{filename}:"); 614 | println!("{e}"); 615 | failed += 1; 616 | } else { 617 | succeeded += 1; 618 | } 619 | } 620 | } 621 | println!("{succeeded} succeeded"); 622 | assert_eq!(failed, 0); 623 | } 624 | } 625 | -------------------------------------------------------------------------------- /src/esr/tests.rs: -------------------------------------------------------------------------------- 1 | use super::decode; 2 | use crate::FieldInfo; 3 | 4 | #[test] 5 | fn unknown() { 6 | let decoded = decode(0).unwrap(); 7 | assert_eq!( 8 | decoded, 9 | vec![ 10 | FieldInfo { 11 | name: "RES0", 12 | long_name: Some("Reserved"), 13 | start: 37, 14 | width: 27, 15 | value: 0, 16 | description: None, 17 | subfields: vec![], 18 | }, 19 | FieldInfo { 20 | name: "ISS2", 21 | long_name: None, 22 | start: 32, 23 | width: 5, 24 | value: 0, 25 | description: None, 26 | subfields: vec![], 27 | }, 28 | FieldInfo { 29 | name: "EC", 30 | long_name: Some("Exception Class"), 31 | start: 26, 32 | width: 6, 33 | value: 0, 34 | description: Some("Unknown reason".to_string()), 35 | subfields: vec![], 36 | }, 37 | FieldInfo { 38 | name: "IL", 39 | long_name: Some("Instruction Length"), 40 | start: 25, 41 | width: 1, 42 | value: 0, 43 | description: Some("16-bit instruction trapped".to_string()), 44 | subfields: vec![], 45 | }, 46 | FieldInfo { 47 | name: "ISS", 48 | long_name: Some("Instruction Specific Syndrome"), 49 | start: 0, 50 | width: 25, 51 | value: 0, 52 | description: None, 53 | subfields: vec![FieldInfo { 54 | name: "RES0", 55 | long_name: Some("Reserved"), 56 | start: 0, 57 | width: 25, 58 | value: 0, 59 | description: Some("ISS is RES0".to_string()), 60 | subfields: vec![], 61 | }], 62 | }, 63 | ] 64 | ); 65 | } 66 | 67 | #[test] 68 | fn data_abort() { 69 | assert_eq!( 70 | decode(0x96000050).unwrap(), 71 | vec![ 72 | FieldInfo { 73 | name: "RES0", 74 | long_name: Some("Reserved"), 75 | start: 37, 76 | width: 27, 77 | value: 0, 78 | description: None, 79 | subfields: vec![], 80 | }, 81 | FieldInfo { 82 | name: "ISS2", 83 | long_name: None, 84 | start: 32, 85 | width: 5, 86 | value: 0, 87 | description: None, 88 | subfields: vec![], 89 | }, 90 | FieldInfo { 91 | name: "EC", 92 | long_name: Some("Exception Class"), 93 | start: 26, 94 | width: 6, 95 | value: 37, 96 | description: Some( 97 | "Data Abort taken without a change in Exception level".to_string() 98 | ), 99 | subfields: vec![], 100 | }, 101 | FieldInfo { 102 | name: "IL", 103 | long_name: Some("Instruction Length"), 104 | start: 25, 105 | width: 1, 106 | value: 1, 107 | description: Some("32-bit instruction trapped".to_string()), 108 | subfields: vec![], 109 | }, 110 | FieldInfo { 111 | name: "ISS", 112 | long_name: Some("Instruction Specific Syndrome"), 113 | start: 0, 114 | width: 25, 115 | value: 80, 116 | description: None, 117 | subfields: vec![ 118 | FieldInfo { 119 | name: "ISV", 120 | long_name: Some("Instruction Syndrome Valid"), 121 | start: 24, 122 | width: 1, 123 | value: 0, 124 | description: Some("No valid instruction syndrome".to_string()), 125 | subfields: vec![], 126 | }, 127 | FieldInfo { 128 | name: "RES0", 129 | long_name: Some("Reserved"), 130 | start: 14, 131 | width: 10, 132 | value: 0, 133 | description: None, 134 | subfields: vec![], 135 | }, 136 | FieldInfo { 137 | name: "VNCR", 138 | long_name: None, 139 | start: 13, 140 | width: 1, 141 | value: 0, 142 | description: None, 143 | subfields: vec![], 144 | }, 145 | FieldInfo { 146 | name: "SET", 147 | long_name: Some("Synchronous Error Type"), 148 | start: 11, 149 | width: 2, 150 | value: 0, 151 | description: Some("Recoverable state (UER)".to_string()), 152 | subfields: vec![], 153 | }, 154 | FieldInfo { 155 | name: "FnV", 156 | long_name: Some("FAR not Valid"), 157 | start: 10, 158 | width: 1, 159 | value: 0, 160 | description: Some("FAR is valid".to_string()), 161 | subfields: vec![], 162 | }, 163 | FieldInfo { 164 | name: "EA", 165 | long_name: Some("External abort type"), 166 | start: 9, 167 | width: 1, 168 | value: 0, 169 | description: None, 170 | subfields: vec![], 171 | }, 172 | FieldInfo { 173 | name: "CM", 174 | long_name: Some("Cache Maintenance"), 175 | start: 8, 176 | width: 1, 177 | value: 0, 178 | description: None, 179 | subfields: vec![], 180 | }, 181 | FieldInfo { 182 | name: "S1PTW", 183 | long_name: Some("Stage-1 translation table walk"), 184 | start: 7, 185 | width: 1, 186 | value: 0, 187 | description: None, 188 | subfields: vec![], 189 | }, 190 | FieldInfo { 191 | name: "WnR", 192 | long_name: Some("Write not Read"), 193 | start: 6, 194 | width: 1, 195 | value: 1, 196 | description: Some("Abort caused by writing to memory".to_string()), 197 | subfields: vec![], 198 | }, 199 | FieldInfo { 200 | name: "DFSC", 201 | long_name: Some("Data Fault Status Code"), 202 | start: 0, 203 | width: 6, 204 | value: 16, 205 | description: Some( 206 | "Synchronous External abort, not on translation table \ 207 | walk or hardware update of translation table." 208 | .to_string() 209 | ), 210 | subfields: vec![], 211 | } 212 | ] 213 | }, 214 | ], 215 | ); 216 | } 217 | 218 | #[test] 219 | fn data_abort_isv() { 220 | assert_eq!( 221 | decode(0x97523050).unwrap(), 222 | vec![ 223 | FieldInfo { 224 | name: "RES0", 225 | long_name: Some("Reserved"), 226 | start: 37, 227 | width: 27, 228 | value: 0, 229 | description: None, 230 | subfields: vec![], 231 | }, 232 | FieldInfo { 233 | name: "ISS2", 234 | long_name: None, 235 | start: 32, 236 | width: 5, 237 | value: 0, 238 | description: None, 239 | subfields: vec![], 240 | }, 241 | FieldInfo { 242 | name: "EC", 243 | long_name: Some("Exception Class"), 244 | start: 26, 245 | width: 6, 246 | value: 37, 247 | description: Some( 248 | "Data Abort taken without a change in Exception level".to_string() 249 | ), 250 | subfields: vec![], 251 | }, 252 | FieldInfo { 253 | name: "IL", 254 | long_name: Some("Instruction Length"), 255 | start: 25, 256 | width: 1, 257 | value: 1, 258 | description: Some("32-bit instruction trapped".to_string()), 259 | subfields: vec![], 260 | }, 261 | FieldInfo { 262 | name: "ISS", 263 | long_name: Some("Instruction Specific Syndrome"), 264 | start: 0, 265 | width: 25, 266 | value: 22163536, 267 | description: None, 268 | subfields: vec![ 269 | FieldInfo { 270 | name: "ISV", 271 | long_name: Some("Instruction Syndrome Valid"), 272 | start: 24, 273 | width: 1, 274 | value: 1, 275 | description: Some("Valid instruction syndrome".to_string()), 276 | subfields: vec![], 277 | }, 278 | FieldInfo { 279 | name: "SAS", 280 | long_name: Some("Syndrome Access Size"), 281 | start: 22, 282 | width: 2, 283 | value: 1, 284 | description: Some("halfword".to_string()), 285 | subfields: vec![], 286 | }, 287 | FieldInfo { 288 | name: "SSE", 289 | long_name: Some("Syndrome Sign Extend"), 290 | start: 21, 291 | width: 1, 292 | value: 0, 293 | description: None, 294 | subfields: vec![], 295 | }, 296 | FieldInfo { 297 | name: "SRT", 298 | long_name: Some("Syndrome Register Transfer"), 299 | start: 16, 300 | width: 5, 301 | value: 18, 302 | description: None, 303 | subfields: vec![], 304 | }, 305 | FieldInfo { 306 | name: "SF", 307 | long_name: Some("Sixty-Four"), 308 | start: 15, 309 | width: 1, 310 | value: 0, 311 | description: Some("32-bit wide register".to_string()), 312 | subfields: vec![], 313 | }, 314 | FieldInfo { 315 | name: "AR", 316 | long_name: Some("Acquire/Release"), 317 | start: 14, 318 | width: 1, 319 | value: 0, 320 | description: Some("No acquire/release semantics".to_string()), 321 | subfields: vec![], 322 | }, 323 | FieldInfo { 324 | name: "VNCR", 325 | long_name: None, 326 | start: 13, 327 | width: 1, 328 | value: 1, 329 | description: None, 330 | subfields: vec![], 331 | }, 332 | FieldInfo { 333 | name: "SET", 334 | long_name: Some("Synchronous Error Type"), 335 | start: 11, 336 | width: 2, 337 | value: 2, 338 | description: Some("Uncontainable (UC)".to_string()), 339 | subfields: vec![], 340 | }, 341 | FieldInfo { 342 | name: "FnV", 343 | long_name: Some("FAR not Valid"), 344 | start: 10, 345 | width: 1, 346 | value: 0, 347 | description: Some("FAR is valid".to_string()), 348 | subfields: vec![], 349 | }, 350 | FieldInfo { 351 | name: "EA", 352 | long_name: Some("External abort type"), 353 | start: 9, 354 | width: 1, 355 | value: 0, 356 | description: None, 357 | subfields: vec![], 358 | }, 359 | FieldInfo { 360 | name: "CM", 361 | long_name: Some("Cache Maintenance"), 362 | start: 8, 363 | width: 1, 364 | value: 0, 365 | description: None, 366 | subfields: vec![], 367 | }, 368 | FieldInfo { 369 | name: "S1PTW", 370 | long_name: Some("Stage-1 translation table walk"), 371 | start: 7, 372 | width: 1, 373 | value: 0, 374 | description: None, 375 | subfields: vec![], 376 | }, 377 | FieldInfo { 378 | name: "WnR", 379 | long_name: Some("Write not Read"), 380 | start: 6, 381 | width: 1, 382 | value: 1, 383 | description: Some("Abort caused by writing to memory".to_string()), 384 | subfields: vec![], 385 | }, 386 | FieldInfo { 387 | name: "DFSC", 388 | long_name: Some("Data Fault Status Code"), 389 | start: 0, 390 | width: 6, 391 | value: 16, 392 | description: Some( 393 | "Synchronous External abort, not on translation table \ 394 | walk or hardware update of translation table." 395 | .to_string() 396 | ), 397 | subfields: vec![], 398 | } 399 | ] 400 | } 401 | ], 402 | ); 403 | } 404 | 405 | #[test] 406 | fn instruction_abort() { 407 | assert_eq!( 408 | decode(0x82001e10).unwrap(), 409 | vec![ 410 | FieldInfo { 411 | name: "RES0", 412 | long_name: Some("Reserved"), 413 | start: 37, 414 | width: 27, 415 | value: 0, 416 | description: None, 417 | subfields: vec![], 418 | }, 419 | FieldInfo { 420 | name: "ISS2", 421 | long_name: None, 422 | start: 32, 423 | width: 5, 424 | value: 0, 425 | description: None, 426 | subfields: vec![], 427 | }, 428 | FieldInfo { 429 | name: "EC", 430 | long_name: Some("Exception Class"), 431 | start: 26, 432 | width: 6, 433 | value: 32, 434 | description: Some("Instruction Abort from a lower Exception level".to_string()), 435 | subfields: vec![], 436 | }, 437 | FieldInfo { 438 | name: "IL", 439 | long_name: Some("Instruction Length"), 440 | start: 25, 441 | width: 1, 442 | value: 1, 443 | description: Some("32-bit instruction trapped".to_string()), 444 | subfields: vec![], 445 | }, 446 | FieldInfo { 447 | name: "ISS", 448 | long_name: Some("Instruction Specific Syndrome"), 449 | start: 0, 450 | width: 25, 451 | value: 7696, 452 | description: None, 453 | subfields: vec![ 454 | FieldInfo { 455 | name: "RES0", 456 | long_name: Some("Reserved"), 457 | start: 13, 458 | width: 12, 459 | value: 0, 460 | description: None, 461 | subfields: vec![], 462 | }, 463 | FieldInfo { 464 | name: "SET", 465 | long_name: Some("Synchronous Error Type"), 466 | start: 11, 467 | width: 2, 468 | value: 3, 469 | description: Some("Restartable state (UEO)".to_string()), 470 | subfields: vec![], 471 | }, 472 | FieldInfo { 473 | name: "FnV", 474 | long_name: Some("FAR not Valid"), 475 | start: 10, 476 | width: 1, 477 | value: 1, 478 | description: Some( 479 | "FAR is not valid, it holds an unknown value".to_string() 480 | ), 481 | subfields: vec![], 482 | }, 483 | FieldInfo { 484 | name: "EA", 485 | long_name: Some("External abort type"), 486 | start: 9, 487 | width: 1, 488 | value: 1, 489 | description: None, 490 | subfields: vec![], 491 | }, 492 | FieldInfo { 493 | name: "RES0", 494 | long_name: Some("Reserved"), 495 | start: 8, 496 | width: 1, 497 | value: 0, 498 | description: None, 499 | subfields: vec![], 500 | }, 501 | FieldInfo { 502 | name: "S1PTW", 503 | long_name: Some("Stage-1 translation table walk"), 504 | start: 7, 505 | width: 1, 506 | value: 0, 507 | description: None, 508 | subfields: vec![], 509 | }, 510 | FieldInfo { 511 | name: "RES0", 512 | long_name: Some("Reserved"), 513 | start: 6, 514 | width: 1, 515 | value: 0, 516 | description: None, 517 | subfields: vec![], 518 | }, 519 | FieldInfo { 520 | name: "IFSC", 521 | long_name: Some("Instruction Fault Status Code"), 522 | start: 0, 523 | width: 6, 524 | value: 16, 525 | description: Some( 526 | "Synchronous External abort, not on translation table \ 527 | walk or hardware update of translation table." 528 | .to_string() 529 | ), 530 | subfields: vec![], 531 | } 532 | ] 533 | } 534 | ] 535 | ); 536 | } 537 | 538 | #[test] 539 | fn sve() { 540 | assert_eq!( 541 | decode(0x1f300000).unwrap(), 542 | vec![ 543 | FieldInfo { 544 | name: "RES0", 545 | long_name: Some("Reserved"), 546 | start: 37, 547 | width: 27, 548 | value: 0, 549 | description: None, 550 | subfields: vec![], 551 | }, 552 | FieldInfo { 553 | name: "ISS2", 554 | long_name: None, 555 | start: 32, 556 | width: 5, 557 | value: 0, 558 | description: None, 559 | subfields: vec![], 560 | }, 561 | FieldInfo { 562 | name: "EC", 563 | long_name: Some("Exception Class"), 564 | start: 26, 565 | width: 6, 566 | value: 7, 567 | description: Some( 568 | "Trapped access to SVE, Advanced SIMD or floating point".to_string() 569 | ), 570 | subfields: vec![] 571 | }, 572 | FieldInfo { 573 | name: "IL", 574 | long_name: Some("Instruction Length"), 575 | start: 25, 576 | width: 1, 577 | value: 1, 578 | description: Some("32-bit instruction trapped".to_string()), 579 | subfields: vec![] 580 | }, 581 | FieldInfo { 582 | name: "ISS", 583 | long_name: Some("Instruction Specific Syndrome"), 584 | start: 0, 585 | width: 25, 586 | value: 19922944, 587 | description: None, 588 | subfields: vec![ 589 | FieldInfo { 590 | name: "CV", 591 | long_name: Some("Condition code valid"), 592 | start: 24, 593 | width: 1, 594 | value: 1, 595 | description: Some("COND is valid".to_string()), 596 | subfields: vec![] 597 | }, 598 | FieldInfo { 599 | name: "COND", 600 | long_name: Some("Condition code of the trapped instruction"), 601 | start: 20, 602 | width: 4, 603 | value: 3, 604 | description: None, 605 | subfields: vec![] 606 | }, 607 | FieldInfo { 608 | name: "RES0", 609 | long_name: Some("Reserved"), 610 | start: 0, 611 | width: 20, 612 | value: 0, 613 | description: None, 614 | subfields: vec![] 615 | } 616 | ] 617 | }, 618 | ] 619 | ); 620 | } 621 | 622 | #[test] 623 | fn ld64b() { 624 | assert_eq!( 625 | decode(0x2a000002).unwrap(), 626 | vec![ 627 | FieldInfo { 628 | name: "RES0", 629 | long_name: Some("Reserved"), 630 | start: 37, 631 | width: 27, 632 | value: 0, 633 | description: None, 634 | subfields: vec![], 635 | }, 636 | FieldInfo { 637 | name: "ISS2", 638 | long_name: None, 639 | start: 32, 640 | width: 5, 641 | value: 0, 642 | description: None, 643 | subfields: vec![], 644 | }, 645 | FieldInfo { 646 | name: "EC", 647 | long_name: Some("Exception Class"), 648 | start: 26, 649 | width: 6, 650 | value: 10, 651 | description: Some( 652 | "Trapped execution of an LD64B, ST64B, ST64BV, or ST64BV0 instruction" 653 | .to_string() 654 | ), 655 | subfields: vec![] 656 | }, 657 | FieldInfo { 658 | name: "IL", 659 | long_name: Some("Instruction Length"), 660 | start: 25, 661 | width: 1, 662 | value: 1, 663 | description: Some("32-bit instruction trapped".to_string()), 664 | subfields: vec![] 665 | }, 666 | FieldInfo { 667 | name: "ISS", 668 | long_name: Some("Instruction Specific Syndrome"), 669 | start: 0, 670 | width: 25, 671 | value: 2, 672 | description: None, 673 | subfields: vec![FieldInfo { 674 | name: "ISS", 675 | long_name: None, 676 | start: 0, 677 | width: 25, 678 | value: 2, 679 | description: Some("LD64B or ST64B trapped".to_string()), 680 | subfields: vec![] 681 | }] 682 | } 683 | ] 684 | ); 685 | } 686 | --------------------------------------------------------------------------------