mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
185 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9a39cf5b5 | ||
|
|
f6326df68b | ||
|
|
7571e93a69 | ||
|
|
ab6e1bd2d8 | ||
|
|
517ea00bd9 | ||
|
|
ea6d5a04fa | ||
|
|
799336459d | ||
|
|
f934e142ba | ||
|
|
0cde3df44a | ||
|
|
7585e16f9d | ||
|
|
ab575a54b9 | ||
|
|
d2ca7b0e87 | ||
|
|
dd9bce31e8 | ||
|
|
a6370b74d9 | ||
|
|
d172a5dddb | ||
|
|
d46517559b | ||
|
|
0463f4f156 | ||
|
|
fff62d9302 | ||
|
|
5db92f79bf | ||
|
|
06f3049d24 | ||
|
|
28e0a95450 | ||
|
|
a615c809eb | ||
|
|
a3dcaedf7e | ||
|
|
eb0b14f70e | ||
|
|
80e66657cc | ||
|
|
fd5cfb75f3 | ||
|
|
8696bc6604 | ||
|
|
93b599e59e | ||
|
|
01761c2596 | ||
|
|
a212343908 | ||
|
|
d1a406143d | ||
|
|
38380198e1 | ||
|
|
0e40a50407 | ||
|
|
262984f8fc | ||
|
|
dce82a34d0 | ||
|
|
1454d39b28 | ||
|
|
2588fc9e42 | ||
|
|
67793ca375 | ||
|
|
96deca9dc9 | ||
|
|
78b0f68b21 | ||
|
|
a007b750ff | ||
|
|
b2993fb2fa | ||
|
|
8d11bf7979 | ||
|
|
d4718a9bc8 | ||
|
|
05968f5c2c | ||
|
|
71a36e0c69 | ||
|
|
2da5e10689 | ||
|
|
a647d7863c | ||
|
|
a23d4b7cf2 | ||
|
|
f3209e4f78 | ||
|
|
596d6453c5 | ||
|
|
1307d31ede | ||
|
|
2f21827430 | ||
|
|
8da7c13e26 | ||
|
|
aaf86ef209 | ||
|
|
59f98a2edc | ||
|
|
cb52cf91df | ||
|
|
5814193722 | ||
|
|
10ee003d66 | ||
|
|
8f402687ce | ||
|
|
f72f16ee50 | ||
|
|
034aa514d7 | ||
|
|
4e48f429eb | ||
|
|
50b0493371 | ||
|
|
df418a2153 | ||
|
|
a072b9a356 | ||
|
|
691fe0ca68 | ||
|
|
aaa3a114dd | ||
|
|
77e042237d | ||
|
|
f16d45e86e | ||
|
|
3eb6b69dd2 | ||
|
|
fa2b5ca12b | ||
|
|
eac44e6af0 | ||
|
|
40055595ff | ||
|
|
149c08981b | ||
|
|
f1f6814774 | ||
|
|
58f71b0c44 | ||
|
|
1968805ba2 | ||
|
|
d0225fe68f | ||
|
|
87007a288f | ||
|
|
f67484a714 | ||
|
|
3c63779302 | ||
|
|
1c22c4a57b | ||
|
|
4af98f4cb2 | ||
|
|
c58c686f3e | ||
|
|
9b13d63f28 | ||
|
|
b5aeb1f63c | ||
|
|
af2ce3e0cc | ||
|
|
7fd1b9a284 | ||
|
|
e69acd1dc3 | ||
|
|
de764456ce | ||
|
|
0095556847 | ||
|
|
a0065452d8 | ||
|
|
56ca26e72c | ||
|
|
21b9806cad | ||
|
|
d374101f38 | ||
|
|
461e31e6d8 | ||
|
|
d22e7e2638 | ||
|
|
960d702255 | ||
|
|
1105243aca | ||
|
|
317f8002d7 | ||
|
|
278b57ba49 | ||
|
|
d7f87425cd | ||
|
|
573868c035 | ||
|
|
2798286278 | ||
|
|
bcc314eb8b | ||
|
|
af28569611 | ||
|
|
1a5dcc5e70 | ||
|
|
29b089296d | ||
|
|
6e4bf84383 | ||
|
|
226ecf47bb | ||
|
|
0e3733e938 | ||
|
|
f811e36443 | ||
|
|
a64ba04e78 | ||
|
|
b686a5bb24 | ||
|
|
ea4693a091 | ||
|
|
3698b8e74c | ||
|
|
6bb33601d0 | ||
|
|
3d88996e5b | ||
|
|
3899cd0e30 | ||
|
|
9d93df4c8f | ||
|
|
4c27572b87 | ||
|
|
19fea1ad88 | ||
|
|
9a96ea44be | ||
|
|
e11b9d6449 | ||
|
|
ce5fe7f89d | ||
|
|
0dd0364bf8 | ||
|
|
3081d01fc3 | ||
|
|
fa58b725cb | ||
|
|
a38b4c7f17 | ||
|
|
5aa1540c5d | ||
|
|
7da8ae9c0f | ||
|
|
108e8f9dff | ||
|
|
5ab2feae70 | ||
|
|
c99660a8f9 | ||
|
|
08c1bb4b00 | ||
|
|
5bd177dde8 | ||
|
|
52ed1082fb | ||
|
|
03a23309b9 | ||
|
|
fa0965a11a | ||
|
|
524f26abef | ||
|
|
846d0976e3 | ||
|
|
4718dc1b72 | ||
|
|
0a9895e009 | ||
|
|
297b41d615 | ||
|
|
396aba7a52 | ||
|
|
35aaa1333a | ||
|
|
6768a13d95 | ||
|
|
0d50d0b257 | ||
|
|
f5a2f8473b | ||
|
|
03bb59db69 | ||
|
|
9e1fb3bfa1 | ||
|
|
4de422ad69 | ||
|
|
c441bb2968 | ||
|
|
5cb5115456 | ||
|
|
0fbba66b21 | ||
|
|
5388fa1ced | ||
|
|
655d512523 | ||
|
|
7d45473ff7 | ||
|
|
294d5fbb08 | ||
|
|
8c796e6d5d | ||
|
|
630f5c1f14 | ||
|
|
5cbf907200 | ||
|
|
bfeab76059 | ||
|
|
12143ca72f | ||
|
|
ba05bdd085 | ||
|
|
171b28ce52 | ||
|
|
5b929cb277 | ||
|
|
ee0b0d43d8 | ||
|
|
061482340e | ||
|
|
c43ae1dc9d | ||
|
|
7db3002e1d | ||
|
|
07b7457c2e | ||
|
|
dc9142f86c | ||
|
|
6fa7c84d2e | ||
|
|
cc734e2e44 | ||
|
|
e3e83362d6 | ||
|
|
730cf1e944 | ||
|
|
1caa69d0cc | ||
|
|
abcbf43433 | ||
|
|
af74de2779 | ||
|
|
dfebc8929d | ||
|
|
9988379f6b | ||
|
|
4d12192490 | ||
|
|
9f9cfeb5be |
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.77.0"
|
||||
- "1.83.0"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
|
||||
14
.github/workflows/docker-image.yaml
vendored
14
.github/workflows/docker-image.yaml
vendored
@@ -21,13 +21,13 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to ghcr
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -36,17 +36,17 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value=20250111-0
|
||||
type=raw,value=20250412-0
|
||||
type=sha
|
||||
|
||||
- name: Build and push
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: ./resources/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
- name: Build only
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: ./resources/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
11
.github/workflows/integration-metrics.yaml
vendored
11
.github/workflows/integration-metrics.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Tests (Metrics)
|
||||
runs-on: garm-jammy-16
|
||||
runs-on: bare-metal-9950x
|
||||
env:
|
||||
METRICS_PUBLISH_KEY: ${{ secrets.METRICS_PUBLISH_KEY }}
|
||||
steps:
|
||||
@@ -15,15 +15,6 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Docker
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ca-certificates curl gnupg
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt install -y docker-ce docker-ce-cli
|
||||
- name: Run metrics tests
|
||||
timeout-minutes: 60
|
||||
run: scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json
|
||||
|
||||
12
.github/workflows/integration-rate-limiter.yaml
vendored
12
.github/workflows/integration-rate-limiter.yaml
vendored
@@ -7,7 +7,7 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
name: Tests (Rate-Limiter)
|
||||
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'garm-jammy-16' }}
|
||||
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'bare-metal-9950x' }}
|
||||
env:
|
||||
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
|
||||
steps:
|
||||
@@ -16,16 +16,6 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Docker
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ca-certificates curl gnupg
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt install -y docker-ce docker-ce-cli
|
||||
- name: Run rate-limiter integration tests
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
timeout-minutes: 20
|
||||
|
||||
16
.github/workflows/lychee.yaml
vendored
Normal file
16
.github/workflows/lychee.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Link Check (lychee)
|
||||
on:
|
||||
pull_request
|
||||
|
||||
jobs:
|
||||
link_check:
|
||||
name: Link Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Link Availability Check
|
||||
uses: lycheeverse/lychee-action@master
|
||||
with:
|
||||
args: --verbose --config .lychee.toml .
|
||||
2
.github/workflows/preview-riscv64.yaml
vendored
2
.github/workflows/preview-riscv64.yaml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.77.0
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.83.0
|
||||
|
||||
- name: Build ${{ matrix.module }} Module (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo rustc --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
|
||||
14
.github/workflows/quality.yaml
vendored
14
.github/workflows/quality.yaml
vendored
@@ -56,6 +56,20 @@ jobs:
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
|
||||
- name: Clippy (mshv)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
|
||||
- name: Clippy (mshv + kvm)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
|
||||
- name: Clippy (default features)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: ${{ matrix.platform.args }}
|
||||
strip: true
|
||||
toolchain: "1.77.0"
|
||||
toolchain: "1.83.0"
|
||||
- name: Copy Release Binaries
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
shell: bash
|
||||
|
||||
6
.gitlint
6
.gitlint
@@ -1,6 +1,7 @@
|
||||
[general]
|
||||
extra-path=scripts/gitlint/rules.py
|
||||
extra-path=scripts/gitlint/rules
|
||||
regex-style-search=true
|
||||
ignore=body-max-line-length
|
||||
|
||||
[ignore-by-author-name]
|
||||
regex=dependabot
|
||||
@@ -10,6 +11,3 @@ ignore=all
|
||||
[title-max-length]
|
||||
line-length=72
|
||||
|
||||
# default 80
|
||||
[body-max-line-length]
|
||||
line-length=72
|
||||
|
||||
21
.lychee.toml
Normal file
21
.lychee.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
verbose = "info"
|
||||
|
||||
exclude = [
|
||||
# Availability of links below should be manually verified.
|
||||
# Page for intel SGX support, returns 403 while querying.
|
||||
'^https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html',
|
||||
# Page for intel TDX support, returns 403 while querying.
|
||||
'^https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html',
|
||||
# Page for TPM, returns 403 while querying.
|
||||
'^https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p05p_r14_pub.pdf',
|
||||
|
||||
# GitHub user smibarber referenced in `CREDITS.md` no longer exist
|
||||
'^https://github.com/smibarber',
|
||||
|
||||
# OSDev has added bot protection and accesses my result in 403 Forbidden.
|
||||
'^https://wiki.osdev.org',
|
||||
]
|
||||
|
||||
max_retries = 3
|
||||
|
||||
retry_wait_time = 5
|
||||
@@ -15,6 +15,7 @@ EXTINT = "EXTINT"
|
||||
INOUT = "INOUT"
|
||||
liness = "liness"
|
||||
outout = "outout"
|
||||
TRANSLATER = "TRANSLATER"
|
||||
|
||||
[default.extend-identifiers]
|
||||
fo = "fo"
|
||||
|
||||
@@ -13,7 +13,7 @@ License](https://opensource.org/licenses/Apache-2.0).
|
||||
|
||||
## Coding Style
|
||||
|
||||
We follow the [Rust Style](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md)
|
||||
We follow the [Rust Style](https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/src)
|
||||
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
||||
for each submitted Pull Request (PR).
|
||||
|
||||
@@ -36,7 +36,7 @@ commit you make.
|
||||
|
||||
## Certificate of Origin
|
||||
|
||||
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://01.org/community/signed-process)
|
||||
In order to get a clear contribution chain of trust we use the [signed-off-by language](https://web.archive.org/web/20230406041855/https://01.org/community/signed-process)
|
||||
used by the Linux kernel project.
|
||||
|
||||
## Patch format
|
||||
@@ -79,12 +79,12 @@ you want to merge your changes to `cloud-hypervisor`:
|
||||
|
||||
1. Fork the [cloud-hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) project
|
||||
into your github organization.
|
||||
2. Within your fork, create a branch for your contribution.
|
||||
3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||
1. Within your fork, create a branch for your contribution.
|
||||
1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)
|
||||
against the main branch of the Cloud Hypervisor repository.
|
||||
4. To update your pull request amend existing commits whenever applicable and
|
||||
1. To update your pull request amend existing commits whenever applicable and
|
||||
then push the new changes to your pull request branch.
|
||||
5. Once the pull request is approved it can be integrated.
|
||||
1. Once the pull request is approved it can be integrated.
|
||||
|
||||
## Issue tracking
|
||||
|
||||
|
||||
231
Cargo.lock
generated
231
Cargo.lock
generated
@@ -1,13 +1,13 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e268627630839bd22f1c13e7e81ec70c7e9b73d6"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e08a3f0b0a59b98859dbf59f5aa7fd4d2eb4018a"
|
||||
dependencies = [
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -66,11 +66,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -137,12 +137,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f2776ead772134d55b62dd45e59a79e21612d85d0af729b8b7d3967d601a62a"
|
||||
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -258,9 +257,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.85"
|
||||
version = "0.1.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||
checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -296,9 +295,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-struct"
|
||||
version = "0.7.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c2ce686adbebce0ee484a502c440b4657739adbad65eadf06d64f5816ee9765"
|
||||
checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -313,9 +312,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
@@ -374,9 +373,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.99"
|
||||
version = "1.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
|
||||
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -420,7 +422,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "44.0.0"
|
||||
version = "46.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -565,7 +567,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"byteorder",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -611,23 +613,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -686,15 +688,15 @@ version = "4.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
@@ -876,7 +878,7 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6341b3480afbb34eaefc7f92713bc92f2d83e338aaa1c44192f9c2956f4a4903"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
@@ -913,10 +915,22 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
@@ -931,9 +945,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -959,6 +973,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"bitfield-struct",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"concat-idents",
|
||||
@@ -972,12 +987,15 @@ dependencies = [
|
||||
"log",
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"open-enum",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"thiserror 2.0.6",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -997,9 +1015,8 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "igvm"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7984b10433b50e06a06bd50c69bca4888a5d7de8975f64ea4c2a7687eb99b09d"
|
||||
version = "0.3.4"
|
||||
source = "git+https://github.com/microsoft/igvm?branch=main#01daa631a596459cb4de58505881007dd13d4410"
|
||||
dependencies = [
|
||||
"bitfield-struct",
|
||||
"crc32fast",
|
||||
@@ -1007,28 +1024,28 @@ dependencies = [
|
||||
"igvm_defs",
|
||||
"open-enum",
|
||||
"range_map_vec",
|
||||
"thiserror 1.0.62",
|
||||
"static_assertions",
|
||||
"thiserror 2.0.6",
|
||||
"tracing",
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "igvm_defs"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b64ec5588c475372ae830475d3ee9a7bd255407dcb9f03faf6d493556eb6105a"
|
||||
version = "0.3.4"
|
||||
source = "git+https://github.com/microsoft/igvm?branch=main#01daa631a596459cb4de58505881007dd13d4410"
|
||||
dependencies = [
|
||||
"bitfield-struct",
|
||||
"open-enum",
|
||||
"static_assertions",
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -1100,7 +1117,7 @@ version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -1135,7 +1152,7 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -1220,7 +1237,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#ef96f623c46e221ebf9b6037567f97ec57683afd"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4f621532e81ee2ad096a9c9592fdacc40d19de48"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -1228,9 +1245,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -1243,23 +1260,23 @@ checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
|
||||
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.3.2"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0cb5031f3243a7459b7c13d960d25420980874eebda816db24ce6077e21d43"
|
||||
checksum = "909de5fd4a5a3347a6c62872f6816e6279efd8615a753f10a3bc4daaef8a72ef"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num_enum",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"vmm-sys-util",
|
||||
"zerocopy 0.8.8",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.3.2"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89abe853221fa6f14ad4066affb9abda241a03d65622887d5794e1422d0bd75a"
|
||||
checksum = "8c7d94972588d562bd349b916de6a43f2ee268e6e9c91cfb5b30549ed4ea2751"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -1273,7 +1290,7 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1288,7 +1305,7 @@ name = "net_util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
@@ -1312,7 +1329,7 @@ version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -1421,6 +1438,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror 2.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-stream"
|
||||
@@ -1672,18 +1692,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.1.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
||||
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
|
||||
dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1724,7 +1744,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1764,13 +1784,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"libredox",
|
||||
"thiserror 1.0.62",
|
||||
"thiserror 2.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1831,7 +1851,7 @@ version = "0.38.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -1840,9 +1860,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -1852,9 +1872,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
|
||||
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1940,10 +1960,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
@@ -2051,6 +2077,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"ssh2",
|
||||
"thiserror 2.0.6",
|
||||
"vmm-sys-util",
|
||||
"wait-timeout",
|
||||
]
|
||||
@@ -2103,15 +2130,15 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.1"
|
||||
version = "0.22.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -2192,9 +2219,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
@@ -2208,7 +2235,7 @@ version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"rand",
|
||||
"uuid-macro-internal",
|
||||
]
|
||||
@@ -2239,15 +2266,15 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#bdbb1cd486484faa23db48c82d51484e2ee43692"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#3d158a14460cac7ca3c99c2effa0a46880935cb0"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#bdbb1cd486484faa23db48c82d51484e2ee43692"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#3d158a14460cac7ca3c99c2effa0a46880935cb0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -2284,7 +2311,7 @@ name = "vhost"
|
||||
version = "0.12.1"
|
||||
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"libc",
|
||||
"uuid",
|
||||
"vm-memory",
|
||||
@@ -2316,6 +2343,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"option_parser",
|
||||
"thiserror 2.0.6",
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"virtio-bindings",
|
||||
@@ -2335,6 +2363,7 @@ dependencies = [
|
||||
"log",
|
||||
"net_util",
|
||||
"option_parser",
|
||||
"thiserror 2.0.6",
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"virtio-bindings",
|
||||
@@ -2461,7 +2490,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"arch",
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"block",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
@@ -2509,7 +2538,7 @@ dependencies = [
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
"zbus",
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2539,6 +2568,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
@@ -2765,13 +2803,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.40"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg-home"
|
||||
version = "1.3.0"
|
||||
@@ -2856,11 +2903,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.8"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a4e33e6dce36f2adba29746927f8e848ba70989fdb61c772773bbdda8b5d6a7"
|
||||
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.8",
|
||||
"zerocopy-derive 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2876,9 +2923,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.8"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cd137b4cc21bde6ecce3bbbb3350130872cda0be2c6888874279ea76e17d4c1"
|
||||
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
29
Cargo.toml
29
Cargo.toml
@@ -7,15 +7,15 @@ edition = "2021"
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
license = "Apache-2.0 AND BSD-3-Clause"
|
||||
name = "cloud-hypervisor"
|
||||
version = "44.0.0"
|
||||
version = "46.0.0"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
# Policy on MSRV (see #4318):
|
||||
# Can only be bumped by:
|
||||
# Can only be bumped if satisfying any of the following:
|
||||
# a.) A dependency requires it,
|
||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||
# c.) There is a security issue that is addressed by the toolchain update.
|
||||
rust-version = "1.77.0"
|
||||
rust-version = "1.83.0"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
@@ -40,8 +40,8 @@ libc = "0.2.167"
|
||||
log = { version = "0.4.22", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = { workspace = true }
|
||||
serde_json = "1.0.120"
|
||||
signal-hook = "0.3.17"
|
||||
serde_json = { workspace = true }
|
||||
signal-hook = "0.3.18"
|
||||
thiserror = "2.0.6"
|
||||
tpm = { path = "tpm" }
|
||||
tracer = { path = "tracer" }
|
||||
@@ -51,10 +51,10 @@ vmm-sys-util = { workspace = true }
|
||||
zbus = { version = "4.4.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "5.0.1"
|
||||
dirs = "6.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.20.2"
|
||||
serde_json = "1.0.120"
|
||||
serde_json = { workspace = true }
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -102,13 +102,14 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
# rust-vmm crates
|
||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
kvm-bindings = "0.10.0"
|
||||
kvm-ioctls = "0.19.1"
|
||||
linux-loader = "0.13.0"
|
||||
mshv-bindings = "0.3.2"
|
||||
mshv-ioctls = "0.3.2"
|
||||
seccompiler = "0.4.0"
|
||||
mshv-bindings = "0.5.1"
|
||||
mshv-ioctls = "0.5.1"
|
||||
seccompiler = "0.5.0"
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main" }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
|
||||
@@ -119,3 +120,11 @@ virtio-queue = "0.14.0"
|
||||
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
||||
vm-memory = "0.16.1"
|
||||
vmm-sys-util = "0.12.1"
|
||||
|
||||
# igvm crates
|
||||
# TODO: bump to 0.3.5 release
|
||||
igvm = { git = "https://github.com/microsoft/igvm", branch = "main" }
|
||||
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main" }
|
||||
|
||||
# serde crates
|
||||
serde_json = "1.0.120"
|
||||
|
||||
15
README.md
15
README.md
@@ -59,9 +59,13 @@ based on the [Rust VMM](https://github.com/rust-vmm) crates.
|
||||
|
||||
### Architectures
|
||||
|
||||
Cloud Hypervisor supports the `x86-64` and `AArch64` architectures. There are
|
||||
minor differences in functionality between the two architectures
|
||||
(see [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125)).
|
||||
Cloud Hypervisor supports the `x86-64`, `AArch64` and `riscv64`
|
||||
architectures, with functionality varying across these platforms. The
|
||||
functionality differences between `x86-64` and `AArch64` are documented
|
||||
in [#1125](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/1125).
|
||||
The `riscv64` architecture support is experimental and offers limited
|
||||
functionality. For more details and instructions, please refer to [riscv
|
||||
documentation](docs/riscv.md).
|
||||
|
||||
### Guest OS
|
||||
|
||||
@@ -297,8 +301,9 @@ Further details can be found in the [release documentation](docs/releases.md).
|
||||
As of 2023-01-03, the following cloud images are supported:
|
||||
|
||||
- [Ubuntu Focal](https://cloud-images.ubuntu.com/focal/current/) (focal-server-cloudimg-{amd64,arm64}.img)
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img )
|
||||
- [Fedora 36](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://fedora.mirrorservice.org/fedora/linux/releases/36/Cloud/aarch64/images/))
|
||||
- [Ubuntu Jammy](https://cloud-images.ubuntu.com/jammy/current/) (jammy-server-cloudimg-{amd64,arm64}.img)
|
||||
- [Ubuntu Noble](https://cloud-images.ubuntu.com/noble/current/) (noble-server-cloudimg-{amd64,arm64}.img)
|
||||
- [Fedora 36](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/) ([Fedora-Cloud-Base-36-1.5.x86_64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/x86_64/images/) / [Fedora-Cloud-Base-36-1.5.aarch64.raw.xz](https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/36/Cloud/aarch64/images/))
|
||||
|
||||
Direct kernel boot to userspace should work with a rootfs from most
|
||||
distributions although you may need to enable exotic filesystem types in the
|
||||
|
||||
@@ -12,15 +12,15 @@ use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Error writing to or reading from HTTP socket: {0}")]
|
||||
Socket(std::io::Error),
|
||||
Socket(#[source] std::io::Error),
|
||||
#[error("Error sending file descriptors: {0}")]
|
||||
SocketSendFds(vmm_sys_util::errno::Error),
|
||||
SocketSendFds(#[source] vmm_sys_util::errno::Error),
|
||||
#[error("Error parsing HTTP status code: {0}")]
|
||||
StatusCodeParsing(std::num::ParseIntError),
|
||||
StatusCodeParsing(#[source] std::num::ParseIntError),
|
||||
#[error("HTTP output is missing protocol statement")]
|
||||
MissingProtocol,
|
||||
#[error("Error parsing HTTP Content-Length field: {0}")]
|
||||
ContentLengthParsing(std::num::ParseIntError),
|
||||
ContentLengthParsing(#[source] std::num::ParseIntError),
|
||||
#[error("Server responded with an error: {0:?}: {1:?}")]
|
||||
ServerResponse(StatusCode, Option<String>),
|
||||
}
|
||||
@@ -32,6 +32,7 @@ pub enum StatusCode {
|
||||
NoContent,
|
||||
BadRequest,
|
||||
NotFound,
|
||||
TooManyRequests,
|
||||
InternalServerError,
|
||||
NotImplemented,
|
||||
Unknown,
|
||||
@@ -45,6 +46,7 @@ impl StatusCode {
|
||||
204 => StatusCode::NoContent,
|
||||
400 => StatusCode::BadRequest,
|
||||
404 => StatusCode::NotFound,
|
||||
429 => StatusCode::TooManyRequests,
|
||||
500 => StatusCode::InternalServerError,
|
||||
501 => StatusCode::NotImplemented,
|
||||
_ => StatusCode::Unknown,
|
||||
|
||||
@@ -15,14 +15,18 @@ use std::{cmp, fs, result, str};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use hypervisor::arch::aarch64::gic::Vgic;
|
||||
use hypervisor::arch::aarch64::regs::{
|
||||
AARCH64_ARCH_TIMER_HYP_IRQ, AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
|
||||
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ, AARCH64_ARCH_TIMER_VIRT_IRQ, AARCH64_PMU_IRQ,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||
|
||||
use super::super::{DeviceType, GuestMemoryMmap, InitramfsConfig};
|
||||
use super::layout::{
|
||||
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
||||
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||
GIC_V2M_COMPATIBLE, IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE,
|
||||
MEM_PCI_IO_START, PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, SPI_BASE, SPI_NUM,
|
||||
};
|
||||
use crate::{NumaNodes, PciSpaceInfo};
|
||||
|
||||
@@ -59,9 +63,6 @@ const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||
|
||||
// PMU PPI interrupt number
|
||||
pub const AARCH64_PMU_IRQ: u32 = 7;
|
||||
|
||||
// Keys and Buttons
|
||||
// System Power Down
|
||||
const KEY_POWER: u32 = 116;
|
||||
@@ -81,7 +82,7 @@ pub trait DeviceInfoForFdt {
|
||||
pub enum Error {
|
||||
/// Failure in writing FDT in memory.
|
||||
#[error("Failure in writing FDT in memory: {0}")]
|
||||
WriteFdtToMemory(GuestMemoryError),
|
||||
WriteFdtToMemory(#[source] GuestMemoryError),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -585,15 +586,14 @@ fn create_memory_node(
|
||||
&& (first_region_end <= &mem_32bit_reserved_start))
|
||||
{
|
||||
panic!(
|
||||
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
|
||||
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
|
||||
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
|
||||
"Unexpected first memory region layout: (start: 0x{first_region_start:08x}, end: 0x{first_region_end:08x}).
|
||||
ram_start: 0x{ram_start:08x}, mem_32bit_reserved_start: 0x{mem_32bit_reserved_start:08x}"
|
||||
);
|
||||
}
|
||||
|
||||
let mem_size = first_region_end - ram_start;
|
||||
let mem_reg_prop = [ram_start, mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", ram_start);
|
||||
let memory_node_name = format!("memory@{ram_start:x}");
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
@@ -606,14 +606,13 @@ fn create_memory_node(
|
||||
|
||||
if second_region_start != &ram_64bit_start {
|
||||
panic!(
|
||||
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
|
||||
second_region_start, ram_64bit_start
|
||||
"Unexpected second memory region layout: start: 0x{second_region_start:08x}, ram_64bit_start: 0x{ram_64bit_start:08x}"
|
||||
);
|
||||
}
|
||||
|
||||
let mem_size = second_region_end - ram_64bit_start;
|
||||
let mem_reg_prop = [ram_64bit_start, mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
|
||||
let memory_node_name = format!("memory@{ram_64bit_start:x}");
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
@@ -670,11 +669,19 @@ fn create_gic_node(fdt: &mut FdtWriter, gic_device: &Arc<Mutex<dyn Vgic>>) -> Fd
|
||||
|
||||
if gic_device.lock().unwrap().msi_compatible() {
|
||||
let msic_node = fdt.begin_node("msic")?;
|
||||
fdt.property_string("compatible", gic_device.lock().unwrap().msi_compatibility())?;
|
||||
let msi_compatibility = gic_device.lock().unwrap().msi_compatibility().to_string();
|
||||
|
||||
fdt.property_string("compatible", msi_compatibility.as_str())?;
|
||||
fdt.property_null("msi-controller")?;
|
||||
fdt.property_u32("phandle", MSI_PHANDLE)?;
|
||||
let msi_reg_prop = gic_device.lock().unwrap().msi_properties();
|
||||
fdt.property_array_u64("reg", &msi_reg_prop)?;
|
||||
|
||||
if msi_compatibility == GIC_V2M_COMPATIBLE {
|
||||
fdt.property_u32("arm,msi-base-spi", SPI_BASE)?;
|
||||
fdt.property_u32("arm,msi-num-spis", SPI_NUM)?;
|
||||
}
|
||||
|
||||
fdt.end_node(msic_node)?;
|
||||
}
|
||||
|
||||
@@ -701,9 +708,14 @@ fn create_clock_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
||||
|
||||
fn create_timer_node(fdt: &mut FdtWriter) -> FdtWriterResult<()> {
|
||||
// See
|
||||
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/interrupt-controller/arch_timer.txt
|
||||
// https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/timer/arm%2Carch_timer.yaml
|
||||
// These are fixed interrupt numbers for the timer device.
|
||||
let irqs = [13, 14, 11, 10];
|
||||
let irqs = [
|
||||
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ,
|
||||
AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
|
||||
AARCH64_ARCH_TIMER_VIRT_IRQ,
|
||||
AARCH64_ARCH_TIMER_HYP_IRQ,
|
||||
];
|
||||
let compatible = "arm,armv8-timer";
|
||||
|
||||
let mut timer_reg_cells: Vec<u32> = Vec::new();
|
||||
@@ -1109,10 +1121,7 @@ fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
|
||||
// - At first, try to convert it to CStr and print,
|
||||
// - If failed, print it as u32 array.
|
||||
let value_result = match CStr::from_bytes_with_nul(value) {
|
||||
Ok(value_cstr) => match value_cstr.to_str() {
|
||||
Ok(value_str) => Some(value_str),
|
||||
Err(_e) => None,
|
||||
},
|
||||
Ok(value_cstr) => value_cstr.to_str().ok(),
|
||||
Err(_e) => None,
|
||||
};
|
||||
|
||||
|
||||
@@ -138,3 +138,12 @@ pub const IRQ_BASE: u32 = 32;
|
||||
|
||||
/// Number of supported interrupts
|
||||
pub const IRQ_NUM: u32 = 256;
|
||||
|
||||
/// Base SPI interrupt number
|
||||
pub const SPI_BASE: u32 = 32;
|
||||
|
||||
/// Total number of SPIs
|
||||
pub const SPI_NUM: u32 = 64;
|
||||
|
||||
/// GICv2M compatible string
|
||||
pub const GIC_V2M_COMPATIBLE: &str = "arm,gic-v2m-frame";
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
pub mod fdt;
|
||||
/// Layout for this aarch64 system.
|
||||
pub mod layout;
|
||||
/// Module for system registers definition
|
||||
pub mod regs;
|
||||
/// Module for loading UEFI binary.
|
||||
pub mod uefi;
|
||||
|
||||
@@ -16,6 +14,7 @@ use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use hypervisor::arch::aarch64::gic::Vgic;
|
||||
use hypervisor::arch::aarch64::regs::MPIDR_EL1;
|
||||
use log::{log_enabled, Level};
|
||||
use thiserror::Error;
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||
@@ -34,7 +33,7 @@ pub enum Error {
|
||||
|
||||
/// Failed to write FDT to memory.
|
||||
#[error("Failed to write FDT to memory: {0}")]
|
||||
WriteFdtToMemory(fdt::Error),
|
||||
WriteFdtToMemory(#[source] fdt::Error),
|
||||
|
||||
/// Failed to create a GIC.
|
||||
#[error("Failed to create a GIC")]
|
||||
@@ -46,11 +45,11 @@ pub enum Error {
|
||||
|
||||
/// Error configuring the general purpose registers
|
||||
#[error("Error configuring the general purpose registers: {0}")]
|
||||
RegsConfiguration(hypervisor::HypervisorCpuError),
|
||||
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
|
||||
|
||||
/// Error configuring the MPIDR register
|
||||
#[error("Error configuring the MPIDR register: {0}")]
|
||||
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
||||
VcpuRegMpidr(#[source] hypervisor::HypervisorCpuError),
|
||||
|
||||
/// Error initializing PMU for vcpu
|
||||
#[error("Error initializing PMU for vcpu")]
|
||||
@@ -86,9 +85,7 @@ pub fn configure_vcpu(
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
}
|
||||
|
||||
let mpidr = vcpu
|
||||
.get_sys_reg(regs::MPIDR_EL1)
|
||||
.map_err(Error::VcpuRegMpidr)?;
|
||||
let mpidr = vcpu.get_sys_reg(MPIDR_EL1).map_err(Error::VcpuRegMpidr)?;
|
||||
Ok(mpidr)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// AArch64 system register encoding:
|
||||
// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||
//
|
||||
// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
//
|
||||
// Notes:
|
||||
// - L and Rt are reserved as implementation defined fields, ignored.
|
||||
|
||||
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
||||
const SYSREG_OP0_SHIFT: u32 = 19;
|
||||
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
||||
const SYSREG_OP1_SHIFT: u32 = 16;
|
||||
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
|
||||
const SYSREG_CRN_SHIFT: u32 = 12;
|
||||
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
|
||||
const SYSREG_CRM_SHIFT: u32 = 8;
|
||||
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
|
||||
const SYSREG_OP2_SHIFT: u32 = 5;
|
||||
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
|
||||
|
||||
/// Define the ID of system registers
|
||||
#[macro_export]
|
||||
macro_rules! arm64_sys_reg {
|
||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||
pub const $name: u32 = SYSREG_HEAD
|
||||
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
|
||||
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
|
||||
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
|
||||
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
|
||||
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
|
||||
};
|
||||
}
|
||||
|
||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
|
||||
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
|
||||
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
|
||||
@@ -28,14 +28,14 @@ type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitma
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[error("Platform specific error (x86_64): {0:?}")]
|
||||
PlatformSpecific(x86_64::Error),
|
||||
#[error("Platform specific error (x86_64): {0}")]
|
||||
PlatformSpecific(#[source] x86_64::Error),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Platform specific error (aarch64): {0:?}")]
|
||||
PlatformSpecific(aarch64::Error),
|
||||
PlatformSpecific(#[source] aarch64::Error),
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
#[error("Platform specific error (riscv64): {0:?}")]
|
||||
PlatformSpecific(riscv64::Error),
|
||||
PlatformSpecific(#[source] riscv64::Error),
|
||||
#[error("The memory map table extends past the end of guest memory")]
|
||||
MemmapTablePastRamEnd,
|
||||
#[error("Error writing memory map table to guest memory")]
|
||||
|
||||
@@ -55,7 +55,7 @@ pub trait DeviceInfoForFdt {
|
||||
pub enum Error {
|
||||
/// Failure in writing FDT in memory.
|
||||
#[error("Failure in writing FDT in memory: {0}")]
|
||||
WriteFdtToMemory(GuestMemoryError),
|
||||
WriteFdtToMemory(#[source] GuestMemoryError),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ pub enum Error {
|
||||
|
||||
/// Failed to write FDT to memory.
|
||||
#[error("Failed to write FDT to memory: {0}")]
|
||||
WriteFdtToMemory(fdt::Error),
|
||||
WriteFdtToMemory(#[source] fdt::Error),
|
||||
|
||||
/// Failed to create a AIA.
|
||||
#[error("Failed to create a AIA")]
|
||||
@@ -43,7 +43,7 @@ pub enum Error {
|
||||
|
||||
/// Error configuring the general purpose registers
|
||||
#[error("Error configuring the general purpose registers: {0}")]
|
||||
RegsConfiguration(hypervisor::HypervisorCpuError),
|
||||
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
|
||||
@@ -133,35 +133,35 @@ pub struct CpuidConfig {
|
||||
pub enum Error {
|
||||
/// Error writing MP table to memory.
|
||||
#[error("Error writing MP table to memory: {0}")]
|
||||
MpTableSetup(mptable::Error),
|
||||
MpTableSetup(#[source] mptable::Error),
|
||||
|
||||
/// Error configuring the general purpose registers
|
||||
#[error("Error configuring the general purpose registers: {0}")]
|
||||
RegsConfiguration(regs::Error),
|
||||
RegsConfiguration(#[source] regs::Error),
|
||||
|
||||
/// Error configuring the special registers
|
||||
#[error("Error configuring the special registers: {0}")]
|
||||
SregsConfiguration(regs::Error),
|
||||
SregsConfiguration(#[source] regs::Error),
|
||||
|
||||
/// Error configuring the floating point related registers
|
||||
#[error("Error configuring the floating point related registers: {0}")]
|
||||
FpuConfiguration(regs::Error),
|
||||
FpuConfiguration(#[source] regs::Error),
|
||||
|
||||
/// Error configuring the MSR registers
|
||||
#[error("Error configuring the MSR registers: {0}")]
|
||||
MsrsConfiguration(regs::Error),
|
||||
MsrsConfiguration(#[source] regs::Error),
|
||||
|
||||
/// Failed to set supported CPUs.
|
||||
#[error("Failed to set supported CPUs: {0}")]
|
||||
SetSupportedCpusFailed(anyhow::Error),
|
||||
SetSupportedCpusFailed(#[source] anyhow::Error),
|
||||
|
||||
/// Cannot set the local interruption due to bad configuration.
|
||||
#[error("Cannot set the local interruption due to bad configuration: {0}")]
|
||||
LocalIntConfiguration(anyhow::Error),
|
||||
LocalIntConfiguration(#[source] anyhow::Error),
|
||||
|
||||
/// Error setting up SMBIOS table
|
||||
#[error("Error setting up SMBIOS table: {0}")]
|
||||
SmbiosSetup(smbios::Error),
|
||||
SmbiosSetup(#[source] smbios::Error),
|
||||
|
||||
/// Could not find any SGX EPC section
|
||||
#[error("Could not find any SGX EPC section")]
|
||||
@@ -177,15 +177,15 @@ pub enum Error {
|
||||
|
||||
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
|
||||
#[error("Error getting supported CPUID through the hypervisor API: {0}")]
|
||||
CpuidGetSupported(HypervisorError),
|
||||
CpuidGetSupported(#[source] HypervisorError),
|
||||
|
||||
/// Error populating CPUID with KVM HyperV emulation details
|
||||
#[error("Error populating CPUID with KVM HyperV emulation details: {0}")]
|
||||
CpuidKvmHyperV(vmm_sys_util::fam::Error),
|
||||
CpuidKvmHyperV(#[source] vmm_sys_util::fam::Error),
|
||||
|
||||
/// Error populating CPUID with CPU identification
|
||||
#[error("Error populating CPUID with CPU identification: {0}")]
|
||||
CpuidIdentification(vmm_sys_util::fam::Error),
|
||||
CpuidIdentification(#[source] vmm_sys_util::fam::Error),
|
||||
|
||||
/// Error checking CPUID compatibility
|
||||
#[error("Error checking CPUID compatibility")]
|
||||
@@ -193,16 +193,16 @@ pub enum Error {
|
||||
|
||||
// Error writing EBDA address
|
||||
#[error("Error writing EBDA address: {0}")]
|
||||
EbdaSetup(vm_memory::GuestMemoryError),
|
||||
EbdaSetup(#[source] vm_memory::GuestMemoryError),
|
||||
|
||||
// Error getting CPU TSC frequency
|
||||
#[error("Error getting CPU TSC frequency: {0}")]
|
||||
GetTscFrequency(HypervisorCpuError),
|
||||
GetTscFrequency(#[source] HypervisorCpuError),
|
||||
|
||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||
#[cfg(feature = "tdx")]
|
||||
#[error("Error retrieving TDX capabilities through the hypervisor API: {0}")]
|
||||
TdxCapabilities(HypervisorError),
|
||||
TdxCapabilities(#[source] HypervisorError),
|
||||
|
||||
/// Failed to configure E820 map for bzImage
|
||||
#[error("Failed to configure E820 map for bzImage")]
|
||||
@@ -747,12 +747,12 @@ pub fn generate_common_cpuid(
|
||||
// These features are not supported by TDX
|
||||
#[cfg(feature = "tdx")]
|
||||
if config.tdx {
|
||||
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
|
||||
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
|
||||
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
|
||||
| 1 << KVM_FEATURE_ASYNC_PF_BIT
|
||||
| 1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT
|
||||
| 1 << KVM_FEATURE_STEAL_TIME_BIT)
|
||||
entry.eax &= !((1 << KVM_FEATURE_CLOCKSOURCE_BIT)
|
||||
| (1 << KVM_FEATURE_CLOCKSOURCE2_BIT)
|
||||
| (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)
|
||||
| (1 << KVM_FEATURE_ASYNC_PF_BIT)
|
||||
| (1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT)
|
||||
| (1 << KVM_FEATURE_STEAL_TIME_BIT))
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -1411,6 +1411,7 @@ fn update_cpuid_topology(
|
||||
u32::from(dies_per_package * cores_per_die * threads_per_core),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::ECX, 2 << 8);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(1), CpuidReg::EDX, x2apic_id);
|
||||
|
||||
// CPU Topology leaf 0x1f
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::EAX, thread_width);
|
||||
|
||||
@@ -60,31 +60,31 @@ pub enum Error {
|
||||
AddressOverflow,
|
||||
/// Failure while zeroing out the memory for the MP table.
|
||||
#[error("Failure while zeroing out the memory for the MP table: {0}")]
|
||||
Clear(GuestMemoryError),
|
||||
Clear(#[source] GuestMemoryError),
|
||||
/// Number of CPUs exceeds the maximum supported CPUs
|
||||
#[error("Number of CPUs exceeds the maximum supported CPUs")]
|
||||
TooManyCpus,
|
||||
/// Failure to write the MP floating pointer.
|
||||
#[error("Failure to write the MP floating pointer: {0}")]
|
||||
WriteMpfIntel(GuestMemoryError),
|
||||
WriteMpfIntel(#[source] GuestMemoryError),
|
||||
/// Failure to write MP CPU entry.
|
||||
#[error("Failure to write MP CPU entry: {0}")]
|
||||
WriteMpcCpu(GuestMemoryError),
|
||||
WriteMpcCpu(#[source] GuestMemoryError),
|
||||
/// Failure to write MP ioapic entry.
|
||||
#[error("Failure to write MP ioapic entry: {0}")]
|
||||
WriteMpcIoapic(GuestMemoryError),
|
||||
WriteMpcIoapic(#[source] GuestMemoryError),
|
||||
/// Failure to write MP bus entry.
|
||||
#[error("Failure to write MP bus entry: {0}")]
|
||||
WriteMpcBus(GuestMemoryError),
|
||||
WriteMpcBus(#[source] GuestMemoryError),
|
||||
/// Failure to write MP interrupt source entry.
|
||||
#[error("Failure to write MP interrupt source entry: {0}")]
|
||||
WriteMpcIntsrc(GuestMemoryError),
|
||||
WriteMpcIntsrc(#[source] GuestMemoryError),
|
||||
/// Failure to write MP local interrupt source entry.
|
||||
#[error("Failure to write MP local interrupt source entry: {0}")]
|
||||
WriteMpcLintsrc(GuestMemoryError),
|
||||
WriteMpcLintsrc(#[source] GuestMemoryError),
|
||||
/// Failure to write MP table header.
|
||||
#[error("Failure to write MP table header: {0}")]
|
||||
WriteMpcTable(GuestMemoryError),
|
||||
WriteMpcTable(#[source] GuestMemoryError),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -24,40 +24,40 @@ use crate::{EntryPoint, GuestMemoryMmap};
|
||||
pub enum Error {
|
||||
/// Failed to get SREGs for this CPU.
|
||||
#[error("Failed to get SREGs for this CPU: {0}")]
|
||||
GetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||
GetStatusRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Failed to set base registers for this CPU.
|
||||
#[error("Failed to set base registers for this CPU: {0}")]
|
||||
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
||||
SetBaseRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Failed to configure the FPU.
|
||||
#[error("Failed to configure the FPU: {0}")]
|
||||
SetFpuRegisters(hypervisor::HypervisorCpuError),
|
||||
SetFpuRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Setting up MSRs failed.
|
||||
#[error("Setting up MSRs failed: {0}")]
|
||||
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
||||
SetModelSpecificRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Failed to set SREGs for this CPU.
|
||||
#[error("Failed to set SREGs for this CPU: {0}")]
|
||||
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||
SetStatusRegisters(#[source] hypervisor::HypervisorCpuError),
|
||||
/// Checking the GDT address failed.
|
||||
#[error("Checking the GDT address failed")]
|
||||
CheckGdtAddr,
|
||||
/// Writing the GDT to RAM failed.
|
||||
#[error("Writing the GDT to RAM failed: {0}")]
|
||||
WriteGdt(GuestMemoryError),
|
||||
WriteGdt(#[source] GuestMemoryError),
|
||||
/// Writing the IDT to RAM failed.
|
||||
#[error("Writing the IDT to RAM failed: {0}")]
|
||||
WriteIdt(GuestMemoryError),
|
||||
WriteIdt(#[source] GuestMemoryError),
|
||||
/// Writing PDPTE to RAM failed.
|
||||
#[error("Writing PDPTE to RAM failed: {0}")]
|
||||
WritePdpteAddress(GuestMemoryError),
|
||||
WritePdpteAddress(#[source] GuestMemoryError),
|
||||
/// Writing PDE to RAM failed.
|
||||
#[error("Writing PDE to RAM failed: {0}")]
|
||||
WritePdeAddress(GuestMemoryError),
|
||||
WritePdeAddress(#[source] GuestMemoryError),
|
||||
/// Writing PML4 to RAM failed.
|
||||
#[error("Writing PML4 to RAM failed: {0}")]
|
||||
WritePml4Address(GuestMemoryError),
|
||||
WritePml4Address(#[source] GuestMemoryError),
|
||||
/// Writing PML5 to RAM failed.
|
||||
#[error("Writing PML5 to RAM failed: {0}")]
|
||||
WritePml5Address(GuestMemoryError),
|
||||
WritePml5Address(#[source] GuestMemoryError),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -34,7 +34,7 @@ pub enum Error {
|
||||
WriteData,
|
||||
/// Failure to parse uuid, uuid format may be error
|
||||
#[error("Failure to parse uuid: {0}")]
|
||||
ParseUuid(uuid::Error),
|
||||
ParseUuid(#[source] uuid::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -299,7 +299,7 @@ pub struct TdHob {
|
||||
}
|
||||
|
||||
fn align_hob(v: u64) -> u64 {
|
||||
(v + 7) / 8 * 8
|
||||
v.div_ceil(8) * 8
|
||||
}
|
||||
|
||||
impl TdHob {
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
|
||||
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -19,12 +22,50 @@ pub enum DiskFileError {
|
||||
|
||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||
|
||||
/// A wrapper for [`RawFd`] capturing the lifetime of a corresponding [`DiskFile`].
|
||||
///
|
||||
/// This fulfills the same role as [`BorrowedFd`] but is tailored to the limitations
|
||||
/// by some implementations of [`DiskFile`], which wrap the effective [`File`]
|
||||
/// in an `Arc<Mutex<T>>`, making the use of [`BorrowedFd`] impossible.
|
||||
///
|
||||
/// [`BorrowedFd`]: std::os::fd::BorrowedFd
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BorrowedDiskFd<'fd> {
|
||||
raw_fd: RawFd,
|
||||
_lifetime: PhantomData<&'fd OwnedFd>,
|
||||
}
|
||||
|
||||
impl BorrowedDiskFd<'_> {
|
||||
pub(super) fn new(raw_fd: RawFd) -> Self {
|
||||
Self {
|
||||
raw_fd,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for BorrowedDiskFd<'_> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.raw_fd
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over the effective [`File`] backing up a block device,
|
||||
/// with support for synchronous and asynchronous I/O.
|
||||
///
|
||||
/// This allows abstracting over raw image formats as well as structured
|
||||
/// image formats.
|
||||
pub trait DiskFile: Send {
|
||||
fn size(&mut self) -> DiskFileResult<u64>;
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
||||
fn topology(&mut self) -> DiskTopology {
|
||||
DiskTopology::default()
|
||||
}
|
||||
/// Returns the file descriptor of the underlying disk image file.
|
||||
///
|
||||
/// The file descriptor is supposed to be used for `fcntl()` calls but no
|
||||
/// other operation.
|
||||
fn fd(&mut self) -> BorrowedDiskFd;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
||||
173
block/src/fcntl.rs
Normal file
173
block/src/fcntl.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright © 2025 Cyberus Technology GmbH
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
//! Helpers for advisory file locking.
|
||||
//!
|
||||
//! Under the hood, the implementation uses OFD locks for the entire file,
|
||||
//! as described in [[0]]. The advantage over `F_SETLKW` (currently used by
|
||||
//! Rust std: `File::try_lock()`) is that only the very last `close()` on a
|
||||
//! file descriptor releases the lock. This prevents mistakes and unexpected
|
||||
//! behavior.
|
||||
//!
|
||||
//! [0]: <https://apenwarr.ca/log/20101213>.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can happen when working with file locks.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LockError {
|
||||
/// The file is already locked.
|
||||
///
|
||||
/// A call to [`get_lock_state`] can help to identify the reason.
|
||||
#[error("The file is already locked")]
|
||||
AlreadyLocked,
|
||||
/// IO error.
|
||||
#[error("The lock state could not be checked or set: {0}")]
|
||||
Io(#[source] io::Error),
|
||||
}
|
||||
|
||||
/// Commands for use with [`fcntl`].
|
||||
#[allow(non_camel_case_types)]
|
||||
enum FcntlArg<'a> {
|
||||
/// Set an OFD lock from the given lock description.
|
||||
F_OFD_SETLK(&'a libc::flock),
|
||||
/// Get the first OFD lock for the given lock description.
|
||||
F_OFD_GETLK(&'a mut libc::flock),
|
||||
}
|
||||
|
||||
/// Wrapper for [`libc::fcntl`] that properly sets the function arguments.
|
||||
fn fcntl(fd: RawFd, arg: FcntlArg) -> libc::c_int {
|
||||
// SAFETY: We use a valid FD.
|
||||
unsafe {
|
||||
match arg {
|
||||
FcntlArg::F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
|
||||
FcntlArg::F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the type of lock you want to set.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LockType {
|
||||
/// Clear a lock.
|
||||
Unlock,
|
||||
/// Set a write lock (exclusive).
|
||||
Write,
|
||||
/// Set a read lock (shared).
|
||||
Read,
|
||||
}
|
||||
|
||||
impl LockType {
|
||||
pub const fn to_libc_val(self) -> libc::c_int {
|
||||
match self {
|
||||
Self::Unlock => libc::F_UNLCK as libc::c_int,
|
||||
Self::Write => libc::F_WRLCK as libc::c_int,
|
||||
Self::Read => libc::F_RDLCK as libc::c_int,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the current state of a lock.
|
||||
#[derive(Debug)]
|
||||
pub enum LockState {
|
||||
/// No lock set.
|
||||
Unlocked,
|
||||
/// Locked for reading (non-exclusive).
|
||||
SharedRead,
|
||||
/// Locked for writing (exclusive mode).
|
||||
ExclusiveWrite,
|
||||
}
|
||||
|
||||
impl LockState {
|
||||
fn new(value: libc::c_int) -> Self {
|
||||
const F_UNLCK: libc::c_int = libc::F_UNLCK as libc::c_int;
|
||||
const F_WRLCK: libc::c_int = libc::F_WRLCK as libc::c_int;
|
||||
const F_RDLCK: libc::c_int = libc::F_RDLCK as libc::c_int;
|
||||
match value {
|
||||
F_UNLCK => Self::Unlocked,
|
||||
F_WRLCK => Self::ExclusiveWrite,
|
||||
F_RDLCK => Self::SharedRead,
|
||||
// This is so unlikely that we want to avoid the complexity of
|
||||
// coping with this error case. Can only fail if either Linux
|
||||
// is broken or memory is messed up.
|
||||
other => panic!("Unexpected lock state: {other}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [`struct@libc::flock`] structure for the whole file.
|
||||
const fn get_flock(lock_type: LockType) -> libc::flock {
|
||||
libc::flock {
|
||||
l_type: lock_type.to_libc_val() as libc::c_short,
|
||||
l_whence: libc::SEEK_SET as libc::c_short,
|
||||
l_start: 0,
|
||||
l_len: 0, /* EOF */
|
||||
l_pid: 0, /* filled by callee */
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to acquire a lock using [`fcntl`] with respect to the given
|
||||
/// parameters.
|
||||
///
|
||||
/// Please note that `fcntl()` OFD locks are **advisory locks**, which do not
|
||||
/// prevent to `open()` a file if a lock is already placed.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `file`: The file to acquire a lock for [`LockType`]. The file's state will
|
||||
/// be logically mutated, but not technically.
|
||||
/// - `lock_type`: The [`LockType`]
|
||||
pub fn try_acquire_lock<Fd: AsRawFd>(file: Fd, lock_type: LockType) -> Result<(), LockError> {
|
||||
let flock = get_flock(lock_type);
|
||||
|
||||
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_SETLK(&flock));
|
||||
match res {
|
||||
0 => Ok(()),
|
||||
-1 => {
|
||||
let io_error = io::Error::last_os_error();
|
||||
let errno = io_error.raw_os_error().unwrap();
|
||||
match errno {
|
||||
// See man page for error code:
|
||||
// <https://man7.org/linux/man-pages/man2/fcntl.2.html>
|
||||
libc::EAGAIN | libc::EACCES => Err(LockError::AlreadyLocked),
|
||||
_ => Err(LockError::Io(io_error)),
|
||||
}
|
||||
}
|
||||
val => panic!("Unexpected return value from fcntl(): {val}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears a lock.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `file`: The file to clear all locks for [`LockType`].
|
||||
pub fn clear_lock<Fd: AsRawFd>(file: Fd) -> Result<(), LockError> {
|
||||
try_acquire_lock(file, LockType::Unlock)
|
||||
}
|
||||
|
||||
/// Returns the current lock state using [`fcntl`] with respect to the given
|
||||
/// parameters.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `file`: The file for which to get the lock state.
|
||||
pub fn get_lock_state<Fd: AsRawFd>(file: Fd) -> Result<LockState, LockError> {
|
||||
let mut flock = get_flock(LockType::Write);
|
||||
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_GETLK(&mut flock));
|
||||
match res {
|
||||
0 => {
|
||||
let state = flock.l_type as libc::c_int;
|
||||
let state = LockState::new(state);
|
||||
Ok(state)
|
||||
}
|
||||
-1 => {
|
||||
let io_error = io::Error::last_os_error();
|
||||
Err(LockError::Io(io_error))
|
||||
}
|
||||
val => panic!("Unexpected return value from fcntl(): {val}"),
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::fixed_vhd::FixedVhd;
|
||||
use crate::raw_async::RawFileAsync;
|
||||
@@ -33,6 +33,10 @@ impl DiskFile for FixedVhdDiskAsync {
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
BorrowedDiskFd::new(self.0.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FixedVhdAsync {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::fixed_vhd::FixedVhd;
|
||||
use crate::raw_sync::RawFileSync;
|
||||
@@ -33,6 +33,10 @@ impl DiskFile for FixedVhdDiskSync {
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
BorrowedDiskFd::new(self.0.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FixedVhdSync {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
extern crate log;
|
||||
|
||||
pub mod async_io;
|
||||
pub mod fcntl;
|
||||
pub mod fixed_vhd;
|
||||
#[cfg(feature = "io_uring")]
|
||||
/// Enabled with the `"io_uring"` feature
|
||||
@@ -67,9 +68,9 @@ pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Guest gave us bad memory addresses")]
|
||||
GuestMemory(GuestMemoryError),
|
||||
GuestMemory(#[source] GuestMemoryError),
|
||||
#[error("Guest gave us offsets that would have overflowed a usize")]
|
||||
CheckedOffset(GuestAddress, usize),
|
||||
CheckedOffset(GuestAddress, usize /* sector offset */),
|
||||
#[error("Guest gave us a write only descriptor that protocol says to read from")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
#[error("Guest gave us a read only descriptor that protocol says to write to")]
|
||||
@@ -79,21 +80,21 @@ pub enum Error {
|
||||
#[error("Guest gave us a descriptor that was too short to use")]
|
||||
DescriptorLengthTooSmall,
|
||||
#[error("Failed to detect image type: {0}")]
|
||||
DetectImageType(std::io::Error),
|
||||
DetectImageType(#[source] std::io::Error),
|
||||
#[error("Failure in fixed vhd: {0}")]
|
||||
FixedVhdError(std::io::Error),
|
||||
FixedVhdError(#[source] std::io::Error),
|
||||
#[error("Getting a block's metadata fails for any reason")]
|
||||
GetFileMetadata,
|
||||
#[error("The requested operation would cause a seek beyond disk end")]
|
||||
InvalidOffset,
|
||||
#[error("Failure in qcow: {0}")]
|
||||
QcowError(qcow::Error),
|
||||
QcowError(#[source] qcow::Error),
|
||||
#[error("Failure in raw file: {0}")]
|
||||
RawFileError(std::io::Error),
|
||||
RawFileError(#[source] std::io::Error),
|
||||
#[error("The requested operation does not support multiple descriptors")]
|
||||
TooManyDescriptors,
|
||||
#[error("Failure in vhdx: {0}")]
|
||||
VhdxError(VhdxError),
|
||||
VhdxError(#[source] VhdxError),
|
||||
}
|
||||
|
||||
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
|
||||
@@ -131,33 +132,33 @@ pub fn build_serial(disk_path: &Path) -> Vec<u8> {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExecuteError {
|
||||
#[error("Bad request: {0}")]
|
||||
BadRequest(Error),
|
||||
BadRequest(#[source] Error),
|
||||
#[error("Failed to flush: {0}")]
|
||||
Flush(io::Error),
|
||||
Flush(#[source] io::Error),
|
||||
#[error("Failed to read: {0}")]
|
||||
Read(GuestMemoryError),
|
||||
Read(#[source] GuestMemoryError),
|
||||
#[error("Failed to read_exact: {0}")]
|
||||
ReadExact(io::Error),
|
||||
ReadExact(#[source] io::Error),
|
||||
#[error("Failed to seek: {0}")]
|
||||
Seek(io::Error),
|
||||
Seek(#[source] io::Error),
|
||||
#[error("Failed to write: {0}")]
|
||||
Write(GuestMemoryError),
|
||||
Write(#[source] GuestMemoryError),
|
||||
#[error("Failed to write_all: {0}")]
|
||||
WriteAll(io::Error),
|
||||
WriteAll(#[source] io::Error),
|
||||
#[error("Unsupported request: {0}")]
|
||||
Unsupported(u32),
|
||||
#[error("Failed to submit io uring: {0}")]
|
||||
SubmitIoUring(io::Error),
|
||||
SubmitIoUring(#[source] io::Error),
|
||||
#[error("Failed to get guest address: {0}")]
|
||||
GetHostAddress(GuestMemoryError),
|
||||
GetHostAddress(#[source] GuestMemoryError),
|
||||
#[error("Failed to async read: {0}")]
|
||||
AsyncRead(AsyncIoError),
|
||||
AsyncRead(#[source] AsyncIoError),
|
||||
#[error("Failed to async write: {0}")]
|
||||
AsyncWrite(AsyncIoError),
|
||||
AsyncWrite(#[source] AsyncIoError),
|
||||
#[error("failed to async flush: {0}")]
|
||||
AsyncFlush(AsyncIoError),
|
||||
AsyncFlush(#[source] AsyncIoError),
|
||||
#[error("Failed allocating a temporary buffer: {0}")]
|
||||
TemporaryBufferAllocation(io::Error),
|
||||
TemporaryBufferAllocation(#[source] io::Error),
|
||||
}
|
||||
|
||||
impl ExecuteError {
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::cmp::{max, min};
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::mem::size_of;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::str;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -36,23 +37,23 @@ const MAX_NESTING_DEPTH: u32 = 10;
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Backing file io error: {0}")]
|
||||
BackingFileIo(io::Error),
|
||||
BackingFileIo(#[source] io::Error),
|
||||
#[error("Backing file open error: {0}")]
|
||||
BackingFileOpen(Box<Error>),
|
||||
BackingFileOpen(#[source] Box<Error>),
|
||||
#[error("Backing file name is too long: {0} bytes over")]
|
||||
BackingFileTooLong(usize),
|
||||
#[error("Compressed blocks not supported")]
|
||||
CompressedBlocksNotSupported,
|
||||
#[error("Failed to evict cache: {0}")]
|
||||
EvictingCache(io::Error),
|
||||
EvictingCache(#[source] io::Error),
|
||||
#[error("File larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
|
||||
FileTooBig(u64),
|
||||
#[error("Failed to get file size: {0}")]
|
||||
GettingFileSize(io::Error),
|
||||
GettingFileSize(#[source] io::Error),
|
||||
#[error("Failed to get refcount: {0}")]
|
||||
GettingRefcount(refcount::Error),
|
||||
GettingRefcount(#[source] refcount::Error),
|
||||
#[error("Failed to parse filename: {0}")]
|
||||
InvalidBackingFileName(str::Utf8Error),
|
||||
InvalidBackingFileName(#[source] str::Utf8Error),
|
||||
#[error("Invalid cluster index")]
|
||||
InvalidClusterIndex,
|
||||
#[error("Invalid cluster size")]
|
||||
@@ -80,29 +81,29 @@ pub enum Error {
|
||||
#[error("Not enough space for refcounts")]
|
||||
NotEnoughSpaceForRefcounts,
|
||||
#[error("Failed to open file {0}")]
|
||||
OpeningFile(io::Error),
|
||||
OpeningFile(#[source] io::Error),
|
||||
#[error("Failed to read data: {0}")]
|
||||
ReadingData(io::Error),
|
||||
ReadingData(#[source] io::Error),
|
||||
#[error("Failed to read header: {0}")]
|
||||
ReadingHeader(io::Error),
|
||||
ReadingHeader(#[source] io::Error),
|
||||
#[error("Failed to read pointers: {0}")]
|
||||
ReadingPointers(io::Error),
|
||||
ReadingPointers(#[source] io::Error),
|
||||
#[error("Failed to read ref count block: {0}")]
|
||||
ReadingRefCountBlock(refcount::Error),
|
||||
ReadingRefCountBlock(#[source] refcount::Error),
|
||||
#[error("Failed to read ref counts: {0}")]
|
||||
ReadingRefCounts(io::Error),
|
||||
ReadingRefCounts(#[source] io::Error),
|
||||
#[error("Failed to rebuild ref counts: {0}")]
|
||||
RebuildingRefCounts(io::Error),
|
||||
RebuildingRefCounts(#[source] io::Error),
|
||||
#[error("Refcount table offset past file end")]
|
||||
RefcountTableOffEnd,
|
||||
#[error("Too many clusters specified for refcount")]
|
||||
RefcountTableTooLarge,
|
||||
#[error("Failed to seek file: {0}")]
|
||||
SeekingFile(io::Error),
|
||||
SeekingFile(#[source] io::Error),
|
||||
#[error("Failed to set file size: {0}")]
|
||||
SettingFileSize(io::Error),
|
||||
SettingFileSize(#[source] io::Error),
|
||||
#[error("Failed to set refcount refcount: {0}")]
|
||||
SettingRefcountRefcount(io::Error),
|
||||
SettingRefcountRefcount(#[source] io::Error),
|
||||
#[error("Size too small for number of clusters")]
|
||||
SizeTooSmallForNumberOfClusters,
|
||||
#[error("L1 entry table too large: {0}")]
|
||||
@@ -114,9 +115,9 @@ pub enum Error {
|
||||
#[error("Unsupported version: {0}")]
|
||||
UnsupportedVersion(u32),
|
||||
#[error("Failed to write data: {0}")]
|
||||
WritingData(io::Error),
|
||||
WritingData(#[source] io::Error),
|
||||
#[error("Failed to write header: {0}")]
|
||||
WritingHeader(io::Error),
|
||||
WritingHeader(#[source] io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -378,7 +379,7 @@ impl QcowHeader {
|
||||
}
|
||||
|
||||
if let Some(backing_file_path) = self.backing_file_path.as_ref() {
|
||||
write!(file, "{}", backing_file_path).map_err(Error::WritingHeader)?;
|
||||
write!(file, "{backing_file_path}").map_err(Error::WritingHeader)?;
|
||||
}
|
||||
|
||||
// Set the file length by seeking and writing a zero to the last byte. This avoids needing
|
||||
@@ -502,7 +503,7 @@ impl QcowFile {
|
||||
if refcount_bits != 16 {
|
||||
return Err(Error::UnsupportedRefcountOrder);
|
||||
}
|
||||
let refcount_bytes = (refcount_bits + 7) / 8;
|
||||
let refcount_bytes = refcount_bits.div_ceil(8);
|
||||
|
||||
// Need at least one refcount cluster
|
||||
if header.refcount_table_clusters == 0 {
|
||||
@@ -1516,6 +1517,12 @@ impl QcowFile {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for QcowFile {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.raw_file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for QcowFile {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.sync_caches();
|
||||
@@ -1625,8 +1632,7 @@ impl FileSync for QcowFile {
|
||||
|
||||
impl FileSetLen for QcowFile {
|
||||
fn set_len(&self, _len: u64) -> std::io::Result<()> {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
Err(std::io::Error::other(
|
||||
"set_len() not supported for QcowFile",
|
||||
))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use std::io::{self, BufWriter, Seek, SeekFrom, Write};
|
||||
use std::mem::size_of;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||
@@ -159,3 +160,9 @@ impl Clone for QcowRawFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for QcowRawFile {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,3 +369,9 @@ impl Clone for RawFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RawFile {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::qcow::vec_cache::{CacheMap, Cacheable, VecCache};
|
||||
pub enum Error {
|
||||
/// `EvictingCache` - Error writing a refblock from the cache to disk.
|
||||
#[error("Failed to write a refblock from the cache to disk: {0}")]
|
||||
EvictingRefCounts(io::Error),
|
||||
EvictingRefCounts(#[source] io::Error),
|
||||
/// `InvalidIndex` - Address requested isn't within the range of the disk.
|
||||
#[error("Address requested is not within the range of the disk")]
|
||||
InvalidIndex,
|
||||
@@ -28,7 +28,7 @@ pub enum Error {
|
||||
NeedNewCluster,
|
||||
/// `ReadingRefCounts` - Error reading the file into the refcount cache.
|
||||
#[error("Failed to read the file into the refcount cache: {0}")]
|
||||
ReadingRefCounts(io::Error),
|
||||
ReadingRefCounts(#[source] io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::qcow::{QcowFile, RawFile, Result as QcowResult};
|
||||
use crate::AsyncAdaptor;
|
||||
|
||||
@@ -35,6 +38,11 @@ impl DiskFile for QcowDiskSync {
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(QcowSync::new(self.qcow_file.clone())) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
let lock = self.qcow_file.lock().unwrap();
|
||||
BorrowedDiskFd::new(lock.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QcowSync {
|
||||
|
||||
@@ -10,7 +10,7 @@ use io_uring::{opcode, types, IoUring};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::DiskTopology;
|
||||
|
||||
@@ -46,6 +46,10 @@ impl DiskFile for RawFileDisk {
|
||||
DiskTopology::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawFileAsync {
|
||||
|
||||
@@ -13,7 +13,7 @@ use vmm_sys_util::aio;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::DiskTopology;
|
||||
|
||||
@@ -49,6 +49,10 @@ impl DiskFile for RawFileDiskAio {
|
||||
DiskTopology::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawFileAsyncAio {
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::DiskTopology;
|
||||
|
||||
@@ -43,6 +43,10 @@ impl DiskFile for RawFileDiskSync {
|
||||
DiskTopology::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RawFileSync {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use std::collections::btree_map::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use remain::sorted;
|
||||
@@ -25,19 +26,19 @@ mod vhdx_metadata;
|
||||
#[sorted]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum VhdxError {
|
||||
#[error("Not a VHDx file {0}")]
|
||||
#[error("Not a VHDx file: {0}")]
|
||||
NotVhdx(#[source] VhdxHeaderError),
|
||||
#[error("Failed to parse VHDx header {0}")]
|
||||
#[error("Failed to parse VHDx header: {0}")]
|
||||
ParseVhdxHeader(#[source] VhdxHeaderError),
|
||||
#[error("Failed to parse VHDx metadata {0}")]
|
||||
#[error("Failed to parse VHDx metadata: {0}")]
|
||||
ParseVhdxMetadata(#[source] VhdxMetadataError),
|
||||
#[error("Failed to parse VHDx region entries {0}")]
|
||||
#[error("Failed to parse VHDx region entries: {0}")]
|
||||
ParseVhdxRegionEntry(#[source] VhdxHeaderError),
|
||||
#[error("Failed reading metadata {0}")]
|
||||
#[error("Failed reading metadata: {0}")]
|
||||
ReadBatEntry(#[source] VhdxBatError),
|
||||
#[error("Failed reading sector from disk {0}")]
|
||||
#[error("Failed reading sector from disk: {0}")]
|
||||
ReadFailed(#[source] VhdxIoError),
|
||||
#[error("Failed writing to sector on disk {0}")]
|
||||
#[error("Failed writing to sector on disk: {0}")]
|
||||
WriteFailed(#[source] VhdxIoError),
|
||||
}
|
||||
|
||||
@@ -111,12 +112,9 @@ impl Read for Vhdx {
|
||||
sector_count,
|
||||
)
|
||||
.map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
|
||||
),
|
||||
)
|
||||
std::io::Error::other(format!(
|
||||
"Failed reading {sector_count} sectors from VHDx at index {sector_index}: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
|
||||
@@ -138,12 +136,9 @@ impl Write for Vhdx {
|
||||
|
||||
if self.first_write {
|
||||
self.first_write = false;
|
||||
self.vhdx_header.update(&mut self.file).map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Failed to update VHDx header: {e}"),
|
||||
)
|
||||
})?;
|
||||
self.vhdx_header
|
||||
.update(&mut self.file)
|
||||
.map_err(|e| std::io::Error::other(format!("Failed to update VHDx header: {e}")))?;
|
||||
}
|
||||
|
||||
let result = vhdx_io::write(
|
||||
@@ -156,12 +151,9 @@ impl Write for Vhdx {
|
||||
sector_count,
|
||||
)
|
||||
.map_err(|e| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
|
||||
),
|
||||
)
|
||||
std::io::Error::other(format!(
|
||||
"Failed writing {sector_count} sectors on VHDx at index {sector_index}: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
self.current_offset = self.current_offset.checked_add(result as u64).unwrap();
|
||||
@@ -231,6 +223,12 @@ impl Clone for Vhdx {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Vhdx {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn uuid_from_guid(buf: &[u8]) -> Uuid {
|
||||
// The first 3 fields of UUID are stored in Big Endian format, and
|
||||
// the last 8 bytes are stored as byte array. Therefore, we read the
|
||||
|
||||
@@ -33,9 +33,9 @@ pub enum VhdxBatError {
|
||||
InvalidBatEntry,
|
||||
#[error("Invalid BAT entry count")]
|
||||
InvalidEntryCount,
|
||||
#[error("Failed to read BAT entry {0}")]
|
||||
#[error("Failed to read BAT entry: {0}")]
|
||||
ReadBat(#[source] io::Error),
|
||||
#[error("Failed to write BAT entry {0}")]
|
||||
#[error("Failed to write BAT entry: {0}")]
|
||||
WriteBat(#[source] io::Error),
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{AsyncIo, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult};
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::vhdx::{Result as VhdxResult, Vhdx};
|
||||
use crate::AsyncAdaptor;
|
||||
|
||||
@@ -35,6 +38,11 @@ impl DiskFile for VhdxDiskSync {
|
||||
as Box<dyn AsyncIo>,
|
||||
)
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd {
|
||||
let lock = self.vhdx_file.lock().unwrap();
|
||||
BorrowedDiskFd::new(lock.as_raw_fd())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VhdxSync {
|
||||
|
||||
9
build.rs
9
build.rs
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
@@ -12,10 +13,18 @@ fn main() {
|
||||
if git_out.status.success() {
|
||||
if let Ok(git_out_str) = String::from_utf8(git_out.stdout) {
|
||||
version = git_out_str;
|
||||
// Pop the trailing newline.
|
||||
version.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append CH_EXTRA_VERSION to version if it is set.
|
||||
if let Ok(extra_version) = env::var("CH_EXTRA_VERSION") {
|
||||
println!("cargo:rerun-if-env-changed=CH_EXTRA_VERSION");
|
||||
version.push_str(&format!("-{extra_version}"));
|
||||
}
|
||||
|
||||
// This println!() has a special behavior, as it will set the environment
|
||||
// variable BUILD_VERSION, so that it can be reused from the binary.
|
||||
// Particularly, this is used from src/main.rs to display the exact
|
||||
|
||||
@@ -8,7 +8,7 @@ version = "0.1.0"
|
||||
acpi_tables = { workspace = true }
|
||||
anyhow = "1.0.94"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "2.6.0"
|
||||
bitflags = "2.9.0"
|
||||
byteorder = "1.5.0"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
|
||||
@@ -9,8 +9,8 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use arch::layout;
|
||||
use hypervisor::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
use hypervisor::{CpuState, GicState};
|
||||
use hypervisor::arch::aarch64::gic::{GicState, Vgic, VgicConfig};
|
||||
use hypervisor::CpuState;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||
|
||||
@@ -18,38 +18,38 @@ pub enum Error {
|
||||
InvalidDeliveryMode,
|
||||
/// Failed creating the interrupt source group.
|
||||
#[error("Failed creating the interrupt source group: {0}")]
|
||||
CreateInterruptSourceGroup(io::Error),
|
||||
CreateInterruptSourceGroup(#[source] io::Error),
|
||||
/// Failed triggering the interrupt.
|
||||
#[error("Failed triggering the interrupt: {0}")]
|
||||
TriggerInterrupt(io::Error),
|
||||
TriggerInterrupt(#[source] io::Error),
|
||||
/// Failed masking the interrupt.
|
||||
#[error("Failed masking the interrupt: {0}")]
|
||||
MaskInterrupt(io::Error),
|
||||
MaskInterrupt(#[source] io::Error),
|
||||
/// Failed unmasking the interrupt.
|
||||
#[error("Failed unmasking the interrupt: {0}")]
|
||||
UnmaskInterrupt(io::Error),
|
||||
UnmaskInterrupt(#[source] io::Error),
|
||||
/// Failed updating the interrupt.
|
||||
#[error("Failed updating the interrupt: {0}")]
|
||||
UpdateInterrupt(io::Error),
|
||||
UpdateInterrupt(#[source] io::Error),
|
||||
/// Failed enabling the interrupt.
|
||||
#[error("Failed enabling the interrupt: {0}")]
|
||||
EnableInterrupt(io::Error),
|
||||
EnableInterrupt(#[source] io::Error),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Failed creating GIC device.
|
||||
#[error("Failed creating GIC device: {0}")]
|
||||
CreateGic(hypervisor::HypervisorVmError),
|
||||
CreateGic(#[source] hypervisor::HypervisorVmError),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
/// Failed restoring GIC device.
|
||||
#[error("Failed restoring GIC device: {0}")]
|
||||
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
||||
RestoreGic(#[source] hypervisor::arch::aarch64::gic::Error),
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
/// Failed creating AIA device.
|
||||
#[error("Failed creating AIA device: {0}")]
|
||||
CreateAia(hypervisor::HypervisorVmError),
|
||||
CreateAia(#[source] hypervisor::HypervisorVmError),
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
/// Failed restoring AIA device.
|
||||
#[error("Failed restoring AIA device: {0}")]
|
||||
RestoreAia(hypervisor::arch::riscv64::aia::Error),
|
||||
RestoreAia(#[source] hypervisor::arch::riscv64::aia::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -418,7 +418,7 @@ impl InterruptController for Ioapic {
|
||||
self.interrupt_source_group
|
||||
.trigger(irq as InterruptIndex)
|
||||
.map_err(Error::TriggerInterrupt)?;
|
||||
debug!("Interrupt {irq} successfully delivered");
|
||||
trace!("Interrupt {irq} successfully delivered");
|
||||
|
||||
// If trigger mode is level sensitive, set the Remote IRR bit.
|
||||
// It will be cleared when the EOI is received.
|
||||
|
||||
@@ -43,11 +43,11 @@ const N_GPIOS: u32 = 8;
|
||||
pub enum Error {
|
||||
#[error("Bad Write Offset: {0}")]
|
||||
BadWriteOffset(u64),
|
||||
#[error("GPIO interrupt disabled by guest driver.")]
|
||||
#[error("GPIO interrupt disabled by guest driver")]
|
||||
GpioInterruptDisabled,
|
||||
#[error("Could not trigger GPIO interrupt: {0}.")]
|
||||
GpioInterruptFailure(io::Error),
|
||||
#[error("Invalid GPIO Input key triggered: {0}.")]
|
||||
#[error("Could not trigger GPIO interrupt: {0}")]
|
||||
GpioInterruptFailure(#[source] io::Error),
|
||||
#[error("Invalid GPIO Input key triggered: {0}")]
|
||||
GpioTriggerKeyFailure(u32),
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ pub enum Error {
|
||||
#[error("Bad Write Offset: {0}")]
|
||||
BadWriteOffset(u64),
|
||||
#[error("Failed to trigger interrupt: {0}")]
|
||||
InterruptFailure(io::Error),
|
||||
InterruptFailure(#[source] io::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -51,14 +51,14 @@ const AMBA_ID_HIGH: u64 = 0x401;
|
||||
pub enum Error {
|
||||
#[error("pl011_write: Bad Write Offset: {0}")]
|
||||
BadWriteOffset(u64),
|
||||
#[error("pl011: DMA not implemented.")]
|
||||
#[error("pl011: DMA not implemented")]
|
||||
DmaNotImplemented,
|
||||
#[error("Failed to trigger interrupt: {0}")]
|
||||
InterruptFailure(io::Error),
|
||||
InterruptFailure(#[source] io::Error),
|
||||
#[error("Failed to write: {0}")]
|
||||
WriteAllFailure(io::Error),
|
||||
WriteAllFailure(#[source] io::Error),
|
||||
#[error("Failed to flush: {0}")]
|
||||
FlushFailure(io::Error),
|
||||
FlushFailure(#[source] io::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -484,7 +484,7 @@ impl PvmemcontrolBusDevice {
|
||||
}
|
||||
|
||||
fn set_vma_anon_name(&self, addr: u64, length: u64, name: u64) -> result::Result<(), Error> {
|
||||
let name = (name != 0).then(|| CString::new(format!("pvmemcontrol-{}", name)).unwrap());
|
||||
let name = (name != 0).then(|| CString::new(format!("pvmemcontrol-{name}")).unwrap());
|
||||
let name_ptr = if let Some(name) = &name {
|
||||
name.as_ptr()
|
||||
} else {
|
||||
@@ -698,10 +698,12 @@ impl PciDevice for PvmemcontrolPciDevice {
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.configuration
|
||||
.write_config_register(reg_idx, offset, data);
|
||||
None
|
||||
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
|
||||
(
|
||||
self.configuration
|
||||
.write_config_register(reg_idx, offset, data),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
@@ -716,14 +718,6 @@ impl PciDevice for PvmemcontrolPciDevice {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
|
||||
fn detect_bar_reprogramming(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
data: &[u8],
|
||||
) -> Option<BarReprogrammingParams> {
|
||||
self.configuration.detect_bar_reprogramming(reg_idx, data)
|
||||
}
|
||||
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
|
||||
@@ -88,7 +88,11 @@ impl PvPanicDevice {
|
||||
);
|
||||
|
||||
let command: [u8; 2] = [0x03, 0x01];
|
||||
configuration.write_config_register(1, 0, &command);
|
||||
let bar_reprogram = configuration.write_config_register(1, 0, &command);
|
||||
assert!(
|
||||
bar_reprogram.is_empty(),
|
||||
"No bar reprogrammig is expected from writing to the COMMAND register"
|
||||
);
|
||||
|
||||
let state: Option<PvPanicDeviceState> = snapshot
|
||||
.as_ref()
|
||||
@@ -156,24 +160,18 @@ impl PciDevice for PvPanicDevice {
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.configuration
|
||||
.write_config_register(reg_idx, offset, data);
|
||||
None
|
||||
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
|
||||
(
|
||||
self.configuration
|
||||
.write_config_register(reg_idx, offset, data),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
self.configuration.read_reg(reg_idx)
|
||||
}
|
||||
|
||||
fn detect_bar_reprogramming(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
data: &[u8],
|
||||
) -> Option<BarReprogrammingParams> {
|
||||
self.configuration.detect_bar_reprogramming(reg_idx, data)
|
||||
}
|
||||
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# AMD SEV-SNP
|
||||
|
||||
### WARNING
|
||||
|
||||
This feature is only currently supported on MSHV.
|
||||
|
||||
AMD Secure Encrypted Virtualization & Secure Nested Paging (SEV-SNP) is an AMD
|
||||
@@ -9,8 +10,8 @@ malicious hypervisor-based attacks like data replay, memory-remapping and more
|
||||
in order to create an isolated execution environment. Here are some useful
|
||||
links:
|
||||
|
||||
* [SNP Homepage] (https://www.amd.com/en/processors/amd-secure-encrypted-virtualization)
|
||||
more information about SEV-SNP technical aspects, design and specification.
|
||||
- [SNP Homepage](https://www.amd.com/content/dam/amd/en/documents/epyc-business-docs/solution-briefs/amd-secure-encrypted-virtualization-solution-brief.pdf):
|
||||
more information about SEV-SNP technical aspects, design and specification.
|
||||
|
||||
## Cloud Hypervisor support
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# `cloud-hypervisor` debug IO ports
|
||||
|
||||
When running x86 guests, `cloud-hypervisor` provides different kinds of debug ports:
|
||||
- [`0x80` debug port](https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
|
||||
- Debug console (by default at `0xe9`).
|
||||
|
||||
- [`0x80` debug port](https://web.archive.org/web/20211028033025/https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
|
||||
- Debug console (by default at `0xe9`).
|
||||
- Firmware debug port at `0x402`.
|
||||
|
||||
All of them can be used to trace user-defined guest events and all of them can
|
||||
@@ -25,13 +26,13 @@ for debugging specific components of the guest software stack. When logging a
|
||||
write of one of those codes to the debug port, `cloud-hypervisor` adds a
|
||||
pre-defined string to the logs.
|
||||
|
||||
| Code Range | Component | Log string |
|
||||
| ---------------- | ----------- | ------------ |
|
||||
| `0x00` to `0x1f` | Firmware | `Firmware` |
|
||||
| `0x20` to `0x3f` | Bootloader | `Bootloader` |
|
||||
| `0x40` to `0x5f` | Kernel | `Kernel` |
|
||||
| `0x60` to `0x7f` | Userspace | `Userspace` |
|
||||
| `0x80` to `0xff` | Custom | `Custom` |
|
||||
| Code Range | Component | Log string |
|
||||
| ---------------- | ---------- | ------------ |
|
||||
| `0x00` to `0x1f` | Firmware | `Firmware` |
|
||||
| `0x20` to `0x3f` | Bootloader | `Bootloader` |
|
||||
| `0x40` to `0x5f` | Kernel | `Kernel` |
|
||||
| `0x60` to `0x7f` | Userspace | `Userspace` |
|
||||
| `0x80` to `0xff` | Custom | `Custom` |
|
||||
|
||||
One typical use case is guest boot time measurement and tracing. By writing
|
||||
different values to the debug I/O port at different boot process steps, the
|
||||
@@ -71,8 +72,8 @@ cloud-hypervisor: 403.499628ms: DEBUG:vmm/src/vm.rs:510 -- [Debug I/O port: Firm
|
||||
|
||||
### Debug console port
|
||||
|
||||
The debug console is inspired by QEMU and Bochs, which have a similar feature.
|
||||
By default, the I/O port `0xe9` is used. This port can be configured like a
|
||||
The debug console is inspired by QEMU and Bochs, which have a similar feature.
|
||||
By default, the I/O port `0xe9` is used. This port can be configured like a
|
||||
console. Thus, it can print to a tty, a file, or a pty, for example.
|
||||
|
||||
### Firmware debug port
|
||||
@@ -83,7 +84,7 @@ it. The firmware debug port only prints to stdout.
|
||||
## When do I need these ports?
|
||||
|
||||
The ports are on the one hand interesting for firmware or kernel developers, as
|
||||
they provide an easy way to print debug information from within a guest.
|
||||
they provide an easy way to print debug information from within a guest.
|
||||
Furthermore, you can patch "normal" software to measure certain events, such as
|
||||
the boot time of a guest.
|
||||
|
||||
@@ -91,6 +92,6 @@ the boot time of a guest.
|
||||
|
||||
The `0x80` debug port and the port of the firmware debug device are always
|
||||
available. The debug console must be activated via the command line, but
|
||||
provides more configuration options.
|
||||
provides more configuration options.
|
||||
|
||||
You can use different ports for different aspect of your logging messages.
|
||||
You can use different ports for different aspect of your logging messages.
|
||||
|
||||
@@ -8,11 +8,12 @@ the host kernel. The required Linux and KVM changes can be found in the
|
||||
[KVM SGX Tree](https://github.com/intel/kvm-sgx).
|
||||
|
||||
Utilizing SGX in the guest requires a kernel/OS with SGX support, e.g. a kernel
|
||||
built using the [SGX Linux Development Tree](https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git)
|
||||
since release 5.11, see
|
||||
[here](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html)
|
||||
or the [KVM SGX Tree](https://github.com/intel/kvm-sgx). Running KVM SGX as the
|
||||
guest kernel allows nested virtualization of SGX.
|
||||
|
||||
For more information about SGX, please refer to the [SGX Homepage](https://software.intel.com/sgx).
|
||||
For more information about SGX, please refer to the [SGX Homepage](https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/linux-overview.html).
|
||||
|
||||
For more information about SGX SDK and how to test SGX, please refer to the
|
||||
following [instructions](https://github.com/intel/linux-sgx).
|
||||
|
||||
@@ -4,21 +4,21 @@ Intel® Trust Domain Extensions (Intel® TDX) is an Intel technology designed to
|
||||
isolate virtual machines from the VMM, hypervisor and any other software on the
|
||||
host platform. Here are some useful links:
|
||||
|
||||
* [TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html):
|
||||
more information about TDX technical aspects, design and specification
|
||||
- [TDX Homepage](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html):
|
||||
more information about TDX technical aspects, design and specification
|
||||
|
||||
* [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
|
||||
- [KVM TDX tree](https://github.com/intel/tdx/tree/kvm): the required
|
||||
Linux kernel changes for the host side
|
||||
|
||||
* [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
|
||||
- [Guest TDX tree](https://github.com/intel/tdx/tree/guest): the Linux
|
||||
kernel changes for the guest side
|
||||
|
||||
* [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
|
||||
- [EDK2 project](https://github.com/tianocore/edk2): the TDVF firmware
|
||||
|
||||
* [Confidential Containers project](https://github.com/confidential-containers/td-shim):
|
||||
- [Confidential Containers project](https://github.com/confidential-containers/td-shim):
|
||||
the TDShim firmware
|
||||
|
||||
* [TDX Tools](https://github.com/intel/tdx-tools): a collection of tools
|
||||
- [TDX Linux](https://github.com/intel/tdx-linux): a collection of tools
|
||||
and scripts to setup TDX environment for testing purpose (such as
|
||||
installing required packages on the host, creating guest images, and
|
||||
building the custom Linux kernel for TDX host and guest)
|
||||
@@ -27,17 +27,13 @@ more information about TDX technical aspects, design and specification
|
||||
|
||||
It is required to use a machine with TDX enabled in hardware and
|
||||
with the host OS compiled from the [KVM TDX tree](https://github.com/intel/tdx/tree/kvm).
|
||||
The host environment can also be setup with the [TDX Tools](https://github.com/intel/tdx-tools).
|
||||
The host environment can also be setup with the [TDX Linux](https://github.com/intel/tdx-linux).
|
||||
|
||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading a TD firmware ([TDVF](https://github.com/tianocore/edk2)),
|
||||
which will then load the guest kernel from the image. The image must be custom
|
||||
as it must include a kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
Cloud Hypervisor can also boot a TDX VM with direct kernel boot using [TDshim](https://github.com/confidential-containers/td-shim).
|
||||
The custom Linux kernel for the guest can be built with the [TDX Tools](https://github.com/intel/tdx-tools).
|
||||
|
||||
> **Note**
|
||||
> The latest version of custom host and guest kernel being tested is
|
||||
> from [TDX Tools - 2023ww01](https://github.com/intel/tdx-tools/commits/2023ww01).
|
||||
The custom Linux kernel for the guest can be built with the [TDX Linux](https://github.com/intel/tdx-linux).
|
||||
|
||||
### TDVF
|
||||
|
||||
@@ -110,6 +106,7 @@ direct kernel boot, which is useful for containers use cases.
|
||||
|
||||
To build TDShim from source, it is required to install `Rust`, `NASM`,
|
||||
and `LLVM` first. The TDshim can be build as follows:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/confidential-containers/td-shim
|
||||
cd td-shim
|
||||
@@ -126,13 +123,14 @@ cargo image --release
|
||||
|
||||
If debug logs from the TDShim is needed, here are the alternative
|
||||
commands:
|
||||
|
||||
```bash
|
||||
cargo image
|
||||
```
|
||||
|
||||
And run a TDX VM by providing the firmware previously built, along with a guest
|
||||
kernel built from the [Guest TDX tree](https://github.com/intel/tdx/tree/guest)
|
||||
or the [TDX Tools](https://github.com/intel/tdx-tools).
|
||||
or the [TDX Linux](https://github.com/intel/tdx-linux).
|
||||
The appropriate kernel boot options must be provided through the `--cmdline`
|
||||
option as well.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# I/O Throttling
|
||||
|
||||
Cloud Hypervisor now supports I/O throttling on virtio-block and virtio-net
|
||||
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/master/src/rate_limiter)
|
||||
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/7a1231b141e958d15d5b2c079dd5e0880528b4b0/src/rate_limiter)
|
||||
from Firecracker. This document explains the user interface of this
|
||||
feature, and highlights some internal implementations that can help users
|
||||
better understand the expected behavior of I/O throttling in practice.
|
||||
@@ -44,10 +44,12 @@ generally advisable to keep `bw/ops_refill_time` larger than `100 ms`
|
||||
expectation ("refill-rate").
|
||||
|
||||
## Rate Limit Groups
|
||||
|
||||
It is possible to throttle the aggregate bandwidth or operations
|
||||
of multiple virtio-blk devices using a `rate_limit_group`. virtio-blk devices may be
|
||||
dynamically added and removed from a `rate_limit_group`. The following example
|
||||
demonstrates how to throttle the aggregate bandwidth of two disks to 10 MiB/s.
|
||||
|
||||
```
|
||||
--disk path=disk0.raw,rate_limit_group=group0 \
|
||||
path=disk1.raw,rate_limit_group=group0 \
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
The target audience of this document is both:
|
||||
|
||||
* Developers who want to understand what log level to use and when,
|
||||
* Users who want to debug issues with running their workloads in Cloud Hypervisor
|
||||
- Developers who want to understand what log level to use and when,
|
||||
- Users who want to debug issues with running their workloads in Cloud Hypervisor
|
||||
|
||||
## Control
|
||||
|
||||
@@ -29,7 +29,6 @@ A typical example of where this level of message should be generated is during a
|
||||
|
||||
The user should investigate the meaning of this warning and take steps to ensure the correct functionality.
|
||||
|
||||
|
||||
### `info!()`
|
||||
|
||||
Use `-v` to enable.
|
||||
@@ -40,4 +39,4 @@ This level is for the benefit of developers. It should be used for sporadic and
|
||||
|
||||
Use `-vv` to enable.
|
||||
|
||||
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
|
||||
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
|
||||
|
||||
168
docs/riscv.md
Normal file
168
docs/riscv.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# How to build and test Cloud Hypervisor on riscv64
|
||||
|
||||
This document introduces how to build and test Cloud Hypervisor on `riscv64`.
|
||||
All instructions here are tested with Ubuntu 24.04.2 as the host OS.
|
||||
|
||||
## Hardware requirements
|
||||
|
||||
- riscv64 servers (recommended) or development boards equipped with the AIA
|
||||
(Advance Interrupt Architecture) interrupt controller.
|
||||
|
||||
## Getting started
|
||||
|
||||
We create a folder to build and run Cloud Hypervisor at `$HOME/cloud-hypervisor`
|
||||
|
||||
```console
|
||||
export CLOUDH=$HOME/cloud-hypervisor
|
||||
mkdir $CLOUDH
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need to install some prerequisite packages to build and test Cloud Hypervisor.
|
||||
|
||||
### Tools
|
||||
|
||||
```console
|
||||
# Install rust tool chain
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
# Install the tools used for building guest kernel, EDK2 and converting guest disk
|
||||
sudo apt-get update
|
||||
sudo apt-get install git build-essential m4 bison flex uuid-dev qemu-utils
|
||||
```
|
||||
|
||||
### Building Cloud Hypervisor
|
||||
|
||||
```console
|
||||
pushd $CLOUDH
|
||||
git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git
|
||||
cd cloud-hypervisor
|
||||
cargo build
|
||||
popd
|
||||
```
|
||||
|
||||
### Disk image
|
||||
|
||||
Download the Ubuntu cloud image and convert the image type.
|
||||
|
||||
```console
|
||||
pushd $CLOUDH
|
||||
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-riscv64.img
|
||||
qemu-img convert -p -f qcow2 -O raw jammy-server-cloudimg-riscv64.img jammy-server-cloudimg-riscv64.raw
|
||||
popd
|
||||
```
|
||||
|
||||
## Direct-kernel booting
|
||||
|
||||
### Building kernel
|
||||
|
||||
```console
|
||||
pushd $CLOUDH
|
||||
git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b ch-6.12.8
|
||||
cd linux
|
||||
make ch_defconfig
|
||||
make -j `nproc`
|
||||
popd
|
||||
```
|
||||
|
||||
### Booting the guest VM
|
||||
|
||||
```console
|
||||
pushd $CLOUDH
|
||||
sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||
--kernel $CLOUDH/linux/arch/riscv64/boot/Image \
|
||||
--disk path=jammy-server-cloudimg-riscv64.raw \
|
||||
--cmdline "console=hvc0 root=/dev/vda rw" \
|
||||
--cpus boot=1 \
|
||||
--memory size=1024M \
|
||||
--seccomp false \
|
||||
--log-file boot.log -vv
|
||||
popd
|
||||
```
|
||||
|
||||
## Virtualized Development Setup
|
||||
|
||||
Since there are few RISC-V development boards on the market and not
|
||||
many details about the AIA interrupt controller featured in product listings,
|
||||
QEMU is a popular and viable choice for creating a RISC-V development environment.
|
||||
Below are the steps used to create a QEMU virtual machine that can be used for
|
||||
cloud-hypervisor RISC-V development:
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```console
|
||||
sudo apt update
|
||||
sudo apt install opensbi qemu-system-misc u-boot-qemu
|
||||
```
|
||||
|
||||
### Download and Build QEMU (>=v9.2.0)
|
||||
|
||||
Older versions of QEMU may not have support for the AIA
|
||||
interrupt controller.
|
||||
|
||||
```console
|
||||
wget https://download.qemu.org/qemu-10.0.0.tar.xz
|
||||
tar xvJf qemu-10.0.0.tar.xz
|
||||
cd qemu-10.0.0
|
||||
./configure --target-list=riscv64-softmmu
|
||||
make -j $(nproc)
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Download Ubuntu Server Image
|
||||
|
||||
At the time of writing, the best results have been seen with
|
||||
the Ubuntu 24.10 (Oracular) server image. Ex:
|
||||
|
||||
```console
|
||||
wget https://cdimage.ubuntu.com/releases/oracular/release/ubuntu-24.10-preinstalled-server-riscv64.img.xz
|
||||
xz -dk ubuntu-24.10-preinstalled-server-riscv64.img.xz
|
||||
```
|
||||
|
||||
### (Optional) Resize Disk
|
||||
|
||||
If you would like a larger disk, you can resize it now.
|
||||
|
||||
```console
|
||||
qemu-img resize -f raw <ubuntu-image> +5G
|
||||
```
|
||||
|
||||
### Boot VM
|
||||
|
||||
Note the inclusion of the AIA interrupt controller in the
|
||||
invocation.
|
||||
|
||||
```console
|
||||
qemu-system-riscv64 \
|
||||
-machine virt,aia=aplic-imsic \
|
||||
-nographic -m 1G -smp 8 \
|
||||
-kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
|
||||
-device virtio-rng-pci \
|
||||
-device virtio-net-device,netdev=eth0 -netdev user,id=eth0 \
|
||||
-drive file=<ubuntu-image>,format=raw,if=virtio
|
||||
```
|
||||
|
||||
### Install KVM Kernel Module Within VM
|
||||
KVM is not enabled within the VM by default, so we must enable
|
||||
it manually.
|
||||
|
||||
```console
|
||||
sudo modprobe kvm
|
||||
```
|
||||
|
||||
From this point, you can continue with the above steps from the beginning.
|
||||
|
||||
### Sources
|
||||
|
||||
https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html
|
||||
|
||||
https://canonical-ubuntu-boards.readthedocs-hosted.com/en/latest/how-to/qemu-riscv/#using-the-live-server-image
|
||||
|
||||
https://www.qemu.org/docs/master/specs/riscv-aia.html
|
||||
|
||||
## Known limitations
|
||||
|
||||
- Direct kernel boot only
|
||||
- `64-bit Linux` guest OS only
|
||||
- For more details, see
|
||||
[here](https://github.com/cloud-hypervisor/cloud-hypervisor/issues/6978).
|
||||
@@ -9,4 +9,4 @@ flume = "0.11.1"
|
||||
libc = "0.2.167"
|
||||
once_cell = "1.20.2"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.120"
|
||||
serde_json = { workspace = true }
|
||||
|
||||
141
fuzz/Cargo.lock
generated
141
fuzz/Cargo.lock
generated
@@ -5,9 +5,9 @@ version = 4
|
||||
[[package]]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#849d5950196f66dd10f2b2606d8fe8c7cb39ec24"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e08a3f0b0a59b98859dbf59f5aa7fd4d2eb4018a"
|
||||
dependencies = [
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -103,6 +103,17 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-struct"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -111,9 +122,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
@@ -291,7 +302,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"byteorder",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -335,7 +346,7 @@ version = "4.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -398,7 +409,7 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbcc892208d6998fb57e7c3e05883def66f8130924bba066beb0cfe71566a9f6"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
@@ -425,10 +436,22 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
@@ -441,6 +464,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"bitfield-struct",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"concat-idents",
|
||||
@@ -450,12 +474,15 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"mshv-bindings",
|
||||
"open-enum",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"thiserror 2.0.9",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -530,7 +557,7 @@ version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e013ae7fcd2c6a8f384104d16afe7ea02969301ea2bb2a56e44b011ebc907cab"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -610,7 +637,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#8182cd5523b63ceb52ad9d0e7eb6fb95683e6d1b"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#4f621532e81ee2ad096a9c9592fdacc40d19de48"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -618,16 +645,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.3.2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0cb5031f3243a7459b7c13d960d25420980874eebda816db24ce6077e21d43"
|
||||
checksum = "577073a0abbf515d17bfe96ca2ce49c44a68454d4179e95ce1244e858a9ebd4e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num_enum",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"vmm-sys-util",
|
||||
"zerocopy 0.8.14",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -636,7 +663,7 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -651,7 +678,7 @@ name = "net_util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
@@ -701,6 +728,26 @@ version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "open-enum"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb2508143a400b3361812094d987dd5adc81f0f5294a46491be648d6c94cab5"
|
||||
dependencies = [
|
||||
"open-enum-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "open-enum-derive"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
version = "0.1.0"
|
||||
@@ -770,20 +817,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
@@ -791,11 +838,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -834,9 +881,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345a3e4dddf721a478089d4697b83c6c0a8f5bf16086f6c13397e4534eb6e2e5"
|
||||
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1052,20 +1099,20 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.10.0"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
"rand",
|
||||
"uuid-macro-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid-macro-internal"
|
||||
version = "1.10.0"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a"
|
||||
checksum = "9521621447c21497fac206ffe6e9f642f977c4f82eeba9201055f64884d9cb01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1118,7 +1165,7 @@ name = "vhost"
|
||||
version = "0.12.1"
|
||||
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"libc",
|
||||
"uuid",
|
||||
"vm-memory",
|
||||
@@ -1243,7 +1290,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"arch",
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.9.0",
|
||||
"block",
|
||||
"cfg-if",
|
||||
"clap",
|
||||
@@ -1282,7 +1329,7 @@ dependencies = [
|
||||
"vm-migration",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1303,6 +1350,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.93"
|
||||
@@ -1462,6 +1518,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
@@ -1474,11 +1539,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.14"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
|
||||
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.14",
|
||||
"zerocopy-derive 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1494,9 +1559,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.14"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
|
||||
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -24,10 +24,10 @@ libc = "0.2.155"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
mshv-bindings = "0.3.2"
|
||||
mshv-bindings = "0.5.0"
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.19.0"
|
||||
seccompiler = "0.4.0"
|
||||
seccompiler = "0.5.0"
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.14.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
|
||||
@@ -15,11 +15,12 @@ tdx = []
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
arc-swap = "1.7.1"
|
||||
bitfield-struct = "0.10.1"
|
||||
byteorder = "1.5.0"
|
||||
cfg-if = "1.0.0"
|
||||
concat-idents = "1.1.5"
|
||||
igvm = { version = "0.3.3", optional = true }
|
||||
igvm_defs = { version = "0.3.1", optional = true }
|
||||
igvm = { workspace = true, optional = true }
|
||||
igvm_defs = { workspace = true, optional = true }
|
||||
kvm-bindings = { workspace = true, optional = true, features = ["serde"] }
|
||||
kvm-ioctls = { workspace = true, optional = true }
|
||||
libc = "0.2.167"
|
||||
@@ -29,7 +30,9 @@ mshv-bindings = { workspace = true, features = [
|
||||
"with-serde",
|
||||
], optional = true }
|
||||
mshv-ioctls = { workspace = true, optional = true }
|
||||
open-enum = "0.5.2"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_with = { version = "3.9.0", default-features = false, features = [
|
||||
"macros",
|
||||
] }
|
||||
@@ -41,6 +44,7 @@ vm-memory = { workspace = true, features = [
|
||||
"backend-mmap",
|
||||
] }
|
||||
vmm-sys-util = { workspace = true, features = ["with-serde"] }
|
||||
zerocopy = { version = "0.8.24", features = ["derive"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
default-features = false
|
||||
|
||||
@@ -5,22 +5,25 @@
|
||||
use std::any::Any;
|
||||
use std::result;
|
||||
|
||||
use serde::de::Error as SerdeError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||
use crate::{CpuState, HypervisorDeviceError, HypervisorVmError};
|
||||
|
||||
/// Errors thrown while setting up the VGIC.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
#[error("Failed creating GIC device: {0}")]
|
||||
CreateGic(HypervisorVmError),
|
||||
CreateGic(#[source] HypervisorVmError),
|
||||
/// Error while setting device attributes for the GIC.
|
||||
#[error("Failed setting device attributes for the GIC: {0}")]
|
||||
SetDeviceAttribute(HypervisorDeviceError),
|
||||
SetDeviceAttribute(#[source] HypervisorDeviceError),
|
||||
/// Error while getting device attributes for the GIC.
|
||||
#[error("Failed getting device attributes for the GIC: {0}")]
|
||||
GetDeviceAttribute(HypervisorDeviceError),
|
||||
GetDeviceAttribute(#[source] HypervisorDeviceError),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
@@ -36,6 +39,56 @@ pub struct VgicConfig {
|
||||
pub nr_irqs: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
pub enum GicState {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(crate::kvm::aarch64::gic::Gicv3ItsState),
|
||||
#[cfg(feature = "mshv")]
|
||||
MshvGicV2M(crate::mshv::aarch64::gic::MshvGicV2MState),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for GicState {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
// GicStateDefaultDeserialize is a helper enum that mirrors GicState but also derives the Deserialize trait.
|
||||
// This enables backward-compatible deserialization of GicState, facilitating live-upgrade scenarios.
|
||||
#[derive(Deserialize)]
|
||||
pub enum GicStateDefaultDeserialize {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(crate::kvm::aarch64::gic::Gicv3ItsState),
|
||||
#[cfg(feature = "mshv")]
|
||||
MshvGicV2M(crate::mshv::aarch64::gic::MshvGicV2MState),
|
||||
}
|
||||
|
||||
const {
|
||||
assert!(
|
||||
std::mem::size_of::<GicStateDefaultDeserialize>()
|
||||
== std::mem::size_of::<GicState>()
|
||||
)
|
||||
};
|
||||
|
||||
let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
#[cfg(feature = "kvm")]
|
||||
if let Ok(gicv3_its_state) =
|
||||
crate::kvm::aarch64::gic::Gicv3ItsState::deserialize(value.clone())
|
||||
{
|
||||
return Ok(GicState::Kvm(gicv3_its_state));
|
||||
}
|
||||
|
||||
if let Ok(gic_state_de) = GicStateDefaultDeserialize::deserialize(value.clone()) {
|
||||
return match gic_state_de {
|
||||
#[cfg(feature = "kvm")]
|
||||
GicStateDefaultDeserialize::Kvm(state) => Ok(GicState::Kvm(state)),
|
||||
#[cfg(feature = "mshv")]
|
||||
GicStateDefaultDeserialize::MshvGicV2M(state) => Ok(GicState::MshvGicV2M(state)),
|
||||
};
|
||||
}
|
||||
Err(SerdeError::custom("Failed to deserialize GicState"))
|
||||
}
|
||||
}
|
||||
/// Hypervisor agnostic interface for a virtualized GIC
|
||||
pub trait Vgic: Send + Sync {
|
||||
/// Returns the fdt compatibility property of the device
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod gic;
|
||||
pub mod regs;
|
||||
|
||||
228
hypervisor/src/arch/aarch64/regs.rs
Normal file
228
hypervisor/src/arch/aarch64/regs.rs
Normal file
@@ -0,0 +1,228 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
// Copyright © 2025, Microsoft Corporation
|
||||
//
|
||||
|
||||
use bitfield_struct::bitfield;
|
||||
use open_enum::open_enum;
|
||||
use zerocopy::{FromBytes, IntoBytes};
|
||||
|
||||
/// ESR_EL2, exception syndrome register.
|
||||
#[bitfield(u64)]
|
||||
#[derive(IntoBytes, FromBytes)]
|
||||
pub struct EsrEl2 {
|
||||
#[bits(25)]
|
||||
pub iss: u32,
|
||||
pub il: bool,
|
||||
#[bits(6)]
|
||||
pub ec: u8,
|
||||
#[bits(5)]
|
||||
pub iss2: u8,
|
||||
#[bits(27)]
|
||||
_rsvd: u32,
|
||||
}
|
||||
|
||||
#[open_enum]
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum FaultStatusCode {
|
||||
ADDRESS_SIZE_FAULT_LEVEL0 = 0b000000,
|
||||
ADDRESS_SIZE_FAULT_LEVEL1 = 0b000001,
|
||||
ADDRESS_SIZE_FAULT_LEVEL2 = 0b000010,
|
||||
ADDRESS_SIZE_FAULT_LEVEL3 = 0b000011,
|
||||
TRANSLATION_FAULT_LEVEL0 = 0b000100,
|
||||
TRANSLATION_FAULT_LEVEL1 = 0b000101,
|
||||
TRANSLATION_FAULT_LEVEL2 = 0b000110,
|
||||
TRANSLATION_FAULT_LEVEL3 = 0b000111,
|
||||
ACCESS_FLAG_FAULT_LEVEL0 = 0b001000,
|
||||
ACCESS_FLAG_FAULT_LEVEL1 = 0b001001,
|
||||
ACCESS_FLAG_FAULT_LEVEL2 = 0b001010,
|
||||
ACCESS_FLAG_FAULT_LEVEL3 = 0b001011,
|
||||
PERMISSION_FAULT_LEVEL0 = 0b001100,
|
||||
PERMISSION_FAULT_LEVEL1 = 0b001101,
|
||||
PERMISSION_FAULT_LEVEL2 = 0b001110,
|
||||
PERMISSION_FAULT_LEVEL3 = 0b001111,
|
||||
SYNCHRONOUS_EXTERNAL_ABORT = 0b010000,
|
||||
SYNC_TAG_CHECK_FAULT = 0b010001,
|
||||
SEA_TTW_LEVEL_NEG1 = 0b010011,
|
||||
SEA_TTW_LEVEL0 = 0b010100,
|
||||
SEA_TTW_LEVEL1 = 0b010101,
|
||||
SEA_TTW_LEVEL2 = 0b010110,
|
||||
SEA_TTW_LEVEL3 = 0b010111,
|
||||
ECC_PARITY = 0b011000,
|
||||
ECC_PARITY_TTW_LEVEL_NEG1 = 0b011011,
|
||||
ECC_PARITY_TTW_LEVEL0 = 0b011100,
|
||||
ECC_PARITY_TTW_LEVEL1 = 0b011101,
|
||||
ECC_PARITY_TTW_LEVEL2 = 0b011110,
|
||||
ECC_PARITY_TTW_LEVEL3 = 0b011111,
|
||||
/// Valid only for data fault.
|
||||
ALIGNMENT_FAULT = 0b100001,
|
||||
/// Valid only for instruction fault.
|
||||
GRANULE_PROTECTION_FAULT_LEVEL_NEG = 0b100011,
|
||||
/// Valid only for instruction fault.
|
||||
GRANULE_PROTECTION_FAULT_LEVEL0 = 0b100100,
|
||||
/// Valid only for instruction fault.
|
||||
GRANULE_PROTECTION_FAULT_LEVEL1 = 0b100101,
|
||||
/// Valid only for instruction fault.
|
||||
GRANULE_PROTECTION_FAULT_LEVEL2 = 0b100110,
|
||||
/// Valid only for instruction fault.
|
||||
GRANULE_PROTECTION_FAULT_LEVEL3 = 0b100111,
|
||||
ADDRESS_SIZE_FAULT_LEVEL_NEG1 = 0b101001,
|
||||
TRANSLATION_FAULT_LEVEL_NEG1 = 0b101011,
|
||||
TLB_CONFLICT_ABORT = 0b110000,
|
||||
UNSUPPORTED_HW_UPDATE_FAULT = 0b110001,
|
||||
}
|
||||
|
||||
/// Support for embedding within IssDataAbort/IssInstructionAbort
|
||||
impl FaultStatusCode {
|
||||
const fn from_bits(bits: u32) -> Self {
|
||||
FaultStatusCode((bits & 0x3f) as u8)
|
||||
}
|
||||
|
||||
const fn into_bits(self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield(u32)]
|
||||
pub struct IssDataAbort {
|
||||
#[bits(6)]
|
||||
pub dfsc: FaultStatusCode,
|
||||
// Write operation (write not read)
|
||||
pub wnr: bool,
|
||||
pub s1ptw: bool,
|
||||
pub cm: bool,
|
||||
pub ea: bool,
|
||||
/// FAR not valid
|
||||
pub fnv: bool,
|
||||
#[bits(2)]
|
||||
pub set: u8,
|
||||
pub vncr: bool,
|
||||
/// Acquire/release
|
||||
pub ar: bool,
|
||||
/// (ISV==1) 64-bit, (ISV==0) FAR is approximate
|
||||
pub sf: bool,
|
||||
#[bits(5)]
|
||||
/// Register index.
|
||||
pub srt: u8,
|
||||
/// Sign extended.
|
||||
pub sse: bool,
|
||||
#[bits(2)]
|
||||
/// access width log2
|
||||
pub sas: u8,
|
||||
/// Valid ESREL2 iss field.
|
||||
pub isv: bool,
|
||||
#[bits(7)]
|
||||
_unused: u8,
|
||||
}
|
||||
|
||||
#[open_enum]
|
||||
#[repr(u8)]
|
||||
pub enum ExceptionClass {
|
||||
UNKNOWN = 0b000000,
|
||||
WFI = 0b000001,
|
||||
MCR_MRC_COPROC_15 = 0b000011,
|
||||
MCRR_MRRC_COPROC_15 = 0b000100,
|
||||
MCR_MRC_COPROC_14 = 0b000101,
|
||||
LDC_STC = 0b000110,
|
||||
FP_OR_SIMD = 0b000111,
|
||||
VMRS = 0b001000,
|
||||
POINTER_AUTH_HCR_OR_SCR = 0b001001,
|
||||
LS64 = 0b001010,
|
||||
MRRC_COPROC_14 = 0b001100,
|
||||
BRANCH_TARGET = 0b001101,
|
||||
ILLEGAL_STATE = 0b001110,
|
||||
SVC32 = 0b010001,
|
||||
HVC32 = 0b010010,
|
||||
SMC32 = 0b010011,
|
||||
SVC = 0b010101,
|
||||
HVC = 0b010110,
|
||||
SMC = 0b010111,
|
||||
SYSTEM = 0b011000,
|
||||
SVE = 0b011001,
|
||||
ERET = 0b011010,
|
||||
TSTART = 0b011011,
|
||||
POINTER_AUTH = 0b011100,
|
||||
SME = 0b011101,
|
||||
INSTRUCTION_ABORT_LOWER = 0b100000,
|
||||
INSTRUCTION_ABORT = 0b100001,
|
||||
PC_ALIGNMENT = 0b100010,
|
||||
DATA_ABORT_LOWER = 0b100100,
|
||||
DATA_ABORT = 0b100101,
|
||||
SP_ALIGNMENT_FAULT = 0b100110,
|
||||
MEMORY_OP = 0b100111,
|
||||
FP_EXCEPTION_32 = 0b101000,
|
||||
FP_EXCEPTION_64 = 0b101100,
|
||||
SERROR = 0b101111,
|
||||
BREAKPOINT_LOWER = 0b110000,
|
||||
BREAKPOINT = 0b110001,
|
||||
STEP_LOWER = 0b110010,
|
||||
STEP = 0b110011,
|
||||
WATCHPOINT_LOWER = 0b110100,
|
||||
WATCHPOINT = 0b110101,
|
||||
BRK32 = 0b111000,
|
||||
VECTOR_CATCH_32 = 0b111010,
|
||||
BRK = 0b111100,
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
// PSR (Processor State Register) bits.
|
||||
// Taken from arch/arm64/include/uapi/asm/ptrace.h.
|
||||
const PSR_MODE_EL1h: u64 = 0x0000_0005;
|
||||
const PSR_F_BIT: u64 = 0x0000_0040;
|
||||
const PSR_I_BIT: u64 = 0x0000_0080;
|
||||
const PSR_A_BIT: u64 = 0x0000_0100;
|
||||
const PSR_D_BIT: u64 = 0x0000_0200;
|
||||
// Taken from arch/arm64/kvm/inject_fault.c.
|
||||
pub const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT | PSR_D_BIT;
|
||||
|
||||
// AArch64 system register encoding:
|
||||
// See https://developer.arm.com/documentation/ddi0487 (chapter D12)
|
||||
//
|
||||
// 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
|
||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
// |1101010100| L | op0 | op1 | CRn | CRm | op2 | Rt |
|
||||
// +----------+---+-----+-----+-----+-----+-----+----+
|
||||
//
|
||||
// Notes:
|
||||
// - L and Rt are reserved as implementation defined fields, ignored.
|
||||
|
||||
const SYSREG_HEAD: u32 = 0b1101010100u32 << 22;
|
||||
const SYSREG_OP0_SHIFT: u32 = 19;
|
||||
const SYSREG_OP0_MASK: u32 = 0b11u32 << 19;
|
||||
const SYSREG_OP1_SHIFT: u32 = 16;
|
||||
const SYSREG_OP1_MASK: u32 = 0b111u32 << 16;
|
||||
const SYSREG_CRN_SHIFT: u32 = 12;
|
||||
const SYSREG_CRN_MASK: u32 = 0b1111u32 << 12;
|
||||
const SYSREG_CRM_SHIFT: u32 = 8;
|
||||
const SYSREG_CRM_MASK: u32 = 0b1111u32 << 8;
|
||||
const SYSREG_OP2_SHIFT: u32 = 5;
|
||||
const SYSREG_OP2_MASK: u32 = 0b111u32 << 5;
|
||||
|
||||
/// Define the ID of system registers
|
||||
#[macro_export]
|
||||
macro_rules! arm64_sys_reg {
|
||||
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => {
|
||||
pub const $name: u32 = SYSREG_HEAD
|
||||
| ((($op0 as u32) << SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK as u32)
|
||||
| ((($op1 as u32) << SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK as u32)
|
||||
| ((($crn as u32) << SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK as u32)
|
||||
| ((($crm as u32) << SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK as u32)
|
||||
| ((($op2 as u32) << SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK as u32);
|
||||
};
|
||||
}
|
||||
|
||||
arm64_sys_reg!(MPIDR_EL1, 3, 0, 0, 0, 5);
|
||||
arm64_sys_reg!(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0);
|
||||
arm64_sys_reg!(TTBR1_EL1, 3, 0, 2, 0, 1);
|
||||
arm64_sys_reg!(TCR_EL1, 3, 0, 2, 0, 2);
|
||||
|
||||
pub const AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ: u32 = 13;
|
||||
pub const AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ: u32 = 14;
|
||||
pub const AARCH64_ARCH_TIMER_VIRT_IRQ: u32 = 11;
|
||||
pub const AARCH64_ARCH_TIMER_HYP_IRQ: u32 = 10;
|
||||
|
||||
// PMU PPI interrupt number
|
||||
pub const AARCH64_PMU_IRQ: u32 = 7;
|
||||
|
||||
pub const AARCH64_MIN_PPI_IRQ: u32 = 16;
|
||||
@@ -82,13 +82,13 @@ pub enum EmulationError<T: Debug> {
|
||||
WrongNumberOperands(#[source] anyhow::Error),
|
||||
|
||||
#[error("Instruction Exception: {0}")]
|
||||
InstructionException(Exception<T>),
|
||||
InstructionException(#[source] Exception<T>),
|
||||
|
||||
#[error("Instruction fetching error: {0}")]
|
||||
InstructionFetchingError(#[source] anyhow::Error),
|
||||
|
||||
#[error("Platform emulation error: {0}")]
|
||||
PlatformEmulationError(PlatformError),
|
||||
PlatformEmulationError(#[source] PlatformError),
|
||||
|
||||
#[error(transparent)]
|
||||
EmulationError(#[from] anyhow::Error),
|
||||
|
||||
@@ -14,13 +14,13 @@ use crate::{AiaState, HypervisorDeviceError, HypervisorVmError};
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
#[error("Failed creating AIA device: {0}")]
|
||||
CreateAia(HypervisorVmError),
|
||||
CreateAia(#[source] HypervisorVmError),
|
||||
/// Error while setting device attributes for the AIA.
|
||||
#[error("Failed setting device attributes for the AIA: {0}")]
|
||||
SetDeviceAttribute(HypervisorDeviceError),
|
||||
SetDeviceAttribute(#[source] HypervisorDeviceError),
|
||||
/// Error while getting device attributes for the AIA.
|
||||
#[error("Failed getting device attributes for the AIA: {0}")]
|
||||
GetDeviceAttribute(HypervisorDeviceError),
|
||||
GetDeviceAttribute(#[source] HypervisorDeviceError),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
||||
@@ -10,18 +10,21 @@
|
||||
//
|
||||
//
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use std::sync::Arc;
|
||||
|
||||
use thiserror::Error;
|
||||
#[cfg(not(target_arch = "riscv64"))]
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::{RegList, VcpuInit};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::{CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
use crate::riscv64::RegList;
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
use crate::RegList;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::VcpuInit;
|
||||
use crate::{CpuState, MpState, StandardRegisters};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -320,7 +323,13 @@ pub enum HypervisorCpuError {
|
||||
#[cfg(feature = "sev_snp")]
|
||||
#[error("Failed to set sev control register: {0}")]
|
||||
SetSevControlRegister(#[source] anyhow::Error),
|
||||
|
||||
///
|
||||
/// Unsupported SysReg registers
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Unsupported SysReg registers: {0}")]
|
||||
UnsupportedSysReg(u32),
|
||||
///
|
||||
/// Error injecting NMI
|
||||
///
|
||||
#[error("Failed to inject NMI")]
|
||||
@@ -449,7 +458,26 @@ pub trait Vcpu: Send + Sync {
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn vcpu_finalize(&self, feature: i32) -> Result<()>;
|
||||
|
||||
///
|
||||
/// Gets the features that have been finalized for a given CPU.
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn vcpu_get_finalized_features(&self) -> i32;
|
||||
///
|
||||
/// Sets processor features for a given CPU.
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn vcpu_set_processor_features(
|
||||
&self,
|
||||
vm: &Arc<dyn crate::Vm>,
|
||||
kvi: &mut VcpuInit,
|
||||
id: u8,
|
||||
) -> Result<()>;
|
||||
///
|
||||
/// Returns VcpuInit with default value set
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_vcpu_init(&self) -> VcpuInit;
|
||||
///
|
||||
/// Gets a list of the guest registers that are supported for the
|
||||
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
|
||||
@@ -562,7 +590,13 @@ pub trait Vcpu: Send + Sync {
|
||||
fn set_sev_control_register(&self, _reg: u64) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the value of GIC redistributor address
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn set_gic_redistributor_addr(&self, _gicr_base_addr: u64) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Trigger NMI interrupt
|
||||
|
||||
@@ -67,22 +67,22 @@ pub enum HypervisorError {
|
||||
///
|
||||
/// Checking extensions failed
|
||||
///
|
||||
#[error("Checking extensions:{0}")]
|
||||
#[error("Checking extensions: {0}")]
|
||||
CheckExtensions(#[source] anyhow::Error),
|
||||
///
|
||||
/// Failed to retrieve TDX capabilities
|
||||
///
|
||||
#[error("Failed to retrieve TDX capabilities:{0}")]
|
||||
#[error("Failed to retrieve TDX capabilities: {0}")]
|
||||
TdxCapabilities(#[source] anyhow::Error),
|
||||
///
|
||||
/// Failed to set partition property
|
||||
///
|
||||
#[error("Failed to set partition property:{0}")]
|
||||
#[error("Failed to set partition property: {0}")]
|
||||
SetPartitionProperty(#[source] anyhow::Error),
|
||||
///
|
||||
/// Running on an unsupported CPU
|
||||
///
|
||||
#[error("Unsupported CPU:{0}")]
|
||||
#[error("Unsupported CPU: {0}")]
|
||||
UnsupportedCpu(#[source] anyhow::Error),
|
||||
///
|
||||
/// Launching a VM with unsupported VM Type
|
||||
|
||||
@@ -14,7 +14,7 @@ use kvm_ioctls::DeviceFd;
|
||||
use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
|
||||
use crate::arch::aarch64::gic::{Error, GicState, Result, Vgic, VgicConfig};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::KvmVm;
|
||||
use crate::{CpuState, Vm};
|
||||
@@ -125,6 +125,23 @@ pub struct Gicv3ItsState {
|
||||
its_baser: [u64; 8],
|
||||
}
|
||||
|
||||
impl From<GicState> for Gicv3ItsState {
|
||||
fn from(state: GicState) -> Self {
|
||||
match state {
|
||||
GicState::Kvm(state) => state,
|
||||
/* Needed in case other hypervisors are enabled */
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("GicState is not valid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Gicv3ItsState> for GicState {
|
||||
fn from(state: Gicv3ItsState) -> Self {
|
||||
GicState::Kvm(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmGicV3Its {
|
||||
/// Device trees specific constants
|
||||
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||
@@ -316,7 +333,7 @@ impl Vgic for KvmGicV3Its {
|
||||
}
|
||||
|
||||
/// Save the state of GICv3ITS.
|
||||
fn state(&self) -> Result<Gicv3ItsState> {
|
||||
fn state(&self) -> Result<GicState> {
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
|
||||
let gicd_ctlr = read_ctlr(&self.device)?;
|
||||
@@ -366,7 +383,7 @@ impl Vgic for KvmGicV3Its {
|
||||
GITS_IIDR,
|
||||
)?;
|
||||
|
||||
Ok(Gicv3ItsState {
|
||||
let gic_state: GicState = Gicv3ItsState {
|
||||
dist: dist_state,
|
||||
rdist: rdist_state,
|
||||
icc: icc_state,
|
||||
@@ -377,48 +394,53 @@ impl Vgic for KvmGicV3Its {
|
||||
its_cwriter: its_cwriter_state,
|
||||
its_creadr: its_creadr_state,
|
||||
its_baser: its_baser_state,
|
||||
})
|
||||
}
|
||||
.into();
|
||||
|
||||
Ok(gic_state)
|
||||
}
|
||||
|
||||
/// Restore the state of GICv3ITS.
|
||||
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
|
||||
fn set_state(&mut self, state: &GicState) -> Result<()> {
|
||||
let kvm_state: Gicv3ItsState = state.clone().into();
|
||||
|
||||
let gicr_typers = self.gicr_typers.clone();
|
||||
|
||||
write_ctlr(&self.device, state.gicd_ctlr)?;
|
||||
write_ctlr(&self.device, kvm_state.gicd_ctlr)?;
|
||||
|
||||
set_dist_regs(&self.device, &state.dist)?;
|
||||
set_dist_regs(&self.device, &kvm_state.dist)?;
|
||||
|
||||
set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
|
||||
set_redist_regs(&self.device, &gicr_typers, &kvm_state.rdist)?;
|
||||
|
||||
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
|
||||
set_icc_regs(&self.device, &gicr_typers, &kvm_state.icc)?;
|
||||
|
||||
//Restore GICv3ITS registers
|
||||
gicv3_its_attr_set(
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_IIDR,
|
||||
state.its_iidr,
|
||||
kvm_state.its_iidr,
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_set(
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CBASER,
|
||||
state.its_cbaser,
|
||||
kvm_state.its_cbaser,
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_set(
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CREADR,
|
||||
state.its_creadr,
|
||||
kvm_state.its_creadr,
|
||||
)?;
|
||||
|
||||
gicv3_its_attr_set(
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CWRITER,
|
||||
state.its_cwriter,
|
||||
kvm_state.its_cwriter,
|
||||
)?;
|
||||
|
||||
for i in 0..8 {
|
||||
@@ -426,7 +448,7 @@ impl Vgic for KvmGicV3Its {
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_BASER + i * 8,
|
||||
state.its_baser[i as usize],
|
||||
kvm_state.its_baser[i as usize],
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -437,7 +459,7 @@ impl Vgic for KvmGicV3Its {
|
||||
self.its_device.as_ref().unwrap(),
|
||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||
GITS_CTLR,
|
||||
state.its_ctlr,
|
||||
kvm_state.its_ctlr,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ use kvm_ioctls::DeviceFd;
|
||||
use crate::arch::aarch64::gic::{Error, Result};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::kvm_bindings::{
|
||||
kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64, KVM_REG_ARM64_SYSREG,
|
||||
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK,
|
||||
KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
|
||||
kvm_device_attr, kvm_one_reg, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_REG_ARM64,
|
||||
KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT,
|
||||
KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_SIZE_U64,
|
||||
};
|
||||
use crate::kvm::{Register, VcpuKvmState};
|
||||
use crate::kvm::VcpuKvmState;
|
||||
use crate::CpuState;
|
||||
|
||||
// Relevant redistributor registers that we want to save/restore.
|
||||
@@ -213,7 +213,7 @@ pub fn construct_gicr_typers(vcpu_states: &[CpuState]) -> Vec<u64> {
|
||||
let state: VcpuKvmState = state.clone().into();
|
||||
let last = (index == vcpu_states.len() - 1) as u64;
|
||||
// state.sys_regs is a big collection of system registers, including MIPDR_EL1
|
||||
let mpidr: Vec<Register> = state
|
||||
let mpidr: Vec<kvm_one_reg> = state
|
||||
.sys_regs
|
||||
.into_iter()
|
||||
.filter(|reg| reg.id == KVM_ARM64_SYSREG_MPIDR_EL1)
|
||||
|
||||
@@ -14,7 +14,6 @@ use kvm_bindings::{
|
||||
kvm_mp_state, kvm_one_reg, kvm_regs, KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE,
|
||||
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||
};
|
||||
pub use kvm_bindings::{kvm_one_reg as Register, kvm_vcpu_init as VcpuInit, RegList};
|
||||
pub use kvm_ioctls::{Cap, Kvm};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,6 @@ pub struct AiaImsicsState {}
|
||||
|
||||
impl KvmAiaImsics {
|
||||
/// Device trees specific constants
|
||||
|
||||
fn version() -> u32 {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_RISCV_AIA
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use kvm_bindings::{
|
||||
kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK,
|
||||
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64,
|
||||
};
|
||||
pub use kvm_bindings::{kvm_one_reg as Register, RegList};
|
||||
pub use kvm_ioctls::{Cap, Kvm};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ pub mod arch;
|
||||
pub mod kvm;
|
||||
|
||||
/// Microsoft Hypervisor implementation module
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
#[cfg(feature = "mshv")]
|
||||
pub mod mshv;
|
||||
|
||||
/// Hypervisor related module
|
||||
@@ -59,7 +59,7 @@ pub use cpu::CpuVendor;
|
||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||
pub use device::HypervisorDeviceError;
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::{aarch64, GicState};
|
||||
pub use kvm::aarch64;
|
||||
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
|
||||
pub use kvm::{riscv64, AiaState};
|
||||
pub use vm::{
|
||||
@@ -148,7 +148,7 @@ pub const USER_MEMORY_REGION_ADJUSTABLE: u32 = 1 << 4;
|
||||
pub enum MpState {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_mp_state),
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
#[cfg(feature = "mshv")]
|
||||
Mshv, /* MSHV does not support MpState yet */
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ pub enum IoEventAddress {
|
||||
pub enum CpuState {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm::VcpuKvmState),
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
#[cfg(feature = "mshv")]
|
||||
Mshv(mshv::VcpuMshvState),
|
||||
}
|
||||
|
||||
@@ -196,16 +196,35 @@ pub enum IrqRoutingEntry {
|
||||
Mshv(mshv_bindings::mshv_user_irq_entry),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum VcpuInit {
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
Kvm(kvm_bindings::kvm_vcpu_init),
|
||||
#[cfg(all(feature = "mshv", target_arch = "aarch64"))]
|
||||
Mshv(mshv_bindings::MshvVcpuInit),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RegList {
|
||||
#[cfg(all(feature = "kvm", any(target_arch = "aarch64", target_arch = "riscv64")))]
|
||||
Kvm(kvm_bindings::RegList),
|
||||
#[cfg(all(feature = "mshv", target_arch = "aarch64"))]
|
||||
Mshv(mshv_bindings::MshvRegList),
|
||||
}
|
||||
|
||||
pub enum Register {
|
||||
#[cfg(feature = "kvm")]
|
||||
Kvm(kvm_bindings::kvm_one_reg),
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum StandardRegisters {
|
||||
#[cfg(all(feature = "kvm", not(target_arch = "riscv64")))]
|
||||
Kvm(kvm_bindings::kvm_regs),
|
||||
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
|
||||
Kvm(kvm_bindings::kvm_riscv_core),
|
||||
#[cfg(all(
|
||||
any(feature = "mshv", feature = "mshv_emulator"),
|
||||
target_arch = "x86_64"
|
||||
))]
|
||||
#[cfg(any(feature = "mshv", feature = "mshv_emulator"))]
|
||||
Mshv(mshv_bindings::StandardRegisters),
|
||||
}
|
||||
|
||||
@@ -292,6 +311,8 @@ macro_rules! set_aarch64_reg {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
StandardRegisters::Kvm(s) => s.regs.$reg_name = val,
|
||||
#[cfg(feature = "mshv")]
|
||||
StandardRegisters::Mshv(s) => s.$reg_name = val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,6 +329,8 @@ macro_rules! get_aarch64_reg {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
StandardRegisters::Kvm(s) => s.regs.$reg_name,
|
||||
#[cfg(feature = "mshv")]
|
||||
StandardRegisters::Mshv(s) => s.$reg_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
113
hypervisor/src/mshv/aarch64/emulator.rs
Normal file
113
hypervisor/src/mshv/aarch64/emulator.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
// Copyright © 2025, Microsoft Corporation
|
||||
//
|
||||
|
||||
use crate::arch::aarch64::regs::{EsrEl2, ExceptionClass, IssDataAbort};
|
||||
use crate::arch::emulator::PlatformError;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::mshv::MshvVcpu;
|
||||
|
||||
pub struct MshvEmulatorContext<'a> {
|
||||
pub vcpu: &'a MshvVcpu,
|
||||
pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
|
||||
pub syndrome: u64,
|
||||
pub instruction_bytes: [u8; 4],
|
||||
pub instruction_byte_count: u8,
|
||||
pub interruption_pending: bool,
|
||||
pub pc: u64,
|
||||
}
|
||||
|
||||
pub struct Emulator<'a> {
|
||||
pub context: MshvEmulatorContext<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Emulator<'a> {
|
||||
/// Create a new emulator instance.
|
||||
pub fn new(context: MshvEmulatorContext<'a>) -> Self {
|
||||
Emulator { context }
|
||||
}
|
||||
|
||||
/// Decode & emulate the instruction using the syndrome register.
|
||||
pub fn emulate_with_syndrome(&mut self) -> Result<bool, PlatformError> {
|
||||
let esr_el2 = EsrEl2::from(self.context.syndrome);
|
||||
if !matches!(
|
||||
ExceptionClass(esr_el2.ec()),
|
||||
ExceptionClass::DATA_ABORT | ExceptionClass::DATA_ABORT_LOWER
|
||||
) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let iss = IssDataAbort::from(esr_el2.iss());
|
||||
if !iss.isv() {
|
||||
return Ok(false);
|
||||
}
|
||||
let len = 1 << iss.sas();
|
||||
let sign_extend = iss.sse();
|
||||
let reg_index = iss.srt();
|
||||
|
||||
let mut regs = self
|
||||
.context
|
||||
.vcpu
|
||||
.get_regs()
|
||||
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
||||
let mut gprs = regs.get_regs();
|
||||
|
||||
if iss.wnr() {
|
||||
let data: [u8; 8] = match reg_index {
|
||||
0..=30 => gprs[reg_index as usize],
|
||||
31 => 0u64,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.to_ne_bytes();
|
||||
|
||||
if let Some(vm_ops) = &self.context.vcpu.vm_ops {
|
||||
vm_ops
|
||||
.mmio_write(self.context.map.1, &data[0..len])
|
||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||
}
|
||||
} else {
|
||||
let mut data = [0_u8; 8];
|
||||
if let Some(vm_ops) = &self.context.vcpu.vm_ops {
|
||||
vm_ops
|
||||
.mmio_read(self.context.map.1, &mut data[0..len])
|
||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||
}
|
||||
|
||||
let mut data = u64::from_ne_bytes(data);
|
||||
if sign_extend {
|
||||
let shift = 64 - len * 8;
|
||||
data = ((data as i64) << shift >> shift) as u64;
|
||||
if !iss.sf() {
|
||||
data &= 0xffffffff;
|
||||
}
|
||||
}
|
||||
gprs[reg_index as usize] = data;
|
||||
}
|
||||
|
||||
let pc = regs.get_pc();
|
||||
regs.set_pc(if esr_el2.il() { pc + 4 } else { pc + 2 });
|
||||
regs.set_regs(gprs);
|
||||
|
||||
self.context
|
||||
.vcpu
|
||||
.set_regs(®s)
|
||||
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Emulate the instruction.
|
||||
pub fn emulate(&mut self) -> Result<(), PlatformError> {
|
||||
match self.emulate_with_syndrome() {
|
||||
Ok(true) => Ok(()),
|
||||
Ok(false) => Err(PlatformError::InvalidState(anyhow!(
|
||||
"Failed to decode instruction using syndrome register"
|
||||
))),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
// TODO: Add support for instruction decoding in case of failure from
|
||||
// decode_with_syndrome. This will require aarch64 instruction emulator
|
||||
// implementation like x86_64.
|
||||
}
|
||||
}
|
||||
126
hypervisor/src/mshv/aarch64/gic/mod.rs
Normal file
126
hypervisor/src/mshv/aarch64/gic/mod.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
// Copyright © 2025, Microsoft Corporation
|
||||
//
|
||||
use std::any::Any;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::arch::aarch64::gic::{GicState, Result, Vgic, VgicConfig};
|
||||
use crate::{CpuState, Vm};
|
||||
|
||||
pub struct MshvGicV2M {
|
||||
/// GIC distributor address
|
||||
pub dist_addr: u64,
|
||||
|
||||
/// GIC distributor size
|
||||
pub dist_size: u64,
|
||||
|
||||
/// GIC re-distributors address
|
||||
pub redists_addr: u64,
|
||||
|
||||
/// GIC re-distributors size
|
||||
pub redists_size: u64,
|
||||
|
||||
/// GITS translator address
|
||||
pub gits_addr: u64,
|
||||
|
||||
/// GITS translator size
|
||||
pub gits_size: u64,
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
pub vcpu_count: u64,
|
||||
}
|
||||
|
||||
pub const BASE_SPI_IRQ: u32 = 32;
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub struct MshvGicV2MState {}
|
||||
|
||||
impl From<GicState> for MshvGicV2MState {
|
||||
fn from(state: GicState) -> Self {
|
||||
match state {
|
||||
GicState::MshvGicV2M(state) => state,
|
||||
/* Needed in case other hypervisors are enabled */
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("GicState is not valid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MshvGicV2MState> for GicState {
|
||||
fn from(state: MshvGicV2MState) -> Self {
|
||||
GicState::MshvGicV2M(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl MshvGicV2M {
|
||||
/// Create a new GICv2m device
|
||||
pub fn new(_vm: &dyn Vm, config: VgicConfig) -> Result<MshvGicV2M> {
|
||||
let gic_device = MshvGicV2M {
|
||||
dist_addr: config.dist_addr,
|
||||
dist_size: config.dist_size,
|
||||
redists_addr: config.redists_addr,
|
||||
redists_size: config.redists_size,
|
||||
gits_addr: config.msi_addr,
|
||||
gits_size: config.msi_size,
|
||||
vcpu_count: config.vcpu_count,
|
||||
};
|
||||
Ok(gic_device)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vgic for MshvGicV2M {
|
||||
fn fdt_compatibility(&self) -> &str {
|
||||
"arm,gic-v3"
|
||||
}
|
||||
|
||||
fn msi_compatible(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn msi_compatibility(&self) -> &str {
|
||||
"arm,gic-v2m-frame"
|
||||
}
|
||||
|
||||
fn fdt_maint_irq(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u64 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn msi_properties(&self) -> [u64; 2] {
|
||||
[self.gits_addr, self.gits_size]
|
||||
}
|
||||
|
||||
fn device_properties(&self) -> [u64; 4] {
|
||||
[
|
||||
self.dist_addr,
|
||||
self.dist_size,
|
||||
self.redists_addr,
|
||||
self.redists_size,
|
||||
]
|
||||
}
|
||||
|
||||
fn set_gicr_typers(&mut self, _vcpu_states: &[CpuState]) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn state(&self) -> Result<GicState> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn set_state(&mut self, _state: &GicState) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn save_data_tables(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
24
hypervisor/src/mshv/aarch64/mod.rs
Normal file
24
hypervisor/src/mshv/aarch64/mod.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
//
|
||||
// Copyright © 2025, Microsoft Corporation
|
||||
//
|
||||
pub mod emulator;
|
||||
pub mod gic;
|
||||
use std::fmt;
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||
///
|
||||
pub use mshv_bindings::StandardRegisters as MshvStandardRegisters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct VcpuMshvState {
|
||||
pub regs: MshvStandardRegisters,
|
||||
}
|
||||
|
||||
impl fmt::Display for VcpuMshvState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Standard registers: {:?}", self.regs)
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,24 @@ use std::sync::{Arc, RwLock};
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use arc_swap::ArcSwap;
|
||||
use mshv_bindings::*;
|
||||
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use mshv_ioctls::InterruptRequest;
|
||||
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
|
||||
use vfio_ioctls::VfioDeviceFd;
|
||||
use vm::DataMatch;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::regs::{
|
||||
AARCH64_ARCH_TIMER_VIRT_IRQ, AARCH64_MIN_PPI_IRQ, AARCH64_PMU_IRQ,
|
||||
};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::emulator::PlatformEmulator;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::emulator::Emulator;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::mshv::aarch64::emulator;
|
||||
use crate::mshv::emulator::MshvEmulatorContext;
|
||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||
use crate::{cpu, hypervisor, vec_with_array_field, HypervisorType};
|
||||
@@ -29,10 +38,19 @@ mod snp_constants;
|
||||
// x86_64 dependencies
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86_64;
|
||||
// aarch64 dependencies
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod aarch64;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use aarch64::gic::{MshvGicV2M, BASE_SPI_IRQ};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub use aarch64::VcpuMshvState;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
@@ -50,6 +68,10 @@ pub use {
|
||||
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls, mshv_ioctls::DeviceFd,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::regs;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -191,6 +213,44 @@ impl From<IrqRoutingEntry> for mshv_user_irq_entry {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
impl From<mshv_bindings::MshvRegList> for crate::RegList {
|
||||
fn from(s: mshv_bindings::MshvRegList) -> Self {
|
||||
crate::RegList::Mshv(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
impl From<crate::RegList> for mshv_bindings::MshvRegList {
|
||||
fn from(e: crate::RegList) -> Self {
|
||||
match e {
|
||||
crate::RegList::Mshv(e) => e,
|
||||
/* Needed in case other hypervisors are enabled */
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("RegList is not valid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
impl From<mshv_bindings::MshvVcpuInit> for crate::VcpuInit {
|
||||
fn from(s: mshv_bindings::MshvVcpuInit) -> Self {
|
||||
crate::VcpuInit::Mshv(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
impl From<crate::VcpuInit> for mshv_bindings::MshvVcpuInit {
|
||||
fn from(e: crate::VcpuInit) -> Self {
|
||||
match e {
|
||||
crate::VcpuInit::Mshv(e) => e,
|
||||
/* Needed in case other hypervisors are enabled */
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("VcpuInit is not valid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MshvDirtyLogSlot {
|
||||
guest_pfn: u64,
|
||||
memory_size: u64,
|
||||
@@ -206,7 +266,7 @@ impl MshvHypervisor {
|
||||
///
|
||||
/// Retrieve the list of MSRs supported by MSHV.
|
||||
///
|
||||
fn get_msr_list(&self) -> hypervisor::Result<MsrList> {
|
||||
fn get_msr_list(&self) -> hypervisor::Result<Vec<u32>> {
|
||||
self.mshv
|
||||
.get_msr_index_list()
|
||||
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
|
||||
@@ -239,67 +299,18 @@ impl MshvHypervisor {
|
||||
break;
|
||||
}
|
||||
|
||||
// Set additional partition property for SEV-SNP partition.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
if mshv_vm_type == VmType::Snp {
|
||||
let snp_policy = snp::get_default_snp_guest_policy();
|
||||
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
|
||||
// SAFETY: access union fields
|
||||
unsafe {
|
||||
debug!(
|
||||
"Setting the partition isolation policy as: 0x{:x}",
|
||||
snp_policy.as_uint64
|
||||
);
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_ISOLATION_POLICY,
|
||||
snp_policy.as_uint64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
debug!(
|
||||
"Setting the partition property to enable VMGEXIT offloads as : 0x{:x}",
|
||||
vmgexit_offloads.as_uint64
|
||||
);
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_SEV_VMGEXIT_OFFLOADS,
|
||||
vmgexit_offloads.as_uint64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
|
||||
// send a fault to the guest if it tries to access it. It is possible
|
||||
// to override this behavior with a more suitable option i.e., ignore
|
||||
// writes from the guest and return zero in attempt to read unimplemented
|
||||
// MSR.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
|
||||
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
|
||||
// Always create a frozen partition
|
||||
fd.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
|
||||
1u64,
|
||||
)
|
||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||
|
||||
let vm_fd = Arc::new(fd);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
let msr_list = self.get_msr_list()?;
|
||||
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
||||
let mut msrs: Vec<MsrEntry> = vec![
|
||||
MsrEntry {
|
||||
..Default::default()
|
||||
};
|
||||
num_msrs
|
||||
msr_list.len()
|
||||
];
|
||||
let indices = msr_list.as_slice();
|
||||
for (pos, index) in indices.iter().enumerate() {
|
||||
for (pos, index) in msr_list.iter().enumerate() {
|
||||
msrs[pos].index = *index;
|
||||
}
|
||||
|
||||
@@ -443,6 +454,23 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
fn get_guest_debug_hw_bps(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
///
|
||||
/// Retrieve AArch64 host maximum IPA size supported by MSHV.
|
||||
///
|
||||
fn get_host_ipa_limit(&self) -> i32 {
|
||||
let host_ipa = self.mshv.get_host_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_PHYSICAL_ADDRESS_WIDTH,
|
||||
);
|
||||
|
||||
match host_ipa {
|
||||
Ok(ipa) => ipa.try_into().unwrap(),
|
||||
Err(e) => {
|
||||
panic!("Failed to get host IPA limit: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
@@ -460,6 +488,7 @@ unsafe impl Send for Ghcb {}
|
||||
unsafe impl Sync for Ghcb {}
|
||||
|
||||
/// Vcpu struct for Microsoft Hypervisor
|
||||
#[allow(dead_code)]
|
||||
pub struct MshvVcpu {
|
||||
fd: VcpuFd,
|
||||
vp_index: u8,
|
||||
@@ -491,11 +520,9 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Returns StandardRegisters with default value set
|
||||
///
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn create_standard_regs(&self) -> crate::StandardRegisters {
|
||||
mshv_bindings::StandardRegisters::default().into()
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Returns the vCPU general purpose registers.
|
||||
///
|
||||
@@ -507,7 +534,6 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.into())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the vCPU general purpose registers.
|
||||
///
|
||||
@@ -648,18 +674,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
*/
|
||||
match port {
|
||||
0x402 | 0x510 | 0x511 | 0x514 => {
|
||||
let insn_len = info.header.instruction_length() as u64;
|
||||
|
||||
/* Advance RIP and update RAX */
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_register_name_HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
self.advance_rip_update_rax(&info, ret_rax)?;
|
||||
return Ok(cpu::VmExit::Ignore);
|
||||
}
|
||||
_ => {}
|
||||
@@ -697,18 +712,39 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
ret_rax = eax as u64;
|
||||
}
|
||||
|
||||
let insn_len = info.header.instruction_length() as u64;
|
||||
self.advance_rip_update_rax(&info, ret_rax)?;
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
hv_message_type_HVMSG_UNMAPPED_GPA => {
|
||||
let info = x.to_memory_info().unwrap();
|
||||
let gva = info.guest_virtual_address;
|
||||
let gpa = info.guest_physical_address;
|
||||
|
||||
debug!("Unmapped GPA exit: GVA {:x} GPA {:x}", gva, gpa);
|
||||
|
||||
let context = MshvEmulatorContext {
|
||||
vcpu: self,
|
||||
map: (gva, gpa),
|
||||
syndrome: info.syndrome,
|
||||
instruction_bytes: info.instruction_bytes,
|
||||
instruction_byte_count: info.instruction_byte_count,
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
interruption_pending: unsafe {
|
||||
info.header
|
||||
.execution_state
|
||||
.__bindgen_anon_1
|
||||
.interruption_pending()
|
||||
!= 0
|
||||
},
|
||||
pc: info.header.pc,
|
||||
};
|
||||
|
||||
let mut emulator = emulator::Emulator::new(context);
|
||||
emulator
|
||||
.emulate()
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
|
||||
/* Advance RIP and update RAX */
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_register_name_HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -1029,8 +1065,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"SVM_EXITCODE_HV_DOORBELL_PAGE: Unhandled exit code: {:0x}",
|
||||
exit_info1
|
||||
"SVM_EXITCODE_HV_DOORBELL_PAGE: Unhandled exit code: {exit_info1:0x}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1190,13 +1225,12 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
_ => panic!(
|
||||
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
|
||||
exit_code
|
||||
),
|
||||
_ => {
|
||||
panic!("GHCB_INFO_NORMAL: Unhandled exit code: {exit_code:0x}")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => panic!("Unsupported VMGEXIT operation: {:0x}", ghcb_op),
|
||||
_ => panic!("Unsupported VMGEXIT operation: {ghcb_op:0x}"),
|
||||
}
|
||||
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
@@ -1218,43 +1252,86 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn init_pmu(&self, irq: u32) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
fn init_pmu(&self, _irq: u32) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn has_pmu_support(&self) -> bool {
|
||||
unimplemented!()
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
let arr_reg_name_value = [(
|
||||
hv_register_name_HV_ARM64_REGISTER_PSTATE,
|
||||
regs::PSTATE_FAULT_BITS_64,
|
||||
)];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
|
||||
if cpu_id == 0 {
|
||||
let arr_reg_name_value = [
|
||||
(hv_register_name_HV_ARM64_REGISTER_PC, boot_ip),
|
||||
(hv_register_name_HV_ARM64_REGISTER_X0, fdt_start),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_sys_reg(&self, sys_reg: u32) -> cpu::Result<u64> {
|
||||
let mshv_reg = self.sys_reg_to_mshv_reg(sys_reg)?;
|
||||
|
||||
let mut reg_assocs = [hv_register_assoc {
|
||||
name: mshv_reg,
|
||||
..Default::default()
|
||||
}];
|
||||
self.fd
|
||||
.get_reg(&mut reg_assocs)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetRegister(e.into()))?;
|
||||
|
||||
// SAFETY: Accessing a union element from bindgen generated definition.
|
||||
let res = unsafe { reg_assocs[0].value.reg64 };
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_reg_list(&self, _reg_list: &mut crate::RegList) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_reg_list(&self, reg_list: &mut RegList) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
fn vcpu_init(&self, _kvi: &crate::VcpuInit) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn vcpu_init(&self, kvi: &VcpuInit) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
fn vcpu_finalize(&self, _feature: i32) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
fn vcpu_get_finalized_features(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_regs(&self) -> cpu::Result<StandardRegisters> {
|
||||
unimplemented!()
|
||||
fn vcpu_set_processor_features(
|
||||
&self,
|
||||
_vm: &Arc<dyn crate::Vm>,
|
||||
_kvi: &mut crate::VcpuInit,
|
||||
_id: u8,
|
||||
) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_vcpu_init(&self) -> crate::VcpuInit {
|
||||
MshvVcpuInit {}.into()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -1364,7 +1441,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// Set CPU state for aarch64 guest.
|
||||
///
|
||||
fn set_state(&self, state: &CpuState) -> cpu::Result<()> {
|
||||
fn set_state(&self, _state: &CpuState) -> cpu::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -1481,6 +1558,24 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.request_virtual_interrupt(&cfg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::Nmi(e.into()))
|
||||
}
|
||||
///
|
||||
/// Set the GICR base address for the vcpu.
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn set_gic_redistributor_addr(&self, gicr_base_addr: u64) -> cpu::Result<()> {
|
||||
debug!(
|
||||
"Setting GICR base address to: {:#x}, for vp_index: {:?}",
|
||||
gicr_base_addr, self.vp_index
|
||||
);
|
||||
let arr_reg_name_value = [(
|
||||
hv_register_name_HV_ARM64_REGISTER_GICR_BASE_GPA,
|
||||
gicr_base_addr,
|
||||
)];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MshvVcpu {
|
||||
@@ -1598,6 +1693,50 @@ impl MshvVcpu {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn advance_rip_update_rax(
|
||||
&self,
|
||||
info: &hv_x64_io_port_intercept_message,
|
||||
ret_rax: u64,
|
||||
) -> cpu::Result<()> {
|
||||
let insn_len = info.header.instruction_length() as u64;
|
||||
/*
|
||||
* Advance RIP and update RAX
|
||||
* First, try to update the registers using VP register page
|
||||
* which is mapped into user space for faster access.
|
||||
* If the register page is not available, fall back to regular
|
||||
* IOCTL to update the registers.
|
||||
*/
|
||||
if let Some(reg_page) = self.fd.get_vp_reg_page() {
|
||||
let vp_reg_page = reg_page.0;
|
||||
set_gp_regs_field_ptr!(vp_reg_page, rax, ret_rax);
|
||||
// SAFETY: access union fields
|
||||
unsafe {
|
||||
(*vp_reg_page).__bindgen_anon_1.__bindgen_anon_1.rip = info.header.rip + insn_len;
|
||||
(*vp_reg_page).dirty |= 1 << HV_X64_REGISTER_CLASS_IP;
|
||||
}
|
||||
} else {
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_register_name_HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn sys_reg_to_mshv_reg(&self, sys_regs: u32) -> cpu::Result<u32> {
|
||||
match sys_regs {
|
||||
regs::MPIDR_EL1 => Ok(hv_register_name_HV_ARM64_REGISTER_MPIDR_EL1),
|
||||
_ => Err(cpu::HypervisorCpuError::UnsupportedSysReg(sys_regs)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper over Mshv VM ioctls.
|
||||
@@ -1720,7 +1859,7 @@ impl vm::Vm for MshvVm {
|
||||
MSHV_VP_MMAP_OFFSET_GHCB as i64 * libc::sysconf(libc::_SC_PAGE_SIZE),
|
||||
)
|
||||
};
|
||||
if addr == libc::MAP_FAILED {
|
||||
if std::ptr::eq(addr, libc::MAP_FAILED) {
|
||||
// No point of continuing, without this mmap VMGEXIT will fail anyway
|
||||
// Return error
|
||||
return Err(vm::HypervisorVmError::MmapToRoot);
|
||||
@@ -1868,7 +2007,7 @@ impl vm::Vm for MshvVm {
|
||||
|
||||
fn create_passthrough_device(&self) -> vm::Result<VfioDeviceFd> {
|
||||
let mut vfio_dev = mshv_create_device {
|
||||
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
|
||||
type_: MSHV_DEV_TYPE_VFIO,
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
@@ -1889,9 +2028,20 @@ impl vm::Vm for MshvVm {
|
||||
data: cfg.data,
|
||||
}
|
||||
.into(),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
InterruptSourceConfig::LegacyIrq(cfg) => mshv_user_irq_entry {
|
||||
gsi,
|
||||
// In order to get IRQ line we need to add `BASE_SPI_IRQ` to the pin number
|
||||
// as `BASE_SPI_IRQ` is the base SPI interrupt number exposed via FDT to the
|
||||
// guest.
|
||||
data: cfg.pin + BASE_SPI_IRQ,
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2091,12 +2241,36 @@ impl vm::Vm for MshvVm {
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_vgic(&self, config: VgicConfig) -> vm::Result<Arc<Mutex<dyn Vgic>>> {
|
||||
unimplemented!()
|
||||
let gic_device = MshvGicV2M::new(self, config)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVgic(anyhow!("Vgic error {:?}", e)))?;
|
||||
|
||||
// Register GICD address with the hypervisor
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_GICD_BASE_ADDRESS,
|
||||
gic_device.dist_addr,
|
||||
)
|
||||
.map_err(|e| {
|
||||
vm::HypervisorVmError::CreateVgic(anyhow!("Failed to set GICD address: {}", e))
|
||||
})?;
|
||||
|
||||
// Register GITS address with the hypervisor
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
// spellchecker:disable-line
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_GITS_TRANSLATER_BASE_ADDRESS,
|
||||
gic_device.gits_addr,
|
||||
)
|
||||
.map_err(|e| {
|
||||
vm::HypervisorVmError::CreateVgic(anyhow!("Failed to set GITS address: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(Arc::new(Mutex::new(gic_device)))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
|
||||
unimplemented!()
|
||||
fn get_preferred_target(&self, _kvi: &mut crate::VcpuInit) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pause the VM
|
||||
@@ -2196,4 +2370,102 @@ impl vm::Vm for MshvVm {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init(&self) -> vm::Result<()> {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_GIC_LPI_INT_ID_BITS,
|
||||
0,
|
||||
)
|
||||
.map_err(|e| {
|
||||
vm::HypervisorVmError::InitializeVm(anyhow!(
|
||||
"Failed to set GIC LPI support: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_GIC_PPI_OVERFLOW_INTERRUPT_FROM_CNTV,
|
||||
(AARCH64_ARCH_TIMER_VIRT_IRQ + AARCH64_MIN_PPI_IRQ) as u64,
|
||||
)
|
||||
.map_err(|e| {
|
||||
vm::HypervisorVmError::InitializeVm(anyhow!(
|
||||
"Failed to set arch timer interrupt ID: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_GIC_PPI_PERFORMANCE_MONITORS_INTERRUPT,
|
||||
(AARCH64_PMU_IRQ + AARCH64_MIN_PPI_IRQ) as u64,
|
||||
)
|
||||
.map_err(|e| {
|
||||
vm::HypervisorVmError::InitializeVm(anyhow!(
|
||||
"Failed to set PMU interrupt ID: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
self.fd
|
||||
.initialize()
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
|
||||
|
||||
// Set additional partition property for SEV-SNP partition.
|
||||
#[cfg(feature = "sev_snp")]
|
||||
if self.sev_snp_enabled {
|
||||
let snp_policy = snp::get_default_snp_guest_policy();
|
||||
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
|
||||
// SAFETY: access union fields
|
||||
unsafe {
|
||||
debug!(
|
||||
"Setting the partition isolation policy as: 0x{:x}",
|
||||
snp_policy.as_uint64
|
||||
);
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_ISOLATION_POLICY,
|
||||
snp_policy.as_uint64,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
|
||||
debug!(
|
||||
"Setting the partition property to enable VMGEXIT offloads as : 0x{:x}",
|
||||
vmgexit_offloads.as_uint64
|
||||
);
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_SEV_VMGEXIT_OFFLOADS,
|
||||
vmgexit_offloads.as_uint64,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
|
||||
}
|
||||
}
|
||||
// Default Microsoft Hypervisor behavior for unimplemented MSR is to
|
||||
// send a fault to the guest if it tries to access it. It is possible
|
||||
// to override this behavior with a more suitable option i.e., ignore
|
||||
// writes from the guest and return zero in attempt to read unimplemented
|
||||
// MSR.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
|
||||
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO
|
||||
as u64,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
|
||||
|
||||
// Always create a frozen partition
|
||||
self.fd
|
||||
.set_partition_property(
|
||||
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
|
||||
1u64,
|
||||
)
|
||||
.map_err(|e| vm::HypervisorVmError::InitializeVm(e.into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,7 @@ impl MshvEmulatorContext<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_memory_flags(
|
||||
&self,
|
||||
gva: u64,
|
||||
data: &mut [u8],
|
||||
flags: u32,
|
||||
) -> Result<(), PlatformError> {
|
||||
fn r(&self, gva: u64, data: &mut [u8], flags: u32) -> Result<(), PlatformError> {
|
||||
let gpa = self.translate(gva, flags)?;
|
||||
debug!(
|
||||
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
||||
@@ -59,17 +54,38 @@ impl MshvEmulatorContext<'_> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform emulation for Hyper-V
|
||||
impl PlatformEmulator for MshvEmulatorContext<'_> {
|
||||
type CpuState = EmulatorCpuState;
|
||||
fn read_memory_flags(
|
||||
&self,
|
||||
gva: u64,
|
||||
data: &mut [u8],
|
||||
flags: u32,
|
||||
) -> Result<(), PlatformError> {
|
||||
let mut len = data.len() as u64;
|
||||
|
||||
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
|
||||
// Compare the page number of the first and last byte. If they are different, this is a
|
||||
// cross-page access.
|
||||
let pg1 = gva >> HV_HYP_PAGE_SHIFT;
|
||||
let pg2 = (gva + len - 1) >> HV_HYP_PAGE_SHIFT;
|
||||
let cross_page = pg1 != pg2;
|
||||
|
||||
if cross_page {
|
||||
// We only handle one page cross-page access
|
||||
assert!(pg1 + 1 == pg2);
|
||||
let n = (gva + len) & HV_HYP_PAGE_MASK as u64;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
self.r(gva, &mut data[..len as usize], flags)?;
|
||||
|
||||
if cross_page {
|
||||
self.r(gva + len, &mut data[len as usize..], flags)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||
fn w(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||
let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
|
||||
debug!(
|
||||
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
||||
@@ -88,6 +104,40 @@ impl PlatformEmulator for MshvEmulatorContext<'_> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform emulation for Hyper-V
|
||||
impl PlatformEmulator for MshvEmulatorContext<'_> {
|
||||
type CpuState = EmulatorCpuState;
|
||||
|
||||
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
|
||||
}
|
||||
|
||||
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||
let mut len = data.len() as u64;
|
||||
|
||||
// Compare the page number of the first and last byte. If they are different, this is a
|
||||
// cross-page access.
|
||||
let pg1 = gva >> HV_HYP_PAGE_SHIFT;
|
||||
let pg2 = (gva + len - 1) >> HV_HYP_PAGE_SHIFT;
|
||||
let cross_page = pg1 != pg2;
|
||||
|
||||
if cross_page {
|
||||
// We only handle one page cross-page access
|
||||
assert!(pg1 + 1 == pg2);
|
||||
let n = (gva + len) & HV_HYP_PAGE_MASK as u64;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
self.w(gva, &data[..len as usize])?;
|
||||
|
||||
if cross_page {
|
||||
self.w(gva + len, &data[len as usize..])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
|
||||
if cpu_id != self.vcpu.vp_index as usize {
|
||||
|
||||
@@ -22,8 +22,6 @@ use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
@@ -257,6 +255,11 @@ pub enum HypervisorVmError {
|
||||
#[cfg(feature = "sev_snp")]
|
||||
#[error("Failed to mmap:")]
|
||||
MmapToRoot,
|
||||
///
|
||||
/// Failed to initialize VM
|
||||
///
|
||||
#[error("Failed to initialize VM: {0}")]
|
||||
InitializeVm(#[source] anyhow::Error),
|
||||
}
|
||||
///
|
||||
/// Result type for returning from a function
|
||||
@@ -351,7 +354,7 @@ pub trait Vm: Send + Sync + Any {
|
||||
fn remove_user_memory_region(&self, user_memory_region: UserMemoryRegion) -> Result<()>;
|
||||
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> Result<()>;
|
||||
fn get_preferred_target(&self, kvi: &mut crate::VcpuInit) -> Result<()>;
|
||||
/// Enable split Irq capability
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_split_irq(&self) -> Result<()>;
|
||||
@@ -419,7 +422,10 @@ pub trait Vm: Send + Sync + Any {
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Initialize the VM
|
||||
fn init(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/// Pause the VM
|
||||
fn pause(&self) -> Result<()> {
|
||||
Ok(())
|
||||
|
||||
41
net_gen/src/ipv6.rs
Normal file
41
net_gen/src/ipv6.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2025 Cloud Hypervisor Authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// bindgen /usr/include/linux/ipv6.h --no-layout-tests --constified-enum '*' --allowlist-type 'sockaddr_in6|in6_ifreq'
|
||||
|
||||
/* automatically generated by rust-bindgen 0.71.1 */
|
||||
|
||||
pub type __u8 = ::std::os::raw::c_uchar;
|
||||
pub type __u16 = ::std::os::raw::c_ushort;
|
||||
pub type __u32 = ::std::os::raw::c_uint;
|
||||
pub type __be16 = __u16;
|
||||
pub type __be32 = __u32;
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct in6_addr {
|
||||
pub in6_u: in6_addr__bindgen_ty_1,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union in6_addr__bindgen_ty_1 {
|
||||
pub u6_addr8: [__u8; 16usize],
|
||||
pub u6_addr16: [__be16; 8usize],
|
||||
pub u6_addr32: [__be32; 4usize],
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_in6 {
|
||||
pub sin6_family: ::std::os::raw::c_ushort,
|
||||
pub sin6_port: __be16,
|
||||
pub sin6_flowinfo: __be32,
|
||||
pub sin6_addr: in6_addr,
|
||||
pub sin6_scope_id: __u32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct in6_ifreq {
|
||||
pub ifr6_addr: in6_addr,
|
||||
pub ifr6_prefixlen: __u32,
|
||||
pub ifr6_ifindex: ::std::os::raw::c_int,
|
||||
}
|
||||
@@ -26,6 +26,9 @@ pub mod if_tun;
|
||||
// --constified-enum '*' --with-derive-default
|
||||
// Name is "inn" to avoid conflicting with "in" keyword.
|
||||
pub mod inn;
|
||||
// generated with bindgen /usr/include/linux/ipv6.h --no-layout-tests --constified-enum '*'
|
||||
// --allowlist-type 'sockaddr_in6|in6_ifreq'
|
||||
pub mod ipv6;
|
||||
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
|
||||
// --constified-enum '*' --with-derive-default
|
||||
pub mod sockios;
|
||||
@@ -35,6 +38,7 @@ pub use if_tun::{
|
||||
};
|
||||
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
|
||||
pub use inn::sockaddr_in;
|
||||
pub use ipv6::{in6_ifreq, sockaddr_in6};
|
||||
|
||||
pub const TUNTAP: ::std::os::raw::c_uint = 84;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.3"
|
||||
getrandom = "0.2.15"
|
||||
getrandom = "0.3.1"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
net_gen = { path = "../net_gen" }
|
||||
@@ -27,4 +27,4 @@ vmm-sys-util = { workspace = true }
|
||||
once_cell = "1.20.2"
|
||||
pnet = "0.35.0"
|
||||
pnet_datalink = "0.35.0"
|
||||
serde_json = "1.0.120"
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc::c_uint;
|
||||
use thiserror::Error;
|
||||
use virtio_bindings::virtio_net::{
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ,
|
||||
VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN,
|
||||
@@ -18,22 +19,29 @@ use vm_virtio::{AccessPlatform, Translatable};
|
||||
|
||||
use crate::{GuestMemoryMmap, Tap};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Read queue failed.
|
||||
GuestMemory(GuestMemoryError),
|
||||
#[error("Read queue failed")]
|
||||
GuestMemory(#[source] GuestMemoryError),
|
||||
/// No control header descriptor
|
||||
#[error("No control header descriptor")]
|
||||
NoControlHeaderDescriptor,
|
||||
/// Missing the data descriptor in the chain.
|
||||
#[error("Missing the data descriptor in the chain")]
|
||||
NoDataDescriptor,
|
||||
/// No status descriptor
|
||||
#[error("No status descriptor")]
|
||||
NoStatusDescriptor,
|
||||
/// Failed adding used index
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
#[error("Failed adding used index")]
|
||||
QueueAddUsed(#[source] virtio_queue::Error),
|
||||
/// Failed creating an iterator over the queue
|
||||
QueueIterator(virtio_queue::Error),
|
||||
#[error("Failed creating an iterator over the queue")]
|
||||
QueueIterator(#[source] virtio_queue::Error),
|
||||
/// Failed enabling notification for the queue
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
#[error("Failed enabling notification for the queue")]
|
||||
QueueEnableNotification(#[source] virtio_queue::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -15,6 +15,7 @@ mod queue_pair;
|
||||
mod tap;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::net::IpAddr;
|
||||
use std::os::raw::c_uint;
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
use std::{io, mem, net};
|
||||
@@ -40,7 +41,7 @@ pub use tap::{Error as TapError, Tap};
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
CreateSocket(IoError),
|
||||
CreateSocket(#[source] IoError),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -76,9 +77,14 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
|
||||
unsafe { mem::transmute(addr_in) }
|
||||
}
|
||||
|
||||
fn create_inet_socket() -> Result<net::UdpSocket> {
|
||||
fn create_inet_socket(addr: IpAddr) -> Result<net::UdpSocket> {
|
||||
let domain = match addr {
|
||||
IpAddr::V4(_) => libc::AF_INET,
|
||||
IpAddr::V6(_) => libc::AF_INET6,
|
||||
};
|
||||
|
||||
// SAFETY: we check the return value.
|
||||
let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
|
||||
let sock = unsafe { libc::socket(domain, libc::SOCK_DGRAM, 0) };
|
||||
if sock < 0 {
|
||||
return Err(Error::CreateSocket(IoError::last_os_error()));
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ impl MacAddr {
|
||||
{
|
||||
let v: Vec<&str> = s.as_ref().split(':').collect();
|
||||
let mut bytes = [0u8; MAC_ADDR_LEN];
|
||||
let common_err = Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("parsing of {} into a MAC address failed", s.as_ref()),
|
||||
));
|
||||
let common_err = Err(io::Error::other(format!(
|
||||
"parsing of {} into a MAC address failed",
|
||||
s.as_ref()
|
||||
)));
|
||||
|
||||
if v.len() != MAC_ADDR_LEN {
|
||||
return common_err;
|
||||
@@ -39,10 +39,11 @@ impl MacAddr {
|
||||
return common_err;
|
||||
}
|
||||
bytes[i] = u8::from_str_radix(v[i], 16).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("parsing of {} into a MAC address failed: {}", s.as_ref(), e),
|
||||
)
|
||||
io::Error::other(format!(
|
||||
"parsing of {} into a MAC address failed: {}",
|
||||
s.as_ref(),
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -64,10 +65,11 @@ impl MacAddr {
|
||||
#[inline]
|
||||
pub fn from_bytes(src: &[u8]) -> Result<MacAddr, io::Error> {
|
||||
if src.len() != MAC_ADDR_LEN {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("invalid length of slice: {} vs {}", src.len(), MAC_ADDR_LEN),
|
||||
));
|
||||
return Err(io::Error::other(format!(
|
||||
"invalid length of slice: {} vs {}",
|
||||
src.len(),
|
||||
MAC_ADDR_LEN
|
||||
)));
|
||||
}
|
||||
Ok(MacAddr::from_bytes_unchecked(src))
|
||||
}
|
||||
@@ -80,7 +82,7 @@ impl MacAddr {
|
||||
pub fn local_random() -> MacAddr {
|
||||
// Generate a fully random MAC
|
||||
let mut random_bytes = [0u8; MAC_ADDR_LEN];
|
||||
if let Err(e) = getrandom::getrandom(&mut random_bytes) {
|
||||
if let Err(e) = getrandom::fill(&mut random_bytes) {
|
||||
error!(
|
||||
"Error populating MAC address with random data: {}",
|
||||
e.to_string()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::IpAddr;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
@@ -13,29 +13,27 @@ use super::{vnet_hdr_len, MacAddr, Tap, TapError};
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Failed to convert an hexadecimal string into an integer: {0}")]
|
||||
ConvertHexStringToInt(std::num::ParseIntError),
|
||||
ConvertHexStringToInt(#[source] std::num::ParseIntError),
|
||||
#[error("Error related to the multiqueue support (no support TAP side)")]
|
||||
MultiQueueNoTapSupport,
|
||||
#[error("Error related to the multiqueue support (no support device side)")]
|
||||
MultiQueueNoDeviceSupport,
|
||||
#[error("Failed to read the TAP flags from sysfs: {0}")]
|
||||
ReadSysfsTunFlags(io::Error),
|
||||
ReadSysfsTunFlags(#[source] io::Error),
|
||||
#[error("Open tap device failed: {0}")]
|
||||
TapOpen(TapError),
|
||||
#[error("Setting tap IP failed: {0}")]
|
||||
TapSetIp(TapError),
|
||||
#[error("Setting tap netmask failed: {0}")]
|
||||
TapSetNetmask(TapError),
|
||||
TapOpen(#[source] TapError),
|
||||
#[error("Setting tap IP and/or netmask failed: {0}")]
|
||||
TapSetIpNetmask(#[source] TapError),
|
||||
#[error("Setting MAC address failed: {0}")]
|
||||
TapSetMac(TapError),
|
||||
TapSetMac(#[source] TapError),
|
||||
#[error("Getting MAC address failed: {0}")]
|
||||
TapGetMac(TapError),
|
||||
TapGetMac(#[source] TapError),
|
||||
#[error("Setting vnet header size failed: {0}")]
|
||||
TapSetVnetHdrSize(TapError),
|
||||
TapSetVnetHdrSize(#[source] TapError),
|
||||
#[error("Setting MTU failed: {0}")]
|
||||
TapSetMtu(TapError),
|
||||
TapSetMtu(#[source] TapError),
|
||||
#[error("Enabling tap interface failed: {0}")]
|
||||
TapEnable(TapError),
|
||||
TapEnable(#[source] TapError),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -64,8 +62,8 @@ fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> {
|
||||
/// netmask.
|
||||
pub fn open_tap(
|
||||
if_name: Option<&str>,
|
||||
ip_addr: Option<Ipv4Addr>,
|
||||
netmask: Option<Ipv4Addr>,
|
||||
ip_addr: Option<IpAddr>,
|
||||
netmask: Option<IpAddr>,
|
||||
host_mac: &mut Option<MacAddr>,
|
||||
mtu: Option<u16>,
|
||||
num_rx_q: usize,
|
||||
@@ -94,10 +92,8 @@ pub fn open_tap(
|
||||
// Don't overwrite ip configuration of existing interfaces:
|
||||
if !tap_existed {
|
||||
if let Some(ip) = ip_addr {
|
||||
tap.set_ip_addr(ip).map_err(Error::TapSetIp)?;
|
||||
}
|
||||
if let Some(mask) = netmask {
|
||||
tap.set_netmask(mask).map_err(Error::TapSetNetmask)?;
|
||||
tap.set_ip_addr(ip, netmask)
|
||||
.map_err(Error::TapSetIpNetmask)?;
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
|
||||
@@ -354,27 +354,27 @@ pub enum NetQueuePairError {
|
||||
#[error("No memory configured")]
|
||||
NoMemoryConfigured,
|
||||
#[error("Error registering listener: {0}")]
|
||||
RegisterListener(io::Error),
|
||||
RegisterListener(#[source] io::Error),
|
||||
#[error("Error unregistering listener: {0}")]
|
||||
UnregisterListener(io::Error),
|
||||
UnregisterListener(#[source] io::Error),
|
||||
#[error("Error writing to the TAP device: {0}")]
|
||||
WriteTap(io::Error),
|
||||
WriteTap(#[source] io::Error),
|
||||
#[error("Error reading from the TAP device: {0}")]
|
||||
ReadTap(io::Error),
|
||||
ReadTap(#[source] io::Error),
|
||||
#[error("Error related to guest memory: {0}")]
|
||||
GuestMemory(vm_memory::GuestMemoryError),
|
||||
GuestMemory(#[source] vm_memory::GuestMemoryError),
|
||||
#[error("Returned an error while iterating through the queue: {0}")]
|
||||
QueueIteratorFailed(virtio_queue::Error),
|
||||
QueueIteratorFailed(#[source] virtio_queue::Error),
|
||||
#[error("Descriptor chain is too short")]
|
||||
DescriptorChainTooShort,
|
||||
#[error("Descriptor chain does not contain valid descriptors")]
|
||||
DescriptorChainInvalid,
|
||||
#[error("Failed to determine if queue needed notification: {0}")]
|
||||
QueueNeedsNotification(virtio_queue::Error),
|
||||
QueueNeedsNotification(#[source] virtio_queue::Error),
|
||||
#[error("Failed to enable notification on the queue: {0}")]
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
QueueEnableNotification(#[source] virtio_queue::Error),
|
||||
#[error("Failed to add used index to the queue: {0}")]
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
QueueAddUsed(#[source] virtio_queue::Error),
|
||||
#[error("Descriptor with invalid virtio-net header")]
|
||||
DescriptorInvalidHeader,
|
||||
#[error("Invalid virtio-net header")]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Error as IoError, Read, Result as IoResult, Write};
|
||||
use std::net;
|
||||
use std::net::{IpAddr, Ipv6Addr};
|
||||
use std::os::raw::*;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
|
||||
@@ -23,21 +23,23 @@ use crate::mac::MAC_ADDR_LEN;
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Couldn't open /dev/net/tun: {0}")]
|
||||
OpenTun(IoError),
|
||||
OpenTun(#[source] IoError),
|
||||
#[error("Unable to configure tap interface: {0}")]
|
||||
ConfigureTap(IoError),
|
||||
ConfigureTap(#[source] IoError),
|
||||
#[error("Unable to retrieve features: {0}")]
|
||||
GetFeatures(IoError),
|
||||
GetFeatures(#[source] IoError),
|
||||
#[error("Missing multiqueue support in the kernel")]
|
||||
MultiQueueKernelSupport,
|
||||
#[error("ioctl ({0}) failed: {1}")]
|
||||
IoctlError(c_ulong, IoError),
|
||||
IoctlError(c_ulong, #[source] IoError),
|
||||
#[error("Failed to create a socket: {0}")]
|
||||
NetUtil(NetUtilError),
|
||||
NetUtil(#[source] NetUtilError),
|
||||
#[error("Invalid interface name")]
|
||||
InvalidIfname,
|
||||
#[error("Error parsing MAC data: {0}")]
|
||||
MacParsing(IoError),
|
||||
MacParsing(#[source] IoError),
|
||||
#[error("Invalid netmask")]
|
||||
InvalidNetmask,
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
@@ -88,6 +90,37 @@ fn build_terminated_if_name(if_name: &str) -> Result<Vec<u8>> {
|
||||
Ok(terminated_if_name)
|
||||
}
|
||||
|
||||
fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8> {
|
||||
let mask = mask.segments();
|
||||
let mut iter = mask.iter();
|
||||
|
||||
let mut prefix = 0;
|
||||
for &segment in &mut iter {
|
||||
if segment == 0xffff {
|
||||
prefix += 16;
|
||||
} else if segment == 0 {
|
||||
break;
|
||||
} else {
|
||||
let prefix_bits = segment.leading_ones() as u8;
|
||||
if segment << prefix_bits != 0 {
|
||||
return Err(Error::InvalidNetmask);
|
||||
}
|
||||
|
||||
prefix += prefix_bits;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that remaining bits are all unset
|
||||
for &segment in iter {
|
||||
if segment != 0 {
|
||||
return Err(Error::InvalidNetmask);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(prefix)
|
||||
}
|
||||
|
||||
impl Tap {
|
||||
unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> Result<()> {
|
||||
let ret = ioctl_with_mut_ref(fd, req, arg);
|
||||
@@ -235,16 +268,78 @@ impl Tap {
|
||||
}
|
||||
|
||||
/// Set the host-side IP address for the tap interface.
|
||||
pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
|
||||
let sock = create_inet_socket().map_err(Error::NetUtil)?;
|
||||
let addr = create_sockaddr(ip_addr);
|
||||
pub fn set_ip_addr(&self, ip_addr: IpAddr, netmask: Option<IpAddr>) -> Result<()> {
|
||||
let sock = create_inet_socket(ip_addr).map_err(Error::NetUtil)?;
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
||||
ifreq.ifr_ifru.ifru_addr = addr;
|
||||
match ip_addr {
|
||||
IpAddr::V4(addr) => {
|
||||
let addr = create_sockaddr(addr);
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq) }
|
||||
ifreq.ifr_ifru.ifru_addr = addr;
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe {
|
||||
Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)?;
|
||||
}
|
||||
|
||||
if let Some(IpAddr::V4(mask)) = netmask {
|
||||
ifreq.ifr_ifru.ifru_netmask = create_sockaddr(mask);
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe {
|
||||
Self::ioctl_with_ref(
|
||||
&sock,
|
||||
net_gen::sockios::SIOCSIFNETMASK as c_ulong,
|
||||
&ifreq,
|
||||
)?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
IpAddr::V6(addr) => {
|
||||
let ifindex = {
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe {
|
||||
Self::ioctl_with_ref(
|
||||
&sock,
|
||||
net_gen::sockios::SIOCGIFINDEX as c_ulong,
|
||||
&ifreq,
|
||||
)?;
|
||||
}
|
||||
|
||||
// SAFETY: ifru_ivalue contains the ifindex and is set by the previous ioctl
|
||||
unsafe {
|
||||
match ifreq.ifr_ifru.ifru_ivalue {
|
||||
0 => return Err(Error::InvalidIfname),
|
||||
i => i,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let prefixlen = match netmask {
|
||||
Some(IpAddr::V6(netmask)) => ipv6_mask_to_prefix(netmask)?,
|
||||
Some(IpAddr::V4(_)) => return Err(Error::InvalidNetmask),
|
||||
None => 0,
|
||||
};
|
||||
|
||||
let ifreq = net_gen::in6_ifreq {
|
||||
// SAFETY: addr can be safely transmuted to in6_addr
|
||||
ifr6_addr: unsafe {
|
||||
std::mem::transmute::<[u8; 16], net_gen::ipv6::in6_addr>(addr.octets())
|
||||
},
|
||||
ifr6_prefixlen: prefixlen as u32,
|
||||
ifr6_ifindex: ifindex,
|
||||
};
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe {
|
||||
Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFADDR as c_ulong, &ifreq)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set mac addr for tap interface.
|
||||
@@ -294,19 +389,6 @@ impl Tap {
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
/// Set the netmask for the subnet that the tap interface will exist on.
|
||||
pub fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
|
||||
let sock = create_inet_socket().map_err(Error::NetUtil)?;
|
||||
let addr = create_sockaddr(netmask);
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
||||
ifreq.ifr_ifru.ifru_addr = addr;
|
||||
|
||||
// SAFETY: ioctl is safe. Called with a valid sock fd, and we check the return.
|
||||
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFNETMASK as c_ulong, &ifreq) }
|
||||
}
|
||||
|
||||
#[cfg(not(fuzzing))]
|
||||
pub fn mtu(&self) -> Result<i32> {
|
||||
let sock = create_unix_socket().map_err(Error::NetUtil)?;
|
||||
@@ -602,11 +684,22 @@ mod tests {
|
||||
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let tap = Tap::new(1).unwrap();
|
||||
let ip_addr: net::Ipv4Addr = (*tap_ip_guard).parse().unwrap();
|
||||
let netmask: net::Ipv4Addr = SUBNET_MASK.parse().unwrap();
|
||||
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
|
||||
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
|
||||
|
||||
tap.set_ip_addr(ip_addr).unwrap();
|
||||
tap.set_netmask(netmask).unwrap();
|
||||
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tap_configure_ipv6() {
|
||||
let tap_ip6_lock: Mutex<&'static str> = Mutex::new("2001:db8:85a3::8a2e:370:7334");
|
||||
let tap_ip6_guard = tap_ip6_lock.lock().unwrap();
|
||||
|
||||
let tap = Tap::new(1).unwrap();
|
||||
let ip_addr = IpAddr::V6((*tap_ip6_guard).parse().unwrap());
|
||||
let netmask = IpAddr::V6("ffff:ffff::".parse().unwrap());
|
||||
|
||||
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -640,8 +733,9 @@ mod tests {
|
||||
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let mut tap = Tap::new(1).unwrap();
|
||||
tap.set_ip_addr((*tap_ip_guard).parse().unwrap()).unwrap();
|
||||
tap.set_netmask(SUBNET_MASK.parse().unwrap()).unwrap();
|
||||
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
|
||||
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
|
||||
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
|
||||
tap.enable().unwrap();
|
||||
|
||||
// Send a packet to the interface. We expect to be able to receive it on the associated fd.
|
||||
@@ -698,8 +792,9 @@ mod tests {
|
||||
let tap_ip_guard = TAP_IP_LOCK.lock().unwrap();
|
||||
|
||||
let mut tap = Tap::new(1).unwrap();
|
||||
tap.set_ip_addr((*tap_ip_guard).parse().unwrap()).unwrap();
|
||||
tap.set_netmask(SUBNET_MASK.parse().unwrap()).unwrap();
|
||||
let ip_addr = IpAddr::V4((*tap_ip_guard).parse().unwrap());
|
||||
let netmask = IpAddr::V4(SUBNET_MASK.parse().unwrap());
|
||||
tap.set_ip_addr(ip_addr, Some(netmask)).unwrap();
|
||||
tap.enable().unwrap();
|
||||
|
||||
let (mac, _, mut rx) = pnet_get_mac_tx_rx(tap_name_to_string(&tap));
|
||||
|
||||
@@ -3,3 +3,6 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
name = "option_parser"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "2.0.6"
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
//
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OptionParser {
|
||||
options: HashMap<String, OptionParserValue>,
|
||||
@@ -18,26 +19,17 @@ struct OptionParserValue {
|
||||
requires_value: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptionParserError {
|
||||
#[error("unknown option: {0}")]
|
||||
UnknownOption(String),
|
||||
#[error("unknown option: {0}")]
|
||||
InvalidSyntax(String),
|
||||
Conversion(String, String),
|
||||
#[error("unable to convert {1} for {0}")]
|
||||
Conversion(String /* field */, String /* value */),
|
||||
#[error("invalid value: {0}")]
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for OptionParserError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
OptionParserError::UnknownOption(s) => write!(f, "unknown option: {s}"),
|
||||
OptionParserError::InvalidSyntax(s) => write!(f, "invalid syntax:{s}"),
|
||||
OptionParserError::Conversion(field, value) => {
|
||||
write!(f, "unable to convert {value} for {field}")
|
||||
}
|
||||
OptionParserError::InvalidValue(s) => write!(f, "invalid value: {s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
|
||||
|
||||
fn split_commas(s: &str) -> OptionParserResult<Vec<String>> {
|
||||
@@ -163,7 +155,9 @@ impl OptionParser {
|
||||
|
||||
pub struct Toggle(pub bool);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ToggleParseError {
|
||||
#[error("invalid value: {0}")]
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
@@ -184,8 +178,9 @@ impl FromStr for Toggle {
|
||||
|
||||
pub struct ByteSized(pub u64);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ByteSizedParseError {
|
||||
#[error("invalid value: {0}")]
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
@@ -215,7 +210,9 @@ impl FromStr for ByteSized {
|
||||
|
||||
pub struct IntegerList(pub Vec<u64>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum IntegerListParseError {
|
||||
#[error("invalid value: {0}")]
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
@@ -305,11 +302,16 @@ impl TupleValue for Vec<usize> {
|
||||
|
||||
pub struct Tuple<S, T>(pub Vec<(S, T)>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TupleError {
|
||||
#[error("invalid value: {0}")]
|
||||
InvalidValue(String),
|
||||
SplitOutsideBrackets(OptionParserError),
|
||||
InvalidIntegerList(IntegerListParseError),
|
||||
InvalidInteger(ParseIntError),
|
||||
#[error("split outside brackets: {0}")]
|
||||
SplitOutsideBrackets(#[source] OptionParserError),
|
||||
#[error("invalid integer list: {0}")]
|
||||
InvalidIntegerList(#[source] IntegerListParseError),
|
||||
#[error("invalid integer: {0}")]
|
||||
InvalidInteger(#[source] ParseIntError),
|
||||
}
|
||||
|
||||
impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
||||
@@ -347,7 +349,9 @@ impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
||||
#[derive(Default)]
|
||||
pub struct StringList(pub Vec<String>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum StringListParseError {
|
||||
#[error("invalid value: {0}")]
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@ use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use thiserror::Error;
|
||||
use vm_device::{Bus, BusDevice, BusDeviceSync};
|
||||
|
||||
use crate::configuration::{
|
||||
PciBarRegionType, PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
||||
};
|
||||
use crate::device::{DeviceRelocation, Error as PciDeviceError, PciDevice};
|
||||
use crate::device::{BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice};
|
||||
use crate::PciBarConfiguration;
|
||||
|
||||
const VENDOR_ID_INTEL: u16 = 0x8086;
|
||||
@@ -23,21 +24,28 @@ const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
||||
const NUM_DEVICE_IDS: usize = 32;
|
||||
|
||||
/// Errors for device manager.
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PciRootError {
|
||||
/// Could not allocate device address space for the device.
|
||||
AllocateDeviceAddrs(PciDeviceError),
|
||||
#[error("Could not allocate device address space for the device: {0}")]
|
||||
AllocateDeviceAddrs(#[source] PciDeviceError),
|
||||
/// Could not allocate an IRQ number.
|
||||
#[error("Could not allocate an IRQ number")]
|
||||
AllocateIrq,
|
||||
/// Could not add a device to the port io bus.
|
||||
PioInsert(vm_device::BusError),
|
||||
#[error("Could not add a device to the port io bus: {0}")]
|
||||
PioInsert(#[source] vm_device::BusError),
|
||||
/// Could not add a device to the mmio bus.
|
||||
MmioInsert(vm_device::BusError),
|
||||
#[error("Could not add a device to the mmio bus: {0}")]
|
||||
MmioInsert(#[source] vm_device::BusError),
|
||||
/// Could not find an available device slot on the PCI bus.
|
||||
#[error("Could not find an available device slot on the PCI bus")]
|
||||
NoPciDeviceSlotAvailable,
|
||||
/// Invalid PCI device identifier provided.
|
||||
#[error("Invalid PCI device identifier provided")]
|
||||
InvalidPciDeviceSlot(usize),
|
||||
/// Valid PCI device identifier but already used.
|
||||
#[error("Valid PCI device identifier but already used")]
|
||||
AlreadyInUsePciDeviceSlot(usize),
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, PciRootError>;
|
||||
@@ -81,9 +89,11 @@ impl PciDevice for PciRoot {
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.config.write_config_register(reg_idx, offset, data);
|
||||
None
|
||||
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>) {
|
||||
(
|
||||
self.config.write_config_register(reg_idx, offset, data),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
@@ -256,9 +266,11 @@ impl PciConfigIo {
|
||||
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
|
||||
let mut device = d.lock().unwrap();
|
||||
|
||||
// Find out if one of the device's BAR is being reprogrammed, and
|
||||
// reprogram it if needed.
|
||||
if let Some(params) = device.detect_bar_reprogramming(register, data) {
|
||||
// Update the register value
|
||||
let (bar_reprogram, ret) = device.write_config_register(register, offset, data);
|
||||
|
||||
// Move the device's BAR if needed
|
||||
for params in &bar_reprogram {
|
||||
if let Err(e) = pci_bus.device_reloc.move_bar(
|
||||
params.old_base,
|
||||
params.new_base,
|
||||
@@ -273,8 +285,7 @@ impl PciConfigIo {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the register value
|
||||
device.write_config_register(register, offset, data)
|
||||
ret
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -380,9 +391,11 @@ impl PciConfigMmio {
|
||||
if let Some(d) = pci_bus.devices.get(&(device as u32)) {
|
||||
let mut device = d.lock().unwrap();
|
||||
|
||||
// Find out if one of the device's BAR is being reprogrammed, and
|
||||
// reprogram it if needed.
|
||||
if let Some(params) = device.detect_bar_reprogramming(register, data) {
|
||||
// Update the register value
|
||||
let (bar_reprogram, _) = device.write_config_register(register, offset, data);
|
||||
|
||||
// Move the device's BAR if needed
|
||||
for params in &bar_reprogram {
|
||||
if let Err(e) = pci_bus.device_reloc.move_bar(
|
||||
params.old_base,
|
||||
params.new_base,
|
||||
@@ -396,9 +409,6 @@ impl PciConfigMmio {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the register value
|
||||
device.write_config_register(register, offset, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use vm_device::PciBarType;
|
||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable};
|
||||
|
||||
@@ -18,6 +18,8 @@ use crate::{MsixConfig, PciInterruptPin};
|
||||
// The number of 32bit registers in the config space, 4096 bytes.
|
||||
const NUM_CONFIGURATION_REGISTERS: usize = 1024;
|
||||
|
||||
pub(crate) const COMMAND_REG: usize = 1;
|
||||
pub(crate) const COMMAND_REG_MEMORY_SPACE_MASK: u32 = 0x0000_0002;
|
||||
const STATUS_REG: usize = 1;
|
||||
const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
|
||||
const BAR0_REG: usize = 4;
|
||||
@@ -436,6 +438,7 @@ pub struct PciConfiguration {
|
||||
last_capability: Option<(usize, usize)>,
|
||||
msix_cap_reg_idx: Option<usize>,
|
||||
msix_config: Option<Arc<Mutex<MsixConfig>>>,
|
||||
pending_bar_reprogram: Vec<BarReprogrammingParams>,
|
||||
}
|
||||
|
||||
/// See pci_regs.h in kernel
|
||||
@@ -490,60 +493,45 @@ pub struct PciBarConfiguration {
|
||||
prefetchable: PciBarPrefetchable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("address {0} size {1} too big")]
|
||||
BarAddressInvalid(u64, u64),
|
||||
#[error("bar {0} already used")]
|
||||
BarInUse(usize),
|
||||
#[error("64bit bar {0} already used (requires two regs)")]
|
||||
BarInUse64(usize),
|
||||
#[error("bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
|
||||
BarInvalid(usize),
|
||||
#[error("64bitbar {0} invalid, requires two regs, max {max}", max = NUM_BAR_REGS - 1)]
|
||||
BarInvalid64(usize),
|
||||
#[error("bar address {0} not a power of two")]
|
||||
BarSizeInvalid(u64),
|
||||
#[error("empty capabilities are invalid")]
|
||||
CapabilityEmpty,
|
||||
#[error("Invalid capability length {0}")]
|
||||
CapabilityLengthInvalid(usize),
|
||||
#[error("capability of size {0} doesn't fit")]
|
||||
CapabilitySpaceFull(usize),
|
||||
#[error("failed to decode 32 bits BAR size")]
|
||||
Decode32BarSize,
|
||||
#[error("failed to decode 64 bits BAR size")]
|
||||
Decode64BarSize,
|
||||
#[error("failed to encode 32 bits BAR size")]
|
||||
Encode32BarSize,
|
||||
#[error("failed to encode 64 bits BAR size")]
|
||||
Encode64BarSize,
|
||||
#[error("address {0} size {1} too big")]
|
||||
RomBarAddressInvalid(u64, u64),
|
||||
#[error("rom bar {0} already used")]
|
||||
RomBarInUse(usize),
|
||||
#[error("rom bar {0} invalid, max {max}", max = NUM_BAR_REGS - 1)]
|
||||
RomBarInvalid(usize),
|
||||
#[error("rom bar address {0} not a power of two")]
|
||||
RomBarSizeInvalid(u64),
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
match self {
|
||||
BarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
|
||||
BarInUse(b) => write!(f, "bar {b} already used"),
|
||||
BarInUse64(b) => write!(f, "64bit bar {b} already used(requires two regs)"),
|
||||
BarInvalid(b) => write!(f, "bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
|
||||
BarInvalid64(b) => write!(
|
||||
f,
|
||||
"64bitbar {} invalid, requires two regs, max {}",
|
||||
b,
|
||||
NUM_BAR_REGS - 1
|
||||
),
|
||||
BarSizeInvalid(s) => write!(f, "bar address {s} not a power of two"),
|
||||
CapabilityEmpty => write!(f, "empty capabilities are invalid"),
|
||||
CapabilityLengthInvalid(l) => write!(f, "Invalid capability length {l}"),
|
||||
CapabilitySpaceFull(s) => write!(f, "capability of size {s} doesn't fit"),
|
||||
Decode32BarSize => write!(f, "failed to decode 32 bits BAR size"),
|
||||
Decode64BarSize => write!(f, "failed to decode 64 bits BAR size"),
|
||||
Encode32BarSize => write!(f, "failed to encode 32 bits BAR size"),
|
||||
Encode64BarSize => write!(f, "failed to encode 64 bits BAR size"),
|
||||
RomBarAddressInvalid(a, s) => write!(f, "address {a} size {s} too big"),
|
||||
RomBarInUse(b) => write!(f, "rom bar {b} already used"),
|
||||
RomBarInvalid(b) => write!(f, "rom bar {} invalid, max {}", b, NUM_BAR_REGS - 1),
|
||||
RomBarSizeInvalid(s) => write!(f, "rom bar address {s} not a power of two"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciConfiguration {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
@@ -630,6 +618,7 @@ impl PciConfiguration {
|
||||
last_capability,
|
||||
msix_cap_reg_idx,
|
||||
msix_config,
|
||||
pending_bar_reprogram: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,9 +896,14 @@ impl PciConfiguration {
|
||||
(next + 3) & !3
|
||||
}
|
||||
|
||||
pub fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
|
||||
pub fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Vec<BarReprogrammingParams> {
|
||||
if offset as usize + data.len() > 4 {
|
||||
return;
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// Handle potential write to MSI-X message control register
|
||||
@@ -938,13 +932,37 @@ impl PciConfiguration {
|
||||
4 => self.write_reg(reg_idx, LittleEndian::read_u32(data)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if let Some(param) = self.detect_bar_reprogramming(reg_idx, data) {
|
||||
self.pending_bar_reprogram.push(param);
|
||||
}
|
||||
|
||||
if !self.pending_bar_reprogram.is_empty() {
|
||||
// Return bar reprogramming only if the MSE bit is enabled;
|
||||
if self.read_config_register(COMMAND_REG) & COMMAND_REG_MEMORY_SPACE_MASK
|
||||
== COMMAND_REG_MEMORY_SPACE_MASK
|
||||
{
|
||||
info!(
|
||||
"BAR reprogramming parameter is returned: {:x?}",
|
||||
self.pending_bar_reprogram
|
||||
);
|
||||
return self.pending_bar_reprogram.drain(..).collect();
|
||||
} else {
|
||||
info!(
|
||||
"MSE bit is disabled. No BAR reprogramming parameter is returned: {:x?}",
|
||||
self.pending_bar_reprogram
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub fn read_config_register(&self, reg_idx: usize) -> u32 {
|
||||
self.read_reg(reg_idx)
|
||||
}
|
||||
|
||||
pub fn detect_bar_reprogramming(
|
||||
fn detect_bar_reprogramming(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
data: &[u8],
|
||||
@@ -981,7 +999,7 @@ impl PciConfiguration {
|
||||
|
||||
info!(
|
||||
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
|
||||
reg_idx, self.registers[reg_idx], value
|
||||
bar_idx, self.bars[bar_idx].addr, value
|
||||
);
|
||||
let old_base = u64::from(self.bars[bar_idx].addr & mask);
|
||||
let new_base = u64::from(value & mask);
|
||||
@@ -1007,7 +1025,7 @@ impl PciConfiguration {
|
||||
{
|
||||
info!(
|
||||
"Detected BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
|
||||
reg_idx, self.registers[reg_idx], value
|
||||
bar_idx, self.bars[bar_idx].addr, value
|
||||
);
|
||||
let old_base = (u64::from(self.bars[bar_idx].addr & mask) << 32)
|
||||
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
|
||||
@@ -1036,8 +1054,8 @@ impl PciConfiguration {
|
||||
}
|
||||
|
||||
info!(
|
||||
"Detected ROM BAR reprogramming: (BAR {}) 0x{:x}->0x{:x}",
|
||||
reg_idx, self.registers[reg_idx], value
|
||||
"Detected ROM BAR reprogramming: (Expansion ROM BAR) 0x{:x}->0x{:x}",
|
||||
self.rom_bar_addr, value
|
||||
);
|
||||
let old_base = u64::from(self.rom_bar_addr & mask);
|
||||
let new_base = u64::from(value & mask);
|
||||
@@ -1060,6 +1078,14 @@ impl PciConfiguration {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn pending_bar_reprogram(&self) -> Vec<BarReprogrammingParams> {
|
||||
self.pending_bar_reprogram.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn clear_pending_bar_reprogram(&mut self) {
|
||||
self.pending_bar_reprogram = Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for PciConfiguration {}
|
||||
|
||||
@@ -5,50 +5,37 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::{io, result};
|
||||
|
||||
use thiserror::Error;
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::Resource;
|
||||
|
||||
use crate::configuration::{self, PciBarRegionType};
|
||||
use crate::PciBarConfiguration;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Setup of the device capabilities failed.
|
||||
CapabilitiesSetup(configuration::Error),
|
||||
#[error("Setup of the device capabilities failed: {0}")]
|
||||
CapabilitiesSetup(#[source] configuration::Error),
|
||||
/// Allocating space for an IO BAR failed.
|
||||
#[error("Allocating space for an IO BAR failed")]
|
||||
IoAllocationFailed(u64),
|
||||
/// Registering an IO BAR failed.
|
||||
IoRegistrationFailed(u64, configuration::Error),
|
||||
#[error("Registering an IO BAR failed: {0}")]
|
||||
IoRegistrationFailed(u64, #[source] configuration::Error),
|
||||
/// Expected resource not found.
|
||||
#[error("Expected resource not found")]
|
||||
MissingResource,
|
||||
/// Invalid resource.
|
||||
#[error("Invalid resource: {0:?}")]
|
||||
InvalidResource(Resource),
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match self {
|
||||
CapabilitiesSetup(e) => write!(f, "failed to add capability {e}"),
|
||||
IoAllocationFailed(size) => {
|
||||
write!(f, "failed to allocate space for an IO BAR, size={size}")
|
||||
}
|
||||
IoRegistrationFailed(addr, e) => {
|
||||
write!(f, "failed to register an IO BAR, addr={addr} err={e}")
|
||||
}
|
||||
MissingResource => write!(f, "failed to find expected resource"),
|
||||
InvalidResource(r) => write!(f, "invalid resource {r:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct BarReprogrammingParams {
|
||||
pub old_base: u64,
|
||||
pub new_base: u64,
|
||||
@@ -87,18 +74,10 @@ pub trait PciDevice: Send {
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>>;
|
||||
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>);
|
||||
/// Gets a register from the configuration space.
|
||||
/// * `reg_idx` - The index of the config register to read.
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32;
|
||||
/// Detects if a BAR is being reprogrammed.
|
||||
fn detect_bar_reprogramming(
|
||||
&mut self,
|
||||
_reg_idx: usize,
|
||||
_data: &[u8],
|
||||
) -> Option<BarReprogrammingParams> {
|
||||
None
|
||||
}
|
||||
/// Reads from a BAR region mapped into the device.
|
||||
/// * `addr` - The guest address inside the BAR.
|
||||
/// * `data` - Filled with the data from `addr`.
|
||||
|
||||
@@ -40,9 +40,9 @@ pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Failed enabling the interrupt route: {0}")]
|
||||
EnableInterruptRoute(io::Error),
|
||||
EnableInterruptRoute(#[source] io::Error),
|
||||
#[error("Failed updating the interrupt route: {0}")]
|
||||
UpdateInterruptRoute(io::Error),
|
||||
UpdateInterruptRoute(#[source] io::Error),
|
||||
}
|
||||
|
||||
pub const MSI_CONFIG_ID: &str = "msi_config";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user