mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
280 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f61c97e2b | ||
|
|
868e566c08 | ||
|
|
0f54fb1f5f | ||
|
|
758ef9604a | ||
|
|
8768a600ff | ||
|
|
5c3dad3efe | ||
|
|
4eb5af3100 | ||
|
|
3e9eff1a9a | ||
|
|
43c4b6f546 | ||
|
|
dc858ed96f | ||
|
|
42c0b4055a | ||
|
|
c6ca964ee6 | ||
|
|
42e8e2b933 | ||
|
|
902adabbbd | ||
|
|
5573faff56 | ||
|
|
7fc0776aac | ||
|
|
4f4eb3f6b8 | ||
|
|
8f4de45937 | ||
|
|
32dd4d10d0 | ||
|
|
27b5f8756f | ||
|
|
15358ef79d | ||
|
|
46672c384c | ||
|
|
a18b818227 | ||
|
|
0a5111b6c3 | ||
|
|
17b0f40154 | ||
|
|
8c88b10384 | ||
|
|
b0d245be70 | ||
|
|
fad29fdf1a | ||
|
|
c9374d87ac | ||
|
|
649e1fa1a6 | ||
|
|
c7725f921c | ||
|
|
82d06c0efa | ||
|
|
bc827ee3be | ||
|
|
ac25172176 | ||
|
|
c7977aa4a6 | ||
|
|
d93b7bb04e | ||
|
|
c4454f54bd | ||
|
|
580db833f0 | ||
|
|
e91409956d | ||
|
|
61ce4b8f31 | ||
|
|
154cca4170 | ||
|
|
6032e30807 | ||
|
|
7b7d720f37 | ||
|
|
d23c1abbf0 | ||
|
|
9b9015d907 | ||
|
|
60b44141b4 | ||
|
|
896a651b5c | ||
|
|
9075809494 | ||
|
|
6b23227e10 | ||
|
|
f68d63ef6c | ||
|
|
1bcb07b68b | ||
|
|
ef1ac5c670 | ||
|
|
5e7c9eaefd | ||
|
|
c4211686ab | ||
|
|
51cfffd24f | ||
|
|
e34c25cf6f | ||
|
|
366f058d61 | ||
|
|
2a312cd4fe | ||
|
|
0d7328da01 | ||
|
|
589dc77354 | ||
|
|
58d984f6b8 | ||
|
|
dd3c086a0a | ||
|
|
46953db3ca | ||
|
|
f855d9e237 | ||
|
|
ff0e92ab88 | ||
|
|
32af6f9723 | ||
|
|
9b3cab8c72 | ||
|
|
84630aa0b5 | ||
|
|
ad8ed80eb1 | ||
|
|
f035cff10f | ||
|
|
1a2b01888c | ||
|
|
94fcfa9f1f | ||
|
|
31b3871eee | ||
|
|
d8becc742c | ||
|
|
d2d6eb0591 | ||
|
|
8ee253cd3f | ||
|
|
6f49cb2860 | ||
|
|
03862314eb | ||
|
|
243b5bad65 | ||
|
|
80bbc47147 | ||
|
|
351a7b52c8 | ||
|
|
8c737793d6 | ||
|
|
419870ae45 | ||
|
|
66a2045148 | ||
|
|
5813ce0307 | ||
|
|
533e47f26b | ||
|
|
6221b6f8a1 | ||
|
|
57cc8bc6fe | ||
|
|
d3526823db | ||
|
|
608ad4894f | ||
|
|
e4dc776ea4 | ||
|
|
1fac67f648 | ||
|
|
592b8bcaaa | ||
|
|
e6e58e6d66 | ||
|
|
7c53896f11 | ||
|
|
a1f1dfddeb | ||
|
|
e2bd35f4bb | ||
|
|
b000b303e2 | ||
|
|
9260e3b273 | ||
|
|
cfdf643237 | ||
|
|
3480e69ff5 | ||
|
|
6a7a588268 | ||
|
|
932c8c9713 | ||
|
|
a4f5ad6076 | ||
|
|
be79a80361 | ||
|
|
34edb8960f | ||
|
|
83af9965fa | ||
|
|
f7e60434fa | ||
|
|
a4d8616676 | ||
|
|
38351f0d96 | ||
|
|
3587adcf5f | ||
|
|
611e71826d | ||
|
|
ed52e5c029 | ||
|
|
c8e3c1eed6 | ||
|
|
ad521fd4e4 | ||
|
|
e4cbafcd01 | ||
|
|
b81d758c41 | ||
|
|
184a3d1fb5 | ||
|
|
d3b2f2a4d9 | ||
|
|
a362e85539 | ||
|
|
9a583e6e9f | ||
|
|
a628854d9c | ||
|
|
7618cd2c2d | ||
|
|
eae4b315e4 | ||
|
|
c1a3b63112 | ||
|
|
751e76db08 | ||
|
|
d96d98d88e | ||
|
|
185f0c1bf3 | ||
|
|
e04cbb2ad4 | ||
|
|
986e43f899 | ||
|
|
a7786b4e0c | ||
|
|
def371e969 | ||
|
|
72d4d2c6f0 | ||
|
|
99467982aa | ||
|
|
6172b6aac5 | ||
|
|
9cddcc8766 | ||
|
|
d2319124f5 | ||
|
|
24fd54401d | ||
|
|
9256c41173 | ||
|
|
b1d4bcf952 | ||
|
|
8b7bfca9b0 | ||
|
|
7da69b59f7 | ||
|
|
50b6ee3947 | ||
|
|
c0ca6b5377 | ||
|
|
8c9f0e407f | ||
|
|
29f46ee470 | ||
|
|
f3e86bdf26 | ||
|
|
311dd7be50 | ||
|
|
bfd3deac7c | ||
|
|
1a35d5a017 | ||
|
|
95e63d5f44 | ||
|
|
f31d218479 | ||
|
|
ef216c37df | ||
|
|
242a7f7ab4 | ||
|
|
eec7634cd7 | ||
|
|
7ad83f1daa | ||
|
|
6c181061df | ||
|
|
99d4f77197 | ||
|
|
3c00a696b5 | ||
|
|
4f496e39a1 | ||
|
|
d92489e0a4 | ||
|
|
93647f0313 | ||
|
|
1877c723cd | ||
|
|
f51bad20bf | ||
|
|
f7da35857f | ||
|
|
5902ceb955 | ||
|
|
395929f1d4 | ||
|
|
4bd0ac6623 | ||
|
|
f917198f27 | ||
|
|
211ee1521d | ||
|
|
d0c3342c97 | ||
|
|
b4f3e1c2a1 | ||
|
|
b11a8a5ab5 | ||
|
|
436f3605e9 | ||
|
|
6cfa794f33 | ||
|
|
cc1db2ea13 | ||
|
|
fbdfc7a426 | ||
|
|
58d25b3ccc | ||
|
|
495d4eb896 | ||
|
|
6859a1694c | ||
|
|
960c7027c7 | ||
|
|
a2e02a8fff | ||
|
|
def98faf37 | ||
|
|
9d1a7e43a7 | ||
|
|
afe95e5a2a | ||
|
|
b8fee11822 | ||
|
|
e20be3e147 | ||
|
|
ec81f377b6 | ||
|
|
438be0dad5 | ||
|
|
1a5a89508b | ||
|
|
bad26133cb | ||
|
|
b881339866 | ||
|
|
0afa619d2c | ||
|
|
ae83e3b383 | ||
|
|
79e43ac534 | ||
|
|
a26ce353d3 | ||
|
|
cd9d1cf8fc | ||
|
|
a6456b50f3 | ||
|
|
7cfeefde57 | ||
|
|
f71f6da907 | ||
|
|
d4f7f42800 | ||
|
|
ca955a47ff | ||
|
|
88378d17a2 | ||
|
|
cf1c2bf0e8 | ||
|
|
e3d6e222a1 | ||
|
|
f8d9c073f0 | ||
|
|
e3c35a3579 | ||
|
|
7a4606f800 | ||
|
|
49f19e061b | ||
|
|
d33d254921 | ||
|
|
b8b0dab1ae | ||
|
|
c118d7d7d3 | ||
|
|
3059ba4305 | ||
|
|
080ce9b068 | ||
|
|
c886d71d29 | ||
|
|
4f5c179b9b | ||
|
|
fbb385834a | ||
|
|
b4fc02857f | ||
|
|
b55f009b8a | ||
|
|
b59f1d90dd | ||
|
|
a7fba8105f | ||
|
|
8b67298ad8 | ||
|
|
21706b02b8 | ||
|
|
bbc300b2cd | ||
|
|
1d095a929c | ||
|
|
5d14541924 | ||
|
|
f17addd36f | ||
|
|
b80df526e4 | ||
|
|
8c1176e33a | ||
|
|
ed5f25446a | ||
|
|
23f63262e7 | ||
|
|
455b0d12e9 | ||
|
|
3c7323230a | ||
|
|
374e1a2eca | ||
|
|
9d8315521c | ||
|
|
bc1e0361c0 | ||
|
|
8d6076653d | ||
|
|
57f063513c | ||
|
|
beb0c0707f | ||
|
|
1c2ab3e771 | ||
|
|
aaf253a2d7 | ||
|
|
bbc223b93f | ||
|
|
b5d5ffa969 | ||
|
|
93dce87cfe | ||
|
|
3dda3669b9 | ||
|
|
4c9a88db87 | ||
|
|
5dcc8cdf39 | ||
|
|
5963002cc5 | ||
|
|
f151a8602c | ||
|
|
0249e8641a | ||
|
|
7c19ae92b8 | ||
|
|
7f0e7d19a6 | ||
|
|
81b9705b10 | ||
|
|
27f7b2c2a1 | ||
|
|
76b6c62d07 | ||
|
|
7aff4decc6 | ||
|
|
bd51f3069c | ||
|
|
c8f108d2f0 | ||
|
|
e3feba287d | ||
|
|
e78c058420 | ||
|
|
70f9fea1c3 | ||
|
|
2f57d1c3f9 | ||
|
|
1311a7f178 | ||
|
|
7d7577007a | ||
|
|
84f0f332b3 | ||
|
|
e9ea9d63f8 | ||
|
|
48d4ccbfeb | ||
|
|
4ae8ee2376 | ||
|
|
8a56720b0f | ||
|
|
a3a0d26959 | ||
|
|
00bfb63607 | ||
|
|
fccd9c8829 | ||
|
|
feed0efc60 | ||
|
|
61e3f0f9e9 | ||
|
|
8df418358e | ||
|
|
2cccdc5ddd | ||
|
|
c3d6aceed1 | ||
|
|
2226207874 | ||
|
|
690bdc3448 | ||
|
|
c25bd447a1 |
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
@@ -34,6 +34,9 @@ jobs:
|
||||
git rev-list origin/main..$GITHUB_SHA | xargs -t -I % sh -c 'git checkout %; cargo check --all --target=${{ matrix.target }}'
|
||||
git checkout $GITHUB_SHA
|
||||
|
||||
- name: Build (default + tdx)
|
||||
run: cargo rustc --bin cloud-hypervisor --features "tdx" -- -D warnings
|
||||
|
||||
- name: Build (acpi,kvm)
|
||||
run: cargo rustc --bin cloud-hypervisor --no-default-features --features "acpi,kvm" -- -D warnings
|
||||
|
||||
|
||||
2
.github/workflows/dco.yaml
vendored
2
.github/workflows/dco.yaml
vendored
@@ -15,4 +15,4 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
pip3 install -U dco-check
|
||||
dco-check
|
||||
dco-check -e "49699333+dependabot[bot]@users.noreply.github.com"
|
||||
|
||||
14
.github/workflows/docker-image.yaml
vendored
14
.github/workflows/docker-image.yaml
vendored
@@ -1,9 +1,11 @@
|
||||
name: Cloud-Hypervisor's Docker image update
|
||||
name: Cloud Hypervisor's Docker image update
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
paths: resources/Dockerfile
|
||||
pull_request:
|
||||
paths: resources/Dockerfile
|
||||
|
||||
jobs:
|
||||
main:
|
||||
@@ -19,12 +21,14 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: ./resources/Dockerfile
|
||||
@@ -32,5 +36,13 @@ jobs:
|
||||
push: true
|
||||
tags: cloudhypervisor/dev:latest
|
||||
|
||||
- name: Build only
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: ./resources/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: cloudhypervisor/dev:latest
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
22
.github/workflows/release.yaml
vendored
22
.github/workflows/release.yaml
vendored
@@ -9,6 +9,8 @@ jobs:
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Create release directory
|
||||
run: rsync -rv --exclude=.git . ../cloud-hypervisor-${{ github.event.ref }}
|
||||
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -37,6 +39,13 @@ jobs:
|
||||
use-cross: true
|
||||
command: build
|
||||
args: --all --release --target=aarch64-unknown-linux-musl
|
||||
- name: Vendor
|
||||
working-directory: ../cloud-hypervisor-${{ github.event.ref }}
|
||||
run: |
|
||||
mkdir ../vendor-cargo-home
|
||||
export CARGO_HOME=$(realpath ../vendor-cargo-home)
|
||||
mkdir .cargo
|
||||
cargo vendor > .cargo/config.toml
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -47,6 +56,19 @@ jobs:
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
- name: Create vendored source archive
|
||||
working-directory: ../
|
||||
run: tar cJf cloud-hypervisor-${{ github.event.ref }}.tar.xz cloud-hypervisor-${{ github.event.ref }}
|
||||
- name: Upload cloud-hypervisor vendored source archive
|
||||
id: upload-release-cloud-hypervisor-vendored-sources
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ../cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||
asset_name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||
asset_content_type: application/x-xz
|
||||
- name: Upload cloud-hypervisor
|
||||
id: upload-release-cloud-hypervisor
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Contributing to Cloud Hypervisor
|
||||
|
||||
Cloud Hypervisor is an open source project licensed under the [Apache v2 License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3 Clause](https://opensource.org/licenses/BSD-3-Clause) license.
|
||||
Cloud Hypervisor is an open source project licensed under the [Apache v2
|
||||
License](https://opensource.org/licenses/Apache-2.0) and the [BSD 3
|
||||
Clause](https://opensource.org/licenses/BSD-3-Clause) license. Contributions
|
||||
can be made under either license or both. Individual files contain details of
|
||||
their licensing and changes to that file are under the same license unless the
|
||||
contribution changes the license of the file. When importing code from a third
|
||||
party project (e.g. Firecracker or CrosVM) please respect the license of those
|
||||
projects.
|
||||
|
||||
## Coding Style
|
||||
|
||||
|
||||
112
Cargo.lock
generated
112
Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@@ -35,18 +35,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.44"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
||||
checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -57,9 +57,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6df5aef5c5830360ce5218cecb8f018af3438af5686ae945094affc86fdec63"
|
||||
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
||||
|
||||
[[package]]
|
||||
name = "arch"
|
||||
@@ -103,9 +103,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.61"
|
||||
version = "0.3.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
|
||||
checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
@@ -144,6 +144,7 @@ dependencies = [
|
||||
"versionize_derive",
|
||||
"vhdx",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
@@ -157,9 +158,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.71"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -169,9 +170,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
@@ -185,7 +186,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "19.0.0"
|
||||
version = "20.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -359,9 +360,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.25.0"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
|
||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
@@ -418,9 +419,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -462,9 +463,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dc14f9047df1873cf6942caccc7431d19c3d496ca7a0d162260c4cf0f64b76"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#d22ef1f51852dfb055da38004e1a4fed81246f81"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -479,9 +479,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
@@ -545,7 +545,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#36e59a083e76a2449e0f58e4283d201bc72fdf13"
|
||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#0a58eb1ece68e326e68365c4297d0a7c08ecd9bc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -564,7 +564,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#99da566389546aedb56fc3d279e01c5dbde79bce"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#8770516859e7d05193c496b77ac5ddad3ca5943a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serde",
|
||||
@@ -576,7 +576,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#99da566389546aedb56fc3d279e01c5dbde79bce"
|
||||
source = "git+https://github.com/rust-vmm/mshv?branch=main#8770516859e7d05193c496b77ac5ddad3ca5943a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -607,6 +607,7 @@ dependencies = [
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
@@ -614,18 +615,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.26.2"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
|
||||
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.67"
|
||||
version = "0.9.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058"
|
||||
checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -687,9 +688,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.20"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
|
||||
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
||||
|
||||
[[package]]
|
||||
name = "pnet"
|
||||
@@ -781,9 +782,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.30"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -881,9 +882,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
@@ -934,9 +935,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.68"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
|
||||
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -994,9 +995,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1148,14 +1149,16 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#d51c1fad37be30c20385c55a217e2d90972ea31d"
|
||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#19e5b83ddfad430109ce3e8d51f4a24796099127"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
"libc",
|
||||
"log",
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"thiserror",
|
||||
"vfio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -1212,6 +1215,7 @@ dependencies = [
|
||||
"log",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
@@ -1285,6 +1289,7 @@ dependencies = [
|
||||
"versionize_derive",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
"vm-memory",
|
||||
@@ -1293,6 +1298,15 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vm-allocator"
|
||||
version = "0.1.0"
|
||||
@@ -1318,8 +1332,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vm-fdt"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#9c2b252dc19f0e87546e4f7577eaf398f8e3c66a"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#9cfa0c8d7c3032f79589c7b01c8722a37f4cf44f"
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
@@ -1352,6 +1366,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
@@ -1393,6 +1408,7 @@ dependencies = [
|
||||
"vfio_user",
|
||||
"vhdx",
|
||||
"virtio-devices",
|
||||
"virtio-queue",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
"vm-memory",
|
||||
@@ -1461,9 +1477,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae0f717764196a220d8c58500e3a3595e2c9054f95d66267f9fd5f6e74ad0fec"
|
||||
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
@@ -1471,9 +1487,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0af017aca1fa6181f5dd7a802456fe6f7666ecdcc18d0910431f0fc89d474e51"
|
||||
checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
|
||||
16
Cargo.toml
16
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "19.0.0"
|
||||
version = "20.2.0"
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2018"
|
||||
default-run = "cloud-hypervisor"
|
||||
@@ -13,17 +13,17 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.44"
|
||||
anyhow = "1.0.51"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
clap = { version = "2.34.0", features = ["wrap_help"] }
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = { version = "0.4.14", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.2.0"
|
||||
serde_json = "1.0.68"
|
||||
serde_json = "1.0.72"
|
||||
signal-hook = "0.3.10"
|
||||
thiserror = "1.0.30"
|
||||
vmm = { path = "vmm" }
|
||||
@@ -31,11 +31,12 @@ vmm-sys-util = "0.9.0"
|
||||
vm-memory = "0.6.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
clap = { version = "2.34.0", features = ["wrap_help"] }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"] }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -43,7 +44,7 @@ credibility = "0.1.3"
|
||||
dirs = "4.0.0"
|
||||
lazy_static= "1.4.0"
|
||||
net_util = { path = "net_util" }
|
||||
serde_json = "1.0.68"
|
||||
serde_json = "1.0.72"
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -83,6 +84,7 @@ members = [
|
||||
"vhost_user_block",
|
||||
"vhost_user_net",
|
||||
"virtio-devices",
|
||||
"virtio-queue",
|
||||
"vmm",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
|
||||
@@ -58,6 +58,12 @@ Cloud Hypervisor supports `64-bit Linux` and Windows 10/Windows Server 2019.
|
||||
|
||||
# 2. Getting Started
|
||||
|
||||
Below sections describe how to build and run Cloud Hypervisor on the `x86_64`
|
||||
platform. For getting started on the `AArch64` platform, please refer to the
|
||||
[Arm64 documentation](docs/arm64.md).
|
||||
|
||||
## Preparation
|
||||
|
||||
We create a folder to build and run `cloud-hypervisor` at `$HOME/cloud-hypervisor`
|
||||
|
||||
```shell
|
||||
|
||||
@@ -6,15 +6,23 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub trait Aml {
|
||||
fn to_aml_bytes(&self) -> Vec<u8>;
|
||||
fn append_aml_bytes(&self, _v: &mut Vec<u8>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
self.append_aml_bytes(&mut v);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
pub const ZERO: Zero = Zero {};
|
||||
pub struct Zero {}
|
||||
|
||||
impl Aml for Zero {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
vec![0u8]
|
||||
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
|
||||
v.push(0u8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +30,8 @@ pub const ONE: One = One {};
|
||||
pub struct One {}
|
||||
|
||||
impl Aml for One {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
vec![1u8]
|
||||
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
|
||||
v.push(1u8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +39,8 @@ pub const ONES: Ones = Ones {};
|
||||
pub struct Ones {}
|
||||
|
||||
impl Aml for Ones {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
vec![0xffu8]
|
||||
fn append_aml_bytes(&self, v: &mut Vec<u8>) {
|
||||
v.push(0xffu8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +50,7 @@ pub struct Path {
|
||||
}
|
||||
|
||||
impl Aml for Path {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
if self.root {
|
||||
bytes.push(b'\\');
|
||||
}
|
||||
@@ -62,10 +68,8 @@ impl Aml for Path {
|
||||
};
|
||||
|
||||
for part in self.name_parts.clone().iter_mut() {
|
||||
bytes.append(&mut part.to_vec());
|
||||
bytes.extend_from_slice(&part.to_vec());
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,40 +98,36 @@ impl From<&str> for Path {
|
||||
pub type Byte = u8;
|
||||
|
||||
impl Aml for Byte {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x0a]; /* BytePrefix */
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x0a); /* BytePrefix */
|
||||
bytes.push(*self);
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
pub type Word = u16;
|
||||
|
||||
impl Aml for Word {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x0bu8]; /* WordPrefix */
|
||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x0b); /* WordPrefix */
|
||||
bytes.extend_from_slice(&self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
pub type DWord = u32;
|
||||
|
||||
impl Aml for DWord {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x0c]; /* DWordPrefix */
|
||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x0c); /* DWordPrefix */
|
||||
bytes.extend_from_slice(&self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
pub type QWord = u64;
|
||||
|
||||
impl Aml for QWord {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x0e]; /* QWordPrefix */
|
||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x0e); /* QWordPrefix */
|
||||
bytes.extend_from_slice(&self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,16 +136,18 @@ pub struct Name {
|
||||
}
|
||||
|
||||
impl Aml for Name {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.clone()
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
// TODO: Refactor this to make more efficient but there are
|
||||
// lifetime/ownership challenges.
|
||||
bytes.extend_from_slice(&self.bytes.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn new(path: Path, inner: &dyn Aml) -> Self {
|
||||
let mut bytes = vec![0x08]; /* NameOp */
|
||||
bytes.append(&mut path.to_aml_bytes());
|
||||
bytes.append(&mut inner.to_aml_bytes());
|
||||
path.append_aml_bytes(&mut bytes);
|
||||
inner.append_aml_bytes(&mut bytes);
|
||||
Name { bytes }
|
||||
}
|
||||
}
|
||||
@@ -155,21 +157,17 @@ pub struct Package<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Package<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![self.children.len() as u8];
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = vec![self.children.len() as u8];
|
||||
for child in &self.children {
|
||||
bytes.append(&mut child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp);
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x12); /* PackageOp */
|
||||
|
||||
bytes
|
||||
bytes.push(0x12); /* PackageOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,51 +255,46 @@ impl EisaName {
|
||||
}
|
||||
|
||||
impl Aml for EisaName {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
self.value.to_aml_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
fn create_integer(v: usize) -> Vec<u8> {
|
||||
if v <= u8::max_value().into() {
|
||||
(v as u8).to_aml_bytes()
|
||||
} else if v <= u16::max_value().into() {
|
||||
(v as u16).to_aml_bytes()
|
||||
} else if v <= u32::max_value() as usize {
|
||||
(v as u32).to_aml_bytes()
|
||||
} else {
|
||||
(v as u64).to_aml_bytes()
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
self.value.append_aml_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Usize = usize;
|
||||
|
||||
impl Aml for Usize {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
create_integer(*self)
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
if *self <= u8::max_value().into() {
|
||||
(*self as u8).append_aml_bytes(bytes)
|
||||
} else if *self <= u16::max_value().into() {
|
||||
(*self as u16).append_aml_bytes(bytes)
|
||||
} else if *self <= u32::max_value() as usize {
|
||||
(*self as u32).append_aml_bytes(bytes)
|
||||
} else {
|
||||
(*self as u64).append_aml_bytes(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_aml_string(v: &str) -> Vec<u8> {
|
||||
let mut data = vec![0x0D]; /* String Op */
|
||||
data.extend_from_slice(v.as_bytes());
|
||||
data.push(0x0); /* NullChar */
|
||||
data
|
||||
fn append_aml_string(v: &str, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x0D); /* String Op */
|
||||
bytes.extend_from_slice(v.as_bytes());
|
||||
bytes.push(0x0); /* NullChar */
|
||||
}
|
||||
|
||||
pub type AmlStr = &'static str;
|
||||
|
||||
impl Aml for AmlStr {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
create_aml_string(self)
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
append_aml_string(self, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub type AmlString = String;
|
||||
|
||||
impl Aml for AmlString {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
create_aml_string(self)
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
append_aml_string(self, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,36 +303,31 @@ pub struct ResourceTemplate<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for ResourceTemplate<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
// Add buffer data
|
||||
for child in &self.children {
|
||||
bytes.append(&mut child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp);
|
||||
}
|
||||
|
||||
// Mark with end and mark checksum as as always valid
|
||||
bytes.push(0x79); /* EndTag */
|
||||
bytes.push(0); /* zero checksum byte */
|
||||
tmp.push(0x79); /* EndTag */
|
||||
tmp.push(0); /* zero checksum byte */
|
||||
|
||||
// Buffer length is an encoded integer including buffer data
|
||||
// and EndTag and checksum byte
|
||||
let mut buffer_length = bytes.len().to_aml_bytes();
|
||||
let mut buffer_length = tmp.len().to_aml_bytes();
|
||||
buffer_length.reverse();
|
||||
for byte in buffer_length {
|
||||
bytes.insert(0, byte);
|
||||
tmp.insert(0, byte);
|
||||
}
|
||||
|
||||
// PkgLength is everything else
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x11); /* BufferOp */
|
||||
|
||||
bytes
|
||||
bytes.push(0x11); /* BufferOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,15 +354,13 @@ impl Memory32Fixed {
|
||||
}
|
||||
|
||||
impl Aml for Memory32Fixed {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x86]; /* Memory32Fixed */
|
||||
bytes.append(&mut 9u16.to_le_bytes().to_vec());
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x86); /* Memory32Fixed */
|
||||
bytes.extend_from_slice(&9u16.to_le_bytes());
|
||||
// 9 bytes of payload
|
||||
bytes.push(self.read_write as u8);
|
||||
bytes.append(&mut self.base.to_le_bytes().to_vec());
|
||||
bytes.append(&mut self.length.to_le_bytes().to_vec());
|
||||
bytes
|
||||
bytes.extend_from_slice(&self.base.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.length.to_le_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +416,7 @@ impl<T> AddressSpace<T> {
|
||||
|
||||
fn push_header(&self, bytes: &mut Vec<u8>, descriptor: u8, length: usize) {
|
||||
bytes.push(descriptor); /* Word Address Space Descriptor */
|
||||
bytes.append(&mut (length as u16).to_le_bytes().to_vec());
|
||||
bytes.extend_from_slice(&(length as u16).to_le_bytes());
|
||||
bytes.push(self.r#type as u8); /* type */
|
||||
let generic_flags = 1 << 2 /* Min Fixed */ | 1 << 3; /* Max Fixed */
|
||||
bytes.push(generic_flags);
|
||||
@@ -439,65 +425,53 @@ impl<T> AddressSpace<T> {
|
||||
}
|
||||
|
||||
impl Aml for AddressSpace<u16> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
self.push_header(
|
||||
&mut bytes,
|
||||
bytes,
|
||||
0x88, /* Word Address Space Descriptor */
|
||||
3 + 5 * std::mem::size_of::<u16>(), /* 3 bytes of header + 5 u16 fields */
|
||||
);
|
||||
|
||||
bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Granularity */
|
||||
bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
|
||||
bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
|
||||
bytes.append(&mut 0u16.to_le_bytes().to_vec()); /* Translation */
|
||||
bytes.extend_from_slice(&0u16.to_le_bytes()); /* Granularity */
|
||||
bytes.extend_from_slice(&self.min.to_le_bytes()); /* Min */
|
||||
bytes.extend_from_slice(&self.max.to_le_bytes()); /* Max */
|
||||
bytes.extend_from_slice(&0u16.to_le_bytes()); /* Translation */
|
||||
let len = self.max - self.min + 1;
|
||||
bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
|
||||
|
||||
bytes
|
||||
bytes.extend_from_slice(&len.to_le_bytes()); /* Length */
|
||||
}
|
||||
}
|
||||
|
||||
impl Aml for AddressSpace<u32> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
self.push_header(
|
||||
&mut bytes,
|
||||
bytes,
|
||||
0x87, /* DWord Address Space Descriptor */
|
||||
3 + 5 * std::mem::size_of::<u32>(), /* 3 bytes of header + 5 u32 fields */
|
||||
);
|
||||
|
||||
bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Granularity */
|
||||
bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
|
||||
bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
|
||||
bytes.append(&mut 0u32.to_le_bytes().to_vec()); /* Translation */
|
||||
bytes.extend_from_slice(&0u32.to_le_bytes()); /* Granularity */
|
||||
bytes.extend_from_slice(&self.min.to_le_bytes()); /* Min */
|
||||
bytes.extend_from_slice(&self.max.to_le_bytes()); /* Max */
|
||||
bytes.extend_from_slice(&0u32.to_le_bytes()); /* Translation */
|
||||
let len = self.max - self.min + 1;
|
||||
bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
|
||||
|
||||
bytes
|
||||
bytes.extend_from_slice(&len.to_le_bytes()); /* Length */
|
||||
}
|
||||
}
|
||||
|
||||
impl Aml for AddressSpace<u64> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
self.push_header(
|
||||
&mut bytes,
|
||||
bytes,
|
||||
0x8A, /* QWord Address Space Descriptor */
|
||||
3 + 5 * std::mem::size_of::<u64>(), /* 3 bytes of header + 5 u64 fields */
|
||||
);
|
||||
|
||||
bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Granularity */
|
||||
bytes.append(&mut self.min.to_le_bytes().to_vec()); /* Min */
|
||||
bytes.append(&mut self.max.to_le_bytes().to_vec()); /* Max */
|
||||
bytes.append(&mut 0u64.to_le_bytes().to_vec()); /* Translation */
|
||||
bytes.extend_from_slice(&0u64.to_le_bytes()); /* Granularity */
|
||||
bytes.extend_from_slice(&self.min.to_le_bytes()); /* Min */
|
||||
bytes.extend_from_slice(&self.max.to_le_bytes()); /* Max */
|
||||
bytes.extend_from_slice(&0u64.to_le_bytes()); /* Translation */
|
||||
let len = self.max - self.min + 1;
|
||||
bytes.append(&mut len.to_le_bytes().to_vec()); /* Length */
|
||||
|
||||
bytes
|
||||
bytes.extend_from_slice(&len.to_le_bytes()); /* Length */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,16 +494,13 @@ impl Io {
|
||||
}
|
||||
|
||||
impl Aml for Io {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x47]; /* Io Port Descriptor */
|
||||
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x47); /* Io Port Descriptor */
|
||||
bytes.push(1); /* IODecode16 */
|
||||
bytes.append(&mut self.min.to_le_bytes().to_vec());
|
||||
bytes.append(&mut self.max.to_le_bytes().to_vec());
|
||||
bytes.extend_from_slice(&self.min.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.max.to_le_bytes());
|
||||
bytes.push(self.alignment);
|
||||
bytes.push(self.length);
|
||||
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,18 +531,16 @@ impl Interrupt {
|
||||
}
|
||||
|
||||
impl Aml for Interrupt {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x89]; /* Extended IRQ Descriptor */
|
||||
bytes.append(&mut 6u16.to_le_bytes().to_vec());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x89); /* Extended IRQ Descriptor */
|
||||
bytes.extend_from_slice(&6u16.to_le_bytes());
|
||||
let flags = (self.shared as u8) << 3
|
||||
| (self.active_low as u8) << 2
|
||||
| (self.edge_triggered as u8) << 1
|
||||
| self.consumer as u8;
|
||||
bytes.push(flags);
|
||||
bytes.push(1u8); /* count */
|
||||
bytes.append(&mut self.number.to_le_bytes().to_vec());
|
||||
|
||||
bytes
|
||||
bytes.extend_from_slice(&self.number.to_le_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,22 +550,20 @@ pub struct Device<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Device<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.append(&mut self.path.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.path.append_aml_bytes(&mut tmp);
|
||||
|
||||
for child in &self.children {
|
||||
bytes.append(&mut child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp);
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x82); /* DeviceOp */
|
||||
bytes.insert(0, 0x5b); /* ExtOpPrefix */
|
||||
bytes
|
||||
bytes.push(0x5b); /* ExtOpPrefix */
|
||||
bytes.push(0x82); /* DeviceOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,21 +579,18 @@ pub struct Scope<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Scope<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.append(&mut self.path.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.path.append_aml_bytes(&mut tmp);
|
||||
for child in &self.children {
|
||||
bytes.append(&mut child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp);
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x10); /* ScopeOp */
|
||||
bytes
|
||||
bytes.push(0x10); /* ScopeOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,23 +619,20 @@ impl<'a> Method<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Method<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.append(&mut self.path.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.path.append_aml_bytes(&mut tmp);
|
||||
let flags: u8 = (self.args & 0x7) | (self.serialized as u8) << 3;
|
||||
bytes.push(flags);
|
||||
tmp.push(flags);
|
||||
for child in &self.children {
|
||||
bytes.append(&mut child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp);
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x14); /* MethodOp */
|
||||
bytes
|
||||
bytes.push(0x14); /* MethodOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,10 +647,9 @@ impl<'a> Return<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Return<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0xa4]; /* ReturnOp */
|
||||
bytes.append(&mut self.value.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0xa4); /* ReturnOp */
|
||||
self.value.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,35 +700,32 @@ impl Field {
|
||||
}
|
||||
|
||||
impl Aml for Field {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.append(&mut self.path.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.path.append_aml_bytes(&mut tmp);
|
||||
|
||||
let flags: u8 = self.access_type as u8 | (self.update_rule as u8) << 5;
|
||||
bytes.push(flags);
|
||||
tmp.push(flags);
|
||||
|
||||
for field in self.fields.iter() {
|
||||
match field {
|
||||
FieldEntry::Named(name, length) => {
|
||||
bytes.extend_from_slice(name);
|
||||
bytes.append(&mut create_pkg_length(&vec![0; *length], false));
|
||||
tmp.extend_from_slice(name);
|
||||
tmp.extend_from_slice(&create_pkg_length(&vec![0; *length], false));
|
||||
}
|
||||
FieldEntry::Reserved(length) => {
|
||||
bytes.push(0x0);
|
||||
bytes.append(&mut create_pkg_length(&vec![0; *length], false));
|
||||
tmp.push(0x0);
|
||||
tmp.extend_from_slice(&create_pkg_length(&vec![0; *length], false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x81); /* FieldOp */
|
||||
bytes.insert(0, 0x5b); /* ExtOpPrefix */
|
||||
bytes
|
||||
bytes.push(0x5b); /* ExtOpPrefix */
|
||||
bytes.push(0x81); /* FieldOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,15 +762,13 @@ impl OpRegion {
|
||||
}
|
||||
|
||||
impl Aml for OpRegion {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.append(&mut self.path.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x5b); /* ExtOpPrefix */
|
||||
bytes.push(0x80); /* OpRegionOp */
|
||||
self.path.append_aml_bytes(bytes);
|
||||
bytes.push(self.space as u8);
|
||||
bytes.extend_from_slice(&self.offset.to_aml_bytes()); /* RegionOffset */
|
||||
bytes.extend_from_slice(&self.length.to_aml_bytes()); /* RegionLen */
|
||||
bytes.insert(0, 0x80); /* OpRegionOp */
|
||||
bytes.insert(0, 0x5b); /* ExtOpPrefix */
|
||||
bytes
|
||||
self.offset.append_aml_bytes(bytes); /* RegionOffset */
|
||||
self.length.append_aml_bytes(bytes); /* RegionLen */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,21 +787,18 @@ impl<'a> If<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for If<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&self.predicate.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.predicate.append_aml_bytes(&mut tmp);
|
||||
for child in self.if_children.iter() {
|
||||
bytes.extend_from_slice(&child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp);
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0xa0); /* IfOp */
|
||||
bytes
|
||||
bytes.push(0xa0); /* IfOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,11 +814,10 @@ impl<'a> Equal<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Equal<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x93]; /* LEqualOp */
|
||||
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x93); /* LEqualOp */
|
||||
self.left.append_aml_bytes(bytes);
|
||||
self.right.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,33 +833,28 @@ impl<'a> LessThan<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for LessThan<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x95]; /* LLessOp */
|
||||
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x95); /* LLessOp */
|
||||
self.left.append_aml_bytes(bytes);
|
||||
self.right.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Arg(pub u8);
|
||||
|
||||
impl Aml for Arg {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
assert!(self.0 <= 6);
|
||||
bytes.push(0x68 + self.0); /* Arg0Op */
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Local(pub u8);
|
||||
|
||||
impl Aml for Local {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
assert!(self.0 <= 7);
|
||||
bytes.push(0x60 + self.0); /* Local0Op */
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,11 +870,10 @@ impl<'a> Store<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Store<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x70]; /* StoreOp */
|
||||
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.name.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x70); /* StoreOp */
|
||||
self.value.append_aml_bytes(bytes);
|
||||
self.name.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,12 +889,11 @@ impl Mutex {
|
||||
}
|
||||
|
||||
impl Aml for Mutex {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x5b); /* ExtOpPrefix */
|
||||
bytes.push(0x01); /* MutexOp */
|
||||
bytes.extend_from_slice(&self.path.to_aml_bytes());
|
||||
self.path.append_aml_bytes(bytes);
|
||||
bytes.push(self.sync_level);
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -965,12 +909,11 @@ impl Acquire {
|
||||
}
|
||||
|
||||
impl Aml for Acquire {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x5b); /* ExtOpPrefix */
|
||||
bytes.push(0x23); /* AcquireOp */
|
||||
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
||||
self.mutex.append_aml_bytes(bytes);
|
||||
bytes.extend_from_slice(&self.timeout.to_le_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,11 +928,10 @@ impl Release {
|
||||
}
|
||||
|
||||
impl Aml for Release {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x5b); /* ExtOpPrefix */
|
||||
bytes.push(0x27); /* ReleaseOp */
|
||||
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
||||
bytes
|
||||
self.mutex.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,11 +947,10 @@ impl<'a> Notify<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for Notify<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x86]; /* NotifyOp */
|
||||
bytes.extend_from_slice(&self.object.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x86); /* NotifyOp */
|
||||
self.object.append_aml_bytes(bytes);
|
||||
self.value.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,21 +969,18 @@ impl<'a> While<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for While<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&self.predicate.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.predicate.append_aml_bytes(&mut tmp);
|
||||
for child in self.while_children.iter() {
|
||||
bytes.extend_from_slice(&child.to_aml_bytes());
|
||||
child.append_aml_bytes(&mut tmp)
|
||||
}
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0xa2); /* WhileOp */
|
||||
bytes
|
||||
bytes.push(0xa2); /* WhileOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,12 +999,11 @@ macro_rules! binary_op {
|
||||
}
|
||||
|
||||
impl<'a> Aml for $name<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![$opcode]; /* Op for the binary operator */
|
||||
bytes.extend_from_slice(&self.a.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.b.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.target.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push($opcode); /* Op for the binary operator */
|
||||
self.a.append_aml_bytes(bytes);
|
||||
self.b.append_aml_bytes(bytes);
|
||||
self.target.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1101,13 +1038,11 @@ impl<'a> MethodCall<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for MethodCall<'a> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&self.name.to_aml_bytes());
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
self.name.append_aml_bytes(bytes);
|
||||
for arg in self.args.iter() {
|
||||
bytes.extend_from_slice(&arg.to_aml_bytes());
|
||||
arg.append_aml_bytes(bytes);
|
||||
}
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1122,20 +1057,16 @@ impl Buffer {
|
||||
}
|
||||
|
||||
impl Aml for Buffer {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&self.data.len().to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.data);
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let mut tmp = Vec::new();
|
||||
self.data.len().append_aml_bytes(&mut tmp);
|
||||
tmp.extend_from_slice(&self.data);
|
||||
|
||||
let mut pkg_length = create_pkg_length(&bytes, true);
|
||||
pkg_length.reverse();
|
||||
for byte in pkg_length {
|
||||
bytes.insert(0, byte);
|
||||
}
|
||||
let pkg_length = create_pkg_length(&tmp, true);
|
||||
|
||||
bytes.insert(0, 0x11); /* BufferOp */
|
||||
|
||||
bytes
|
||||
bytes.push(0x11); /* BufferOp */
|
||||
bytes.extend_from_slice(&pkg_length);
|
||||
bytes.extend_from_slice(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1158,22 +1089,20 @@ impl<'a, T> CreateField<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a> Aml for CreateField<'a, u64> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x8f]; /* CreateQWordFieldOp */
|
||||
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x8f); /* CreateQWordFieldOp */
|
||||
self.buffer.append_aml_bytes(bytes);
|
||||
self.offset.append_aml_bytes(bytes);
|
||||
self.field.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Aml for CreateField<'a, u32> {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = vec![0x8a]; /* CreateDWordFieldOp */
|
||||
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
||||
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
||||
bytes
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(0x8a); /* CreateDWordFieldOp */
|
||||
self.buffer.append_aml_bytes(bytes);
|
||||
self.offset.append_aml_bytes(bytes);
|
||||
self.field.append_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct Rsdp {
|
||||
_reserved: [u8; 3],
|
||||
}
|
||||
|
||||
// SAFETY: Rsdp only contains a series of integers
|
||||
unsafe impl ByteValued for Rsdp {}
|
||||
|
||||
impl Rsdp {
|
||||
|
||||
@@ -11,10 +11,10 @@ tdx = []
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||
anyhow = "1.0.44"
|
||||
anyhow = "1.0.51"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
linux-loader = { version = "0.4.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.14"
|
||||
serde = { version = "1.0.130", features = ["rc"] }
|
||||
|
||||
@@ -82,6 +82,8 @@ pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x2000_0000;
|
||||
/// PCI MMCONFIG space (start: after the device space at 1 GiB, length: 256MiB)
|
||||
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x3000_0000);
|
||||
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||
// One bus with potentially 256 devices (32 slots x 8 functions).
|
||||
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
|
||||
|
||||
/// Start of RAM on 64 bit ARM.
|
||||
pub const RAM_64BIT_START: u64 = 0x4000_0000;
|
||||
|
||||
@@ -95,22 +95,4 @@ mod tests {
|
||||
let mut klapic = LapicState::default();
|
||||
set_klapic_reg(&mut klapic, reg_offset, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apic_delivery_mode() {
|
||||
let mut v: Vec<u32> = Vec::new();
|
||||
v.resize(20, 0);
|
||||
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
libc::getrandom(v.as_mut_ptr() as *mut _ as *mut libc::c_void, 80, 0),
|
||||
80
|
||||
);
|
||||
}
|
||||
|
||||
v.iter_mut()
|
||||
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));
|
||||
let after: Vec<u32> = v.iter().map(|x| ((*x & !0x700) | ((2) << 8))).collect();
|
||||
assert_eq!(v, after);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ pub const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000);
|
||||
|
||||
// == No fixed addresses in the "High RAM" range ==
|
||||
|
||||
// ** 32-bit reserved area (start: 3GiB, length: 1GiB) **
|
||||
// ** 32-bit reserved area (start: 3GiB, length: 896MiB) **
|
||||
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000);
|
||||
pub const MEM_32BIT_RESERVED_SIZE: u64 = 1024 << 20;
|
||||
pub const MEM_32BIT_RESERVED_SIZE: u64 = PCI_MMCONFIG_SIZE + MEM_32BIT_DEVICES_SIZE;
|
||||
|
||||
// == Fixed constants within the "32-bit reserved" range ==
|
||||
|
||||
@@ -93,6 +93,16 @@ pub const MEM_32BIT_DEVICES_SIZE: u64 = 640 << 20;
|
||||
pub const PCI_MMCONFIG_START: GuestAddress =
|
||||
GuestAddress(MEM_32BIT_DEVICES_START.0 + MEM_32BIT_DEVICES_SIZE);
|
||||
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||
// One bus with potentially 256 devices (32 slots x 8 functions).
|
||||
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
|
||||
|
||||
// TSS is 3 pages after the PCI MMCONFIG space
|
||||
pub const KVM_TSS_START: GuestAddress = GuestAddress(PCI_MMCONFIG_START.0 + PCI_MMCONFIG_SIZE);
|
||||
pub const KVM_TSS_SIZE: u64 = (3 * 4) << 10;
|
||||
|
||||
// Identity map is a one page region after the TSS
|
||||
pub const KVM_IDENTITY_MAP_START: GuestAddress = GuestAddress(KVM_TSS_START.0 + KVM_TSS_SIZE);
|
||||
pub const KVM_IDENTITY_MAP_SIZE: u64 = 4 << 10;
|
||||
|
||||
// IOAPIC
|
||||
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
||||
@@ -101,9 +111,6 @@ pub const IOAPIC_SIZE: u64 = 0x20;
|
||||
// APIC
|
||||
pub const APIC_START: GuestAddress = GuestAddress(0xfee0_0000);
|
||||
|
||||
/// Address for the TSS setup.
|
||||
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
||||
|
||||
// == End of "32-bit reserved" range. ==
|
||||
|
||||
// ** 64-bit RAM start (start: 4GiB, length: varies) **
|
||||
|
||||
@@ -57,7 +57,7 @@ const KVM_FEATURE_STEAL_TIME_BIT: u8 = 5;
|
||||
/// is to be used to configure the guest initial state.
|
||||
pub struct EntryPoint {
|
||||
/// Address in guest memory where the guest must start execution
|
||||
pub entry_addr: GuestAddress,
|
||||
pub entry_addr: Option<GuestAddress>,
|
||||
}
|
||||
|
||||
const E820_RAM: u32 = 1;
|
||||
@@ -118,17 +118,15 @@ impl SgxEpcRegion {
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct StartInfoWrapper(hvm_start_info);
|
||||
|
||||
// It is safe to initialize StartInfoWrapper which is a wrapper over `hvm_start_info` (a series of ints).
|
||||
unsafe impl ByteValued for StartInfoWrapper {}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
|
||||
|
||||
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct ModlistEntryWrapper(hvm_modlist_entry);
|
||||
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for StartInfoWrapper {}
|
||||
unsafe impl ByteValued for MemmapTableEntryWrapper {}
|
||||
unsafe impl ByteValued for ModlistEntryWrapper {}
|
||||
|
||||
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
||||
@@ -139,7 +137,7 @@ unsafe impl ByteValued for ModlistEntryWrapper {}
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct BootParamsWrapper(boot_params);
|
||||
|
||||
// It is safe to initialize BootParamsWrap which is a wrapper over `boot_params` (a series of ints).
|
||||
// SAFETY: BootParamsWrap is a wrapper over `boot_params` (a series of ints).
|
||||
unsafe impl ByteValued for BootParamsWrapper {}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -743,11 +741,12 @@ pub fn configure_vcpu(
|
||||
|
||||
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||
// Safe to unwrap because this method is called after the VM is configured
|
||||
regs::setup_regs(fd, kernel_entry_point.entry_addr.raw_value())
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
||||
if let Some(entry_addr) = kernel_entry_point.entry_addr {
|
||||
// Safe to unwrap because this method is called after the VM is configured
|
||||
regs::setup_regs(fd, entry_addr.raw_value()).map_err(Error::RegsConfiguration)?;
|
||||
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
||||
regs::setup_sregs(&vm_memory.memory(), fd).map_err(Error::SregsConfiguration)?;
|
||||
}
|
||||
}
|
||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||
Ok(())
|
||||
|
||||
@@ -35,7 +35,7 @@ struct MpcLintsrcWrapper(mpspec::mpc_lintsrc);
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct MpfIntelWrapper(mpspec::mpf_intel);
|
||||
|
||||
// These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
||||
// SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization.
|
||||
unsafe impl ByteValued for MpcBusWrapper {}
|
||||
unsafe impl ByteValued for MpcCpuWrapper {}
|
||||
unsafe impl ByteValued for MpcIntsrcWrapper {}
|
||||
|
||||
@@ -82,7 +82,6 @@ pub struct Smbios30Entrypoint {
|
||||
pub max_size: u32,
|
||||
pub physptr: u64,
|
||||
}
|
||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||
|
||||
impl Clone for Smbios30Entrypoint {
|
||||
fn clone(&self) -> Self {
|
||||
@@ -112,8 +111,6 @@ impl Clone for SmbiosBiosInfo {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct SmbiosSysInfo {
|
||||
@@ -136,6 +133,9 @@ impl Clone for SmbiosSysInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for Smbios30Entrypoint {}
|
||||
unsafe impl ByteValued for SmbiosBiosInfo {}
|
||||
unsafe impl ByteValued for SmbiosSysInfo {}
|
||||
|
||||
fn write_and_incr<T: ByteValued>(
|
||||
|
||||
@@ -25,6 +25,7 @@ pub enum TdvfError {
|
||||
|
||||
// TDVF_DESCRIPTOR
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
pub struct TdvfDescriptor {
|
||||
signature: [u8; 4],
|
||||
length: u32,
|
||||
@@ -68,8 +69,6 @@ pub struct TdVmmDataRegion {
|
||||
pub region_type: TdVmmDataRegionType,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for TdVmmDataRegion {}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TdVmmDataRegionType {
|
||||
@@ -128,7 +127,7 @@ pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfErro
|
||||
file.seek(SeekFrom::Start(descriptor_offset))
|
||||
.map_err(TdvfError::ReadDescriptor)?;
|
||||
|
||||
let mut descriptor: TdvfDescriptor = unsafe { std::mem::zeroed() };
|
||||
let mut descriptor: TdvfDescriptor = Default::default();
|
||||
// Safe as we read exactly the size of the descriptor header
|
||||
file.read_exact(unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
@@ -191,7 +190,6 @@ struct HobHeader {
|
||||
length: u16,
|
||||
reserved: u32,
|
||||
}
|
||||
unsafe impl ByteValued for HobHeader {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
@@ -205,7 +203,6 @@ struct HobHandoffInfoTable {
|
||||
efi_free_memory_bottom: u64,
|
||||
efi_end_of_hob_list: u64,
|
||||
}
|
||||
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
@@ -226,7 +223,6 @@ struct HobResourceDescriptor {
|
||||
physical_start: u64,
|
||||
resource_length: u64,
|
||||
}
|
||||
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
@@ -234,7 +230,6 @@ struct HobGuidType {
|
||||
header: HobHeader,
|
||||
name: EfiGuid,
|
||||
}
|
||||
unsafe impl ByteValued for HobGuidType {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
@@ -242,6 +237,13 @@ struct TdVmmData {
|
||||
guid_type: HobGuidType,
|
||||
region: TdVmmDataRegion,
|
||||
}
|
||||
|
||||
// SAFETY: These data structures only contain a series of integers
|
||||
unsafe impl ByteValued for TdVmmDataRegion {}
|
||||
unsafe impl ByteValued for HobHeader {}
|
||||
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||
unsafe impl ByteValued for HobGuidType {}
|
||||
unsafe impl ByteValued for TdVmmData {}
|
||||
|
||||
pub struct TdHob {
|
||||
|
||||
@@ -10,7 +10,7 @@ io_uring = []
|
||||
|
||||
[dependencies]
|
||||
io-uring = "0.5.2"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
qcow = { path = "../qcow" }
|
||||
thiserror = "1.0.30"
|
||||
@@ -18,6 +18,7 @@ versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -28,19 +28,17 @@ use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
#[cfg(feature = "io_uring")]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::Path;
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_queue::DescriptorChain;
|
||||
use vm_memory::{
|
||||
bitmap::AtomicBitmap, bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory,
|
||||
GuestMemoryError,
|
||||
GuestMemoryAtomic, GuestMemoryError,
|
||||
};
|
||||
use vm_virtio::DescriptorChain;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
@@ -179,25 +177,32 @@ pub struct Request {
|
||||
|
||||
impl Request {
|
||||
pub fn parse(
|
||||
avail_desc: &DescriptorChain,
|
||||
mem: &GuestMemoryMmap,
|
||||
desc_chain: &mut DescriptorChain<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> result::Result<Request, Error> {
|
||||
let hdr_desc = desc_chain
|
||||
.next()
|
||||
.ok_or(Error::DescriptorChainTooShort)
|
||||
.map_err(|e| {
|
||||
error!("Missing head descriptor");
|
||||
e
|
||||
})?;
|
||||
|
||||
// The head contains the request type which MUST be readable.
|
||||
if avail_desc.is_write_only() {
|
||||
if hdr_desc.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
let mut req = Request {
|
||||
request_type: request_type(mem, avail_desc.addr)?,
|
||||
sector: sector(mem, avail_desc.addr)?,
|
||||
request_type: request_type(desc_chain.memory(), hdr_desc.addr())?,
|
||||
sector: sector(desc_chain.memory(), hdr_desc.addr())?,
|
||||
data_descriptors: Vec::new(),
|
||||
status_addr: GuestAddress(0),
|
||||
writeback: true,
|
||||
};
|
||||
|
||||
let status_desc;
|
||||
let mut desc = avail_desc
|
||||
.next_descriptor()
|
||||
let mut desc = desc_chain
|
||||
.next()
|
||||
.ok_or(Error::DescriptorChainTooShort)
|
||||
.map_err(|e| {
|
||||
error!("Only head descriptor present: request = {:?}", req);
|
||||
@@ -222,9 +227,9 @@ impl Request {
|
||||
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceId {
|
||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||
}
|
||||
req.data_descriptors.push((desc.addr, desc.len));
|
||||
desc = desc
|
||||
.next_descriptor()
|
||||
req.data_descriptors.push((desc.addr(), desc.len()));
|
||||
desc = desc_chain
|
||||
.next()
|
||||
.ok_or(Error::DescriptorChainTooShort)
|
||||
.map_err(|e| {
|
||||
error!("DescriptorChain corrupted: request = {:?}", req);
|
||||
@@ -239,11 +244,11 @@ impl Request {
|
||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||
}
|
||||
|
||||
if status_desc.len < 1 {
|
||||
if status_desc.len() < 1 {
|
||||
return Err(Error::DescriptorLengthTooSmall);
|
||||
}
|
||||
|
||||
req.status_addr = status_desc.addr;
|
||||
req.status_addr = status_desc.addr();
|
||||
|
||||
Ok(req)
|
||||
}
|
||||
@@ -404,8 +409,6 @@ pub struct VirtioBlockConfig {
|
||||
pub write_zeroes_may_unmap: u8,
|
||||
pub unused1: [u8; 3],
|
||||
}
|
||||
unsafe impl ByteValued for VirtioBlockConfig {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Versionize)]
|
||||
#[repr(C, packed)]
|
||||
pub struct VirtioBlockGeometry {
|
||||
@@ -413,6 +416,9 @@ pub struct VirtioBlockGeometry {
|
||||
pub heads: u8,
|
||||
pub sectors: u8,
|
||||
}
|
||||
|
||||
// SAFETY: these data structures only contain a series of integers
|
||||
unsafe impl ByteValued for VirtioBlockConfig {}
|
||||
unsafe impl ByteValued for VirtioBlockGeometry {}
|
||||
|
||||
/// Check if io_uring for block device can be used on the current system, as
|
||||
@@ -433,25 +439,6 @@ pub fn block_io_uring_is_supported() -> bool {
|
||||
|
||||
let submitter = io_uring.submitter();
|
||||
|
||||
let event_fd = match EventFd::new(libc::EFD_NONBLOCK) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
info!("{} failed to create eventfd: {}", error_msg, e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Check we can register an eventfd as this is going to be needed while
|
||||
// using io_uring with the virtio block device. This also validates that
|
||||
// io_uring_register() syscall is supported.
|
||||
match submitter.register_eventfd(event_fd.as_raw_fd()) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
info!("{} failed to register eventfd: {}", error_msg, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let mut probe = Probe::new();
|
||||
|
||||
// Check we can register a probe to validate supported operations.
|
||||
|
||||
@@ -6,12 +6,12 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||
anyhow = "1.0.44"
|
||||
anyhow = "1.0.51"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
|
||||
@@ -104,7 +104,7 @@ impl BusDevice for AcpiGedDevice {
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
impl Aml for AcpiGedDevice {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
aml::Device::new(
|
||||
"_SB_.GED_".into(),
|
||||
vec![
|
||||
@@ -151,7 +151,7 @@ impl Aml for AcpiGedDevice {
|
||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
|
||||
&aml::If::new(
|
||||
&aml::Equal::new(&aml::Local(1), &4usize),
|
||||
vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
|
||||
vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])],
|
||||
),
|
||||
&aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
|
||||
&aml::If::new(
|
||||
@@ -165,7 +165,7 @@ impl Aml for AcpiGedDevice {
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes()
|
||||
.append_aml_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,9 @@ impl BusDevice for Cmos {
|
||||
0x09 => to_bcd((year % 100) as u8),
|
||||
// Bit 5 for 32kHz clock. Bit 7 for Update in Progress
|
||||
0x0a => 1 << 5 | (update_in_progress as u8) << 7,
|
||||
// Bit 0-6 are reserved and must be 0.
|
||||
// Bit 7 must be 1 (CMOS has power)
|
||||
0x0d => 1 << 7,
|
||||
0x32 => to_bcd(((year + 1900) / 100) as u8),
|
||||
_ => {
|
||||
// self.index is always guaranteed to be in range via INDEX_MASK.
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
# How to build and test Cloud Hypervisor on AArch64
|
||||
|
||||
This document introduces how to build and test Cloud Hypervisor on AArch64 servers. Currently Cloud Hypervisor cannot be tested on Raspberry PI. Because on AArch64, Cloud Hypervisor requires GICv3-ITS device for PCIe MSI interrupt handling. But GICv3-ITS has not been equipped on any Raspberry PI product so far.
|
||||
This document introduces how to build and test Cloud Hypervisor on AArch64
|
||||
servers. Currently Cloud Hypervisor cannot be tested on Raspberry PI. Because
|
||||
on AArch64, Cloud Hypervisor requires GICv3-ITS device for PCIe MSI interrupt
|
||||
handling. But GICv3-ITS has not been equipped on any Raspberry PI product so
|
||||
far.
|
||||
|
||||
Now Cloud Hypervisor supports 2 ways of booting on AArch64: UEFI booting and direct-kernel booting. The document covers both of the ways.
|
||||
Now Cloud Hypervisor supports 2 ways of booting on AArch64: UEFI booting and
|
||||
direct-kernel booting. The document covers both of the ways.
|
||||
|
||||
All the steps are based on Ubuntu. We use the Ubuntu cloud image for guest VM disk.
|
||||
All the steps are based on Ubuntu. We use the Ubuntu cloud image for guest VM
|
||||
disk.
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -82,19 +88,29 @@ $ build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtCloudHv.dsc -b RELEASE
|
||||
$ popd
|
||||
```
|
||||
|
||||
If the build goes well, the EDK2 binary is available at `edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
||||
If the build goes well, the EDK2 binary is available at
|
||||
`edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd`.
|
||||
|
||||
### Booting the guest VM
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ sudo RUST_BACKTRACE=1 $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock --kernel $CLOUDH/edk2/edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd --disk path=$CLOUDH/focal-server-cloudimg-arm64.raw --cpus boot=4 --memory size=4096M --serial tty --console off --log-file log.log -vvv --net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0
|
||||
$ sudo RUST_BACKTRACE=1 $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||
--api-socket /tmp/cloud-hypervisor.sock \
|
||||
--kernel $CLOUDH/edk2/Build/ArmVirtCloudHv-AARCH64/RELEASE_GCC5/FV/CLOUDHV_EFI.fd \
|
||||
--disk path=$CLOUDH/focal-server-cloudimg-arm64.raw \
|
||||
--cpus boot=4 \
|
||||
--memory size=4096M \
|
||||
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
||||
--serial tty \
|
||||
--console off
|
||||
$ popd
|
||||
```
|
||||
|
||||
## Direct-kernel booting
|
||||
|
||||
Alternativelly, you can build your own kernel for guest VM. This way, UEFI is not involved and ACPI cannot be enabled.
|
||||
Alternativelly, you can build your own kernel for guest VM. This way, UEFI is
|
||||
not involved and ACPI cannot be enabled.
|
||||
|
||||
### Building kernel
|
||||
|
||||
@@ -111,6 +127,15 @@ $ popd
|
||||
|
||||
```bash
|
||||
$ pushd $CLOUDH
|
||||
$ sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock --kernel $CLOUDH/linux/arch/arm64/boot/Image --disk path=focal-server-cloudimg-arm64.raw --cmdline "keep_bootcon console=ttyAMA0 reboot=k panic=1 root=/dev/vda1 rw" --cpus boot=4 --memory size=4096M --serial tty --console off --log-file log.log -vvv --net "tap=,mac=,ip=,mask="
|
||||
$ sudo $CLOUDH/cloud-hypervisor/target/debug/cloud-hypervisor \
|
||||
--api-socket /tmp/cloud-hypervisor.sock \
|
||||
--kernel $CLOUDH/linux/arch/arm64/boot/Image \
|
||||
--disk path=focal-server-cloudimg-arm64.raw \
|
||||
--cmdline "keep_bootcon console=ttyAMA0 reboot=k panic=1 root=/dev/vda1 rw" \
|
||||
--cpus boot=4 \
|
||||
--memory size=4096M \
|
||||
--net tap=,mac=12:34:56:78:90:01,ip=192.168.1.1,mask=255.255.255.0 \
|
||||
--serial tty \
|
||||
--console off
|
||||
$ popd
|
||||
```
|
||||
|
||||
189
docs/cpu.md
Normal file
189
docs/cpu.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# CPU
|
||||
|
||||
Cloud Hypervisor has many options when it comes to the creation of virtual
|
||||
CPUs. This document aims to explain what Cloud Hypervisor is capable of and
|
||||
how it can be used to meet the needs of very different use cases.
|
||||
|
||||
## Options
|
||||
|
||||
`CpusConfig` or what is known as `--cpus` from the CLI perspective is the way
|
||||
to set vCPUs options for Cloud Hypervisor.
|
||||
|
||||
```rust
|
||||
struct CpusConfig {
|
||||
boot_vcpus: u8,
|
||||
max_vcpus: u8,
|
||||
topology: Option<CpuTopology>,
|
||||
kvm_hyperv: bool,
|
||||
max_phys_bits: u8,
|
||||
affinity: Option<Vec<CpuAffinity>>,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--cpus boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>
|
||||
```
|
||||
|
||||
### `boot`
|
||||
|
||||
Number of vCPUs present at boot time.
|
||||
|
||||
This option allows to define a specific number of vCPUs to be present at the
|
||||
time the VM is started. This option is mandatory when using the `--cpus`
|
||||
parameter. If `--cpus` is not specified, this option takes the default value
|
||||
of `1`, starting the VM with a single vCPU.
|
||||
|
||||
Value is an unsigned integer of 8 bits.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus boot=2
|
||||
```
|
||||
|
||||
### `max`
|
||||
|
||||
Maximum number of vCPUs.
|
||||
|
||||
This option defines the maximum number of vCPUs that can be assigned to the VM.
|
||||
In particular, this option is used when looking for CPU hotplug as it lets the
|
||||
provide an indication about how many vCPUs might be needed later during the
|
||||
runtime of the VM.
|
||||
For instance, if booting the VM with 2 vCPUs and a maximum of 6 vCPUs, it means
|
||||
up to 4 vCPUs can be added later at runtime by resizing the VM.
|
||||
|
||||
The value must be greater than or equal to the number of boot vCPUs.
|
||||
The value is an unsigned integer of 8 bits.
|
||||
|
||||
By default this option takes the value of `boot`, meaning vCPU hotplug is not
|
||||
expected and can't be performed.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus max=3
|
||||
```
|
||||
|
||||
### `topology`
|
||||
|
||||
Topology of the guest platform.
|
||||
|
||||
This option gives the user a way to describe the exact topology that should be
|
||||
exposed to the guest. It can be useful to describe to the guest the same
|
||||
topology found on the host as it allows for proper usage of the resources and
|
||||
is a way to achieve better performances.
|
||||
|
||||
The topology is described through the following structure:
|
||||
|
||||
```rust
|
||||
struct CpuTopology {
|
||||
threads_per_core: u8,
|
||||
cores_per_die: u8,
|
||||
dies_per_package: u8,
|
||||
packages: u8,
|
||||
}
|
||||
```
|
||||
|
||||
or the following syntax through the CLI:
|
||||
|
||||
```
|
||||
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>
|
||||
```
|
||||
|
||||
By default the topology will be `1:1:1:1`.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus boot=2,topology=1:1:2:1
|
||||
```
|
||||
|
||||
### `kvm_hyperv`
|
||||
|
||||
Enable KVM Hyper-V emulation.
|
||||
|
||||
When turned on, this option relies on KVM to emulate the synthetic interrupt
|
||||
controller (SynIC) along with synthetic timers expected by a Windows guest.
|
||||
A Windows guest usually runs on top of Microsoft Hyper-V, therefore expects
|
||||
these synthetic devices to be present. That's why KVM provides a way to emulate
|
||||
them and avoids failures running a Windows guest with Cloud Hypervisor.
|
||||
|
||||
By default this option is turned off.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus kvm_hyperv=on
|
||||
```
|
||||
|
||||
### `max_phys_bits`
|
||||
|
||||
Maximum size for guest's addressable space.
|
||||
|
||||
This option defines the maximum number of physical bits for all vCPUs, which
|
||||
sets a limit for the size of the guest's addressable space. This is mainly
|
||||
useful for debug purpose.
|
||||
|
||||
The value is an unsigned integer of 8 bits.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus max_phys_bits=40
|
||||
```
|
||||
|
||||
### `affinity`
|
||||
|
||||
Affinity of each vCPU.
|
||||
|
||||
This option gives the user a way to provide the host CPU set associated with
|
||||
each vCPU. It is useful for achieving CPU pinning, ensuring multiple VMs won't
|
||||
affect the performance of each other. It might also be used in the context of
|
||||
NUMA as it is way of making sure the VM can run on a specific host NUMA node.
|
||||
In general, this option is used to increase the performances of a VM depending
|
||||
on the host platform and the type of workload running in the guest.
|
||||
|
||||
The affinity is described through the following structure:
|
||||
|
||||
```rust
|
||||
struct CpuAffinity {
|
||||
vcpu: u8,
|
||||
host_cpus: Vec<u8>,
|
||||
}
|
||||
```
|
||||
|
||||
or the following syntax through the CLI:
|
||||
|
||||
```
|
||||
affinity=[<vcpu_id1>@[<host_cpu_id1>, <host_cpu_id2>], <vcpu_id2>@[<host_cpu_id3>, <host_cpu_id4>]]
|
||||
```
|
||||
|
||||
The outer brackets define the list of vCPUs. And for each vCPU, the inner
|
||||
brackets attached to `@` define the list of host CPUs the vCPU is allowed to
|
||||
run onto.
|
||||
|
||||
Multiple values can be provided to define each list. Each value is an unsigned
|
||||
integer of 8 bits.
|
||||
|
||||
For instance, if one needs to run vCPU 0 on host CPUs from 0 to 4, the syntax
|
||||
using `-` will help define a contiguous range with `affinity=0@[0-4]`. The
|
||||
same example could also be described with `affinity=0@[0,1,2,3,4]`.
|
||||
|
||||
A combination of both `-` and `,` separators is useful when one might need to
|
||||
describe a list containing host CPUs from 0 to 99 and the host CPU 255, as it
|
||||
could simply be described with `affinity=0@[0-99,255]`.
|
||||
|
||||
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||
demarcate the list.
|
||||
|
||||
By default each vCPU runs on the entire host CPU set.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus boot=3,affinity=[0@[2,3],1@[0,1]]
|
||||
```
|
||||
|
||||
In this example, assuming the host has 4 CPUs, vCPU 0 will run exclusively on
|
||||
host CPUs 2 and 3, while vCPU 1 will run exclusively on host CPUs 0 and 1.
|
||||
Because nothing is defined for vCPU 2, it can run on any of the 4 host CPUs.
|
||||
@@ -72,7 +72,7 @@ mount -t devpts devpts /dev/pts
|
||||
|
||||
### Install needed packages
|
||||
|
||||
In the context Cloud-Hypervisor's integration tests, we need several utilities.
|
||||
In the context Cloud Hypervisor's integration tests, we need several utilities.
|
||||
Here is the way to install them for a Ubuntu image. This step is specific to
|
||||
Ubuntu distributions.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Cloud Hypervisor Hot Plug
|
||||
|
||||
Currently Cloud Hypervisor only support hot plugging of CPU devices.
|
||||
Currently Cloud Hypervisor only supports hot plugging of CPU devices.
|
||||
|
||||
## Kernel support
|
||||
|
||||
@@ -9,7 +9,7 @@ or by using this kernel patch (available in 5.5-rc1 and later): https://git.kern
|
||||
|
||||
## CPU Hot Plug
|
||||
|
||||
Extra vCPUs can be added and removed from a running Cloud Hypervisor instance. This is controlled by two mechanisms:
|
||||
Extra vCPUs can be added and removed from a running `cloud-hypervisor` instance. This is controlled by two mechanisms:
|
||||
|
||||
1. Specifying a number of maximum potential vCPUs that is greater than the number of default (boot) vCPUs.
|
||||
2. Making a HTTP API request to the VMM to ask for the additional vCPUs to be added.
|
||||
@@ -65,7 +65,7 @@ As per adding CPUs to the guest, after a reboot the VM will be running with the
|
||||
|
||||
### ACPI method
|
||||
|
||||
Extra memory can be added from a running Cloud Hypervisor instance. This is controlled by two mechanisms:
|
||||
Extra memory can be added from a running `cloud-hypervisor` instance. This is controlled by two mechanisms:
|
||||
|
||||
1. Allocating some of the guest physical address space for hotplug memory.
|
||||
2. Making a HTTP API request to the VMM to ask for a new amount of RAM to be assigned to the VM. In the case of expanding the memory for the VM the new memory will be hotplugged into the running VM, if reducing the size of the memory then change will take effect after the next reboot.
|
||||
@@ -157,7 +157,7 @@ The same API can also be used to reduce the desired RAM for a VM. It is importan
|
||||
|
||||
## PCI Device Hot Plug
|
||||
|
||||
Extra PCI devices can be added and removed from a running Cloud Hypervisor instance. This is controlled by making a HTTP API request to the VMM to ask for the additional device to be added, or for the existing device to be removed.
|
||||
Extra PCI devices can be added and removed from a running `cloud-hypervisor` instance. This is controlled by making a HTTP API request to the VMM to ask for the additional device to be added, or for the existing device to be removed.
|
||||
|
||||
Note: On AArch64 platform, PCI device hotplug can only be achieved using ACPI. Please refer to the [documentation](arm64.md#uefi-booting) for more information.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Intel SGX
|
||||
|
||||
Intel® Software Guard Extensions (Intel® SGX) is an Intel technology designed
|
||||
to increase the security of application code and data. Cloud-Hypervisor supports
|
||||
to increase the security of application code and data. Cloud Hypervisor supports
|
||||
SGX virtualization through KVM. Because SGX is built on hardware features that
|
||||
cannot be emulated in software, virtualizing SGX requires support in KVM and in
|
||||
the host kernel. The required Linux and KVM changes can be found in the
|
||||
@@ -17,12 +17,12 @@ For more information about SGX, please refer to the [SGX Homepage](https://softw
|
||||
For more information about SGX SDK and how to test SGX, please refer to the
|
||||
following [instructions](https://github.com/intel/linux-sgx).
|
||||
|
||||
## Cloud-Hypervisor support
|
||||
## Cloud Hypervisor support
|
||||
|
||||
Assuming the host exposes `/dev/sgx_vepc`, we can pass SGX enclaves through
|
||||
the guest.
|
||||
|
||||
In order to use SGX enclaves within a Cloud-Hypervisor VM, we must define one
|
||||
In order to use SGX enclaves within a Cloud Hypervisor VM, we must define one
|
||||
or several Enclave Page Cache (EPC) sections. Here is an example of a VM being
|
||||
created with 2 EPC sections, the first one being 64MiB with pre-allocated
|
||||
memory, the second one being 32MiB with no pre-allocated memory.
|
||||
|
||||
75
docs/intel_tdx.md
Normal file
75
docs/intel_tdx.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Intel TDX
|
||||
|
||||
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.
|
||||
|
||||
For more information about TDX technical aspects, design and specification
|
||||
please refer to the
|
||||
[TDX Homepage](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html).
|
||||
|
||||
The required Linux changes for the host side can be found in the
|
||||
[KVM TDX tree](https://github.com/intel/tdx/tree/kvm) while the changes for
|
||||
the guest side can be found in the [Guest TDX tree](https://github.com/intel/tdx/tree/guest).
|
||||
|
||||
The TDVF firmware can be found in the
|
||||
[EDK2 staging project](https://github.com/tianocore/edk2-staging/tree/TDVF).
|
||||
|
||||
## Cloud Hypervisor support
|
||||
|
||||
First, you must be running on 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).
|
||||
|
||||
Cloud Hypervisor can run TDX VM (Trust Domain) by loading the TDVF firmware,
|
||||
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).
|
||||
The firmware can be built as follows:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/tianocore/edk2-staging.git
|
||||
cd edk2-staging
|
||||
git checkout origin/TDVF
|
||||
git submodule update --init --recursive
|
||||
make -C BaseTools
|
||||
source ./edksetup.sh
|
||||
build -p OvmfPkg/OvmfCh.dsc -a X64 -t GCC5 -b RELEASE
|
||||
```
|
||||
|
||||
If debug logs are needed, here is the alternative command:
|
||||
|
||||
```bash
|
||||
build -p OvmfPkg/OvmfCh.dsc -a X64 -t GCC5 -D DEBUG_ON_SERIAL_PORT=TRUE
|
||||
```
|
||||
|
||||
On the Cloud Hypervisor side, all you need is to build the project with the
|
||||
`tdx` feature enabled:
|
||||
|
||||
```bash
|
||||
cargo build --features tdx
|
||||
```
|
||||
|
||||
And run a TDX VM by providing the firmware previously built, along with the
|
||||
guest image containing the TDX enlightened kernel. Assuming the guest kernel
|
||||
command line contains `console=hvc0` (printing to the `virtio-console` device),
|
||||
run Cloud Hypervisor as follows:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--tdx firmware=edk2-staging/Build/OvmfCh/RELEASE_GCC5/FV/OVMF.fd \
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img
|
||||
```
|
||||
|
||||
And here is the alternative command when looking for debug logs (assuming the
|
||||
guest kernel command line contains `console=ttyS0`):
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
--tdx firmware=edk2-staging/Build/OvmfCh/DEBUG_GCC5/FV/OVMF.fd \
|
||||
--cpus boot=1 \
|
||||
--memory size=1G \
|
||||
--disk path=tdx_guest_img \
|
||||
--serial tty \
|
||||
--console off
|
||||
```
|
||||
@@ -1,13 +1,13 @@
|
||||
# Memory
|
||||
|
||||
Cloud-Hypervisor has many ways to expose memory to the guest VM. This document
|
||||
aims to explain what Cloud-Hypervisor is capable of and how it can be used to
|
||||
Cloud Hypervisor has many ways to expose memory to the guest VM. This document
|
||||
aims to explain what Cloud Hypervisor is capable of and how it can be used to
|
||||
meet the needs of very different use cases.
|
||||
|
||||
## Basic Parameters
|
||||
|
||||
`MemoryConfig` or what is known as `--memory` from the CLI perspective is the
|
||||
easiest way to get started with Cloud-Hypervisor.
|
||||
easiest way to get started with Cloud Hypervisor.
|
||||
|
||||
```rust
|
||||
struct MemoryConfig {
|
||||
@@ -446,17 +446,20 @@ integer of 8 bits.
|
||||
|
||||
For instance, if one needs to attach all CPUs from 0 to 4 to a specific node,
|
||||
the syntax using `-` will help define a contiguous range with `cpus=0-4`. The
|
||||
same example could also be described with `cpus=0:1:2:3:4`.
|
||||
same example could also be described with `cpus=[0,1,2,3,4]`.
|
||||
|
||||
A combination of both `-` and `:` separators is useful when one might need to
|
||||
A combination of both `-` and `,` separators is useful when one might need to
|
||||
describe a list containing all CPUs from 0 to 99 and the CPU 255, as it could
|
||||
simply be described with `cpus=0-99:255`.
|
||||
simply be described with `cpus=[0-99,255]`.
|
||||
|
||||
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||
demarcate the list.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus boot=8
|
||||
--numa guest_numa_id=0,cpus=1-3:7 guest_numa_id=1,cpus=0:4-6
|
||||
--numa guest_numa_id=0,cpus=[1-3,7] guest_numa_id=1,cpus=[0,4-6]
|
||||
```
|
||||
|
||||
### `distances`
|
||||
@@ -473,7 +476,10 @@ node. The second value is an unsigned integer of 8 bits as it represents the
|
||||
distance between the current NUMA node and the destination NUMA node. The two
|
||||
values are separated by `@` (`value1@value2`), meaning the destination NUMA
|
||||
node `value1` is located at a distance of `value2`. Each tuple is separated
|
||||
from the others with `:` separator.
|
||||
from the others with `,` separator.
|
||||
|
||||
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||
demarcate the list.
|
||||
|
||||
For instance, if one wants to define 3 NUMA nodes, with each node located at
|
||||
different distances, it can be described with the following example.
|
||||
@@ -481,7 +487,7 @@ different distances, it can be described with the following example.
|
||||
_Example_
|
||||
|
||||
```
|
||||
--numa guest_numa_id=0,distances=1@15:2@25 guest_numa_id=1,distances=0@15:2@20 guest_numa_id=2,distances=0@25:1@20
|
||||
--numa guest_numa_id=0,distances=[1@15,2@25] guest_numa_id=1,distances=[0@15,2@20] guest_numa_id=2,distances=[0@25,1@20]
|
||||
```
|
||||
|
||||
### `memory_zones`
|
||||
@@ -498,7 +504,10 @@ workload run more efficiently.
|
||||
|
||||
Multiple values can be provided to define the list. Each value is a string
|
||||
referring to an existing memory zone identifier. Values are separated from
|
||||
each other with the `:` separator.
|
||||
each other with the `,` separator.
|
||||
|
||||
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||
demarcate the list.
|
||||
|
||||
Note that a memory zone must belong to a single NUMA node. The following
|
||||
configuration is incorrect, therefore not allowed:
|
||||
@@ -509,7 +518,7 @@ _Example_
|
||||
```
|
||||
--memory size=0
|
||||
--memory-zone id=mem0,size=1G id=mem1,size=1G id=mem2,size=1G
|
||||
--numa guest_numa_id=0,memory_zones=mem0:mem2 guest_numa_id=1,memory_zones=mem1
|
||||
--numa guest_numa_id=0,memory_zones=[mem0,mem2] guest_numa_id=1,memory_zones=mem1
|
||||
```
|
||||
|
||||
### `sgx_epc_sections`
|
||||
@@ -520,13 +529,16 @@ which must be seen by the guest as belonging to the NUMA node `guest_numa_id`.
|
||||
|
||||
Multiple values can be provided to define the list. Each value is a string
|
||||
referring to an existing SGX EPC section identifier. Values are separated from
|
||||
each other with the `:` separator.
|
||||
each other with the `,` separator.
|
||||
|
||||
As soon as one tries to describe a list of values, `[` and `]` must be used to
|
||||
demarcate the list.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--sgx-epc id=epc0,size=32M id=epc1,size=64M id=epc2,size=32M
|
||||
--numa guest_numa_id=0,sgx_epc_sections=epc1 guest_numa_id=1,sgx_epc_sections=epc0:epc2
|
||||
--numa guest_numa_id=0,sgx_epc_sections=epc1 guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
|
||||
```
|
||||
|
||||
### PCI bus
|
||||
|
||||
@@ -11,9 +11,9 @@ before the snapshot was performed.
|
||||
This feature is important for the project as it establishes the first step
|
||||
towards the support for live migration.
|
||||
|
||||
## Snapshot a Cloud-Hypervisor VM
|
||||
## Snapshot a Cloud Hypervisor VM
|
||||
|
||||
First thing, we must run a Cloud-Hypervisor VM:
|
||||
First thing, we must run a Cloud Hypervisor VM:
|
||||
|
||||
```bash
|
||||
./cloud-hypervisor \
|
||||
@@ -54,7 +54,7 @@ drwxr-xr-x 47 foo bar 4096 Jul 22 11:47 ../
|
||||
In this particular example, we can observe that 2 memory region files were
|
||||
created. That is explained by the size of the guest RAM, which is 4GiB in this
|
||||
case. Because it exceeds 3GiB (which is where we can find a ~1GiB memory hole),
|
||||
Cloud-Hypervisor needs 2 distinct memory regions to be created. Each memory
|
||||
Cloud Hypervisor needs 2 distinct memory regions to be created. Each memory
|
||||
region's content is stored through a dedicated file, which explains why we end
|
||||
up with 2 different files, the first one containing the guest RAM range 0-3GiB
|
||||
and the second one containing the guest RAM range 3-4GiB.
|
||||
@@ -65,7 +65,7 @@ with the correct amount of CPUs, RAM, and other expected devices. The state
|
||||
bits are used to restore each component in the state it was left before the
|
||||
snapshot occurred.
|
||||
|
||||
## Restore a Cloud-Hypervisor VM
|
||||
## Restore a Cloud Hypervisor VM
|
||||
|
||||
Given that one has access to an existing snapshot in `/home/foo/snapshot`,
|
||||
it is possible to create a new VM based on this snapshot with the following
|
||||
|
||||
@@ -45,7 +45,9 @@ Use SPDK: https://github.com/spdk/spdk
|
||||
|
||||
Compile with `./configure --with-vfio-user`
|
||||
|
||||
Create an NVMe controller listening on a vfio-user socket with a simple block device:
|
||||
Create an NVMe controller listening on a vfio-user socket with a simple AIO block device in spdk.
|
||||
More details of configuring SPDK bdev can be viewed in [SPDK bdev](https://spdk.io/doc/bdev.html).
|
||||
More details of setting SPDK NVMe-oF target can be viewed in [SDPK NVMe-oF tgt](https://spdk.io/doc/nvmf.html).
|
||||
|
||||
```sh
|
||||
sudo scripts/setup.sh
|
||||
|
||||
54
docs/vfio.md
54
docs/vfio.md
@@ -36,14 +36,22 @@ Now that we have identified the device, we must unbind it from its native driver
|
||||
First we add VFIO support to the host:
|
||||
|
||||
```
|
||||
$ sudo modprobe vfio_pci
|
||||
$ sudo modprobe vfio_iommu_type1 allow_unsafe_interrupts
|
||||
# modprobe -r vfio_pci
|
||||
# modprobe -r vfio_iommu_type1
|
||||
# modprobe vfio_iommu_type1 allow_unsafe_interrupts
|
||||
# modprobe vfio_pci
|
||||
```
|
||||
|
||||
In case the VFIO drivers are built-in, enable unsafe interrupts with:
|
||||
|
||||
```
|
||||
# echo 1 > /sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts
|
||||
```
|
||||
|
||||
Then we unbind it from its native driver:
|
||||
|
||||
```
|
||||
$ echo 0000:01:00.0 > /sys/bus/pci/devices/0000\:01\:00.0/driver/unbind
|
||||
# echo 0000:01:00.0 > /sys/bus/pci/devices/0000\:01\:00.0/driver/unbind
|
||||
```
|
||||
|
||||
And finally we bind it to the VFIO driver. To do that we first need to get the
|
||||
@@ -53,8 +61,14 @@ device's VID (Vendor ID) and PID (Product ID):
|
||||
$ lspci -n -s 01:00.0
|
||||
01:00.0 ff00: 10ec:525a (rev 01)
|
||||
|
||||
$ echo 10ec 525a > /sys/bus/pci/drivers/vfio-pci/new_id
|
||||
$ echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
|
||||
# echo 10ec 525a > /sys/bus/pci/drivers/vfio-pci/new_id
|
||||
```
|
||||
|
||||
If you have more than one device with the same `vendorID`/`deviceID`, starting
|
||||
with the second device, the binding is performed as follows:
|
||||
|
||||
```
|
||||
# echo 0000:02:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
|
||||
```
|
||||
|
||||
Now the device is managed by the VFIO framework.
|
||||
@@ -79,3 +93,33 @@ takes the device's sysfs path as an argument. In our example it is
|
||||
The guest kernel will then detect the card reader on its PCI bus and provided
|
||||
that support for this device is enabled, it will probe and enable it for the
|
||||
guest to use.
|
||||
|
||||
In case you want to pass multiple devices, here is the correct syntax:
|
||||
|
||||
```
|
||||
--device path=/sys/bus/pci/devices/0000:01:00.0/ path=/sys/bus/pci/devices/0000:02:00.0/
|
||||
```
|
||||
|
||||
### Multiple devices in the same IOMMU group
|
||||
|
||||
There are cases where multiple devices can be found under the same IOMMU group.
|
||||
This happens often with graphics card embedding an audio controller.
|
||||
|
||||
```
|
||||
$ lspci
|
||||
[...]
|
||||
01:00.0 VGA compatible controller: NVIDIA Corporation GK208B [GeForce GT 710] (rev a1)
|
||||
01:00.1 Audio device: NVIDIA Corporation GK208 HDMI/DP Audio Controller (rev a1)
|
||||
[...]
|
||||
```
|
||||
|
||||
This is usually exposed as follows through `sysfs`:
|
||||
|
||||
```
|
||||
$ ls /sys/kernel/iommu_groups/22/devices/
|
||||
0000:01:00.0 0000:01:00.1
|
||||
```
|
||||
|
||||
This means these two devices are under the same IOMMU group 22. In such case,
|
||||
it is important to bind both devices to VFIO and pass them both through the
|
||||
VM, otherwise this could cause some functional and security issues.
|
||||
@@ -69,12 +69,16 @@ make
|
||||
```
|
||||
|
||||
### Set the SPDK environment
|
||||
|
||||
Run SPDK vhost target with 2 CPU cores, i.e., core 0 and 1.
|
||||
```bash
|
||||
sudo HUGEMEM=2048 scripts/setup.sh
|
||||
sudo ./build/bin/vhost -S /var/tmp -s 1024 -m 0x3 &
|
||||
```
|
||||
|
||||
### Create 512M block device
|
||||
|
||||
Create a 512M (first parameter) block device with 512 bytes (second parameter) block size.
|
||||
```bash
|
||||
sudo scripts/rpc.py bdev_malloc_create 512 512 -b Malloc0
|
||||
sudo scripts/rpc.py vhost_create_blk_controller --cpumask 0x1 vhost.1 Malloc0
|
||||
|
||||
@@ -5,7 +5,7 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
serde = { version = "1.0.130", features = ["rc"] }
|
||||
serde_derive = "1.0.130"
|
||||
serde_json = "1.0.68"
|
||||
serde_json = "1.0.72"
|
||||
|
||||
68
fuzz/Cargo.lock
generated
68
fuzz/Cargo.lock
generated
@@ -11,18 +11,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.44"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
||||
checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -33,15 +33,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "577b08a4acd7b99869f863c50011b01eb73424ccc798ecd996f2e24817adfca7"
|
||||
checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6df5aef5c5830360ce5218cecb8f018af3438af5686ae945094affc86fdec63"
|
||||
checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f"
|
||||
|
||||
[[package]]
|
||||
name = "arch"
|
||||
@@ -105,6 +105,7 @@ dependencies = [
|
||||
"versionize_derive",
|
||||
"vhdx",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
@@ -118,9 +119,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.71"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -130,9 +131,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
@@ -146,7 +147,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "18.0.0"
|
||||
version = "20.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -178,6 +179,7 @@ dependencies = [
|
||||
"seccompiler",
|
||||
"vhdx",
|
||||
"virtio-devices",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
@@ -321,9 +323,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dc14f9047df1873cf6942caccc7431d19c3d496ca7a0d162260c4cf0f64b76"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/rust-vmm/kvm-ioctls?branch=main#d22ef1f51852dfb055da38004e1a4fed81246f81"
|
||||
dependencies = [
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
@@ -338,9 +339,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -401,6 +402,7 @@ dependencies = [
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
@@ -440,9 +442,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.30"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -498,9 +500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
@@ -545,9 +547,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.68"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
|
||||
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -587,9 +589,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -702,7 +704,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#d51c1fad37be30c20385c55a217e2d90972ea31d"
|
||||
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=main#8f7f2210ebe71660938b05b2ed7eec9253478bc5"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -786,6 +788,7 @@ dependencies = [
|
||||
"versionize_derive",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
"vm-memory",
|
||||
@@ -794,6 +797,15 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vm-allocator"
|
||||
version = "0.1.0"
|
||||
@@ -853,6 +865,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
@@ -893,6 +906,7 @@ dependencies = [
|
||||
"vfio_user",
|
||||
"vhdx",
|
||||
"virtio-devices",
|
||||
"virtio-queue",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
"vm-memory",
|
||||
|
||||
@@ -10,12 +10,13 @@ cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
libfuzzer-sys = "0.4.2"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.2.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vm-memory = "0.6.0"
|
||||
@@ -25,6 +26,7 @@ path = ".."
|
||||
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"] }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
|
||||
@@ -15,10 +15,12 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
|
||||
use vm_virtio::Queue;
|
||||
use virtio_queue::{Queue, QueueState};
|
||||
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
|
||||
const MEM_SIZE: u64 = 256 * 1024 * 1024;
|
||||
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
|
||||
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
|
||||
@@ -73,10 +75,14 @@ fuzz_target!(|bytes| {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut q = Queue::new(QUEUE_SIZE);
|
||||
q.ready = true;
|
||||
q.size = QUEUE_SIZE / 2;
|
||||
q.max_size = QUEUE_SIZE;
|
||||
let guest_memory = GuestMemoryAtomic::new(mem);
|
||||
|
||||
let mut q = Queue::<
|
||||
GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
QueueState<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
>::new(guest_memory.clone(), QUEUE_SIZE);
|
||||
q.state.ready = true;
|
||||
q.state.size = QUEUE_SIZE / 2;
|
||||
|
||||
let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()];
|
||||
let queue_fd = queue_evts[0].as_raw_fd();
|
||||
@@ -102,7 +108,7 @@ fuzz_target!(|bytes| {
|
||||
|
||||
block
|
||||
.activate(
|
||||
GuestMemoryAtomic::new(mem),
|
||||
guest_memory,
|
||||
Arc::new(NoopVirtioInterrupt {}),
|
||||
vec![q],
|
||||
queue_evts,
|
||||
@@ -134,7 +140,7 @@ impl VirtioInterrupt for NoopVirtioInterrupt {
|
||||
fn trigger(
|
||||
&self,
|
||||
_int_type: &VirtioInterruptType,
|
||||
_queue: Option<&Queue>,
|
||||
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> std::result::Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,18 +11,18 @@ mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.44"
|
||||
anyhow = "1.0.51"
|
||||
epoll = "4.3.1"
|
||||
thiserror = "1.0.30"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
kvm-ioctls = { version = "0.10.0", optional = true }
|
||||
kvm-ioctls = { version = "0.11.0", optional = true }
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.5.0", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-bindings = {git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
||||
serde = { version = "1.0.130", features = ["rc"] }
|
||||
serde_derive = "1.0.130"
|
||||
serde_json = "1.0.68"
|
||||
serde_json = "1.0.72"
|
||||
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vmm-sys-util = { version = "0.9.0", features = ["with-serde"] }
|
||||
|
||||
|
||||
@@ -18,7 +18,18 @@ pub struct Exception<T: Debug> {
|
||||
|
||||
impl<T: Debug> Display for Exception<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Exception {:?} at IP {:#x}", self.vector, self.ip)
|
||||
write!(
|
||||
f,
|
||||
"Exception {:?} at IP {:#x}{}{}",
|
||||
self.vector,
|
||||
self.ip,
|
||||
self.error
|
||||
.map(|e| format!(": error {:x}", e))
|
||||
.unwrap_or_else(|| "".to_owned()),
|
||||
self.payload
|
||||
.map(|payload| format!(": payload {:x}", payload))
|
||||
.unwrap_or_else(|| "".to_owned())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -653,8 +653,6 @@ mod mock_vmm {
|
||||
state: Arc<Mutex<CpuState>>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for MockVmm {}
|
||||
|
||||
pub type MockResult = Result<(), EmulationError<Exception>>;
|
||||
|
||||
impl MockVmm {
|
||||
|
||||
@@ -174,6 +174,16 @@ pub enum HypervisorCpuError {
|
||||
#[error("Failed to get debug registers: {0}")]
|
||||
GetDebugRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting misc register error
|
||||
///
|
||||
#[error("Failed to set misc registers: {0}")]
|
||||
SetMiscRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting misc register error
|
||||
///
|
||||
#[error("Failed to get misc registers: {0}")]
|
||||
GetMiscRegs(#[source] anyhow::Error),
|
||||
///
|
||||
/// Write to Guest Mem
|
||||
///
|
||||
#[error("Failed to write to Guest Mem at: {0}")]
|
||||
|
||||
@@ -26,23 +26,33 @@ pub use kvm_bindings::{
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
pub use {kvm_ioctls::Cap, kvm_ioctls::Kvm};
|
||||
|
||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||
// The core register are represented by the user_pt_regs structure. Look for it in
|
||||
// arch/arm64/include/uapi/asm/ptrace.h.
|
||||
|
||||
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
||||
// an instance of that structure.
|
||||
#[macro_export]
|
||||
macro_rules! offset__of {
|
||||
($str:ty, $($field:ident)+) => ({
|
||||
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
||||
let tmp = unsafe { tmp.assume_init() };
|
||||
let base = &tmp as *const _ as usize;
|
||||
let member = &tmp.$($field)* as *const _ as usize;
|
||||
let base = tmp.as_ptr();
|
||||
|
||||
member - base
|
||||
// Avoid warnings when nesting `unsafe` blocks.
|
||||
#[allow(unused_unsafe)]
|
||||
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
|
||||
// that we don't actually read from `base` (which would be UB) nor create an intermediate
|
||||
// reference.
|
||||
let member = unsafe { core::ptr::addr_of!((*base).$($field)*) } as *const u8;
|
||||
|
||||
// Avoid warnings when nesting `unsafe` blocks.
|
||||
#[allow(unused_unsafe)]
|
||||
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
|
||||
// from offset_from are upheld.
|
||||
unsafe { member.offset_from(base as *const u8) as usize }
|
||||
});
|
||||
}
|
||||
|
||||
// Following are macros that help with getting the ID of a aarch64 core register.
|
||||
// The core register are represented by the user_pt_regs structure. Look for it in
|
||||
// arch/arm64/include/uapi/asm/ptrace.h.
|
||||
|
||||
// Get the ID of a core register
|
||||
#[macro_export]
|
||||
macro_rules! arm64_core_reg_id {
|
||||
@@ -115,9 +125,13 @@ pub fn is_system_register(regid: u64) -> bool {
|
||||
}
|
||||
|
||||
let size = regid & KVM_REG_SIZE_MASK;
|
||||
if size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64 {
|
||||
panic!("Unexpected register size for system register {}", size);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!(size != KVM_REG_SIZE_U32 && size != KVM_REG_SIZE_U64),
|
||||
"Unexpected register size for system register {}",
|
||||
size
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ use std::result;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vm_memory::Address;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
// x86_64 dependencies
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -47,9 +45,7 @@ use kvm_bindings::{
|
||||
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
||||
};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use x86_64::{
|
||||
check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters, KVM_TSS_ADDRESS,
|
||||
};
|
||||
use x86_64::{check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use x86_64::{
|
||||
CpuId, CpuIdEntry, ExtendedControlRegisters, LapicState, MsrEntries, VcpuKvmState as CpuState,
|
||||
@@ -139,6 +135,15 @@ pub struct KvmVm {
|
||||
/// vm.set/get().unwrap()
|
||||
///
|
||||
impl vm::Vm for KvmVm {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the address of the one-page region in the VM's address space.
|
||||
///
|
||||
fn set_identity_map_address(&self, address: u64) -> vm::Result<()> {
|
||||
self.fd
|
||||
.set_identity_map_address(address)
|
||||
.map_err(|e| vm::HypervisorVmError::SetIdentityMapAddress(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
@@ -238,6 +243,9 @@ impl vm::Vm for KvmVm {
|
||||
irq_routing[0].nr = entries.len() as u32;
|
||||
irq_routing[0].flags = 0;
|
||||
|
||||
// SAFETY: irq_routing initialized with entries.len() and now it is being turned into
|
||||
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
|
||||
// everything from entries.
|
||||
unsafe {
|
||||
let entries_slice: &mut [kvm_irq_routing_entry] =
|
||||
irq_routing[0].entries.as_mut_slice(entries.len());
|
||||
@@ -302,7 +310,7 @@ impl vm::Vm for KvmVm {
|
||||
region.flags = 0;
|
||||
}
|
||||
|
||||
// Safe because guest regions are guaranteed not to overlap.
|
||||
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||
unsafe {
|
||||
self.fd
|
||||
.set_user_memory_region(region)
|
||||
@@ -320,7 +328,7 @@ impl vm::Vm for KvmVm {
|
||||
|
||||
// Setting the size to 0 means "remove"
|
||||
region.memory_size = 0;
|
||||
// Safe because guest regions are guaranteed not to overlap.
|
||||
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||
unsafe {
|
||||
self.fd
|
||||
.set_user_memory_region(region)
|
||||
@@ -350,10 +358,6 @@ impl vm::Vm for KvmVm {
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn enable_split_irq(&self) -> vm::Result<()> {
|
||||
// Set TSS
|
||||
self.fd
|
||||
.set_tss_address(KVM_TSS_ADDRESS.raw_value() as usize)
|
||||
.map_err(|e| vm::HypervisorVmError::EnableSplitIrq(e.into()))?;
|
||||
// Create split irqchip
|
||||
// Only the local APIC is emulated in kernel, both PICs and IOAPIC
|
||||
// are not.
|
||||
@@ -434,7 +438,7 @@ impl vm::Vm for KvmVm {
|
||||
userspace_addr: s.userspace_addr,
|
||||
flags: KVM_MEM_LOG_DIRTY_PAGES,
|
||||
};
|
||||
// Safe because guest regions are guaranteed not to overlap.
|
||||
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||
unsafe {
|
||||
self.fd
|
||||
.set_user_memory_region(region)
|
||||
@@ -458,7 +462,7 @@ impl vm::Vm for KvmVm {
|
||||
userspace_addr: s.userspace_addr,
|
||||
flags: 0,
|
||||
};
|
||||
// Safe because guest regions are guaranteed not to overlap.
|
||||
// SAFETY: Safe because guest regions are guaranteed not to overlap.
|
||||
unsafe {
|
||||
self.fd
|
||||
.set_user_memory_region(region)
|
||||
@@ -574,6 +578,7 @@ fn tdx_command(
|
||||
metadata,
|
||||
data,
|
||||
};
|
||||
// SAFETY: FFI call. All input parameters are valid.
|
||||
let ret = unsafe {
|
||||
ioctl_with_val(
|
||||
fd,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
///
|
||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||
@@ -92,8 +91,6 @@ impl SegmentRegisterOps for SegmentRegister {
|
||||
}
|
||||
}
|
||||
|
||||
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
||||
|
||||
pub fn boot_msr_entries() -> MsrEntries {
|
||||
MsrEntries::from_entries(&[
|
||||
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||
@@ -127,6 +124,12 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||
if !kvm.check_extension(Cap::SplitIrqchip) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::SplitIrqchip));
|
||||
}
|
||||
if !kvm.check_extension(Cap::SetIdentityMapAddr) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::SetIdentityMapAddr));
|
||||
}
|
||||
if !kvm.check_extension(Cap::SetTssAddr) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::SetTssAddr));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -302,6 +302,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
|
||||
let info = x.to_ioport_info().unwrap();
|
||||
let access_info = info.access_info;
|
||||
// SAFETY: access_info is valid, otherwise we won't be here
|
||||
let len = unsafe { access_info.__bindgen_anon_1.access_size() } as usize;
|
||||
let is_write = info.header.intercept_access_type == 1;
|
||||
let port = info.port_number;
|
||||
@@ -342,12 +343,15 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if unsafe { access_info.__bindgen_anon_1.string_op() } == 1 {
|
||||
panic!("String IN/OUT not supported");
|
||||
}
|
||||
if unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1 {
|
||||
panic!("Rep IN/OUT not supported");
|
||||
}
|
||||
// SAFETY: access_info is valid, otherwise we won't be here
|
||||
assert!(
|
||||
!(unsafe { access_info.__bindgen_anon_1.string_op() } == 1),
|
||||
"String IN/OUT not supported"
|
||||
);
|
||||
assert!(
|
||||
!(unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1),
|
||||
"Rep IN/OUT not supported"
|
||||
);
|
||||
|
||||
if is_write {
|
||||
let data = (info.rax as u32).to_le_bytes();
|
||||
@@ -506,6 +510,13 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
self.set_xcrs(&state.xcrs)?;
|
||||
self.set_lapic(&state.lapic)?;
|
||||
self.set_xsave(&state.xsave)?;
|
||||
// These registers are global and needed to be set only for first VCPU
|
||||
// as Microsoft Hypervisor allows setting this regsier for only one VCPU
|
||||
if self.vp_index == 0 {
|
||||
self.fd
|
||||
.set_misc_regs(&state.misc)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetMiscRegs(e.into()))?
|
||||
}
|
||||
self.fd
|
||||
.set_debug_regs(&state.dbg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetDebugRegs(e.into()))?;
|
||||
@@ -524,10 +535,15 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
self.get_msrs(&mut msrs)?;
|
||||
let lapic = self.get_lapic()?;
|
||||
let xsave = self.get_xsave()?;
|
||||
let misc = self
|
||||
.fd
|
||||
.get_misc_regs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetMiscRegs(e.into()))?;
|
||||
let dbg = self
|
||||
.fd
|
||||
.get_debug_regs()
|
||||
.map_err(|e| cpu::HypervisorCpuError::GetDebugRegs(e.into()))?;
|
||||
|
||||
Ok(CpuState {
|
||||
msrs,
|
||||
vcpu_events,
|
||||
@@ -538,6 +554,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
lapic,
|
||||
dbg,
|
||||
xsave,
|
||||
misc,
|
||||
})
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -614,6 +631,7 @@ impl<'a> MshvEmulatorContext<'a> {
|
||||
.translate_gva(gva, flags.into())
|
||||
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
||||
|
||||
// SAFETY: r is valid, otherwise this function will have returned
|
||||
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
|
||||
match result_code {
|
||||
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(r.0),
|
||||
@@ -746,6 +764,13 @@ fn hv_state_init() -> Arc<RwLock<HvState>> {
|
||||
/// vm.set/get().unwrap()
|
||||
///
|
||||
impl vm::Vm for MshvVm {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the address of the one-page region in the VM's address space.
|
||||
///
|
||||
fn set_identity_map_address(&self, _address: u64) -> vm::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
@@ -935,6 +960,9 @@ impl vm::Vm for MshvVm {
|
||||
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
||||
msi_routing[0].nr = entries.len() as u32;
|
||||
|
||||
// SAFETY: msi_routing initialized with entries.len() and now it is being turned into
|
||||
// entries_slice with entries.len() again. It is guaranteed to be large enough to hold
|
||||
// everything from entries.
|
||||
unsafe {
|
||||
let entries_slice: &mut [mshv_msi_routing_entry] =
|
||||
msi_routing[0].entries.as_mut_slice(entries.len());
|
||||
|
||||
@@ -18,10 +18,11 @@ pub use {
|
||||
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
||||
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
||||
mshv_bindings::MsrList, mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs,
|
||||
mshv_bindings::SegmentRegister, mshv_bindings::SpecialRegisters,
|
||||
mshv_bindings::StandardRegisters, mshv_bindings::SuspendRegisters, mshv_bindings::VcpuEvents,
|
||||
mshv_bindings::XSave as Xsave, mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||
mshv_bindings::MiscRegs as MiscRegisters, mshv_bindings::MsrList,
|
||||
mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs, mshv_bindings::SegmentRegister,
|
||||
mshv_bindings::SpecialRegisters, mshv_bindings::StandardRegisters,
|
||||
mshv_bindings::SuspendRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
||||
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||
};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
@@ -35,6 +36,7 @@ pub struct VcpuMshvState {
|
||||
pub lapic: LapicState,
|
||||
pub dbg: DebugRegisters,
|
||||
pub xsave: Xsave,
|
||||
pub misc: MiscRegisters,
|
||||
}
|
||||
|
||||
impl fmt::Display for VcpuMshvState {
|
||||
|
||||
@@ -58,6 +58,11 @@ pub enum HypervisorVmError {
|
||||
#[error("Failed to create Vcpu: {0}")]
|
||||
CreateVcpu(#[source] anyhow::Error),
|
||||
///
|
||||
/// Identity map address error
|
||||
///
|
||||
#[error("Failed to set identity map address: {0}")]
|
||||
SetIdentityMapAddress(#[source] anyhow::Error),
|
||||
///
|
||||
/// TSS address error
|
||||
///
|
||||
#[error("Failed to set TSS address: {0}")]
|
||||
@@ -217,6 +222,9 @@ pub type Result<T> = std::result::Result<T, HypervisorVmError>;
|
||||
/// This crate provides a hypervisor-agnostic interfaces for Vm
|
||||
///
|
||||
pub trait Vm: Send + Sync {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Sets the address of the one-page region in the VM's address space.
|
||||
fn set_identity_map_address(&self, address: u64) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
fn set_tss_address(&self, offset: usize) -> Result<()>;
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
getrandom = "0.2"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
@@ -15,6 +15,7 @@ serde = "1.0.130"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
@@ -22,4 +23,4 @@ vmm-sys-util = "0.9.0"
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
pnet = "0.28.0"
|
||||
serde_json = "1.0.68"
|
||||
serde_json = "1.0.72"
|
||||
|
||||
@@ -12,17 +12,25 @@ use virtio_bindings::bindings::virtio_net::{
|
||||
VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
|
||||
VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK,
|
||||
};
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryError};
|
||||
use vm_virtio::Queue;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Read queue failed.
|
||||
GuestMemory(GuestMemoryError),
|
||||
/// No queue pairs number.
|
||||
NoQueuePairsDescriptor,
|
||||
/// No control header descriptor
|
||||
NoControlHeaderDescriptor,
|
||||
/// Missing the data descriptor in the chain.
|
||||
NoDataDescriptor,
|
||||
/// No status descriptor
|
||||
NoStatusDescriptor,
|
||||
/// Failed adding used index
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
/// Failed creating an iterator over the queue
|
||||
QueueIterator(virtio_queue::Error),
|
||||
/// Failed enabling notification for the queue
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -34,6 +42,7 @@ pub struct ControlHeader {
|
||||
pub cmd: u8,
|
||||
}
|
||||
|
||||
// SAFETY: ControlHeader only contains a series of integers
|
||||
unsafe impl ByteValued for ControlHeader {}
|
||||
|
||||
pub struct CtrlQueue {
|
||||
@@ -45,22 +54,26 @@ impl CtrlQueue {
|
||||
CtrlQueue { taps }
|
||||
}
|
||||
|
||||
pub fn process(&mut self, mem: &GuestMemoryMmap, queue: &mut Queue) -> Result<bool> {
|
||||
pub fn process(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> Result<bool> {
|
||||
let mut used_desc_heads = Vec::new();
|
||||
for avail_desc in queue.iter(mem) {
|
||||
let ctrl_hdr: ControlHeader =
|
||||
mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?;
|
||||
let data_desc = avail_desc
|
||||
.next_descriptor()
|
||||
.ok_or(Error::NoQueuePairsDescriptor)?;
|
||||
let status_desc = data_desc
|
||||
.next_descriptor()
|
||||
.ok_or(Error::NoStatusDescriptor)?;
|
||||
for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? {
|
||||
let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?;
|
||||
|
||||
let ctrl_hdr: ControlHeader = desc_chain
|
||||
.memory()
|
||||
.read_obj(ctrl_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?;
|
||||
let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?;
|
||||
|
||||
let ok = match u32::from(ctrl_hdr.class) {
|
||||
VIRTIO_NET_CTRL_MQ => {
|
||||
let queue_pairs = mem
|
||||
.read_obj::<u16>(data_desc.addr)
|
||||
let queue_pairs = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u16>(data_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
@@ -76,8 +89,9 @@ impl CtrlQueue {
|
||||
}
|
||||
}
|
||||
VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
|
||||
let features = mem
|
||||
.read_obj::<u64>(data_desc.addr)
|
||||
let features = desc_chain
|
||||
.memory()
|
||||
.read_obj::<u64>(data_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET {
|
||||
warn!("Unsupported command: {}", ctrl_hdr.cmd);
|
||||
@@ -102,17 +116,24 @@ impl CtrlQueue {
|
||||
}
|
||||
};
|
||||
|
||||
mem.write_obj(
|
||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||
status_desc.addr,
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
used_desc_heads.push((avail_desc.index, avail_desc.len));
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(
|
||||
if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8,
|
||||
status_desc.addr(),
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let len = ctrl_desc.len() + data_desc.len() + status_desc.len();
|
||||
used_desc_heads.push((desc_chain.head_index(), len));
|
||||
}
|
||||
|
||||
for (desc_index, len) in used_desc_heads.iter() {
|
||||
queue.add_used(mem, *desc_index, *len);
|
||||
queue.update_avail_event(mem);
|
||||
queue
|
||||
.add_used(*desc_index, *len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
queue
|
||||
.enable_notification()
|
||||
.map_err(Error::QueueEnableNotification)?;
|
||||
}
|
||||
|
||||
Ok(!used_desc_heads.is_empty())
|
||||
|
||||
@@ -59,7 +59,7 @@ pub struct VirtioNetConfig {
|
||||
pub duplex: u8,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioNetConfig {}
|
||||
|
||||
/// Create a sockaddr_in from an IPv4 address, and expose it as
|
||||
@@ -121,7 +121,7 @@ pub fn unregister_listener(
|
||||
}
|
||||
|
||||
pub fn build_net_config_space(
|
||||
mut config: &mut VirtioNetConfig,
|
||||
config: &mut VirtioNetConfig,
|
||||
mac: MacAddr,
|
||||
num_queues: usize,
|
||||
avail_features: &mut u64,
|
||||
@@ -129,7 +129,7 @@ pub fn build_net_config_space(
|
||||
config.mac.copy_from_slice(mac.get_bytes());
|
||||
*avail_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
|
||||
build_net_config_space_with_mq(&mut config, num_queues, avail_features);
|
||||
build_net_config_space_with_mq(config, num_queues, avail_features);
|
||||
}
|
||||
|
||||
pub fn build_net_config_space_with_mq(
|
||||
|
||||
@@ -161,7 +161,7 @@ mod tests {
|
||||
|
||||
let mac = MacAddr::parse_str("12:34:56:78:9a:BC").unwrap();
|
||||
|
||||
println!("parsed MAC address: {}", mac.to_string());
|
||||
println!("parsed MAC address: {}", mac);
|
||||
|
||||
let bytes = mac.get_bytes();
|
||||
assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
||||
|
||||
@@ -10,8 +10,8 @@ use std::num::Wrapping;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use vm_memory::{Bytes, GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
|
||||
use vm_virtio::Queue;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{Bytes, GuestMemory, GuestMemoryAtomic};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TxVirtio {
|
||||
@@ -35,78 +35,93 @@ impl TxVirtio {
|
||||
|
||||
pub fn process_desc_chain(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
tap: &mut Tap,
|
||||
queue: &mut Queue,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let mut retry_write = false;
|
||||
let mut rate_limit_reached = false;
|
||||
while let Some(avail_desc) = queue.iter(mem).next() {
|
||||
if rate_limit_reached {
|
||||
queue.go_to_previous_position();
|
||||
|
||||
loop {
|
||||
let used_desc_head: (u16, u32);
|
||||
let mut avail_iter = queue
|
||||
.iter()
|
||||
.map_err(NetQueuePairError::QueueIteratorFailed)?;
|
||||
|
||||
if let Some(mut desc_chain) = avail_iter.next() {
|
||||
if rate_limit_reached {
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
let mut next_desc = desc_chain.next();
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
if !desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc.addr(), desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
}
|
||||
next_desc = desc_chain.next();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::writev(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
avail_iter.go_to_previous_position();
|
||||
retry_write = true;
|
||||
break;
|
||||
}
|
||||
error!("net: tx: failed writing to tap: {}", e);
|
||||
return Err(NetQueuePairError::WriteTap(e));
|
||||
}
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
used_desc_head = (desc_chain.head_index(), len);
|
||||
|
||||
// For the sake of simplicity (similar to the RX rate limiting), we always
|
||||
// let the 'last' descriptor chain go-through even if it was over the rate
|
||||
// limit, and simply stop processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
let head_index = avail_desc.index;
|
||||
let mut next_desc = Some(avail_desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
if !desc.is_write_only() && desc.len > 0 {
|
||||
let buf = mem
|
||||
.get_slice(desc.addr, desc.len as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
}
|
||||
next_desc = desc.next_descriptor();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::writev(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
queue.go_to_previous_position();
|
||||
retry_write = true;
|
||||
break;
|
||||
}
|
||||
error!("net: tx: failed writing to tap: {}", e);
|
||||
return Err(NetQueuePairError::WriteTap(e));
|
||||
}
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
queue.add_used(mem, head_index, 0);
|
||||
queue.update_avail_event(mem);
|
||||
|
||||
// For the sake of simplicity (similar to the RX rate limiting), we always
|
||||
// let the 'last' descriptor chain go-through even if it was over the rate
|
||||
// limit, and simply stop processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
queue
|
||||
.add_used(used_desc_head.0, used_desc_head.1)
|
||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||
queue
|
||||
.enable_notification()
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?;
|
||||
}
|
||||
|
||||
Ok(retry_write)
|
||||
@@ -135,87 +150,106 @@ impl RxVirtio {
|
||||
|
||||
pub fn process_desc_chain(
|
||||
&mut self,
|
||||
mem: &GuestMemoryMmap,
|
||||
tap: &mut Tap,
|
||||
queue: &mut Queue,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
rate_limiter: &mut Option<RateLimiter>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let mut exhausted_descs = true;
|
||||
let mut rate_limit_reached = false;
|
||||
|
||||
while let Some(avail_desc) = queue.iter(mem).next() {
|
||||
if rate_limit_reached {
|
||||
exhausted_descs = false;
|
||||
queue.go_to_previous_position();
|
||||
loop {
|
||||
let used_desc_head: (u16, u32);
|
||||
let mut avail_iter = queue
|
||||
.iter()
|
||||
.map_err(NetQueuePairError::QueueIteratorFailed)?;
|
||||
|
||||
if let Some(mut desc_chain) = avail_iter.next() {
|
||||
if rate_limit_reached {
|
||||
exhausted_descs = false;
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
let desc = desc_chain
|
||||
.next()
|
||||
.ok_or(NetQueuePairError::DescriptorChainTooShort)?;
|
||||
let num_buffers_addr = desc_chain.memory().checked_offset(desc.addr(), 10).unwrap();
|
||||
let mut next_desc = Some(desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
if desc.is_write_only() && desc.len() > 0 {
|
||||
let buf = desc_chain
|
||||
.memory()
|
||||
.get_slice(desc.addr(), desc.len() as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len() as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
}
|
||||
next_desc = desc_chain.next();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::readv(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
exhausted_descs = false;
|
||||
avail_iter.go_to_previous_position();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
|
||||
error!("net: rx: failed reading from tap: {}", e);
|
||||
return Err(NetQueuePairError::ReadTap(e));
|
||||
}
|
||||
|
||||
// Write num_buffers to guest memory. We simply write 1 as we
|
||||
// never spread the frame over more than one descriptor chain.
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(1u16, num_buffers_addr)
|
||||
.map_err(NetQueuePairError::GuestMemory)?;
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
used_desc_head = (desc_chain.head_index(), len);
|
||||
|
||||
// For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
|
||||
// RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
|
||||
// chain go-through even if it was over the rate limit, and simply stop
|
||||
// processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
let head_index = avail_desc.index;
|
||||
let num_buffers_addr = mem.checked_offset(avail_desc.addr, 10).unwrap();
|
||||
let mut next_desc = Some(avail_desc);
|
||||
|
||||
let mut iovecs = Vec::new();
|
||||
while let Some(desc) = next_desc {
|
||||
if desc.is_write_only() && desc.len > 0 {
|
||||
let buf = mem
|
||||
.get_slice(desc.addr, desc.len as usize)
|
||||
.map_err(NetQueuePairError::GuestMemory)?
|
||||
.as_ptr();
|
||||
let iovec = libc::iovec {
|
||||
iov_base: buf as *mut libc::c_void,
|
||||
iov_len: desc.len as libc::size_t,
|
||||
};
|
||||
iovecs.push(iovec);
|
||||
}
|
||||
next_desc = desc.next_descriptor();
|
||||
}
|
||||
|
||||
let len = if !iovecs.is_empty() {
|
||||
let result = unsafe {
|
||||
libc::readv(
|
||||
tap.as_raw_fd() as libc::c_int,
|
||||
iovecs.as_ptr() as *const libc::iovec,
|
||||
iovecs.len() as libc::c_int,
|
||||
)
|
||||
};
|
||||
if result < 0 {
|
||||
let e = std::io::Error::last_os_error();
|
||||
exhausted_descs = false;
|
||||
queue.go_to_previous_position();
|
||||
|
||||
/* EAGAIN */
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
|
||||
error!("net: rx: failed reading from tap: {}", e);
|
||||
return Err(NetQueuePairError::ReadTap(e));
|
||||
}
|
||||
|
||||
// Write num_buffers to guest memory. We simply write 1 as we
|
||||
// never spread the frame over more than one descriptor chain.
|
||||
mem.write_obj(1u16, num_buffers_addr)
|
||||
.map_err(NetQueuePairError::GuestMemory)?;
|
||||
|
||||
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||
self.counter_frames += Wrapping(1);
|
||||
|
||||
result as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
queue.add_used(mem, head_index, len);
|
||||
queue.update_avail_event(mem);
|
||||
|
||||
// For the sake of simplicity (keeping the handling of RX_QUEUE_EVENT and
|
||||
// RX_TAP_EVENT totally asynchronous), we always let the 'last' descriptor
|
||||
// chain go-through even if it was over the rate limit, and simply stop
|
||||
// processing oncoming `avail_desc` if any.
|
||||
if let Some(rate_limiter) = rate_limiter {
|
||||
rate_limit_reached = !rate_limiter.consume(1, TokenType::Ops)
|
||||
|| !rate_limiter.consume(len as u64, TokenType::Bytes);
|
||||
}
|
||||
queue
|
||||
.add_used(used_desc_head.0, used_desc_head.1)
|
||||
.map_err(NetQueuePairError::QueueAddUsed)?;
|
||||
queue
|
||||
.enable_notification()
|
||||
.map_err(NetQueuePairError::QueueEnableNotification)?;
|
||||
}
|
||||
|
||||
Ok(exhausted_descs)
|
||||
@@ -244,10 +278,19 @@ pub enum NetQueuePairError {
|
||||
ReadTap(io::Error),
|
||||
/// Error related to guest memory
|
||||
GuestMemory(vm_memory::GuestMemoryError),
|
||||
/// Returned an error while iterating through the queue
|
||||
QueueIteratorFailed(virtio_queue::Error),
|
||||
/// Descriptor chain is too short
|
||||
DescriptorChainTooShort,
|
||||
/// Failed to determine if queue needed notification
|
||||
QueueNeedsNotification(virtio_queue::Error),
|
||||
/// Failed to enable notification on the queue
|
||||
QueueEnableNotification(virtio_queue::Error),
|
||||
/// Failed to add used index to the queue
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
}
|
||||
|
||||
pub struct NetQueuePair {
|
||||
pub mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
pub tap: Tap,
|
||||
// With epoll each FD must be unique. So in order to filter the
|
||||
// events we need to get a second FD responding to the original
|
||||
@@ -268,19 +311,13 @@ pub struct NetQueuePair {
|
||||
}
|
||||
|
||||
impl NetQueuePair {
|
||||
pub fn process_tx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
||||
let mem = self
|
||||
.mem
|
||||
.as_ref()
|
||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
||||
.map(|m| m.memory())?;
|
||||
|
||||
let tx_tap_retry = self.tx.process_desc_chain(
|
||||
&mem,
|
||||
&mut self.tap,
|
||||
&mut queue,
|
||||
&mut self.tx_rate_limiter,
|
||||
)?;
|
||||
pub fn process_tx(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
let tx_tap_retry =
|
||||
self.tx
|
||||
.process_desc_chain(&mut self.tap, queue, &mut self.tx_rate_limiter)?;
|
||||
|
||||
// We got told to try again when writing to the tap. Wait for the TAP to be writable
|
||||
if tx_tap_retry && !self.tx_tap_listening {
|
||||
@@ -314,22 +351,19 @@ impl NetQueuePair {
|
||||
self.tx.counter_bytes = Wrapping(0);
|
||||
self.tx.counter_frames = Wrapping(0);
|
||||
|
||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
||||
queue
|
||||
.needs_notification()
|
||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||
}
|
||||
|
||||
pub fn process_rx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
||||
let mem = self
|
||||
.mem
|
||||
.as_ref()
|
||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
||||
.map(|m| m.memory())?;
|
||||
|
||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
||||
&mem,
|
||||
&mut self.tap,
|
||||
&mut queue,
|
||||
&mut self.rx_rate_limiter,
|
||||
)?;
|
||||
pub fn process_rx(
|
||||
&mut self,
|
||||
queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> Result<bool, NetQueuePairError> {
|
||||
self.rx_desc_avail =
|
||||
!self
|
||||
.rx
|
||||
.process_desc_chain(&mut self.tap, queue, &mut self.rx_rate_limiter)?;
|
||||
let rate_limit_reached = self
|
||||
.rx_rate_limiter
|
||||
.as_ref()
|
||||
@@ -358,6 +392,8 @@ impl NetQueuePair {
|
||||
self.rx.counter_bytes = Wrapping(0);
|
||||
self.rx.counter_frames = Wrapping(0);
|
||||
|
||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
||||
queue
|
||||
.needs_notification()
|
||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -37,6 +38,32 @@ impl fmt::Display for OptionParserError {
|
||||
}
|
||||
type OptionParserResult<T> = std::result::Result<T, OptionParserError>;
|
||||
|
||||
fn split_commas_outside_brackets(s: &str) -> OptionParserResult<Vec<String>> {
|
||||
let mut list: Vec<String> = Vec::new();
|
||||
let mut opened_brackets: usize = 0;
|
||||
for element in s.trim().split(',') {
|
||||
if opened_brackets > 0 {
|
||||
if let Some(last) = list.last_mut() {
|
||||
*last = format!("{},{}", last, element);
|
||||
} else {
|
||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||
}
|
||||
} else {
|
||||
list.push(element.to_string());
|
||||
}
|
||||
|
||||
opened_brackets += element.matches('[').count();
|
||||
let closing_brackets = element.matches(']').count();
|
||||
if closing_brackets > opened_brackets {
|
||||
return Err(OptionParserError::InvalidSyntax(s.to_owned()));
|
||||
} else {
|
||||
opened_brackets -= closing_brackets;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
impl OptionParser {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -49,9 +76,7 @@ impl OptionParser {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let options_list: Vec<&str> = input.trim().split(',').collect();
|
||||
|
||||
for option in options_list.iter() {
|
||||
for option in split_commas_outside_brackets(input)?.iter() {
|
||||
let parts: Vec<&str> = option.split('=').collect();
|
||||
|
||||
match self.options.get_mut(parts[0]) {
|
||||
@@ -183,7 +208,11 @@ impl FromStr for IntegerList {
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let mut integer_list = Vec::new();
|
||||
let ranges_list: Vec<&str> = s.trim().split(':').collect();
|
||||
let ranges_list: Vec<&str> = s
|
||||
.trim()
|
||||
.trim_matches(|c| c == '[' || c == ']')
|
||||
.split(',')
|
||||
.collect();
|
||||
|
||||
for range in ranges_list.iter() {
|
||||
let items: Vec<&str> = range.split('-').collect();
|
||||
@@ -216,39 +245,71 @@ impl FromStr for IntegerList {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TupleTwoIntegers(pub Vec<(u64, u64)>);
|
||||
|
||||
pub enum TupleTwoIntegersParseError {
|
||||
InvalidValue(String),
|
||||
pub trait TupleValue {
|
||||
fn parse_value(input: &str) -> Result<Self, TupleError>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl FromStr for TupleTwoIntegers {
|
||||
type Err = TupleTwoIntegersParseError;
|
||||
impl TupleValue for u64 {
|
||||
fn parse_value(input: &str) -> Result<Self, TupleError> {
|
||||
input.parse::<u64>().map_err(TupleError::InvalidInteger)
|
||||
}
|
||||
}
|
||||
|
||||
impl TupleValue for Vec<u8> {
|
||||
fn parse_value(input: &str) -> Result<Self, TupleError> {
|
||||
Ok(IntegerList::from_str(input)
|
||||
.map_err(TupleError::InvalidIntegerList)?
|
||||
.0
|
||||
.iter()
|
||||
.map(|v| *v as u8)
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl TupleValue for Vec<u64> {
|
||||
fn parse_value(input: &str) -> Result<Self, TupleError> {
|
||||
Ok(IntegerList::from_str(input)
|
||||
.map_err(TupleError::InvalidIntegerList)?
|
||||
.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tuple<S, T>(pub Vec<(S, T)>);
|
||||
|
||||
pub enum TupleError {
|
||||
InvalidValue(String),
|
||||
SplitOutsideBrackets(OptionParserError),
|
||||
InvalidIntegerList(IntegerListParseError),
|
||||
InvalidInteger(ParseIntError),
|
||||
}
|
||||
|
||||
impl<S: FromStr, T: TupleValue> FromStr for Tuple<S, T> {
|
||||
type Err = TupleError;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let mut list = Vec::new();
|
||||
let tuples_list: Vec<&str> = s.trim().split(':').collect();
|
||||
let mut list: Vec<(S, T)> = Vec::new();
|
||||
|
||||
let tuples_list =
|
||||
split_commas_outside_brackets(s.trim().trim_matches(|c| c == '[' || c == ']'))
|
||||
.map_err(TupleError::SplitOutsideBrackets)?;
|
||||
for tuple in tuples_list.iter() {
|
||||
let items: Vec<&str> = tuple.split('@').collect();
|
||||
|
||||
if items.len() != 2 {
|
||||
return Err(TupleTwoIntegersParseError::InvalidValue(
|
||||
(*tuple).to_string(),
|
||||
));
|
||||
return Err(TupleError::InvalidValue((*tuple).to_string()));
|
||||
}
|
||||
|
||||
let item1 = items[0]
|
||||
.parse::<u64>()
|
||||
.map_err(|_| TupleTwoIntegersParseError::InvalidValue(items[0].to_owned()))?;
|
||||
let item2 = items[1]
|
||||
.parse::<u64>()
|
||||
.map_err(|_| TupleTwoIntegersParseError::InvalidValue(items[1].to_owned()))?;
|
||||
.parse::<S>()
|
||||
.map_err(|_| TupleError::InvalidValue(items[0].to_owned()))?;
|
||||
let item2 = TupleValue::parse_value(items[1])?;
|
||||
|
||||
list.push((item1, item2));
|
||||
}
|
||||
|
||||
Ok(TupleTwoIntegers(list))
|
||||
Ok(Tuple(list))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +323,12 @@ impl FromStr for StringList {
|
||||
type Err = StringListParseError;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let string_list: Vec<String> = s.trim().split(':').map(|e| e.to_owned()).collect();
|
||||
let string_list: Vec<String> = s
|
||||
.trim()
|
||||
.trim_matches(|c| c == '[' || c == ']')
|
||||
.split(',')
|
||||
.map(|e| e.to_owned())
|
||||
.collect();
|
||||
|
||||
Ok(StringList(string_list))
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.44"
|
||||
anyhow = "1.0.51"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "main", default-features = false }
|
||||
vfio_user = { path = "../vfio_user" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
thiserror = "1.0.30"
|
||||
versionize = "0.1.6"
|
||||
|
||||
@@ -140,12 +140,8 @@ impl PciBus {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_device(
|
||||
&mut self,
|
||||
pci_device_bdf: u32,
|
||||
device: Arc<Mutex<dyn PciDevice>>,
|
||||
) -> Result<()> {
|
||||
self.devices.insert(pci_device_bdf >> 3, device);
|
||||
pub fn add_device(&mut self, device_id: u32, device: Arc<Mutex<dyn PciDevice>>) -> Result<()> {
|
||||
self.devices.insert(device_id, device);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -961,7 +961,7 @@ mod tests {
|
||||
foo: u8,
|
||||
}
|
||||
|
||||
// It is safe to implement BytesValued; all members are simple numbers and any value is valid.
|
||||
// SAFETY: All members are simple numbers and any value is valid.
|
||||
unsafe impl ByteValued for TestCap {}
|
||||
|
||||
impl PciCapability for TestCap {
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::any::Any;
|
||||
use std::fmt::{self, Display};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::{self, io, result};
|
||||
use vm_allocator::SystemAllocator;
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::{GuestAddress, GuestUsize};
|
||||
|
||||
@@ -52,12 +52,17 @@ pub trait PciDevice: BusDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
_allocator: &mut SystemAllocator,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Frees the PCI BARs previously allocated with a call to allocate_bars().
|
||||
fn free_bars(&mut self, _allocator: &mut SystemAllocator) -> Result<()> {
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
_allocator: &mut SystemAllocator,
|
||||
_mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ pub use self::msi::{msi_num_enabled_vectors, MsiCap, MsiConfig};
|
||||
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE};
|
||||
pub use self::vfio::{VfioPciDevice, VfioPciError};
|
||||
pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError};
|
||||
use std::fmt::Display;
|
||||
|
||||
/// PCI has four interrupt pins A->D.
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -47,3 +48,76 @@ impl PciInterruptPin {
|
||||
pub const PCI_CONFIG_IO_PORT: u64 = 0xcf8;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const PCI_CONFIG_IO_PORT_SIZE: u64 = 0x8;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct PciBdf(u32);
|
||||
|
||||
impl PciBdf {
|
||||
pub fn segment(&self) -> u16 {
|
||||
((self.0 >> 16) & 0xffff) as u16
|
||||
}
|
||||
|
||||
pub fn bus(&self) -> u8 {
|
||||
((self.0 >> 8) & 0xff) as u8
|
||||
}
|
||||
|
||||
pub fn device(&self) -> u8 {
|
||||
((self.0 >> 3) & 0x1f) as u8
|
||||
}
|
||||
|
||||
pub fn function(&self) -> u8 {
|
||||
(self.0 & 0x7) as u8
|
||||
}
|
||||
|
||||
pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> Self {
|
||||
Self(
|
||||
(segment as u32) << 16
|
||||
| (bus as u32) << 8
|
||||
| ((device & 0x1f) as u32) << 3
|
||||
| (function & 0x7) as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for PciBdf {
|
||||
fn from(bdf: u32) -> Self {
|
||||
Self(bdf)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PciBdf> for u32 {
|
||||
fn from(bdf: PciBdf) -> Self {
|
||||
bdf.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PciBdf> for u32 {
|
||||
fn from(bdf: &PciBdf) -> Self {
|
||||
bdf.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PciBdf> for u16 {
|
||||
fn from(bdf: PciBdf) -> Self {
|
||||
(bdf.0 & 0xffff) as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PciBdf> for u16 {
|
||||
fn from(bdf: &PciBdf) -> Self {
|
||||
(bdf.0 & 0xffff) as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PciBdf {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:04x}:{:02x}:{:02x}.{:01x}",
|
||||
self.segment(),
|
||||
self.bus(),
|
||||
self.device(),
|
||||
self.function()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ pub struct MsixCap {
|
||||
pub pba: u32,
|
||||
}
|
||||
|
||||
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
|
||||
// SAFETY: All members are simple numbers and any value is valid.
|
||||
unsafe impl ByteValued for MsixCap {}
|
||||
|
||||
impl PciCapability for MsixCap {
|
||||
@@ -533,4 +533,16 @@ impl MsixCap {
|
||||
pub fn table_size(&self) -> u16 {
|
||||
(self.msg_ctl & 0x7ff) + 1
|
||||
}
|
||||
|
||||
pub fn table_range(&self) -> (u64, u64) {
|
||||
// The table takes 16 bytes per entry.
|
||||
let size = self.table_size() as u64 * 16;
|
||||
(self.table_offset() as u64, size)
|
||||
}
|
||||
|
||||
pub fn pba_range(&self) -> (u64, u64) {
|
||||
// The table takes 1 bit per entry modulo 8 bytes.
|
||||
let size = ((self.table_size() as u64 / 64) + 1) * 8;
|
||||
(self.pba_offset() as u64, size)
|
||||
}
|
||||
}
|
||||
|
||||
263
pci/src/vfio.rs
263
pci/src/vfio.rs
@@ -11,14 +11,15 @@ use crate::{
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use hypervisor::HypervisorVmError;
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use thiserror::Error;
|
||||
use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::{VfioContainer, VfioDevice, VfioIrq};
|
||||
use vm_allocator::SystemAllocator;
|
||||
use vfio_ioctls::{VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
};
|
||||
@@ -209,6 +210,14 @@ impl Interrupt {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct UserMemoryRegion {
|
||||
slot: u32,
|
||||
start: u64,
|
||||
size: u64,
|
||||
host_addr: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MmioRegion {
|
||||
pub start: GuestAddress,
|
||||
pub length: GuestUsize,
|
||||
@@ -217,6 +226,7 @@ pub struct MmioRegion {
|
||||
pub(crate) mem_slot: Option<u32>,
|
||||
pub(crate) host_addr: Option<u64>,
|
||||
pub(crate) mmap_size: Option<usize>,
|
||||
pub(crate) user_memory_regions: Vec<UserMemoryRegion>,
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VfioError {
|
||||
@@ -351,6 +361,7 @@ impl VfioCommon {
|
||||
pub(crate) fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
vfio_wrapper: &dyn Vfio,
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||
let mut ranges = Vec::new();
|
||||
@@ -448,8 +459,8 @@ impl VfioCommon {
|
||||
region_size = (!combined_size + 1) as u64;
|
||||
|
||||
// BAR allocation must be naturally aligned
|
||||
bar_addr = allocator
|
||||
.allocate_mmio_addresses(None, region_size, Some(region_size))
|
||||
bar_addr = mmio_allocator
|
||||
.allocate(None, region_size, Some(region_size))
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
|
||||
} else {
|
||||
// Mask out flag bits (lowest 4 for memory bars)
|
||||
@@ -501,6 +512,7 @@ impl VfioCommon {
|
||||
mem_slot: None,
|
||||
host_addr: None,
|
||||
mmap_size: None,
|
||||
user_memory_regions: Vec::new(),
|
||||
});
|
||||
|
||||
bar_id += 1;
|
||||
@@ -515,6 +527,7 @@ impl VfioCommon {
|
||||
pub(crate) fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
for region in self.mmio_regions.iter() {
|
||||
match region.type_ {
|
||||
@@ -528,7 +541,7 @@ impl VfioCommon {
|
||||
allocator.free_mmio_hole_addresses(region.start, region.length);
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
allocator.free_mmio_addresses(region.start, region.length);
|
||||
mmio_allocator.free(region.start, region.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -786,7 +799,7 @@ impl VfioCommon {
|
||||
if addr >= region.start.raw_value()
|
||||
&& addr < region.start.unchecked_add(region.length).raw_value()
|
||||
{
|
||||
return Some(*region);
|
||||
return Some(region.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
@@ -999,6 +1012,85 @@ impl VfioPciDevice {
|
||||
self.iommu_attached
|
||||
}
|
||||
|
||||
fn align_4k(address: u64) -> u64 {
|
||||
(address + 0xfff) & 0xffff_ffff_ffff_f000
|
||||
}
|
||||
|
||||
fn is_4k_aligned(address: u64) -> bool {
|
||||
(address & 0xfff) == 0
|
||||
}
|
||||
|
||||
fn is_4k_multiple(size: u64) -> bool {
|
||||
(size & 0xfff) == 0
|
||||
}
|
||||
|
||||
fn generate_user_memory_regions<F>(
|
||||
region_index: u32,
|
||||
region_start: u64,
|
||||
region_size: u64,
|
||||
host_addr: u64,
|
||||
mem_slot: F,
|
||||
vfio_msix: Option<&VfioMsix>,
|
||||
) -> Vec<UserMemoryRegion>
|
||||
where
|
||||
F: Fn() -> u32,
|
||||
{
|
||||
if !Self::is_4k_aligned(region_start) {
|
||||
error!(
|
||||
"Region start address 0x{:x} must be at least aligned on 4KiB",
|
||||
region_start
|
||||
);
|
||||
}
|
||||
if !Self::is_4k_multiple(region_size) {
|
||||
error!(
|
||||
"Region size 0x{:x} must be at least a multiple of 4KiB",
|
||||
region_size
|
||||
);
|
||||
}
|
||||
|
||||
// Using a BtreeMap as the list provided through the iterator is sorted
|
||||
// by key. This ensures proper split of the whole region.
|
||||
let mut inter_ranges = BTreeMap::new();
|
||||
if let Some(msix) = vfio_msix {
|
||||
if region_index == msix.cap.table_bir() {
|
||||
let (offset, size) = msix.cap.table_range();
|
||||
let base = region_start + offset;
|
||||
inter_ranges.insert(base, size);
|
||||
}
|
||||
if region_index == msix.cap.pba_bir() {
|
||||
let (offset, size) = msix.cap.pba_range();
|
||||
let base = region_start + offset;
|
||||
inter_ranges.insert(base, size);
|
||||
}
|
||||
}
|
||||
|
||||
let mut user_memory_regions = Vec::new();
|
||||
let mut new_start = region_start;
|
||||
for (range_start, range_size) in inter_ranges {
|
||||
if range_start > new_start {
|
||||
user_memory_regions.push(UserMemoryRegion {
|
||||
slot: mem_slot(),
|
||||
start: new_start,
|
||||
size: range_start - new_start,
|
||||
host_addr: host_addr + new_start - region_start,
|
||||
});
|
||||
}
|
||||
|
||||
new_start = Self::align_4k(range_start + range_size);
|
||||
}
|
||||
|
||||
if region_start + region_size > new_start {
|
||||
user_memory_regions.push(UserMemoryRegion {
|
||||
slot: mem_slot(),
|
||||
start: new_start,
|
||||
size: region_start + region_size - new_start,
|
||||
host_addr: host_addr + new_start - region_start,
|
||||
});
|
||||
}
|
||||
|
||||
user_memory_regions
|
||||
}
|
||||
|
||||
/// Map MMIO regions into the guest, and avoid VM exits when the guest tries
|
||||
/// to reach those regions.
|
||||
///
|
||||
@@ -1018,16 +1110,6 @@ impl VfioPciDevice {
|
||||
let fd = self.device.as_raw_fd();
|
||||
|
||||
for region in self.common.mmio_regions.iter_mut() {
|
||||
// We want to skip the mapping of the BAR containing the MSI-X
|
||||
// table even if it is mappable. The reason is we need to trap
|
||||
// any access to the MSI-X table and update the GSI routing
|
||||
// accordingly.
|
||||
if let Some(msix) = &self.common.interrupt.msix {
|
||||
if region.index == msix.cap.table_bir() || region.index == msix.cap.pba_bir() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let region_flags = self.device.get_region_flags(region.index);
|
||||
if region_flags & VFIO_REGION_INFO_FLAG_MMAP != 0 {
|
||||
let mut prot = 0;
|
||||
@@ -1037,8 +1119,27 @@ impl VfioPciDevice {
|
||||
if region_flags & VFIO_REGION_INFO_FLAG_WRITE != 0 {
|
||||
prot |= libc::PROT_WRITE;
|
||||
}
|
||||
let (mmap_offset, mmap_size) = self.device.get_region_mmap(region.index);
|
||||
let offset = self.device.get_region_offset(region.index) + mmap_offset;
|
||||
|
||||
// Retrieve the list of capabilities found on the region
|
||||
let caps = if region_flags & VFIO_REGION_INFO_FLAG_CAPS != 0 {
|
||||
self.device.get_region_caps(region.index)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// Don't try to mmap the region if it contains MSI-X table or
|
||||
// MSI-X PBA subregion, and if we couldn't find MSIX_MAPPABLE
|
||||
// in the list of supported capabilities.
|
||||
if let Some(msix) = self.common.interrupt.msix.as_ref() {
|
||||
if (region.index == msix.cap.table_bir() || region.index == msix.cap.pba_bir())
|
||||
&& !caps.contains(&VfioRegionInfoCap::MsixMappable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mmap_size = self.device.get_region_size(region.index);
|
||||
let offset = self.device.get_region_offset(region.index);
|
||||
|
||||
let host_addr = unsafe {
|
||||
libc::mmap(
|
||||
@@ -1053,29 +1154,44 @@ impl VfioPciDevice {
|
||||
|
||||
if host_addr == libc::MAP_FAILED {
|
||||
error!(
|
||||
"Could not mmap regions, error:{}",
|
||||
"Could not mmap region index {}: {}",
|
||||
region.index,
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let slot = mem_slot();
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
slot,
|
||||
region.start.raw_value() + mmap_offset,
|
||||
mmap_size as u64,
|
||||
// In case the region that is being mapped contains the MSI-X
|
||||
// vectors table or the MSI-X PBA table, we must adjust what
|
||||
// is being declared through the hypervisor. We want to make
|
||||
// sure we will still trap MMIO accesses to these MSI-X
|
||||
// specific ranges.
|
||||
let user_memory_regions = Self::generate_user_memory_regions(
|
||||
region.index,
|
||||
region.start.raw_value(),
|
||||
mmap_size,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
&mem_slot,
|
||||
self.common.interrupt.msix.as_ref(),
|
||||
);
|
||||
for user_memory_region in user_memory_regions.iter() {
|
||||
let mem_region = vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
vm.create_user_memory_region(mem_region)
|
||||
.map_err(VfioPciError::MapRegionGuest)?;
|
||||
vm.create_user_memory_region(mem_region)
|
||||
.map_err(VfioPciError::MapRegionGuest)?;
|
||||
}
|
||||
|
||||
// Update the region with memory mapped info.
|
||||
region.mem_slot = Some(slot);
|
||||
region.host_addr = Some(host_addr as u64);
|
||||
region.mmap_size = Some(mmap_size as usize);
|
||||
region.user_memory_regions = user_memory_regions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1084,17 +1200,13 @@ impl VfioPciDevice {
|
||||
|
||||
pub fn unmap_mmio_regions(&mut self) {
|
||||
for region in self.common.mmio_regions.iter() {
|
||||
if let (Some(host_addr), Some(mmap_size), Some(mem_slot)) =
|
||||
(region.host_addr, region.mmap_size, region.mem_slot)
|
||||
{
|
||||
let (mmap_offset, _) = self.device.get_region_mmap(region.index);
|
||||
|
||||
for user_memory_region in region.user_memory_regions.iter() {
|
||||
// Remove region
|
||||
let r = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
region.start.raw_value() + mmap_offset,
|
||||
mmap_size as u64,
|
||||
host_addr as u64,
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
@@ -1102,7 +1214,9 @@ impl VfioPciDevice {
|
||||
if let Err(e) = self.vm.remove_user_memory_region(r) {
|
||||
error!("Could not remove the userspace memory region: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(host_addr), Some(mmap_size)) = (region.host_addr, region.mmap_size) {
|
||||
let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, mmap_size) };
|
||||
if ret != 0 {
|
||||
error!(
|
||||
@@ -1195,12 +1309,18 @@ impl PciDevice for VfioPciDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||
self.common.allocate_bars(allocator, &self.vfio_wrapper)
|
||||
self.common
|
||||
.allocate_bars(allocator, mmio_allocator, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn free_bars(&mut self, allocator: &mut SystemAllocator) -> Result<(), PciDeviceError> {
|
||||
self.common.free_bars(allocator)
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
self.common.free_bars(allocator, mmio_allocator)
|
||||
}
|
||||
|
||||
fn write_config_register(
|
||||
@@ -1242,38 +1362,41 @@ impl PciDevice for VfioPciDevice {
|
||||
if region.start.raw_value() == old_base {
|
||||
region.start = GuestAddress(new_base);
|
||||
|
||||
if let Some(mem_slot) = region.mem_slot {
|
||||
if let Some(host_addr) = region.host_addr {
|
||||
let (mmap_offset, mmap_size) = self.device.get_region_mmap(region.index);
|
||||
for user_memory_region in region.user_memory_regions.iter_mut() {
|
||||
// Remove old region
|
||||
let old_mem_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
// Remove old region
|
||||
let old_mem_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
old_base + mmap_offset,
|
||||
mmap_size as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
self.vm
|
||||
.remove_user_memory_region(old_mem_region)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
self.vm
|
||||
.remove_user_memory_region(old_mem_region)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
// Insert new region
|
||||
let new_mem_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
new_base + mmap_offset,
|
||||
mmap_size as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_mem_region)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
// Update the user memory region with the correct start address.
|
||||
if new_base > old_base {
|
||||
user_memory_region.start += new_base - old_base;
|
||||
} else {
|
||||
user_memory_region.start -= old_base - new_base;
|
||||
}
|
||||
|
||||
// Insert new region
|
||||
let new_mem_region = self.vm.make_user_memory_region(
|
||||
user_memory_region.slot,
|
||||
user_memory_region.start,
|
||||
user_memory_region.size,
|
||||
user_memory_region.host_addr,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_mem_region)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use thiserror::Error;
|
||||
use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::VfioIrq;
|
||||
use vfio_user::{Client, Error as VfioUserError};
|
||||
use vm_allocator::SystemAllocator;
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig};
|
||||
use vm_device::BusDevice;
|
||||
@@ -116,215 +116,7 @@ impl VfioUserPciDevice {
|
||||
common,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for VfioUserPciDevice {
|
||||
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.read_bar(base, offset, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.write_bar(base, offset, data)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[allow(dead_code)]
|
||||
enum Regions {
|
||||
Bar0,
|
||||
Bar1,
|
||||
Bar2,
|
||||
Bar3,
|
||||
Bar4,
|
||||
Bar5,
|
||||
Rom,
|
||||
Config,
|
||||
Vga,
|
||||
Migration,
|
||||
}
|
||||
|
||||
struct VfioUserClientWrapper {
|
||||
client: Arc<Mutex<Client>>,
|
||||
}
|
||||
|
||||
impl Vfio for VfioUserClientWrapper {
|
||||
fn region_read(&self, index: u32, offset: u64, data: &mut [u8]) {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.region_read(index, offset, data)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn region_write(&self, index: u32, offset: u64, data: &[u8]) {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.region_write(index, offset, data)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn get_irq_info(&self, irq_index: u32) -> Option<VfioIrq> {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_irq_info(irq_index)
|
||||
.ok()
|
||||
.map(|i| VfioIrq {
|
||||
index: i.index,
|
||||
flags: i.flags,
|
||||
count: i.count,
|
||||
})
|
||||
}
|
||||
|
||||
fn enable_irq(&self, irq_index: u32, event_fds: Vec<&EventFd>) -> Result<(), VfioError> {
|
||||
info!(
|
||||
"Enabling IRQ {:x} number of fds = {:?}",
|
||||
irq_index,
|
||||
event_fds.len()
|
||||
);
|
||||
let fds: Vec<i32> = event_fds.iter().map(|e| e.as_raw_fd()).collect();
|
||||
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_irqs(
|
||||
irq_index,
|
||||
VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
|
||||
0,
|
||||
event_fds.len() as u32,
|
||||
&fds,
|
||||
)
|
||||
.map_err(VfioError::VfioUser)
|
||||
}
|
||||
|
||||
fn disable_irq(&self, irq_index: u32) -> Result<(), VfioError> {
|
||||
info!("Disabling IRQ {:x}", irq_index);
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_irqs(
|
||||
irq_index,
|
||||
VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER,
|
||||
0,
|
||||
0,
|
||||
&[],
|
||||
)
|
||||
.map_err(VfioError::VfioUser)
|
||||
}
|
||||
|
||||
fn unmask_irq(&self, irq_index: u32) -> Result<(), VfioError> {
|
||||
info!("Unmasking IRQ {:x}", irq_index);
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_irqs(
|
||||
irq_index,
|
||||
VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK,
|
||||
0,
|
||||
1,
|
||||
&[],
|
||||
)
|
||||
.map_err(VfioError::VfioUser)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciDevice for VfioUserPciDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||
self.common.allocate_bars(allocator, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn free_bars(&mut self, allocator: &mut SystemAllocator) -> Result<(), PciDeviceError> {
|
||||
self.common.free_bars(allocator)
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn detect_bar_reprogramming(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
data: &[u8],
|
||||
) -> Option<BarReprogrammingParams> {
|
||||
self.common
|
||||
.configuration
|
||||
.detect_bar_reprogramming(reg_idx, data)
|
||||
}
|
||||
|
||||
fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.common
|
||||
.write_config_register(reg_idx, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
self.common
|
||||
.read_config_register(reg_idx, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_bar(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.common.read_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.common
|
||||
.write_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn move_bar(&mut self, old_base: u64, new_base: u64) -> Result<(), std::io::Error> {
|
||||
info!("Moving BAR 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||
for mmio_region in self.common.mmio_regions.iter_mut() {
|
||||
if mmio_region.start.raw_value() == old_base {
|
||||
mmio_region.start = GuestAddress(new_base);
|
||||
|
||||
if let Some(mem_slot) = mmio_region.mem_slot {
|
||||
if let Some(host_addr) = mmio_region.host_addr {
|
||||
// Remove original region
|
||||
let old_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
old_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.remove_user_memory_region(old_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
let new_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
new_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
}
|
||||
}
|
||||
info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VfioUserPciDevice {
|
||||
pub fn map_mmio_regions<F>(
|
||||
&mut self,
|
||||
vm: &Arc<dyn hypervisor::Vm>,
|
||||
@@ -466,6 +258,234 @@ impl VfioUserPciDevice {
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for VfioUserPciDevice {
|
||||
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.read_bar(base, offset, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.write_bar(base, offset, data)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[allow(dead_code)]
|
||||
enum Regions {
|
||||
Bar0,
|
||||
Bar1,
|
||||
Bar2,
|
||||
Bar3,
|
||||
Bar4,
|
||||
Bar5,
|
||||
Rom,
|
||||
Config,
|
||||
Vga,
|
||||
Migration,
|
||||
}
|
||||
|
||||
struct VfioUserClientWrapper {
|
||||
client: Arc<Mutex<Client>>,
|
||||
}
|
||||
|
||||
impl Vfio for VfioUserClientWrapper {
|
||||
fn region_read(&self, index: u32, offset: u64, data: &mut [u8]) {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.region_read(index, offset, data)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn region_write(&self, index: u32, offset: u64, data: &[u8]) {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.region_write(index, offset, data)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn get_irq_info(&self, irq_index: u32) -> Option<VfioIrq> {
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_irq_info(irq_index)
|
||||
.ok()
|
||||
.map(|i| VfioIrq {
|
||||
index: i.index,
|
||||
flags: i.flags,
|
||||
count: i.count,
|
||||
})
|
||||
}
|
||||
|
||||
fn enable_irq(&self, irq_index: u32, event_fds: Vec<&EventFd>) -> Result<(), VfioError> {
|
||||
info!(
|
||||
"Enabling IRQ {:x} number of fds = {:?}",
|
||||
irq_index,
|
||||
event_fds.len()
|
||||
);
|
||||
let fds: Vec<i32> = event_fds.iter().map(|e| e.as_raw_fd()).collect();
|
||||
|
||||
// Batch into blocks of 16 fds as sendmsg() has a size limit
|
||||
let mut sent_fds = 0;
|
||||
let num_fds = event_fds.len() as u32;
|
||||
while sent_fds < num_fds {
|
||||
let remaining_fds = num_fds - sent_fds;
|
||||
let count = if remaining_fds > 16 {
|
||||
16
|
||||
} else {
|
||||
remaining_fds
|
||||
};
|
||||
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_irqs(
|
||||
irq_index,
|
||||
VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
|
||||
sent_fds,
|
||||
count,
|
||||
&fds[sent_fds as usize..(sent_fds + count) as usize],
|
||||
)
|
||||
.map_err(VfioError::VfioUser)?;
|
||||
|
||||
sent_fds += count;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_irq(&self, irq_index: u32) -> Result<(), VfioError> {
|
||||
info!("Disabling IRQ {:x}", irq_index);
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_irqs(
|
||||
irq_index,
|
||||
VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER,
|
||||
0,
|
||||
0,
|
||||
&[],
|
||||
)
|
||||
.map_err(VfioError::VfioUser)
|
||||
}
|
||||
|
||||
fn unmask_irq(&self, irq_index: u32) -> Result<(), VfioError> {
|
||||
info!("Unmasking IRQ {:x}", irq_index);
|
||||
self.client
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_irqs(
|
||||
irq_index,
|
||||
VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK,
|
||||
0,
|
||||
1,
|
||||
&[],
|
||||
)
|
||||
.map_err(VfioError::VfioUser)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciDevice for VfioUserPciDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError> {
|
||||
self.common
|
||||
.allocate_bars(allocator, mmio_allocator, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
self.common.free_bars(allocator, mmio_allocator)
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn detect_bar_reprogramming(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
data: &[u8],
|
||||
) -> Option<BarReprogrammingParams> {
|
||||
self.common
|
||||
.configuration
|
||||
.detect_bar_reprogramming(reg_idx, data)
|
||||
}
|
||||
|
||||
fn write_config_register(
|
||||
&mut self,
|
||||
reg_idx: usize,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
) -> Option<Arc<Barrier>> {
|
||||
self.common
|
||||
.write_config_register(reg_idx, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||
self.common
|
||||
.read_config_register(reg_idx, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn read_bar(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.common.read_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
self.common
|
||||
.write_bar(base, offset, data, &self.vfio_wrapper)
|
||||
}
|
||||
|
||||
fn move_bar(&mut self, old_base: u64, new_base: u64) -> Result<(), std::io::Error> {
|
||||
info!("Moving BAR 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||
for mmio_region in self.common.mmio_regions.iter_mut() {
|
||||
if mmio_region.start.raw_value() == old_base {
|
||||
mmio_region.start = GuestAddress(new_base);
|
||||
|
||||
if let Some(mem_slot) = mmio_region.mem_slot {
|
||||
if let Some(host_addr) = mmio_region.host_addr {
|
||||
// Remove original region
|
||||
let old_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
old_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.remove_user_memory_region(old_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
let new_region = self.vm.make_user_memory_region(
|
||||
mem_slot,
|
||||
new_base,
|
||||
mmio_region.length as u64,
|
||||
host_addr as u64,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
self.vm
|
||||
.create_user_memory_region(new_region)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
}
|
||||
}
|
||||
info!("Moved bar 0x{:x} -> 0x{:x}", old_base, new_base);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VfioUserPciDevice {
|
||||
fn drop(&mut self) {
|
||||
self.unmap_mmio_regions();
|
||||
|
||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
remain = "0.2.2"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -28,8 +28,11 @@ const BLK_ALIGNMENTS: [usize; 2] = [512, 4096];
|
||||
|
||||
fn is_valid_alignment(fd: RawFd, alignment: usize) -> bool {
|
||||
let layout = Layout::from_size_align(alignment, alignment).unwrap();
|
||||
// SAFETY: layout has non-zero size
|
||||
let ptr = unsafe { alloc_zeroed(layout) };
|
||||
assert!(!ptr.is_null());
|
||||
|
||||
// SAFETY: FFI call
|
||||
let ret = unsafe {
|
||||
::libc::pread(
|
||||
fd,
|
||||
@@ -39,6 +42,7 @@ fn is_valid_alignment(fd: RawFd, alignment: usize) -> bool {
|
||||
)
|
||||
};
|
||||
|
||||
// SAFETY: ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(ptr, layout) };
|
||||
|
||||
ret >= 0
|
||||
@@ -141,11 +145,18 @@ impl Read for RawFile {
|
||||
.unwrap();
|
||||
|
||||
let layout = Layout::from_size_align(rounded_len, self.alignment).unwrap();
|
||||
// SAFETY: layout has non-zero size
|
||||
let tmp_ptr = unsafe { alloc_zeroed(layout) };
|
||||
if tmp_ptr.is_null() {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
// SAFETY: tmp_ptr is valid and at least rounded_len long
|
||||
let tmp_buf = unsafe { slice::from_raw_parts_mut(tmp_ptr, rounded_len) };
|
||||
|
||||
// This can eventually replaced with read_at once its interface
|
||||
// has been stabilized.
|
||||
// SAFETY: FFI call. All parameters are valid.
|
||||
let ret = unsafe {
|
||||
::libc::pread64(
|
||||
self.file.as_raw_fd(),
|
||||
@@ -155,12 +166,14 @@ impl Read for RawFile {
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
// SAFETY: tmp_ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(tmp_ptr, layout) };
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let read: usize = ret.try_into().unwrap();
|
||||
if read < file_offset {
|
||||
// SAFETY: tmp_ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(tmp_ptr, layout) };
|
||||
return Ok(0);
|
||||
}
|
||||
@@ -171,6 +184,7 @@ impl Read for RawFile {
|
||||
}
|
||||
|
||||
buf.copy_from_slice(&tmp_buf[file_offset..(file_offset + buf_len)]);
|
||||
// SAFETY: tmp_ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(tmp_ptr, layout) };
|
||||
|
||||
self.seek(SeekFrom::Current(to_copy.try_into().unwrap()))
|
||||
@@ -211,11 +225,17 @@ impl Write for RawFile {
|
||||
.unwrap();
|
||||
|
||||
let layout = Layout::from_size_align(rounded_len, self.alignment).unwrap();
|
||||
// SAFETY: layout has non-zero size
|
||||
let tmp_ptr = unsafe { alloc_zeroed(layout) };
|
||||
if tmp_ptr.is_null() {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let tmp_buf = unsafe { slice::from_raw_parts_mut(tmp_ptr, rounded_len) };
|
||||
|
||||
// This can eventually replaced with read_at once its interface
|
||||
// has been stabilized.
|
||||
// SAFETY: FFI call
|
||||
let ret = unsafe {
|
||||
::libc::pread64(
|
||||
self.file.as_raw_fd(),
|
||||
@@ -225,6 +245,7 @@ impl Write for RawFile {
|
||||
)
|
||||
};
|
||||
if ret < 0 {
|
||||
// SAFETY: tmp_ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(tmp_ptr, layout) };
|
||||
return Err(io::Error::last_os_error());
|
||||
};
|
||||
@@ -233,6 +254,7 @@ impl Write for RawFile {
|
||||
|
||||
// This can eventually replaced with write_at once its interface
|
||||
// has been stabilized.
|
||||
// SAFETY: FFI call
|
||||
let ret = unsafe {
|
||||
::libc::pwrite64(
|
||||
self.file.as_raw_fd(),
|
||||
@@ -242,6 +264,7 @@ impl Write for RawFile {
|
||||
)
|
||||
};
|
||||
|
||||
// SAFETY: tmp_ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(tmp_ptr, layout) };
|
||||
|
||||
if ret < 0 {
|
||||
|
||||
@@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
140
release-notes.md
140
release-notes.md
@@ -1,3 +1,13 @@
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
- [Multiple PCI segments support](#multiple-pci-segments-support)
|
||||
- [CPU pinning](#cpu-pinning)
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors)
|
||||
- [v19.0](#v190)
|
||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||
@@ -5,8 +15,8 @@
|
||||
- [Live migration enhancements](#live-migration-enhancements)
|
||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||
- [Notable bug fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors)
|
||||
- [Notable bug fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v18.0](#v180)
|
||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||
@@ -16,23 +26,23 @@
|
||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||
- [Notable bug fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Notable bug fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [v17.0](#v170)
|
||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||
- [Improved SGX support](#improved-sgx-support)
|
||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||
- [Notable bug fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Notable bug fixes](#notable-bug-fixes-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v16.0](#v160)
|
||||
- [Improved live migration support](#improved-live-migration-support)
|
||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||
- [Notable bug fixes](#notable-bug-fixes-3)
|
||||
- [Notable bug fixes](#notable-bug-fixes-4)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -40,7 +50,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -50,7 +60,7 @@
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Contributors](#contributors-6)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -58,13 +68,13 @@
|
||||
- [VHD disk image support](#vhd-disk-image-support)
|
||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v0.12.0](#v0120)
|
||||
- [ARM64 enhancements](#arm64-enhancements)
|
||||
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||
- [Enhanced "info" API](#enhanced-info-api)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v0.11.0](#v0110)
|
||||
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
||||
- [Windows Guest Support](#windows-guest-support)
|
||||
@@ -76,15 +86,15 @@
|
||||
- [Default Log Level Changed](#default-log-level-changed)
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v0.10.0](#v0100)
|
||||
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
||||
- [Memory Zones](#memory-zones)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
||||
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v0.9.0](#v090)
|
||||
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
||||
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
||||
@@ -97,17 +107,17 @@
|
||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v0.7.0](#v070)
|
||||
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
||||
- [Alternative `libc` Support](#alternative-libc-support)
|
||||
@@ -117,14 +127,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Contributors](#contributors-13)
|
||||
- [v0.6.0](#v060)
|
||||
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
||||
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
||||
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [PVH Boot](#pvh-boot)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -132,7 +142,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Contributors](#contributors-15)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -141,7 +151,7 @@
|
||||
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
||||
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
||||
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -168,6 +178,86 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
|
||||
# v20.2
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Don't error out when setting up the SIGWINCH handler (for console resize)
|
||||
when this fails due to older kernel (#3456)
|
||||
* Seccomp rules were refined to remove syscalls that are now unused
|
||||
* Fix reboot on older host kernels when SIGWINCH handler was not initialised
|
||||
(#3496)
|
||||
* Fix virtio-vsock blocking issue (#3497)
|
||||
|
||||
# v20.1
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Networking performance regression with `virtio-net` (#3450)
|
||||
* Limit file descriptors sent in `vfio-user` support (#3401)
|
||||
* Fully advertise PCI MMIO config regions in ACPI tables (#3432)
|
||||
* Set the TSS and KVM identity maps so they don't overlap with firmware RAM
|
||||
* Correctly update the `DeviceTree` on restore
|
||||
|
||||
# v20.0
|
||||
|
||||
This release has been tracked through the [v20.0
|
||||
project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/23).
|
||||
|
||||
### Multiple PCI segments support
|
||||
|
||||
Cloud Hypervisor is no longer limited to 31 PCI devices. For both `x86_64` and
|
||||
`aarch64` architectures, it is now possible to create up to 16 PCI segments,
|
||||
increasing the total amount of supported PCI devices to 496.
|
||||
|
||||
### CPU pinning
|
||||
|
||||
For each vCPU, the user can define a limited set of host CPUs on which it is
|
||||
allowed to run. This can be useful when assigning a 1:1 mapping between host and
|
||||
guest resources, or when running a VM on a specific NUMA node.
|
||||
|
||||
### Improved VFIO support
|
||||
|
||||
Based on VFIO region capabilities, all regions can be memory mapped, limiting
|
||||
the amount of triggered VM exits, and therefore increasing the performance of
|
||||
the passthrough device.
|
||||
|
||||
### Safer code
|
||||
|
||||
Several sections containing unsafe Rust code have been replaced with safe
|
||||
alternatives, and multiple comments have been added to clarify why the remaining
|
||||
unsafe sections are safe to use.
|
||||
|
||||
### Extended documentation
|
||||
|
||||
The documentation related to VFIO has been updated while some new documents have
|
||||
been introduced to cover the usage of `--cpus` parameter as well as how to run
|
||||
Cloud Hypervisor on Intel TDX.
|
||||
|
||||
### Notable bug fixes
|
||||
|
||||
* Naturally align PCI BARs on relocation (#3244)
|
||||
* Fix panic in SIGWINCH listener thread when no seccomp filter set (#3338)
|
||||
* Use the tty raw mode implementation from libc (#3344)
|
||||
* Fix the emulation of register D for CMOS/RTC device (#3393)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Alyssa Ross <hi@alyssa.is>
|
||||
* Bo Chen <chen.bo@intel.com>
|
||||
* Fabiano Fidêncio <fabiano.fidencio@intel.com>
|
||||
* Michael Zhao <michael.zhao@arm.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Rob Bradford <robert.bradford@intel.com>
|
||||
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
* Willen Yang <willenyang@gmail.com>
|
||||
* William Douglas <william.douglas@intel.com>
|
||||
* Ziye Yang <ziye.yang@intel.com>
|
||||
|
||||
# v19.0
|
||||
|
||||
This release has been tracked through the [v19.0
|
||||
@@ -1244,7 +1334,7 @@ Highlights for `cloud-hypervisor` version 0.4.0 include:
|
||||
|
||||
### Dynamic virtual CPUs addition
|
||||
|
||||
As a way to vertically scale Cloud-Hypervisor guests, we now support dynamically
|
||||
As a way to vertically scale Cloud Hypervisor guests, we now support dynamically
|
||||
adding virtual CPUs to the guests, a mechanism also known as CPU hot plug.
|
||||
Through hardware-reduced ACPI notifications, Cloud Hypervisor can now add CPUs
|
||||
to an already running guest and the high level operations for that process are
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM ubuntu:20.04 as dev
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG RUST_TOOLCHAIN="1.55.0"
|
||||
ARG RUST_TOOLCHAIN="1.56.1"
|
||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||
@@ -111,16 +111,20 @@ RUN git clone --depth 1 https://gitlab.com/virtio-fs/qemu.git -b qemu5.0-virtiof
|
||||
&& cd .. && rm -rf qemu
|
||||
|
||||
# install SPDK NVMe
|
||||
RUN git clone https://github.com/spdk/spdk \
|
||||
&& cd spdk \
|
||||
&& git checkout a827fd7eeca67209d4c0aaad9a3ed55692e7e36e \
|
||||
&& git submodule update --init \
|
||||
&& apt-get update \
|
||||
&& ./scripts/pkgdep.sh \
|
||||
&& ./configure --with-vfio-user \
|
||||
&& make -j `nproc` \
|
||||
&& mkdir /usr/local/bin/spdk-nvme \
|
||||
&& cp ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \
|
||||
&& cp ./scripts/rpc.py /usr/local/bin/spdk-nvme \
|
||||
&& cp -r ./scripts/rpc /usr/local/bin/spdk-nvme \
|
||||
&& cd .. && rm -rf spdk
|
||||
# only for 'x86_64' platform images as 'docker buildx' can't build 'spdk'
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
git clone https://github.com/spdk/spdk \
|
||||
&& cd spdk \
|
||||
&& git checkout a827fd7eeca67209d4c0aaad9a3ed55692e7e36e \
|
||||
&& git submodule update --init \
|
||||
&& sed -i 's/pip3 install meson/pip3 install meson==0.59.2/g' scripts/pkgdep/ubuntu.sh \
|
||||
&& sed -i 's/meson/meson==0.59.2/g' scripts/pkgdep/requirements.txt \
|
||||
&& apt-get update \
|
||||
&& ./scripts/pkgdep.sh \
|
||||
&& ./configure --with-vfio-user \
|
||||
&& make -j `nproc` \
|
||||
&& mkdir /usr/local/bin/spdk-nvme \
|
||||
&& cp ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \
|
||||
&& cp ./scripts/rpc.py /usr/local/bin/spdk-nvme \
|
||||
&& cp -r ./scripts/rpc /usr/local/bin/spdk-nvme \
|
||||
&& cd .. && rm -rf spdk; fi
|
||||
|
||||
@@ -1227,7 +1227,7 @@ CONFIG_VIRTIO_BLK=y
|
||||
#
|
||||
CONFIG_NVME_CORE=y
|
||||
CONFIG_BLK_DEV_NVME=y
|
||||
# CONFIG_NVME_MULTIPATH is not set
|
||||
CONFIG_NVME_MULTIPATH=y
|
||||
# CONFIG_NVME_FC is not set
|
||||
# CONFIG_NVME_TCP is not set
|
||||
# CONFIG_NVME_TARGET is not set
|
||||
@@ -1288,7 +1288,38 @@ CONFIG_SCSI_MOD=y
|
||||
# end of SCSI device support
|
||||
|
||||
# CONFIG_ATA is not set
|
||||
# CONFIG_MD is not set
|
||||
CONFIG_MD=y
|
||||
# CONFIG_BLK_DEV_MD is not set
|
||||
# CONFIG_BCACHE is not set
|
||||
CONFIG_BLK_DEV_DM_BUILTIN=y
|
||||
CONFIG_BLK_DEV_DM=y
|
||||
# CONFIG_DM_DEBUG is not set
|
||||
# CONFIG_DM_UNSTRIPED is not set
|
||||
# CONFIG_DM_CRYPT is not set
|
||||
# CONFIG_DM_SNAPSHOT is not set
|
||||
# CONFIG_DM_THIN_PROVISIONING is not set
|
||||
# CONFIG_DM_CACHE is not set
|
||||
# CONFIG_DM_WRITECACHE is not set
|
||||
# CONFIG_DM_EBS is not set
|
||||
# CONFIG_DM_ERA is not set
|
||||
# CONFIG_DM_CLONE is not set
|
||||
# CONFIG_DM_MIRROR is not set
|
||||
# CONFIG_DM_RAID is not set
|
||||
# CONFIG_DM_ZERO is not set
|
||||
CONFIG_DM_MULTIPATH=y
|
||||
# CONFIG_DM_MULTIPATH_QL is not set
|
||||
# CONFIG_DM_MULTIPATH_ST is not set
|
||||
# CONFIG_DM_MULTIPATH_HST is not set
|
||||
# CONFIG_DM_MULTIPATH_IOA is not set
|
||||
# CONFIG_DM_DELAY is not set
|
||||
# CONFIG_DM_DUST is not set
|
||||
# CONFIG_DM_INIT is not set
|
||||
# CONFIG_DM_UEVENT is not set
|
||||
# CONFIG_DM_FLAKEY is not set
|
||||
# CONFIG_DM_VERITY is not set
|
||||
# CONFIG_DM_SWITCH is not set
|
||||
# CONFIG_DM_LOG_WRITES is not set
|
||||
# CONFIG_DM_INTEGRITY is not set
|
||||
# CONFIG_TARGET_CORE is not set
|
||||
# CONFIG_FUSION is not set
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
# This spec file assumes you're building on an environment where:
|
||||
# * You have access to the internet during the build
|
||||
# * You have rustup installed on your system
|
||||
# * You have both x86_64-unknown-linux-gnu and x86_64-unknown-linux-musl
|
||||
# targets installed.
|
||||
|
||||
%define using_rustup 1
|
||||
%define using_musl_libc 1
|
||||
|
||||
Name: cloud-hypervisor
|
||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||
Version: 18.0
|
||||
Version: 20.2
|
||||
Release: 0%{?dist}
|
||||
License: ASL 2.0 or BSD-3-clause
|
||||
Group: Applications/System
|
||||
@@ -12,7 +21,7 @@ BuildRequires: glibc-devel
|
||||
BuildRequires: binutils
|
||||
BuildRequires: git
|
||||
|
||||
%if 0%{?fedora} || 0%{?rhel}
|
||||
%if ! 0%{?using_rustup}
|
||||
BuildRequires: rust
|
||||
BuildRequires: cargo
|
||||
%endif
|
||||
@@ -39,11 +48,13 @@ install -d %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
|
||||
|
||||
%if 0%{?using_musl_libc}
|
||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
install -D -m755 target/x86_64-unknown-linux-musl/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||
%endif
|
||||
|
||||
|
||||
%build
|
||||
@@ -52,22 +63,33 @@ if [[ $? -ne 0 ]]; then
|
||||
echo "Cargo not found, please install cargo. exiting"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
%if 0%{?using_rustup}
|
||||
which rustup
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Rustup not found please install rustup #curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
|
||||
fi
|
||||
%endif
|
||||
|
||||
echo ${cargo_version}
|
||||
|
||||
%if 0%{?using_rustup}
|
||||
rustup target list --installed | grep x86_64-unknown-linux-gnu
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Target x86_64-unknown-linux-gnu not found, please install(#rustup target add x86_64-unknown-linux-gnu). exiting"
|
||||
fi
|
||||
%if 0%{?using_musl_libc}
|
||||
rustup target list --installed | grep x86_64-unknown-linux-musl
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Target x86_64-unknown-linux-musl not found, please install(#rustup target add x86_64-unknown-linux-musl). exiting"
|
||||
fi
|
||||
%endif
|
||||
%endif
|
||||
|
||||
cargo build --release --target=x86_64-unknown-linux-gnu --all
|
||||
%if 0%{?using_musl_libc}
|
||||
cargo build --release --target=x86_64-unknown-linux-musl --all
|
||||
%endif
|
||||
|
||||
|
||||
%clean
|
||||
@@ -75,19 +97,33 @@ rm -rf %{buildroot}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_bindir}/*
|
||||
%{_libdir}/cloud-hypervisor
|
||||
%{_bindir}/ch-remote
|
||||
%caps(cap_net_admin=ep) %{_bindir}/cloud-hypervisor
|
||||
%{_libdir}/cloud-hypervisor/vhost_user_block
|
||||
%caps(cap_net_admin=ep) %{_libdir}/cloud-hypervisor/vhost_user_net
|
||||
%if 0%{?using_musl_libc}
|
||||
%{_libdir}/cloud-hypervisor/static/ch-remote
|
||||
%caps(cap_net_admim=ep) %{_libdir}/cloud-hypervisor/static/cloud-hypervisor
|
||||
%{_libdir}/cloud-hypervisor/static/vhost_user_block
|
||||
%caps(cap_net_admin=ep) %{_libdir}/cloud-hypervisor/static/vhost_user_net
|
||||
%endif
|
||||
%license LICENSE-APACHE
|
||||
%license LICENSE-BSD-3-Clause
|
||||
|
||||
%post
|
||||
setcap cap_net_admin+ep %{_bindir}/cloud-hypervisor
|
||||
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/vhost_user_net
|
||||
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/cloud-hypervisor
|
||||
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/vhost_user_net
|
||||
|
||||
|
||||
%changelog
|
||||
* Tue Jan 04 2022 Rob Bradford <robert.bradford@intel.com> 20.2-0
|
||||
- Update to 20.2
|
||||
|
||||
* Mon Dec 13 2021 Rob Bradford <robert.bradford@intel.com> 20.1-0
|
||||
- Update to 20.1
|
||||
|
||||
* Thu Dec 02 2021 Sebastien Boeuf <sebastien.boeuf@intel.com> 20.0-0
|
||||
- Update to 20.0
|
||||
|
||||
* Mon Nov 08 2021 Fabiano Fidêncio <fabiano.fidencio@intel.com> 19.0-0
|
||||
- Update to 19.0
|
||||
|
||||
* Fri May 28 2021 Muminul Islam <muislam@microsoft.com> 15.0-0
|
||||
- Update version to 15.0
|
||||
|
||||
|
||||
@@ -203,8 +203,8 @@ update_workloads() {
|
||||
if [ ! -f "$VIRTIOFSD_RS" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
git clone "https://gitlab.com/virtio-fs/virtiofsd-rs.git" $VIRTIOFSD_RS_DIR
|
||||
git checkout c847ab63acabed2ed6e6913b9c76bb5099a1d4cb
|
||||
pushd $VIRTIOFSD_RS_DIR
|
||||
git checkout 21d20035a582fb0389697b1bd7f8331623a77939
|
||||
time cargo build --release
|
||||
cp target/release/virtiofsd-rs $VIRTIOFSD_RS || exit 1
|
||||
popd
|
||||
|
||||
@@ -128,8 +128,8 @@ VIRTIOFSD_RS_DIR="virtiofsd_rs_build"
|
||||
if [ ! -f "$VIRTIOFSD_RS" ]; then
|
||||
pushd $WORKLOADS_DIR
|
||||
git clone "https://gitlab.com/virtio-fs/virtiofsd-rs.git" $VIRTIOFSD_RS_DIR
|
||||
git checkout c847ab63acabed2ed6e6913b9c76bb5099a1d4cb
|
||||
pushd $VIRTIOFSD_RS_DIR
|
||||
git checkout 21d20035a582fb0389697b1bd7f8331623a77939
|
||||
time cargo build --release
|
||||
cp target/release/virtiofsd-rs $VIRTIOFSD_RS || exit 1
|
||||
popd
|
||||
|
||||
14
src/main.rs
14
src/main.rs
@@ -150,11 +150,21 @@ fn create_app<'a, 'b>(
|
||||
.help(
|
||||
"boot=<boot_vcpus>,max=<max_vcpus>,\
|
||||
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,\
|
||||
kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>",
|
||||
kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,\
|
||||
affinity=<list_of_vcpus_with_their_associated_cpuset>",
|
||||
)
|
||||
.default_value(default_vcpus)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("platform")
|
||||
.long("platform")
|
||||
.help(
|
||||
"num_pci_segments=<num pci segments>",
|
||||
)
|
||||
.takes_value(true)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("memory")
|
||||
.long("memory")
|
||||
@@ -638,6 +648,7 @@ mod unit_tests {
|
||||
topology: None,
|
||||
kvm_hyperv: false,
|
||||
max_phys_bits: 46,
|
||||
affinity: None,
|
||||
},
|
||||
memory: MemoryConfig {
|
||||
size: 536_870_912,
|
||||
@@ -687,6 +698,7 @@ mod unit_tests {
|
||||
watchdog: false,
|
||||
#[cfg(feature = "tdx")]
|
||||
tdx: None,
|
||||
platform: None,
|
||||
};
|
||||
|
||||
aver_eq!(tb, expected_vm_config, result_vm_config);
|
||||
|
||||
@@ -1136,9 +1136,9 @@ mod tests {
|
||||
])
|
||||
.args(&[
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=0-2:9,distances=1@15:2@20,memory_zones=mem0",
|
||||
"guest_numa_id=1,cpus=3-4:6-8,distances=0@20:2@25,memory_zones=mem1",
|
||||
"guest_numa_id=2,cpus=5:10-11,distances=0@25:1@30,memory_zones=mem2",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
|
||||
])
|
||||
.args(&["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
@@ -1607,6 +1607,7 @@ mod tests {
|
||||
virtiofsd_cache: &str,
|
||||
prepare_daemon: &dyn Fn(&TempDir, &str, &str) -> (std::process::Child, String),
|
||||
hotplug: bool,
|
||||
pci_segment: Option<u16>,
|
||||
) {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let focal_image = if hotplug {
|
||||
@@ -1657,10 +1658,20 @@ mod tests {
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.args(&["--api-socket", &api_socket]);
|
||||
if pci_segment.is_some() {
|
||||
guest_command.args(&["--platform", "num_pci_segments=16"]);
|
||||
}
|
||||
|
||||
let fs_params = format!(
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024,dax={}{}",
|
||||
virtiofsd_socket_path, dax_vmm_param, cache_size_vmm_param
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024,dax={}{}{}",
|
||||
virtiofsd_socket_path,
|
||||
dax_vmm_param,
|
||||
cache_size_vmm_param,
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
);
|
||||
|
||||
if !hotplug {
|
||||
@@ -1677,8 +1688,16 @@ mod tests {
|
||||
let (cmd_success, cmd_output) =
|
||||
remote_command_w_output(&api_socket, "add-fs", Some(&fs_params));
|
||||
assert!(cmd_success);
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"myfs0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
assert!(String::from_utf8_lossy(&cmd_output).contains(&format!(
|
||||
"{{\"id\":\"myfs0\",\"bdf\":\"{:04x}:00:01.0\"}}",
|
||||
pci_segment
|
||||
)));
|
||||
} else {
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"myfs0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
}
|
||||
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
}
|
||||
@@ -1749,16 +1768,31 @@ mod tests {
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
let fs_params = format!(
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024,dax={}{}",
|
||||
virtiofsd_socket_path, dax_vmm_param, cache_size_vmm_param
|
||||
"id=myfs0,tag=myfs,socket={},num_queues=1,queue_size=1024,dax={}{}{}",
|
||||
virtiofsd_socket_path,
|
||||
dax_vmm_param,
|
||||
cache_size_vmm_param,
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
);
|
||||
|
||||
// Add back and check it works
|
||||
let (cmd_success, cmd_output) =
|
||||
remote_command_w_output(&api_socket, "add-fs", Some(&fs_params));
|
||||
assert!(cmd_success);
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"myfs0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
assert!(String::from_utf8_lossy(&cmd_output).contains(&format!(
|
||||
"{{\"id\":\"myfs0\",\"bdf\":\"{:04x}:00:01.0\"}}",
|
||||
pci_segment
|
||||
)));
|
||||
} else {
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"myfs0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
}
|
||||
|
||||
thread::sleep(std::time::Duration::new(10, 0));
|
||||
// Mount shared directory through virtio_fs filesystem
|
||||
let mount_cmd = format!(
|
||||
@@ -2464,6 +2498,48 @@ mod tests {
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cpu_affinity() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
// We need the host to have at least 4 CPUs if we want to be able
|
||||
// to run this test.
|
||||
let host_cpus_count = exec_host_command_output("nproc");
|
||||
assert!(
|
||||
String::from_utf8_lossy(&host_cpus_count.stdout)
|
||||
.trim()
|
||||
.parse::<u8>()
|
||||
.unwrap_or(0)
|
||||
>= 4
|
||||
);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--cpus", "boot=2,affinity=[0@[0,2],1@[1,3]]"])
|
||||
.args(&["--memory", "size=512M"])
|
||||
.args(&["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
let pid = child.id();
|
||||
let taskset_vcpu0 = exec_host_command_output(format!("taskset -pc $(ps -T -p {} | grep vcpu0 | xargs | cut -f 2 -d \" \") | cut -f 6 -d \" \"", pid).as_str());
|
||||
assert_eq!(String::from_utf8_lossy(&taskset_vcpu0.stdout).trim(), "0,2");
|
||||
let taskset_vcpu1 = exec_host_command_output(format!("taskset -pc $(ps -T -p {} | grep vcpu1 | xargs | cut -f 2 -d \" \") | cut -f 6 -d \" \"", pid).as_str());
|
||||
assert_eq!(String::from_utf8_lossy(&taskset_vcpu1.stdout).trim(), "1,3");
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_large_vm() {
|
||||
@@ -3059,24 +3135,38 @@ mod tests {
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_dax_on_default_cache_size() {
|
||||
test_virtio_fs(true, None, "none", &prepare_virtiofsd, false)
|
||||
test_virtio_fs(true, None, "none", &prepare_virtiofsd, false, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_dax_on_cache_size_1_gib() {
|
||||
test_virtio_fs(true, Some(0x4000_0000), "none", &prepare_virtiofsd, false)
|
||||
test_virtio_fs(
|
||||
true,
|
||||
Some(0x4000_0000),
|
||||
"none",
|
||||
&prepare_virtiofsd,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_fs_dax_off() {
|
||||
test_virtio_fs(false, None, "none", &prepare_virtiofsd, false)
|
||||
test_virtio_fs(false, None, "none", &prepare_virtiofsd, false, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_dax_on_default_cache_size_w_virtiofsd_rs_daemon() {
|
||||
test_virtio_fs(true, None, "never", &prepare_virtiofsd_rs_daemon, false)
|
||||
test_virtio_fs(
|
||||
true,
|
||||
None,
|
||||
"never",
|
||||
&prepare_virtiofsd_rs_daemon,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3088,34 +3178,82 @@ mod tests {
|
||||
"never",
|
||||
&prepare_virtiofsd_rs_daemon,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_fs_dax_off_w_virtiofsd_rs_daemon() {
|
||||
test_virtio_fs(false, None, "never", &prepare_virtiofsd_rs_daemon, false)
|
||||
test_virtio_fs(
|
||||
false,
|
||||
None,
|
||||
"never",
|
||||
&prepare_virtiofsd_rs_daemon,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_hotplug_dax_on() {
|
||||
test_virtio_fs(true, None, "none", &prepare_virtiofsd, true)
|
||||
test_virtio_fs(true, None, "none", &prepare_virtiofsd, true, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_fs_hotplug_dax_off() {
|
||||
test_virtio_fs(false, None, "none", &prepare_virtiofsd, true)
|
||||
test_virtio_fs(false, None, "none", &prepare_virtiofsd, true, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_hotplug_dax_on_w_virtiofsd_rs_daemon() {
|
||||
test_virtio_fs(true, None, "never", &prepare_virtiofsd_rs_daemon, true)
|
||||
test_virtio_fs(
|
||||
true,
|
||||
None,
|
||||
"never",
|
||||
&prepare_virtiofsd_rs_daemon,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtio_fs_hotplug_dax_off_w_virtiofsd_rs_daemon() {
|
||||
test_virtio_fs(false, None, "never", &prepare_virtiofsd_rs_daemon, true)
|
||||
test_virtio_fs(
|
||||
false,
|
||||
None,
|
||||
"never",
|
||||
&prepare_virtiofsd_rs_daemon,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_virtio_fs_multi_segment_hotplug() {
|
||||
test_virtio_fs(
|
||||
true,
|
||||
Some(0x4000_0000),
|
||||
"none",
|
||||
&prepare_virtiofsd,
|
||||
true,
|
||||
Some(15),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(not(feature = "mshv"), target_arch = "x86_64"))]
|
||||
fn test_virtio_fs_multi_segment() {
|
||||
test_virtio_fs(
|
||||
true,
|
||||
Some(0x4000_0000),
|
||||
"none",
|
||||
&prepare_virtiofsd,
|
||||
false,
|
||||
Some(15),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3518,9 +3656,7 @@ mod tests {
|
||||
}
|
||||
Err(mpsc::TryRecvError::Empty) => {
|
||||
empty += 1;
|
||||
if empty > 5 {
|
||||
panic!("No login on pty");
|
||||
}
|
||||
assert!(!(empty > 5), "No login on pty");
|
||||
}
|
||||
_ => panic!("No login on pty"),
|
||||
}
|
||||
@@ -4791,6 +4927,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_pmem_hotplug() {
|
||||
_test_pmem_hotplug(None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pmem_multi_segment_hotplug() {
|
||||
_test_pmem_hotplug(Some(15))
|
||||
}
|
||||
|
||||
fn _test_pmem_hotplug(pci_segment: Option<u16>) {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let focal_image = FOCAL_IMAGE_UPDATE_KERNEL_NAME.to_string();
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -4805,17 +4950,22 @@ mod tests {
|
||||
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--api-socket", &api_socket])
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
cmd.args(&["--api-socket", &api_socket])
|
||||
.args(&["--cpus", "boot=1"])
|
||||
.args(&["--memory", "size=512M"])
|
||||
.args(&["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
.capture_output();
|
||||
|
||||
if pci_segment.is_some() {
|
||||
cmd.args(&["--platform", "num_pci_segments=16"]);
|
||||
}
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
@@ -4837,13 +4987,25 @@ mod tests {
|
||||
&api_socket,
|
||||
"add-pmem",
|
||||
Some(&format!(
|
||||
"file={},id=test0",
|
||||
pmem_temp_file.as_path().to_str().unwrap()
|
||||
"file={},id=test0{}",
|
||||
pmem_temp_file.as_path().to_str().unwrap(),
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
)),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"test0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
assert!(String::from_utf8_lossy(&cmd_output).contains(&format!(
|
||||
"{{\"id\":\"test0\",\"bdf\":\"{:04x}:00:01.0\"}}",
|
||||
pci_segment
|
||||
)));
|
||||
} else {
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"test0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
}
|
||||
|
||||
// Check that /dev/pmem0 exists and the block size is 128M
|
||||
assert_eq!(
|
||||
@@ -4906,6 +5068,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_net_hotplug() {
|
||||
_test_net_hotplug(None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_net_multi_segment_hotplug() {
|
||||
_test_net_hotplug(Some(15))
|
||||
}
|
||||
|
||||
fn _test_net_hotplug(pci_segment: Option<u16>) {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
@@ -4917,16 +5088,21 @@ mod tests {
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
|
||||
// Boot without network
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--api-socket", &api_socket])
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
cmd.args(&["--api-socket", &api_socket])
|
||||
.args(&["--cpus", "boot=1"])
|
||||
.args(&["--memory", "size=512M"])
|
||||
.args(&["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
.capture_output();
|
||||
|
||||
if pci_segment.is_some() {
|
||||
cmd.args(&["--platform", "num_pci_segments=16"]);
|
||||
}
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
|
||||
thread::sleep(std::time::Duration::new(20, 0));
|
||||
|
||||
@@ -4935,16 +5111,30 @@ mod tests {
|
||||
let (cmd_success, cmd_output) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-net",
|
||||
Some(guest.default_net_string().as_str()),
|
||||
Some(
|
||||
format!(
|
||||
"{}{},id=test0",
|
||||
guest.default_net_string(),
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"_net2\",\"bdf\":\"0000:00:05.0\"}"));
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"_net0\",\"bdf\":\"0000:00:05.0\"}"));
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
assert!(String::from_utf8_lossy(&cmd_output).contains(&format!(
|
||||
"{{\"id\":\"test0\",\"bdf\":\"{:04x}:00:01.0\"}}",
|
||||
pci_segment
|
||||
)));
|
||||
} else {
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"test0\",\"bdf\":\"0000:00:05.0\"}"));
|
||||
}
|
||||
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
|
||||
@@ -4960,29 +5150,36 @@ mod tests {
|
||||
);
|
||||
|
||||
// Remove network
|
||||
assert!(remote_command(
|
||||
&api_socket,
|
||||
"remove-device",
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
Some("_net2"),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
Some("_net0")
|
||||
));
|
||||
assert!(remote_command(&api_socket, "remove-device", Some("test0"),));
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
|
||||
// Add network again
|
||||
let (cmd_success, cmd_output) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-net",
|
||||
Some(guest.default_net_string().as_str()),
|
||||
Some(
|
||||
format!(
|
||||
"{}{},id=test1",
|
||||
guest.default_net_string(),
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
format!(",pci_segment={}", pci_segment)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"_net3\",\"bdf\":\"0000:00:05.0\"}"));
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"_net1\",\"bdf\":\"0000:00:05.0\"}"));
|
||||
|
||||
if let Some(pci_segment) = pci_segment {
|
||||
assert!(String::from_utf8_lossy(&cmd_output).contains(&format!(
|
||||
"{{\"id\":\"test1\",\"bdf\":\"{:04x}:00:01.0\"}}",
|
||||
pci_segment
|
||||
)));
|
||||
} else {
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"test1\",\"bdf\":\"0000:00:05.0\"}"));
|
||||
}
|
||||
|
||||
thread::sleep(std::time::Duration::new(5, 0));
|
||||
|
||||
@@ -5423,7 +5620,7 @@ mod tests {
|
||||
.args(&[
|
||||
"--net",
|
||||
&format!(
|
||||
"fd={}:{},mac={},num_queues={}",
|
||||
"fd=[{},{}],mac={},num_queues={}",
|
||||
taps[0].as_raw_fd(),
|
||||
taps[1].as_raw_fd(),
|
||||
guest.network.guest_mac,
|
||||
@@ -5722,6 +5919,148 @@ mod tests {
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
|
||||
fn setup_spdk_nvme(nvme_dir: &std::path::Path) {
|
||||
cleanup_spdk_nvme();
|
||||
|
||||
assert!(exec_host_command_status(&format!(
|
||||
"mkdir -p {}",
|
||||
nvme_dir.join("nvme-vfio-user").to_str().unwrap()
|
||||
))
|
||||
.success());
|
||||
assert!(exec_host_command_status(&format!(
|
||||
"truncate {} -s 128M",
|
||||
nvme_dir.join("test-disk.raw").to_str().unwrap()
|
||||
))
|
||||
.success());
|
||||
assert!(exec_host_command_status(&format!(
|
||||
"mkfs.ext4 {}",
|
||||
nvme_dir.join("test-disk.raw").to_str().unwrap()
|
||||
))
|
||||
.success());
|
||||
|
||||
// Start the SPDK nvmf_tgt daemon to present NVMe device as a VFIO user device
|
||||
Command::new("/usr/local/bin/spdk-nvme/nvmf_tgt")
|
||||
.args(&["-i", "0", "-m", "0x1"])
|
||||
.spawn()
|
||||
.unwrap();
|
||||
thread::sleep(std::time::Duration::new(2, 0));
|
||||
|
||||
assert!(exec_host_command_status(
|
||||
"/usr/local/bin/spdk-nvme/rpc.py nvmf_create_transport -t VFIOUSER"
|
||||
)
|
||||
.success());
|
||||
assert!(exec_host_command_status(&format!(
|
||||
"/usr/local/bin/spdk-nvme/rpc.py bdev_aio_create {} test 512",
|
||||
nvme_dir.join("test-disk.raw").to_str().unwrap()
|
||||
))
|
||||
.success());
|
||||
assert!(exec_host_command_status(
|
||||
"/usr/local/bin/spdk-nvme/rpc.py nvmf_create_subsystem nqn.2019-07.io.spdk:cnode -a -s test"
|
||||
)
|
||||
.success());
|
||||
assert!(exec_host_command_status(
|
||||
"/usr/local/bin/spdk-nvme/rpc.py nvmf_subsystem_add_ns nqn.2019-07.io.spdk:cnode test"
|
||||
)
|
||||
.success());
|
||||
assert!(exec_host_command_status(&format!(
|
||||
"/usr/local/bin/spdk-nvme/rpc.py nvmf_subsystem_add_listener nqn.2019-07.io.spdk:cnode -t VFIOUSER -a {} -s 0",
|
||||
nvme_dir.join("nvme-vfio-user").to_str().unwrap()
|
||||
))
|
||||
.success());
|
||||
}
|
||||
|
||||
fn cleanup_spdk_nvme() {
|
||||
exec_host_command_status("pkill -f nvmf_tgt");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_vfio_user() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
let spdk_nvme_dir = guest.tmp_dir.as_path().join("test-vfio-user");
|
||||
setup_spdk_nvme(spdk_nvme_dir.as_path());
|
||||
|
||||
let kernel_path = direct_kernel_boot_path();
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(&["--api-socket", &api_socket])
|
||||
.args(&["--cpus", "boot=1"])
|
||||
.args(&["--memory", "size=512M,shared=on"])
|
||||
.args(&["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
|
||||
// Hotplug the SPDK-NVMe device to the VM
|
||||
let (cmd_success, cmd_output) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-user-device",
|
||||
Some(&format!(
|
||||
"socket={},id=vfio_user0",
|
||||
spdk_nvme_dir
|
||||
.as_path()
|
||||
.join("nvme-vfio-user/cntrl")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)),
|
||||
);
|
||||
assert!(cmd_success);
|
||||
assert!(String::from_utf8_lossy(&cmd_output)
|
||||
.contains("{\"id\":\"vfio_user0\",\"bdf\":\"0000:00:06.0\"}"));
|
||||
|
||||
thread::sleep(std::time::Duration::new(1, 0));
|
||||
|
||||
// Check both if /dev/nvme exists and if the block size is 128M.
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command("lsblk | grep nvme0n1 | grep -c 128M")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.parse::<u32>()
|
||||
.unwrap_or_default(),
|
||||
1
|
||||
);
|
||||
|
||||
// Check changes persist after reboot
|
||||
assert_eq!(
|
||||
guest.ssh_command("sudo mount /dev/nvme0n1 /mnt").unwrap(),
|
||||
""
|
||||
);
|
||||
assert_eq!(guest.ssh_command("ls /mnt").unwrap(), "lost+found\n");
|
||||
guest
|
||||
.ssh_command("echo test123 | sudo tee /mnt/test")
|
||||
.unwrap();
|
||||
assert_eq!(guest.ssh_command("sudo umount /mnt").unwrap(), "");
|
||||
assert_eq!(guest.ssh_command("ls /mnt").unwrap(), "");
|
||||
|
||||
guest.reboot_linux(0, None);
|
||||
assert_eq!(
|
||||
guest.ssh_command("sudo mount /dev/nvme0n1 /mnt").unwrap(),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
guest.ssh_command("sudo cat /mnt/test").unwrap().trim(),
|
||||
"test123"
|
||||
);
|
||||
});
|
||||
|
||||
cleanup_spdk_nvme();
|
||||
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output().unwrap();
|
||||
|
||||
handle_child_output(r, &output);
|
||||
}
|
||||
}
|
||||
|
||||
mod sequential {
|
||||
@@ -6957,9 +7296,9 @@ mod tests {
|
||||
"id=mem1,size=1G,hotplug_size=32G",
|
||||
"id=mem2,size=1G,hotplug_size=32G",
|
||||
"--numa",
|
||||
"guest_numa_id=0,cpus=0-2:9,distances=1@15:2@20,memory_zones=mem0",
|
||||
"guest_numa_id=1,cpus=3-4:6-8,distances=0@20:2@25,memory_zones=mem1",
|
||||
"guest_numa_id=2,cpus=5:10-11,distances=0@25:1@30,memory_zones=mem2",
|
||||
"guest_numa_id=0,cpus=[0-2,9],distances=[1@15,2@20],memory_zones=mem0",
|
||||
"guest_numa_id=1,cpus=[3-4,6-8],distances=[0@20,2@25],memory_zones=mem1",
|
||||
"guest_numa_id=2,cpus=[5,10-11],distances=[0@25,1@30],memory_zones=mem2",
|
||||
]
|
||||
} else {
|
||||
&["--memory", "size=4G"]
|
||||
|
||||
@@ -5,8 +5,8 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.44"
|
||||
libc = "0.2.103"
|
||||
anyhow = "1.0.51"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||
serde_derive = ">=1.0.27"
|
||||
|
||||
@@ -73,8 +73,6 @@ struct Header {
|
||||
error: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for Header {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct Version {
|
||||
@@ -82,7 +80,6 @@ struct Version {
|
||||
major: u16,
|
||||
minor: u16,
|
||||
}
|
||||
unsafe impl ByteValued for Version {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct MigrationCapabilities {
|
||||
@@ -128,8 +125,6 @@ struct DmaMap {
|
||||
size: u64,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for DmaMap {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DmaUnmap {
|
||||
@@ -140,8 +135,6 @@ struct DmaUnmap {
|
||||
size: u64,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for DmaUnmap {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DeviceGetInfo {
|
||||
@@ -152,8 +145,6 @@ struct DeviceGetInfo {
|
||||
num_irqs: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for DeviceGetInfo {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DeviceGetRegionInfo {
|
||||
@@ -161,8 +152,6 @@ struct DeviceGetRegionInfo {
|
||||
region_info: vfio_region_info,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for DeviceGetRegionInfo {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct RegionAccess {
|
||||
@@ -172,8 +161,6 @@ struct RegionAccess {
|
||||
count: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for RegionAccess {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct GetIrqInfo {
|
||||
@@ -184,8 +171,6 @@ struct GetIrqInfo {
|
||||
count: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for GetIrqInfo {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct SetIrqs {
|
||||
@@ -197,14 +182,22 @@ struct SetIrqs {
|
||||
count: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for SetIrqs {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
struct DeviceReset {
|
||||
header: Header,
|
||||
}
|
||||
|
||||
// SAFETY: these data structures only contain a sereis of integers
|
||||
unsafe impl ByteValued for Header {}
|
||||
unsafe impl ByteValued for Version {}
|
||||
unsafe impl ByteValued for DmaMap {}
|
||||
unsafe impl ByteValued for DmaUnmap {}
|
||||
unsafe impl ByteValued for DeviceGetInfo {}
|
||||
unsafe impl ByteValued for DeviceGetRegionInfo {}
|
||||
unsafe impl ByteValued for RegionAccess {}
|
||||
unsafe impl ByteValued for GetIrqInfo {}
|
||||
unsafe impl ByteValued for SetIrqs {}
|
||||
unsafe impl ByteValued for DeviceReset {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -305,7 +298,7 @@ impl Client {
|
||||
major: 0,
|
||||
minor: 1,
|
||||
};
|
||||
info!("Command: {:?}", version);
|
||||
debug!("Command: {:?}", version);
|
||||
|
||||
let version_data = CString::new(version_data.as_bytes()).unwrap();
|
||||
let bufs = vec![
|
||||
@@ -319,7 +312,7 @@ impl Client {
|
||||
.write_vectored(&bufs)
|
||||
.map_err(Error::StreamWrite)?;
|
||||
|
||||
info!(
|
||||
debug!(
|
||||
"Sent client version information: major = {} minor = {} capabilities = {:?}",
|
||||
version.major, version.minor, &caps
|
||||
);
|
||||
@@ -331,7 +324,7 @@ impl Client {
|
||||
.read_exact(server_version.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
|
||||
info!("Reply: {:?}", server_version);
|
||||
debug!("Reply: {:?}", server_version);
|
||||
|
||||
let mut server_version_data = Vec::new();
|
||||
server_version_data.resize(
|
||||
@@ -346,7 +339,7 @@ impl Client {
|
||||
serde_json::from_slice(&server_version_data[0..server_version_data.len() - 1])
|
||||
.map_err(Error::DeserializeCapabilites)?;
|
||||
|
||||
info!(
|
||||
debug!(
|
||||
"Received server version information: major = {} minor = {} capabilities = {:?}",
|
||||
server_version.major, server_version.minor, &server_caps
|
||||
);
|
||||
@@ -375,7 +368,7 @@ impl Client {
|
||||
address,
|
||||
size,
|
||||
};
|
||||
info!("Command: {:?}", dma_map);
|
||||
debug!("Command: {:?}", dma_map);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.send_with_fd(dma_map.as_slice(), fd)
|
||||
@@ -385,7 +378,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -404,7 +397,7 @@ impl Client {
|
||||
address,
|
||||
size,
|
||||
};
|
||||
info!("Command: {:?}", dma_unmap);
|
||||
debug!("Command: {:?}", dma_unmap);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.write_all(dma_unmap.as_slice())
|
||||
@@ -414,7 +407,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -429,7 +422,7 @@ impl Client {
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
info!("Command: {:?}", reset);
|
||||
debug!("Command: {:?}", reset);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.write_all(reset.as_slice())
|
||||
@@ -439,7 +432,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -456,7 +449,7 @@ impl Client {
|
||||
argsz: std::mem::size_of::<DeviceGetInfo>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
info!("Command: {:?}", get_info);
|
||||
debug!("Command: {:?}", get_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
@@ -467,7 +460,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
self.num_irqs = reply.num_irqs;
|
||||
|
||||
if reply.flags & VFIO_DEVICE_FLAGS_PCI != VFIO_DEVICE_FLAGS_PCI {
|
||||
@@ -493,7 +486,7 @@ impl Client {
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
info!("Command: {:?}", get_region_info);
|
||||
debug!("Command: {:?}", get_region_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
@@ -505,7 +498,7 @@ impl Client {
|
||||
.stream
|
||||
.recv_with_fd(reply.as_mut_slice())
|
||||
.map_err(Error::ReceiveWithFd)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
regions.push(Region {
|
||||
flags: reply.region_info.flags,
|
||||
@@ -540,7 +533,7 @@ impl Client {
|
||||
count: data.len() as u32,
|
||||
region,
|
||||
};
|
||||
info!("Command: {:?}", region_read);
|
||||
debug!("Command: {:?}", region_read);
|
||||
self.next_message_id += Wrapping(1);
|
||||
self.stream
|
||||
.write_all(region_read.as_slice())
|
||||
@@ -550,7 +543,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
self.stream.read_exact(data).map_err(Error::StreamRead)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -568,7 +561,7 @@ impl Client {
|
||||
count: data.len() as u32,
|
||||
region,
|
||||
};
|
||||
info!("Command: {:?}", region_write);
|
||||
debug!("Command: {:?}", region_write);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
let bufs = vec![IoSlice::new(region_write.as_slice()), IoSlice::new(data)];
|
||||
@@ -583,7 +576,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -601,7 +594,7 @@ impl Client {
|
||||
index,
|
||||
count: 0,
|
||||
};
|
||||
info!("Command: {:?}", get_irq_info);
|
||||
debug!("Command: {:?}", get_irq_info);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
@@ -612,7 +605,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(IrqInfo {
|
||||
index: reply.index,
|
||||
@@ -643,7 +636,7 @@ impl Client {
|
||||
index,
|
||||
count,
|
||||
};
|
||||
info!("Command: {:?}", set_irqs);
|
||||
debug!("Command: {:?}", set_irqs);
|
||||
self.next_message_id += Wrapping(1);
|
||||
|
||||
self.stream
|
||||
@@ -654,7 +647,7 @@ impl Client {
|
||||
self.stream
|
||||
.read_exact(reply.as_mut_slice())
|
||||
.map_err(Error::StreamRead)?;
|
||||
info!("Reply: {:?}", reply);
|
||||
debug!("Reply: {:?}", reply);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ license = "Apache-2.0"
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
crc32c = "0.6.0"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
remain = "0.2.2"
|
||||
thiserror = "1.0"
|
||||
|
||||
@@ -348,8 +348,8 @@ impl RegionTableEntry {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct RegionEntry {
|
||||
start: u64,
|
||||
end: u64,
|
||||
_start: u64,
|
||||
_end: u64,
|
||||
}
|
||||
|
||||
enum HeaderNo {
|
||||
@@ -360,17 +360,17 @@ enum HeaderNo {
|
||||
/// Contains the information from the header of a VHDx file
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VhdxHeader {
|
||||
file_type_identifier: FileTypeIdentifier,
|
||||
_file_type_identifier: FileTypeIdentifier,
|
||||
header_1: Header,
|
||||
header_2: Header,
|
||||
region_table_1: RegionTableHeader,
|
||||
region_table_2: RegionTableHeader,
|
||||
_region_table_2: RegionTableHeader,
|
||||
}
|
||||
|
||||
impl VhdxHeader {
|
||||
/// Creates a VhdxHeader from a reference to a file
|
||||
pub fn new(f: &mut File) -> Result<VhdxHeader> {
|
||||
let file_type_identifier: FileTypeIdentifier = FileTypeIdentifier::new(f)?;
|
||||
let _file_type_identifier: FileTypeIdentifier = FileTypeIdentifier::new(f)?;
|
||||
let header_1 = Header::new(f, HEADER_1_START);
|
||||
let header_2 = Header::new(f, HEADER_2_START);
|
||||
|
||||
@@ -383,11 +383,11 @@ impl VhdxHeader {
|
||||
let (header_1, header_2) =
|
||||
VhdxHeader::update_headers(f, header_1, header_2, file_write_guid)?;
|
||||
Ok(VhdxHeader {
|
||||
file_type_identifier,
|
||||
_file_type_identifier,
|
||||
header_1,
|
||||
header_2,
|
||||
region_table_1: RegionTableHeader::new(f, REGION_TABLE_1_START)?,
|
||||
region_table_2: RegionTableHeader::new(f, REGION_TABLE_2_START)?,
|
||||
_region_table_2: RegionTableHeader::new(f, REGION_TABLE_2_START)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ default = []
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
virtio-bindings = "0.1.0"
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vm-memory = { version = "0.6.0", features = ["backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.9.0"
|
||||
|
||||
@@ -10,7 +10,6 @@ extern crate log;
|
||||
use std::error;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::num::Wrapping;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
@@ -26,9 +25,10 @@ use vhost::vhost_user::{
|
||||
VhostUserSlaveReqHandlerMut,
|
||||
};
|
||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::guest_memory::FileOffset;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress, MmapRegion};
|
||||
use vm_virtio::Queue;
|
||||
use vm_memory::GuestAddressSpace;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress, GuestMemoryAtomic, MmapRegion};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
@@ -83,9 +83,6 @@ pub trait VhostUserBackend: Send + Sync + 'static {
|
||||
/// Tell the backend if EVENT_IDX has been negotiated.
|
||||
fn set_event_idx(&mut self, enabled: bool);
|
||||
|
||||
/// Update guest memory regions.
|
||||
fn update_memory(&mut self, mem: GuestMemoryMmap) -> result::Result<(), io::Error>;
|
||||
|
||||
/// This function gets called if the backend registered some additional
|
||||
/// listeners onto specific file descriptors. The library can handle
|
||||
/// virtqueues on its own, but does not know what to do with events
|
||||
@@ -232,7 +229,7 @@ struct AddrMapping {
|
||||
}
|
||||
|
||||
pub struct Vring {
|
||||
queue: Queue,
|
||||
queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
kick: Option<EventFd>,
|
||||
call: Option<EventFd>,
|
||||
#[allow(dead_code)]
|
||||
@@ -241,9 +238,9 @@ pub struct Vring {
|
||||
}
|
||||
|
||||
impl Vring {
|
||||
fn new(max_queue_size: u16) -> Self {
|
||||
fn new(mem: GuestMemoryAtomic<GuestMemoryMmap>, max_queue_size: u16) -> Self {
|
||||
Vring {
|
||||
queue: Queue::new(max_queue_size),
|
||||
queue: Queue::new(mem, max_queue_size),
|
||||
kick: None,
|
||||
call: None,
|
||||
err: None,
|
||||
@@ -251,7 +248,7 @@ impl Vring {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mut_queue(&mut self) -> &mut Queue {
|
||||
pub fn mut_queue(&mut self) -> &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>> {
|
||||
&mut self.queue
|
||||
}
|
||||
|
||||
@@ -468,7 +465,7 @@ struct VhostUserHandler<S: VhostUserBackend> {
|
||||
max_queue_size: usize,
|
||||
queues_per_thread: Vec<u64>,
|
||||
mappings: Vec<AddrMapping>,
|
||||
guest_memory: Option<GuestMemoryMmap>,
|
||||
guest_memory: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
vrings: Vec<Arc<RwLock<Vring>>>,
|
||||
worker_threads: Vec<thread::JoinHandle<VringWorkerResult<()>>>,
|
||||
}
|
||||
@@ -478,10 +475,14 @@ impl<S: VhostUserBackend> VhostUserHandler<S> {
|
||||
let num_queues = backend.read().unwrap().num_queues();
|
||||
let max_queue_size = backend.read().unwrap().max_queue_size();
|
||||
let queues_per_thread = backend.read().unwrap().queues_per_thread();
|
||||
let guest_memory = GuestMemoryAtomic::new(GuestMemoryMmap::new());
|
||||
|
||||
let mut vrings: Vec<Arc<RwLock<Vring>>> = Vec::new();
|
||||
for _ in 0..num_queues {
|
||||
let vring = Arc::new(RwLock::new(Vring::new(max_queue_size as u16)));
|
||||
let vring = Arc::new(RwLock::new(Vring::new(
|
||||
guest_memory.clone(),
|
||||
max_queue_size as u16,
|
||||
)));
|
||||
vrings.push(vring);
|
||||
}
|
||||
|
||||
@@ -544,7 +545,7 @@ impl<S: VhostUserBackend> VhostUserHandler<S> {
|
||||
max_queue_size,
|
||||
queues_per_thread,
|
||||
mappings: Vec::new(),
|
||||
guest_memory: None,
|
||||
guest_memory,
|
||||
vrings,
|
||||
worker_threads,
|
||||
})
|
||||
@@ -649,15 +650,7 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
let mem = GuestMemoryMmap::from_ranges_with_files(regions).map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?;
|
||||
self.backend
|
||||
.write()
|
||||
.unwrap()
|
||||
.update_memory(mem.clone())
|
||||
.map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?;
|
||||
|
||||
self.guest_memory = Some(mem);
|
||||
self.guest_memory.lock().unwrap().replace(mem);
|
||||
self.mappings = mappings;
|
||||
|
||||
Ok(())
|
||||
@@ -671,7 +664,12 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
if index as usize >= self.num_queues || num == 0 || num as usize > self.max_queue_size {
|
||||
return Err(VhostUserError::InvalidParam);
|
||||
}
|
||||
self.vrings[index as usize].write().unwrap().queue.size = num as u16;
|
||||
self.vrings[index as usize]
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.state
|
||||
.size = num as u16;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -702,13 +700,20 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.state
|
||||
.desc_table = GuestAddress(desc_table);
|
||||
self.vrings[index as usize]
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.state
|
||||
.avail_ring = GuestAddress(avail_ring);
|
||||
self.vrings[index as usize].write().unwrap().queue.used_ring = GuestAddress(used_ring);
|
||||
self.vrings[index as usize]
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.state
|
||||
.used_ring = GuestAddress(used_ring);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VhostUserError::InvalidParam)
|
||||
@@ -720,8 +725,7 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.next_avail = Wrapping(base as u16);
|
||||
self.vrings[index as usize].write().unwrap().queue.next_used = Wrapping(base as u16);
|
||||
.set_next_avail(base as u16);
|
||||
|
||||
let event_idx: bool = (self.acked_features & (1 << VIRTIO_RING_F_EVENT_IDX)) != 0;
|
||||
self.vrings[index as usize]
|
||||
@@ -742,7 +746,12 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
// that file descriptor is readable) on the descriptor specified by
|
||||
// VHOST_USER_SET_VRING_KICK, and stop ring upon receiving
|
||||
// VHOST_USER_GET_VRING_BASE.
|
||||
self.vrings[index as usize].write().unwrap().queue.ready = false;
|
||||
self.vrings[index as usize]
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.state
|
||||
.ready = false;
|
||||
if let Some(fd) = self.vrings[index as usize].read().unwrap().kick.as_ref() {
|
||||
for (thread_index, queues_mask) in self.queues_per_thread.iter().enumerate() {
|
||||
let shifted_queues_mask = queues_mask >> index;
|
||||
@@ -764,8 +773,7 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
.read()
|
||||
.unwrap()
|
||||
.queue
|
||||
.next_avail
|
||||
.0 as u16;
|
||||
.next_avail();
|
||||
|
||||
Ok(VhostUserVringState::new(index, u32::from(next_avail)))
|
||||
}
|
||||
@@ -783,7 +791,12 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
// that file descriptor is readable) on the descriptor specified by
|
||||
// VHOST_USER_SET_VRING_KICK, and stop ring upon receiving
|
||||
// VHOST_USER_GET_VRING_BASE.
|
||||
self.vrings[index as usize].write().unwrap().queue.ready = true;
|
||||
self.vrings[index as usize]
|
||||
.write()
|
||||
.unwrap()
|
||||
.queue
|
||||
.state
|
||||
.ready = true;
|
||||
if let Some(fd) = self.vrings[index as usize].read().unwrap().kick.as_ref() {
|
||||
for (thread_index, queues_mask) in self.queues_per_thread.iter().enumerate() {
|
||||
let shifted_queues_mask = queues_mask >> index;
|
||||
@@ -890,25 +903,14 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
)?,
|
||||
);
|
||||
|
||||
let guest_memory = if let Some(guest_memory) = &self.guest_memory {
|
||||
guest_memory.insert_region(guest_region).map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?
|
||||
} else {
|
||||
GuestMemoryMmap::from_arc_regions(vec![guest_region]).map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?
|
||||
};
|
||||
|
||||
self.backend
|
||||
.write()
|
||||
.unwrap()
|
||||
.update_memory(guest_memory.clone())
|
||||
let guest_memory = self
|
||||
.guest_memory
|
||||
.memory()
|
||||
.insert_region(guest_region)
|
||||
.map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?;
|
||||
|
||||
self.guest_memory = Some(guest_memory);
|
||||
self.guest_memory.lock().unwrap().replace(guest_memory);
|
||||
|
||||
self.mappings.push(AddrMapping {
|
||||
vmm_addr: region.user_addr,
|
||||
@@ -920,26 +922,14 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||
}
|
||||
|
||||
fn remove_mem_region(&mut self, region: &VhostUserSingleMemoryRegion) -> VhostUserResult<()> {
|
||||
let guest_memory = if let Some(guest_memory) = &self.guest_memory {
|
||||
let (updated_guest_memory, _) = guest_memory
|
||||
.remove_region(GuestAddress(region.guest_phys_addr), region.memory_size)
|
||||
.map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?;
|
||||
updated_guest_memory
|
||||
} else {
|
||||
return Err(VhostUserError::InvalidOperation);
|
||||
};
|
||||
|
||||
self.backend
|
||||
.write()
|
||||
.unwrap()
|
||||
.update_memory(guest_memory.clone())
|
||||
let (guest_memory, _) = self
|
||||
.guest_memory
|
||||
.memory()
|
||||
.remove_region(GuestAddress(region.guest_phys_addr), region.memory_size)
|
||||
.map_err(|e| {
|
||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||
})?;
|
||||
|
||||
self.guest_memory = Some(guest_memory);
|
||||
self.guest_memory.lock().unwrap().replace(guest_memory);
|
||||
|
||||
self.mappings
|
||||
.retain(|mapping| mapping.gpa_base != region.guest_phys_addr);
|
||||
|
||||
@@ -6,10 +6,10 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
block_util = { path = "../block_util" }
|
||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
clap = { version = "2.34.0", features = ["wrap_help"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
|
||||
@@ -17,7 +17,6 @@ use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Read;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::num::Wrapping;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::PathBuf;
|
||||
@@ -30,7 +29,7 @@ use std::vec::Vec;
|
||||
use std::{convert, error, fmt, io};
|
||||
use vhost::vhost_user::message::*;
|
||||
use vhost::vhost_user::Listener;
|
||||
use vhost_user_backend::{GuestMemoryMmap, VhostUserBackend, VhostUserDaemon, Vring};
|
||||
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring};
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use vm_memory::ByteValued;
|
||||
@@ -87,7 +86,6 @@ impl convert::From<Error> for io::Error {
|
||||
}
|
||||
|
||||
struct VhostUserBlkThread {
|
||||
mem: Option<GuestMemoryMmap>,
|
||||
disk_image: Arc<Mutex<dyn DiskFile>>,
|
||||
disk_image_id: Vec<u8>,
|
||||
disk_nsectors: u64,
|
||||
@@ -104,7 +102,6 @@ impl VhostUserBlkThread {
|
||||
writeback: Arc<AtomicBool>,
|
||||
) -> Result<Self> {
|
||||
Ok(VhostUserBlkThread {
|
||||
mem: None,
|
||||
disk_image,
|
||||
disk_image_id,
|
||||
disk_nsectors,
|
||||
@@ -116,22 +113,18 @@ impl VhostUserBlkThread {
|
||||
|
||||
fn process_queue(&mut self, vring: &mut Vring) -> bool {
|
||||
let mut used_any = false;
|
||||
let mem = match self.mem.as_ref() {
|
||||
Some(m) => m,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
while let Some(head) = vring.mut_queue().iter(mem).next() {
|
||||
while let Some(mut desc_chain) = vring.mut_queue().iter().unwrap().next() {
|
||||
debug!("got an element in the queue");
|
||||
let len;
|
||||
match Request::parse(&head, mem) {
|
||||
match Request::parse(&mut desc_chain) {
|
||||
Ok(mut request) => {
|
||||
debug!("element is a valid request");
|
||||
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||
let status = match request.execute(
|
||||
&mut self.disk_image.lock().unwrap().deref_mut(),
|
||||
self.disk_nsectors,
|
||||
mem,
|
||||
desc_chain.memory(),
|
||||
&self.disk_image_id,
|
||||
) {
|
||||
Ok(l) => {
|
||||
@@ -143,7 +136,10 @@ impl VhostUserBlkThread {
|
||||
e.status()
|
||||
}
|
||||
};
|
||||
mem.write_obj(status, request.status_addr).unwrap();
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(status, request.status_addr)
|
||||
.unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
error!("failed to parse available descriptor chain: {:?}", err);
|
||||
@@ -153,8 +149,8 @@ impl VhostUserBlkThread {
|
||||
|
||||
if self.event_idx {
|
||||
let queue = vring.mut_queue();
|
||||
if let Some(used_idx) = queue.add_used(mem, head.index, len) {
|
||||
if queue.needs_notification(mem, Wrapping(used_idx)) {
|
||||
if queue.add_used(desc_chain.head_index(), len).is_ok() {
|
||||
if queue.needs_notification().unwrap() {
|
||||
debug!("signalling queue");
|
||||
vring.signal_used_queue().unwrap();
|
||||
} else {
|
||||
@@ -164,7 +160,10 @@ impl VhostUserBlkThread {
|
||||
}
|
||||
} else {
|
||||
debug!("signalling queue");
|
||||
vring.mut_queue().add_used(mem, head.index, len);
|
||||
vring
|
||||
.mut_queue()
|
||||
.add_used(desc_chain.head_index(), len)
|
||||
.unwrap();
|
||||
vring.signal_used_queue().unwrap();
|
||||
used_any = true;
|
||||
}
|
||||
@@ -316,13 +315,6 @@ impl VhostUserBackend for VhostUserBlkBackend {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_memory(&mut self, mem: GuestMemoryMmap) -> VhostUserBackendResult<()> {
|
||||
for thread in self.threads.iter() {
|
||||
thread.lock().unwrap().mem = Some(mem.clone());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_event(
|
||||
&self,
|
||||
device_event: u16,
|
||||
@@ -360,9 +352,7 @@ impl VhostUserBackend for VhostUserBlkBackend {
|
||||
// calling process_queue() until it stops finding new
|
||||
// requests on the queue.
|
||||
loop {
|
||||
vring
|
||||
.mut_queue()
|
||||
.update_avail_event(thread.mem.as_ref().unwrap());
|
||||
vring.mut_queue().enable_notification().unwrap();
|
||||
if !thread.process_queue(&mut vring) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||
clap = { version = "2.34.0", features = ["wrap_help"] }
|
||||
env_logger = "0.9.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
|
||||
@@ -22,9 +22,8 @@ use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::vec::Vec;
|
||||
use vhost::vhost_user::message::*;
|
||||
use vhost::vhost_user::Listener;
|
||||
use vhost_user_backend::{GuestMemoryMmap, VhostUserBackend, VhostUserDaemon, Vring, VringWorker};
|
||||
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring, VringWorker};
|
||||
use virtio_bindings::bindings::virtio_net::*;
|
||||
use vm_memory::GuestMemoryAtomic;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -32,9 +31,9 @@ type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to create kill eventfd
|
||||
/// Failed to create kill eventfd.
|
||||
CreateKillEventFd(io::Error),
|
||||
/// Failed to parse configuration string
|
||||
/// Failed to parse configuration string.
|
||||
FailedConfigParse(OptionParserError),
|
||||
/// Failed to signal used queue.
|
||||
FailedSignalingUsedQueue(io::Error),
|
||||
@@ -42,13 +41,13 @@ pub enum Error {
|
||||
HandleEventNotEpollIn,
|
||||
/// Failed to handle unknown event.
|
||||
HandleEventUnknownEvent,
|
||||
/// Open tap device failed.
|
||||
/// Failed to open tap device.
|
||||
OpenTap(OpenTapError),
|
||||
/// No socket provided
|
||||
/// No socket provided.
|
||||
SocketParameterMissing,
|
||||
/// Underlying QueuePair error
|
||||
/// Underlying QueuePair error.
|
||||
NetQueuePair(net_util::NetQueuePairError),
|
||||
/// Failed registering the TAP listener
|
||||
/// Failed to register the TAP listener.
|
||||
RegisterTapListener(io::Error),
|
||||
}
|
||||
|
||||
@@ -81,7 +80,6 @@ impl VhostUserNetThread {
|
||||
Ok(VhostUserNetThread {
|
||||
kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?,
|
||||
net: NetQueuePair {
|
||||
mem: None,
|
||||
tap_for_write_epoll: tap.clone(),
|
||||
tap,
|
||||
rx: RxVirtio::new(),
|
||||
@@ -184,13 +182,6 @@ impl VhostUserBackend for VhostUserNetBackend {
|
||||
|
||||
fn set_event_idx(&mut self, _enabled: bool) {}
|
||||
|
||||
fn update_memory(&mut self, mem: GuestMemoryMmap) -> VhostUserBackendResult<()> {
|
||||
for thread in self.threads.iter() {
|
||||
thread.lock().unwrap().net.mem = Some(GuestMemoryAtomic::new(mem.clone()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_event(
|
||||
&self,
|
||||
device_event: u16,
|
||||
@@ -216,7 +207,7 @@ impl VhostUserBackend for VhostUserNetBackend {
|
||||
let mut vring = vrings[1].write().unwrap();
|
||||
if thread
|
||||
.net
|
||||
.process_tx(&mut vring.mut_queue())
|
||||
.process_tx(vring.mut_queue())
|
||||
.map_err(Error::NetQueuePair)?
|
||||
{
|
||||
vring
|
||||
@@ -228,7 +219,7 @@ impl VhostUserBackend for VhostUserNetBackend {
|
||||
let mut vring = vrings[0].write().unwrap();
|
||||
if thread
|
||||
.net
|
||||
.process_rx(&mut vring.mut_queue())
|
||||
.process_rx(vring.mut_queue())
|
||||
.map_err(Error::NetQueuePair)?
|
||||
{
|
||||
vring
|
||||
|
||||
@@ -10,14 +10,14 @@ io_uring = ["block_util/io_uring"]
|
||||
mshv = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.44"
|
||||
arc-swap = "1.4.0"
|
||||
anyhow = "1.0.51"
|
||||
arc-swap = "1.5.0"
|
||||
block_util = { path = "../block_util" }
|
||||
byteorder = "1.4.3"
|
||||
epoll = "4.3.1"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
io-uring = "0.5.2"
|
||||
libc = "0.2.103"
|
||||
libc = "0.2.108"
|
||||
log = "0.4.14"
|
||||
net_gen = { path = "../net_gen" }
|
||||
net_util = { path = "../net_util" }
|
||||
@@ -26,11 +26,12 @@ rate_limiter = { path = "../rate_limiter" }
|
||||
seccompiler = "0.2.0"
|
||||
serde = "1.0.130"
|
||||
serde_derive = "1.0.130"
|
||||
serde_json = "1.0.68"
|
||||
serde_json = "1.0.72"
|
||||
versionize = "0.1.6"
|
||||
versionize_derive = "0.1.4"
|
||||
vhost = { version = "0.2.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern"] }
|
||||
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
|
||||
virtio-queue = { path = "../virtio-queue" }
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.6.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
|
||||
use super::{
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||
VirtioCommon, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon,
|
||||
VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
@@ -31,11 +31,9 @@ use std::sync::mpsc;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::GuestMemory;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic,
|
||||
GuestMemoryError,
|
||||
};
|
||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -81,6 +79,12 @@ pub enum Error {
|
||||
ProcessQueueWrongEvType(u16),
|
||||
// Fail tp signal
|
||||
FailedSignal(io::Error),
|
||||
/// Descriptor chain is too short
|
||||
DescriptorChainTooShort,
|
||||
/// Failed adding used index
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
/// Failed creating an iterator over the queue
|
||||
QueueIterator(virtio_queue::Error),
|
||||
}
|
||||
|
||||
// Got from include/uapi/linux/virtio_balloon.h
|
||||
@@ -96,7 +100,7 @@ pub struct VirtioBalloonConfig {
|
||||
const CONFIG_ACTUAL_OFFSET: u64 = 4;
|
||||
const CONFIG_ACTUAL_SIZE: usize = 4;
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioBalloonConfig {}
|
||||
|
||||
struct VirtioBalloonResizeReceiver {
|
||||
@@ -152,8 +156,7 @@ impl VirtioBalloonResize {
|
||||
struct BalloonEpollHandler {
|
||||
config: Arc<Mutex<VirtioBalloonConfig>>,
|
||||
resize_receiver: VirtioBalloonResizeReceiver,
|
||||
queues: Vec<Queue>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
inflate_queue_evt: EventFd,
|
||||
deflate_queue_evt: EventFd,
|
||||
@@ -165,7 +168,7 @@ impl BalloonEpollHandler {
|
||||
fn signal(
|
||||
&self,
|
||||
int_type: &VirtioInterruptType,
|
||||
queue: Option<&Queue>,
|
||||
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> result::Result<(), Error> {
|
||||
self.interrupt_cb.trigger(int_type, queue).map_err(|e| {
|
||||
error!("Failed to signal used queue: {:?}", e);
|
||||
@@ -182,38 +185,45 @@ impl BalloonEpollHandler {
|
||||
|
||||
let mut used_desc_heads = [0; QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in self.queues[queue_index].iter(&mem) {
|
||||
used_desc_heads[used_count] = avail_desc.index;
|
||||
for mut desc_chain in self.queues[queue_index]
|
||||
.iter()
|
||||
.map_err(Error::QueueIterator)?
|
||||
{
|
||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
|
||||
used_desc_heads[used_count] = desc_chain.head_index();
|
||||
used_count += 1;
|
||||
|
||||
let data_chunk_size = size_of::<u32>();
|
||||
|
||||
// The head contains the request type which MUST be readable.
|
||||
if avail_desc.is_write_only() {
|
||||
if desc.is_write_only() {
|
||||
error!("The head contains the request type is not right");
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
if avail_desc.len as usize % data_chunk_size != 0 {
|
||||
error!("the request size {} is not right", avail_desc.len);
|
||||
if desc.len() as usize % data_chunk_size != 0 {
|
||||
error!("the request size {} is not right", desc.len());
|
||||
return Err(Error::InvalidRequest);
|
||||
}
|
||||
|
||||
let mut offset = 0u64;
|
||||
while offset < avail_desc.len as u64 {
|
||||
let addr = avail_desc.addr.checked_add(offset).unwrap();
|
||||
let pfn: u32 = mem.read_obj(addr).map_err(Error::GuestMemory)?;
|
||||
while offset < desc.len() as u64 {
|
||||
let addr = desc.addr().checked_add(offset).unwrap();
|
||||
let pfn: u32 = desc_chain
|
||||
.memory()
|
||||
.read_obj(addr)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
offset += data_chunk_size as u64;
|
||||
|
||||
let gpa = (pfn as u64) << VIRTIO_BALLOON_PFN_SHIFT;
|
||||
if let Ok(hva) = mem.get_host_address(GuestAddress(gpa)) {
|
||||
if let Ok(hva) = desc_chain.memory().get_host_address(GuestAddress(gpa)) {
|
||||
let advice = match ev_type {
|
||||
INFLATE_QUEUE_EVENT => {
|
||||
let region =
|
||||
mem.find_region(GuestAddress(gpa))
|
||||
.ok_or(Error::GuestMemory(
|
||||
GuestMemoryError::InvalidGuestAddress(GuestAddress(gpa)),
|
||||
))?;
|
||||
let region = desc_chain.memory().find_region(GuestAddress(gpa)).ok_or(
|
||||
Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
|
||||
GuestAddress(gpa),
|
||||
)),
|
||||
)?;
|
||||
if let Some(f_off) = region.file_offset() {
|
||||
let offset = hva as usize - region.as_ptr() as usize;
|
||||
let res = unsafe {
|
||||
@@ -253,7 +263,9 @@ impl BalloonEpollHandler {
|
||||
}
|
||||
|
||||
for &desc_index in &used_desc_heads[..used_count] {
|
||||
self.queues[queue_index].add_used(&mem, desc_index, 0);
|
||||
self.queues[queue_index]
|
||||
.add_used(desc_index, 0)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
if used_count > 0 {
|
||||
self.signal(&VirtioInterruptType::Queue, Some(&self.queues[queue_index]))?;
|
||||
@@ -463,9 +475,9 @@ impl VirtioDevice for Balloon {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -478,7 +490,6 @@ impl VirtioDevice for Balloon {
|
||||
ActivateError::BadActivate
|
||||
})?,
|
||||
queues,
|
||||
mem,
|
||||
interrupt_cb,
|
||||
inflate_queue_evt: queue_evts.remove(0),
|
||||
deflate_queue_evt: queue_evts.remove(0),
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler,
|
||||
RateLimiterConfig, VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType,
|
||||
EPOLL_HELPER_EVENT_LAST,
|
||||
};
|
||||
@@ -35,6 +35,7 @@ use std::{collections::HashMap, convert::TryInto};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_blk::*;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
@@ -62,6 +63,10 @@ pub enum Error {
|
||||
AsyncRequestFailure,
|
||||
/// Failed synchronizing the file
|
||||
Fsync(AsyncIoError),
|
||||
/// Failed adding used index
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
/// Failed creating an iterator over the queue
|
||||
QueueIterator(virtio_queue::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
@@ -75,7 +80,7 @@ pub struct BlockCounters {
|
||||
}
|
||||
|
||||
struct BlockEpollHandler {
|
||||
queue: Queue,
|
||||
queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
disk_image: Box<dyn AsyncIo>,
|
||||
disk_nsectors: u64,
|
||||
@@ -93,13 +98,13 @@ struct BlockEpollHandler {
|
||||
impl BlockEpollHandler {
|
||||
fn process_queue_submit(&mut self) -> Result<bool> {
|
||||
let queue = &mut self.queue;
|
||||
let mem = self.mem.memory();
|
||||
|
||||
let mut used_desc_heads = Vec::new();
|
||||
let mut used_count = 0;
|
||||
|
||||
for avail_desc in queue.iter(&mem) {
|
||||
let mut request = Request::parse(&avail_desc, &mem).map_err(Error::RequestParsing)?;
|
||||
let mut avail_iter = queue.iter().map_err(Error::QueueIterator)?;
|
||||
for mut desc_chain in &mut avail_iter {
|
||||
let mut request = Request::parse(&mut desc_chain).map_err(Error::RequestParsing)?;
|
||||
|
||||
if let Some(rate_limiter) = &mut self.rate_limiter {
|
||||
// If limiter.consume() fails it means there is no more TokenType::Ops
|
||||
@@ -107,7 +112,7 @@ impl BlockEpollHandler {
|
||||
if !rate_limiter.consume(1, TokenType::Ops) {
|
||||
// Stop processing the queue and return this descriptor chain to the
|
||||
// avail ring, for later processing.
|
||||
queue.go_to_previous_position();
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
// Exercise the rate limiter only if this request is of data transfer type.
|
||||
@@ -126,7 +131,7 @@ impl BlockEpollHandler {
|
||||
rate_limiter.manual_replenish(1, TokenType::Ops);
|
||||
// Stop processing the queue and return this descriptor chain to the
|
||||
// avail ring, for later processing.
|
||||
queue.go_to_previous_position();
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -136,29 +141,34 @@ impl BlockEpollHandler {
|
||||
|
||||
if request
|
||||
.execute_async(
|
||||
&mem,
|
||||
desc_chain.memory(),
|
||||
self.disk_nsectors,
|
||||
self.disk_image.as_mut(),
|
||||
&self.disk_image_id,
|
||||
avail_desc.index as u64,
|
||||
desc_chain.head_index() as u64,
|
||||
)
|
||||
.map_err(Error::RequestExecuting)?
|
||||
{
|
||||
self.request_list.insert(avail_desc.index, request);
|
||||
self.request_list.insert(desc_chain.head_index(), request);
|
||||
} else {
|
||||
// We use unwrap because the request parsing process already
|
||||
// checked that the status_addr was valid.
|
||||
mem.write_obj(VIRTIO_BLK_S_OK, request.status_addr).unwrap();
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj(VIRTIO_BLK_S_OK, request.status_addr)
|
||||
.unwrap();
|
||||
|
||||
// If no asynchronous operation has been submitted, we can
|
||||
// simply return the used descriptor.
|
||||
used_desc_heads.push((avail_desc.index, 0));
|
||||
used_desc_heads.push((desc_chain.head_index(), 0));
|
||||
used_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue.add_used(&mem, desc_index, len);
|
||||
queue
|
||||
.add_used(desc_index, len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
|
||||
Ok(used_count > 0)
|
||||
@@ -221,7 +231,9 @@ impl BlockEpollHandler {
|
||||
}
|
||||
|
||||
for &(desc_index, len) in used_desc_heads.iter() {
|
||||
queue.add_used(&mem, desc_index, len);
|
||||
queue
|
||||
.add_used(desc_index, len)
|
||||
.map_err(Error::QueueAddUsed)?;
|
||||
}
|
||||
|
||||
self.counters
|
||||
@@ -545,7 +557,7 @@ impl VirtioDevice for Block {
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
mut queues: Vec<Queue>,
|
||||
mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -557,7 +569,7 @@ impl VirtioDevice for Block {
|
||||
for i in 0..queues.len() {
|
||||
let queue_evt = queue_evts.remove(0);
|
||||
let queue = queues.remove(0);
|
||||
let queue_size = queue.size;
|
||||
let queue_size = queue.state.size;
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
|
||||
let rate_limiter: Option<RateLimiter> = self
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue, VirtioCommon,
|
||||
VirtioDevice, VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
|
||||
VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||
ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon, VirtioDevice,
|
||||
VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM,
|
||||
VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
@@ -24,7 +24,8 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -68,12 +69,11 @@ impl Default for VirtioConsoleConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioConsoleConfig {}
|
||||
|
||||
struct ConsoleEpollHandler {
|
||||
queues: Vec<Queue>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
||||
resizer: Arc<ConsoleResizer>,
|
||||
@@ -140,17 +140,21 @@ impl ConsoleEpollHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in recv_queue.iter(&mem) {
|
||||
let len = cmp::min(avail_desc.len as u32, in_buffer.len() as u32);
|
||||
let mut avail_iter = recv_queue.iter().unwrap();
|
||||
for mut desc_chain in &mut avail_iter {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
let len = cmp::min(desc.len() as u32, in_buffer.len() as u32);
|
||||
let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
|
||||
if let Err(e) = mem.write_slice(&source_slice[..], avail_desc.addr) {
|
||||
if let Err(e) = desc_chain
|
||||
.memory()
|
||||
.write_slice(&source_slice[..], desc.addr())
|
||||
{
|
||||
error!("Failed to write slice: {:?}", e);
|
||||
recv_queue.go_to_previous_position();
|
||||
avail_iter.go_to_previous_position();
|
||||
break;
|
||||
}
|
||||
|
||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), len);
|
||||
used_count += 1;
|
||||
|
||||
if in_buffer.is_empty() {
|
||||
@@ -159,7 +163,7 @@ impl ConsoleEpollHandler {
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
recv_queue.add_used(&mem, desc_index, len);
|
||||
recv_queue.add_used(desc_index, len).unwrap();
|
||||
}
|
||||
|
||||
used_count > 0
|
||||
@@ -177,20 +181,20 @@ impl ConsoleEpollHandler {
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in trans_queue.iter(&mem) {
|
||||
let len;
|
||||
for mut desc_chain in trans_queue.iter().unwrap() {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
if let Some(ref mut out) = self.endpoint.out_file() {
|
||||
let _ = mem.write_to(avail_desc.addr, out, avail_desc.len as usize);
|
||||
let _ = desc_chain
|
||||
.memory()
|
||||
.write_to(desc.addr(), out, desc.len() as usize);
|
||||
let _ = out.flush();
|
||||
}
|
||||
len = avail_desc.len;
|
||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), desc.len());
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
trans_queue.add_used(&mem, desc_index, len);
|
||||
trans_queue.add_used(desc_index, len).unwrap();
|
||||
}
|
||||
used_count > 0
|
||||
}
|
||||
@@ -477,9 +481,9 @@ impl VirtioDevice for Console {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -498,7 +502,6 @@ impl VirtioDevice for Console {
|
||||
|
||||
let mut handler = ConsoleEpollHandler {
|
||||
queues,
|
||||
mem,
|
||||
interrupt_cb,
|
||||
in_buffer: self.in_buffer.clone(),
|
||||
endpoint: self.endpoint.clone(),
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::{ActivateError, ActivateResult, Error, Queue};
|
||||
use crate::{ActivateError, ActivateResult, Error};
|
||||
use crate::{GuestMemoryMmap, GuestRegionMmap};
|
||||
use libc::EFD_NONBLOCK;
|
||||
use std::collections::HashMap;
|
||||
@@ -17,6 +17,7 @@ use std::sync::{
|
||||
Arc, Barrier,
|
||||
};
|
||||
use std::thread;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestUsize};
|
||||
use vm_migration::{MigratableError, Pausable};
|
||||
use vm_virtio::VirtioDeviceType;
|
||||
@@ -31,9 +32,13 @@ pub trait VirtioInterrupt: Send + Sync {
|
||||
fn trigger(
|
||||
&self,
|
||||
int_type: &VirtioInterruptType,
|
||||
queue: Option<&Queue>,
|
||||
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> std::result::Result<(), std::io::Error>;
|
||||
fn notifier(&self, _int_type: &VirtioInterruptType, _queue: Option<&Queue>) -> Option<EventFd> {
|
||||
fn notifier(
|
||||
&self,
|
||||
_int_type: &VirtioInterruptType,
|
||||
_queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> Option<EventFd> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -107,7 +112,7 @@ pub trait VirtioDevice: Send {
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_evt: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult;
|
||||
|
||||
@@ -247,7 +252,7 @@ impl VirtioCommon {
|
||||
|
||||
pub fn activate(
|
||||
&mut self,
|
||||
queues: &[Queue],
|
||||
queues: &[Queue<GuestMemoryAtomic<GuestMemoryMmap>>],
|
||||
queue_evts: &[EventFd],
|
||||
interrupt_cb: &Arc<dyn VirtioInterrupt>,
|
||||
) -> ActivateResult {
|
||||
|
||||
@@ -26,6 +26,7 @@ pub enum EpollHelperError {
|
||||
Ctl(std::io::Error),
|
||||
IoError(std::io::Error),
|
||||
Wait(std::io::Error),
|
||||
QueueRingIndex(virtio_queue::Error),
|
||||
}
|
||||
|
||||
pub const EPOLL_HELPER_EVENT_PAUSE: u16 = 0;
|
||||
@@ -33,7 +34,7 @@ pub const EPOLL_HELPER_EVENT_KILL: u16 = 1;
|
||||
pub const EPOLL_HELPER_EVENT_LAST: u16 = 15;
|
||||
|
||||
pub trait EpollHelperHandler {
|
||||
// Return true if execution of the loop should be stopped
|
||||
// Return true if the loop execution should be stopped
|
||||
fn handle_event(&mut self, helper: &mut EpollHelper, event: &epoll::Event) -> bool;
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ impl EpollHelper {
|
||||
|
||||
// Drain pause event after the device has been resumed.
|
||||
// This ensures the pause event has been seen by each
|
||||
// and every thread related to this virtio device.
|
||||
// thread related to this virtio device.
|
||||
let _ = self.pause_evt.read();
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateResult, DescriptorChain, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||
VirtioCommon, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
||||
ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon, VirtioDevice,
|
||||
VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
@@ -23,11 +23,9 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Barrier, RwLock};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::{AccessPlatform, DescriptorChain, Queue};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic,
|
||||
GuestMemoryError,
|
||||
};
|
||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -75,35 +73,32 @@ const VIRTIO_IOMMU_PAGE_SIZE_MASK: u64 = (2 << 20) | (4 << 10);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuRange32 {
|
||||
start: u32,
|
||||
end: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuRange32 {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuRange64 {
|
||||
start: u64,
|
||||
end: u64,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuRange64 {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuConfig {
|
||||
page_size_mask: u64,
|
||||
input_range: VirtioIommuRange64,
|
||||
domain_range: VirtioIommuRange32,
|
||||
probe_size: u32,
|
||||
bypass: u8,
|
||||
reserved: [u8; 7],
|
||||
_reserved: [u8; 7],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuConfig {}
|
||||
|
||||
/// Virtio IOMMU request type
|
||||
const VIRTIO_IOMMU_T_ATTACH: u8 = 1;
|
||||
const VIRTIO_IOMMU_T_DETACH: u8 = 2;
|
||||
@@ -115,11 +110,9 @@ const VIRTIO_IOMMU_T_PROBE: u8 = 5;
|
||||
#[repr(packed)]
|
||||
struct VirtioIommuReqHead {
|
||||
type_: u8,
|
||||
reserved: [u8; 3],
|
||||
_reserved: [u8; 3],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqHead {}
|
||||
|
||||
/// Virtio IOMMU request status
|
||||
const VIRTIO_IOMMU_S_OK: u8 = 0;
|
||||
#[allow(unused)]
|
||||
@@ -141,35 +134,30 @@ const VIRTIO_IOMMU_S_NOMEM: u8 = 8;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuReqTail {
|
||||
status: u8,
|
||||
reserved: [u8; 3],
|
||||
_reserved: [u8; 3],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqTail {}
|
||||
|
||||
/// ATTACH request
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
struct VirtioIommuReqAttach {
|
||||
domain: u32,
|
||||
endpoint: u32,
|
||||
reserved: [u8; 8],
|
||||
_reserved: [u8; 8],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqAttach {}
|
||||
|
||||
/// DETACH request
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
struct VirtioIommuReqDetach {
|
||||
domain: u32,
|
||||
endpoint: u32,
|
||||
reserved: [u8; 8],
|
||||
_reserved: [u8; 8],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqDetach {}
|
||||
|
||||
/// Virtio IOMMU request MAP flags
|
||||
#[allow(unused)]
|
||||
const VIRTIO_IOMMU_MAP_F_READ: u32 = 1;
|
||||
@@ -189,11 +177,9 @@ struct VirtioIommuReqMap {
|
||||
virt_start: u64,
|
||||
virt_end: u64,
|
||||
phys_start: u64,
|
||||
flags: u32,
|
||||
_flags: u32,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqMap {}
|
||||
|
||||
/// UNMAP request
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
@@ -201,11 +187,9 @@ struct VirtioIommuReqUnmap {
|
||||
domain: u32,
|
||||
virt_start: u64,
|
||||
virt_end: u64,
|
||||
reserved: [u8; 4],
|
||||
_reserved: [u8; 4],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqUnmap {}
|
||||
|
||||
/// Virtio IOMMU request PROBE types
|
||||
#[allow(unused)]
|
||||
const VIRTIO_IOMMU_PROBE_T_NONE: u16 = 0;
|
||||
@@ -216,22 +200,20 @@ const VIRTIO_IOMMU_PROBE_T_MASK: u16 = 0xfff;
|
||||
/// PROBE request
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuReqProbe {
|
||||
endpoint: u32,
|
||||
reserved: [u64; 8],
|
||||
_reserved: [u64; 8],
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuReqProbe {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuProbeProperty {
|
||||
type_: u16,
|
||||
length: u16,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuProbeProperty {}
|
||||
|
||||
/// Virtio IOMMU request PROBE property RESV_MEM subtypes
|
||||
#[allow(unused)]
|
||||
const VIRTIO_IOMMU_RESV_MEM_T_RESERVED: u8 = 0;
|
||||
@@ -239,15 +221,14 @@ const VIRTIO_IOMMU_RESV_MEM_T_MSI: u8 = 1;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct VirtioIommuProbeResvMem {
|
||||
subtype: u8,
|
||||
reserved: [u8; 3],
|
||||
_reserved: [u8; 3],
|
||||
start: u64,
|
||||
end: u64,
|
||||
}
|
||||
|
||||
unsafe impl ByteValued for VirtioIommuProbeResvMem {}
|
||||
|
||||
/// Virtio IOMMU fault flags
|
||||
#[allow(unused)]
|
||||
const VIRTIO_IOMMU_FAULT_F_READ: u32 = 1;
|
||||
@@ -279,6 +260,19 @@ struct VirtioIommuFault {
|
||||
address: u64,
|
||||
}
|
||||
|
||||
// SAFETY: these data structures only contain integers and have no implicit padding
|
||||
unsafe impl ByteValued for VirtioIommuRange32 {}
|
||||
unsafe impl ByteValued for VirtioIommuRange64 {}
|
||||
unsafe impl ByteValued for VirtioIommuConfig {}
|
||||
unsafe impl ByteValued for VirtioIommuReqHead {}
|
||||
unsafe impl ByteValued for VirtioIommuReqTail {}
|
||||
unsafe impl ByteValued for VirtioIommuReqAttach {}
|
||||
unsafe impl ByteValued for VirtioIommuReqDetach {}
|
||||
unsafe impl ByteValued for VirtioIommuReqMap {}
|
||||
unsafe impl ByteValued for VirtioIommuReqUnmap {}
|
||||
unsafe impl ByteValued for VirtioIommuReqProbe {}
|
||||
unsafe impl ByteValued for VirtioIommuProbeProperty {}
|
||||
unsafe impl ByteValued for VirtioIommuProbeResvMem {}
|
||||
unsafe impl ByteValued for VirtioIommuFault {}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -345,27 +339,36 @@ impl Request {
|
||||
// is created based on the information provided from the guest driver for
|
||||
// virtio-iommu (giving the link device_id <=> domain).
|
||||
fn parse(
|
||||
avail_desc: &DescriptorChain,
|
||||
mem: &GuestMemoryMmap,
|
||||
desc_chain: &mut DescriptorChain<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
mapping: &Arc<IommuMapping>,
|
||||
ext_mapping: &BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||
ext_domain_mapping: &mut BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||
msi_iova_space: (u64, u64),
|
||||
) -> result::Result<usize, Error> {
|
||||
// The head contains the request type which MUST be readable.
|
||||
if avail_desc.is_write_only() {
|
||||
let desc = desc_chain
|
||||
.next()
|
||||
.ok_or(Error::DescriptorChainTooShort)
|
||||
.map_err(|e| {
|
||||
error!("Missing head descriptor");
|
||||
e
|
||||
})?;
|
||||
|
||||
// The descriptor contains the request type which MUST be readable.
|
||||
if desc.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
if (avail_desc.len as usize) < size_of::<VirtioIommuReqHead>() {
|
||||
if (desc.len() as usize) < size_of::<VirtioIommuReqHead>() {
|
||||
return Err(Error::InvalidRequest);
|
||||
}
|
||||
|
||||
let req_head: VirtioIommuReqHead =
|
||||
mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?;
|
||||
let req_head: VirtioIommuReqHead = desc_chain
|
||||
.memory()
|
||||
.read_obj(desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let req_offset = size_of::<VirtioIommuReqHead>();
|
||||
let desc_size_left = (avail_desc.len as usize) - req_offset;
|
||||
let req_addr = if let Some(addr) = avail_desc.addr.checked_add(req_offset as u64) {
|
||||
let desc_size_left = (desc.len() as usize) - req_offset;
|
||||
let req_addr = if let Some(addr) = desc.addr().checked_add(req_offset as u64) {
|
||||
addr
|
||||
} else {
|
||||
return Err(Error::InvalidRequest);
|
||||
@@ -382,7 +385,8 @@ impl Request {
|
||||
return Err(Error::InvalidAttachRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqAttach = mem
|
||||
let req: VirtioIommuReqAttach = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Attach request {:?}", req);
|
||||
@@ -412,7 +416,8 @@ impl Request {
|
||||
return Err(Error::InvalidDetachRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqDetach = mem
|
||||
let req: VirtioIommuReqDetach = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Detach request {:?}", req);
|
||||
@@ -438,7 +443,8 @@ impl Request {
|
||||
return Err(Error::InvalidMapRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqMap = mem
|
||||
let req: VirtioIommuReqMap = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Map request {:?}", req);
|
||||
@@ -474,7 +480,8 @@ impl Request {
|
||||
return Err(Error::InvalidUnmapRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqUnmap = mem
|
||||
let req: VirtioIommuReqUnmap = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Unmap request {:?}", req);
|
||||
@@ -503,7 +510,8 @@ impl Request {
|
||||
return Err(Error::InvalidProbeRequest);
|
||||
}
|
||||
|
||||
let req: VirtioIommuReqProbe = mem
|
||||
let req: VirtioIommuReqProbe = desc_chain
|
||||
.memory()
|
||||
.read_obj(req_addr as GuestAddress)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
debug!("Probe request {:?}", req);
|
||||
@@ -527,16 +535,14 @@ impl Request {
|
||||
_ => return Err(Error::InvalidRequest),
|
||||
};
|
||||
|
||||
let status_desc = avail_desc
|
||||
.next_descriptor()
|
||||
.ok_or(Error::DescriptorChainTooShort)?;
|
||||
let status_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
|
||||
// The status MUST always be writable
|
||||
if !status_desc.is_write_only() {
|
||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||
}
|
||||
|
||||
if status_desc.len < hdr_len + size_of::<VirtioIommuReqTail>() as u32 {
|
||||
if status_desc.len() < hdr_len + size_of::<VirtioIommuReqTail>() as u32 {
|
||||
return Err(Error::BufferLengthTooSmall);
|
||||
}
|
||||
|
||||
@@ -546,7 +552,9 @@ impl Request {
|
||||
};
|
||||
reply.extend_from_slice(tail.as_slice());
|
||||
|
||||
mem.write_slice(reply.as_slice(), status_desc.addr)
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_slice(reply.as_slice(), status_desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
|
||||
Ok((hdr_len as usize) + size_of::<VirtioIommuReqTail>())
|
||||
@@ -554,8 +562,7 @@ impl Request {
|
||||
}
|
||||
|
||||
struct IommuEpollHandler {
|
||||
queues: Vec<Queue>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
kill_evt: EventFd,
|
||||
@@ -570,11 +577,9 @@ impl IommuEpollHandler {
|
||||
fn request_queue(&mut self) -> bool {
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in self.queues[0].iter(&mem) {
|
||||
for mut desc_chain in self.queues[0].iter().unwrap() {
|
||||
let len = match Request::parse(
|
||||
&avail_desc,
|
||||
&mem,
|
||||
&mut desc_chain,
|
||||
&self.mapping,
|
||||
&self.ext_mapping,
|
||||
&mut self.ext_domain_mapping,
|
||||
@@ -587,12 +592,12 @@ impl IommuEpollHandler {
|
||||
}
|
||||
};
|
||||
|
||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), len);
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
self.queues[0].add_used(&mem, desc_index, len);
|
||||
self.queues[0].add_used(desc_index, len).unwrap();
|
||||
}
|
||||
used_count > 0
|
||||
}
|
||||
@@ -601,7 +606,10 @@ impl IommuEpollHandler {
|
||||
false
|
||||
}
|
||||
|
||||
fn signal_used_queue(&self, queue: &Queue) -> result::Result<(), DeviceError> {
|
||||
fn signal_used_queue(
|
||||
&self,
|
||||
queue: &Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> result::Result<(), DeviceError> {
|
||||
self.interrupt_cb
|
||||
.trigger(&VirtioInterruptType::Queue, Some(queue))
|
||||
.map_err(|e| {
|
||||
@@ -659,12 +667,13 @@ impl EpollHelperHandler for IommuEpollHandler {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Versionize)]
|
||||
#[derive(Clone, Copy, Debug, Versionize)]
|
||||
struct Mapping {
|
||||
gpa: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IommuMapping {
|
||||
// Domain related to an endpoint.
|
||||
endpoints: Arc<RwLock<BTreeMap<u32, u32>>>,
|
||||
@@ -697,6 +706,24 @@ impl DmaRemapping for IommuMapping {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccessPlatformMapping {
|
||||
id: u32,
|
||||
mapping: Arc<IommuMapping>,
|
||||
}
|
||||
|
||||
impl AccessPlatformMapping {
|
||||
pub fn new(id: u32, mapping: Arc<IommuMapping>) -> Self {
|
||||
AccessPlatformMapping { id, mapping }
|
||||
}
|
||||
}
|
||||
|
||||
impl AccessPlatform for AccessPlatformMapping {
|
||||
fn translate(&self, base: u64, _size: u64) -> std::result::Result<u64, std::io::Error> {
|
||||
self.mapping.translate(self.id, base)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iommu {
|
||||
common: VirtioCommon,
|
||||
id: String,
|
||||
@@ -832,16 +859,15 @@ impl VirtioDevice for Iommu {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
let mut handler = IommuEpollHandler {
|
||||
queues,
|
||||
mem,
|
||||
interrupt_cb,
|
||||
queue_evts,
|
||||
kill_evt,
|
||||
|
||||
@@ -51,7 +51,7 @@ pub use self::rng::*;
|
||||
pub use self::vsock::*;
|
||||
pub use self::watchdog::*;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress, GuestMemory};
|
||||
use vm_virtio::{queue::*, VirtioDeviceType};
|
||||
use vm_virtio::VirtioDeviceType;
|
||||
|
||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||
@@ -115,6 +115,8 @@ pub enum Error {
|
||||
SetShmRegionsNotSupported,
|
||||
NetQueuePair(::net_util::NetQueuePairError),
|
||||
ApplySeccompFilter(seccompiler::Error),
|
||||
QueueAddUsed(virtio_queue::Error),
|
||||
QueueIterator(virtio_queue::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq)]
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateError, ActivateResult, DescriptorChain, EpollHelper, EpollHelperError,
|
||||
EpollHelperHandler, Queue, VirtioCommon, VirtioDevice, VirtioDeviceType,
|
||||
EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon,
|
||||
VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
@@ -35,10 +34,11 @@ use std::sync::mpsc;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_queue::{DescriptorChain, Queue};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic,
|
||||
GuestMemoryError, GuestMemoryRegion,
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError,
|
||||
GuestMemoryRegion,
|
||||
};
|
||||
use vm_migration::protocol::MemoryRangeTable;
|
||||
use vm_migration::{
|
||||
@@ -149,7 +149,7 @@ struct VirtioMemReq {
|
||||
padding_1: [u16; 3],
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioMemReq {}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -160,7 +160,7 @@ struct VirtioMemResp {
|
||||
state: u16,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioMemResp {}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -186,7 +186,7 @@ pub struct VirtioMemConfig {
|
||||
requested_size: u64,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioMemConfig {}
|
||||
|
||||
impl VirtioMemConfig {
|
||||
@@ -277,34 +277,35 @@ struct Request {
|
||||
|
||||
impl Request {
|
||||
fn parse(
|
||||
avail_desc: &DescriptorChain,
|
||||
mem: &GuestMemoryMmap,
|
||||
desc_chain: &mut DescriptorChain<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> result::Result<Request, Error> {
|
||||
// The head contains the request type which MUST be readable.
|
||||
if avail_desc.is_write_only() {
|
||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
// The descriptor contains the request type which MUST be readable.
|
||||
if desc.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
if avail_desc.len as usize != size_of::<VirtioMemReq>() {
|
||||
if desc.len() as usize != size_of::<VirtioMemReq>() {
|
||||
return Err(Error::InvalidRequest);
|
||||
}
|
||||
let req: VirtioMemReq = mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?;
|
||||
let req: VirtioMemReq = desc_chain
|
||||
.memory()
|
||||
.read_obj(desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
|
||||
let status_desc = avail_desc
|
||||
.next_descriptor()
|
||||
.ok_or(Error::DescriptorChainTooShort)?;
|
||||
let status_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
|
||||
// The status MUST always be writable
|
||||
if !status_desc.is_write_only() {
|
||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||
}
|
||||
|
||||
if (status_desc.len as usize) < size_of::<VirtioMemResp>() {
|
||||
if (status_desc.len() as usize) < size_of::<VirtioMemResp>() {
|
||||
return Err(Error::BufferLengthTooSmall);
|
||||
}
|
||||
|
||||
Ok(Request {
|
||||
req,
|
||||
status_addr: status_desc.addr,
|
||||
status_addr: status_desc.addr(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -455,8 +456,7 @@ struct MemEpollHandler {
|
||||
blocks_state: Arc<Mutex<BlocksState>>,
|
||||
config: Arc<Mutex<VirtioMemConfig>>,
|
||||
resize: ResizeSender,
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evt: EventFd,
|
||||
kill_evt: EventFd,
|
||||
@@ -656,12 +656,16 @@ impl MemEpollHandler {
|
||||
fn process_queue(&mut self) -> bool {
|
||||
let mut request_list = Vec::new();
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in self.queue.iter(&mem) {
|
||||
request_list.push((avail_desc.index, Request::parse(&avail_desc, &mem)));
|
||||
|
||||
for mut desc_chain in self.queue.iter().unwrap() {
|
||||
request_list.push((
|
||||
desc_chain.head_index(),
|
||||
Request::parse(&mut desc_chain),
|
||||
desc_chain.memory().clone(),
|
||||
));
|
||||
}
|
||||
|
||||
for (desc_index, request) in request_list.iter() {
|
||||
for (head_index, request, memory) in request_list {
|
||||
let len = match request {
|
||||
Err(e) => {
|
||||
error!("failed parse VirtioMemReq: {:?}", e);
|
||||
@@ -671,21 +675,21 @@ impl MemEpollHandler {
|
||||
VIRTIO_MEM_REQ_PLUG => {
|
||||
let resp_type =
|
||||
self.state_change_request(r.req.addr, r.req.nb_blocks, true);
|
||||
r.send_response(&mem, resp_type, 0u16)
|
||||
r.send_response(&memory, resp_type, 0u16)
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG => {
|
||||
let resp_type =
|
||||
self.state_change_request(r.req.addr, r.req.nb_blocks, false);
|
||||
r.send_response(&mem, resp_type, 0u16)
|
||||
r.send_response(&memory, resp_type, 0u16)
|
||||
}
|
||||
VIRTIO_MEM_REQ_UNPLUG_ALL => {
|
||||
let resp_type = self.unplug_all();
|
||||
r.send_response(&mem, resp_type, 0u16)
|
||||
r.send_response(&memory, resp_type, 0u16)
|
||||
}
|
||||
VIRTIO_MEM_REQ_STATE => {
|
||||
let (resp_type, resp_state) =
|
||||
self.state_request(r.req.addr, r.req.nb_blocks);
|
||||
r.send_response(&mem, resp_type, resp_state)
|
||||
r.send_response(&memory, resp_type, resp_state)
|
||||
}
|
||||
_ => {
|
||||
error!("VirtioMemReq unknown request type {:?}", r.req.req_type);
|
||||
@@ -694,8 +698,7 @@ impl MemEpollHandler {
|
||||
},
|
||||
};
|
||||
|
||||
self.queue.add_used(&mem, *desc_index, len);
|
||||
|
||||
self.queue.add_used(head_index, len).unwrap();
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
@@ -990,9 +993,9 @@ impl VirtioDevice for Mem {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
mut queues: Vec<Queue>,
|
||||
mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -1004,7 +1007,6 @@ impl VirtioDevice for Mem {
|
||||
config: self.config.clone(),
|
||||
resize: self.resize.clone(),
|
||||
queue: queues.remove(0),
|
||||
mem,
|
||||
interrupt_cb,
|
||||
queue_evt: queue_evts.remove(0),
|
||||
kill_evt,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler,
|
||||
RateLimiterConfig, VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType,
|
||||
EPOLL_HELPER_EVENT_LAST,
|
||||
};
|
||||
@@ -35,7 +35,8 @@ use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use virtio_bindings::bindings::virtio_net::*;
|
||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, GuestMemoryAtomic};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -45,12 +46,11 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
const CTRL_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
|
||||
pub struct NetCtrlEpollHandler {
|
||||
pub mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
pub kill_evt: EventFd,
|
||||
pub pause_evt: EventFd,
|
||||
pub ctrl_q: CtrlQueue,
|
||||
pub queue_evt: EventFd,
|
||||
pub queue: Queue,
|
||||
pub queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
}
|
||||
|
||||
impl NetCtrlEpollHandler {
|
||||
@@ -72,12 +72,11 @@ impl EpollHelperHandler for NetCtrlEpollHandler {
|
||||
let ev_type = event.data as u16;
|
||||
match ev_type {
|
||||
CTRL_QUEUE_EVENT => {
|
||||
let mem = self.mem.memory();
|
||||
if let Err(e) = self.queue_evt.read() {
|
||||
error!("failed to get ctl queue event: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
if let Err(e) = self.ctrl_q.process(&mem, &mut self.queue) {
|
||||
if let Err(e) = self.ctrl_q.process(&mut self.queue) {
|
||||
error!("failed to process ctrl queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
@@ -125,7 +124,7 @@ struct NetEpollHandler {
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
queue_pair: Vec<Queue>,
|
||||
queue_pair: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queue_evt_pair: Vec<EventFd>,
|
||||
// Always generate interrupts until the driver has signalled to the device.
|
||||
// This mitigates a problem with interrupts from tap events being "lost" upon
|
||||
@@ -135,7 +134,10 @@ struct NetEpollHandler {
|
||||
}
|
||||
|
||||
impl NetEpollHandler {
|
||||
fn signal_used_queue(&self, queue: &Queue) -> result::Result<(), DeviceError> {
|
||||
fn signal_used_queue(
|
||||
&self,
|
||||
queue: &Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> result::Result<(), DeviceError> {
|
||||
self.interrupt_cb
|
||||
.trigger(&VirtioInterruptType::Queue, Some(queue))
|
||||
.map_err(|e| {
|
||||
@@ -235,8 +237,11 @@ impl NetEpollHandler {
|
||||
// If there are some already available descriptors on the RX queue,
|
||||
// then we can start the thread while listening onto the TAP.
|
||||
if self.queue_pair[0]
|
||||
.available_descriptors(&self.net.mem.as_ref().unwrap().memory())
|
||||
.unwrap()
|
||||
.used_idx(Ordering::Acquire)
|
||||
.map_err(EpollHelperError::QueueRingIndex)?
|
||||
< self.queue_pair[0]
|
||||
.avail_idx(Ordering::Acquire)
|
||||
.map_err(EpollHelperError::QueueRingIndex)?
|
||||
{
|
||||
helper.add_event(self.net.tap.as_raw_fd(), RX_TAP_EVENT)?;
|
||||
self.net.rx_tap_listening = true;
|
||||
@@ -479,6 +484,7 @@ impl Net {
|
||||
|
||||
for fd in fds.iter() {
|
||||
// Duplicate so that it can survive reboots
|
||||
// SAFETY: FFI call to dup. Trivially safe.
|
||||
let fd = unsafe { libc::dup(*fd) };
|
||||
if fd < 0 {
|
||||
return Err(Error::DuplicateTapFd(std::io::Error::last_os_error()));
|
||||
@@ -549,9 +555,9 @@ impl VirtioDevice for Net {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
mut queues: Vec<Queue>,
|
||||
mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -563,7 +569,6 @@ impl VirtioDevice for Net {
|
||||
|
||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||
let mut ctrl_handler = NetCtrlEpollHandler {
|
||||
mem: mem.clone(),
|
||||
kill_evt,
|
||||
pause_evt,
|
||||
ctrl_q: CtrlQueue::new(self.taps.clone()),
|
||||
@@ -632,7 +637,6 @@ impl VirtioDevice for Net {
|
||||
|
||||
let mut handler = NetEpollHandler {
|
||||
net: NetQueuePair {
|
||||
mem: Some(mem.clone()),
|
||||
tap_for_write_epoll: tap.clone(),
|
||||
tap,
|
||||
rx,
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateError, ActivateResult, DescriptorChain, EpollHelper, EpollHelperError,
|
||||
EpollHelperHandler, Queue, UserspaceMapping, VirtioCommon, VirtioDevice, VirtioDeviceType,
|
||||
EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler,
|
||||
UserspaceMapping, VirtioCommon, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST,
|
||||
VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
@@ -27,10 +27,8 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic,
|
||||
GuestMemoryError,
|
||||
};
|
||||
use virtio_queue::{DescriptorChain, Queue};
|
||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -52,7 +50,7 @@ struct VirtioPmemConfig {
|
||||
size: u64,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioPmemConfig {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
@@ -61,7 +59,7 @@ struct VirtioPmemReq {
|
||||
type_: u32,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioPmemReq {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
@@ -70,7 +68,7 @@ struct VirtioPmemResp {
|
||||
ret: u32,
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
// SAFETY: it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioPmemResp {}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -116,48 +114,48 @@ struct Request {
|
||||
|
||||
impl Request {
|
||||
fn parse(
|
||||
avail_desc: &DescriptorChain,
|
||||
mem: &GuestMemoryMmap,
|
||||
desc_chain: &mut DescriptorChain<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
) -> result::Result<Request, Error> {
|
||||
// The head contains the request type which MUST be readable.
|
||||
if avail_desc.is_write_only() {
|
||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
// The descriptor contains the request type which MUST be readable.
|
||||
if desc.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
if avail_desc.len as usize != size_of::<VirtioPmemReq>() {
|
||||
if desc.len() as usize != size_of::<VirtioPmemReq>() {
|
||||
return Err(Error::InvalidRequest);
|
||||
}
|
||||
|
||||
let request: VirtioPmemReq = mem.read_obj(avail_desc.addr).map_err(Error::GuestMemory)?;
|
||||
let request: VirtioPmemReq = desc_chain
|
||||
.memory()
|
||||
.read_obj(desc.addr())
|
||||
.map_err(Error::GuestMemory)?;
|
||||
|
||||
let request_type = match request.type_ {
|
||||
VIRTIO_PMEM_REQ_TYPE_FLUSH => RequestType::Flush,
|
||||
_ => return Err(Error::InvalidRequest),
|
||||
};
|
||||
|
||||
let status_desc = avail_desc
|
||||
.next_descriptor()
|
||||
.ok_or(Error::DescriptorChainTooShort)?;
|
||||
let status_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||
|
||||
// The status MUST always be writable
|
||||
if !status_desc.is_write_only() {
|
||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||
}
|
||||
|
||||
if (status_desc.len as usize) < size_of::<VirtioPmemResp>() {
|
||||
if (status_desc.len() as usize) < size_of::<VirtioPmemResp>() {
|
||||
return Err(Error::BufferLengthTooSmall);
|
||||
}
|
||||
|
||||
Ok(Request {
|
||||
type_: request_type,
|
||||
status_addr: status_desc.addr,
|
||||
status_addr: status_desc.addr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct PmemEpollHandler {
|
||||
queue: Queue,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queue: Queue<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
disk: File,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evt: EventFd,
|
||||
@@ -169,9 +167,8 @@ impl PmemEpollHandler {
|
||||
fn process_queue(&mut self) -> bool {
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in self.queue.iter(&mem) {
|
||||
let len = match Request::parse(&avail_desc, &mem) {
|
||||
for mut desc_chain in self.queue.iter().unwrap() {
|
||||
let len = match Request::parse(&mut desc_chain) {
|
||||
Ok(ref req) if (req.type_ == RequestType::Flush) => {
|
||||
let status_code = match self.disk.sync_all() {
|
||||
Ok(()) => VIRTIO_PMEM_RESP_TYPE_OK,
|
||||
@@ -182,7 +179,7 @@ impl PmemEpollHandler {
|
||||
};
|
||||
|
||||
let resp = VirtioPmemResp { ret: status_code };
|
||||
match mem.write_obj(resp, req.status_addr) {
|
||||
match desc_chain.memory().write_obj(resp, req.status_addr) {
|
||||
Ok(_) => size_of::<VirtioPmemResp>() as u32,
|
||||
Err(e) => {
|
||||
error!("bad guest memory address: {}", e);
|
||||
@@ -201,12 +198,12 @@ impl PmemEpollHandler {
|
||||
}
|
||||
};
|
||||
|
||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), len);
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
self.queue.add_used(&mem, desc_index, len);
|
||||
self.queue.add_used(desc_index, len).unwrap();
|
||||
}
|
||||
used_count > 0
|
||||
}
|
||||
@@ -369,9 +366,9 @@ impl VirtioDevice for Pmem {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
mut queues: Vec<Queue>,
|
||||
mut queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -383,7 +380,6 @@ impl VirtioDevice for Pmem {
|
||||
})?;
|
||||
let mut handler = PmemEpollHandler {
|
||||
queue: queues.remove(0),
|
||||
mem,
|
||||
disk,
|
||||
interrupt_cb,
|
||||
queue_evt: queue_evts.remove(0),
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
use super::Error as DeviceError;
|
||||
use super::{
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||
VirtioCommon, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM,
|
||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon,
|
||||
VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM,
|
||||
VIRTIO_F_VERSION_1,
|
||||
};
|
||||
use crate::seccomp_filters::Thread;
|
||||
@@ -21,7 +21,8 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{Bytes, GuestMemoryAtomic};
|
||||
use vm_migration::VersionMapped;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -33,8 +34,7 @@ const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
|
||||
const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
|
||||
struct RngEpollHandler {
|
||||
queues: Vec<Queue>,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
random_file: File,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queue_evt: EventFd,
|
||||
@@ -48,31 +48,28 @@ impl RngEpollHandler {
|
||||
|
||||
let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
|
||||
let mut used_count = 0;
|
||||
let mem = self.mem.memory();
|
||||
for avail_desc in queue.iter(&mem) {
|
||||
for mut desc_chain in queue.iter().unwrap() {
|
||||
let desc = desc_chain.next().unwrap();
|
||||
let mut len = 0;
|
||||
|
||||
// Drivers can only read from the random device.
|
||||
if avail_desc.is_write_only() {
|
||||
if desc.is_write_only() {
|
||||
// Fill the read with data from the random device on the host.
|
||||
if mem
|
||||
.read_from(
|
||||
avail_desc.addr,
|
||||
&mut self.random_file,
|
||||
avail_desc.len as usize,
|
||||
)
|
||||
if desc_chain
|
||||
.memory()
|
||||
.read_from(desc.addr(), &mut self.random_file, desc.len() as usize)
|
||||
.is_ok()
|
||||
{
|
||||
len = avail_desc.len;
|
||||
len = desc.len();
|
||||
}
|
||||
}
|
||||
|
||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||
used_desc_heads[used_count] = (desc_chain.head_index(), len);
|
||||
used_count += 1;
|
||||
}
|
||||
|
||||
for &(desc_index, len) in &used_desc_heads[..used_count] {
|
||||
queue.add_used(&mem, desc_index, len);
|
||||
queue.add_used(desc_index, len).unwrap();
|
||||
}
|
||||
used_count > 0
|
||||
}
|
||||
@@ -213,9 +210,9 @@ impl VirtioDevice for Rng {
|
||||
|
||||
fn activate(
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
mut queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
@@ -228,7 +225,6 @@ impl VirtioDevice for Rng {
|
||||
})?;
|
||||
let mut handler = RngEpollHandler {
|
||||
queues,
|
||||
mem,
|
||||
random_file,
|
||||
interrupt_cb,
|
||||
queue_evt: queue_evts.remove(0),
|
||||
|
||||
@@ -44,7 +44,7 @@ where
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::panic::catch_unwind(AssertUnwindSafe(move || f()))
|
||||
std::panic::catch_unwind(AssertUnwindSafe(f))
|
||||
.or_else(|_| {
|
||||
error!("{} thread panicked", thread_name);
|
||||
thread_exit_evt.write(1)
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use crate::{Queue, VirtioDevice};
|
||||
use crate::{GuestMemoryMmap, VirtioDevice};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_memory::GuestAddress;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{GuestAddress, GuestMemoryAtomic};
|
||||
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, VersionMapped};
|
||||
|
||||
#[derive(Clone, Versionize)]
|
||||
@@ -83,7 +84,7 @@ impl VirtioPciCommonConfig {
|
||||
&mut self,
|
||||
offset: u64,
|
||||
data: &mut [u8],
|
||||
queues: &mut Vec<Queue>,
|
||||
queues: &mut Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
device: Arc<Mutex<dyn VirtioDevice>>,
|
||||
) {
|
||||
assert!(data.len() <= 8);
|
||||
@@ -113,7 +114,7 @@ impl VirtioPciCommonConfig {
|
||||
&mut self,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
queues: &mut Vec<Queue>,
|
||||
queues: &mut Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
device: Arc<Mutex<dyn VirtioDevice>>,
|
||||
) {
|
||||
assert!(data.len() <= 8);
|
||||
@@ -152,16 +153,20 @@ impl VirtioPciCommonConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_common_config_word(&self, offset: u64, queues: &[Queue]) -> u16 {
|
||||
fn read_common_config_word(
|
||||
&self,
|
||||
offset: u64,
|
||||
queues: &[Queue<GuestMemoryAtomic<GuestMemoryMmap>>],
|
||||
) -> u16 {
|
||||
debug!("read_common_config_word: offset 0x{:x}", offset);
|
||||
match offset {
|
||||
0x10 => self.msix_config.load(Ordering::Acquire),
|
||||
0x12 => queues.len() as u16, // num_queues
|
||||
0x16 => self.queue_select,
|
||||
0x18 => self.with_queue(queues, |q| q.size).unwrap_or(0),
|
||||
0x1a => self.with_queue(queues, |q| q.vector).unwrap_or(0),
|
||||
0x18 => self.with_queue(queues, |q| q.state.size).unwrap_or(0),
|
||||
0x1a => self.with_queue(queues, |q| q.state.vector).unwrap_or(0),
|
||||
0x1c => {
|
||||
if self.with_queue(queues, |q| q.ready).unwrap_or(false) {
|
||||
if self.with_queue(queues, |q| q.state.ready).unwrap_or(false) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
@@ -175,13 +180,18 @@ impl VirtioPciCommonConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut Vec<Queue>) {
|
||||
fn write_common_config_word(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
value: u16,
|
||||
queues: &mut Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) {
|
||||
debug!("write_common_config_word: offset 0x{:x}", offset);
|
||||
match offset {
|
||||
0x10 => self.msix_config.store(value, Ordering::Release),
|
||||
0x16 => self.queue_select = value,
|
||||
0x18 => self.with_queue_mut(queues, |q| q.size = value),
|
||||
0x1a => self.with_queue_mut(queues, |q| q.vector = value),
|
||||
0x18 => self.with_queue_mut(queues, |q| q.state.size = value),
|
||||
0x1a => self.with_queue_mut(queues, |q| q.state.vector = value),
|
||||
0x1c => self.with_queue_mut(queues, |q| q.enable(value == 1)),
|
||||
_ => {
|
||||
warn!("invalid virtio register word write: 0x{:x}", offset);
|
||||
@@ -215,7 +225,7 @@ impl VirtioPciCommonConfig {
|
||||
&mut self,
|
||||
offset: u64,
|
||||
value: u32,
|
||||
queues: &mut Vec<Queue>,
|
||||
queues: &mut Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
device: Arc<Mutex<dyn VirtioDevice>>,
|
||||
) {
|
||||
debug!("write_common_config_dword: offset 0x{:x}", offset);
|
||||
@@ -242,12 +252,12 @@ impl VirtioPciCommonConfig {
|
||||
);
|
||||
}
|
||||
}
|
||||
0x20 => self.with_queue_mut(queues, |q| lo(&mut q.desc_table, value)),
|
||||
0x24 => self.with_queue_mut(queues, |q| hi(&mut q.desc_table, value)),
|
||||
0x28 => self.with_queue_mut(queues, |q| lo(&mut q.avail_ring, value)),
|
||||
0x2c => self.with_queue_mut(queues, |q| hi(&mut q.avail_ring, value)),
|
||||
0x30 => self.with_queue_mut(queues, |q| lo(&mut q.used_ring, value)),
|
||||
0x34 => self.with_queue_mut(queues, |q| hi(&mut q.used_ring, value)),
|
||||
0x20 => self.with_queue_mut(queues, |q| lo(&mut q.state.desc_table, value)),
|
||||
0x24 => self.with_queue_mut(queues, |q| hi(&mut q.state.desc_table, value)),
|
||||
0x28 => self.with_queue_mut(queues, |q| lo(&mut q.state.avail_ring, value)),
|
||||
0x2c => self.with_queue_mut(queues, |q| hi(&mut q.state.avail_ring, value)),
|
||||
0x30 => self.with_queue_mut(queues, |q| lo(&mut q.state.used_ring, value)),
|
||||
0x34 => self.with_queue_mut(queues, |q| hi(&mut q.state.used_ring, value)),
|
||||
_ => {
|
||||
warn!("invalid virtio register dword write: 0x{:x}", offset);
|
||||
}
|
||||
@@ -259,26 +269,39 @@ impl VirtioPciCommonConfig {
|
||||
0 // Assume the guest has no reason to read write-only registers.
|
||||
}
|
||||
|
||||
fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut Vec<Queue>) {
|
||||
fn write_common_config_qword(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
value: u64,
|
||||
queues: &mut Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) {
|
||||
debug!("write_common_config_qword: offset 0x{:x}", offset);
|
||||
match offset {
|
||||
0x20 => self.with_queue_mut(queues, |q| q.desc_table = GuestAddress(value)),
|
||||
0x28 => self.with_queue_mut(queues, |q| q.avail_ring = GuestAddress(value)),
|
||||
0x30 => self.with_queue_mut(queues, |q| q.used_ring = GuestAddress(value)),
|
||||
0x20 => self.with_queue_mut(queues, |q| q.state.desc_table = GuestAddress(value)),
|
||||
0x28 => self.with_queue_mut(queues, |q| q.state.avail_ring = GuestAddress(value)),
|
||||
0x30 => self.with_queue_mut(queues, |q| q.state.used_ring = GuestAddress(value)),
|
||||
_ => {
|
||||
warn!("invalid virtio register qword write: 0x{:x}", offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_queue<U, F>(&self, queues: &[Queue], f: F) -> Option<U>
|
||||
fn with_queue<U, F>(
|
||||
&self,
|
||||
queues: &[Queue<GuestMemoryAtomic<GuestMemoryMmap>>],
|
||||
f: F,
|
||||
) -> Option<U>
|
||||
where
|
||||
F: FnOnce(&Queue) -> U,
|
||||
F: FnOnce(&Queue<GuestMemoryAtomic<GuestMemoryMmap>>) -> U,
|
||||
{
|
||||
queues.get(self.queue_select as usize).map(f)
|
||||
}
|
||||
|
||||
fn with_queue_mut<F: FnOnce(&mut Queue)>(&self, queues: &mut Vec<Queue>, f: F) {
|
||||
fn with_queue_mut<F: FnOnce(&mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>)>(
|
||||
&self,
|
||||
queues: &mut Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
f: F,
|
||||
) {
|
||||
if let Some(queue) = queues.get_mut(self.queue_select as usize) {
|
||||
f(queue);
|
||||
}
|
||||
@@ -308,6 +331,7 @@ mod tests {
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::{ActivateResult, VirtioInterrupt};
|
||||
use std::sync::Arc;
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::GuestMemoryAtomic;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -326,7 +350,7 @@ mod tests {
|
||||
&mut self,
|
||||
_mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
_interrupt_evt: Arc<dyn VirtioInterrupt>,
|
||||
_queues: Vec<Queue>,
|
||||
_queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
_queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
Ok(())
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::VirtioPciCommonConfig;
|
||||
use crate::transport::VirtioTransport;
|
||||
use crate::GuestMemoryMmap;
|
||||
use crate::{
|
||||
ActivateResult, Queue, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
|
||||
ActivateResult, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
|
||||
DEVICE_ACKNOWLEDGE, DEVICE_DRIVER, DEVICE_DRIVER_OK, DEVICE_FAILED, DEVICE_FEATURES_OK,
|
||||
DEVICE_INIT,
|
||||
};
|
||||
@@ -24,30 +24,28 @@ use pci::{
|
||||
use std::any::Any;
|
||||
use std::cmp;
|
||||
use std::io::Write;
|
||||
use std::num::Wrapping;
|
||||
use std::result;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||
use versionize_derive::Versionize;
|
||||
use vm_allocator::SystemAllocator;
|
||||
use virtio_queue::AccessPlatform;
|
||||
use virtio_queue::{defs::VIRTQ_MSI_NO_VECTOR, Error as QueueError, Queue};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestUsize, Le32,
|
||||
};
|
||||
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryAtomic, GuestUsize, Le32};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||
};
|
||||
use vm_virtio::{queue, VirtioIommuRemapping, VIRTIO_MSI_NO_VECTOR};
|
||||
use vmm_sys_util::{errno::Result, eventfd::EventFd};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
/// Failed to retrieve queue ring's index.
|
||||
QueueRingIndex(queue::Error),
|
||||
QueueRingIndex(QueueError),
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
@@ -77,7 +75,7 @@ struct VirtioPciCap {
|
||||
offset: Le32, // Offset within bar.
|
||||
length: Le32, // Length of the structure, in bytes.
|
||||
}
|
||||
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
|
||||
// SAFETY: All members are simple numbers and any value is valid.
|
||||
unsafe impl ByteValued for VirtioPciCap {}
|
||||
|
||||
impl PciCapability for VirtioPciCap {
|
||||
@@ -113,7 +111,7 @@ struct VirtioPciNotifyCap {
|
||||
cap: VirtioPciCap,
|
||||
notify_off_multiplier: Le32,
|
||||
}
|
||||
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
|
||||
// SAFETY: All members are simple numbers and any value is valid.
|
||||
unsafe impl ByteValued for VirtioPciNotifyCap {}
|
||||
|
||||
impl PciCapability for VirtioPciNotifyCap {
|
||||
@@ -158,7 +156,7 @@ struct VirtioPciCap64 {
|
||||
offset_hi: Le32,
|
||||
length_hi: Le32,
|
||||
}
|
||||
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
|
||||
// SAFETY: All members are simple numbers and any value is valid.
|
||||
unsafe impl ByteValued for VirtioPciCap64 {}
|
||||
|
||||
impl PciCapability for VirtioPciCap64 {
|
||||
@@ -196,7 +194,7 @@ struct VirtioPciCfgCap {
|
||||
cap: VirtioPciCap,
|
||||
pci_cfg_data: [u8; 4],
|
||||
}
|
||||
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
|
||||
// SAFETY: All members are simple numbers and any value is valid.
|
||||
unsafe impl ByteValued for VirtioPciCfgCap {}
|
||||
|
||||
impl PciCapability for VirtioPciCfgCap {
|
||||
@@ -309,7 +307,7 @@ pub struct VirtioPciDevice {
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
|
||||
// virtio queues
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
|
||||
// Guest memory
|
||||
@@ -348,10 +346,11 @@ impl VirtioPciDevice {
|
||||
memory: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
device: Arc<Mutex<dyn VirtioDevice>>,
|
||||
msix_num: u16,
|
||||
iommu_mapping_cb: Option<Arc<VirtioIommuRemapping>>,
|
||||
access_platform: Option<Arc<dyn AccessPlatform>>,
|
||||
interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
pci_device_bdf: u32,
|
||||
activate_evt: EventFd,
|
||||
use_64bit_bar: bool,
|
||||
) -> Result<Self> {
|
||||
let device_clone = device.clone();
|
||||
let locked_device = device_clone.lock().unwrap();
|
||||
@@ -363,8 +362,11 @@ impl VirtioPciDevice {
|
||||
.queue_max_sizes()
|
||||
.iter()
|
||||
.map(|&s| {
|
||||
let mut queue = Queue::new(s);
|
||||
queue.iommu_mapping_cb = iommu_mapping_cb.clone();
|
||||
let mut queue = Queue::<
|
||||
GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
virtio_queue::QueueState<GuestMemoryAtomic<GuestMemoryMmap>>,
|
||||
>::new(memory.clone(), s);
|
||||
queue.state.access_platform = access_platform.clone();
|
||||
queue
|
||||
})
|
||||
.collect();
|
||||
@@ -388,22 +390,15 @@ impl VirtioPciDevice {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// All device types *except* virtio block devices should be allocated a 64-bit bar
|
||||
// The block devices should be given a 32-bit BAR so that they are easily accessible
|
||||
// to firmware without requiring excessive identity mapping.
|
||||
let mut use_64bit_bar = true;
|
||||
let (class, subclass) = match VirtioDeviceType::from(locked_device.device_type()) {
|
||||
VirtioDeviceType::Net => (
|
||||
PciClassCode::NetworkController,
|
||||
&PciNetworkControllerSubclass::EthernetController as &dyn PciSubclass,
|
||||
),
|
||||
VirtioDeviceType::Block => {
|
||||
use_64bit_bar = false;
|
||||
(
|
||||
PciClassCode::MassStorage,
|
||||
&PciMassStorageSubclass::MassStorage as &dyn PciSubclass,
|
||||
)
|
||||
}
|
||||
VirtioDeviceType::Block => (
|
||||
PciClassCode::MassStorage,
|
||||
&PciMassStorageSubclass::MassStorage as &dyn PciSubclass,
|
||||
),
|
||||
_ => (
|
||||
PciClassCode::Other,
|
||||
&PciVirtioSubclass::NonTransitionalBase as &dyn PciSubclass,
|
||||
@@ -432,7 +427,7 @@ impl VirtioPciDevice {
|
||||
device_feature_select: 0,
|
||||
driver_feature_select: 0,
|
||||
queue_select: 0,
|
||||
msix_config: Arc::new(AtomicU16::new(VIRTIO_MSI_NO_VECTOR)),
|
||||
msix_config: Arc::new(AtomicU16::new(VIRTQ_MSI_NO_VECTOR)),
|
||||
},
|
||||
msix_config,
|
||||
msix_num,
|
||||
@@ -472,13 +467,13 @@ impl VirtioPciDevice {
|
||||
.queues
|
||||
.iter()
|
||||
.map(|q| QueueState {
|
||||
max_size: q.max_size,
|
||||
size: q.size,
|
||||
ready: q.ready,
|
||||
vector: q.vector,
|
||||
desc_table: q.desc_table.0,
|
||||
avail_ring: q.avail_ring.0,
|
||||
used_ring: q.used_ring.0,
|
||||
max_size: q.max_size(),
|
||||
size: q.state.size,
|
||||
ready: q.state.ready,
|
||||
vector: q.state.vector,
|
||||
desc_table: q.state.desc_table.0,
|
||||
avail_ring: q.state.avail_ring.0,
|
||||
used_ring: q.state.used_ring.0,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
@@ -491,27 +486,25 @@ impl VirtioPciDevice {
|
||||
.store(state.interrupt_status, Ordering::Release);
|
||||
|
||||
// Update virtqueues indexes for both available and used rings.
|
||||
if let Some(mem) = self.memory.as_ref() {
|
||||
let mem = mem.memory();
|
||||
for (i, queue) in self.queues.iter_mut().enumerate() {
|
||||
queue.max_size = state.queues[i].max_size;
|
||||
queue.size = state.queues[i].size;
|
||||
queue.ready = state.queues[i].ready;
|
||||
queue.vector = state.queues[i].vector;
|
||||
queue.desc_table = GuestAddress(state.queues[i].desc_table);
|
||||
queue.avail_ring = GuestAddress(state.queues[i].avail_ring);
|
||||
queue.used_ring = GuestAddress(state.queues[i].used_ring);
|
||||
queue.next_avail = Wrapping(
|
||||
queue
|
||||
.used_index_from_memory(&mem)
|
||||
.map_err(Error::QueueRingIndex)?,
|
||||
);
|
||||
queue.next_used = Wrapping(
|
||||
queue
|
||||
.used_index_from_memory(&mem)
|
||||
.map_err(Error::QueueRingIndex)?,
|
||||
);
|
||||
}
|
||||
for (i, queue) in self.queues.iter_mut().enumerate() {
|
||||
queue.state.size = state.queues[i].size;
|
||||
queue.state.ready = state.queues[i].ready;
|
||||
queue.state.vector = state.queues[i].vector;
|
||||
queue.state.desc_table = GuestAddress(state.queues[i].desc_table);
|
||||
queue.state.avail_ring = GuestAddress(state.queues[i].avail_ring);
|
||||
queue.state.used_ring = GuestAddress(state.queues[i].used_ring);
|
||||
queue.set_next_avail(
|
||||
queue
|
||||
.used_idx(Ordering::Acquire)
|
||||
.map_err(Error::QueueRingIndex)?
|
||||
.0,
|
||||
);
|
||||
queue.set_next_used(
|
||||
queue
|
||||
.used_idx(Ordering::Acquire)
|
||||
.map_err(Error::QueueRingIndex)?
|
||||
.0,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -673,10 +666,10 @@ impl VirtioPciDevice {
|
||||
let mut device = self.device.lock().unwrap();
|
||||
let mut queue_evts = Vec::new();
|
||||
let mut queues = self.queues.clone();
|
||||
queues.retain(|q| q.ready);
|
||||
queues.retain(|q| q.state.ready);
|
||||
for (i, queue) in queues.iter().enumerate() {
|
||||
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
|
||||
if !queue.is_valid(&mem.memory()) {
|
||||
if !queue.is_valid() {
|
||||
error!("Queue {} is not valid", i);
|
||||
}
|
||||
}
|
||||
@@ -743,20 +736,20 @@ impl VirtioInterrupt for VirtioInterruptMsix {
|
||||
fn trigger(
|
||||
&self,
|
||||
int_type: &VirtioInterruptType,
|
||||
queue: Option<&Queue>,
|
||||
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> std::result::Result<(), std::io::Error> {
|
||||
let vector = match int_type {
|
||||
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
|
||||
VirtioInterruptType::Queue => {
|
||||
if let Some(q) = queue {
|
||||
q.vector
|
||||
q.state.vector
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if vector == VIRTIO_MSI_NO_VECTOR {
|
||||
if vector == VIRTQ_MSI_NO_VECTOR {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -776,12 +769,16 @@ impl VirtioInterrupt for VirtioInterruptMsix {
|
||||
.trigger(vector as InterruptIndex)
|
||||
}
|
||||
|
||||
fn notifier(&self, int_type: &VirtioInterruptType, queue: Option<&Queue>) -> Option<EventFd> {
|
||||
fn notifier(
|
||||
&self,
|
||||
int_type: &VirtioInterruptType,
|
||||
queue: Option<&Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
) -> Option<EventFd> {
|
||||
let vector = match int_type {
|
||||
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
|
||||
VirtioInterruptType::Queue => {
|
||||
if let Some(q) = queue {
|
||||
q.vector
|
||||
q.state.vector
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@@ -845,6 +842,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> std::result::Result<Vec<(GuestAddress, GuestUsize, PciBarRegionType)>, PciDeviceError>
|
||||
{
|
||||
let mut ranges = Vec::new();
|
||||
@@ -855,8 +853,8 @@ impl PciDevice for VirtioPciDevice {
|
||||
// See http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-740004
|
||||
let (virtio_pci_bar_addr, region_type) = if self.use_64bit_bar {
|
||||
let region_type = PciBarRegionType::Memory64BitRegion;
|
||||
let addr = allocator
|
||||
.allocate_mmio_addresses(
|
||||
let addr = mmio_allocator
|
||||
.allocate(
|
||||
self.settings_bar_addr,
|
||||
CAPABILITY_BAR_SIZE,
|
||||
Some(CAPABILITY_BAR_SIZE),
|
||||
@@ -928,6 +926,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
fn free_bars(
|
||||
&mut self,
|
||||
allocator: &mut SystemAllocator,
|
||||
mmio_allocator: &mut AddressAllocator,
|
||||
) -> std::result::Result<(), PciDeviceError> {
|
||||
for (addr, length, type_) in self.bar_regions.drain(..) {
|
||||
match type_ {
|
||||
@@ -935,7 +934,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
allocator.free_mmio_hole_addresses(addr, length);
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
allocator.free_mmio_addresses(addr, length);
|
||||
mmio_allocator.free(addr, length);
|
||||
}
|
||||
_ => error!("Unexpected PCI bar type"),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2019 Intel Corporation. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::super::{ActivateResult, Queue, VirtioCommon, VirtioDevice, VirtioDeviceType};
|
||||
use super::super::{ActivateResult, VirtioCommon, VirtioDevice, VirtioDeviceType};
|
||||
use super::vu_common_ctrl::{VhostUserConfig, VhostUserHandle};
|
||||
use super::{Error, Result, DEFAULT_VIRTIO_FEATURES};
|
||||
use crate::seccomp_filters::Thread;
|
||||
@@ -28,6 +28,7 @@ use virtio_bindings::bindings::virtio_blk::{
|
||||
VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX,
|
||||
VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES,
|
||||
};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{ByteValued, GuestMemoryAtomic};
|
||||
use vm_migration::{
|
||||
protocol::MemoryRangeTable, Migratable, MigratableError, Pausable, Snapshot, Snapshottable,
|
||||
@@ -279,7 +280,7 @@ impl VirtioDevice for Blk {
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::seccomp_filters::Thread;
|
||||
use crate::thread_helper::spawn_virtio_thread;
|
||||
use crate::vhost_user::VhostUserCommon;
|
||||
use crate::{
|
||||
ActivateError, ActivateResult, Queue, UserspaceMapping, VirtioCommon, VirtioDevice,
|
||||
VirtioDeviceType, VirtioInterrupt, VirtioSharedMemoryList,
|
||||
ActivateError, ActivateResult, UserspaceMapping, VirtioCommon, VirtioDevice, VirtioDeviceType,
|
||||
VirtioInterrupt, VirtioSharedMemoryList,
|
||||
};
|
||||
use crate::{GuestMemoryMmap, GuestRegionMmap, MmapRegion};
|
||||
use libc::{self, c_void, off64_t, pread64, pwrite64};
|
||||
@@ -27,6 +27,7 @@ use vhost::vhost_user::message::{
|
||||
use vhost::vhost_user::{
|
||||
HandlerResult, MasterReqHandler, VhostUserMaster, VhostUserMasterReqHandler,
|
||||
};
|
||||
use virtio_queue::Queue;
|
||||
use vm_memory::{
|
||||
Address, ByteValued, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||
};
|
||||
@@ -501,7 +502,7 @@ impl VirtioDevice for Fs {
|
||||
&mut self,
|
||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
queues: Vec<Queue>,
|
||||
queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
|
||||
queue_evts: Vec<EventFd>,
|
||||
) -> ActivateResult {
|
||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user