mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
225 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84b3992146 | ||
|
|
40c63dcf5e | ||
|
|
970bc05271 | ||
|
|
4da6bcd5d5 | ||
|
|
3671f5e94c | ||
|
|
40da6210f4 | ||
|
|
3c6dfd7709 | ||
|
|
e294688904 | ||
|
|
5c010d489d | ||
|
|
c5d15fd938 | ||
|
|
827229d8e4 | ||
|
|
6837de9057 | ||
|
|
7c9a83f6e1 | ||
|
|
2571cc8041 | ||
|
|
3b8d1f1411 | ||
|
|
9762c8bc28 | ||
|
|
7c302373ed | ||
|
|
19c5e91b6e | ||
|
|
4a5939973c | ||
|
|
0c27f69f1c | ||
|
|
7d5d9bdcf4 | ||
|
|
85ad72490f | ||
|
|
80e48b545d | ||
|
|
eb18ea61f4 | ||
|
|
bfbd75b2cf | ||
|
|
6dc3d60b2d | ||
|
|
aa34d545f6 | ||
|
|
261c039831 | ||
|
|
db6516931d | ||
|
|
030d6046b2 | ||
|
|
7390475636 | ||
|
|
c4564f3ba8 | ||
|
|
27ba8133a4 | ||
|
|
f5c550affb | ||
|
|
1d9f27c9fb | ||
|
|
8b7aafad16 | ||
|
|
46d082763f | ||
|
|
6170ba06d1 | ||
|
|
63304d0be7 | ||
|
|
1cd186c83c | ||
|
|
5581486149 | ||
|
|
3e3c163285 | ||
|
|
6190e64f2f | ||
|
|
88c30764e0 | ||
|
|
91145afa9b | ||
|
|
50d57b2b6f | ||
|
|
68401e6e4a | ||
|
|
7fad74cb04 | ||
|
|
b8c0cd5cd4 | ||
|
|
874ffe60ca | ||
|
|
e9793020c2 | ||
|
|
d370ea585b | ||
|
|
1e11e6789a | ||
|
|
f6da2bb5e1 | ||
|
|
987e52090f | ||
|
|
195ac4c5dc | ||
|
|
685a1760f3 | ||
|
|
e39924d45a | ||
|
|
b06fd80fa9 | ||
|
|
c8c3cad8cb | ||
|
|
e311fd66cd | ||
|
|
fbd624d816 | ||
|
|
305e095d15 | ||
|
|
62aaccee28 | ||
|
|
13724dbd22 | ||
|
|
16cb40733a | ||
|
|
9440304183 | ||
|
|
78f9ddc6be | ||
|
|
9b0996a71f | ||
|
|
f685f0e0b2 | ||
|
|
40a3c31df4 | ||
|
|
04dc496808 | ||
|
|
d0c0a98eda | ||
|
|
205e587fad | ||
|
|
2bb153de2b | ||
|
|
c853ed3fdc | ||
|
|
a59ff42a95 | ||
|
|
7c86ef8a69 | ||
|
|
c7438c8b22 | ||
|
|
3df2e2629b | ||
|
|
06d83d2eb3 | ||
|
|
9c3b489f0e | ||
|
|
3965a8505b | ||
|
|
a8cde12b14 | ||
|
|
fd95acc60f | ||
|
|
c85fba0c43 | ||
|
|
fea1f370ff | ||
|
|
6307db5699 | ||
|
|
548426c128 | ||
|
|
c3a75dafc5 | ||
|
|
ee7fcdb3cf | ||
|
|
afc83582be | ||
|
|
2a8bdf710d | ||
|
|
210a9d40c8 | ||
|
|
5bb10adf22 | ||
|
|
97f8f0fb31 | ||
|
|
5bc311184e | ||
|
|
7f96eb2b67 | ||
|
|
ab4b30edd3 | ||
|
|
cc78a597cd | ||
|
|
ebcbab739e | ||
|
|
0c7541473c | ||
|
|
169d6f6756 | ||
|
|
af8def364d | ||
|
|
c45a9a390d | ||
|
|
b5929645c6 | ||
|
|
ec9e6edcd0 | ||
|
|
00873e5f84 | ||
|
|
9e53efa3ca | ||
|
|
4f22f57651 | ||
|
|
1e1cbc53c1 | ||
|
|
8255d5f774 | ||
|
|
e3a8d6c13c | ||
|
|
581bf4aad5 | ||
|
|
16b82f3dfe | ||
|
|
675a8f808c | ||
|
|
59e5048436 | ||
|
|
7b3e79cdfa | ||
|
|
66ffaceffc | ||
|
|
4e307788b7 | ||
|
|
8739f6cccb | ||
|
|
5599cbef50 | ||
|
|
4c52ad2550 | ||
|
|
a0c07474a3 | ||
|
|
be0cbb09b1 | ||
|
|
d24aa887b6 | ||
|
|
835a31e283 | ||
|
|
57c8c250fd | ||
|
|
62abc117ab | ||
|
|
1c54fc3ab7 | ||
|
|
57ce0986f7 | ||
|
|
c8cad394b5 | ||
|
|
b02aff5761 | ||
|
|
f282cc001a | ||
|
|
c48e82915d | ||
|
|
66a3bed086 | ||
|
|
45cc26f940 | ||
|
|
77955bd8f9 | ||
|
|
e61ee6bcac | ||
|
|
fd9bd1c86c | ||
|
|
cf9e81c05a | ||
|
|
a4a2d6fbee | ||
|
|
94083793a6 | ||
|
|
8ae966e975 | ||
|
|
f68fe54b9b | ||
|
|
54e1796da6 | ||
|
|
c8be08adf6 | ||
|
|
ccfa34d066 | ||
|
|
d65a0b68b9 | ||
|
|
56028fb214 | ||
|
|
933d41cf2f | ||
|
|
61f9a4ec6c | ||
|
|
080ea31813 | ||
|
|
d6db2fdf96 | ||
|
|
f1aa09f178 | ||
|
|
8cbb7b15a9 | ||
|
|
b65502c3c1 | ||
|
|
3ef7c8eac5 | ||
|
|
526cf32a78 | ||
|
|
b0ddc5bd60 | ||
|
|
b72f6046e5 | ||
|
|
8a2bd01e25 | ||
|
|
d433ae1656 | ||
|
|
0348f480fa | ||
|
|
74565538ae | ||
|
|
99baee8d37 | ||
|
|
030a86db17 | ||
|
|
3eb5b67dc3 | ||
|
|
c27d6df233 | ||
|
|
30cd3cb764 | ||
|
|
e31c2be60a | ||
|
|
fb853a50b1 | ||
|
|
8c38d9576c | ||
|
|
5e98810c2d | ||
|
|
f8875acec2 | ||
|
|
fa8fcf5f4c | ||
|
|
3e847112db | ||
|
|
a0a89b1346 | ||
|
|
aee1155870 | ||
|
|
5bd05b1af0 | ||
|
|
727287b69d | ||
|
|
deedfcdc35 | ||
|
|
2122233047 | ||
|
|
afce21ba59 | ||
|
|
ade5097878 | ||
|
|
24922ce1e3 | ||
|
|
c1be41bfbf | ||
|
|
d361fc1a36 | ||
|
|
49214cf02b | ||
|
|
d78b2ec8b5 | ||
|
|
946c484590 | ||
|
|
cd700bf449 | ||
|
|
cf7a05ecb5 | ||
|
|
0497a7c311 | ||
|
|
05a2b3fac2 | ||
|
|
d33c0563af | ||
|
|
4ed0e1a3c8 | ||
|
|
5ed2a654e8 | ||
|
|
ae04fe432c | ||
|
|
c8d142eb55 | ||
|
|
8533d63514 | ||
|
|
0ef69fa592 | ||
|
|
29f924405a | ||
|
|
282134a490 | ||
|
|
1fbdca16bf | ||
|
|
32a2000ecc | ||
|
|
6a499f02d9 | ||
|
|
0a9c052ebd | ||
|
|
bfb12b7777 | ||
|
|
c89095ab85 | ||
|
|
9260c4c10e | ||
|
|
4822ed79e1 | ||
|
|
ddbef7450d | ||
|
|
c1d9edbfc0 | ||
|
|
0d209e135e | ||
|
|
e22b6ec768 | ||
|
|
b59243f6cf | ||
|
|
07a09eda27 | ||
|
|
38c41a5074 | ||
|
|
6e4c90f305 | ||
|
|
707bb0ba72 | ||
|
|
29881a2d6a | ||
|
|
a330a1569a | ||
|
|
cf6480f012 | ||
|
|
a838d0bde1 |
32
.github/workflows/cross-build.yaml
vendored
32
.github/workflows/cross-build.yaml
vendored
@@ -1,32 +0,0 @@
|
|||||||
name: Cloud Hypervisor Cross Build
|
|
||||||
on: [pull_request, create]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
rust:
|
|
||||||
- 1.46.0
|
|
||||||
target:
|
|
||||||
- aarch64-unknown-linux-gnu
|
|
||||||
- aarch64-unknown-linux-musl
|
|
||||||
steps:
|
|
||||||
- name: Code checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: ${{ matrix.rust }}
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
override: true
|
|
||||||
- name: Install arm64 libfdt
|
|
||||||
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && mkdir -p target/debug/deps && cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/ && echo "libfdt copied into build tree"
|
|
||||||
- name: Build
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
use-cross: true
|
|
||||||
command: build
|
|
||||||
args: --target=${{ matrix.target }} --no-default-features --features "kvm"
|
|
||||||
2
.github/workflows/quality-aarch64.yaml
vendored
2
.github/workflows/quality-aarch64.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
- name: Install arm64 libfdt
|
- name: Install arm64 libfdt
|
||||||
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && sudo mkdir /tmmmp && mkdir target && mkdir target/debug && mkdir target/debug/deps && sudo cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/libfdt.a && echo "libfdt installed"
|
run: wget http://ftp.us.debian.org/debian/pool/main/d/device-tree-compiler/libfdt-dev_1.6.0-1_arm64.deb && dpkg-deb -xv libfdt-dev_1.6.0-1_arm64.deb ./tlibfdtdev && mkdir -p target/debug/deps && sudo cp ./tlibfdtdev/usr/lib/aarch64-linux-gnu/libfdt.a target/debug/deps/libfdt.a && echo "libfdt installed"
|
||||||
- name: Formatting (rustfmt)
|
- name: Formatting (rustfmt)
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
- name: Clippy (kvm)
|
- name: Clippy (kvm)
|
||||||
|
|||||||
3
.github/workflows/quality.yaml
vendored
3
.github/workflows/quality.yaml
vendored
@@ -35,6 +35,9 @@ jobs:
|
|||||||
- name: Clippy (acpi,kvm)
|
- name: Clippy (acpi,kvm)
|
||||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm" -- -D warnings
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (acpi,kvm,tdx)
|
||||||
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm,tdx" -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (kvm)
|
- name: Clippy (kvm)
|
||||||
run: cargo clippy --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
||||||
|
|
||||||
|
|||||||
415
Cargo.lock
generated
415
Cargo.lock
generated
@@ -18,9 +18,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler"
|
||||||
version = "0.2.3"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
@@ -42,9 +42,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.38"
|
version = "1.0.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
|
checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api_client"
|
name = "api_client"
|
||||||
@@ -68,10 +68,10 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"rand 0.8.3",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-migration",
|
"vm-migration",
|
||||||
]
|
]
|
||||||
@@ -163,7 +163,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
@@ -173,15 +172,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.3.4"
|
version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.66"
|
version = "1.0.67"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -213,7 +212,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api_client",
|
"api_client",
|
||||||
@@ -221,6 +220,7 @@ dependencies = [
|
|||||||
"credibility",
|
"credibility",
|
||||||
"dirs",
|
"dirs",
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -230,9 +230,7 @@ dependencies = [
|
|||||||
"seccomp",
|
"seccomp",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"ssh2",
|
"test_infra",
|
||||||
"tempdir",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm",
|
"vmm",
|
||||||
@@ -267,9 +265,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.1"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
@@ -290,7 +288,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
|
||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-migration",
|
"vm-migration",
|
||||||
@@ -340,6 +337,16 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event_monitor"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "failure"
|
name = "failure"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@@ -362,22 +369,6 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "form_urlencoded"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
|
|
||||||
dependencies = [
|
|
||||||
"matches",
|
|
||||||
"percent-encoding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fuchsia-cprng"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
@@ -389,17 +380,6 @@ dependencies = [
|
|||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 0.1.10",
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.23.0"
|
version = "0.23.0"
|
||||||
@@ -451,31 +431,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced-x86"
|
name = "iced-x86"
|
||||||
version = "1.10.3"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "644fd8d7e49d1ccae568a8fbb873a7ee7fcb3bc9214a474a5e55c03b6d1fa5ac"
|
checksum = "4261b15a556aa9b83b976b7aa4613f5a3b80351683af14c5d8d7e62c00869d5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rustc_version",
|
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "idna"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094"
|
|
||||||
dependencies = [
|
|
||||||
"matches",
|
|
||||||
"unicode-bidi",
|
|
||||||
"unicode-normalization",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.4.0"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f7589adca0ddd74f56ed83a5098b45e3abf264dc27e150a8bec3397fcc34338"
|
checksum = "fb82832e05cc4ca298f198a8db108837b4f7b7b1248e3cba8e48f151aece80cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -508,8 +476,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-bindings"
|
name = "kvm-bindings"
|
||||||
version = "0.2.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch#2164129ff03bb4d7a4243de5e2078845064d922c"
|
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.4.0#1a68725639283e622f4bb64584885b30bfe8be44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -518,8 +486,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kvm-ioctls"
|
name = "kvm-ioctls"
|
||||||
version = "0.6.0"
|
version = "0.8.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/kvm-ioctls?branch=ch#b7f21758bf8e46ac55a28a1b2ed008b5732bbb44"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74058b16912c6723db02d4ca3ec919b73cd41512ccd3b6202cf91ae8d6c9dce5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -534,9 +503,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.86"
|
version = "0.2.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libssh2-sys"
|
name = "libssh2-sys"
|
||||||
@@ -566,8 +535,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/rust-vmm/linux-loader#2855be15a725e32df0b5198487d7b52f4a90ac0f"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c819cc8275b0f2c1ed9feec455ca288b45d82932384a6a5f7a86812ee3427459"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
@@ -599,12 +569,6 @@ dependencies = [
|
|||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matches"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.3.4"
|
version = "2.3.4"
|
||||||
@@ -621,9 +585,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
|
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
"autocfg",
|
"autocfg",
|
||||||
@@ -632,7 +596,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mshv-bindings"
|
name = "mshv-bindings"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#3a7d7ba07e12409a3b23794c72c30b5052d221d9"
|
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#1c2ae9a3bc67783f7ffe792e31de97cb851b6f4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -644,7 +608,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mshv-ioctls"
|
name = "mshv-ioctls"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#3a7d7ba07e12409a3b23794c72c30b5052d221d9"
|
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#1c2ae9a3bc67783f7ffe792e31de97cb851b6f4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mshv-bindings",
|
"mshv-bindings",
|
||||||
@@ -668,7 +632,6 @@ dependencies = [
|
|||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"net_gen",
|
"net_gen",
|
||||||
"pnet",
|
"pnet",
|
||||||
"rand 0.8.3",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
@@ -683,17 +646,11 @@ version = "0.23.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.60"
|
version = "0.9.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
|
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -725,7 +682,7 @@ dependencies = [
|
|||||||
"cfg-if 0.1.10",
|
"cfg-if 0.1.10",
|
||||||
"cloudabi",
|
"cloudabi",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.1.57",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
@@ -751,12 +708,6 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "percent-encoding"
|
|
||||||
version = "2.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
@@ -851,12 +802,6 @@ dependencies = [
|
|||||||
"pnet_sys",
|
"pnet_sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
@@ -874,7 +819,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"remain",
|
"remain",
|
||||||
"tempfile",
|
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -887,126 +831,39 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
|
||||||
dependencies = [
|
|
||||||
"fuchsia-cprng",
|
|
||||||
"libc",
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
"rdrand",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.8.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core 0.6.0",
|
|
||||||
"rand_hc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core 0.6.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8b34ba8cfb21243bd8df91854c830ff0d785fff2e82ebd4434c2644cb9ada18"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.2.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.6.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rdrand"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.2.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.1.16",
|
"getrandom",
|
||||||
"redox_syscall 0.1.57",
|
"redox_syscall",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.4.3"
|
version = "1.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
"thread_local",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.22"
|
version = "0.6.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remain"
|
name = "remain"
|
||||||
@@ -1019,15 +876,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "remove_dir_all"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
|
||||||
dependencies = [
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-argon2"
|
name = "rust-argon2"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -1052,15 +900,6 @@ version = "0.3.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc_version"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
|
||||||
dependencies = [
|
|
||||||
"semver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -1081,32 +920,17 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
|
||||||
dependencies = [
|
|
||||||
"semver-parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver-parser"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.123"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.123"
|
version = "1.0.125"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1115,9 +939,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.62"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
|
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -1126,9 +950,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.4"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "780f5e3fe0c66f67197236097d89de1e86216f1f6fdeaf47c442f854ab46c240"
|
checksum = "6aa894ef3fade0ee7243422f4fbbd6c2b48e6de767e621d37ef65f2310f53cea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@@ -1163,9 +987,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "0.3.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
@@ -1175,9 +999,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.60"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1245,30 +1069,6 @@ dependencies = [
|
|||||||
"unicode-xid 0.0.3",
|
"unicode-xid 0.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempdir"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
|
||||||
dependencies = [
|
|
||||||
"rand 0.4.6",
|
|
||||||
"remove_dir_all",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempfile"
|
|
||||||
version = "3.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"libc",
|
|
||||||
"rand 0.8.3",
|
|
||||||
"redox_syscall 0.2.4",
|
|
||||||
"remove_dir_all",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term"
|
name = "term"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -1298,6 +1098,18 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_infra"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"dirs",
|
||||||
|
"epoll",
|
||||||
|
"libc",
|
||||||
|
"ssh2",
|
||||||
|
"vmm-sys-util",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -1310,66 +1122,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.23"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.23"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinyvec"
|
|
||||||
version = "1.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
|
|
||||||
dependencies = [
|
|
||||||
"tinyvec_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinyvec_macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-bidi"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
|
||||||
dependencies = [
|
|
||||||
"matches",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-normalization"
|
|
||||||
version = "0.1.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
|
|
||||||
dependencies = [
|
|
||||||
"tinyvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@@ -1388,18 +1158,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "url"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
|
|
||||||
dependencies = [
|
|
||||||
"form_urlencoded",
|
|
||||||
"idna",
|
|
||||||
"matches",
|
|
||||||
"percent-encoding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@@ -1424,7 +1182,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vfio-ioctls"
|
name = "vfio-ioctls"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/vfio-ioctls?branch=ch#6ea1b934bbff9102bbdc80c23a357a48645cd722"
|
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=master#ed8d2534576371000361e615eeb91b4266c55713"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
@@ -1438,7 +1196,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vhost"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/vhost?branch=ch#6fc980bf5083d67586ab91d6a965c3a593c33a78"
|
source = "git+https://github.com/rust-vmm/vhost?branch=master#05cd8b2ad3c7bb1a84f15b629711d1886b97b7ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1509,6 +1267,7 @@ dependencies = [
|
|||||||
"block_util",
|
"block_util",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"event_monitor",
|
||||||
"io-uring",
|
"io-uring",
|
||||||
"libc",
|
"libc",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
@@ -1519,8 +1278,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
|
||||||
"vfio-ioctls",
|
|
||||||
"vhost",
|
"vhost",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
@@ -1549,6 +1306,7 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"vfio-ioctls",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
@@ -1602,6 +1360,7 @@ dependencies = [
|
|||||||
"credibility",
|
"credibility",
|
||||||
"devices",
|
"devices",
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1617,9 +1376,7 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"tempfile",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
|
||||||
"vfio-ioctls",
|
"vfio-ioctls",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
"vm-allocator",
|
"vm-allocator",
|
||||||
@@ -1632,9 +1389,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vmm-sys-util"
|
name = "vmm-sys-util"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d1cdd1d72e262bbfb014de65ada24c1ac50e10a2e3b1e8ec052df188c2ee5dfa"
|
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
|
|||||||
34
Cargo.toml
34
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
default-run = "cloud-hypervisor"
|
default-run = "cloud-hypervisor"
|
||||||
@@ -13,39 +13,38 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
|||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.39"
|
||||||
api_client = { path = "api_client" }
|
api_client = { path = "api_client" }
|
||||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||||
epoll = ">=4.0.1"
|
epoll = "4.3.1"
|
||||||
|
event_monitor = { path = "event_monitor" }
|
||||||
hypervisor = { path = "hypervisor" }
|
hypervisor = { path = "hypervisor" }
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = { version = "0.4.14", features = ["std"] }
|
log = { version = "0.4.14", features = ["std"] }
|
||||||
option_parser = { path = "option_parser" }
|
option_parser = { path = "option_parser" }
|
||||||
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
|
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
|
||||||
serde_json = "1.0.62"
|
serde_json = "1.0.64"
|
||||||
signal-hook = "0.3.4"
|
signal-hook = "0.3.7"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0.24"
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.7.0"
|
vmm-sys-util = "0.8.0"
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.5.0"
|
||||||
wait-timeout = "0.2.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
clap = { version = "2.33.3", features = ["wrap_help"] }
|
clap = { version = "2.33.3", features = ["wrap_help"] }
|
||||||
|
|
||||||
# List of patched crates
|
# List of patched crates
|
||||||
[patch.'https://github.com/rust-vmm/vhost']
|
[patch.crates-io]
|
||||||
vhost_rs = { git = "https://github.com/cloud-hypervisor/vhost", branch = "ch", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ssh2 = "0.9.1"
|
|
||||||
dirs = "3.0.1"
|
|
||||||
credibility = "0.1.3"
|
credibility = "0.1.3"
|
||||||
tempdir = "0.3.7"
|
dirs = "3.0.1"
|
||||||
lazy_static= "1.4.0"
|
lazy_static= "1.4.0"
|
||||||
tempfile = "3.2.0"
|
|
||||||
serde_json = "1.0.62"
|
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
test_infra = { path = "test_infra" }
|
||||||
|
wait-timeout = "0.2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["acpi", "cmos", "io_uring", "kvm"]
|
default = ["acpi", "cmos", "io_uring", "kvm"]
|
||||||
@@ -57,6 +56,7 @@ fwdebug = ["vmm/fwdebug"]
|
|||||||
kvm = ["vmm/kvm"]
|
kvm = ["vmm/kvm"]
|
||||||
mshv = ["vmm/mshv"]
|
mshv = ["vmm/mshv"]
|
||||||
io_uring = ["vmm/io_uring"]
|
io_uring = ["vmm/io_uring"]
|
||||||
|
tdx = ["vmm/tdx"]
|
||||||
|
|
||||||
# Integration tests require a special environment to run in
|
# Integration tests require a special environment to run in
|
||||||
integration_tests = []
|
integration_tests = []
|
||||||
@@ -69,6 +69,7 @@ members = [
|
|||||||
"arch_gen",
|
"arch_gen",
|
||||||
"block_util",
|
"block_util",
|
||||||
"devices",
|
"devices",
|
||||||
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"net_gen",
|
"net_gen",
|
||||||
"net_util",
|
"net_util",
|
||||||
@@ -85,3 +86,4 @@ members = [
|
|||||||
"vm-migration",
|
"vm-migration",
|
||||||
"vm-virtio"
|
"vm-virtio"
|
||||||
]
|
]
|
||||||
|
exclude = ["test_infra"]
|
||||||
|
|||||||
35
Jenkinsfile
vendored
35
Jenkinsfile
vendored
@@ -136,6 +136,39 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage ('Worker build VFIO') {
|
||||||
|
agent { node { label 'bionic-vfio' } }
|
||||||
|
when { branch 'master' }
|
||||||
|
stages {
|
||||||
|
stage ('Checkout') {
|
||||||
|
steps {
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Run VFIO integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh "scripts/dev_cli.sh tests --integration-vfio"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage ('Run VFIO integration tests for musl') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh "scripts/dev_cli.sh tests --integration-vfio --libc musl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
|
||||||
|
deleteDir()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
stage ('Worker build - Windows guest') {
|
stage ('Worker build - Windows guest') {
|
||||||
agent { node { label 'groovy-win' } }
|
agent { node { label 'groovy-win' } }
|
||||||
stages {
|
stages {
|
||||||
@@ -149,7 +182,7 @@ pipeline{
|
|||||||
sh "mkdir ${env.HOME}/workloads"
|
sh "mkdir ${env.HOME}/workloads"
|
||||||
azureDownload(storageCredentialId: 'ch-image-store',
|
azureDownload(storageCredentialId: 'ch-image-store',
|
||||||
containerName: 'private-images',
|
containerName: 'private-images',
|
||||||
includeFilesPattern: 'OVMF.fd',
|
includeFilesPattern: 'OVMF-4b47d0c6c8.fd',
|
||||||
downloadType: 'container',
|
downloadType: 'container',
|
||||||
downloadDirLoc: "${env.HOME}/workloads")
|
downloadDirLoc: "${env.HOME}/workloads")
|
||||||
azureDownload(storageCredentialId: 'ch-image-store',
|
azureDownload(storageCredentialId: 'ch-image-store',
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ We need to get the latest `rust-hypervisor-firmware` release and also a working
|
|||||||
$ pushd $CLOUDH
|
$ pushd $CLOUDH
|
||||||
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
|
||||||
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
$ qemu-img convert -p -f qcow2 -O raw focal-server-cloudimg-amd64.img focal-server-cloudimg-amd64.raw
|
||||||
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.2.8/hypervisor-fw
|
$ wget https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.3.0/hypervisor-fw
|
||||||
$ popd
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -95,8 +95,7 @@ pub type Byte = u8;
|
|||||||
|
|
||||||
impl Aml for Byte {
|
impl Aml for Byte {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x0a]; /* BytePrefix */
|
||||||
bytes.push(0x0a); /* BytePrefix */
|
|
||||||
bytes.push(*self);
|
bytes.push(*self);
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -106,8 +105,7 @@ pub type Word = u16;
|
|||||||
|
|
||||||
impl Aml for Word {
|
impl Aml for Word {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x0bu8]; /* WordPrefix */
|
||||||
bytes.push(0x0bu8); /* WordPrefix */
|
|
||||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
bytes.append(&mut self.to_le_bytes().to_vec());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -117,8 +115,7 @@ pub type DWord = u32;
|
|||||||
|
|
||||||
impl Aml for DWord {
|
impl Aml for DWord {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x0c]; /* DWordPrefix */
|
||||||
bytes.push(0x0c); /* DWordPrefix */
|
|
||||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
bytes.append(&mut self.to_le_bytes().to_vec());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -128,8 +125,7 @@ pub type QWord = u64;
|
|||||||
|
|
||||||
impl Aml for QWord {
|
impl Aml for QWord {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x0e]; /* QWordPrefix */
|
||||||
bytes.push(0x0e); /* QWordPrefix */
|
|
||||||
bytes.append(&mut self.to_le_bytes().to_vec());
|
bytes.append(&mut self.to_le_bytes().to_vec());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -147,8 +143,7 @@ impl Aml for Name {
|
|||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
pub fn new(path: Path, inner: &dyn Aml) -> Self {
|
pub fn new(path: Path, inner: &dyn Aml) -> Self {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x08]; /* NameOp */
|
||||||
bytes.push(0x08); /* NameOp */
|
|
||||||
bytes.append(&mut path.to_aml_bytes());
|
bytes.append(&mut path.to_aml_bytes());
|
||||||
bytes.append(&mut inner.to_aml_bytes());
|
bytes.append(&mut inner.to_aml_bytes());
|
||||||
Name { bytes }
|
Name { bytes }
|
||||||
@@ -161,8 +156,7 @@ pub struct Package<'a> {
|
|||||||
|
|
||||||
impl<'a> Aml for Package<'a> {
|
impl<'a> Aml for Package<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![self.children.len() as u8];
|
||||||
bytes.push(self.children.len() as u8);
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
bytes.append(&mut child.to_aml_bytes());
|
bytes.append(&mut child.to_aml_bytes());
|
||||||
}
|
}
|
||||||
@@ -239,11 +233,11 @@ fn create_pkg_length(data: &[u8], include_self: bool) -> Vec<u8> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EISAName {
|
pub struct EisaName {
|
||||||
value: DWord,
|
value: DWord,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EISAName {
|
impl EisaName {
|
||||||
pub fn new(name: &str) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
assert_eq!(name.len(), 7);
|
assert_eq!(name.len(), 7);
|
||||||
|
|
||||||
@@ -258,11 +252,11 @@ impl EISAName {
|
|||||||
| name.chars().nth(6).unwrap().to_digit(16).unwrap())
|
| name.chars().nth(6).unwrap().to_digit(16).unwrap())
|
||||||
.swap_bytes();
|
.swap_bytes();
|
||||||
|
|
||||||
EISAName { value }
|
EisaName { value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for EISAName {
|
impl Aml for EisaName {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
self.value.to_aml_bytes()
|
self.value.to_aml_bytes()
|
||||||
}
|
}
|
||||||
@@ -289,8 +283,7 @@ impl Aml for Usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_aml_string(v: &str) -> Vec<u8> {
|
fn create_aml_string(v: &str) -> Vec<u8> {
|
||||||
let mut data = Vec::new();
|
let mut data = vec![0x0D]; /* String Op */
|
||||||
data.push(0x0D); /* String Op */
|
|
||||||
data.extend_from_slice(v.as_bytes());
|
data.extend_from_slice(v.as_bytes());
|
||||||
data.push(0x0); /* NullChar */
|
data.push(0x0); /* NullChar */
|
||||||
data
|
data
|
||||||
@@ -374,9 +367,7 @@ impl Memory32Fixed {
|
|||||||
|
|
||||||
impl Aml for Memory32Fixed {
|
impl Aml for Memory32Fixed {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x86]; /* Memory32Fixed */
|
||||||
|
|
||||||
bytes.push(0x86); /* Memory32Fixed */
|
|
||||||
bytes.append(&mut 9u16.to_le_bytes().to_vec());
|
bytes.append(&mut 9u16.to_le_bytes().to_vec());
|
||||||
|
|
||||||
// 9 bytes of payload
|
// 9 bytes of payload
|
||||||
@@ -390,7 +381,7 @@ impl Aml for Memory32Fixed {
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum AddressSpaceType {
|
enum AddressSpaceType {
|
||||||
Memory,
|
Memory,
|
||||||
IO,
|
Io,
|
||||||
BusNumber,
|
BusNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +412,7 @@ impl<T> AddressSpace<T> {
|
|||||||
|
|
||||||
pub fn new_io(min: T, max: T) -> Self {
|
pub fn new_io(min: T, max: T) -> Self {
|
||||||
AddressSpace {
|
AddressSpace {
|
||||||
r#type: AddressSpaceType::IO,
|
r#type: AddressSpaceType::Io,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
type_flags: 3, /* EntireRange */
|
type_flags: 3, /* EntireRange */
|
||||||
@@ -510,16 +501,16 @@ impl Aml for AddressSpace<u64> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IO {
|
pub struct Io {
|
||||||
min: u16,
|
min: u16,
|
||||||
max: u16,
|
max: u16,
|
||||||
alignment: u8,
|
alignment: u8,
|
||||||
length: u8,
|
length: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IO {
|
impl Io {
|
||||||
pub fn new(min: u16, max: u16, alignment: u8, length: u8) -> Self {
|
pub fn new(min: u16, max: u16, alignment: u8, length: u8) -> Self {
|
||||||
IO {
|
Io {
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
alignment,
|
alignment,
|
||||||
@@ -528,11 +519,10 @@ impl IO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aml for IO {
|
impl Aml for Io {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x47]; /* Io Port Descriptor */
|
||||||
|
|
||||||
bytes.push(0x47); /* IO Port Descriptor */
|
|
||||||
bytes.push(1); /* IODecode16 */
|
bytes.push(1); /* IODecode16 */
|
||||||
bytes.append(&mut self.min.to_le_bytes().to_vec());
|
bytes.append(&mut self.min.to_le_bytes().to_vec());
|
||||||
bytes.append(&mut self.max.to_le_bytes().to_vec());
|
bytes.append(&mut self.max.to_le_bytes().to_vec());
|
||||||
@@ -571,9 +561,7 @@ impl Interrupt {
|
|||||||
|
|
||||||
impl Aml for Interrupt {
|
impl Aml for Interrupt {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x89]; /* Extended IRQ Descriptor */
|
||||||
|
|
||||||
bytes.push(0x89); /* Extended IRQ Descriptor */
|
|
||||||
bytes.append(&mut 6u16.to_le_bytes().to_vec());
|
bytes.append(&mut 6u16.to_le_bytes().to_vec());
|
||||||
let flags = (self.shared as u8) << 3
|
let flags = (self.shared as u8) << 3
|
||||||
| (self.active_low as u8) << 2
|
| (self.active_low as u8) << 2
|
||||||
@@ -699,8 +687,7 @@ impl<'a> Return<'a> {
|
|||||||
|
|
||||||
impl<'a> Aml for Return<'a> {
|
impl<'a> Aml for Return<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0xa4]; /* ReturnOp */
|
||||||
bytes.push(0xa4); /* ReturnOp */
|
|
||||||
bytes.append(&mut self.value.to_aml_bytes());
|
bytes.append(&mut self.value.to_aml_bytes());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -788,14 +775,14 @@ impl Aml for Field {
|
|||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum OpRegionSpace {
|
pub enum OpRegionSpace {
|
||||||
SystemMemory,
|
SystemMemory,
|
||||||
SystemIO,
|
SystemIo,
|
||||||
PCIConfig,
|
PConfig,
|
||||||
EmbeddedControl,
|
EmbeddedControl,
|
||||||
SMBus,
|
Smbus,
|
||||||
SystemCMOS,
|
SystemCmos,
|
||||||
PciBarTarget,
|
PciBarTarget,
|
||||||
IPMI,
|
Ipmi,
|
||||||
GeneralPurposeIO,
|
GeneralPurposeIo,
|
||||||
GenericSerialBus,
|
GenericSerialBus,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,8 +863,7 @@ impl<'a> Equal<'a> {
|
|||||||
|
|
||||||
impl<'a> Aml for Equal<'a> {
|
impl<'a> Aml for Equal<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x93]; /* LEqualOp */
|
||||||
bytes.push(0x93); /* LEqualOp */
|
|
||||||
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
||||||
bytes
|
bytes
|
||||||
@@ -897,8 +883,7 @@ impl<'a> LessThan<'a> {
|
|||||||
|
|
||||||
impl<'a> Aml for LessThan<'a> {
|
impl<'a> Aml for LessThan<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x95]; /* LLessOp */
|
||||||
bytes.push(0x95); /* LLessOp */
|
|
||||||
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
bytes.extend_from_slice(&self.left.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
bytes.extend_from_slice(&self.right.to_aml_bytes());
|
||||||
bytes
|
bytes
|
||||||
@@ -940,8 +925,7 @@ impl<'a> Store<'a> {
|
|||||||
|
|
||||||
impl<'a> Aml for Store<'a> {
|
impl<'a> Aml for Store<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x70]; /* StoreOp */
|
||||||
bytes.push(0x70); /* StoreOp */
|
|
||||||
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.name.to_aml_bytes());
|
bytes.extend_from_slice(&self.name.to_aml_bytes());
|
||||||
bytes
|
bytes
|
||||||
@@ -961,8 +945,7 @@ impl Mutex {
|
|||||||
|
|
||||||
impl Aml for Mutex {
|
impl Aml for Mutex {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
||||||
bytes.push(0x5b); /* ExtOpPrefix */
|
|
||||||
bytes.push(0x01); /* MutexOp */
|
bytes.push(0x01); /* MutexOp */
|
||||||
bytes.extend_from_slice(&self.path.to_aml_bytes());
|
bytes.extend_from_slice(&self.path.to_aml_bytes());
|
||||||
bytes.push(self.sync_level);
|
bytes.push(self.sync_level);
|
||||||
@@ -983,8 +966,7 @@ impl Acquire {
|
|||||||
|
|
||||||
impl Aml for Acquire {
|
impl Aml for Acquire {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
||||||
bytes.push(0x5b); /* ExtOpPrefix */
|
|
||||||
bytes.push(0x23); /* AcquireOp */
|
bytes.push(0x23); /* AcquireOp */
|
||||||
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.timeout.to_le_bytes());
|
bytes.extend_from_slice(&self.timeout.to_le_bytes());
|
||||||
@@ -1004,8 +986,7 @@ impl Release {
|
|||||||
|
|
||||||
impl Aml for Release {
|
impl Aml for Release {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x5b]; /* ExtOpPrefix */
|
||||||
bytes.push(0x5b); /* ExtOpPrefix */
|
|
||||||
bytes.push(0x27); /* ReleaseOp */
|
bytes.push(0x27); /* ReleaseOp */
|
||||||
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
bytes.extend_from_slice(&self.mutex.to_aml_bytes());
|
||||||
bytes
|
bytes
|
||||||
@@ -1025,8 +1006,7 @@ impl<'a> Notify<'a> {
|
|||||||
|
|
||||||
impl<'a> Aml for Notify<'a> {
|
impl<'a> Aml for Notify<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x86]; /* NotifyOp */
|
||||||
bytes.push(0x86); /* NotifyOp */
|
|
||||||
bytes.extend_from_slice(&self.object.to_aml_bytes());
|
bytes.extend_from_slice(&self.object.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
bytes.extend_from_slice(&self.value.to_aml_bytes());
|
||||||
bytes
|
bytes
|
||||||
@@ -1082,8 +1062,7 @@ macro_rules! binary_op {
|
|||||||
|
|
||||||
impl<'a> Aml for $name<'a> {
|
impl<'a> Aml for $name<'a> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![$opcode]; /* Op for the binary operator */
|
||||||
bytes.push($opcode); /* Op for the binary operator */
|
|
||||||
bytes.extend_from_slice(&self.a.to_aml_bytes());
|
bytes.extend_from_slice(&self.a.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.b.to_aml_bytes());
|
bytes.extend_from_slice(&self.b.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.target.to_aml_bytes());
|
bytes.extend_from_slice(&self.target.to_aml_bytes());
|
||||||
@@ -1180,8 +1159,7 @@ impl<'a, T> CreateField<'a, T> {
|
|||||||
|
|
||||||
impl<'a> Aml for CreateField<'a, u64> {
|
impl<'a> Aml for CreateField<'a, u64> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x8f]; /* CreateQWordFieldOp */
|
||||||
bytes.push(0x8f); /* CreateQWordFieldOp */
|
|
||||||
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
||||||
@@ -1191,8 +1169,7 @@ impl<'a> Aml for CreateField<'a, u64> {
|
|||||||
|
|
||||||
impl<'a> Aml for CreateField<'a, u32> {
|
impl<'a> Aml for CreateField<'a, u32> {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0x8a]; /* CreateDWordFieldOp */
|
||||||
bytes.push(0x8a); /* CreateDWordFieldOp */
|
|
||||||
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
bytes.extend_from_slice(&self.buffer.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
bytes.extend_from_slice(&self.offset.to_aml_bytes());
|
||||||
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
bytes.extend_from_slice(&self.field.to_aml_bytes());
|
||||||
@@ -1235,12 +1212,12 @@ mod tests {
|
|||||||
Device::new(
|
Device::new(
|
||||||
"_SB_.COM1".into(),
|
"_SB_.COM1".into(),
|
||||||
vec![
|
vec![
|
||||||
&Name::new("_HID".into(), &EISAName::new("PNP0501")),
|
&Name::new("_HID".into(), &EisaName::new("PNP0501")),
|
||||||
&Name::new(
|
&Name::new(
|
||||||
"_CRS".into(),
|
"_CRS".into(),
|
||||||
&ResourceTemplate::new(vec![
|
&ResourceTemplate::new(vec![
|
||||||
&Interrupt::new(true, true, false, false, 4),
|
&Interrupt::new(true, true, false, false, 4),
|
||||||
&IO::new(0x3f8, 0x3f8, 0, 0x8)
|
&Io::new(0x3f8, 0x3f8, 0, 0x8)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -1476,7 +1453,7 @@ mod tests {
|
|||||||
"_CRS".into(),
|
"_CRS".into(),
|
||||||
&ResourceTemplate::new(vec![
|
&ResourceTemplate::new(vec![
|
||||||
&Interrupt::new(true, true, false, false, 4),
|
&Interrupt::new(true, true, false, false, 4),
|
||||||
&IO::new(0x3f8, 0x3f8, 0, 0x8)
|
&Io::new(0x3f8, 0x3f8, 0, 0x8)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
.to_aml_bytes(),
|
.to_aml_bytes(),
|
||||||
@@ -1519,7 +1496,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_eisa_name() {
|
fn test_eisa_name() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Name::new("_HID".into(), &EISAName::new("PNP0501")).to_aml_bytes(),
|
Name::new("_HID".into(), &EisaName::new("PNP0501")).to_aml_bytes(),
|
||||||
[0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01],
|
[0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1665,14 +1642,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_op_region() {
|
fn test_op_region() {
|
||||||
/*
|
/*
|
||||||
OperationRegion (PRST, SystemIO, 0x0CD8, 0x0C)
|
OperationRegion (PRST, SystemIo, 0x0CD8, 0x0C)
|
||||||
*/
|
*/
|
||||||
let op_region_data = [
|
let op_region_data = [
|
||||||
0x5Bu8, 0x80, 0x50, 0x52, 0x53, 0x54, 0x01, 0x0B, 0xD8, 0x0C, 0x0A, 0x0C,
|
0x5Bu8, 0x80, 0x50, 0x52, 0x53, 0x54, 0x01, 0x0B, 0xD8, 0x0C, 0x0A, 0x0C,
|
||||||
];
|
];
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OpRegion::new("PRST".into(), OpRegionSpace::SystemIO, 0xcd8, 0xc).to_aml_bytes(),
|
OpRegion::new("PRST".into(), OpRegionSpace::SystemIo, 0xcd8, 0xc).to_aml_bytes(),
|
||||||
&op_region_data[..]
|
&op_region_data[..]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1766,7 +1743,7 @@ mod tests {
|
|||||||
Device::new(
|
Device::new(
|
||||||
"_SB_.MHPC".into(),
|
"_SB_.MHPC".into(),
|
||||||
vec![
|
vec![
|
||||||
&Name::new("_HID".into(), &EISAName::new("PNP0A06")),
|
&Name::new("_HID".into(), &EisaName::new("PNP0A06")),
|
||||||
&mutex,
|
&mutex,
|
||||||
&Method::new(
|
&Method::new(
|
||||||
"TEST".into(),
|
"TEST".into(),
|
||||||
@@ -1807,7 +1784,7 @@ mod tests {
|
|||||||
Device::new(
|
Device::new(
|
||||||
"_SB_.MHPC".into(),
|
"_SB_.MHPC".into(),
|
||||||
vec![
|
vec![
|
||||||
&Name::new("_HID".into(), &EISAName::new("PNP0A06")),
|
&Name::new("_HID".into(), &EisaName::new("PNP0A06")),
|
||||||
&Method::new(
|
&Method::new(
|
||||||
"TEST".into(),
|
"TEST".into(),
|
||||||
0,
|
0,
|
||||||
@@ -1848,7 +1825,7 @@ mod tests {
|
|||||||
Device::new(
|
Device::new(
|
||||||
"_SB_.MHPC".into(),
|
"_SB_.MHPC".into(),
|
||||||
vec![
|
vec![
|
||||||
&Name::new("_HID".into(), &EISAName::new("PNP0A06")),
|
&Name::new("_HID".into(), &EisaName::new("PNP0A06")),
|
||||||
&Method::new(
|
&Method::new(
|
||||||
"TEST".into(),
|
"TEST".into(),
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use vm_memory::ByteValued;
|
|||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct RSDP {
|
pub struct Rsdp {
|
||||||
pub signature: [u8; 8],
|
pub signature: [u8; 8],
|
||||||
pub checksum: u8,
|
pub checksum: u8,
|
||||||
pub oem_id: [u8; 6],
|
pub oem_id: [u8; 6],
|
||||||
@@ -19,17 +19,17 @@ pub struct RSDP {
|
|||||||
_reserved: [u8; 3],
|
_reserved: [u8; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ByteValued for RSDP {}
|
unsafe impl ByteValued for Rsdp {}
|
||||||
|
|
||||||
impl RSDP {
|
impl Rsdp {
|
||||||
pub fn new(oem_id: [u8; 6], xsdt_addr: u64) -> Self {
|
pub fn new(oem_id: [u8; 6], xsdt_addr: u64) -> Self {
|
||||||
let mut rsdp = RSDP {
|
let mut rsdp = Rsdp {
|
||||||
signature: *b"RSD PTR ",
|
signature: *b"RSD PTR ",
|
||||||
checksum: 0,
|
checksum: 0,
|
||||||
oem_id,
|
oem_id,
|
||||||
revision: 2,
|
revision: 2,
|
||||||
_rsdt_addr: 0,
|
_rsdt_addr: 0,
|
||||||
length: std::mem::size_of::<RSDP>() as u32,
|
length: std::mem::size_of::<Rsdp>() as u32,
|
||||||
xsdt_addr,
|
xsdt_addr,
|
||||||
extended_checksum: 0,
|
extended_checksum: 0,
|
||||||
_reserved: [0; 3],
|
_reserved: [0; 3],
|
||||||
@@ -41,18 +41,18 @@ impl RSDP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn len() -> usize {
|
pub fn len() -> usize {
|
||||||
std::mem::size_of::<RSDP>()
|
std::mem::size_of::<Rsdp>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::RSDP;
|
use super::Rsdp;
|
||||||
use vm_memory::bytes::ByteValued;
|
use vm_memory::bytes::ByteValued;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rsdp() {
|
fn test_rsdp() {
|
||||||
let rsdp = RSDP::new(*b"CHYPER", 0xdead_beef);
|
let rsdp = Rsdp::new(*b"CHYPER", 0xdead_beef);
|
||||||
let sum = rsdp
|
let sum = rsdp
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ impl GenericAddress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SDT {
|
pub struct Sdt {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::len_without_is_empty)]
|
#[allow(clippy::len_without_is_empty)]
|
||||||
impl SDT {
|
impl Sdt {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
signature: [u8; 4],
|
signature: [u8; 4],
|
||||||
length: u32,
|
length: u32,
|
||||||
@@ -53,7 +53,7 @@ impl SDT {
|
|||||||
assert_eq!(data.len(), 36);
|
assert_eq!(data.len(), 36);
|
||||||
|
|
||||||
data.resize(length as usize, 0);
|
data.resize(length as usize, 0);
|
||||||
let mut sdt = SDT { data };
|
let mut sdt = Sdt { data };
|
||||||
|
|
||||||
sdt.update_checksum();
|
sdt.update_checksum();
|
||||||
sdt
|
sdt
|
||||||
@@ -117,11 +117,11 @@ impl SDT {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::SDT;
|
use super::Sdt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sdt() {
|
fn test_sdt() {
|
||||||
let mut sdt = SDT::new(*b"TEST", 40, 1, *b"CLOUDH", *b"TESTTEST", 1);
|
let mut sdt = Sdt::new(*b"TEST", 40, 1, *b"CLOUDH", *b"TESTTEST", 1);
|
||||||
let sum: u8 = sdt
|
let sum: u8 = sdt
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ impl fmt::Display for Error {
|
|||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum StatusCode {
|
pub enum StatusCode {
|
||||||
Continue,
|
Continue,
|
||||||
OK,
|
Ok,
|
||||||
NoContent,
|
NoContent,
|
||||||
BadRequest,
|
BadRequest,
|
||||||
NotFound,
|
NotFound,
|
||||||
@@ -50,7 +50,7 @@ impl StatusCode {
|
|||||||
fn from_raw(code: usize) -> StatusCode {
|
fn from_raw(code: usize) -> StatusCode {
|
||||||
match code {
|
match code {
|
||||||
100 => StatusCode::Continue,
|
100 => StatusCode::Continue,
|
||||||
200 => StatusCode::OK,
|
200 => StatusCode::Ok,
|
||||||
204 => StatusCode::NoContent,
|
204 => StatusCode::NoContent,
|
||||||
400 => StatusCode::BadRequest,
|
400 => StatusCode::BadRequest,
|
||||||
404 => StatusCode::NotFound,
|
404 => StatusCode::NotFound,
|
||||||
@@ -69,7 +69,7 @@ impl StatusCode {
|
|||||||
fn is_server_error(self) -> bool {
|
fn is_server_error(self) -> bool {
|
||||||
!matches!(
|
!matches!(
|
||||||
self,
|
self,
|
||||||
StatusCode::OK | StatusCode::Continue | StatusCode::NoContent
|
StatusCode::Ok | StatusCode::Continue | StatusCode::NoContent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,24 +6,21 @@ authors = ["The Chromium OS Authors"]
|
|||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
acpi = ["acpi_tables"]
|
acpi = ["acpi_tables"]
|
||||||
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
byteorder = "1.3.4"
|
arch_gen = { path = "../arch_gen" }
|
||||||
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
|
linux-loader = { version = "0.3.0", features = ["elf", "bzimage", "pe"] }
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = ">=1.0.27"
|
||||||
serde_json = ">=1.0.9"
|
serde_json = ">=1.0.9"
|
||||||
|
thiserror = "1.0"
|
||||||
vm-memory = { version = "0.5.0", features = ["backend-mmap"] }
|
vm-memory = { version = "0.5.0", features = ["backend-mmap"] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
|
||||||
arch_gen = { path = "../arch_gen" }
|
|
||||||
|
|
||||||
[dependencies.linux-loader]
|
|
||||||
git = "https://github.com/rust-vmm/linux-loader"
|
|
||||||
features = ["elf", "bzimage"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rand = "0.8.3"
|
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ use std::{io, result};
|
|||||||
use super::super::DeviceType;
|
use super::super::DeviceType;
|
||||||
use super::super::InitramfsConfig;
|
use super::super::InitramfsConfig;
|
||||||
use super::get_fdt_addr;
|
use super::get_fdt_addr;
|
||||||
use super::gic::GICDevice;
|
use super::gic::GicDevice;
|
||||||
use super::layout::{
|
use super::layout::{
|
||||||
FDT_MAX_SIZE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, PCI_MMCONFIG_SIZE,
|
FDT_MAX_SIZE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, PCI_MMCONFIG_SIZE,
|
||||||
PCI_MMCONFIG_START,
|
PCI_MMCONFIG_START,
|
||||||
};
|
};
|
||||||
use crate::aarch64::fdt::Error::CstringFDTTransform;
|
use crate::aarch64::fdt::Error::CstringFdtTransform;
|
||||||
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
|
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
|
||||||
|
|
||||||
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
|
// This is a value for uniquely identifying the FDT node declaring the interrupt controller.
|
||||||
@@ -30,6 +30,8 @@ const GIC_PHANDLE: u32 = 1;
|
|||||||
const MSI_PHANDLE: u32 = 2;
|
const MSI_PHANDLE: u32 = 2;
|
||||||
// This is a value for uniquely identifying the FDT node containing the clock definition.
|
// This is a value for uniquely identifying the FDT node containing the clock definition.
|
||||||
const CLOCK_PHANDLE: u32 = 3;
|
const CLOCK_PHANDLE: u32 = 3;
|
||||||
|
// This is a value for uniquely identifying the FDT node containing the gpio controller.
|
||||||
|
const GPIO_PHANDLE: u32 = 4;
|
||||||
|
|
||||||
// Read the documentation specified when appending the root node to the FDT.
|
// Read the documentation specified when appending the root node to the FDT.
|
||||||
const ADDRESS_CELLS: u32 = 0x2;
|
const ADDRESS_CELLS: u32 = 0x2;
|
||||||
@@ -45,6 +47,10 @@ const GIC_FDT_IRQ_TYPE_PPI: u32 = 1;
|
|||||||
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
const IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||||
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||||
|
|
||||||
|
// Keys and Buttons
|
||||||
|
// System Power Down
|
||||||
|
const KEY_POWER: u32 = 116;
|
||||||
|
|
||||||
// This links to libfdt which handles the creation of the binary blob
|
// This links to libfdt which handles the creation of the binary blob
|
||||||
// flattened device tree (fdt) that is passed to the kernel and indicates
|
// flattened device tree (fdt) that is passed to the kernel and indicates
|
||||||
// the hardware configuration of the machine.
|
// the hardware configuration of the machine.
|
||||||
@@ -62,7 +68,7 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for devices to be added to the Flattened Device Tree.
|
/// Trait for devices to be added to the Flattened Device Tree.
|
||||||
pub trait DeviceInfoForFDT {
|
pub trait DeviceInfoForFdt {
|
||||||
/// Returns the address where this device will be loaded.
|
/// Returns the address where this device will be loaded.
|
||||||
fn addr(&self) -> u64;
|
fn addr(&self) -> u64;
|
||||||
/// Returns the associated interrupt for this device.
|
/// Returns the associated interrupt for this device.
|
||||||
@@ -75,27 +81,27 @@ pub trait DeviceInfoForFDT {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to append node to the FDT.
|
/// Failed to append node to the FDT.
|
||||||
AppendFDTNode(io::Error),
|
AppendFdtNode(io::Error),
|
||||||
/// Failed to append a property to the FDT.
|
/// Failed to append a property to the FDT.
|
||||||
AppendFDTProperty(io::Error),
|
AppendFdtProperty(io::Error),
|
||||||
/// Syscall for creating FDT failed.
|
/// Syscall for creating FDT failed.
|
||||||
CreateFDT(io::Error),
|
CreateFdt(io::Error),
|
||||||
/// Failed to obtain a C style string.
|
/// Failed to obtain a C style string.
|
||||||
CstringFDTTransform(NulError),
|
CstringFdtTransform(NulError),
|
||||||
/// Failure in calling syscall for terminating this FDT.
|
/// Failure in calling syscall for terminating this FDT.
|
||||||
FinishFDTReserveMap(io::Error),
|
FinishFdtReserveMap(io::Error),
|
||||||
/// Failure in writing FDT in memory.
|
/// Failure in writing FDT in memory.
|
||||||
WriteFDTToMemory(GuestMemoryError),
|
WriteFdtToMemory(GuestMemoryError),
|
||||||
}
|
}
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// Creates the flattened device tree for this aarch64 VM.
|
/// Creates the flattened device tree for this aarch64 VM.
|
||||||
pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHasher>(
|
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
guest_mem: &GuestMemoryMmap,
|
guest_mem: &GuestMemoryMmap,
|
||||||
cmdline: &CStr,
|
cmdline: &CStr,
|
||||||
vcpu_mpidr: Vec<u64>,
|
vcpu_mpidr: Vec<u64>,
|
||||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
gic_device: &dyn GICDevice,
|
gic_device: &dyn GicDevice,
|
||||||
initrd: &Option<InitramfsConfig>,
|
initrd: &Option<InitramfsConfig>,
|
||||||
pci_space_address: &(u64, u64),
|
pci_space_address: &(u64, u64),
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<Vec<u8>> {
|
||||||
@@ -139,7 +145,7 @@ pub fn create_fdt<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHash
|
|||||||
let fdt_address = GuestAddress(get_fdt_addr(&guest_mem));
|
let fdt_address = GuestAddress(get_fdt_addr(&guest_mem));
|
||||||
guest_mem
|
guest_mem
|
||||||
.write_slice(fdt_final.as_slice(), fdt_address)
|
.write_slice(fdt_final.as_slice(), fdt_address)
|
||||||
.map_err(Error::WriteFDTToMemory)?;
|
.map_err(Error::WriteFdtToMemory)?;
|
||||||
Ok(fdt_final)
|
Ok(fdt_final)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +155,7 @@ fn allocate_fdt(fdt: &mut Vec<u8>) -> Result<()> {
|
|||||||
let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, FDT_MAX_SIZE as c_int) };
|
let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, FDT_MAX_SIZE as c_int) };
|
||||||
|
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::CreateFDT(io::Error::last_os_error()));
|
return Err(Error::CreateFdt(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The flattened device trees created with fdt_create() contains a list of
|
// The flattened device trees created with fdt_create() contains a list of
|
||||||
@@ -160,7 +166,7 @@ fn allocate_fdt(fdt: &mut Vec<u8>) -> Result<()> {
|
|||||||
// Safe since we previously allocated this array.
|
// Safe since we previously allocated this array.
|
||||||
fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
|
fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
|
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -169,7 +175,7 @@ fn finish_fdt(from_fdt: &mut Vec<u8>, to_fdt: &mut Vec<u8>) -> Result<()> {
|
|||||||
// Safe since we allocated `fdt_final` and previously passed in its size.
|
// Safe since we allocated `fdt_final` and previously passed in its size.
|
||||||
let mut fdt_ret = unsafe { fdt_finish(from_fdt.as_mut_ptr() as *mut c_void) };
|
let mut fdt_ret = unsafe { fdt_finish(from_fdt.as_mut_ptr() as *mut c_void) };
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
|
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe because we allocated both arrays with the correct size.
|
// Safe because we allocated both arrays with the correct size.
|
||||||
@@ -181,25 +187,25 @@ fn finish_fdt(from_fdt: &mut Vec<u8>, to_fdt: &mut Vec<u8>) -> Result<()> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
|
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe since we allocated `to_fdt`.
|
// Safe since we allocated `to_fdt`.
|
||||||
fdt_ret = unsafe { fdt_pack(to_fdt.as_mut_ptr() as *mut c_void) };
|
fdt_ret = unsafe { fdt_pack(to_fdt.as_mut_ptr() as *mut c_void) };
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::FinishFDTReserveMap(io::Error::last_os_error()));
|
return Err(Error::FinishFdtReserveMap(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following are auxiliary functions for appending nodes to FDT.
|
// Following are auxiliary functions for appending nodes to FDT.
|
||||||
fn append_begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
|
fn append_begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
|
||||||
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
|
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
|
||||||
|
|
||||||
// Safe because we allocated fdt and converted name to a CString
|
// Safe because we allocated fdt and converted name to a CString
|
||||||
let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
|
let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::AppendFDTNode(io::Error::last_os_error()));
|
return Err(Error::AppendFdtNode(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -208,7 +214,7 @@ fn append_end_node(fdt: &mut Vec<u8>) -> Result<()> {
|
|||||||
// Safe because we allocated fdt.
|
// Safe because we allocated fdt.
|
||||||
let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
|
let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::AppendFDTNode(io::Error::last_os_error()));
|
return Err(Error::AppendFdtNode(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -223,13 +229,13 @@ fn append_property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn append_property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<()> {
|
fn append_property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<()> {
|
||||||
let cstr_value = CString::new(value).map_err(CstringFDTTransform)?;
|
let cstr_value = CString::new(value).map_err(CstringFdtTransform)?;
|
||||||
append_property_cstring(fdt, name, &cstr_value)
|
append_property_cstring(fdt, name, &cstr_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<()> {
|
fn append_property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<()> {
|
||||||
let value_bytes = cstr_value.to_bytes_with_nul();
|
let value_bytes = cstr_value.to_bytes_with_nul();
|
||||||
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
|
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
|
||||||
// Safe because we allocated fdt, converted name and value to CStrings
|
// Safe because we allocated fdt, converted name and value to CStrings
|
||||||
let fdt_ret = unsafe {
|
let fdt_ret = unsafe {
|
||||||
fdt_property(
|
fdt_property(
|
||||||
@@ -240,13 +246,13 @@ fn append_property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) ->
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::AppendFDTProperty(io::Error::last_os_error()));
|
return Err(Error::AppendFdtProperty(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
|
fn append_property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
|
||||||
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
|
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
|
||||||
|
|
||||||
// Safe because we allocated fdt, converted name to a CString
|
// Safe because we allocated fdt, converted name to a CString
|
||||||
let fdt_ret = unsafe {
|
let fdt_ret = unsafe {
|
||||||
@@ -258,13 +264,13 @@ fn append_property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::AppendFDTProperty(io::Error::last_os_error()));
|
return Err(Error::AppendFdtProperty(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
|
fn append_property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
|
||||||
let cstr_name = CString::new(name).map_err(CstringFDTTransform)?;
|
let cstr_name = CString::new(name).map_err(CstringFdtTransform)?;
|
||||||
let val_ptr = val.as_ptr() as *const c_void;
|
let val_ptr = val.as_ptr() as *const c_void;
|
||||||
|
|
||||||
// Safe because we allocated fdt and converted name to a CString
|
// Safe because we allocated fdt and converted name to a CString
|
||||||
@@ -277,7 +283,7 @@ fn append_property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if fdt_ret != 0 {
|
if fdt_ret != 0 {
|
||||||
return Err(Error::AppendFDTProperty(io::Error::last_os_error()));
|
return Err(Error::AppendFdtProperty(io::Error::last_os_error()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -374,7 +380,7 @@ fn create_chosen_node(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_gic_node(fdt: &mut Vec<u8>, gic_device: &dyn GICDevice) -> Result<()> {
|
fn create_gic_node(fdt: &mut Vec<u8>, gic_device: &dyn GicDevice) -> Result<()> {
|
||||||
let gic_reg_prop = generate_prop64(gic_device.device_properties());
|
let gic_reg_prop = generate_prop64(gic_device.device_properties());
|
||||||
|
|
||||||
append_begin_node(fdt, "intc")?;
|
append_begin_node(fdt, "intc")?;
|
||||||
@@ -466,7 +472,7 @@ fn create_psci_node(fdt: &mut Vec<u8>) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_virtio_node<T: DeviceInfoForFDT + Clone + Debug>(
|
fn create_virtio_node<T: DeviceInfoForFdt + Clone + Debug>(
|
||||||
fdt: &mut Vec<u8>,
|
fdt: &mut Vec<u8>,
|
||||||
dev_info: &T,
|
dev_info: &T,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -483,15 +489,16 @@ fn create_virtio_node<T: DeviceInfoForFDT + Clone + Debug>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_serial_node<T: DeviceInfoForFDT + Clone + Debug>(
|
fn create_serial_node<T: DeviceInfoForFdt + Clone + Debug>(
|
||||||
fdt: &mut Vec<u8>,
|
fdt: &mut Vec<u8>,
|
||||||
dev_info: &T,
|
dev_info: &T,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let compatible = b"arm,pl011\0arm,primecell\0";
|
||||||
let serial_reg_prop = generate_prop64(&[dev_info.addr(), dev_info.length()]);
|
let serial_reg_prop = generate_prop64(&[dev_info.addr(), dev_info.length()]);
|
||||||
let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]);
|
let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]);
|
||||||
|
|
||||||
append_begin_node(fdt, &format!("uart@{:x}", dev_info.addr()))?;
|
append_begin_node(fdt, &format!("pl011@{:x}", dev_info.addr()))?;
|
||||||
append_property_string(fdt, "compatible", "ns16550a")?;
|
append_property(fdt, "compatible", compatible)?;
|
||||||
append_property(fdt, "reg", &serial_reg_prop)?;
|
append_property(fdt, "reg", &serial_reg_prop)?;
|
||||||
append_property_u32(fdt, "clocks", CLOCK_PHANDLE)?;
|
append_property_u32(fdt, "clocks", CLOCK_PHANDLE)?;
|
||||||
append_property_string(fdt, "clock-names", "apb_pclk")?;
|
append_property_string(fdt, "clock-names", "apb_pclk")?;
|
||||||
@@ -501,7 +508,7 @@ fn create_serial_node<T: DeviceInfoForFDT + Clone + Debug>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_rtc_node<T: DeviceInfoForFDT + Clone + Debug>(
|
fn create_rtc_node<T: DeviceInfoForFdt + Clone + Debug>(
|
||||||
fdt: &mut Vec<u8>,
|
fdt: &mut Vec<u8>,
|
||||||
dev_info: &T,
|
dev_info: &T,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -519,7 +526,42 @@ fn create_rtc_node<T: DeviceInfoForFDT + Clone + Debug>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHasher>(
|
fn create_gpio_node<T: DeviceInfoForFdt + Clone + Debug>(
|
||||||
|
fdt: &mut Vec<u8>,
|
||||||
|
dev_info: &T,
|
||||||
|
) -> Result<()> {
|
||||||
|
// PL061 GPIO controller node
|
||||||
|
let compatible = b"arm,pl061\0arm,primecell\0";
|
||||||
|
let gpio_reg_prop = generate_prop64(&[dev_info.addr(), dev_info.length()]);
|
||||||
|
let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]);
|
||||||
|
append_begin_node(fdt, &format!("pl061@{:x}", dev_info.addr()))?;
|
||||||
|
append_property(fdt, "compatible", compatible)?;
|
||||||
|
append_property(fdt, "reg", &gpio_reg_prop)?;
|
||||||
|
append_property(fdt, "interrupts", &irq)?;
|
||||||
|
append_property_null(fdt, "gpio-controller")?;
|
||||||
|
append_property_u32(fdt, "#gpio-cells", 2)?;
|
||||||
|
append_property_u32(fdt, "clocks", CLOCK_PHANDLE)?;
|
||||||
|
append_property_string(fdt, "clock-names", "apb_pclk")?;
|
||||||
|
append_property_u32(fdt, "phandle", GPIO_PHANDLE)?;
|
||||||
|
append_end_node(fdt)?;
|
||||||
|
|
||||||
|
// gpio-keys node
|
||||||
|
append_begin_node(fdt, "/gpio-keys")?;
|
||||||
|
append_property_string(fdt, "compatible", "gpio-keys")?;
|
||||||
|
append_property_u32(fdt, "#size-cells", 0)?;
|
||||||
|
append_property_u32(fdt, "#address-cells", 1)?;
|
||||||
|
append_begin_node(fdt, "/gpio-keys/poweroff")?;
|
||||||
|
append_property_string(fdt, "label", "GPIO Key Poweroff")?;
|
||||||
|
append_property_u32(fdt, "linux,code", KEY_POWER)?;
|
||||||
|
let gpios = generate_prop32(&[GPIO_PHANDLE, 3, 0]);
|
||||||
|
append_property(fdt, "gpios", &gpios)?;
|
||||||
|
append_end_node(fdt)?;
|
||||||
|
append_end_node(fdt)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
fdt: &mut Vec<u8>,
|
fdt: &mut Vec<u8>,
|
||||||
dev_info: &HashMap<(DeviceType, String), T, S>,
|
dev_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -528,7 +570,8 @@ fn create_devices_node<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::Buil
|
|||||||
|
|
||||||
for ((device_type, _device_id), info) in dev_info {
|
for ((device_type, _device_id), info) in dev_info {
|
||||||
match device_type {
|
match device_type {
|
||||||
DeviceType::RTC => create_rtc_node(fdt, info)?,
|
DeviceType::Gpio => create_gpio_node(fdt, info)?,
|
||||||
|
DeviceType::Rtc => create_rtc_node(fdt, info)?,
|
||||||
DeviceType::Serial => create_serial_node(fdt, info)?,
|
DeviceType::Serial => create_serial_node(fdt, info)?,
|
||||||
DeviceType::Virtio(_) => {
|
DeviceType::Virtio(_) => {
|
||||||
ordered_virtio_device.push(info);
|
ordered_virtio_device.push(info);
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
pub mod kvm {
|
pub mod kvm {
|
||||||
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||||
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs};
|
||||||
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGICDevice};
|
use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice};
|
||||||
use crate::aarch64::gic::redist_regs::{get_redist_regs, set_redist_regs};
|
use crate::aarch64::gic::redist_regs::{get_redist_regs, set_redist_regs};
|
||||||
use crate::aarch64::gic::GICDevice;
|
use crate::aarch64::gic::GicDevice;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use hypervisor::kvm::kvm_bindings;
|
use hypervisor::kvm::kvm_bindings;
|
||||||
@@ -37,14 +37,14 @@ pub mod kvm {
|
|||||||
/// Error in restoring GIC redistributor registers.
|
/// Error in restoring GIC redistributor registers.
|
||||||
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
RestoreRedistributorRegisters(crate::aarch64::gic::Error),
|
||||||
/// Error in saving GIC CPU interface registers.
|
/// Error in saving GIC CPU interface registers.
|
||||||
SaveICCRegisters(crate::aarch64::gic::Error),
|
SaveIccRegisters(crate::aarch64::gic::Error),
|
||||||
/// Error in restoring GIC CPU interface registers.
|
/// Error in restoring GIC CPU interface registers.
|
||||||
RestoreICCRegisters(crate::aarch64::gic::Error),
|
RestoreIccRegisters(crate::aarch64::gic::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
pub struct KvmGICv3 {
|
pub struct KvmGicV3 {
|
||||||
/// The hypervisor agnostic device
|
/// The hypervisor agnostic device
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
|
|
||||||
@@ -67,34 +67,34 @@ pub mod kvm {
|
|||||||
gicd_ctlr: u32,
|
gicd_ctlr: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KvmGICv3 {
|
impl KvmGicV3 {
|
||||||
// Unfortunately bindgen omits defines that are based on other defines.
|
// Unfortunately bindgen omits defines that are based on other defines.
|
||||||
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
|
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
|
||||||
pub const SZ_64K: u64 = 0x0001_0000;
|
pub const SZ_64K: u64 = 0x0001_0000;
|
||||||
const KVM_VGIC_V3_DIST_SIZE: u64 = KvmGICv3::SZ_64K;
|
const KVM_VGIC_V3_DIST_SIZE: u64 = KvmGicV3::SZ_64K;
|
||||||
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
|
const KVM_VGIC_V3_REDIST_SIZE: u64 = (2 * KvmGicV3::SZ_64K);
|
||||||
|
|
||||||
// Device trees specific constants
|
// Device trees specific constants
|
||||||
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
|
||||||
|
|
||||||
/// Get the address of the GIC distributor.
|
/// Get the address of the GIC distributor.
|
||||||
pub fn get_dist_addr() -> u64 {
|
pub fn get_dist_addr() -> u64 {
|
||||||
layout::MAPPED_IO_START - KvmGICv3::KVM_VGIC_V3_DIST_SIZE
|
layout::MAPPED_IO_START - KvmGicV3::KVM_VGIC_V3_DIST_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the size of the GIC distributor.
|
/// Get the size of the GIC distributor.
|
||||||
pub fn get_dist_size() -> u64 {
|
pub fn get_dist_size() -> u64 {
|
||||||
KvmGICv3::KVM_VGIC_V3_DIST_SIZE
|
KvmGicV3::KVM_VGIC_V3_DIST_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the address of the GIC redistributors.
|
/// Get the address of the GIC redistributors.
|
||||||
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
|
pub fn get_redists_addr(vcpu_count: u64) -> u64 {
|
||||||
KvmGICv3::get_dist_addr() - KvmGICv3::get_redists_size(vcpu_count)
|
KvmGicV3::get_dist_addr() - KvmGicV3::get_redists_size(vcpu_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the size of the GIC redistributors.
|
/// Get the size of the GIC redistributors.
|
||||||
pub fn get_redists_size(vcpu_count: u64) -> u64 {
|
pub fn get_redists_size(vcpu_count: u64) -> u64 {
|
||||||
vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE
|
vcpu_count * KvmGicV3::KVM_VGIC_V3_REDIST_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the state of GIC.
|
/// Save the state of GIC.
|
||||||
@@ -112,7 +112,7 @@ pub mod kvm {
|
|||||||
.map_err(Error::SaveRedistributorRegisters)?;
|
.map_err(Error::SaveRedistributorRegisters)?;
|
||||||
|
|
||||||
let icc_state =
|
let icc_state =
|
||||||
get_icc_regs(&self.device(), &gicr_typers).map_err(Error::SaveICCRegisters)?;
|
get_icc_regs(&self.device(), &gicr_typers).map_err(Error::SaveIccRegisters)?;
|
||||||
|
|
||||||
Ok(Gicv3State {
|
Ok(Gicv3State {
|
||||||
dist: dist_state,
|
dist: dist_state,
|
||||||
@@ -134,13 +134,13 @@ pub mod kvm {
|
|||||||
.map_err(Error::RestoreRedistributorRegisters)?;
|
.map_err(Error::RestoreRedistributorRegisters)?;
|
||||||
|
|
||||||
set_icc_regs(&self.device(), &gicr_typers, &state.icc)
|
set_icc_regs(&self.device(), &gicr_typers, &state.icc)
|
||||||
.map_err(Error::RestoreICCRegisters)?;
|
.map_err(Error::RestoreIccRegisters)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GICDevice for KvmGICv3 {
|
impl GicDevice for KvmGicV3 {
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
||||||
&self.device
|
&self.device
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fdt_maint_irq(&self) -> u32 {
|
fn fdt_maint_irq(&self) -> u32 {
|
||||||
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
|
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_properties(&self) -> &[u64] {
|
fn device_properties(&self) -> &[u64] {
|
||||||
@@ -170,7 +170,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KvmGICDevice for KvmGICv3 {
|
impl KvmGicDevice for KvmGicV3 {
|
||||||
fn version() -> u32 {
|
fn version() -> u32 {
|
||||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3
|
||||||
}
|
}
|
||||||
@@ -178,15 +178,15 @@ pub mod kvm {
|
|||||||
fn create_device(
|
fn create_device(
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
vcpu_count: u64,
|
vcpu_count: u64,
|
||||||
) -> Box<dyn GICDevice> {
|
) -> Box<dyn GicDevice> {
|
||||||
Box::new(KvmGICv3 {
|
Box::new(KvmGicV3 {
|
||||||
device,
|
device,
|
||||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||||
properties: [
|
properties: [
|
||||||
KvmGICv3::get_dist_addr(),
|
KvmGicV3::get_dist_addr(),
|
||||||
KvmGICv3::get_dist_size(),
|
KvmGicV3::get_dist_size(),
|
||||||
KvmGICv3::get_redists_addr(vcpu_count),
|
KvmGicV3::get_redists_addr(vcpu_count),
|
||||||
KvmGICv3::get_redists_size(vcpu_count),
|
KvmGicV3::get_redists_size(vcpu_count),
|
||||||
],
|
],
|
||||||
vcpu_count,
|
vcpu_count,
|
||||||
})
|
})
|
||||||
@@ -194,7 +194,7 @@ pub mod kvm {
|
|||||||
|
|
||||||
fn init_device_attributes(
|
fn init_device_attributes(
|
||||||
_vm: &Arc<dyn hypervisor::Vm>,
|
_vm: &Arc<dyn hypervisor::Vm>,
|
||||||
gic_device: &dyn GICDevice,
|
gic_device: &dyn GicDevice,
|
||||||
) -> crate::aarch64::gic::Result<()> {
|
) -> crate::aarch64::gic::Result<()> {
|
||||||
/* Setting up the distributor attribute.
|
/* Setting up the distributor attribute.
|
||||||
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
We are placing the GIC below 1GB so we need to substract the size of the distributor.
|
||||||
@@ -203,7 +203,7 @@ pub mod kvm {
|
|||||||
gic_device.device(),
|
gic_device.device(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST),
|
||||||
&KvmGICv3::get_dist_addr() as *const u64 as u64,
|
&KvmGicV3::get_dist_addr() as *const u64 as u64,
|
||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ pub mod kvm {
|
|||||||
gic_device.device(),
|
gic_device.device(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST),
|
||||||
&KvmGICv3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
&KvmGicV3::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const GIC_V3_SNAPSHOT_ID: &str = "gic-v3";
|
pub const GIC_V3_SNAPSHOT_ID: &str = "gic-v3";
|
||||||
impl Snapshottable for KvmGICv3 {
|
impl Snapshottable for KvmGicV3 {
|
||||||
fn id(&self) -> String {
|
fn id(&self) -> String {
|
||||||
GIC_V3_SNAPSHOT_ID.to_string()
|
GIC_V3_SNAPSHOT_ID.to_string()
|
||||||
}
|
}
|
||||||
@@ -269,7 +269,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pausable for KvmGICv3 {}
|
impl Pausable for KvmGicV3 {}
|
||||||
impl Transportable for KvmGICv3 {}
|
impl Transportable for KvmGicV3 {}
|
||||||
impl Migratable for KvmGICv3 {}
|
impl Migratable for KvmGicV3 {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ pub mod kvm {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{boxed::Box, result};
|
use std::{boxed::Box, result};
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
use crate::aarch64::gic::gicv3::kvm::KvmGICv3;
|
use crate::aarch64::gic::gicv3::kvm::KvmGicV3;
|
||||||
use crate::aarch64::gic::kvm::KvmGICDevice;
|
use crate::aarch64::gic::kvm::KvmGicDevice;
|
||||||
use crate::aarch64::gic::{Error, GICDevice};
|
use crate::aarch64::gic::{Error, GicDevice};
|
||||||
use hypervisor::kvm::kvm_bindings;
|
use hypervisor::kvm::kvm_bindings;
|
||||||
|
|
||||||
pub struct KvmGICv3ITS {
|
pub struct KvmGicV3Its {
|
||||||
/// The hypervisor agnostic device
|
/// The hypervisor agnostic device
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
|
|
||||||
@@ -29,19 +29,19 @@ pub mod kvm {
|
|||||||
vcpu_count: u64,
|
vcpu_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KvmGICv3ITS {
|
impl KvmGicV3Its {
|
||||||
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGICv3::SZ_64K);
|
const KVM_VGIC_V3_ITS_SIZE: u64 = (2 * KvmGicV3::SZ_64K);
|
||||||
|
|
||||||
fn get_msi_size() -> u64 {
|
fn get_msi_size() -> u64 {
|
||||||
KvmGICv3ITS::KVM_VGIC_V3_ITS_SIZE
|
KvmGicV3Its::KVM_VGIC_V3_ITS_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_msi_addr(vcpu_count: u64) -> u64 {
|
fn get_msi_addr(vcpu_count: u64) -> u64 {
|
||||||
KvmGICv3::get_redists_addr(vcpu_count) - KvmGICv3ITS::get_msi_size()
|
KvmGicV3::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GICDevice for KvmGICv3ITS {
|
impl GicDevice for KvmGicV3Its {
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
fn device(&self) -> &Arc<dyn hypervisor::Device> {
|
||||||
&self.device
|
&self.device
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fdt_maint_irq(&self) -> u32 {
|
fn fdt_maint_irq(&self) -> u32 {
|
||||||
KvmGICv3::ARCH_GIC_V3_MAINT_IRQ
|
KvmGicV3::ARCH_GIC_V3_MAINT_IRQ
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msi_properties(&self) -> &[u64] {
|
fn msi_properties(&self) -> &[u64] {
|
||||||
@@ -83,27 +83,27 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KvmGICDevice for KvmGICv3ITS {
|
impl KvmGicDevice for KvmGicV3Its {
|
||||||
fn version() -> u32 {
|
fn version() -> u32 {
|
||||||
KvmGICv3::version()
|
KvmGicV3::version()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_device(
|
fn create_device(
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
vcpu_count: u64,
|
vcpu_count: u64,
|
||||||
) -> Box<dyn GICDevice> {
|
) -> Box<dyn GicDevice> {
|
||||||
Box::new(KvmGICv3ITS {
|
Box::new(KvmGicV3Its {
|
||||||
device,
|
device,
|
||||||
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
gicr_typers: vec![0; vcpu_count.try_into().unwrap()],
|
||||||
gic_properties: [
|
gic_properties: [
|
||||||
KvmGICv3::get_dist_addr(),
|
KvmGicV3::get_dist_addr(),
|
||||||
KvmGICv3::get_dist_size(),
|
KvmGicV3::get_dist_size(),
|
||||||
KvmGICv3::get_redists_addr(vcpu_count),
|
KvmGicV3::get_redists_addr(vcpu_count),
|
||||||
KvmGICv3::get_redists_size(vcpu_count),
|
KvmGicV3::get_redists_size(vcpu_count),
|
||||||
],
|
],
|
||||||
msi_properties: [
|
msi_properties: [
|
||||||
KvmGICv3ITS::get_msi_addr(vcpu_count),
|
KvmGicV3Its::get_msi_addr(vcpu_count),
|
||||||
KvmGICv3ITS::get_msi_size(),
|
KvmGicV3Its::get_msi_size(),
|
||||||
],
|
],
|
||||||
vcpu_count,
|
vcpu_count,
|
||||||
})
|
})
|
||||||
@@ -111,9 +111,9 @@ pub mod kvm {
|
|||||||
|
|
||||||
fn init_device_attributes(
|
fn init_device_attributes(
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
gic_device: &dyn GICDevice,
|
gic_device: &dyn GicDevice,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
KvmGICv3::init_device_attributes(vm, gic_device)?;
|
KvmGicV3::init_device_attributes(vm, gic_device)?;
|
||||||
|
|
||||||
let mut its_device = kvm_bindings::kvm_create_device {
|
let mut its_device = kvm_bindings::kvm_create_device {
|
||||||
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS,
|
||||||
@@ -123,13 +123,13 @@ pub mod kvm {
|
|||||||
|
|
||||||
let its_fd = vm
|
let its_fd = vm
|
||||||
.create_device(&mut its_device)
|
.create_device(&mut its_device)
|
||||||
.map_err(Error::CreateGIC)?;
|
.map_err(Error::CreateGic)?;
|
||||||
|
|
||||||
Self::set_device_attribute(
|
Self::set_device_attribute(
|
||||||
&its_fd,
|
&its_fd,
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||||
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE),
|
||||||
&KvmGICv3ITS::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
&KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64,
|
||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use std::sync::Arc;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||||
CreateGIC(hypervisor::HypervisorVmError),
|
CreateGic(hypervisor::HypervisorVmError),
|
||||||
/// Error while setting device attributes for the GIC.
|
/// Error while setting device attributes for the GIC.
|
||||||
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
|
SetDeviceAttribute(hypervisor::HypervisorDeviceError),
|
||||||
/// Error while getting device attributes for the GIC.
|
/// Error while getting device attributes for the GIC.
|
||||||
@@ -26,7 +26,7 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
pub trait GICDevice: Send {
|
pub trait GicDevice: Send {
|
||||||
/// Returns the hypervisor agnostic Device of the GIC device
|
/// Returns the hypervisor agnostic Device of the GIC device
|
||||||
fn device(&self) -> &Arc<dyn hypervisor::Device>;
|
fn device(&self) -> &Arc<dyn hypervisor::Device>;
|
||||||
|
|
||||||
@@ -65,16 +65,16 @@ pub trait GICDevice: Send {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod kvm {
|
pub mod kvm {
|
||||||
use super::GICDevice;
|
use super::GicDevice;
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::aarch64::gic::gicv3_its::kvm::KvmGICv3ITS;
|
use crate::aarch64::gic::gicv3_its::kvm::KvmGicV3Its;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use hypervisor::kvm::kvm_bindings;
|
use hypervisor::kvm::kvm_bindings;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Trait for GIC devices.
|
/// Trait for GIC devices.
|
||||||
pub trait KvmGICDevice: Send + Sync + GICDevice {
|
pub trait KvmGicDevice: Send + Sync + GicDevice {
|
||||||
/// Returns the GIC version of the device
|
/// Returns the GIC version of the device
|
||||||
fn version() -> u32;
|
fn version() -> u32;
|
||||||
|
|
||||||
@@ -82,12 +82,12 @@ pub mod kvm {
|
|||||||
fn create_device(
|
fn create_device(
|
||||||
device: Arc<dyn hypervisor::Device>,
|
device: Arc<dyn hypervisor::Device>,
|
||||||
vcpu_count: u64,
|
vcpu_count: u64,
|
||||||
) -> Box<dyn GICDevice>;
|
) -> Box<dyn GicDevice>;
|
||||||
|
|
||||||
/// Setup the device-specific attributes
|
/// Setup the device-specific attributes
|
||||||
fn init_device_attributes(
|
fn init_device_attributes(
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
gic_device: &dyn GICDevice,
|
gic_device: &dyn GicDevice,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
/// Initialize a GIC device
|
/// Initialize a GIC device
|
||||||
@@ -99,7 +99,7 @@ pub mod kvm {
|
|||||||
};
|
};
|
||||||
|
|
||||||
vm.create_device(&mut gic_device)
|
vm.create_device(&mut gic_device)
|
||||||
.map_err(super::Error::CreateGIC)
|
.map_err(super::Error::CreateGic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a GIC device attribute
|
/// Set a GIC device attribute
|
||||||
@@ -145,7 +145,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finalize the setup of a GIC device
|
/// Finalize the setup of a GIC device
|
||||||
fn finalize_device(gic_device: &dyn GICDevice) -> Result<()> {
|
fn finalize_device(gic_device: &dyn GicDevice) -> Result<()> {
|
||||||
/* We need to tell the kernel how many irqs to support with this vgic.
|
/* We need to tell the kernel how many irqs to support with this vgic.
|
||||||
* See the `layout` module for details.
|
* See the `layout` module for details.
|
||||||
*/
|
*/
|
||||||
@@ -175,7 +175,7 @@ pub mod kvm {
|
|||||||
|
|
||||||
/// Method to initialize the GIC device
|
/// Method to initialize the GIC device
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
|
fn new(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
||||||
let vgic_fd = Self::init_device(vm)?;
|
let vgic_fd = Self::init_device(vm)?;
|
||||||
|
|
||||||
let device = Self::create_device(vgic_fd, vcpu_count);
|
let device = Self::create_device(vgic_fd, vcpu_count);
|
||||||
@@ -190,9 +190,9 @@ pub mod kvm {
|
|||||||
|
|
||||||
/// Create a GICv3-ITS device.
|
/// Create a GICv3-ITS device.
|
||||||
///
|
///
|
||||||
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GICDevice>> {
|
pub fn create_gic(vm: &Arc<dyn hypervisor::Vm>, vcpu_count: u64) -> Result<Box<dyn GicDevice>> {
|
||||||
debug!("creating a GICv3-ITS");
|
debug!("creating a GICv3-ITS");
|
||||||
KvmGICv3ITS::new(vm, vcpu_count)
|
KvmGicV3Its::new(vm, vcpu_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function that saves RDIST pending tables into guest RAM.
|
/// Function that saves RDIST pending tables into guest RAM.
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ pub const MAPPED_IO_START: u64 = 0x0900_0000;
|
|||||||
/// Space 0x0900_0000 ~ 0x1000_0000 is reserved for legacy devices.
|
/// Space 0x0900_0000 ~ 0x1000_0000 is reserved for legacy devices.
|
||||||
pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000;
|
pub const LEGACY_SERIAL_MAPPED_IO_START: u64 = 0x0900_0000;
|
||||||
pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
pub const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
||||||
|
pub const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
|
||||||
|
|
||||||
/// Legacy space will be allocated at once whiling setting up legacy devices.
|
/// Legacy space will be allocated at once whiling setting up legacy devices.
|
||||||
pub const LEGACY_DEVICES_MAPPED_IO_SIZE: u64 = 0x0700_0000;
|
pub const LEGACY_DEVICES_MAPPED_IO_SIZE: u64 = 0x0700_0000;
|
||||||
@@ -78,9 +79,9 @@ pub const FDT_MAX_SIZE: usize = 0x20_0000;
|
|||||||
// * bigger than 32
|
// * bigger than 32
|
||||||
// * less than 1023 and
|
// * less than 1023 and
|
||||||
// * a multiple of 32.
|
// * a multiple of 32.
|
||||||
// We are setting up our interrupt controller to support a maximum of 128 interrupts.
|
// We are setting up our interrupt controller to support a maximum of 256 interrupts.
|
||||||
/// First usable interrupt on aarch64.
|
/// First usable interrupt on aarch64.
|
||||||
pub const IRQ_BASE: u32 = 32;
|
pub const IRQ_BASE: u32 = 0;
|
||||||
|
|
||||||
/// Last usable interrupt on aarch64.
|
/// Last usable interrupt on aarch64.
|
||||||
pub const IRQ_MAX: u32 = 159;
|
pub const IRQ_MAX: u32 = 255;
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ pub mod layout;
|
|||||||
/// Logic for configuring aarch64 registers.
|
/// Logic for configuring aarch64 registers.
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
|
|
||||||
pub use self::fdt::DeviceInfoForFDT;
|
pub use self::fdt::DeviceInfoForFdt;
|
||||||
use crate::DeviceType;
|
use crate::DeviceType;
|
||||||
use crate::RegionType;
|
use crate::RegionType;
|
||||||
use aarch64::gic::GICDevice;
|
use aarch64::gic::GicDevice;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@@ -28,19 +28,19 @@ use vm_memory::{
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to create a FDT.
|
/// Failed to create a FDT.
|
||||||
SetupFDT(fdt::Error),
|
SetupFdt(fdt::Error),
|
||||||
|
|
||||||
/// Failed to create a GIC.
|
/// Failed to create a GIC.
|
||||||
SetupGIC(gic::Error),
|
SetupGic(gic::Error),
|
||||||
|
|
||||||
/// Failed to compute the initramfs address.
|
/// Failed to compute the initramfs address.
|
||||||
InitramfsAddress,
|
InitramfsAddress,
|
||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
REGSConfiguration(regs::Error),
|
RegsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the MPIDR register
|
/// Error configuring the MPIDR register
|
||||||
VcpuRegMPIDR(hypervisor::HypervisorCpuError),
|
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for super::Error {
|
impl From<Error> for super::Error {
|
||||||
@@ -71,43 +71,39 @@ pub fn configure_vcpu(
|
|||||||
kernel_entry_point.entry_addr.raw_value(),
|
kernel_entry_point.entry_addr.raw_value(),
|
||||||
&vm_memory.memory(),
|
&vm_memory.memory(),
|
||||||
)
|
)
|
||||||
.map_err(Error::REGSConfiguration)?;
|
.map_err(Error::RegsConfiguration)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMPIDR)?;
|
let mpidr = fd.read_mpidr().map_err(Error::VcpuRegMpidr)?;
|
||||||
Ok(mpidr)
|
Ok(mpidr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||||
let mut regions = Vec::new();
|
vec![
|
||||||
// 0 ~ 256 MiB: Reserved
|
// 0 ~ 256 MiB: Reserved
|
||||||
regions.push((
|
(
|
||||||
GuestAddress(0),
|
GuestAddress(0),
|
||||||
layout::MEM_32BIT_DEVICES_START.0 as usize,
|
layout::MEM_32BIT_DEVICES_START.0 as usize,
|
||||||
RegionType::Reserved,
|
RegionType::Reserved,
|
||||||
));
|
),
|
||||||
|
// 256 MiB ~ 1 G: MMIO space
|
||||||
// 256 MiB ~ 1 G: MMIO space
|
(
|
||||||
regions.push((
|
layout::MEM_32BIT_DEVICES_START,
|
||||||
layout::MEM_32BIT_DEVICES_START,
|
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||||
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
RegionType::SubRegion,
|
||||||
RegionType::SubRegion,
|
),
|
||||||
));
|
// 1G ~ 2G: reserved. The leading 256M for PCIe MMCONFIG space
|
||||||
|
(
|
||||||
// 1G ~ 2G: reserved. The leading 256M for PCIe MMCONFIG space
|
layout::PCI_MMCONFIG_START,
|
||||||
regions.push((
|
(layout::RAM_64BIT_START - layout::PCI_MMCONFIG_START.0) as usize,
|
||||||
layout::PCI_MMCONFIG_START,
|
RegionType::Reserved,
|
||||||
(layout::RAM_64BIT_START - layout::PCI_MMCONFIG_START.0) as usize,
|
),
|
||||||
RegionType::Reserved,
|
(
|
||||||
));
|
GuestAddress(layout::RAM_64BIT_START),
|
||||||
|
size as usize,
|
||||||
regions.push((
|
RegionType::Ram,
|
||||||
GuestAddress(layout::RAM_64BIT_START),
|
),
|
||||||
size as usize,
|
]
|
||||||
RegionType::Ram,
|
|
||||||
));
|
|
||||||
|
|
||||||
regions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||||
@@ -117,7 +113,7 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
|||||||
/// * `guest_mem` - The memory to be used by the guest.
|
/// * `guest_mem` - The memory to be used by the guest.
|
||||||
/// * `num_cpus` - Number of virtual CPUs the guest will have.
|
/// * `num_cpus` - Number of virtual CPUs the guest will have.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::BuildHasher>(
|
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
guest_mem: &GuestMemoryMmap,
|
guest_mem: &GuestMemoryMmap,
|
||||||
cmdline_cstring: &CStr,
|
cmdline_cstring: &CStr,
|
||||||
@@ -126,8 +122,8 @@ pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::Bui
|
|||||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||||
initrd: &Option<super::InitramfsConfig>,
|
initrd: &Option<super::InitramfsConfig>,
|
||||||
pci_space_address: &(u64, u64),
|
pci_space_address: &(u64, u64),
|
||||||
) -> super::Result<Box<dyn GICDevice>> {
|
) -> super::Result<Box<dyn GicDevice>> {
|
||||||
let gic_device = gic::kvm::create_gic(vm, vcpu_count).map_err(Error::SetupGIC)?;
|
let gic_device = gic::kvm::create_gic(vm, vcpu_count).map_err(Error::SetupGic)?;
|
||||||
|
|
||||||
fdt::create_fdt(
|
fdt::create_fdt(
|
||||||
guest_mem,
|
guest_mem,
|
||||||
@@ -138,7 +134,7 @@ pub fn configure_system<T: DeviceInfoForFDT + Clone + Debug, S: ::std::hash::Bui
|
|||||||
initrd,
|
initrd,
|
||||||
pci_space_address,
|
pci_space_address,
|
||||||
)
|
)
|
||||||
.map_err(Error::SetupFDT)?;
|
.map_err(Error::SetupFdt)?;
|
||||||
|
|
||||||
Ok(gic_device)
|
Ok(gic_device)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,7 @@
|
|||||||
|
|
||||||
//! Implements platform specific functionality.
|
//! Implements platform specific functionality.
|
||||||
//! Supported platforms: x86_64, aarch64.
|
//! Supported platforms: x86_64, aarch64.
|
||||||
#![allow(
|
#![allow(clippy::transmute_ptr_to_ptr, clippy::redundant_static_lifetimes)]
|
||||||
clippy::unreadable_literal,
|
|
||||||
clippy::redundant_static_lifetimes,
|
|
||||||
clippy::cast_lossless,
|
|
||||||
clippy::transmute_ptr_to_ptr,
|
|
||||||
clippy::cast_ptr_alignment
|
|
||||||
)]
|
|
||||||
|
|
||||||
extern crate anyhow;
|
extern crate anyhow;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
@@ -31,6 +25,7 @@ extern crate vm_migration;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
extern crate thiserror;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::result;
|
use std::result;
|
||||||
@@ -61,7 +56,7 @@ pub enum Error {
|
|||||||
/// Error writing module entry to guest memory.
|
/// Error writing module entry to guest memory.
|
||||||
ModlistSetup(vm_memory::GuestMemoryError),
|
ModlistSetup(vm_memory::GuestMemoryError),
|
||||||
/// RSDP Beyond Guest Memory
|
/// RSDP Beyond Guest Memory
|
||||||
RSDPPastRamEnd,
|
RsdpPastRamEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for returning public functions outcome.
|
/// Type for returning public functions outcome.
|
||||||
@@ -92,7 +87,7 @@ pub mod aarch64;
|
|||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub use aarch64::{
|
pub use aarch64::{
|
||||||
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFDT,
|
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt,
|
||||||
get_host_cpu_phys_bits, get_kernel_start, initramfs_load_addr, layout,
|
get_host_cpu_phys_bits, get_kernel_start, initramfs_load_addr, layout,
|
||||||
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, EntryPoint,
|
layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE, layout::IRQ_MAX, EntryPoint,
|
||||||
};
|
};
|
||||||
@@ -133,7 +128,10 @@ pub enum DeviceType {
|
|||||||
Serial,
|
Serial,
|
||||||
/// Device Type: RTC.
|
/// Device Type: RTC.
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
RTC,
|
Rtc,
|
||||||
|
/// Device Type: GPIO.
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
Gpio,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default (smallest) memory page size for the supported architectures.
|
/// Default (smallest) memory page size for the supported architectures.
|
||||||
@@ -148,13 +146,13 @@ impl fmt::Display for DeviceType {
|
|||||||
/// Structure to describe MMIO device information
|
/// Structure to describe MMIO device information
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub struct MMIODeviceInfo {
|
pub struct MmioDeviceInfo {
|
||||||
pub addr: u64,
|
pub addr: u64,
|
||||||
pub irq: u32,
|
pub irq: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
impl DeviceInfoForFDT for MMIODeviceInfo {
|
impl DeviceInfoForFdt for MmioDeviceInfo {
|
||||||
fn addr(&self) -> u64 {
|
fn addr(&self) -> u64 {
|
||||||
self.addr
|
self.addr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use hypervisor::x86_64::LapicState;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
|
|
||||||
use hypervisor::x86_64::LapicState;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
GetLapic(anyhow::Error),
|
GetLapic(anyhow::Error),
|
||||||
@@ -82,12 +80,7 @@ pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(feature = "kvm")]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
extern crate rand;
|
|
||||||
use self::rand::Rng;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const KVM_APIC_REG_SIZE: usize = 0x400;
|
const KVM_APIC_REG_SIZE: usize = 0x400;
|
||||||
@@ -111,8 +104,15 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_apic_delivery_mode() {
|
fn test_apic_delivery_mode() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut v: Vec<u32> = Vec::new();
|
||||||
let mut v: Vec<u32> = (0..20).map(|_| rng.gen::<u32>()).collect();
|
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()
|
v.iter_mut()
|
||||||
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));
|
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ use vm_memory::{
|
|||||||
};
|
};
|
||||||
mod smbios;
|
mod smbios;
|
||||||
use std::arch::x86_64;
|
use std::arch::x86_64;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
pub mod tdx;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum BootProtocol {
|
pub enum BootProtocol {
|
||||||
@@ -145,16 +147,16 @@ pub enum Error {
|
|||||||
MpTableSetup(mptable::Error),
|
MpTableSetup(mptable::Error),
|
||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
REGSConfiguration(regs::Error),
|
RegsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the special registers
|
/// Error configuring the special registers
|
||||||
SREGSConfiguration(regs::Error),
|
SregsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the floating point related registers
|
/// Error configuring the floating point related registers
|
||||||
FPUConfiguration(regs::Error),
|
FpuConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the MSR registers
|
/// Error configuring the MSR registers
|
||||||
MSRSConfiguration(regs::Error),
|
MsrsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Failed to set supported CPUs.
|
/// Failed to set supported CPUs.
|
||||||
SetSupportedCpusFailed(anyhow::Error),
|
SetSupportedCpusFailed(anyhow::Error),
|
||||||
@@ -181,7 +183,7 @@ impl From<Error> for super::Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum CpuidReg {
|
pub enum CpuidReg {
|
||||||
EAX,
|
EAX,
|
||||||
@@ -346,7 +348,7 @@ pub fn configure_vcpu(
|
|||||||
fd.enable_hyperv_synic().unwrap();
|
fd.enable_hyperv_synic().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
regs::setup_msrs(fd).map_err(Error::MSRSConfiguration)?;
|
regs::setup_msrs(fd).map_err(Error::MsrsConfiguration)?;
|
||||||
if let Some(kernel_entry_point) = kernel_entry_point {
|
if let Some(kernel_entry_point) = kernel_entry_point {
|
||||||
// Safe to unwrap because this method is called after the VM is configured
|
// Safe to unwrap because this method is called after the VM is configured
|
||||||
regs::setup_regs(
|
regs::setup_regs(
|
||||||
@@ -356,10 +358,10 @@ pub fn configure_vcpu(
|
|||||||
layout::ZERO_PAGE_START.raw_value(),
|
layout::ZERO_PAGE_START.raw_value(),
|
||||||
kernel_entry_point.protocol,
|
kernel_entry_point.protocol,
|
||||||
)
|
)
|
||||||
.map_err(Error::REGSConfiguration)?;
|
.map_err(Error::RegsConfiguration)?;
|
||||||
regs::setup_fpu(fd).map_err(Error::FPUConfiguration)?;
|
regs::setup_fpu(fd).map_err(Error::FpuConfiguration)?;
|
||||||
regs::setup_sregs(&vm_memory.memory(), fd, kernel_entry_point.protocol)
|
regs::setup_sregs(&vm_memory.memory(), fd, kernel_entry_point.protocol)
|
||||||
.map_err(Error::SREGSConfiguration)?;
|
.map_err(Error::SregsConfiguration)?;
|
||||||
}
|
}
|
||||||
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
interrupts::set_lint(fd).map_err(|e| Error::LocalIntConfiguration(e.into()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -442,7 +444,7 @@ pub fn configure_system(
|
|||||||
// Check that the RAM is not smaller than the RSDP start address
|
// Check that the RAM is not smaller than the RSDP start address
|
||||||
if let Some(rsdp_addr) = rsdp_addr {
|
if let Some(rsdp_addr) = rsdp_addr {
|
||||||
if rsdp_addr.0 > guest_mem.last_addr().0 {
|
if rsdp_addr.0 > guest_mem.last_addr().0 {
|
||||||
return Err(super::Error::RSDPPastRamEnd);
|
return Err(super::Error::RsdpPastRamEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,6 +459,7 @@ pub fn configure_system(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
BootProtocol::LinuxBoot => {
|
BootProtocol::LinuxBoot => {
|
||||||
|
error!("Using deprecated LinuxBoot protocol: Please configure your kernel with CONFIG_PVH=y and supply the `vmlinux` file to `--kernel`");
|
||||||
configure_64bit_boot(
|
configure_64bit_boot(
|
||||||
guest_mem,
|
guest_mem,
|
||||||
cmdline_addr,
|
cmdline_addr,
|
||||||
|
|||||||
@@ -25,25 +25,25 @@ pub enum Error {
|
|||||||
/// Failed to set base registers for this CPU.
|
/// Failed to set base registers for this CPU.
|
||||||
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Failed to configure the FPU.
|
/// Failed to configure the FPU.
|
||||||
SetFPURegisters(hypervisor::HypervisorCpuError),
|
SetFpuRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Setting up MSRs failed.
|
/// Setting up MSRs failed.
|
||||||
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Failed to set SREGs for this CPU.
|
/// Failed to set SREGs for this CPU.
|
||||||
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Checking the GDT address failed.
|
/// Checking the GDT address failed.
|
||||||
CheckGDTAddr,
|
CheckGdtAddr,
|
||||||
/// Writing the GDT to RAM failed.
|
/// Writing the GDT to RAM failed.
|
||||||
WriteGDT(GuestMemoryError),
|
WriteGdt(GuestMemoryError),
|
||||||
/// Writing the IDT to RAM failed.
|
/// Writing the IDT to RAM failed.
|
||||||
WriteIDT(GuestMemoryError),
|
WriteIdt(GuestMemoryError),
|
||||||
/// Writing PDPTE to RAM failed.
|
/// Writing PDPTE to RAM failed.
|
||||||
WritePDPTEAddress(GuestMemoryError),
|
WritePdpteAddress(GuestMemoryError),
|
||||||
/// Writing PDE to RAM failed.
|
/// Writing PDE to RAM failed.
|
||||||
WritePDEAddress(GuestMemoryError),
|
WritePdeAddress(GuestMemoryError),
|
||||||
/// Writing PML4 to RAM failed.
|
/// Writing PML4 to RAM failed.
|
||||||
WritePML4Address(GuestMemoryError),
|
WritePml4Address(GuestMemoryError),
|
||||||
/// Writing PML5 to RAM failed.
|
/// Writing PML5 to RAM failed.
|
||||||
WritePML5Address(GuestMemoryError),
|
WritePml5Address(GuestMemoryError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@@ -60,7 +60,7 @@ pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
vcpu.set_fpu(&fpu).map_err(Error::SetFPURegisters)
|
vcpu.set_fpu(&fpu).map_err(Error::SetFpuRegisters)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure Model Specific Registers (MSRs) for a given CPU.
|
/// Configure Model Specific Registers (MSRs) for a given CPU.
|
||||||
@@ -140,8 +140,8 @@ fn write_gdt_table(table: &[u64], guest_mem: &GuestMemoryMmap) -> Result<()> {
|
|||||||
for (index, entry) in table.iter().enumerate() {
|
for (index, entry) in table.iter().enumerate() {
|
||||||
let addr = guest_mem
|
let addr = guest_mem
|
||||||
.checked_offset(boot_gdt_addr, index * mem::size_of::<u64>())
|
.checked_offset(boot_gdt_addr, index * mem::size_of::<u64>())
|
||||||
.ok_or(Error::CheckGDTAddr)?;
|
.ok_or(Error::CheckGdtAddr)?;
|
||||||
guest_mem.write_obj(*entry, addr).map_err(Error::WriteGDT)?;
|
guest_mem.write_obj(*entry, addr).map_err(Error::WriteGdt)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
|||||||
let boot_idt_addr = BOOT_IDT_START;
|
let boot_idt_addr = BOOT_IDT_START;
|
||||||
guest_mem
|
guest_mem
|
||||||
.write_obj(val, boot_idt_addr)
|
.write_obj(val, boot_idt_addr)
|
||||||
.map_err(Error::WriteIDT)
|
.map_err(Error::WriteIdt)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_segments_and_sregs(
|
pub fn configure_segments_and_sregs(
|
||||||
@@ -220,7 +220,7 @@ pub fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) ->
|
|||||||
if unsafe { std::arch::x86_64::__cpuid(7).ecx } & (1 << 16) != 0 {
|
if unsafe { std::arch::x86_64::__cpuid(7).ecx } & (1 << 16) != 0 {
|
||||||
// Entry covering VA [0..256TB)
|
// Entry covering VA [0..256TB)
|
||||||
mem.write_obj(PML4_START.raw_value() | 0x03, PML5_START)
|
mem.write_obj(PML4_START.raw_value() | 0x03, PML5_START)
|
||||||
.map_err(Error::WritePML5Address)?;
|
.map_err(Error::WritePml5Address)?;
|
||||||
|
|
||||||
sregs.cr3 = PML5_START.raw_value();
|
sregs.cr3 = PML5_START.raw_value();
|
||||||
sregs.cr4 |= CR4_LA57;
|
sregs.cr4 |= CR4_LA57;
|
||||||
@@ -230,17 +230,17 @@ pub fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) ->
|
|||||||
|
|
||||||
// Entry covering VA [0..512GB)
|
// Entry covering VA [0..512GB)
|
||||||
mem.write_obj(PDPTE_START.raw_value() | 0x03, PML4_START)
|
mem.write_obj(PDPTE_START.raw_value() | 0x03, PML4_START)
|
||||||
.map_err(Error::WritePML4Address)?;
|
.map_err(Error::WritePml4Address)?;
|
||||||
|
|
||||||
// Entry covering VA [0..1GB)
|
// Entry covering VA [0..1GB)
|
||||||
mem.write_obj(PDE_START.raw_value() | 0x03, PDPTE_START)
|
mem.write_obj(PDE_START.raw_value() | 0x03, PDPTE_START)
|
||||||
.map_err(Error::WritePDPTEAddress)?;
|
.map_err(Error::WritePdpteAddress)?;
|
||||||
|
|
||||||
// 512 2MB entries together covering VA [0..1GB). Note we are assuming
|
// 512 2MB entries together covering VA [0..1GB). Note we are assuming
|
||||||
// CPU supports 2MB pages (/proc/cpuinfo has 'pse'). All modern CPUs do.
|
// CPU supports 2MB pages (/proc/cpuinfo has 'pse'). All modern CPUs do.
|
||||||
for i in 0..512 {
|
for i in 0..512 {
|
||||||
mem.write_obj((i << 21) + 0x83u64, PDE_START.unchecked_add(i * 8))
|
mem.write_obj((i << 21) + 0x83u64, PDE_START.unchecked_add(i * 8))
|
||||||
.map_err(Error::WritePDEAddress)?;
|
.map_err(Error::WritePdeAddress)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
sregs.cr4 |= CR4_PAE;
|
sregs.cr4 |= CR4_PAE;
|
||||||
|
|||||||
318
arch/src/x86_64/tdx/mod.rs
Normal file
318
arch/src/x86_64/tdx/mod.rs
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
use thiserror::Error;
|
||||||
|
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap};
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum TdvfError {
|
||||||
|
#[error("Failed read TDVF descriptor: {0}")]
|
||||||
|
ReadDescriptor(#[source] std::io::Error),
|
||||||
|
#[error("Failed read TDVF descriptor offset: {0}")]
|
||||||
|
ReadDescriptorOffset(#[source] std::io::Error),
|
||||||
|
#[error("Invalid descriptor signature")]
|
||||||
|
InvalidDescriptorSignature,
|
||||||
|
#[error("Invalid descriptor size")]
|
||||||
|
InvalidDescriptorSize,
|
||||||
|
#[error("Invalid descriptor version")]
|
||||||
|
InvalidDescriptorVersion,
|
||||||
|
#[error("Failed to write HOB details to guest memory: {0}")]
|
||||||
|
GuestMemoryWriteHob(#[source] GuestMemoryError),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TDVF_DESCRIPTOR
|
||||||
|
#[repr(packed)]
|
||||||
|
pub struct TdvfDescriptor {
|
||||||
|
signature: [u8; 4],
|
||||||
|
length: u32,
|
||||||
|
version: u32,
|
||||||
|
num_sections: u32, // NumberOfSectionEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// TDVF_SECTION
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
|
pub struct TdvfSection {
|
||||||
|
pub data_offset: u32,
|
||||||
|
pub data_size: u32, // RawDataSize
|
||||||
|
pub address: u64, // MemoryAddress
|
||||||
|
pub size: u64, // MemoryDataSize
|
||||||
|
pub r#type: TdvfSectionType,
|
||||||
|
pub attributes: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TdvfSectionType {
|
||||||
|
Bfv,
|
||||||
|
Cfv,
|
||||||
|
TdHob,
|
||||||
|
TempMem,
|
||||||
|
Reserved = 0xffffffff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TdvfSectionType {
|
||||||
|
fn default() -> Self {
|
||||||
|
TdvfSectionType::Reserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_tdvf_sections(file: &mut File) -> Result<Vec<TdvfSection>, TdvfError> {
|
||||||
|
// The 32-bit offset to the TDVF metadata is located 32 bytes from
|
||||||
|
// the end of the file.
|
||||||
|
// See "TDVF Metadata Pointer" in "TDX Virtual Firmware Design Guide
|
||||||
|
file.seek(SeekFrom::End(-0x20))
|
||||||
|
.map_err(TdvfError::ReadDescriptorOffset)?;
|
||||||
|
|
||||||
|
let mut descriptor_offset: [u8; 4] = [0; 4];
|
||||||
|
file.read_exact(&mut descriptor_offset)
|
||||||
|
.map_err(TdvfError::ReadDescriptorOffset)?;
|
||||||
|
let descriptor_offset = u32::from_le_bytes(descriptor_offset) as u64;
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(descriptor_offset))
|
||||||
|
.map_err(TdvfError::ReadDescriptor)?;
|
||||||
|
|
||||||
|
let mut descriptor: TdvfDescriptor = unsafe { std::mem::zeroed() };
|
||||||
|
// Safe as we read exactly the size of the descriptor header
|
||||||
|
file.read_exact(unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(
|
||||||
|
&mut descriptor as *mut _ as *mut u8,
|
||||||
|
std::mem::size_of::<TdvfDescriptor>(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(TdvfError::ReadDescriptor)?;
|
||||||
|
|
||||||
|
if &descriptor.signature != b"TDVF" {
|
||||||
|
return Err(TdvfError::InvalidDescriptorSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if descriptor.length as usize
|
||||||
|
!= std::mem::size_of::<TdvfDescriptor>()
|
||||||
|
+ std::mem::size_of::<TdvfSection>() * descriptor.num_sections as usize
|
||||||
|
{
|
||||||
|
return Err(TdvfError::InvalidDescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if descriptor.version != 1 {
|
||||||
|
return Err(TdvfError::InvalidDescriptorVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sections = Vec::new();
|
||||||
|
sections.resize_with(descriptor.num_sections as usize, TdvfSection::default);
|
||||||
|
|
||||||
|
// Safe as we read exactly the advertised sections
|
||||||
|
file.read_exact(unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(
|
||||||
|
sections.as_mut_ptr() as *mut u8,
|
||||||
|
descriptor.num_sections as usize * std::mem::size_of::<TdvfSection>(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(TdvfError::ReadDescriptor)?;
|
||||||
|
|
||||||
|
Ok(sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum HobType {
|
||||||
|
Handoff = 0x1,
|
||||||
|
ResourceDescriptor = 0x3,
|
||||||
|
Unused = 0xfffe,
|
||||||
|
EndOfHobList = 0xffff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for HobType {
|
||||||
|
fn default() -> Self {
|
||||||
|
HobType::Unused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
struct HobHeader {
|
||||||
|
r#type: HobType,
|
||||||
|
length: u16,
|
||||||
|
reserved: u32,
|
||||||
|
}
|
||||||
|
unsafe impl ByteValued for HobHeader {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
struct HobHandoffInfoTable {
|
||||||
|
header: HobHeader,
|
||||||
|
version: u32,
|
||||||
|
efi_memory_top: u64,
|
||||||
|
efi_memory_bottom: u64,
|
||||||
|
efi_free_memory_top: u64,
|
||||||
|
efi_free_memory_bottom: u64,
|
||||||
|
efi_end_of_hob_list: u64,
|
||||||
|
}
|
||||||
|
unsafe impl ByteValued for HobHandoffInfoTable {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
struct EfiGuid {
|
||||||
|
data1: u32,
|
||||||
|
data2: u16,
|
||||||
|
data3: u16,
|
||||||
|
data4: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
struct HobResourceDescriptor {
|
||||||
|
header: HobHeader,
|
||||||
|
owner: EfiGuid,
|
||||||
|
resource_type: u32,
|
||||||
|
resource_attribute: u32,
|
||||||
|
physical_start: u64,
|
||||||
|
resource_length: u64,
|
||||||
|
}
|
||||||
|
unsafe impl ByteValued for HobResourceDescriptor {}
|
||||||
|
|
||||||
|
pub struct TdHob {
|
||||||
|
start_offset: u64,
|
||||||
|
current_offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn align_hob(v: u64) -> u64 {
|
||||||
|
(v + 7) / 8 * 8
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TdHob {
|
||||||
|
fn update_offset<T>(&mut self) {
|
||||||
|
self.current_offset = align_hob(self.current_offset + std::mem::size_of::<T>() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(offset: u64) -> TdHob {
|
||||||
|
// Leave a gap to place the HandoffTable at the start as it can only be filled in later
|
||||||
|
let mut hob = TdHob {
|
||||||
|
start_offset: offset,
|
||||||
|
current_offset: offset,
|
||||||
|
};
|
||||||
|
hob.update_offset::<HobHandoffInfoTable>();
|
||||||
|
hob
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self, mem: &GuestMemoryMmap) -> Result<(), TdvfError> {
|
||||||
|
// Write end
|
||||||
|
let end = HobHeader {
|
||||||
|
r#type: HobType::EndOfHobList,
|
||||||
|
length: std::mem::size_of::<HobHeader>() as u16,
|
||||||
|
reserved: 0,
|
||||||
|
};
|
||||||
|
info!("Writing HOB end {:x} {:x?}", self.current_offset, end);
|
||||||
|
mem.write_obj(end, GuestAddress(self.current_offset))
|
||||||
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
||||||
|
self.update_offset::<HobHeader>();
|
||||||
|
|
||||||
|
// Write handoff, delayed as it needs end of HOB list
|
||||||
|
let efi_end_of_hob_list = self.current_offset;
|
||||||
|
let handoff = HobHandoffInfoTable {
|
||||||
|
header: HobHeader {
|
||||||
|
r#type: HobType::Handoff,
|
||||||
|
length: std::mem::size_of::<HobHandoffInfoTable>() as u16,
|
||||||
|
reserved: 0,
|
||||||
|
},
|
||||||
|
version: 0x9,
|
||||||
|
efi_memory_top: 0,
|
||||||
|
efi_memory_bottom: 0,
|
||||||
|
efi_free_memory_top: 0,
|
||||||
|
efi_free_memory_bottom: 0,
|
||||||
|
efi_end_of_hob_list,
|
||||||
|
};
|
||||||
|
info!("Writing HOB start {:x} {:x?}", self.start_offset, handoff);
|
||||||
|
mem.write_obj(handoff, GuestAddress(self.start_offset))
|
||||||
|
.map_err(TdvfError::GuestMemoryWriteHob)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_resource(
|
||||||
|
&mut self,
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
physical_start: u64,
|
||||||
|
resource_length: u64,
|
||||||
|
resource_type: u32,
|
||||||
|
resource_attribute: u32,
|
||||||
|
) -> Result<(), TdvfError> {
|
||||||
|
let resource_descriptor = HobResourceDescriptor {
|
||||||
|
header: HobHeader {
|
||||||
|
r#type: HobType::ResourceDescriptor,
|
||||||
|
length: std::mem::size_of::<HobResourceDescriptor>() as u16,
|
||||||
|
reserved: 0,
|
||||||
|
},
|
||||||
|
owner: EfiGuid::default(),
|
||||||
|
resource_type,
|
||||||
|
resource_attribute,
|
||||||
|
physical_start,
|
||||||
|
resource_length,
|
||||||
|
};
|
||||||
|
info!(
|
||||||
|
"Writing HOB resource {:x} {:x?}",
|
||||||
|
self.current_offset, resource_descriptor
|
||||||
|
);
|
||||||
|
mem.write_obj(resource_descriptor, GuestAddress(self.current_offset))
|
||||||
|
.map_err(TdvfError::GuestMemoryWriteHob)?;
|
||||||
|
self.update_offset::<HobResourceDescriptor>();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_memory_resource(
|
||||||
|
&mut self,
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
physical_start: u64,
|
||||||
|
resource_length: u64,
|
||||||
|
ram: bool,
|
||||||
|
) -> Result<(), TdvfError> {
|
||||||
|
self.add_resource(
|
||||||
|
mem,
|
||||||
|
physical_start,
|
||||||
|
resource_length,
|
||||||
|
if ram {
|
||||||
|
0 /* EFI_RESOURCE_SYSTEM_MEMORY */
|
||||||
|
} else {
|
||||||
|
0x5 /*EFI_RESOURCE_MEMORY_RESERVED */
|
||||||
|
},
|
||||||
|
/* TODO:
|
||||||
|
* QEMU currently fills it in like this:
|
||||||
|
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED
|
||||||
|
* which differs from the spec (due to TDVF implementation issue?)
|
||||||
|
*/
|
||||||
|
0x7,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_mmio_resource(
|
||||||
|
&mut self,
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
physical_start: u64,
|
||||||
|
resource_length: u64,
|
||||||
|
) -> Result<(), TdvfError> {
|
||||||
|
self.add_resource(
|
||||||
|
mem,
|
||||||
|
physical_start,
|
||||||
|
resource_length,
|
||||||
|
0x1, /* EFI_RESOURCE_MEMORY_MAPPED_IO */
|
||||||
|
/*
|
||||||
|
* EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE
|
||||||
|
*/
|
||||||
|
0x403,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_parse_tdvf_sections() {
|
||||||
|
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
||||||
|
let sections = parse_tdvf_sections(&mut f).unwrap();
|
||||||
|
for section in sections {
|
||||||
|
eprintln!("{:x?}", section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,6 @@
|
|||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(
|
|
||||||
clippy::unreadable_literal,
|
|
||||||
clippy::redundant_static_lifetimes,
|
|
||||||
clippy::trivially_copy_pass_by_ref,
|
|
||||||
clippy::useless_transmute,
|
|
||||||
clippy::should_implement_trait,
|
|
||||||
clippy::transmute_ptr_to_ptr
|
|
||||||
)]
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[allow(clippy::unreadable_literal, clippy::redundant_static_lifetimes)]
|
#[allow(clippy::unreadable_literal, clippy::redundant_static_lifetimes)]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ io_uring = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
io-uring = ">=0.4.0"
|
io-uring = ">=0.4.0"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
serde = ">=1.0.27"
|
serde = ">=1.0.27"
|
||||||
@@ -22,5 +22,3 @@ vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
|||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tempfile = "3.2.0"
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
|||||||
use std::os::linux::fs::MetadataExt;
|
use std::os::linux::fs::MetadataExt;
|
||||||
#[cfg(feature = "io_uring")]
|
#[cfg(feature = "io_uring")]
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::path::PathBuf;
|
use std::path::Path;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use virtio_bindings::bindings::virtio_blk::*;
|
use virtio_bindings::bindings::virtio_blk::*;
|
||||||
@@ -65,7 +65,7 @@ pub enum Error {
|
|||||||
TooManyDescriptors,
|
TooManyDescriptors,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
|
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
|
||||||
let blk_metadata = match disk_path.metadata() {
|
let blk_metadata = match disk_path.metadata() {
|
||||||
Err(_) => return Err(Error::GetFileMetadata),
|
Err(_) => return Err(Error::GetFileMetadata),
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
@@ -80,7 +80,7 @@ fn build_device_id(disk_path: &PathBuf) -> result::Result<String, Error> {
|
|||||||
Ok(device_id)
|
Ok(device_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_disk_image_id(disk_path: &PathBuf) -> Vec<u8> {
|
pub fn build_disk_image_id(disk_path: &Path) -> Vec<u8> {
|
||||||
let mut default_disk_image_id = vec![0; VIRTIO_BLK_ID_BYTES as usize];
|
let mut default_disk_image_id = vec![0; VIRTIO_BLK_ID_BYTES as usize];
|
||||||
match build_device_id(disk_path) {
|
match build_device_id(disk_path) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -135,7 +135,7 @@ pub enum RequestType {
|
|||||||
In,
|
In,
|
||||||
Out,
|
Out,
|
||||||
Flush,
|
Flush,
|
||||||
GetDeviceID,
|
GetDeviceId,
|
||||||
Unsupported(u32),
|
Unsupported(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ pub fn request_type(
|
|||||||
VIRTIO_BLK_T_IN => Ok(RequestType::In),
|
VIRTIO_BLK_T_IN => Ok(RequestType::In),
|
||||||
VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
|
VIRTIO_BLK_T_OUT => Ok(RequestType::Out),
|
||||||
VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
|
VIRTIO_BLK_T_FLUSH => Ok(RequestType::Flush),
|
||||||
VIRTIO_BLK_T_GET_ID => Ok(RequestType::GetDeviceID),
|
VIRTIO_BLK_T_GET_ID => Ok(RequestType::GetDeviceId),
|
||||||
t => Ok(RequestType::Unsupported(t)),
|
t => Ok(RequestType::Unsupported(t)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,6 +163,7 @@ fn sector(mem: &GuestMemoryMmap, desc_addr: GuestAddress) -> result::Result<u64,
|
|||||||
mem.read_obj(addr).map_err(Error::GuestMemory)
|
mem.read_obj(addr).map_err(Error::GuestMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub request_type: RequestType,
|
pub request_type: RequestType,
|
||||||
pub sector: u64,
|
pub sector: u64,
|
||||||
@@ -192,12 +193,17 @@ impl Request {
|
|||||||
let status_desc;
|
let status_desc;
|
||||||
let mut desc = avail_desc
|
let mut desc = avail_desc
|
||||||
.next_descriptor()
|
.next_descriptor()
|
||||||
.ok_or(Error::DescriptorChainTooShort)?;
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Only head descriptor present: request = {:?}", req);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
if !desc.has_next() {
|
if !desc.has_next() {
|
||||||
status_desc = desc;
|
status_desc = desc;
|
||||||
// Only flush requests are allowed to skip the data descriptor.
|
// Only flush requests are allowed to skip the data descriptor.
|
||||||
if req.request_type != RequestType::Flush {
|
if req.request_type != RequestType::Flush {
|
||||||
|
error!("Need a data descriptor: request = {:?}", req);
|
||||||
return Err(Error::DescriptorChainTooShort);
|
return Err(Error::DescriptorChainTooShort);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -208,13 +214,17 @@ impl Request {
|
|||||||
if !desc.is_write_only() && req.request_type == RequestType::In {
|
if !desc.is_write_only() && req.request_type == RequestType::In {
|
||||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||||
}
|
}
|
||||||
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceID {
|
if !desc.is_write_only() && req.request_type == RequestType::GetDeviceId {
|
||||||
return Err(Error::UnexpectedReadOnlyDescriptor);
|
return Err(Error::UnexpectedReadOnlyDescriptor);
|
||||||
}
|
}
|
||||||
req.data_descriptors.push((desc.addr, desc.len));
|
req.data_descriptors.push((desc.addr, desc.len));
|
||||||
desc = desc
|
desc = desc
|
||||||
.next_descriptor()
|
.next_descriptor()
|
||||||
.ok_or(Error::DescriptorChainTooShort)?;
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("DescriptorChain corrupted: request = {:?}", req);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
status_desc = desc;
|
status_desc = desc;
|
||||||
}
|
}
|
||||||
@@ -270,7 +280,7 @@ impl Request {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RequestType::Flush => disk.flush().map_err(ExecuteError::Flush)?,
|
RequestType::Flush => disk.flush().map_err(ExecuteError::Flush)?,
|
||||||
RequestType::GetDeviceID => {
|
RequestType::GetDeviceId => {
|
||||||
if (*data_len as usize) < disk_id.len() {
|
if (*data_len as usize) < disk_id.len() {
|
||||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||||
}
|
}
|
||||||
@@ -336,7 +346,7 @@ impl Request {
|
|||||||
.fsync(Some(user_data))
|
.fsync(Some(user_data))
|
||||||
.map_err(ExecuteError::AsyncFlush)?;
|
.map_err(ExecuteError::AsyncFlush)?;
|
||||||
}
|
}
|
||||||
RequestType::GetDeviceID => {
|
RequestType::GetDeviceId => {
|
||||||
let (data_addr, data_len) = if self.data_descriptors.len() == 1 {
|
let (data_addr, data_len) = if self.data_descriptors.len() == 1 {
|
||||||
(self.data_descriptors[0].0, self.data_descriptors[0].1)
|
(self.data_descriptors[0].0, self.data_descriptors[0].1)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
|
||||||
};
|
};
|
||||||
use io_uring::{opcode, squeue, IoUring};
|
use io_uring::{opcode, squeue, types, IoUring};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom};
|
use std::io::{Seek, SeekFrom};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
@@ -71,28 +71,23 @@ impl AsyncIo for RawFileAsync {
|
|||||||
iovecs: Vec<libc::iovec>,
|
iovecs: Vec<libc::iovec>,
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
let (submitter, sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
let mut avail_sq = sq.available();
|
|
||||||
|
|
||||||
// Safe because we know the file descriptor is valid and we
|
// Safe because we know the file descriptor is valid and we
|
||||||
// relied on vm-memory to provide the buffer address.
|
// relied on vm-memory to provide the buffer address.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
avail_sq.push(
|
sq.push(
|
||||||
opcode::Readv::new(
|
&opcode::Readv::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
|
||||||
opcode::types::Fd(self.fd),
|
.offset(offset)
|
||||||
iovecs.as_ptr(),
|
.build()
|
||||||
iovecs.len() as u32,
|
.flags(squeue::Flags::ASYNC)
|
||||||
)
|
.user_data(user_data),
|
||||||
.offset(offset)
|
|
||||||
.build()
|
|
||||||
.flags(squeue::Flags::ASYNC)
|
|
||||||
.user_data(user_data),
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the submission queue and submit new operations to the
|
// Update the submission queue and submit new operations to the
|
||||||
// io_uring instance.
|
// io_uring instance.
|
||||||
avail_sq.sync();
|
sq.sync();
|
||||||
submitter.submit().map_err(AsyncIoError::ReadVectored)?;
|
submitter.submit().map_err(AsyncIoError::ReadVectored)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -104,28 +99,23 @@ impl AsyncIo for RawFileAsync {
|
|||||||
iovecs: Vec<libc::iovec>,
|
iovecs: Vec<libc::iovec>,
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> AsyncIoResult<()> {
|
||||||
let (submitter, sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
let mut avail_sq = sq.available();
|
|
||||||
|
|
||||||
// Safe because we know the file descriptor is valid and we
|
// Safe because we know the file descriptor is valid and we
|
||||||
// relied on vm-memory to provide the buffer address.
|
// relied on vm-memory to provide the buffer address.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
avail_sq.push(
|
sq.push(
|
||||||
opcode::Writev::new(
|
&opcode::Writev::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32)
|
||||||
opcode::types::Fd(self.fd),
|
.offset(offset)
|
||||||
iovecs.as_ptr(),
|
.build()
|
||||||
iovecs.len() as u32,
|
.flags(squeue::Flags::ASYNC)
|
||||||
)
|
.user_data(user_data),
|
||||||
.offset(offset)
|
|
||||||
.build()
|
|
||||||
.flags(squeue::Flags::ASYNC)
|
|
||||||
.user_data(user_data),
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the submission queue and submit new operations to the
|
// Update the submission queue and submit new operations to the
|
||||||
// io_uring instance.
|
// io_uring instance.
|
||||||
avail_sq.sync();
|
sq.sync();
|
||||||
submitter.submit().map_err(AsyncIoError::WriteVectored)?;
|
submitter.submit().map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -133,13 +123,12 @@ impl AsyncIo for RawFileAsync {
|
|||||||
|
|
||||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||||
if let Some(user_data) = user_data {
|
if let Some(user_data) = user_data {
|
||||||
let (submitter, sq, _) = self.io_uring.split();
|
let (submitter, mut sq, _) = self.io_uring.split();
|
||||||
let mut avail_sq = sq.available();
|
|
||||||
|
|
||||||
// Safe because we know the file descriptor is valid.
|
// Safe because we know the file descriptor is valid.
|
||||||
let _ = unsafe {
|
let _ = unsafe {
|
||||||
avail_sq.push(
|
sq.push(
|
||||||
opcode::Fsync::new(opcode::types::Fd(self.fd))
|
&opcode::Fsync::new(types::Fd(self.fd))
|
||||||
.build()
|
.build()
|
||||||
.flags(squeue::Flags::ASYNC)
|
.flags(squeue::Flags::ASYNC)
|
||||||
.user_data(user_data),
|
.user_data(user_data),
|
||||||
@@ -148,7 +137,7 @@ impl AsyncIo for RawFileAsync {
|
|||||||
|
|
||||||
// Update the submission queue and submit new operations to the
|
// Update the submission queue and submit new operations to the
|
||||||
// io_uring instance.
|
// io_uring instance.
|
||||||
avail_sq.sync();
|
sq.sync();
|
||||||
submitter.submit().map_err(AsyncIoError::Fsync)?;
|
submitter.submit().map_err(AsyncIoError::Fsync)?;
|
||||||
} else {
|
} else {
|
||||||
unsafe { libc::fsync(self.fd) };
|
unsafe { libc::fsync(self.fd) };
|
||||||
@@ -161,7 +150,7 @@ impl AsyncIo for RawFileAsync {
|
|||||||
let mut completion_list = Vec::new();
|
let mut completion_list = Vec::new();
|
||||||
|
|
||||||
let cq = self.io_uring.completion();
|
let cq = self.io_uring.completion();
|
||||||
for cq_entry in cq.available() {
|
for cq_entry in cq {
|
||||||
completion_list.push((cq_entry.user_data(), cq_entry.result()));
|
completion_list.push((cq_entry.user_data(), cq_entry.result()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ mod tests {
|
|||||||
use super::{is_fixed_vhd, VhdFooter};
|
use super::{is_fixed_vhd, VhdFooter};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Seek, SeekFrom, Write};
|
use std::io::{Seek, SeekFrom, Write};
|
||||||
use tempfile::tempfile;
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
|
|
||||||
fn valid_fixed_vhd_footer() -> Vec<u8> {
|
fn valid_fixed_vhd_footer() -> Vec<u8> {
|
||||||
vec![
|
vec![
|
||||||
@@ -172,7 +172,7 @@ mod tests {
|
|||||||
where
|
where
|
||||||
F: FnMut(File),
|
F: FnMut(File),
|
||||||
{
|
{
|
||||||
let mut disk_file: File = tempfile().unwrap();
|
let mut disk_file: File = TempFile::new().unwrap().into_file();
|
||||||
disk_file.set_len(0x1000_0200).unwrap();
|
disk_file.set_len(0x1000_0200).unwrap();
|
||||||
disk_file.seek(SeekFrom::Start(0x1000_0000)).unwrap();
|
disk_file.seek(SeekFrom::Start(0x1000_0000)).unwrap();
|
||||||
disk_file.write_all(&footer).unwrap();
|
disk_file.write_all(&footer).unwrap();
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ authors = ["The Chromium OS Authors"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bitflags = ">=1.2.1"
|
bitflags = ">=1.2.1"
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.4.3"
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = ">=1.0.27"
|
||||||
@@ -19,9 +19,6 @@ vm-memory = "0.5.0"
|
|||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tempfile = "3.2.0"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
acpi = ["acpi_tables"]
|
acpi = ["acpi_tables"]
|
||||||
|
|||||||
@@ -62,20 +62,20 @@ impl BusDevice for AcpiShutdownDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A device for handling ACPI GED event generation
|
/// A device for handling ACPI GED event generation
|
||||||
pub struct AcpiGEDDevice {
|
pub struct AcpiGedDevice {
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
||||||
notification_type: AcpiNotificationFlags,
|
notification_type: AcpiNotificationFlags,
|
||||||
ged_irq: u32,
|
ged_irq: u32,
|
||||||
address: GuestAddress,
|
address: GuestAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcpiGEDDevice {
|
impl AcpiGedDevice {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
||||||
ged_irq: u32,
|
ged_irq: u32,
|
||||||
address: GuestAddress,
|
address: GuestAddress,
|
||||||
) -> AcpiGEDDevice {
|
) -> AcpiGedDevice {
|
||||||
AcpiGEDDevice {
|
AcpiGedDevice {
|
||||||
interrupt,
|
interrupt,
|
||||||
notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
|
notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
|
||||||
ged_irq,
|
ged_irq,
|
||||||
@@ -97,7 +97,7 @@ impl AcpiGEDDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// I/O port reports what type of notification was made
|
// I/O port reports what type of notification was made
|
||||||
impl BusDevice for AcpiGEDDevice {
|
impl BusDevice for AcpiGedDevice {
|
||||||
// Spec has all fields as zero
|
// Spec has all fields as zero
|
||||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||||
data[0] = self.notification_type.bits();
|
data[0] = self.notification_type.bits();
|
||||||
@@ -106,7 +106,7 @@ impl BusDevice for AcpiGEDDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
impl Aml for AcpiGEDDevice {
|
impl Aml for AcpiGedDevice {
|
||||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||||
aml::Device::new(
|
aml::Device::new(
|
||||||
"_SB_.GED_".into(),
|
"_SB_.GED_".into(),
|
||||||
@@ -172,11 +172,11 @@ impl Aml for AcpiGEDDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AcpiPMTimerDevice {
|
pub struct AcpiPmTimerDevice {
|
||||||
start: Instant,
|
start: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcpiPMTimerDevice {
|
impl AcpiPmTimerDevice {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
start: Instant::now(),
|
start: Instant::now(),
|
||||||
@@ -184,13 +184,13 @@ impl AcpiPMTimerDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AcpiPMTimerDevice {
|
impl Default for AcpiPmTimerDevice {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BusDevice for AcpiPMTimerDevice {
|
impl BusDevice for AcpiPmTimerDevice {
|
||||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let since = now.duration_since(self.start);
|
let since = now.duration_since(self.start);
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ use super::interrupt_controller::{Error, InterruptController};
|
|||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vm_device::interrupt::{
|
use vm_device::interrupt::{
|
||||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||||
|
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||||
};
|
};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
// Reserve 32 IRQs (GSI 32 ~ 64) for legacy device.
|
// Reserve 32 IRQs for legacy device.
|
||||||
// GsiAllocator should allocate beyond this: from 64 on
|
pub const IRQ_LEGACY_BASE: usize = 0;
|
||||||
pub const IRQ_LEGACY_COUNT: usize = 32;
|
pub const IRQ_LEGACY_COUNT: usize = 32;
|
||||||
pub const IRQ_SPI_OFFSET: usize = 32;
|
|
||||||
|
|
||||||
// This Gic struct implements InterruptController to provide interrupt delivery service.
|
// This Gic struct implements InterruptController to provide interrupt delivery service.
|
||||||
// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device.
|
// The Gic source files in arch/ folder maintain the Aarch64 specific Gic device.
|
||||||
@@ -34,7 +34,7 @@ impl Gic {
|
|||||||
) -> Result<Gic> {
|
) -> Result<Gic> {
|
||||||
let interrupt_source_group = interrupt_manager
|
let interrupt_source_group = interrupt_manager
|
||||||
.create_group(MsiIrqGroupConfig {
|
.create_group(MsiIrqGroupConfig {
|
||||||
base: IRQ_SPI_OFFSET as InterruptIndex,
|
base: IRQ_LEGACY_BASE as InterruptIndex,
|
||||||
count: IRQ_LEGACY_COUNT as InterruptIndex,
|
count: IRQ_LEGACY_COUNT as InterruptIndex,
|
||||||
})
|
})
|
||||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||||
@@ -47,9 +47,26 @@ impl Gic {
|
|||||||
|
|
||||||
impl InterruptController for Gic {
|
impl InterruptController for Gic {
|
||||||
fn enable(&self) -> Result<()> {
|
fn enable(&self) -> Result<()> {
|
||||||
|
// Set irqfd for legacy interrupts
|
||||||
self.interrupt_source_group
|
self.interrupt_source_group
|
||||||
.enable()
|
.enable()
|
||||||
.map_err(Error::EnableInterrupt)?;
|
.map_err(Error::EnableInterrupt)?;
|
||||||
|
|
||||||
|
// Set irq_routing for legacy interrupts.
|
||||||
|
// irqchip: Hardcode to 0 as we support only 1 GIC
|
||||||
|
// pin: Use irq number as pin
|
||||||
|
for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) {
|
||||||
|
let config = LegacyIrqSourceConfig {
|
||||||
|
irqchip: 0,
|
||||||
|
pin: i as u32,
|
||||||
|
};
|
||||||
|
self.interrupt_source_group
|
||||||
|
.update(
|
||||||
|
i as InterruptIndex,
|
||||||
|
InterruptSourceConfig::LegacyIrq(config),
|
||||||
|
)
|
||||||
|
.map_err(Error::EnableInterrupt)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ enum TriggerMode {
|
|||||||
enum DeliveryMode {
|
enum DeliveryMode {
|
||||||
Fixed = 0b000,
|
Fixed = 0b000,
|
||||||
Lowest = 0b001,
|
Lowest = 0b001,
|
||||||
SMI = 0b010, // System management interrupt
|
Smi = 0b010, // System management interrupt
|
||||||
RemoteRead = 0b011, // This is no longer supported by intel.
|
RemoteRead = 0b011, // This is no longer supported by intel.
|
||||||
NMI = 0b100, // Non maskable interrupt
|
Nmi = 0b100, // Non maskable interrupt
|
||||||
Init = 0b101,
|
Init = 0b101,
|
||||||
Startup = 0b110,
|
Startup = 0b110,
|
||||||
External = 0b111,
|
External = 0b111,
|
||||||
@@ -333,9 +333,9 @@ impl Ioapic {
|
|||||||
match delivery_mode {
|
match delivery_mode {
|
||||||
x if (x == DeliveryMode::Fixed as u8)
|
x if (x == DeliveryMode::Fixed as u8)
|
||||||
|| (x == DeliveryMode::Lowest as u8)
|
|| (x == DeliveryMode::Lowest as u8)
|
||||||
|| (x == DeliveryMode::SMI as u8)
|
|| (x == DeliveryMode::Smi as u8)
|
||||||
|| (x == DeliveryMode::RemoteRead as u8)
|
|| (x == DeliveryMode::RemoteRead as u8)
|
||||||
|| (x == DeliveryMode::NMI as u8)
|
|| (x == DeliveryMode::Nmi as u8)
|
||||||
|| (x == DeliveryMode::Init as u8)
|
|| (x == DeliveryMode::Init as u8)
|
||||||
|| (x == DeliveryMode::Startup as u8)
|
|| (x == DeliveryMode::Startup as u8)
|
||||||
|| (x == DeliveryMode::External as u8) => {}
|
|| (x == DeliveryMode::External as u8) => {}
|
||||||
|
|||||||
484
devices/src/legacy/gpio_pl061.rs
Normal file
484
devices/src/legacy/gpio_pl061.rs
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//! ARM PrimeCell General Purpose Input/Output(PL061)
|
||||||
|
//!
|
||||||
|
//! This module implements an ARM PrimeCell General Purpose Input/Output(PL061) to support gracefully poweroff microvm from external.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use std::result;
|
||||||
|
use std::sync::{Arc, Barrier};
|
||||||
|
use std::{fmt, io};
|
||||||
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
use vm_migration::{
|
||||||
|
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||||
|
Transportable,
|
||||||
|
};
|
||||||
|
|
||||||
|
const OFS_DATA: u64 = 0x400; // Data Register
|
||||||
|
const GPIODIR: u64 = 0x400; // Direction Register
|
||||||
|
const GPIOIS: u64 = 0x404; // Interrupt Sense Register
|
||||||
|
const GPIOIBE: u64 = 0x408; // Interrupt Both Edges Register
|
||||||
|
const GPIOIEV: u64 = 0x40c; // Interrupt Event Register
|
||||||
|
const GPIOIE: u64 = 0x410; // Interrupt Mask Register
|
||||||
|
const GPIORIE: u64 = 0x414; // Raw Interrupt Status Register
|
||||||
|
const GPIOMIS: u64 = 0x418; // Masked Interrupt Status Register
|
||||||
|
const GPIOIC: u64 = 0x41c; // Interrupt Clear Register
|
||||||
|
const GPIOAFSEL: u64 = 0x420; // Mode Control Select Register
|
||||||
|
// From 0x424 to 0xFDC => reserved space.
|
||||||
|
// From 0xFE0 to 0xFFC => Peripheral and PrimeCell Identification Registers which are Read Only registers.
|
||||||
|
// Thses registers can conceptually be treated as a 32-bit register, and PartNumber[11:0] is used to identify the peripheral.
|
||||||
|
// We are putting the expected values (look at 'Reset value' column from above mentioned document) in an array.
|
||||||
|
const GPIO_ID: [u8; 8] = [0x61, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
||||||
|
// ID Margins
|
||||||
|
const GPIO_ID_LOW: u64 = 0xfe0;
|
||||||
|
const GPIO_ID_HIGH: u64 = 0x1000;
|
||||||
|
|
||||||
|
const N_GPIOS: u32 = 8;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
BadWriteOffset(u64),
|
||||||
|
GpioInterruptDisabled,
|
||||||
|
GpioInterruptFailure(io::Error),
|
||||||
|
GpioTriggerKeyFailure(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {}", offset),
|
||||||
|
Error::GpioInterruptDisabled => write!(f, "GPIO interrupt disabled by guest driver.",),
|
||||||
|
Error::GpioInterruptFailure(ref e) => {
|
||||||
|
write!(f, "Could not trigger GPIO interrupt: {}.", e)
|
||||||
|
}
|
||||||
|
Error::GpioTriggerKeyFailure(key) => {
|
||||||
|
write!(f, "Invalid GPIO Input key triggerd: {}.", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// A GPIO device following the PL061 specification.
|
||||||
|
pub struct Gpio {
|
||||||
|
id: String,
|
||||||
|
// Data Register
|
||||||
|
data: u32,
|
||||||
|
old_in_data: u32,
|
||||||
|
// Direction Register
|
||||||
|
dir: u32,
|
||||||
|
// Interrupt Sense Register
|
||||||
|
isense: u32,
|
||||||
|
// Interrupt Both Edges Register
|
||||||
|
ibe: u32,
|
||||||
|
// Interrupt Event Register
|
||||||
|
iev: u32,
|
||||||
|
// Interrupt Mask Register
|
||||||
|
im: u32,
|
||||||
|
// Raw Interrupt Status Register
|
||||||
|
istate: u32,
|
||||||
|
// Mode Control Select Register
|
||||||
|
afsel: u32,
|
||||||
|
// GPIO irq_field
|
||||||
|
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct GpioState {
|
||||||
|
data: u32,
|
||||||
|
old_in_data: u32,
|
||||||
|
dir: u32,
|
||||||
|
isense: u32,
|
||||||
|
ibe: u32,
|
||||||
|
iev: u32,
|
||||||
|
im: u32,
|
||||||
|
istate: u32,
|
||||||
|
afsel: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpio {
|
||||||
|
/// Constructs an PL061 GPIO device.
|
||||||
|
pub fn new(id: String, interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
data: 0,
|
||||||
|
old_in_data: 0,
|
||||||
|
dir: 0,
|
||||||
|
isense: 0,
|
||||||
|
ibe: 0,
|
||||||
|
iev: 0,
|
||||||
|
im: 0,
|
||||||
|
istate: 0,
|
||||||
|
afsel: 0,
|
||||||
|
interrupt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> GpioState {
|
||||||
|
GpioState {
|
||||||
|
data: self.data,
|
||||||
|
old_in_data: self.old_in_data,
|
||||||
|
dir: self.dir,
|
||||||
|
isense: self.isense,
|
||||||
|
ibe: self.ibe,
|
||||||
|
iev: self.iev,
|
||||||
|
im: self.im,
|
||||||
|
istate: self.istate,
|
||||||
|
afsel: self.afsel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_state(&mut self, state: &GpioState) {
|
||||||
|
self.data = state.data;
|
||||||
|
self.old_in_data = state.old_in_data;
|
||||||
|
self.dir = state.dir;
|
||||||
|
self.isense = state.isense;
|
||||||
|
self.ibe = state.ibe;
|
||||||
|
self.iev = state.iev;
|
||||||
|
self.im = state.im;
|
||||||
|
self.istate = state.istate;
|
||||||
|
self.afsel = state.afsel;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pl061_internal_update(&mut self) {
|
||||||
|
// FIXME:
|
||||||
|
// Missing Output Interrupt Emulation.
|
||||||
|
|
||||||
|
// Input Edging Interrupt Emulation.
|
||||||
|
let changed = ((self.old_in_data ^ self.data) & !self.dir) as u32;
|
||||||
|
if changed > 0 {
|
||||||
|
self.old_in_data = self.data;
|
||||||
|
for i in 0..N_GPIOS {
|
||||||
|
let mask = (1 << i) as u32;
|
||||||
|
if (changed & mask) > 0 {
|
||||||
|
// Bits set high in GPIOIS(Interrupt sense register) configure the corresponding
|
||||||
|
// pins to detect levels, otherwise, detect edges.
|
||||||
|
if (self.isense & mask) == 0 {
|
||||||
|
if (self.ibe & mask) > 0 {
|
||||||
|
// Bits set high in GPIOIBE(Interrupt both-edges register) configure the corresponding
|
||||||
|
// pins to detect both falling and rising edges.
|
||||||
|
// Clearing a bit configures the pin to be controlled by GPIOIEV.
|
||||||
|
self.istate |= mask;
|
||||||
|
} else {
|
||||||
|
// Bits set to high in GPIOIEV(Interrupt event register) configure the
|
||||||
|
// corresponding pin to detect rising edges, otherwise, detect falling edges.
|
||||||
|
self.istate |= !(self.data ^ self.iev) & mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input Level Interrupt Emulation.
|
||||||
|
self.istate |= !(self.data ^ self.iev) & self.isense;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> {
|
||||||
|
if offset < OFS_DATA {
|
||||||
|
// In order to write to data register, the corresponding bits in the mask, resulting
|
||||||
|
// from the offsite[9:2], must be HIGH. otherwise the bit values remain unchanged.
|
||||||
|
let mask = (offset >> 2) as u32 & self.dir;
|
||||||
|
self.data = (self.data & !mask) | (val & mask);
|
||||||
|
} else {
|
||||||
|
match offset {
|
||||||
|
GPIODIR => {
|
||||||
|
/* Direction Register */
|
||||||
|
self.dir = val & 0xff;
|
||||||
|
}
|
||||||
|
GPIOIS => {
|
||||||
|
/* Interrupt Sense Register */
|
||||||
|
self.isense = val & 0xff;
|
||||||
|
}
|
||||||
|
GPIOIBE => {
|
||||||
|
/* Interrupt Both Edges Register */
|
||||||
|
self.ibe = val & 0xff;
|
||||||
|
}
|
||||||
|
GPIOIEV => {
|
||||||
|
/* Interrupt Event Register */
|
||||||
|
self.iev = val & 0xff;
|
||||||
|
}
|
||||||
|
GPIOIE => {
|
||||||
|
/* Interrupt Mask Register */
|
||||||
|
self.im = val & 0xff;
|
||||||
|
}
|
||||||
|
GPIOIC => {
|
||||||
|
/* Interrupt Clear Register */
|
||||||
|
self.istate &= !val;
|
||||||
|
}
|
||||||
|
GPIOAFSEL => {
|
||||||
|
/* Mode Control Select Register */
|
||||||
|
self.afsel = val & 0xff;
|
||||||
|
}
|
||||||
|
o => {
|
||||||
|
return Err(Error::BadWriteOffset(o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_key(&mut self, key: u32) -> Result<()> {
|
||||||
|
let mask = (1 << key) as u32;
|
||||||
|
if (!self.dir & mask) > 0 {
|
||||||
|
// emulate key event
|
||||||
|
// By default, Input Pin is configured to detect both rising and falling edges.
|
||||||
|
// So reverse the input pin data to generate a pulse.
|
||||||
|
self.data |= !(self.data & mask) & mask;
|
||||||
|
self.pl061_internal_update();
|
||||||
|
|
||||||
|
match self.trigger_gpio_interrupt() {
|
||||||
|
Ok(_) | Err(Error::GpioInterruptDisabled) => return Ok(()),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::GpioTriggerKeyFailure(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_gpio_interrupt(&self) -> Result<()> {
|
||||||
|
// Bits set to high in GPIOIE(Interrupt mask register) allow the corresponding pins to
|
||||||
|
// trigger their individual interrupts and then the combined GPIOINTR line.
|
||||||
|
if (self.istate & self.im) == 0 {
|
||||||
|
warn!("Failed to trigger GPIO input interrupt (disabled by guest OS)");
|
||||||
|
return Err(Error::GpioInterruptDisabled);
|
||||||
|
}
|
||||||
|
self.interrupt
|
||||||
|
.trigger(0)
|
||||||
|
.map_err(Error::GpioInterruptFailure)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusDevice for Gpio {
|
||||||
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
let value;
|
||||||
|
let mut read_ok = true;
|
||||||
|
|
||||||
|
if (GPIO_ID_LOW..GPIO_ID_HIGH).contains(&offset) {
|
||||||
|
let index = ((offset - GPIO_ID_LOW) >> 2) as usize;
|
||||||
|
value = u32::from(GPIO_ID[index]);
|
||||||
|
} else if offset < OFS_DATA {
|
||||||
|
value = self.data & ((offset >> 2) as u32)
|
||||||
|
} else {
|
||||||
|
value = match offset {
|
||||||
|
GPIODIR => self.dir,
|
||||||
|
GPIOIS => self.isense,
|
||||||
|
GPIOIBE => self.ibe,
|
||||||
|
GPIOIEV => self.iev,
|
||||||
|
GPIOIE => self.im,
|
||||||
|
GPIORIE => self.istate,
|
||||||
|
GPIOMIS => self.istate & self.im,
|
||||||
|
GPIOAFSEL => self.afsel,
|
||||||
|
_ => {
|
||||||
|
read_ok = false;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if read_ok && data.len() <= 4 {
|
||||||
|
write_le_u32(data, value);
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Invalid GPIO PL061 read: offset {}, data length {}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
if data.len() <= 4 {
|
||||||
|
let value = read_le_u32(&data);
|
||||||
|
if let Err(e) = self.handle_write(offset, value) {
|
||||||
|
warn!("Failed to write to GPIO PL061 device: {}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Invalid GPIO PL061 write: offset {}, data length {}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshottable for Gpio {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
|
let snapshot =
|
||||||
|
serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?;
|
||||||
|
|
||||||
|
let mut gpio_snapshot = Snapshot::new(self.id.as_str());
|
||||||
|
gpio_snapshot.add_data_section(SnapshotDataSection {
|
||||||
|
id: format!("{}-section", self.id),
|
||||||
|
snapshot,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(gpio_snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||||
|
if let Some(gpio_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) {
|
||||||
|
let gpio_state = match serde_json::from_slice(&gpio_section.snapshot) {
|
||||||
|
Ok(state) => state,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(MigratableError::Restore(anyhow!(
|
||||||
|
"Could not deserialize GPIO {}",
|
||||||
|
error
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_state(&gpio_state);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(MigratableError::Restore(anyhow!(
|
||||||
|
"Could not find the GPIO snapshot section"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pausable for Gpio {}
|
||||||
|
impl Transportable for Gpio {}
|
||||||
|
impl Migratable for Gpio {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
const GPIO_NAME: &str = "gpio";
|
||||||
|
const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
|
||||||
|
|
||||||
|
struct TestInterrupt {
|
||||||
|
event_fd: EventFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptSourceGroup for TestInterrupt {
|
||||||
|
fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
|
||||||
|
self.event_fd.write(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&self,
|
||||||
|
_index: InterruptIndex,
|
||||||
|
_config: InterruptSourceConfig,
|
||||||
|
) -> result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
|
||||||
|
Some(self.event_fd.try_clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestInterrupt {
|
||||||
|
fn new(event_fd: EventFd) -> Self {
|
||||||
|
TestInterrupt { event_fd }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gpio_read_write_and_event() {
|
||||||
|
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
|
let mut gpio = Gpio::new(
|
||||||
|
String::from(GPIO_NAME),
|
||||||
|
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||||
|
);
|
||||||
|
let mut data = [0; 4];
|
||||||
|
|
||||||
|
// Read and write to the GPIODIR register.
|
||||||
|
// Set pin 0 output pin.
|
||||||
|
write_le_u32(&mut data, 1);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 1);
|
||||||
|
|
||||||
|
// Read and write to the GPIODATA register.
|
||||||
|
write_le_u32(&mut data, 1);
|
||||||
|
// Set pin 0 high.
|
||||||
|
let offset = 0x00000004 as u64;
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 1);
|
||||||
|
|
||||||
|
// Read and write to the GPIOIS register.
|
||||||
|
// Configure pin 0 detecting level interrupt.
|
||||||
|
write_le_u32(&mut data, 1);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 1);
|
||||||
|
|
||||||
|
// Read and write to the GPIOIBE register.
|
||||||
|
// Configure pin 1 detecting both falling and rising edges.
|
||||||
|
write_le_u32(&mut data, 2);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 2);
|
||||||
|
|
||||||
|
// Read and write to the GPIOIEV register.
|
||||||
|
// Configure pin 2 detecting both falling and rising edges.
|
||||||
|
write_le_u32(&mut data, 4);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 4);
|
||||||
|
|
||||||
|
// Read and write to the GPIOIE register.
|
||||||
|
// Configure pin 0...2 capable of triggering their individual interrupts
|
||||||
|
// and then the combined GPIOINTR line.
|
||||||
|
write_le_u32(&mut data, 7);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 7);
|
||||||
|
|
||||||
|
let mask = 0x00000002 as u32;
|
||||||
|
// emulate an rising pulse in pin 1.
|
||||||
|
gpio.data |= !(gpio.data & mask) & mask;
|
||||||
|
gpio.pl061_internal_update();
|
||||||
|
// The interrupt line on pin 1 should be on.
|
||||||
|
// Read the GPIOMIS register.
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOMIS, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 2);
|
||||||
|
|
||||||
|
// Read and Write to the GPIOIC register.
|
||||||
|
// clear interrupt in pin 1.
|
||||||
|
write_le_u32(&mut data, 2);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
assert_eq!(v, 2);
|
||||||
|
|
||||||
|
// Attempts to write beyond the writable space.
|
||||||
|
write_le_u32(&mut data, 0);
|
||||||
|
gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
|
||||||
|
|
||||||
|
let mut data = [0; 4];
|
||||||
|
gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
|
||||||
|
let index = GPIO_ID_LOW + 3;
|
||||||
|
assert_eq!(data[0], GPIO_ID[((index - GPIO_ID_LOW) >> 2) as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,14 @@
|
|||||||
mod cmos;
|
mod cmos;
|
||||||
#[cfg(feature = "fwdebug")]
|
#[cfg(feature = "fwdebug")]
|
||||||
mod fwdebug;
|
mod fwdebug;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
mod gpio_pl061;
|
||||||
mod i8042;
|
mod i8042;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod rtc_pl031;
|
mod rtc_pl031;
|
||||||
mod serial;
|
mod serial;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
mod uart_pl011;
|
||||||
|
|
||||||
#[cfg(feature = "cmos")]
|
#[cfg(feature = "cmos")]
|
||||||
pub use self::cmos::Cmos;
|
pub use self::cmos::Cmos;
|
||||||
@@ -22,4 +26,10 @@ pub use self::i8042::I8042Device;
|
|||||||
pub use self::serial::Serial;
|
pub use self::serial::Serial;
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub use self::rtc_pl031::RTC;
|
pub use self::gpio_pl061::Error as GpioDeviceError;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
pub use self::gpio_pl061::Gpio;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
pub use self::rtc_pl031::Rtc;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
pub use self::uart_pl011::Pl011;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
|
//! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
|
||||||
//! a real-time clock input.
|
//! a real-time clock input.
|
||||||
//!
|
//!
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
@@ -38,52 +39,6 @@ const AMBA_ID_HIGH: u64 = 0x1000;
|
|||||||
/// Constant to convert seconds to nanoseconds.
|
/// Constant to convert seconds to nanoseconds.
|
||||||
pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
|
pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! generate_read_fn {
|
|
||||||
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
|
|
||||||
assert!($type_size == std::mem::size_of::<$data_type>());
|
|
||||||
let mut array = [0u8; $type_size];
|
|
||||||
for (byte, read) in array.iter_mut().zip(input.iter().cloned()) {
|
|
||||||
*byte = read as u8;
|
|
||||||
}
|
|
||||||
<$data_type>::$endian_type(array)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! generate_write_fn {
|
|
||||||
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
|
|
||||||
for (byte, read) in buf
|
|
||||||
.iter_mut()
|
|
||||||
.zip(<$data_type>::$endian_type(n).iter().cloned())
|
|
||||||
{
|
|
||||||
*byte = read as $byte_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes);
|
|
||||||
generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes);
|
|
||||||
generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes);
|
|
||||||
generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes);
|
|
||||||
|
|
||||||
generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes);
|
|
||||||
generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes);
|
|
||||||
|
|
||||||
generate_write_fn!(write_le_u16, u16, u8, to_le_bytes);
|
|
||||||
generate_write_fn!(write_le_u32, u32, u8, to_le_bytes);
|
|
||||||
generate_write_fn!(write_le_u64, u64, u8, to_le_bytes);
|
|
||||||
generate_write_fn!(write_le_i32, i32, i8, to_le_bytes);
|
|
||||||
|
|
||||||
generate_write_fn!(write_be_u16, u16, u8, to_be_bytes);
|
|
||||||
generate_write_fn!(write_be_u32, u32, u8, to_be_bytes);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
@@ -115,9 +70,9 @@ pub enum ClockType {
|
|||||||
ThreadCpu,
|
ThreadCpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<libc::clockid_t> for ClockType {
|
impl From<ClockType> for libc::clockid_t {
|
||||||
fn into(self) -> libc::clockid_t {
|
fn from(ct: ClockType) -> libc::clockid_t {
|
||||||
match self {
|
match ct {
|
||||||
ClockType::Monotonic => libc::CLOCK_MONOTONIC,
|
ClockType::Monotonic => libc::CLOCK_MONOTONIC,
|
||||||
ClockType::Real => libc::CLOCK_REALTIME,
|
ClockType::Real => libc::CLOCK_REALTIME,
|
||||||
ClockType::ProcessCpu => libc::CLOCK_PROCESS_CPUTIME_ID,
|
ClockType::ProcessCpu => libc::CLOCK_PROCESS_CPUTIME_ID,
|
||||||
@@ -260,7 +215,7 @@ pub fn seconds_to_nanoseconds(value: i64) -> Option<i64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A RTC device following the PL031 specification..
|
/// A RTC device following the PL031 specification..
|
||||||
pub struct RTC {
|
pub struct Rtc {
|
||||||
previous_now: Instant,
|
previous_now: Instant,
|
||||||
tick_offset: i64,
|
tick_offset: i64,
|
||||||
// This is used for implementing the RTC alarm. However, in Firecracker we do not need it.
|
// This is used for implementing the RTC alarm. However, in Firecracker we do not need it.
|
||||||
@@ -272,10 +227,10 @@ pub struct RTC {
|
|||||||
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
interrupt: Arc<Box<dyn InterruptSourceGroup>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RTC {
|
impl Rtc {
|
||||||
/// Constructs an AMBA PL031 RTC device.
|
/// Constructs an AMBA PL031 RTC device.
|
||||||
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> RTC {
|
pub fn new(interrupt: Arc<Box<dyn InterruptSourceGroup>>) -> Self {
|
||||||
RTC {
|
Self {
|
||||||
// This is used only for duration measuring purposes.
|
// This is used only for duration measuring purposes.
|
||||||
previous_now: Instant::now(),
|
previous_now: Instant::now(),
|
||||||
tick_offset: get_time(ClockType::Real) as i64,
|
tick_offset: get_time(ClockType::Real) as i64,
|
||||||
@@ -334,7 +289,7 @@ impl RTC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BusDevice for RTC {
|
impl BusDevice for Rtc {
|
||||||
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
let v;
|
let v;
|
||||||
let mut read_ok = true;
|
let mut read_ok = true;
|
||||||
@@ -373,7 +328,7 @@ impl BusDevice for RTC {
|
|||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if data.len() <= 4 {
|
if data.len() <= 4 {
|
||||||
let v = read_le_u32(&data[..]);
|
let v = read_le_u32(&data);
|
||||||
if let Err(e) = self.handle_write(offset, v) {
|
if let Err(e) = self.handle_write(offset, v) {
|
||||||
warn!("Failed to write to RTC PL031 device: {}", e);
|
warn!("Failed to write to RTC PL031 device: {}", e);
|
||||||
}
|
}
|
||||||
@@ -392,6 +347,10 @@ impl BusDevice for RTC {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u32, read_le_u64, write_be_u16,
|
||||||
|
write_be_u32, write_le_i32, write_le_u16, write_le_u32, write_le_u64,
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
@@ -491,7 +450,7 @@ mod tests {
|
|||||||
fn test_rtc_read_write_and_event() {
|
fn test_rtc_read_write_and_event() {
|
||||||
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
||||||
|
|
||||||
let mut rtc = RTC::new(Arc::new(Box::new(TestInterrupt::new(
|
let mut rtc = Rtc::new(Arc::new(Box::new(TestInterrupt::new(
|
||||||
intr_evt.try_clone().unwrap(),
|
intr_evt.try_clone().unwrap(),
|
||||||
))));
|
))));
|
||||||
let mut data = [0; 4];
|
let mut data = [0; 4];
|
||||||
@@ -500,7 +459,7 @@ mod tests {
|
|||||||
write_le_u32(&mut data, 123);
|
write_le_u32(&mut data, 123);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
|
||||||
let v = read_le_u32(&data[..]);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 123);
|
assert_eq!(v, 123);
|
||||||
|
|
||||||
// Read and write to the LR register.
|
// Read and write to the LR register.
|
||||||
@@ -512,7 +471,7 @@ mod tests {
|
|||||||
assert!(rtc.previous_now > previous_now_before);
|
assert!(rtc.previous_now > previous_now_before);
|
||||||
|
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCLR, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCLR, &mut data);
|
||||||
let v_read = read_le_u32(&data[..]);
|
let v_read = read_le_u32(&data);
|
||||||
assert_eq!((v / NANOS_PER_SECOND) as u32, v_read);
|
assert_eq!((v / NANOS_PER_SECOND) as u32, v_read);
|
||||||
|
|
||||||
// Read and write to IMSC register.
|
// Read and write to IMSC register.
|
||||||
@@ -523,14 +482,14 @@ mod tests {
|
|||||||
// The interrupt line should be on.
|
// The interrupt line should be on.
|
||||||
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
|
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
||||||
let v = read_le_u32(&data[..]);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(non_zero & 1, v);
|
assert_eq!(non_zero & 1, v);
|
||||||
|
|
||||||
// Now test with 0.
|
// Now test with 0.
|
||||||
write_le_u32(&mut data, 0);
|
write_le_u32(&mut data, 0);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
|
||||||
let v = read_le_u32(&data[..]);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(0, v);
|
assert_eq!(0, v);
|
||||||
|
|
||||||
// Read and write to the ICR register.
|
// Read and write to the ICR register.
|
||||||
@@ -538,10 +497,10 @@ mod tests {
|
|||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
|
||||||
// The interrupt line should be on.
|
// The interrupt line should be on.
|
||||||
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
|
assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
|
||||||
let v_before = read_le_u32(&data[..]);
|
let v_before = read_le_u32(&data);
|
||||||
|
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
|
||||||
let v = read_le_u32(&data[..]);
|
let v = read_le_u32(&data);
|
||||||
// ICR is a write only register. Data received should stay equal to data sent.
|
// ICR is a write only register. Data received should stay equal to data sent.
|
||||||
assert_eq!(v, v_before);
|
assert_eq!(v, v_before);
|
||||||
|
|
||||||
@@ -549,7 +508,7 @@ mod tests {
|
|||||||
write_le_u32(&mut data, 0);
|
write_le_u32(&mut data, 0);
|
||||||
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
||||||
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
|
||||||
let v = read_le_u32(&data[..]);
|
let v = read_le_u32(&data);
|
||||||
assert_eq!(v, 1);
|
assert_eq!(v, 1);
|
||||||
|
|
||||||
// Attempts to write beyond the writable space. Using here the space used to read
|
// Attempts to write beyond the writable space. Using here the space used to read
|
||||||
|
|||||||
506
devices/src/legacy/uart_pl011.rs
Normal file
506
devices/src/legacy/uart_pl011.rs
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
// Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//! ARM PrimeCell UART(PL011)
|
||||||
|
//!
|
||||||
|
//! This module implements an ARM PrimeCell UART(PL011).
|
||||||
|
//!
|
||||||
|
|
||||||
|
use crate::{read_le_u32, write_le_u32};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt;
|
||||||
|
use std::sync::{Arc, Barrier};
|
||||||
|
use std::{io, result};
|
||||||
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
|
use vm_device::BusDevice;
|
||||||
|
use vm_migration::{
|
||||||
|
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||||
|
Transportable,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Registers */
|
||||||
|
const UARTDR: u64 = 0;
|
||||||
|
const UARTRSR_UARTECR: u64 = 1;
|
||||||
|
const UARTFR: u64 = 6;
|
||||||
|
const UARTILPR: u64 = 8;
|
||||||
|
const UARTIBRD: u64 = 9;
|
||||||
|
const UARTFBRD: u64 = 10;
|
||||||
|
const UARTLCR_H: u64 = 11;
|
||||||
|
const UARTCR: u64 = 12;
|
||||||
|
const UARTIFLS: u64 = 13;
|
||||||
|
const UARTIMSC: u64 = 14;
|
||||||
|
const UARTRIS: u64 = 15;
|
||||||
|
const UARTMIS: u64 = 16;
|
||||||
|
const UARTICR: u64 = 17;
|
||||||
|
const UARTDMACR: u64 = 18;
|
||||||
|
|
||||||
|
const PL011_INT_TX: u32 = 0x20;
|
||||||
|
const PL011_INT_RX: u32 = 0x10;
|
||||||
|
|
||||||
|
const PL011_FLAG_RXFF: u32 = 0x40;
|
||||||
|
const PL011_FLAG_RXFE: u32 = 0x10;
|
||||||
|
|
||||||
|
const PL011_ID: [u8; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
||||||
|
// We are only interested in the margins.
|
||||||
|
const AMBA_ID_LOW: u64 = 0x3f8;
|
||||||
|
const AMBA_ID_HIGH: u64 = 0x401;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
BadWriteOffset(u64),
|
||||||
|
DmaNotImplemented,
|
||||||
|
InterruptFailure(io::Error),
|
||||||
|
WriteAllFailure(io::Error),
|
||||||
|
FlushFailure(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::BadWriteOffset(offset) => write!(f, "pl011_write: Bad Write Offset: {}", offset),
|
||||||
|
Error::DmaNotImplemented => write!(f, "pl011: DMA not implemented."),
|
||||||
|
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e),
|
||||||
|
Error::WriteAllFailure(e) => write!(f, "Failed to write: {}", e),
|
||||||
|
Error::FlushFailure(e) => write!(f, "Failed to flush: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// A PL011 device following the PL011 specification.
|
||||||
|
pub struct Pl011 {
|
||||||
|
id: String,
|
||||||
|
flags: u32,
|
||||||
|
lcr: u32,
|
||||||
|
rsr: u32,
|
||||||
|
cr: u32,
|
||||||
|
dmacr: u32,
|
||||||
|
int_enabled: u32,
|
||||||
|
int_level: u32,
|
||||||
|
read_fifo: VecDeque<u8>,
|
||||||
|
ilpr: u32,
|
||||||
|
ibrd: u32,
|
||||||
|
fbrd: u32,
|
||||||
|
ifl: u32,
|
||||||
|
read_count: u32,
|
||||||
|
read_trigger: u32,
|
||||||
|
irq: Arc<Box<dyn InterruptSourceGroup>>,
|
||||||
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Pl011State {
|
||||||
|
flags: u32,
|
||||||
|
lcr: u32,
|
||||||
|
rsr: u32,
|
||||||
|
cr: u32,
|
||||||
|
dmacr: u32,
|
||||||
|
int_enabled: u32,
|
||||||
|
int_level: u32,
|
||||||
|
read_fifo: VecDeque<u8>,
|
||||||
|
ilpr: u32,
|
||||||
|
ibrd: u32,
|
||||||
|
fbrd: u32,
|
||||||
|
ifl: u32,
|
||||||
|
read_count: u32,
|
||||||
|
read_trigger: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pl011 {
|
||||||
|
/// Constructs an AMBA PL011 UART device.
|
||||||
|
pub fn new(
|
||||||
|
id: String,
|
||||||
|
irq: Arc<Box<dyn InterruptSourceGroup>>,
|
||||||
|
out: Option<Box<dyn io::Write + Send>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
flags: 0x90u32,
|
||||||
|
lcr: 0u32,
|
||||||
|
rsr: 0u32,
|
||||||
|
cr: 0x300u32,
|
||||||
|
dmacr: 0u32,
|
||||||
|
int_enabled: 0u32,
|
||||||
|
int_level: 0u32,
|
||||||
|
read_fifo: VecDeque::new(),
|
||||||
|
ilpr: 0u32,
|
||||||
|
ibrd: 0u32,
|
||||||
|
fbrd: 0u32,
|
||||||
|
ifl: 0x12u32,
|
||||||
|
read_count: 0u32,
|
||||||
|
read_trigger: 1u32,
|
||||||
|
irq,
|
||||||
|
out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> Pl011State {
|
||||||
|
Pl011State {
|
||||||
|
flags: self.flags,
|
||||||
|
lcr: self.lcr,
|
||||||
|
rsr: self.rsr,
|
||||||
|
cr: self.cr,
|
||||||
|
dmacr: self.dmacr,
|
||||||
|
int_enabled: self.int_enabled,
|
||||||
|
int_level: self.int_level,
|
||||||
|
read_fifo: self.read_fifo.clone(),
|
||||||
|
ilpr: self.ilpr,
|
||||||
|
ibrd: self.ibrd,
|
||||||
|
fbrd: self.fbrd,
|
||||||
|
ifl: self.ifl,
|
||||||
|
read_count: self.read_count,
|
||||||
|
read_trigger: self.read_trigger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_state(&mut self, state: &Pl011State) {
|
||||||
|
self.flags = state.flags;
|
||||||
|
self.lcr = state.lcr;
|
||||||
|
self.rsr = state.rsr;
|
||||||
|
self.cr = state.cr;
|
||||||
|
self.dmacr = state.dmacr;
|
||||||
|
self.int_enabled = state.int_enabled;
|
||||||
|
self.int_level = state.int_level;
|
||||||
|
self.read_fifo = state.read_fifo.clone();
|
||||||
|
self.ilpr = state.ilpr;
|
||||||
|
self.ibrd = state.ibrd;
|
||||||
|
self.fbrd = state.fbrd;
|
||||||
|
self.ifl = state.ifl;
|
||||||
|
self.read_count = state.read_count;
|
||||||
|
self.read_trigger = state.read_trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queues raw bytes for the guest to read and signals the interrupt
|
||||||
|
pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> {
|
||||||
|
self.read_fifo.extend(c);
|
||||||
|
self.read_count += c.len() as u32;
|
||||||
|
self.flags &= !PL011_FLAG_RXFE;
|
||||||
|
|
||||||
|
if ((self.lcr & 0x10) == 0) || (self.read_count == 16) {
|
||||||
|
self.flags |= PL011_FLAG_RXFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.read_count >= self.read_trigger {
|
||||||
|
self.int_level |= PL011_INT_RX;
|
||||||
|
self.trigger_interrupt()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pl011_get_baudrate(&self) -> u32 {
|
||||||
|
if self.fbrd == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clk = 24_000_000; // We set the APB_PLCK to 24M in device tree
|
||||||
|
(clk / ((self.ibrd << 6) + self.fbrd)) << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pl011_trace_baudrate_change(&self) {
|
||||||
|
debug!(
|
||||||
|
"=== New baudrate: {:#?} (clk: {:#?}Hz, ibrd: {:#?}, fbrd: {:#?}) ===",
|
||||||
|
self.pl011_get_baudrate(),
|
||||||
|
24_000_000, // We set the APB_PLCK to 24M in device tree
|
||||||
|
self.ibrd,
|
||||||
|
self.fbrd
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pl011_set_read_trigger(&mut self) {
|
||||||
|
self.read_trigger = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> {
|
||||||
|
match offset >> 2 {
|
||||||
|
UARTDR => {
|
||||||
|
self.int_level |= PL011_INT_TX;
|
||||||
|
if let Some(out) = self.out.as_mut() {
|
||||||
|
out.write_all(&[val.to_le_bytes()[0]])
|
||||||
|
.map_err(Error::WriteAllFailure)?;
|
||||||
|
out.flush().map_err(Error::FlushFailure)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UARTRSR_UARTECR => {
|
||||||
|
self.rsr = 0;
|
||||||
|
}
|
||||||
|
UARTFR => { /* Writes to Flag register are ignored.*/ }
|
||||||
|
UARTILPR => {
|
||||||
|
self.ilpr = val;
|
||||||
|
}
|
||||||
|
UARTIBRD => {
|
||||||
|
self.ibrd = val;
|
||||||
|
self.pl011_trace_baudrate_change();
|
||||||
|
}
|
||||||
|
UARTFBRD => {
|
||||||
|
self.fbrd = val;
|
||||||
|
self.pl011_trace_baudrate_change();
|
||||||
|
}
|
||||||
|
UARTLCR_H => {
|
||||||
|
/* Reset the FIFO state on FIFO enable or disable */
|
||||||
|
if ((self.lcr ^ val) & 0x10) != 0 {
|
||||||
|
self.read_count = 0;
|
||||||
|
}
|
||||||
|
self.lcr = val;
|
||||||
|
self.pl011_set_read_trigger();
|
||||||
|
}
|
||||||
|
UARTCR => {
|
||||||
|
self.cr = val;
|
||||||
|
}
|
||||||
|
UARTIFLS => {
|
||||||
|
self.ifl = val;
|
||||||
|
self.pl011_set_read_trigger();
|
||||||
|
}
|
||||||
|
UARTIMSC => {
|
||||||
|
self.int_enabled = val;
|
||||||
|
self.trigger_interrupt().map_err(Error::InterruptFailure)?;
|
||||||
|
}
|
||||||
|
UARTICR => {
|
||||||
|
self.int_level &= !val;
|
||||||
|
self.trigger_interrupt().map_err(Error::InterruptFailure)?;
|
||||||
|
}
|
||||||
|
UARTDMACR => {
|
||||||
|
self.dmacr = val;
|
||||||
|
if (val & 3) != 0 {
|
||||||
|
return Err(Error::DmaNotImplemented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off => {
|
||||||
|
return Err(Error::BadWriteOffset(off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_interrupt(&mut self) -> result::Result<(), io::Error> {
|
||||||
|
self.irq.trigger(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusDevice for Pl011 {
|
||||||
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
let v;
|
||||||
|
let mut read_ok = true;
|
||||||
|
if (AMBA_ID_LOW..AMBA_ID_HIGH).contains(&(offset >> 2)) {
|
||||||
|
let index = ((offset - 0xfe0) >> 2) as usize;
|
||||||
|
v = u32::from(PL011_ID[index]);
|
||||||
|
} else {
|
||||||
|
v = match offset >> 2 {
|
||||||
|
UARTDR => {
|
||||||
|
let c: u32;
|
||||||
|
let r: u32;
|
||||||
|
|
||||||
|
self.flags &= !PL011_FLAG_RXFF;
|
||||||
|
c = self.read_fifo.pop_front().unwrap_or_default().into();
|
||||||
|
if self.read_count > 0 {
|
||||||
|
self.read_count -= 1;
|
||||||
|
}
|
||||||
|
if self.read_count == 0 {
|
||||||
|
self.flags |= PL011_FLAG_RXFE;
|
||||||
|
}
|
||||||
|
if self.read_count == (self.read_trigger - 1) {
|
||||||
|
self.int_level &= !PL011_INT_RX;
|
||||||
|
}
|
||||||
|
self.rsr = c >> 8;
|
||||||
|
r = c;
|
||||||
|
r
|
||||||
|
}
|
||||||
|
UARTRSR_UARTECR => self.rsr,
|
||||||
|
UARTFR => self.flags,
|
||||||
|
UARTILPR => self.ilpr,
|
||||||
|
UARTIBRD => self.ibrd,
|
||||||
|
UARTFBRD => self.fbrd,
|
||||||
|
UARTLCR_H => self.lcr,
|
||||||
|
UARTCR => self.cr,
|
||||||
|
UARTIFLS => self.ifl,
|
||||||
|
UARTIMSC => self.int_enabled,
|
||||||
|
UARTRIS => self.int_level,
|
||||||
|
UARTMIS => (self.int_level & self.int_enabled),
|
||||||
|
UARTDMACR => self.dmacr,
|
||||||
|
_ => {
|
||||||
|
read_ok = false;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if read_ok && data.len() <= 4 {
|
||||||
|
write_le_u32(data, v);
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Invalid PL011 read: offset {}, data length {}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
if data.len() <= 4 {
|
||||||
|
let v = read_le_u32(&data);
|
||||||
|
if let Err(e) = self.handle_write(offset, v) {
|
||||||
|
warn!("Failed to write to PL011 device: {}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Invalid PL011 write: offset {}, data length {}",
|
||||||
|
offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshottable for Pl011 {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
|
let snapshot =
|
||||||
|
serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?;
|
||||||
|
|
||||||
|
let mut pl011_snapshot = Snapshot::new(self.id.as_str());
|
||||||
|
pl011_snapshot.add_data_section(SnapshotDataSection {
|
||||||
|
id: format!("{}-section", self.id),
|
||||||
|
snapshot,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(pl011_snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||||
|
if let Some(pl011_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) {
|
||||||
|
let pl011_state = match serde_json::from_slice(&pl011_section.snapshot) {
|
||||||
|
Ok(state) => state,
|
||||||
|
Err(error) => {
|
||||||
|
return Err(MigratableError::Restore(anyhow!(
|
||||||
|
"Could not deserialize PL011 {}",
|
||||||
|
error
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_state(&pl011_state);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(MigratableError::Restore(anyhow!(
|
||||||
|
"Could not find the PL011 snapshot section"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pausable for Pl011 {}
|
||||||
|
impl Transportable for Pl011 {}
|
||||||
|
impl Migratable for Pl011 {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
const SERIAL_NAME: &str = "serial";
|
||||||
|
|
||||||
|
struct TestInterrupt {
|
||||||
|
event_fd: EventFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptSourceGroup for TestInterrupt {
|
||||||
|
fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
|
||||||
|
self.event_fd.write(1)
|
||||||
|
}
|
||||||
|
fn update(
|
||||||
|
&self,
|
||||||
|
_index: InterruptIndex,
|
||||||
|
_config: InterruptSourceConfig,
|
||||||
|
) -> result::Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
|
||||||
|
Some(self.event_fd.try_clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestInterrupt {
|
||||||
|
fn new(event_fd: EventFd) -> Self {
|
||||||
|
TestInterrupt { event_fd }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct SharedBuffer {
|
||||||
|
buf: Arc<Mutex<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedBuffer {
|
||||||
|
fn new() -> SharedBuffer {
|
||||||
|
SharedBuffer {
|
||||||
|
buf: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for SharedBuffer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.buf.lock().unwrap().write(buf)
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.buf.lock().unwrap().flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pl011_output() {
|
||||||
|
let intr_evt = EventFd::new(0).unwrap();
|
||||||
|
let pl011_out = SharedBuffer::new();
|
||||||
|
let mut pl011 = Pl011::new(
|
||||||
|
String::from(SERIAL_NAME),
|
||||||
|
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||||
|
Some(Box::new(pl011_out.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
pl011.write(0, UARTDR as u64, &[b'x', b'y']);
|
||||||
|
pl011.write(0, UARTDR as u64, &[b'a']);
|
||||||
|
pl011.write(0, UARTDR as u64, &[b'b']);
|
||||||
|
pl011.write(0, UARTDR as u64, &[b'c']);
|
||||||
|
assert_eq!(
|
||||||
|
pl011_out.buf.lock().unwrap().as_slice(),
|
||||||
|
&[b'x', b'a', b'b', b'c']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pl011_input() {
|
||||||
|
let intr_evt = EventFd::new(0).unwrap();
|
||||||
|
let pl011_out = SharedBuffer::new();
|
||||||
|
let mut pl011 = Pl011::new(
|
||||||
|
String::from(SERIAL_NAME),
|
||||||
|
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||||
|
Some(Box::new(pl011_out)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
||||||
|
// counter doesn't change (for 0 it blocks)
|
||||||
|
assert!(intr_evt.write(1).is_ok());
|
||||||
|
pl011.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
|
|
||||||
|
let mut data = [0u8];
|
||||||
|
pl011.read(0, UARTDR as u64, &mut data);
|
||||||
|
assert_eq!(data[0], b'a');
|
||||||
|
pl011.read(0, UARTDR as u64, &mut data);
|
||||||
|
assert_eq!(data[0], b'b');
|
||||||
|
pl011.read(0, UARTDR as u64, &mut data);
|
||||||
|
assert_eq!(data[0], b'c');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ pub mod ioapic;
|
|||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
|
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
pub use self::acpi::{AcpiGEDDevice, AcpiPMTimerDevice, AcpiShutdownDevice};
|
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct AcpiNotificationFlags: u8 {
|
pub struct AcpiNotificationFlags: u8 {
|
||||||
@@ -46,3 +46,63 @@ bitflags! {
|
|||||||
const POWER_BUTTON_CHANGED = 0b1000;
|
const POWER_BUTTON_CHANGED = 0b1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_macros)]
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
macro_rules! generate_read_fn {
|
||||||
|
($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn $fn_name(input: &[$byte_type]) -> $data_type {
|
||||||
|
assert!($type_size == std::mem::size_of::<$data_type>());
|
||||||
|
let mut array = [0u8; $type_size];
|
||||||
|
for (byte, read) in array.iter_mut().zip(input.iter().cloned()) {
|
||||||
|
*byte = read as u8;
|
||||||
|
}
|
||||||
|
<$data_type>::$endian_type(array)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_macros)]
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
macro_rules! generate_write_fn {
|
||||||
|
($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) {
|
||||||
|
for (byte, read) in buf
|
||||||
|
.iter_mut()
|
||||||
|
.zip(<$data_type>::$endian_type(n).iter().cloned())
|
||||||
|
{
|
||||||
|
*byte = read as $byte_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_write_fn!(write_le_u16, u16, u8, to_le_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_write_fn!(write_le_u32, u32, u8, to_le_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_write_fn!(write_le_u64, u64, u8, to_le_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_write_fn!(write_le_i32, i32, i8, to_le_bytes);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_write_fn!(write_be_u16, u16, u8, to_be_bytes);
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
generate_write_fn!(write_be_u32, u32, u8, to_be_bytes);
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ This document describes the device model supported by `cloud-hypervisor`.
|
|||||||
### Serial port
|
### Serial port
|
||||||
|
|
||||||
Simple emulation of a serial port by reading and writing to specific port I/O
|
Simple emulation of a serial port by reading and writing to specific port I/O
|
||||||
addresses. Used as the default console for Linux when booting with the option
|
addresses. The serial port can be very useful to gather early logs from the
|
||||||
`console=ttyS0`, the serial port can be very useful to gather early logs from
|
operating system booted inside the VM.
|
||||||
the operating system booted inside the VM.
|
|
||||||
|
For x86_64, The default serial port is from an emulated 16550A device. It can
|
||||||
|
be used as the default console for Linux when booting with the option
|
||||||
|
`console=ttyS0`. For AArch64, the default serial port is from an emulated
|
||||||
|
PL011 UART device. The related command line for AArch64 is `console=ttyAMA0`.
|
||||||
|
|
||||||
This device is always built-in, and it is disabled by default. It can be
|
This device is always built-in, and it is disabled by default. It can be
|
||||||
enabled with the `--serial` option, as long as its parameter is not `off`.
|
enabled with the `--serial` option, as long as its parameter is not `off`.
|
||||||
@@ -44,6 +48,10 @@ This device is built-in by default, but it can be compiled out with Rust
|
|||||||
features. When compiled in, it is always enabled, and cannot be disabled
|
features. When compiled in, it is always enabled, and cannot be disabled
|
||||||
from the command line.
|
from the command line.
|
||||||
|
|
||||||
|
For AArch64 machines, an ARM PrimeCell Real Time Clock(PL031) is implemented.
|
||||||
|
This device is built-in by default for the AArch64 platform, and it is always
|
||||||
|
enabled, and cannot be disabled from the command line.
|
||||||
|
|
||||||
### I/O APIC
|
### I/O APIC
|
||||||
|
|
||||||
`cloud-hypervisor` supports a so-called split IRQ chip implementation by
|
`cloud-hypervisor` supports a so-called split IRQ chip implementation by
|
||||||
@@ -65,6 +73,11 @@ enabled by default, the handling of reboot/shutdown goes through the dedicated
|
|||||||
ACPI device. In case ACPI is disabled, this device is enabled to bring to the
|
ACPI device. In case ACPI is disabled, this device is enabled to bring to the
|
||||||
VM some reboot/shutdown support.
|
VM some reboot/shutdown support.
|
||||||
|
|
||||||
|
### ARM PrimeCell General Purpose Input/Output (PL061)
|
||||||
|
|
||||||
|
Simplified ARM PrimeCell GPIO (PL061) implementation. Only supports key 3 to
|
||||||
|
trigger a graceful shutdown of the AArch64 guest.
|
||||||
|
|
||||||
### ACPI device
|
### ACPI device
|
||||||
|
|
||||||
This is a dedicated device for handling ACPI shutdown and reboot when ACPI is
|
This is a dedicated device for handling ACPI shutdown and reboot when ACPI is
|
||||||
|
|||||||
149
docs/hotplug.md
149
docs/hotplug.md
@@ -27,7 +27,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
|||||||
--memory size=1024M \
|
--memory size=1024M \
|
||||||
--net "tap=,mac=,ip=,mask=" \
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
--rng \
|
--rng \
|
||||||
--api-socket=/tmp/ch-socket
|
--api-socket=/tmp/ch-socket
|
||||||
$ popd
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ Notice the addition of `--api-socket=/tmp/ch-socket` and a `max` parameter on `-
|
|||||||
To ask the VMM to add additional vCPUs then use the resize API:
|
To ask the VMM to add additional vCPUs then use the resize API:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H "Accept: application/json" -H "Content-Type: application/json" -i -XPUT --unix-socket /tmp/ch-socket -d "{ \"desired_vcpus\":8}" http://localhost/api/v1/vm.resize
|
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 8
|
||||||
```
|
```
|
||||||
|
|
||||||
The extra vCPU threads will be created and advertised to the running kernel. The kernel does not bring up the CPUs immediately and instead the user must "online" them from inside the VM:
|
The extra vCPU threads will be created and advertised to the running kernel. The kernel does not bring up the CPUs immediately and instead the user must "online" them from inside the VM:
|
||||||
@@ -56,19 +56,23 @@ After a reboot the added CPUs will remain.
|
|||||||
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the reisze API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
|
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the reisze API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H "Accept: application/json" -H "Content-Type: application/json" -i -XPUT --unix-socket /tmp/ch-socket -d "{ \"desired_vcpus\":2}" http://localhost/api/v1/vm.resize
|
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 2
|
||||||
```
|
```
|
||||||
|
|
||||||
As per adding CPUs to the guest, after a reboot the VM will be running with the reduced number of vCPUs.
|
As per adding CPUs to the guest, after a reboot the VM will be running with the reduced number of vCPUs.
|
||||||
|
|
||||||
## Memory Hot Plug
|
## Memory Hot Plug
|
||||||
|
|
||||||
|
### 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.
|
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.
|
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.
|
||||||
|
|
||||||
To use memory hotplug start the VM specifying some size RAM in the "hotplug_size" parameter to the memory configuration. Not all the memory specified in this parameter will be available to hotplug as there are spacing and alignment requirements so it is recommended to make it larger than the hotplug RAM needed.
|
To use memory hotplug start the VM specifying some size RAM in the `hotplug_size` parameter to the memory configuration. Not all the memory specified in this parameter will be available to hotplug as there are spacing and alignment requirements so it is recommended to make it larger than the hotplug RAM needed.
|
||||||
|
|
||||||
|
Because the ACPI method is the default, there is no need to add the extra option `hotplug_method=acpi`.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ pushd $CLOUDH
|
$ pushd $CLOUDH
|
||||||
@@ -81,7 +85,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
|||||||
--memory size=1024M,hotplug_size=8192M \
|
--memory size=1024M,hotplug_size=8192M \
|
||||||
--net "tap=,mac=,ip=,mask=" \
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
--rng \
|
--rng \
|
||||||
--api-socket=/tmp/ch-socket
|
--api-socket=/tmp/ch-socket
|
||||||
$ popd
|
$ popd
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -91,10 +95,10 @@ Before issuing the API request it is necessary to run the following command insi
|
|||||||
root@ch-guest ~ # echo online | sudo tee /sys/devices/system/memory/auto_online_blocks
|
root@ch-guest ~ # echo online | sudo tee /sys/devices/system/memory/auto_online_blocks
|
||||||
```
|
```
|
||||||
|
|
||||||
To ask the VMM to add expand the RAM for the VM (request is in bytes):
|
To ask the VMM to expand the RAM for the VM:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H "Accept: application/json" -H "Content-Type: application/json" -i -XPUT --unix-socket /tmp/ch-socket -d "{ \"desired_vcpus\": 4, \"desired_ram\" : 3221225472}" http://localhost/api/v1/vm.resize
|
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
|
||||||
```
|
```
|
||||||
|
|
||||||
The new memory is now available to use inside the VM:
|
The new memory is now available to use inside the VM:
|
||||||
@@ -111,3 +115,134 @@ Due to guest OS limitations is is necessary to ensure that amount of memory adde
|
|||||||
The same API can also be used to reduce the desired RAM for a VM but the change will not be applied until the VM is rebooted.
|
The same API can also be used to reduce the desired RAM for a VM but the change will not be applied until the VM is rebooted.
|
||||||
|
|
||||||
Memory and CPU resizing can be combined together into the same HTTP API request.
|
Memory and CPU resizing can be combined together into the same HTTP API request.
|
||||||
|
|
||||||
|
### virtio-mem method
|
||||||
|
|
||||||
|
Extra memory can be added and removed 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.
|
||||||
|
|
||||||
|
To use memory hotplug start the VM specifying some size RAM in the `hotplug_size` parameter along with `hotplug_method=virtio-mem` to the memory configuration.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ pushd $CLOUDH
|
||||||
|
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
||||||
|
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||||
|
--kernel custom-vmlinux.bin \
|
||||||
|
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
||||||
|
--disk path=focal-server-cloudimg-amd64.raw \
|
||||||
|
--memory size=1024M,hotplug_size=8192M,hotplug_method=virtio-mem \
|
||||||
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
|
--api-socket=/tmp/ch-socket
|
||||||
|
$ popd
|
||||||
|
```
|
||||||
|
|
||||||
|
To ask the VMM to expand the RAM for the VM (request is in bytes):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
|
||||||
|
```
|
||||||
|
|
||||||
|
The new memory is now available to use inside the VM:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
free -h
|
||||||
|
total used free shared buff/cache available
|
||||||
|
Mem: 3.0Gi 71Mi 2.8Gi 0.0Ki 47Mi 2.8Gi
|
||||||
|
Swap: 32Mi 0B 32Mi
|
||||||
|
```
|
||||||
|
|
||||||
|
The same API can also be used to reduce the desired RAM for a VM. It is important to note that reducing RAM size might only partially work, as the guest might be using some of it.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
To use PCI device hotplug start the VM with the HTTP server.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor/target/release/cloud-hypervisor
|
||||||
|
$ ./cloud-hypervisor/target/release/cloud-hypervisor \
|
||||||
|
--kernel custom-vmlinux.bin \
|
||||||
|
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
|
||||||
|
--disk path=focal-server-cloudimg-amd64.raw \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
|
--api-socket=/tmp/ch-socket
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice the addition of `--api-socket=/tmp/ch-socket`.
|
||||||
|
|
||||||
|
### Add VFIO Device
|
||||||
|
|
||||||
|
To ask the VMM to add additional VFIO device then use the `add-device` API.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket add-device path=/sys/bus/pci/devices/0000:01:00.0/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Disk Device
|
||||||
|
|
||||||
|
To ask the VMM to add additional disk device then use the `add-disk` API.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket add-disk path=/foo/bar/cloud.img
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Fs Device
|
||||||
|
|
||||||
|
To ask the VMM to add additional fs device then use the `add-fs` API.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket add-fs tag=myfs,socket=/foo/bar/virtiofs.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Net Device
|
||||||
|
|
||||||
|
To ask the VMM to add additional network device then use the `add-net` API.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket add-net tap=chtap0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Pmem Device
|
||||||
|
|
||||||
|
To ask the VMM to add additional PMEM device then use the `add-pmem` API.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket add-pmem file=/foo/bar.cloud.img
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Vsock Device
|
||||||
|
|
||||||
|
To ask the VMM to add additional vsock device then use the `add-vsock` API.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket add-vsock cid=3,socket=/foo/bar/vsock.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Across All PCI Devices
|
||||||
|
|
||||||
|
The extra PCI device will be created and advertised to the running kernel. The new device can be found by checking the list of PCI devices.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
root@ch-guest ~ # lspci
|
||||||
|
00:00.0 Host bridge: Intel Corporation Device 0d57
|
||||||
|
00:01.0 Unassigned class [ffff]: Red Hat, Inc. Virtio console (rev 01)
|
||||||
|
00:02.0 Mass storage controller: Red Hat, Inc. Virtio block device (rev 01)
|
||||||
|
00:03.0 Unassigned class [ffff]: Red Hat, Inc. Virtio RNG (rev 01)
|
||||||
|
```
|
||||||
|
|
||||||
|
After a reboot the added PCI device will remain.
|
||||||
|
|
||||||
|
### Remove PCI device
|
||||||
|
|
||||||
|
Removing a PCI device works the same way for all kind of PCI devices. The unique identifier related to the device must be provided. This identifier can be provided by the user when adding the new device, or by default Cloud Hypervisor will assign one.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./ch-remote --api-socket=/tmp/ch-socket remove-device _disk0
|
||||||
|
```
|
||||||
|
|
||||||
|
As per adding a PCI device to the guest, after a reboot the VM will be running without the removed PCI device.
|
||||||
|
|||||||
41
docs/io_throttling.md
Normal file
41
docs/io_throttling.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# I/O Throttling
|
||||||
|
|
||||||
|
Cloud Hypervisor now supports I/O throttling on virtio-block
|
||||||
|
devices. This support is based on the [`rate-limiter` module](https://github.com/firecracker-microvm/firecracker/tree/master/src/rate_limiter)
|
||||||
|
from Firecracker. This document explains the user interface of this
|
||||||
|
feature, and highlights some internal implementations that can help users
|
||||||
|
better understand the expected behavior of I/O throttling in practice.
|
||||||
|
|
||||||
|
Cloud Hypervisor allows to limit both the I/O bandwidth (e.g. bytes/s)
|
||||||
|
and I/O operations (ops/s) independently. To limit the I/O bandwidth, it
|
||||||
|
provides three user options, i.e., `bw_size` (bytes), `bw_one_time_burst`
|
||||||
|
(bytes), and `bw_refill_time` (ms). Both `bw_size` and `bw_refill_time`
|
||||||
|
are required, while `bw_one_time_burst` is optional.
|
||||||
|
Internally, these options define a TokenBucket with a maximum capacity
|
||||||
|
(`bw_size` bytes), an initial burst size (`bw_one_time_burst`) and an
|
||||||
|
interval for refilling purposes (`bw_refill_time`). The "refill-rate" is
|
||||||
|
`bw_size` bytes per `bw_refill_time` ms, and it is the constant rate at
|
||||||
|
which the tokens replenish. The refill process only starts happening
|
||||||
|
after the initial burst budget is consumed. Consumption from the token
|
||||||
|
bucket is unbounded in speed which allows for bursts bound in size by
|
||||||
|
the amount of tokens available. Once the token bucket is empty,
|
||||||
|
consumption speed is bound by the "refill-rate". Similarly, Cloud
|
||||||
|
Hypervisor provides another three options for limiting I/O operations,
|
||||||
|
i.e., `ops_size` (I/O operations), `bw_one_time_burst` (I/O operations),
|
||||||
|
and `bw_refill_time` (ms).
|
||||||
|
|
||||||
|
One caveat in the I/O throttling is that every-time the bucket gets
|
||||||
|
empty, it will stop I/O operations for a fixed amount of time
|
||||||
|
(`cool_down_time`). The `cool_down_time` now is fixed at `100 ms`, it
|
||||||
|
can have big implications to the actual rate limit (which can be a lot
|
||||||
|
different the expected "refill-rate" derived from user inputs). For
|
||||||
|
example, to have a 1000 IOPS limit, users should be able to provide
|
||||||
|
either of the following two options:
|
||||||
|
`ops_size=1000,ops_refill_time=1000` or
|
||||||
|
`ops_size=10,ops_refill_time=10`. However, the actual IOPS limits are
|
||||||
|
likely to be ~1000 IOPS and ~100 IOPS respectively. The reason is the
|
||||||
|
actual rate limit users get can be as low as
|
||||||
|
`ops_size/(ops_refill_time+cool_down_time)`. As a result, it is
|
||||||
|
generally advisable to keep `bw/ops_refill_time` larger than `100 ms`
|
||||||
|
(`cool_down_time`) to make sure the actual rate limit is close to users'
|
||||||
|
expectation ("refill-rate").
|
||||||
68
docs/seccomp.md
Normal file
68
docs/seccomp.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Seccomp filtering
|
||||||
|
|
||||||
|
As a means to harden Cloud Hypervisor's security, the project leverages seccomp
|
||||||
|
filtering.
|
||||||
|
|
||||||
|
## What is seccomp filtering
|
||||||
|
|
||||||
|
A seccomp filter is a way for a process to tell the kernel which system calls
|
||||||
|
are authorized.
|
||||||
|
In case this process calls into a prohibited system call, the kernel will kill
|
||||||
|
the process right away.
|
||||||
|
|
||||||
|
## How does it apply to Cloud Hypervisor
|
||||||
|
|
||||||
|
Cloud Hypervisor is a multi threaded application. It spawns dedicated threads
|
||||||
|
for virtual CPUs, virtio devices and HTTP server, along with the main thread
|
||||||
|
representing the VMM.
|
||||||
|
|
||||||
|
Each of these threads has a limited scope of what it is expected to perform,
|
||||||
|
which is why different filters are applied to each of them.
|
||||||
|
|
||||||
|
By default, Cloud Hypervisor enables seccomp filtering as the project believes
|
||||||
|
that security should not be an option.
|
||||||
|
|
||||||
|
For development and debugging purposes, one might want to disable this feature
|
||||||
|
or log the faulty system call.
|
||||||
|
|
||||||
|
### Disabling seccomp filters
|
||||||
|
|
||||||
|
Append `--seccomp false` to Cloud Hypervisor's command line to prevent seccomp
|
||||||
|
filtering from being applied.
|
||||||
|
|
||||||
|
### Logging prohibited system calls
|
||||||
|
|
||||||
|
In the context of debug, one alternative to disabling seccomp filtering is to
|
||||||
|
log faulty system calls that would have caused the application to be killed by
|
||||||
|
the kernel.
|
||||||
|
|
||||||
|
Append `--seccomp log` to Cloud Hypervisor's command line to enable faulty
|
||||||
|
system calls to be logged.
|
||||||
|
|
||||||
|
The kernel running on the host machine must have the `audit` parameter enabled.
|
||||||
|
If this is not the case, update kernel boot options by appending `audit=1`.
|
||||||
|
|
||||||
|
Unauthorized system calls will be logged to the journal similarly to the
|
||||||
|
following example
|
||||||
|
|
||||||
|
```
|
||||||
|
type=SECCOMP msg=audit(1423263412.694:7878): auid=1000 uid=1000 gid=1000 ses=3 subj=unconfined_u:unconfined_r:cloud_hypervisor:s0-s0:c0.c1023 pid=1193 comm="cloud-hypervisor" exe="/usr/bin/cloud-hypervisor" sig=0 arch=c000003e syscall=47 compat=0 ip=0x7f4f63982604 code=0x50000
|
||||||
|
```
|
||||||
|
|
||||||
|
Provided `ausyscall` has been installed on the host, the system call can be
|
||||||
|
identified with
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ausyscall 47
|
||||||
|
recvmsg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Further debug with `strace`
|
||||||
|
|
||||||
|
One more way of debugging seccomp related issues is to use the `strace` tool as
|
||||||
|
it will log every system call issued by the process. It is important to use
|
||||||
|
`-f` option in order to trace each and every thread belonging to the process.
|
||||||
|
|
||||||
|
```
|
||||||
|
strace -f ./cloud-hypervisor ...
|
||||||
|
```
|
||||||
92
docs/virtiofs-root.md
Normal file
92
docs/virtiofs-root.md
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# HOWTO VirtioFS rootfs
|
||||||
|
|
||||||
|
A quick guide for using virtiofs as a cloud-hypervisor guest's rootfs (i.e.
|
||||||
|
with no root block device). This document is a quick getting started guide.
|
||||||
|
There are many more steps to take to make this a production ready, secure
|
||||||
|
setup.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. virtiofsd from the qemu project
|
||||||
|
* We are using the Qemu version for now
|
||||||
|
* There is a Rust version being worked on that may be a better option in the future
|
||||||
|
* Part of the qemu-system-common package on Ubuntu
|
||||||
|
* Part of the qemu-common package on Fedora
|
||||||
|
2. cloud-hypervisor - the newer the better, but I tested with 0.12
|
||||||
|
3. a rootfs - This howto uses an alpine rootfs available here:
|
||||||
|
* https://dl-cdn.alpinelinux.org/alpine/v3.13/releases/x86_64/alpine-minirootfs-3.13.2-x86_64.tar.gz
|
||||||
|
* Others should work
|
||||||
|
|
||||||
|
## To create the VM rootfs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir rootfs/
|
||||||
|
cd rootfs
|
||||||
|
# this needs sudo to be able to set root permissions on fs components
|
||||||
|
sudo tar -xf /path/to/alpine-minirootfs-3.13.1-x86_64.tar.gz
|
||||||
|
# this will get created when the VM actually boots by the dhcp client
|
||||||
|
# but we need it in the chroot to download packages
|
||||||
|
sudo cp /etc/resolv.conf etc/
|
||||||
|
# the alpine mini rootfs is meant for docker containers, we need a few extra
|
||||||
|
# things for a working rootfs
|
||||||
|
sudo chroot $PWD apk add openrc busybox-initscripts
|
||||||
|
# we are using the paravirt console in cloud-hypervisor, so enable it in init
|
||||||
|
# append it after the other console since it doesn't work just appending it
|
||||||
|
sudo sed -i '/vt100/a \n# paravirt console\nhvc0::respawn:/sbin/getty -L hvc0 115200 vt100' etc/inittab
|
||||||
|
# set no password for root user... you obviously don't want to do this for
|
||||||
|
# any sort of production setup
|
||||||
|
sudo sed -i 's/root:!::0:::::/root:::0:::::/' etc/shadow
|
||||||
|
# set up init scripts
|
||||||
|
for i in acpid crond
|
||||||
|
sudo ln -sf /etc/init.d/$i etc/runlevels/default/$i
|
||||||
|
end
|
||||||
|
for i in bootmisc hostname hwclock loadkmap modules networking swap sysctl syslog urandom
|
||||||
|
sudo ln -sf /etc/init.d/$i etc/runlevels/boot/$i
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in killprocs mount-ro savecache
|
||||||
|
sudo ln -sf /etc/init.d/$i etc/runlevels/shutdown/$i
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in devfs dmesg hwdrivers mdev
|
||||||
|
sudo ln -sf /etc/init.d/$i etc/runlevels/sysinit/$i
|
||||||
|
end
|
||||||
|
# setup network config
|
||||||
|
echo 'auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
auto eth0
|
||||||
|
iface eth0 inet dhcp
|
||||||
|
' | sudo tee etc/network/interfaces
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## To run the VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# starting in the directory above rootfs
|
||||||
|
sudo virtiofsd --socket-path=$PWD/virtiofs-rootfs.sock -o source=$PWD/rootfs -o cache=none &
|
||||||
|
sudo cloud-hypervisor \
|
||||||
|
--cpus boot=1,max=1 \
|
||||||
|
--kernel vmlinux \
|
||||||
|
--fs tag=/dev/root,socket=$PWD/virtiofs-rootfs.sock \
|
||||||
|
--memory size=2G,shared=on \
|
||||||
|
--cmdline "console=hvc0 rootfstype=virtiofs root=/dev/root ro debug" \
|
||||||
|
--api-socket $PWD/ch.sock \
|
||||||
|
--rng \
|
||||||
|
--net ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: an important part of the above is the `tag=/dev/root` and
|
||||||
|
`root=/dev/root` parts. For whatever reason, it would only work with that as
|
||||||
|
the tag.
|
||||||
|
|
||||||
|
Note: another important bit is that the memory is shared. This is required for
|
||||||
|
virtiofs
|
||||||
|
|
||||||
|
## Message from the author
|
||||||
|
|
||||||
|
If you find any issues or have suggestions, feel free to reach out to @iggy on
|
||||||
|
the cloud-hypervisor slack. Also if this works for you, I'd like to know as
|
||||||
|
well. It would also be nice to get steps for preparing other distribution root
|
||||||
|
filesystems.
|
||||||
11
event_monitor/Cargo.toml
Normal file
11
event_monitor/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "event_monitor"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2.91"
|
||||||
|
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||||
|
serde_derive = ">=1.0.27"
|
||||||
|
serde_json = ">=1.0.9"
|
||||||
77
event_monitor/src/lib.rs
Normal file
77
event_monitor/src/lib.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
static mut MONITOR: Option<(File, Instant)> = None;
|
||||||
|
|
||||||
|
/// This function must only be called once from the main process before any threads
|
||||||
|
/// are created to avoid race conditions
|
||||||
|
pub fn set_monitor(file: File) -> Result<(), std::io::Error> {
|
||||||
|
assert!(unsafe { MONITOR.is_none() });
|
||||||
|
let fd = file.as_raw_fd();
|
||||||
|
let ret = unsafe {
|
||||||
|
let mut flags = libc::fcntl(fd, libc::F_GETFL);
|
||||||
|
flags |= libc::O_NONBLOCK;
|
||||||
|
libc::fcntl(fd, libc::F_SETFL, flags)
|
||||||
|
};
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(std::io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
MONITOR = Some((file, Instant::now()));
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Event<'a> {
|
||||||
|
timestamp: Duration,
|
||||||
|
source: &'a str,
|
||||||
|
event: &'a str,
|
||||||
|
properties: Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
||||||
|
if let Some((file, start)) = unsafe { MONITOR.as_ref() } {
|
||||||
|
let e = Event {
|
||||||
|
timestamp: start.elapsed(),
|
||||||
|
source,
|
||||||
|
event,
|
||||||
|
properties,
|
||||||
|
};
|
||||||
|
serde_json::to_writer_pretty(file, &e).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Through the use of Cow<'a, str> it is possible to use String as well as
|
||||||
|
&str as the parameters:
|
||||||
|
e.g.
|
||||||
|
event!("cpu_manager", "create_vcpu", "id", cpu_id.to_string());
|
||||||
|
*/
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! event {
|
||||||
|
($source:expr, $event:expr) => {
|
||||||
|
$crate::event_log($source, $event, None)
|
||||||
|
};
|
||||||
|
($source:expr, $event:expr, $($key:expr, $value:expr),*) => {
|
||||||
|
{
|
||||||
|
let mut properties = ::std::collections::HashMap::new();
|
||||||
|
$(
|
||||||
|
properties.insert($key.into(), $value.into());
|
||||||
|
)+
|
||||||
|
$crate::event_log($source, $event, Some(&properties))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
765
fuzz/Cargo.lock
generated
Normal file
765
fuzz/Cargo.lock
generated
Normal file
@@ -0,0 +1,765 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "acpi_tables"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"vm-memory",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "api_client"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "698b65a961a9d730fb45b6b0327e20207810c9f61ee421b082b27ba003f49e2b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arc-swap"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4d7d63395147b81a9e570bcc6243aaf71c017bd666d4909cfef0085bdda8d73"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arch"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"acpi_tables",
|
||||||
|
"anyhow",
|
||||||
|
"arch_gen",
|
||||||
|
"byteorder",
|
||||||
|
"hypervisor",
|
||||||
|
"libc",
|
||||||
|
"linux-loader",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-migration",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arch_gen"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block_util"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"io-uring",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"qcow",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"virtio-bindings",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-virtio",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"term_size",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloud-hypervisor"
|
||||||
|
version = "0.13.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"api_client",
|
||||||
|
"clap",
|
||||||
|
"epoll",
|
||||||
|
"event_monitor",
|
||||||
|
"hypervisor",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"option_parser",
|
||||||
|
"seccomp",
|
||||||
|
"serde_json",
|
||||||
|
"signal-hook",
|
||||||
|
"thiserror",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloud-hypervisor-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"block_util",
|
||||||
|
"cloud-hypervisor",
|
||||||
|
"libc",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"qcow",
|
||||||
|
"seccomp",
|
||||||
|
"virtio-devices",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-virtio",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "devices"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"acpi_tables",
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"byteorder",
|
||||||
|
"epoll",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"vm-device",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-migration",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "epoll"
|
||||||
|
version = "4.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event_monitor"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hypervisor"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"epoll",
|
||||||
|
"iced-x86",
|
||||||
|
"kvm-bindings",
|
||||||
|
"kvm-ioctls",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iced-x86"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4261b15a556aa9b83b976b7aa4613f5a3b80351683af14c5d8d7e62c00869d5a"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-uring"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb82832e05cc4ca298f198a8db108837b4f7b7b1248e3cba8e48f151aece80cf"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kvm-bindings"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/cloud-hypervisor/kvm-bindings?branch=ch-v0.4.0#1a68725639283e622f4bb64584885b30bfe8be44"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kvm-ioctls"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74058b16912c6723db02d4ca3ec919b73cd41512ccd3b6202cf91ae8d6c9dce5"
|
||||||
|
dependencies = [
|
||||||
|
"kvm-bindings",
|
||||||
|
"libc",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.91"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-loader"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c819cc8275b0f2c1ed9feec455ca288b45d82932384a6a5f7a86812ee3427459"
|
||||||
|
dependencies = [
|
||||||
|
"vm-memory",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "micro_http"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=master#59ab64440a95f7b16253fef67b5201c2ec4adaf7"
|
||||||
|
dependencies = [
|
||||||
|
"epoll",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "net_gen"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "net_util"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"epoll",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"net_gen",
|
||||||
|
"serde",
|
||||||
|
"virtio-bindings",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-virtio",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option_parser"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pci"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
|
"hypervisor",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"vfio-bindings",
|
||||||
|
"vfio-ioctls",
|
||||||
|
"vm-allocator",
|
||||||
|
"vm-device",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-migration",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qcow"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"remain",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remain"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seccomp"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/firecracker-microvm/firecracker?tag=v0.22.0#cc5387637c132e500b4857b80b5a239d8d732b77"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.125"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.125"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6aa894ef3fade0ee7243422f4fbbd6c2b48e6de767e621d37ef65f2310f53cea"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "term_size"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"term_size",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vfio-bindings"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7"
|
||||||
|
dependencies = [
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vfio-ioctls"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/rust-vmm/vfio-ioctls?branch=master#a87b13bdec026e8144b91f30f35451a966d8c1ca"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"kvm-bindings",
|
||||||
|
"kvm-ioctls",
|
||||||
|
"log",
|
||||||
|
"vfio-bindings",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vhost"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/rust-vmm/vhost?branch=master#ee3e8722706c984b3dfe12d3a130e92101b78e8f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtio-bindings"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtio-devices"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"arc-swap",
|
||||||
|
"block_util",
|
||||||
|
"byteorder",
|
||||||
|
"epoll",
|
||||||
|
"event_monitor",
|
||||||
|
"io-uring",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"net_gen",
|
||||||
|
"net_util",
|
||||||
|
"pci",
|
||||||
|
"seccomp",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"vhost",
|
||||||
|
"virtio-bindings",
|
||||||
|
"vm-allocator",
|
||||||
|
"vm-device",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-migration",
|
||||||
|
"vm-virtio",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vm-allocator"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arch",
|
||||||
|
"libc",
|
||||||
|
"vm-memory",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vm-device"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"vfio-ioctls",
|
||||||
|
"vm-memory",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vm-memory"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "625f401b1b8b3ac3d43f53903cd138cfe840bd985f8581e553027b31d2bb8ae8"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vm-migration"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"vm-memory",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vm-virtio"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"virtio-bindings",
|
||||||
|
"vm-memory",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vmm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"acpi_tables",
|
||||||
|
"anyhow",
|
||||||
|
"arc-swap",
|
||||||
|
"arch",
|
||||||
|
"bitflags",
|
||||||
|
"block_util",
|
||||||
|
"clap",
|
||||||
|
"devices",
|
||||||
|
"epoll",
|
||||||
|
"event_monitor",
|
||||||
|
"hypervisor",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"linux-loader",
|
||||||
|
"log",
|
||||||
|
"micro_http",
|
||||||
|
"net_util",
|
||||||
|
"option_parser",
|
||||||
|
"pci",
|
||||||
|
"qcow",
|
||||||
|
"seccomp",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"signal-hook",
|
||||||
|
"thiserror",
|
||||||
|
"vfio-ioctls",
|
||||||
|
"virtio-devices",
|
||||||
|
"vm-allocator",
|
||||||
|
"vm-device",
|
||||||
|
"vm-memory",
|
||||||
|
"vm-migration",
|
||||||
|
"vm-virtio",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vmm-sys-util"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
@@ -10,15 +10,18 @@ cargo-fuzz = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
libc = "0.2.72"
|
libc = "0.2.90"
|
||||||
libfuzzer-sys = "0.3"
|
libfuzzer-sys = "0.4"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
|
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = "0.8.0"
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.5.0"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"] }
|
||||||
|
|
||||||
[dependencies.cloud-hypervisor]
|
[dependencies.cloud-hypervisor]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ fuzz_target!(|bytes| {
|
|||||||
2,
|
2,
|
||||||
256,
|
256,
|
||||||
SeccompAction::Allow,
|
SeccompAction::Allow,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -6,21 +6,20 @@ edition = "2018"
|
|||||||
license = "Apache-2.0 OR BSD-3-Clause"
|
license = "Apache-2.0 OR BSD-3-Clause"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
kvm = []
|
kvm = ["kvm-ioctls", "kvm-bindings"]
|
||||||
mshv = []
|
mshv = ["mshv-ioctls", "mshv-bindings"]
|
||||||
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
|
kvm-ioctls = { version = "0.8.0", optional = true }
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.4.0", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
|
mshv-bindings = {git = "https://github.com/cloud-hypervisor/mshv", branch = "master", features = ["with-serde", "fam-wrappers"], optional = true }
|
||||||
mshv-bindings = {git = "https://github.com/cloud-hypervisor/mshv", branch = "master", features = ["with-serde", "fam-wrappers"] }
|
mshv-ioctls = { git = "https://github.com/cloud-hypervisor/mshv", branch = "master", optional = true}
|
||||||
mshv-ioctls = { git = "https://github.com/cloud-hypervisor/mshv", branch = "master" }
|
|
||||||
|
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = ">=1.0.27"
|
||||||
serde_json = ">=1.0.9"
|
serde_json = ">=1.0.9"
|
||||||
@@ -28,7 +27,7 @@ vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
|||||||
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
|
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||||
version = "1.10"
|
version = "1.11"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ pub enum PlatformError {
|
|||||||
#[error("Set CPU state failure: {0}")]
|
#[error("Set CPU state failure: {0}")]
|
||||||
SetCpuStateFailure(#[source] anyhow::Error),
|
SetCpuStateFailure(#[source] anyhow::Error),
|
||||||
|
|
||||||
#[error("Unmapped virtual address: {0}")]
|
#[error("Translate virtual address: {0}")]
|
||||||
UnmappedGVA(#[source] anyhow::Error),
|
TranslateVirtualAddress(#[source] anyhow::Error),
|
||||||
|
|
||||||
#[error("Unsupported CPU Mode: {0}")]
|
#[error("Unsupported CPU Mode: {0}")]
|
||||||
UnsupportedCpuMode(#[source] anyhow::Error),
|
UnsupportedCpuMode(#[source] anyhow::Error),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
//
|
//
|
||||||
// CMP-Compare Two Operands
|
// CMP-Compare Two Operands
|
||||||
@@ -229,7 +229,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x38, 0xc4]; // cmp ah,al
|
let insn = [0x38, 0xc4]; // cmp ah,al
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
@@ -243,7 +243,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x83, 0xf8, 0x64]; // cmp eax,100
|
let insn = [0x83, 0xf8, 0x64]; // cmp eax,100
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
@@ -257,7 +257,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x83, 0xf8, 0xff]; // cmp eax,-1
|
let insn = [0x83, 0xf8, 0xff]; // cmp eax,-1
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
@@ -272,7 +272,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x48, 0x39, 0xd8, 0x00, 0xc3]; // cmp rax,rbx + two bytes garbage
|
let insn = [0x48, 0x39, 0xd8, 0x00, 0xc3]; // cmp rax,rbx + two bytes garbage
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax), (Register::RBX, rbx)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax), (Register::RBX, rbx)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
@@ -295,7 +295,7 @@ mod tests {
|
|||||||
let rax = d.0;
|
let rax = d.0;
|
||||||
let rbx = d.1;
|
let rbx = d.1;
|
||||||
let insn = [0x48, 0x39, 0xd8]; // cmp rax,rbx
|
let insn = [0x48, 0x39, 0xd8]; // cmp rax,rbx
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
0x1000,
|
0x1000,
|
||||||
vec![(Register::RAX, rax), (Register::RBX, rbx)],
|
vec![(Register::RAX, rax), (Register::RBX, rbx)],
|
||||||
None,
|
None,
|
||||||
@@ -323,7 +323,7 @@ mod tests {
|
|||||||
let rax = d.0;
|
let rax = d.0;
|
||||||
let rbx = d.1;
|
let rbx = d.1;
|
||||||
let insn = [0x39, 0xd8]; // cmp eax,ebx
|
let insn = [0x39, 0xd8]; // cmp eax,ebx
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
0x1000,
|
0x1000,
|
||||||
vec![(Register::RAX, rax), (Register::RBX, rbx)],
|
vec![(Register::RAX, rax), (Register::RBX, rbx)],
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use iced_x86::*;
|
|||||||
|
|
||||||
pub mod cmp;
|
pub mod cmp;
|
||||||
pub mod mov;
|
pub mod mov;
|
||||||
|
pub mod movs;
|
||||||
|
|
||||||
fn get_op<T: CpuStateManager>(
|
fn get_op<T: CpuStateManager>(
|
||||||
insn: &Instruction,
|
insn: &Instruction,
|
||||||
@@ -130,7 +131,7 @@ fn memory_operand_address<T: CpuStateManager>(
|
|||||||
address += index;
|
address += index;
|
||||||
}
|
}
|
||||||
|
|
||||||
address += insn.memory_displacement() as u64;
|
address += insn.memory_displacement64();
|
||||||
|
|
||||||
// Translate to a linear address.
|
// Translate to a linear address.
|
||||||
state.linearize(insn.memory_segment(), address, write)
|
state.linearize(insn.memory_segment(), address, write)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ macro_rules! mov_rm_imm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! movzx {
|
macro_rules! movzx {
|
||||||
($src_op_size:ty, $dest_op_size:ty) => {
|
($dest_op_size:ty, $src_op_size:ty) => {
|
||||||
fn emulate(
|
fn emulate(
|
||||||
&self,
|
&self,
|
||||||
insn: &Instruction,
|
insn: &Instruction,
|
||||||
@@ -254,7 +254,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x48, 0x89, 0xd8];
|
let insn = [0x48, 0x89, 0xd8];
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RBX, rbx)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rax: u64 = vmm
|
let rax: u64 = vmm
|
||||||
@@ -272,7 +272,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
|
let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
|
||||||
let mut vmm = MockVMM::new(ip, vec![], None);
|
let mut vmm = MockVmm::new(ip, vec![], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rax: u64 = vmm
|
let rax: u64 = vmm
|
||||||
@@ -292,7 +292,7 @@ mod tests {
|
|||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let memory: [u8; 8] = target_rax.to_le_bytes();
|
let memory: [u8; 8] = target_rax.to_le_bytes();
|
||||||
let insn = [0x48, 0x8b, 0x04, 0x00];
|
let insn = [0x48, 0x8b, 0x04, 0x00];
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], Some((rax + rax, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], Some((rax + rax, &memory)));
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
rax = vmm
|
rax = vmm
|
||||||
@@ -310,7 +310,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0xb0, 0x11];
|
let insn = [0xb0, 0x11];
|
||||||
let mut vmm = MockVMM::new(ip, vec![], None);
|
let mut vmm = MockVmm::new(ip, vec![], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let al = vmm
|
let al = vmm
|
||||||
@@ -328,7 +328,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
|
let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
|
||||||
let mut vmm = MockVMM::new(ip, vec![], None);
|
let mut vmm = MockVmm::new(ip, vec![], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let eax = vmm
|
let eax = vmm
|
||||||
@@ -346,7 +346,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
|
let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
|
||||||
let mut vmm = MockVMM::new(ip, vec![], None);
|
let mut vmm = MockVmm::new(ip, vec![], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let rax: u64 = vmm
|
let rax: u64 = vmm
|
||||||
@@ -365,7 +365,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x88, 0x30];
|
let insn = [0x88, 0x30];
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
ip,
|
ip,
|
||||||
vec![(Register::RAX, rax), (Register::DH, dh.into())],
|
vec![(Register::RAX, rax), (Register::DH, dh.into())],
|
||||||
None,
|
None,
|
||||||
@@ -386,7 +386,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x89, 0x30];
|
let insn = [0x89, 0x30];
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
ip,
|
ip,
|
||||||
vec![(Register::RAX, rax), (Register::ESI, esi.into())],
|
vec![(Register::RAX, rax), (Register::ESI, esi.into())],
|
||||||
None,
|
None,
|
||||||
@@ -408,7 +408,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
|
let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
ip,
|
ip,
|
||||||
vec![(Register::RAX, rax), (Register::EDI, edi.into())],
|
vec![(Register::RAX, rax), (Register::EDI, edi.into())],
|
||||||
None,
|
None,
|
||||||
@@ -431,7 +431,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x8b, 0x40, 0x10];
|
let insn = [0x8b, 0x40, 0x10];
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
ip,
|
ip,
|
||||||
vec![(Register::RAX, rax)],
|
vec![(Register::RAX, rax)],
|
||||||
Some((rax + displacement, &memory)),
|
Some((rax + displacement, &memory)),
|
||||||
@@ -456,7 +456,7 @@ mod tests {
|
|||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x8a, 0x40, 0x10];
|
let insn = [0x8a, 0x40, 0x10];
|
||||||
let memory: [u8; 1] = al.to_le_bytes();
|
let memory: [u8; 1] = al.to_le_bytes();
|
||||||
let mut vmm = MockVMM::new(
|
let mut vmm = MockVmm::new(
|
||||||
ip,
|
ip,
|
||||||
vec![(Register::RAX, rax)],
|
vec![(Register::RAX, rax)],
|
||||||
Some((rax + displacement, &memory)),
|
Some((rax + displacement, &memory)),
|
||||||
@@ -485,7 +485,7 @@ mod tests {
|
|||||||
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
|
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
|
||||||
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
];
|
];
|
||||||
let mut vmm = MockVMM::new(ip, vec![], Some((rax + displacement, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
|
||||||
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
let rbx: u64 = vmm
|
let rbx: u64 = vmm
|
||||||
@@ -511,7 +511,7 @@ mod tests {
|
|||||||
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut vmm = MockVMM::new(ip, vec![], Some((rax + displacement, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
|
||||||
// Only run the first instruction.
|
// Only run the first instruction.
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
@@ -542,7 +542,7 @@ mod tests {
|
|||||||
0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
|
0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut vmm = MockVMM::new(ip, vec![], Some((rax + displacement, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
|
||||||
// Run the 2 first instructions.
|
// Run the 2 first instructions.
|
||||||
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
@@ -571,7 +571,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x0f, 0xb6, 0xc3];
|
let insn = [0x0f, 0xb6, 0xc3];
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::BX, bx as u64)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let eax: u64 = vmm
|
let eax: u64 = vmm
|
||||||
@@ -589,7 +589,7 @@ mod tests {
|
|||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x0f, 0xb6, 0xc7];
|
let insn = [0x0f, 0xb6, 0xc7];
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::BX, bx as u64)], None);
|
let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let eax: u64 = vmm
|
let eax: u64 = vmm
|
||||||
@@ -609,7 +609,7 @@ mod tests {
|
|||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let insn = [0x0f, 0xb7, 0x03];
|
let insn = [0x0f, 0xb7, 0x03];
|
||||||
let memory: [u8; 1] = value.to_le_bytes();
|
let memory: [u8; 1] = value.to_le_bytes();
|
||||||
let mut vmm = MockVMM::new(ip, vec![(Register::RBX, rbx)], Some((rbx, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], Some((rbx, &memory)));
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
let eax: u64 = vmm
|
let eax: u64 = vmm
|
||||||
|
|||||||
147
hypervisor/src/arch/x86/emulator/instructions/movs.rs
Normal file
147
hypervisor/src/arch/x86/emulator/instructions/movs.rs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2021 Microsoft
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
//
|
||||||
|
// MOVS - Move Data from String to String
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate iced_x86;
|
||||||
|
|
||||||
|
use crate::arch::emulator::{EmulationError, PlatformEmulator};
|
||||||
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
|
use crate::arch::x86::regs::DF;
|
||||||
|
use crate::arch::x86::Exception;
|
||||||
|
|
||||||
|
pub struct Movsd_m32_m32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let mut count: u64 = if insn.has_rep_prefix() {
|
||||||
|
state
|
||||||
|
.read_reg(Register::ECX)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rsi = state
|
||||||
|
.read_reg(Register::RSI)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
let mut rdi = state
|
||||||
|
.read_reg(Register::RDI)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
|
||||||
|
let df = (state.flags() & DF) != 0;
|
||||||
|
let len = std::mem::size_of::<u32>();
|
||||||
|
|
||||||
|
while count > 0 {
|
||||||
|
let mut memory: [u8; 4] = [0; 4];
|
||||||
|
|
||||||
|
let src = state
|
||||||
|
.linearize(Register::DS, rsi, false)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
let dst = state
|
||||||
|
.linearize(Register::ES, rdi, true)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
|
||||||
|
platform
|
||||||
|
.read_memory(src, &mut memory[0..len])
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
platform
|
||||||
|
.write_memory(dst, &memory[0..len])
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
if df {
|
||||||
|
rsi = rsi.wrapping_sub(len as u64);
|
||||||
|
rdi = rdi.wrapping_sub(len as u64);
|
||||||
|
} else {
|
||||||
|
rsi = rsi.wrapping_add(len as u64);
|
||||||
|
rdi = rdi.wrapping_add(len as u64);
|
||||||
|
}
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
.write_reg(Register::RSI, rsi)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
state
|
||||||
|
.write_reg(Register::RDI, rdi)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
if insn.has_rep_prefix() {
|
||||||
|
state
|
||||||
|
.write_reg(Register::ECX, 0)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
use super::*;
|
||||||
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rep_movsd_m32_m32() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 24] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
|
||||||
|
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
];
|
||||||
|
let insn = [0xf3, 0xa5]; // rep movsd
|
||||||
|
let regs = vec![(Register::ECX, 3), (Register::ESI, 0), (Register::EDI, 0xc)];
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0xc, &mut data).unwrap();
|
||||||
|
assert_eq!(0x12345678, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0xc + 4, &mut data).unwrap();
|
||||||
|
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0xc + 8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
|
||||||
|
// The rest should be default value 0 from MockVmm
|
||||||
|
vmm.read_memory(0xc + 12, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movsd_m32_m32() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 4] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
];
|
||||||
|
let insn = [0xa5]; // movsd
|
||||||
|
let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0x8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x12345678, <u32>::from_le_bytes(data));
|
||||||
|
// The rest should be default value 0 from MockVmm
|
||||||
|
vmm.read_memory(0x4, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x8 + 8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -524,7 +524,9 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
|||||||
(mov, Movzx_r32_rm8),
|
(mov, Movzx_r32_rm8),
|
||||||
(mov, Movzx_r64_rm8),
|
(mov, Movzx_r64_rm8),
|
||||||
(mov, Movzx_r32_rm16),
|
(mov, Movzx_r32_rm16),
|
||||||
(mov, Movzx_r64_rm16)
|
(mov, Movzx_r64_rm16),
|
||||||
|
// MOVS
|
||||||
|
(movs, Movsd_m32_m32)
|
||||||
);
|
);
|
||||||
|
|
||||||
handler
|
handler
|
||||||
@@ -648,17 +650,17 @@ mod mock_vmm {
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MockVMM {
|
pub struct MockVmm {
|
||||||
memory: Vec<u8>,
|
memory: Vec<u8>,
|
||||||
state: Arc<Mutex<CpuState>>,
|
state: Arc<Mutex<CpuState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for MockVMM {}
|
unsafe impl Sync for MockVmm {}
|
||||||
|
|
||||||
pub type MockResult = Result<(), EmulationError<Exception>>;
|
pub type MockResult = Result<(), EmulationError<Exception>>;
|
||||||
|
|
||||||
impl MockVMM {
|
impl MockVmm {
|
||||||
pub fn new(ip: u64, regs: Vec<(Register, u64)>, memory: Option<(u64, &[u8])>) -> MockVMM {
|
pub fn new(ip: u64, regs: Vec<(Register, u64)>, memory: Option<(u64, &[u8])>) -> MockVmm {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1);
|
let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1);
|
||||||
let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2);
|
let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2);
|
||||||
@@ -672,7 +674,7 @@ mod mock_vmm {
|
|||||||
initial_state.write_reg(reg, value).unwrap();
|
initial_state.write_reg(reg, value).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vmm = MockVMM {
|
let mut vmm = MockVmm {
|
||||||
memory: vec![0; 8192],
|
memory: vec![0; 8192],
|
||||||
state: Arc::new(Mutex::new(initial_state)),
|
state: Arc::new(Mutex::new(initial_state)),
|
||||||
};
|
};
|
||||||
@@ -708,7 +710,7 @@ mod mock_vmm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformEmulator for MockVMM {
|
impl PlatformEmulator for MockVmm {
|
||||||
type CpuState = CpuState;
|
type CpuState = CpuState;
|
||||||
|
|
||||||
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
@@ -790,7 +792,7 @@ mod tests {
|
|||||||
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
|
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
|
||||||
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
let rax: u64 = vmm
|
let rax: u64 = vmm
|
||||||
@@ -825,7 +827,7 @@ mod tests {
|
|||||||
0x48, 0x8b, // Truncated mov rbx, qword ptr [rax+10h] -- missing [0x58, 0x10]
|
0x48, 0x8b, // Truncated mov rbx, qword ptr [rax+10h] -- missing [0x58, 0x10]
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
|
||||||
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
let rbx: u64 = vmm
|
let rbx: u64 = vmm
|
||||||
@@ -855,7 +857,7 @@ mod tests {
|
|||||||
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
|
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
|
||||||
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_err());
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,27 +11,14 @@
|
|||||||
// Copyright © 2020, Microsoft Corporation
|
// Copyright © 2020, Microsoft Corporation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
pub mod emulator;
|
||||||
pub mod gdt;
|
pub mod gdt;
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[allow(unused)]
|
|
||||||
#[allow(
|
|
||||||
clippy::unreadable_literal,
|
|
||||||
clippy::redundant_static_lifetimes,
|
|
||||||
clippy::trivially_copy_pass_by_ref,
|
|
||||||
clippy::useless_transmute,
|
|
||||||
clippy::should_implement_trait,
|
|
||||||
clippy::transmute_ptr_to_ptr,
|
|
||||||
clippy::unreadable_literal,
|
|
||||||
clippy::redundant_static_lifetimes
|
|
||||||
)]
|
|
||||||
pub mod msr_index;
|
pub mod msr_index;
|
||||||
|
|
||||||
pub mod emulator;
|
|
||||||
|
|
||||||
// MTRR constants
|
// MTRR constants
|
||||||
pub const MTRR_ENABLE: u64 = 0x800; // IA32_MTRR_DEF_TYPE MSR: E (MTRRs enabled) flag, bit 11
|
pub const MTRR_ENABLE: u64 = 0x800; // IA32_MTRR_DEF_TYPE MSR: E (MTRRs enabled) flag, bit 11
|
||||||
pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
||||||
@@ -40,7 +27,7 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
|||||||
pub const NUM_IOAPIC_PINS: usize = 24;
|
pub const NUM_IOAPIC_PINS: usize = 24;
|
||||||
|
|
||||||
// X86 Exceptions
|
// X86 Exceptions
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Exception {
|
pub enum Exception {
|
||||||
DE = 0, // Divide Error
|
DE = 0, // Divide Error
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub const PF_SHIFT: usize = 2;
|
|||||||
pub const AF_SHIFT: usize = 4;
|
pub const AF_SHIFT: usize = 4;
|
||||||
pub const ZF_SHIFT: usize = 6;
|
pub const ZF_SHIFT: usize = 6;
|
||||||
pub const SF_SHIFT: usize = 7;
|
pub const SF_SHIFT: usize = 7;
|
||||||
|
pub const DF_SHIFT: usize = 10;
|
||||||
pub const OF_SHIFT: usize = 11;
|
pub const OF_SHIFT: usize = 11;
|
||||||
|
|
||||||
pub const CF: u64 = 1 << CF_SHIFT;
|
pub const CF: u64 = 1 << CF_SHIFT;
|
||||||
@@ -29,4 +30,5 @@ pub const PF: u64 = 1 << PF_SHIFT;
|
|||||||
pub const AF: u64 = 1 << AF_SHIFT;
|
pub const AF: u64 = 1 << AF_SHIFT;
|
||||||
pub const ZF: u64 = 1 << ZF_SHIFT;
|
pub const ZF: u64 = 1 << ZF_SHIFT;
|
||||||
pub const SF: u64 = 1 << SF_SHIFT;
|
pub const SF: u64 = 1 << SF_SHIFT;
|
||||||
|
pub const DF: u64 = 1 << DF_SHIFT;
|
||||||
pub const OF: u64 = 1 << OF_SHIFT;
|
pub const OF: u64 = 1 << OF_SHIFT;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ use crate::CpuState;
|
|||||||
use crate::MpState;
|
use crate::MpState;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::Xsave;
|
use crate::Xsave;
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
use mshv_bindings::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -172,7 +174,7 @@ pub enum HypervisorCpuError {
|
|||||||
/// Enabling HyperV SynIC error
|
/// Enabling HyperV SynIC error
|
||||||
///
|
///
|
||||||
#[error("Failed to enable HyperV SynIC")]
|
#[error("Failed to enable HyperV SynIC")]
|
||||||
EnableHyperVSynIC(#[source] anyhow::Error),
|
EnableHyperVSyncIc(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// Getting AArch64 core register error
|
/// Getting AArch64 core register error
|
||||||
///
|
///
|
||||||
@@ -198,6 +200,17 @@ pub enum HypervisorCpuError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to set system register: {0}")]
|
#[error("Failed to set system register: {0}")]
|
||||||
SetSysRegister(#[source] anyhow::Error),
|
SetSysRegister(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// GVA translation error
|
||||||
|
///
|
||||||
|
#[error("Failed to translate GVA: {0}")]
|
||||||
|
TranslateVirtualAddress(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Failed to initialize TDX on CPU
|
||||||
|
///
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
#[error("Failed to initialize TDX: {0}")]
|
||||||
|
InitializeTdx(#[source] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -398,4 +411,14 @@ pub trait Vcpu: Send + Sync {
|
|||||||
/// Triggers the running of the current virtual CPU returning an exit reason.
|
/// Triggers the running of the current virtual CPU returning an exit reason.
|
||||||
///
|
///
|
||||||
fn run(&self) -> std::result::Result<VmExit, HypervisorCpuError>;
|
fn run(&self) -> std::result::Result<VmExit, HypervisorCpuError>;
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
///
|
||||||
|
/// Translate guest virtual address to guest physical address
|
||||||
|
///
|
||||||
|
fn translate_gva(&self, gva: u64, flags: u64) -> Result<(u64, hv_translate_gva_result)>;
|
||||||
|
///
|
||||||
|
/// Initialize TDX support on the vCPU
|
||||||
|
///
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn tdx_init(&self, hob_address: u64) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use crate::x86_64::MsrList;
|
|||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
use kvm_ioctls::Cap;
|
use kvm_ioctls::Cap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -87,6 +86,12 @@ pub trait Hypervisor: Send + Sync {
|
|||||||
fn create_vm(&self) -> Result<Arc<dyn Vm>>;
|
fn create_vm(&self) -> Result<Arc<dyn Vm>>;
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
///
|
///
|
||||||
|
/// Create a Vm of a specific type using the underlying hypervisor
|
||||||
|
/// Return a hypervisor-agnostic Vm trait object
|
||||||
|
///
|
||||||
|
fn create_vm_with_type(&self, vm_type: u64) -> Result<Arc<dyn Vm>>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
///
|
||||||
/// Returns the size of the memory mapping required to use the vcpu's structures
|
/// Returns the size of the memory mapping required to use the vcpu's structures
|
||||||
///
|
///
|
||||||
fn get_vcpu_mmap_size(&self) -> Result<usize>;
|
fn get_vcpu_mmap_size(&self) -> Result<usize>;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub use crate::aarch64::{
|
|||||||
use crate::cpu;
|
use crate::cpu;
|
||||||
use crate::device;
|
use crate::device;
|
||||||
use crate::hypervisor;
|
use crate::hypervisor;
|
||||||
|
use crate::vec_with_array_field;
|
||||||
use crate::vm::{self, VmmOps};
|
use crate::vm::{self, VmmOps};
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::{arm64_core_reg_id, offset__of};
|
use crate::{arm64_core_reg_id, offset__of};
|
||||||
@@ -32,48 +33,45 @@ use vmm_sys_util::eventfd::EventFd;
|
|||||||
// x86_64 dependencies
|
// x86_64 dependencies
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::arch::x86::NUM_IOAPIC_PINS;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use aarch64::{RegList, Register, StandardRegisters};
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use kvm_bindings::{
|
||||||
|
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
||||||
|
};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use x86_64::{
|
use x86_64::{
|
||||||
check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters, KVM_TSS_ADDRESS,
|
check_required_kvm_extensions, FpuState, SpecialRegisters, StandardRegisters, KVM_TSS_ADDRESS,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use aarch64::{RegList, Register, StandardRegisters};
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::{
|
pub use x86_64::{
|
||||||
CpuId, CpuIdEntry, ExtendedControlRegisters, LapicState, MsrEntries, VcpuKvmState as CpuState,
|
CpuId, CpuIdEntry, ExtendedControlRegisters, LapicState, MsrEntries, VcpuKvmState as CpuState,
|
||||||
Xsave, CPUID_FLAG_VALID_INDEX,
|
Xsave, CPUID_FLAG_VALID_INDEX,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use kvm_bindings::{
|
|
||||||
kvm_enable_cap, kvm_msr_entry, MsrList, KVM_CAP_HYPERV_SYNIC, KVM_CAP_SPLIT_IRQCHIP,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use crate::arch::x86::NUM_IOAPIC_PINS;
|
|
||||||
|
|
||||||
// aarch64 dependencies
|
// aarch64 dependencies
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub mod aarch64;
|
pub mod aarch64;
|
||||||
|
pub use kvm_bindings;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
use kvm_bindings::KVMIO;
|
||||||
|
pub use kvm_bindings::{
|
||||||
|
kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_irq_routing, kvm_irq_routing_entry,
|
||||||
|
kvm_userspace_memory_region, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI,
|
||||||
|
KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY, KVM_MSI_VALID_DEVID,
|
||||||
|
};
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use kvm_bindings::{
|
use kvm_bindings::{
|
||||||
kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM_CORE,
|
kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM_CORE,
|
||||||
KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64,
|
||||||
};
|
};
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
pub use kvm_bindings;
|
|
||||||
pub use kvm_bindings::{
|
|
||||||
kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_irq_routing, kvm_irq_routing_entry,
|
|
||||||
kvm_userspace_memory_region, KVM_IRQ_ROUTING_MSI, KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY,
|
|
||||||
KVM_MSI_VALID_DEVID,
|
|
||||||
};
|
|
||||||
pub use kvm_ioctls;
|
pub use kvm_ioctls;
|
||||||
pub use kvm_ioctls::{Cap, Kvm};
|
pub use kvm_ioctls::{Cap, Kvm};
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use std::mem;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
use vmm_sys_util::{ioctl::ioctl_with_val, ioctl_expr, ioctl_ioc_nr, ioctl_iowr_nr};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
/// Export generically-named wrappers of kvm-bindings for Unix-based platforms
|
||||||
@@ -86,6 +84,21 @@ pub use {
|
|||||||
kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress,
|
kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress,
|
||||||
kvm_ioctls::VcpuExit,
|
kvm_ioctls::VcpuExit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong);
|
||||||
|
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
#[repr(u32)]
|
||||||
|
enum TdxCommand {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Capabilities = 0,
|
||||||
|
InitVm,
|
||||||
|
InitVcpu,
|
||||||
|
InitMemRegion,
|
||||||
|
Finalize,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct KvmVmState {}
|
pub struct KvmVmState {}
|
||||||
|
|
||||||
@@ -98,36 +111,6 @@ pub struct KvmVm {
|
|||||||
state: KvmVmState,
|
state: KvmVmState,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
|
||||||
fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
|
|
||||||
let rounded_size = (size_in_bytes + size_of::<T>() - 1) / size_of::<T>();
|
|
||||||
let mut v = Vec::with_capacity(rounded_size);
|
|
||||||
v.resize_with(rounded_size, T::default);
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
// The kvm API has many structs that resemble the following `Foo` structure:
|
|
||||||
//
|
|
||||||
// ```
|
|
||||||
// #[repr(C)]
|
|
||||||
// struct Foo {
|
|
||||||
// some_data: u32
|
|
||||||
// entries: __IncompleteArrayField<__u32>,
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// In order to allocate such a structure, `size_of::<Foo>()` would be too small because it would not
|
|
||||||
// include any space for `entries`. To make the allocation large enough while still being aligned
|
|
||||||
// for `Foo`, a `Vec<Foo>` is created. Only the first element of `Vec<Foo>` would actually be used
|
|
||||||
// as a `Foo`. The remaining memory in the `Vec<Foo>` is for `entries`, which must be contiguous
|
|
||||||
// with `Foo`. This function is used to make the `Vec<Foo>` with enough space for `count` entries.
|
|
||||||
use std::mem::size_of;
|
|
||||||
fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
|
|
||||||
let element_space = count * size_of::<F>();
|
|
||||||
let vec_size_bytes = size_of::<T>() + element_space;
|
|
||||||
vec_with_size_in_bytes(vec_size_bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Implementation of Vm trait for KVM
|
/// Implementation of Vm trait for KVM
|
||||||
/// Example:
|
/// Example:
|
||||||
@@ -374,7 +357,109 @@ impl vm::Vm for KvmVm {
|
|||||||
.get_dirty_log(slot, memory_size as usize)
|
.get_dirty_log(slot, memory_size as usize)
|
||||||
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialize TDX for this VM
|
||||||
|
///
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> vm::Result<()> {
|
||||||
|
#[repr(C)]
|
||||||
|
struct TdxInitVm {
|
||||||
|
max_vcpus: u32,
|
||||||
|
reserved: u32,
|
||||||
|
attributes: u64,
|
||||||
|
cpuid: u64,
|
||||||
|
}
|
||||||
|
let data = TdxInitVm {
|
||||||
|
max_vcpus,
|
||||||
|
reserved: 0,
|
||||||
|
attributes: 0,
|
||||||
|
cpuid: cpuid.as_fam_struct_ptr() as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
tdx_command(
|
||||||
|
&self.fd.as_raw_fd(),
|
||||||
|
TdxCommand::InitVm,
|
||||||
|
0,
|
||||||
|
&data as *const _ as u64,
|
||||||
|
)
|
||||||
|
.map_err(vm::HypervisorVmError::InitializeTdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Finalize the TDX setup for this VM
|
||||||
|
///
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn tdx_finalize(&self) -> vm::Result<()> {
|
||||||
|
tdx_command(&self.fd.as_raw_fd(), TdxCommand::Finalize, 0, 0)
|
||||||
|
.map_err(vm::HypervisorVmError::FinalizeTdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialize memory regions for the TDX VM
|
||||||
|
///
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn tdx_init_memory_region(
|
||||||
|
&self,
|
||||||
|
host_address: u64,
|
||||||
|
guest_address: u64,
|
||||||
|
size: u64,
|
||||||
|
measure: bool,
|
||||||
|
) -> vm::Result<()> {
|
||||||
|
#[repr(C)]
|
||||||
|
struct TdxInitMemRegion {
|
||||||
|
host_address: u64,
|
||||||
|
guest_address: u64,
|
||||||
|
pages: u64,
|
||||||
|
}
|
||||||
|
let data = TdxInitMemRegion {
|
||||||
|
host_address,
|
||||||
|
guest_address,
|
||||||
|
pages: size / 4096,
|
||||||
|
};
|
||||||
|
|
||||||
|
tdx_command(
|
||||||
|
&self.fd.as_raw_fd(),
|
||||||
|
TdxCommand::InitMemRegion,
|
||||||
|
if measure { 1 } else { 0 },
|
||||||
|
&data as *const _ as u64,
|
||||||
|
)
|
||||||
|
.map_err(vm::HypervisorVmError::InitMemRegionTdx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn tdx_command(
|
||||||
|
fd: &RawFd,
|
||||||
|
command: TdxCommand,
|
||||||
|
metadata: u32,
|
||||||
|
data: u64,
|
||||||
|
) -> std::result::Result<(), std::io::Error> {
|
||||||
|
#[repr(C)]
|
||||||
|
struct TdxIoctlCmd {
|
||||||
|
command: TdxCommand,
|
||||||
|
metadata: u32,
|
||||||
|
data: u64,
|
||||||
|
}
|
||||||
|
let cmd = TdxIoctlCmd {
|
||||||
|
command,
|
||||||
|
metadata,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
let ret = unsafe {
|
||||||
|
ioctl_with_val(
|
||||||
|
fd,
|
||||||
|
KVM_MEMORY_ENCRYPT_OP(),
|
||||||
|
&cmd as *const TdxIoctlCmd as std::os::raw::c_ulong,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(std::io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper over KVM system ioctls.
|
/// Wrapper over KVM system ioctls.
|
||||||
pub struct KvmHypervisor {
|
pub struct KvmHypervisor {
|
||||||
kvm: Kvm,
|
kvm: Kvm,
|
||||||
@@ -407,18 +492,18 @@ impl KvmHypervisor {
|
|||||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||||
///
|
///
|
||||||
impl hypervisor::Hypervisor for KvmHypervisor {
|
impl hypervisor::Hypervisor for KvmHypervisor {
|
||||||
/// Create a KVM vm object and return the object as Vm trait object
|
/// Create a KVM vm object of a specific VM type and return the object as Vm trait object
|
||||||
/// Example
|
/// Example
|
||||||
/// # extern crate hypervisor;
|
/// # extern crate hypervisor;
|
||||||
/// # use hypervisor::KvmHypervisor;
|
/// # use hypervisor::KvmHypervisor;
|
||||||
/// use hypervisor::KvmVm;
|
/// use hypervisor::KvmVm;
|
||||||
/// let hypervisor = KvmHypervisor::new().unwrap();
|
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||||
/// let vm = hypervisor.create_vm().unwrap()
|
/// let vm = hypervisor.create_vm_with_type(KvmVmType::LegacyVm).unwrap()
|
||||||
///
|
///
|
||||||
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
||||||
let fd: VmFd;
|
let fd: VmFd;
|
||||||
loop {
|
loop {
|
||||||
match self.kvm.create_vm() {
|
match self.kvm.create_vm_with_type(vm_type) {
|
||||||
Ok(res) => fd = res,
|
Ok(res) => fd = res,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if e.errno() == libc::EINTR {
|
if e.errno() == libc::EINTR {
|
||||||
@@ -440,7 +525,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
{
|
{
|
||||||
let msr_list = self.get_msr_list()?;
|
let msr_list = self.get_msr_list()?;
|
||||||
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
||||||
let mut msrs = MsrEntries::new(num_msrs);
|
let mut msrs = MsrEntries::new(num_msrs).unwrap();
|
||||||
let indices = msr_list.as_slice();
|
let indices = msr_list.as_slice();
|
||||||
let msr_entries = msrs.as_mut_slice();
|
let msr_entries = msrs.as_mut_slice();
|
||||||
for (pos, index) in indices.iter().enumerate() {
|
for (pos, index) in indices.iter().enumerate() {
|
||||||
@@ -463,6 +548,18 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a KVM vm object and return the object as Vm trait object
|
||||||
|
/// Example
|
||||||
|
/// # extern crate hypervisor;
|
||||||
|
/// # use hypervisor::KvmHypervisor;
|
||||||
|
/// use hypervisor::KvmVm;
|
||||||
|
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||||
|
/// let vm = hypervisor.create_vm().unwrap()
|
||||||
|
///
|
||||||
|
fn create_vm(&self) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
||||||
|
self.create_vm_with_type(0) // Create with default platform type
|
||||||
|
}
|
||||||
|
|
||||||
fn check_required_extensions(&self) -> hypervisor::Result<()> {
|
fn check_required_extensions(&self) -> hypervisor::Result<()> {
|
||||||
check_required_kvm_extensions(&self.kvm).expect("Missing KVM capabilities");
|
check_required_kvm_extensions(&self.kvm).expect("Missing KVM capabilities");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -612,7 +709,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
};
|
};
|
||||||
self.fd
|
self.fd
|
||||||
.enable_cap(&cap)
|
.enable_cap(&cap)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::EnableHyperVSynIC(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::EnableHyperVSyncIc(e.into()))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// X86 specific call to retrieve the CPUID registers.
|
/// X86 specific call to retrieve the CPUID registers.
|
||||||
@@ -1043,7 +1140,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
fn system_registers(&self, state: &mut Vec<Register>) -> cpu::Result<()> {
|
fn system_registers(&self, state: &mut Vec<Register>) -> cpu::Result<()> {
|
||||||
// Call KVM_GET_REG_LIST to get all registers available to the guest. For ArmV8 there are
|
// Call KVM_GET_REG_LIST to get all registers available to the guest. For ArmV8 there are
|
||||||
// around 500 registers.
|
// around 500 registers.
|
||||||
let mut reg_list = RegList::new(512);
|
let mut reg_list = RegList::new(500).unwrap();
|
||||||
self.fd
|
self.fd
|
||||||
.get_reg_list(&mut reg_list)
|
.get_reg_list(&mut reg_list)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GetRegList(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::GetRegList(e.into()))?;
|
||||||
@@ -1177,7 +1274,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
let msrs = if num_msrs != expected_num_msrs {
|
let msrs = if num_msrs != expected_num_msrs {
|
||||||
let mut faulty_msr_index = num_msrs;
|
let mut faulty_msr_index = num_msrs;
|
||||||
let mut msr_entries_tmp =
|
let mut msr_entries_tmp =
|
||||||
MsrEntries::from_entries(&msr_entries.as_slice()[..faulty_msr_index]);
|
MsrEntries::from_entries(&msr_entries.as_slice()[..faulty_msr_index]).unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
warn!(
|
warn!(
|
||||||
@@ -1187,7 +1284,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
|
|
||||||
let start_pos = faulty_msr_index + 1;
|
let start_pos = faulty_msr_index + 1;
|
||||||
let mut sub_msr_entries =
|
let mut sub_msr_entries =
|
||||||
MsrEntries::from_entries(&msr_entries.as_slice()[start_pos..]);
|
MsrEntries::from_entries(&msr_entries.as_slice()[start_pos..]).unwrap();
|
||||||
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
|
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
|
||||||
let num_msrs = self.get_msrs(&mut sub_msr_entries)?;
|
let num_msrs = self.get_msrs(&mut sub_msr_entries)?;
|
||||||
|
|
||||||
@@ -1311,7 +1408,8 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let start_pos = faulty_msr_index + 1;
|
let start_pos = faulty_msr_index + 1;
|
||||||
let sub_msr_entries = MsrEntries::from_entries(&state.msrs.as_slice()[start_pos..]);
|
let sub_msr_entries =
|
||||||
|
MsrEntries::from_entries(&state.msrs.as_slice()[start_pos..]).unwrap();
|
||||||
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
|
let expected_num_msrs = sub_msr_entries.as_fam_struct_ref().nmsrs as usize;
|
||||||
let num_msrs = self.set_msrs(&sub_msr_entries)?;
|
let num_msrs = self.set_msrs(&sub_msr_entries)?;
|
||||||
|
|
||||||
@@ -1338,6 +1436,15 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialize TDX for this CPU
|
||||||
|
///
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn tdx_init(&self, hob_address: u64) -> cpu::Result<()> {
|
||||||
|
tdx_command(&self.fd.as_raw_fd(), TdxCommand::InitVcpu, 0, hob_address)
|
||||||
|
.map_err(cpu::HypervisorCpuError::InitializeTdx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Device struct for KVM
|
/// Device struct for KVM
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ pub fn boot_msr_entries() -> MsrEntries {
|
|||||||
),
|
),
|
||||||
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
msr_data!(msr_index::MSR_MTRRdefType, MTRR_ENABLE | MTRR_MEM_TYPE_WB),
|
||||||
])
|
])
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -72,3 +72,33 @@ pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
|||||||
|
|
||||||
Ok(Arc::new(hv))
|
Ok(Arc::new(hv))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
||||||
|
fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
|
||||||
|
let rounded_size = (size_in_bytes + size_of::<T>() - 1) / size_of::<T>();
|
||||||
|
let mut v = Vec::with_capacity(rounded_size);
|
||||||
|
v.resize_with(rounded_size, T::default);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
// The kvm API has many structs that resemble the following `Foo` structure:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// #[repr(C)]
|
||||||
|
// struct Foo {
|
||||||
|
// some_data: u32
|
||||||
|
// entries: __IncompleteArrayField<__u32>,
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// In order to allocate such a structure, `size_of::<Foo>()` would be too small because it would not
|
||||||
|
// include any space for `entries`. To make the allocation large enough while still being aligned
|
||||||
|
// for `Foo`, a `Vec<Foo>` is created. Only the first element of `Vec<Foo>` would actually be used
|
||||||
|
// as a `Foo`. The remaining memory in the `Vec<Foo>` is for `entries`, which must be contiguous
|
||||||
|
// with `Foo`. This function is used to make the `Vec<Foo>` with enough space for `count` entries.
|
||||||
|
use std::mem::size_of;
|
||||||
|
pub fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
|
||||||
|
let element_space = count * size_of::<F>();
|
||||||
|
let vec_size_bytes = size_of::<T>() + element_space;
|
||||||
|
vec_with_size_in_bytes(vec_size_bytes)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,17 +5,16 @@
|
|||||||
|
|
||||||
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
||||||
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
||||||
use crate::cpu;
|
use crate::cpu;
|
||||||
use crate::cpu::Vcpu;
|
use crate::cpu::Vcpu;
|
||||||
use crate::hypervisor;
|
use crate::hypervisor;
|
||||||
|
use crate::vec_with_array_field;
|
||||||
use crate::vm::{self, VmmOps};
|
use crate::vm::{self, VmmOps};
|
||||||
pub use mshv_bindings::*;
|
pub use mshv_bindings::*;
|
||||||
pub use mshv_ioctls::IoEventAddress;
|
pub use mshv_ioctls::IoEventAddress;
|
||||||
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd};
|
use mshv_ioctls::{set_registers_64, Mshv, NoDatamatch, VcpuFd, VmFd};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vm::DataMatch;
|
use vm::DataMatch;
|
||||||
@@ -29,7 +28,6 @@ pub use x86_64::VcpuMshvState as CpuState;
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::*;
|
pub use x86_64::*;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
@@ -93,7 +91,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
|
|
||||||
let msr_list = self.get_msr_list()?;
|
let msr_list = self.get_msr_list()?;
|
||||||
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize;
|
||||||
let mut msrs = MsrEntries::new(num_msrs);
|
let mut msrs = MsrEntries::new(num_msrs).unwrap();
|
||||||
let indices = msr_list.as_slice();
|
let indices = msr_list.as_slice();
|
||||||
let msr_entries = msrs.as_mut_slice();
|
let msr_entries = msrs.as_mut_slice();
|
||||||
for (pos, index) in indices.iter().enumerate() {
|
for (pos, index) in indices.iter().enumerate() {
|
||||||
@@ -101,12 +99,9 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
}
|
}
|
||||||
let vm_fd = Arc::new(fd);
|
let vm_fd = Arc::new(fd);
|
||||||
|
|
||||||
let gsi_routes = Arc::new(RwLock::new(HashMap::new()));
|
|
||||||
|
|
||||||
Ok(Arc::new(MshvVm {
|
Ok(Arc::new(MshvVm {
|
||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
msrs,
|
msrs,
|
||||||
gsi_routes,
|
|
||||||
hv_state: hv_state_init(),
|
hv_state: hv_state_init(),
|
||||||
vmmops: None,
|
vmmops: None,
|
||||||
}))
|
}))
|
||||||
@@ -115,7 +110,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
/// Get the supported CpuID
|
/// Get the supported CpuID
|
||||||
///
|
///
|
||||||
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
|
fn get_cpuid(&self) -> hypervisor::Result<CpuId> {
|
||||||
Ok(CpuId::new(1))
|
Ok(CpuId::new(1).unwrap())
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
@@ -128,38 +123,6 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
// A software emulated TLB.
|
|
||||||
// This is mostly used by the instruction emulator to cache gva to gpa translations
|
|
||||||
// passed from the hypervisor.
|
|
||||||
struct SoftTLB {
|
|
||||||
addr_map: HashMap<u64, u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SoftTLB {
|
|
||||||
fn new() -> SoftTLB {
|
|
||||||
SoftTLB {
|
|
||||||
addr_map: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a gva -> gpa mapping into the TLB.
|
|
||||||
fn add_mapping(&mut self, gva: u64, gpa: u64) {
|
|
||||||
*self.addr_map.entry(gva).or_insert(gpa) = gpa;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the actual gva -> gpa translation
|
|
||||||
fn translate(&self, gva: u64) -> Result<u64, PlatformError> {
|
|
||||||
self.addr_map
|
|
||||||
.get(&gva)
|
|
||||||
.ok_or_else(|| PlatformError::UnmappedGVA(anyhow!("{:#?}", gva)))
|
|
||||||
.map(|v| *v)
|
|
||||||
|
|
||||||
// TODO Check if we could fallback to e.g. an hypercall for doing
|
|
||||||
// the translation for us.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Vcpu struct for Microsoft Hypervisor
|
/// Vcpu struct for Microsoft Hypervisor
|
||||||
pub struct MshvVcpu {
|
pub struct MshvVcpu {
|
||||||
@@ -167,7 +130,6 @@ pub struct MshvVcpu {
|
|||||||
vp_index: u8,
|
vp_index: u8,
|
||||||
cpuid: CpuId,
|
cpuid: CpuId,
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
gsi_routes: Arc<RwLock<HashMap<u32, MshvIrqRoutingEntry>>>,
|
|
||||||
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
hv_state: Arc<RwLock<HvState>>, // Mshv State
|
||||||
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
||||||
}
|
}
|
||||||
@@ -322,17 +284,52 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
|
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
|
||||||
let info = x.to_ioport_info().unwrap();
|
let info = x.to_ioport_info().unwrap();
|
||||||
let access_info = info.access_info;
|
let access_info = info.access_info;
|
||||||
|
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;
|
||||||
|
let mut data: [u8; 4] = [0; 4];
|
||||||
|
let mut ret_rax = info.rax;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: Ignore QEMU fw_cfg (0x5xx) and debug console (0x402) ports.
|
||||||
|
*
|
||||||
|
* Cloud Hypervisor doesn't support fw_cfg at the moment. It does support 0x402
|
||||||
|
* under the "fwdebug" feature flag. But that feature is not enabled by default
|
||||||
|
* and is considered legacy.
|
||||||
|
*
|
||||||
|
* OVMF unconditionally pokes these IO ports with string IO.
|
||||||
|
*
|
||||||
|
* Instead of trying to implement string IO support now which does not do much
|
||||||
|
* now, skip those ports explicitly to avoid panicking.
|
||||||
|
*
|
||||||
|
* Proper string IO support can be added once we gain the ability to translate
|
||||||
|
* guest virtual addresses to guest physical addresses on MSHV.
|
||||||
|
*/
|
||||||
|
match port {
|
||||||
|
0x402 | 0x510 | 0x511 | 0x514 => {
|
||||||
|
let insn_len = info.header.instruction_length() as u64;
|
||||||
|
|
||||||
|
/* Advance RIP and update RAX */
|
||||||
|
let arr_reg_name_value = [
|
||||||
|
(
|
||||||
|
hv_register_name::HV_X64_REGISTER_RIP,
|
||||||
|
info.header.rip + insn_len,
|
||||||
|
),
|
||||||
|
(hv_register_name::HV_X64_REGISTER_RAX, ret_rax),
|
||||||
|
];
|
||||||
|
set_registers_64!(self.fd, arr_reg_name_value)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||||
|
return Ok(cpu::VmExit::Ignore);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
if unsafe { access_info.__bindgen_anon_1.string_op() } == 1 {
|
if unsafe { access_info.__bindgen_anon_1.string_op() } == 1 {
|
||||||
panic!("String IN/OUT not supported");
|
panic!("String IN/OUT not supported");
|
||||||
}
|
}
|
||||||
if unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1 {
|
if unsafe { access_info.__bindgen_anon_1.rep_prefix() } == 1 {
|
||||||
panic!("Rep IN/OUT not supported");
|
panic!("Rep IN/OUT not supported");
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
let mut data: [u8; 4] = [0; 4];
|
|
||||||
let mut ret_rax = info.rax;
|
|
||||||
|
|
||||||
if is_write {
|
if is_write {
|
||||||
let data = (info.rax as u32).to_le_bytes();
|
let data = (info.rax as u32).to_le_bytes();
|
||||||
@@ -376,14 +373,9 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
|
|
||||||
let mut context = MshvEmulatorContext {
|
let mut context = MshvEmulatorContext {
|
||||||
vcpu: self,
|
vcpu: self,
|
||||||
tlb: SoftTLB::new(),
|
map: (info.guest_virtual_address, info.guest_physical_address),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the GVA <-> GPA mapping.
|
|
||||||
context
|
|
||||||
.tlb
|
|
||||||
.add_mapping(info.guest_virtual_address, info.guest_physical_address);
|
|
||||||
|
|
||||||
// Create a new emulator.
|
// Create a new emulator.
|
||||||
let mut emul = Emulator::new(&mut context);
|
let mut emul = Emulator::new(&mut context);
|
||||||
|
|
||||||
@@ -530,11 +522,47 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
xsave,
|
xsave,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
///
|
||||||
|
/// Translate guest virtual address to guest physical address
|
||||||
|
///
|
||||||
|
fn translate_gva(&self, gva: u64, flags: u64) -> cpu::Result<(u64, hv_translate_gva_result)> {
|
||||||
|
let r = self
|
||||||
|
.fd
|
||||||
|
.translate_gva(gva, flags)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::TranslateVirtualAddress(e.into()))?;
|
||||||
|
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MshvEmulatorContext<'a> {
|
struct MshvEmulatorContext<'a> {
|
||||||
vcpu: &'a MshvVcpu,
|
vcpu: &'a MshvVcpu,
|
||||||
tlb: SoftTLB,
|
map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MshvEmulatorContext<'a> {
|
||||||
|
// Do the actual gva -> gpa translation
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
fn translate(&self, gva: u64) -> Result<u64, PlatformError> {
|
||||||
|
if self.map.0 == gva {
|
||||||
|
return Ok(self.map.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: More fine-grained control for the flags
|
||||||
|
let flags = HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_WRITE;
|
||||||
|
|
||||||
|
let r = self
|
||||||
|
.vcpu
|
||||||
|
.translate_gva(gva, flags.into())
|
||||||
|
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
||||||
|
|
||||||
|
let result_code = unsafe { r.1.__bindgen_anon_1.result_code };
|
||||||
|
match result_code {
|
||||||
|
hv_translate_gva_result_code_HvTranslateGvaSuccess => Ok(r.0),
|
||||||
|
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Platform emulation for Hyper-V
|
/// Platform emulation for Hyper-V
|
||||||
@@ -542,7 +570,7 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
type CpuState = EmulatorCpuState;
|
type CpuState = EmulatorCpuState;
|
||||||
|
|
||||||
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
let gpa = self.tlb.translate(gva)?;
|
let gpa = self.translate(gva)?;
|
||||||
debug!(
|
debug!(
|
||||||
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
||||||
data.len(),
|
data.len(),
|
||||||
@@ -551,16 +579,18 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||||
vmmops
|
if vmmops.guest_mem_read(gpa, data).is_err() {
|
||||||
.mmio_read(gpa, data)
|
vmmops
|
||||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
.mmio_read(gpa, data)
|
||||||
|
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||||
let gpa = self.tlb.translate(gva)?;
|
let gpa = self.translate(gva)?;
|
||||||
debug!(
|
debug!(
|
||||||
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
||||||
data.len(),
|
data.len(),
|
||||||
@@ -569,9 +599,11 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(vmmops) = &self.vcpu.vmmops {
|
if let Some(vmmops) = &self.vcpu.vmmops {
|
||||||
vmmops
|
if vmmops.guest_mem_write(gpa, data).is_err() {
|
||||||
.mmio_write(gpa, data)
|
vmmops
|
||||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
.mmio_write(gpa, data)
|
||||||
|
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -622,7 +654,7 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
|
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
|
||||||
self.tlb.translate(gva)
|
self.translate(gva)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&self, _ip: u64, _instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
fn fetch(&self, _ip: u64, _instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
@@ -630,14 +662,11 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Wrapper over Mshv VM ioctls.
|
/// Wrapper over Mshv VM ioctls.
|
||||||
pub struct MshvVm {
|
pub struct MshvVm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
// GSI routing information
|
|
||||||
gsi_routes: Arc<RwLock<HashMap<u32, MshvIrqRoutingEntry>>>,
|
|
||||||
// Hypervisor State
|
// Hypervisor State
|
||||||
hv_state: Arc<RwLock<HvState>>,
|
hv_state: Arc<RwLock<HvState>>,
|
||||||
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
||||||
@@ -678,19 +707,9 @@ impl vm::Vm for MshvVm {
|
|||||||
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
||||||
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||||
|
|
||||||
let gsi_routes = self.gsi_routes.read().unwrap();
|
self.fd
|
||||||
|
.register_irqfd(&fd, gsi)
|
||||||
if let Some(e) = gsi_routes.get(&gsi) {
|
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
||||||
let msi = e
|
|
||||||
.get_msi_routing()
|
|
||||||
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
|
||||||
let request = msi.to_interrupt_request();
|
|
||||||
self.fd
|
|
||||||
.register_irqfd(&fd, gsi, &request)
|
|
||||||
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
|
||||||
} else {
|
|
||||||
error!("No routing info found for GSI {}", gsi)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -721,9 +740,8 @@ impl vm::Vm for MshvVm {
|
|||||||
let vcpu = MshvVcpu {
|
let vcpu = MshvVcpu {
|
||||||
fd: vcpu_fd,
|
fd: vcpu_fd,
|
||||||
vp_index: id,
|
vp_index: id,
|
||||||
cpuid: CpuId::new(1),
|
cpuid: CpuId::new(1).unwrap(),
|
||||||
msrs: self.msrs.clone(),
|
msrs: self.msrs.clone(),
|
||||||
gsi_routes: self.gsi_routes.clone(),
|
|
||||||
hv_state: self.hv_state.clone(),
|
hv_state: self.hv_state.clone(),
|
||||||
vmmops,
|
vmmops,
|
||||||
};
|
};
|
||||||
@@ -807,17 +825,20 @@ impl vm::Vm for MshvVm {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_gsi_routing(&self, irq_routing: &[IrqRoutingEntry]) -> vm::Result<()> {
|
fn set_gsi_routing(&self, entries: &[IrqRoutingEntry]) -> vm::Result<()> {
|
||||||
let mut routes = self.gsi_routes.write().unwrap();
|
let mut msi_routing =
|
||||||
|
vec_with_array_field::<mshv_msi_routing, mshv_msi_routing_entry>(entries.len());
|
||||||
|
msi_routing[0].nr = entries.len() as u32;
|
||||||
|
|
||||||
routes.drain();
|
unsafe {
|
||||||
|
let entries_slice: &mut [mshv_msi_routing_entry] =
|
||||||
for r in irq_routing {
|
msi_routing[0].entries.as_mut_slice(entries.len());
|
||||||
debug!("gsi routing {:x?}", r);
|
entries_slice.copy_from_slice(&entries);
|
||||||
routes.insert(r.gsi, *r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
self.fd
|
||||||
|
.set_msi_routing(&msi_routing[0])
|
||||||
|
.map_err(|e| vm::HypervisorVmError::SetGsiRouting(e.into()))
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Get the Vm state. Return VM specific data
|
/// Get the Vm state. Return VM specific data
|
||||||
@@ -843,104 +864,6 @@ impl vm::Vm for MshvVm {
|
|||||||
}
|
}
|
||||||
pub use hv_cpuid_entry as CpuIdEntry;
|
pub use hv_cpuid_entry as CpuIdEntry;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
pub type IrqRoutingEntry = mshv_msi_routing_entry;
|
||||||
pub struct MshvIrqRoutingMsi {
|
|
||||||
pub address_lo: u32,
|
|
||||||
pub address_hi: u32,
|
|
||||||
pub data: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum MshvIrqRouting {
|
|
||||||
Msi(MshvIrqRoutingMsi),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum MshvIrqRoutingEntryError {
|
|
||||||
#[error("Invalid MSI address: {0}")]
|
|
||||||
InvalidMsiAddress(#[source] anyhow::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct MshvIrqRoutingEntry {
|
|
||||||
pub gsi: u32,
|
|
||||||
pub route: MshvIrqRouting,
|
|
||||||
}
|
|
||||||
pub type IrqRoutingEntry = MshvIrqRoutingEntry;
|
|
||||||
|
|
||||||
impl MshvIrqRoutingEntry {
|
|
||||||
fn get_msi_routing(&self) -> Result<MshvIrqRoutingMsi, MshvIrqRoutingEntryError> {
|
|
||||||
let MshvIrqRouting::Msi(msi) = self.route;
|
|
||||||
if msi.address_hi != 0 {
|
|
||||||
return Err(MshvIrqRoutingEntryError::InvalidMsiAddress(anyhow!(
|
|
||||||
"MSI high address part is not zero"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Ok(msi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MshvIrqRoutingMsi {
|
|
||||||
///
|
|
||||||
/// See Intel SDM vol3 10.11.1
|
|
||||||
/// We assume APIC ID and Hyper-V Vcpu ID are the same value
|
|
||||||
///
|
|
||||||
|
|
||||||
fn get_destination(&self) -> u64 {
|
|
||||||
((self.address_lo >> 12) & 0xff).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_destination_mode(&self) -> bool {
|
|
||||||
if (self.address_lo >> 2) & 0x1 == 0x1 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_vector(&self) -> u8 {
|
|
||||||
(self.data & 0xff) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// True means level triggered
|
|
||||||
///
|
|
||||||
fn get_trigger_mode(&self) -> bool {
|
|
||||||
if (self.data >> 15) & 0x1 == 0x1 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_delivery_mode(&self) -> u8 {
|
|
||||||
((self.data & 0x700) >> 8) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Translate from architectural defined delivery mode to Hyper-V type
|
|
||||||
/// See Intel SDM vol3 10.11.2
|
|
||||||
///
|
|
||||||
fn get_interrupt_type(&self) -> Option<hv_interrupt_type> {
|
|
||||||
match self.get_delivery_mode() {
|
|
||||||
0 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED),
|
|
||||||
1 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_LOWESTPRIORITY),
|
|
||||||
2 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_SMI),
|
|
||||||
4 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_NMI),
|
|
||||||
5 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_INIT),
|
|
||||||
7 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_EXTINT),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_interrupt_request(&self) -> InterruptRequest {
|
|
||||||
InterruptRequest {
|
|
||||||
interrupt_type: self.get_interrupt_type().unwrap(),
|
|
||||||
apic_id: self.get_destination(),
|
|
||||||
vector: self.get_vector() as u32,
|
|
||||||
level_triggered: self.get_trigger_mode(),
|
|
||||||
logical_destination_mode: self.get_destination_mode(),
|
|
||||||
long_mode: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||||
|
|||||||
@@ -119,4 +119,5 @@ pub fn boot_msr_entries() -> MsrEntries {
|
|||||||
msr!(msr_index::MSR_SYSCALL_MASK),
|
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||||
msr!(msr_index::MSR_IA32_TSC),
|
msr!(msr_index::MSR_IA32_TSC),
|
||||||
])
|
])
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
use crate::aarch64::VcpuInit;
|
use crate::aarch64::VcpuInit;
|
||||||
use crate::cpu::Vcpu;
|
use crate::cpu::Vcpu;
|
||||||
use crate::device::Device;
|
use crate::device::Device;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
use crate::x86_64::CpuId;
|
||||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
use crate::ClockData;
|
use crate::ClockData;
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
@@ -36,9 +38,9 @@ pub enum DataMatch {
|
|||||||
DataMatch64(u64),
|
DataMatch64(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<u64> for DataMatch {
|
impl From<DataMatch> for u64 {
|
||||||
fn into(self) -> u64 {
|
fn from(dm: DataMatch) -> u64 {
|
||||||
match self {
|
match dm {
|
||||||
DataMatch::DataMatch32(dm) => dm.into(),
|
DataMatch::DataMatch32(dm) => dm.into(),
|
||||||
DataMatch::DataMatch64(dm) => dm,
|
DataMatch::DataMatch64(dm) => dm,
|
||||||
}
|
}
|
||||||
@@ -163,6 +165,25 @@ pub enum HypervisorVmError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to assert virtual Interrupt: {0}")]
|
#[error("Failed to assert virtual Interrupt: {0}")]
|
||||||
AsserttVirtualInterrupt(#[source] anyhow::Error),
|
AsserttVirtualInterrupt(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
///
|
||||||
|
/// Error initializing TDX on the VM
|
||||||
|
///
|
||||||
|
#[error("Failed to initialize TDX: {0}")]
|
||||||
|
InitializeTdx(#[source] std::io::Error),
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
///
|
||||||
|
/// Error finalizing the TDX configuration on the VM
|
||||||
|
///
|
||||||
|
#[error("Failed to finalize TDX: {0}")]
|
||||||
|
FinalizeTdx(#[source] std::io::Error),
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
///
|
||||||
|
/// Error initializing the TDX memory region
|
||||||
|
///
|
||||||
|
#[error("Failed to initialize memory region TDX: {0}")]
|
||||||
|
InitMemRegionTdx(#[source] std::io::Error),
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Result type for returning from a function
|
/// Result type for returning from a function
|
||||||
@@ -235,6 +256,21 @@ pub trait Vm: Send + Sync {
|
|||||||
fn set_state(&self, state: VmState) -> Result<()>;
|
fn set_state(&self, state: VmState) -> Result<()>;
|
||||||
/// Get dirty pages bitmap
|
/// Get dirty pages bitmap
|
||||||
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
|
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
/// Initalize TDX on this VM
|
||||||
|
fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
/// Finalize the configuration of TDX on this VM
|
||||||
|
fn tdx_finalize(&self) -> Result<()>;
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
/// Initalize a TDX memory region for this VM
|
||||||
|
fn tdx_init_memory_region(
|
||||||
|
&self,
|
||||||
|
host_address: u64,
|
||||||
|
guest_address: u64,
|
||||||
|
size: u64,
|
||||||
|
measure: bool,
|
||||||
|
) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VmmOps: Send + Sync {
|
pub trait VmmOps: Send + Sync {
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ authors = ["The Chromium OS Authors"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
rand = "0.8.3"
|
serde = "1.0.125"
|
||||||
serde = "1.0.123"
|
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.5.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
@@ -18,4 +17,4 @@ vmm-sys-util = ">=0.3.1"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.3.0"
|
lazy_static = "1.3.0"
|
||||||
pnet = "0.27.2"
|
pnet = "0.27.2"
|
||||||
serde_json = "1.0.62"
|
serde_json = "1.0.64"
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ extern crate libc;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate net_gen;
|
extern crate net_gen;
|
||||||
extern crate rand;
|
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate virtio_bindings;
|
extern crate virtio_bindings;
|
||||||
extern crate vm_memory;
|
extern crate vm_memory;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the THIRD-PARTY file.
|
// found in the THIRD-PARTY file.
|
||||||
|
|
||||||
use rand::Rng;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
@@ -58,7 +57,7 @@ impl MacAddr {
|
|||||||
// TODO: using something like std::mem::uninitialized could avoid the extra initialization,
|
// TODO: using something like std::mem::uninitialized could avoid the extra initialization,
|
||||||
// if this ever becomes a performance bottleneck.
|
// if this ever becomes a performance bottleneck.
|
||||||
let mut bytes = [0u8; MAC_ADDR_LEN];
|
let mut bytes = [0u8; MAC_ADDR_LEN];
|
||||||
bytes[..].copy_from_slice(&src[..]);
|
bytes[..].copy_from_slice(&src);
|
||||||
|
|
||||||
MacAddr { bytes }
|
MacAddr { bytes }
|
||||||
}
|
}
|
||||||
@@ -82,7 +81,22 @@ impl MacAddr {
|
|||||||
|
|
||||||
pub fn local_random() -> MacAddr {
|
pub fn local_random() -> MacAddr {
|
||||||
// Generate a fully random MAC
|
// Generate a fully random MAC
|
||||||
let mut random_bytes = rand::thread_rng().gen::<[u8; MAC_ADDR_LEN]>();
|
let mut random_bytes = [0u8; MAC_ADDR_LEN];
|
||||||
|
unsafe {
|
||||||
|
// Man page says this function will not be interrupted by a signal
|
||||||
|
// for requests less than 256 bytes
|
||||||
|
if libc::getrandom(
|
||||||
|
random_bytes.as_mut_ptr() as *mut _ as *mut libc::c_void,
|
||||||
|
MAC_ADDR_LEN,
|
||||||
|
0,
|
||||||
|
) < 0
|
||||||
|
{
|
||||||
|
error!(
|
||||||
|
"Error populating MAC address with random data: {}",
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Set the first byte to make the OUI a locally administered OUI
|
// Set the first byte to make the OUI a locally administered OUI
|
||||||
random_bytes[0] = 0x2e;
|
random_bytes[0] = 0x2e;
|
||||||
|
|||||||
@@ -2,27 +2,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use super::{register_listener, unregister_listener, vnet_hdr_len, Tap};
|
use super::{unregister_listener, vnet_hdr_len, Tap};
|
||||||
use libc::EAGAIN;
|
|
||||||
use std::cmp;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
|
use vm_memory::{Bytes, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryMmap};
|
||||||
use vm_virtio::{DescriptorChain, Queue};
|
use vm_virtio::Queue;
|
||||||
|
|
||||||
/// The maximum buffer size when segmentation offload is enabled. This
|
|
||||||
/// includes the 12-byte virtio net header.
|
|
||||||
/// http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html#x1-1740003
|
|
||||||
const MAX_BUFFER_SIZE: usize = 65562;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TxVirtio {
|
pub struct TxVirtio {
|
||||||
pub iovec: Vec<(GuestAddress, usize)>,
|
|
||||||
pub frame_buf: [u8; MAX_BUFFER_SIZE],
|
|
||||||
pub counter_bytes: Wrapping<u64>,
|
pub counter_bytes: Wrapping<u64>,
|
||||||
pub counter_frames: Wrapping<u64>,
|
pub counter_frames: Wrapping<u64>,
|
||||||
}
|
}
|
||||||
@@ -36,73 +26,66 @@ impl Default for TxVirtio {
|
|||||||
impl TxVirtio {
|
impl TxVirtio {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
TxVirtio {
|
TxVirtio {
|
||||||
iovec: Vec::new(),
|
|
||||||
frame_buf: [0u8; MAX_BUFFER_SIZE],
|
|
||||||
counter_bytes: Wrapping(0),
|
counter_bytes: Wrapping(0),
|
||||||
counter_frames: Wrapping(0),
|
counter_frames: Wrapping(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_desc_chain(&mut self, mem: &GuestMemoryMmap, tap: &mut Tap, queue: &mut Queue) {
|
pub fn process_desc_chain(
|
||||||
|
&mut self,
|
||||||
|
mem: &GuestMemoryMmap,
|
||||||
|
tap: &mut Tap,
|
||||||
|
queue: &mut Queue,
|
||||||
|
) -> Result<(), NetQueuePairError> {
|
||||||
while let Some(avail_desc) = queue.iter(&mem).next() {
|
while let Some(avail_desc) = queue.iter(&mem).next() {
|
||||||
let head_index = avail_desc.index;
|
let head_index = avail_desc.index;
|
||||||
let mut read_count = 0;
|
|
||||||
let mut next_desc = Some(avail_desc);
|
let mut next_desc = Some(avail_desc);
|
||||||
|
|
||||||
self.iovec.clear();
|
let mut iovecs = Vec::new();
|
||||||
while let Some(desc) = next_desc {
|
while let Some(desc) = next_desc {
|
||||||
if desc.is_write_only() {
|
if !desc.is_write_only() && desc.len > 0 {
|
||||||
break;
|
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);
|
||||||
}
|
}
|
||||||
self.iovec.push((desc.addr, desc.len as usize));
|
|
||||||
read_count += desc.len as usize;
|
|
||||||
next_desc = desc.next_descriptor();
|
next_desc = desc.next_descriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
read_count = 0;
|
if !iovecs.is_empty() {
|
||||||
// Copy buffer from across multiple descriptors.
|
let result = unsafe {
|
||||||
// TODO(performance - Issue #420): change this to use `writev()` instead of `write()`
|
libc::writev(
|
||||||
// and get rid of the intermediate buffer.
|
tap.as_raw_fd() as libc::c_int,
|
||||||
for (desc_addr, desc_len) in self.iovec.drain(..) {
|
iovecs.as_ptr() as *const libc::iovec,
|
||||||
let limit = cmp::min((read_count + desc_len) as usize, self.frame_buf.len());
|
iovecs.len() as libc::c_int,
|
||||||
|
)
|
||||||
let read_result =
|
};
|
||||||
mem.read_slice(&mut self.frame_buf[read_count..limit as usize], desc_addr);
|
if result < 0 {
|
||||||
match read_result {
|
let e = std::io::Error::last_os_error();
|
||||||
Ok(_) => {
|
error!("net: tx: failed writing to tap: {}", e);
|
||||||
// Increment by number of bytes actually read
|
queue.go_to_previous_position();
|
||||||
read_count += limit - read_count;
|
return Err(NetQueuePairError::WriteTap(e));
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to read slice: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.counter_bytes += Wrapping(result as u64 - vnet_hdr_len() as u64);
|
||||||
|
self.counter_frames += Wrapping(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let write_result = tap.write(&self.frame_buf[..read_count]);
|
|
||||||
match write_result {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
println!("net: tx: error failed to write to tap: {}", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.counter_bytes += Wrapping((read_count - vnet_hdr_len()) as u64);
|
|
||||||
self.counter_frames += Wrapping(1);
|
|
||||||
|
|
||||||
queue.add_used(&mem, head_index, 0);
|
queue.add_used(&mem, head_index, 0);
|
||||||
queue.update_avail_event(&mem);
|
queue.update_avail_event(&mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RxVirtio {
|
pub struct RxVirtio {
|
||||||
pub deferred_frame: bool,
|
|
||||||
pub deferred_irqs: bool,
|
|
||||||
pub bytes_read: usize,
|
|
||||||
pub frame_buf: [u8; MAX_BUFFER_SIZE],
|
|
||||||
pub counter_bytes: Wrapping<u64>,
|
pub counter_bytes: Wrapping<u64>,
|
||||||
pub counter_frames: Wrapping<u64>,
|
pub counter_frames: Wrapping<u64>,
|
||||||
}
|
}
|
||||||
@@ -116,10 +99,6 @@ impl Default for RxVirtio {
|
|||||||
impl RxVirtio {
|
impl RxVirtio {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
RxVirtio {
|
RxVirtio {
|
||||||
deferred_frame: false,
|
|
||||||
deferred_irqs: false,
|
|
||||||
bytes_read: 0,
|
|
||||||
frame_buf: [0u8; MAX_BUFFER_SIZE],
|
|
||||||
counter_bytes: Wrapping(0),
|
counter_bytes: Wrapping(0),
|
||||||
counter_frames: Wrapping(0),
|
counter_frames: Wrapping(0),
|
||||||
}
|
}
|
||||||
@@ -128,63 +107,72 @@ impl RxVirtio {
|
|||||||
pub fn process_desc_chain(
|
pub fn process_desc_chain(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &GuestMemoryMmap,
|
||||||
mut next_desc: Option<DescriptorChain>,
|
tap: &mut Tap,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue,
|
||||||
) -> bool {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
let head_index = next_desc.as_ref().unwrap().index;
|
let mut exhausted_descs = true;
|
||||||
let mut write_count = 0;
|
while let Some(avail_desc) = queue.iter(&mem).next() {
|
||||||
|
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);
|
||||||
|
|
||||||
// Copy from frame into buffer, which may span multiple descriptors.
|
let mut iovecs = Vec::new();
|
||||||
loop {
|
while let Some(desc) = next_desc {
|
||||||
match next_desc {
|
if desc.is_write_only() && desc.len > 0 {
|
||||||
Some(desc) => {
|
let buf = mem
|
||||||
if !desc.is_write_only() {
|
.get_slice(desc.addr, desc.len as usize)
|
||||||
break;
|
.map_err(NetQueuePairError::GuestMemory)?
|
||||||
}
|
.as_ptr();
|
||||||
let limit = cmp::min(write_count + desc.len as usize, self.bytes_read);
|
let iovec = libc::iovec {
|
||||||
let source_slice = &self.frame_buf[write_count..limit];
|
iov_base: buf as *mut libc::c_void,
|
||||||
let write_result = mem.write_slice(source_slice, desc.addr);
|
iov_len: desc.len as libc::size_t,
|
||||||
|
};
|
||||||
|
iovecs.push(iovec);
|
||||||
|
}
|
||||||
|
next_desc = desc.next_descriptor();
|
||||||
|
}
|
||||||
|
|
||||||
match write_result {
|
let len = if !iovecs.is_empty() {
|
||||||
Ok(_) => {
|
let result = unsafe {
|
||||||
write_count = limit;
|
libc::readv(
|
||||||
}
|
tap.as_raw_fd() as libc::c_int,
|
||||||
Err(e) => {
|
iovecs.as_ptr() as *const libc::iovec,
|
||||||
error!("Failed to write slice: {:?}", e);
|
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();
|
||||||
|
|
||||||
|
if let Some(raw_err) = e.raw_os_error() {
|
||||||
|
if raw_err == libc::EAGAIN {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if write_count >= self.bytes_read {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
next_desc = desc.next_descriptor();
|
|
||||||
|
error!("net: rx: failed reading from tap: {}", e);
|
||||||
|
return Err(NetQueuePairError::ReadTap(e));
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
warn!("Receiving buffer is too small to hold frame of current size");
|
// Write num_buffers to guest memory. We simply write 1 as we
|
||||||
break;
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.counter_bytes += Wrapping((write_count - vnet_hdr_len()) as u64);
|
Ok(exhausted_descs)
|
||||||
self.counter_frames += Wrapping(1);
|
|
||||||
|
|
||||||
queue.add_used(&mem, head_index, write_count as u32);
|
|
||||||
queue.update_avail_event(&mem);
|
|
||||||
|
|
||||||
// Mark that we have at least one pending packet and we need to interrupt the guest.
|
|
||||||
self.deferred_irqs = true;
|
|
||||||
|
|
||||||
// Update the frame_buf buffer.
|
|
||||||
if write_count < self.bytes_read {
|
|
||||||
self.frame_buf.copy_within(write_count..self.bytes_read, 0);
|
|
||||||
self.bytes_read -= write_count;
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
self.bytes_read = 0;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +192,12 @@ pub enum NetQueuePairError {
|
|||||||
RegisterListener(io::Error),
|
RegisterListener(io::Error),
|
||||||
/// Error unregistering listener
|
/// Error unregistering listener
|
||||||
UnregisterListener(io::Error),
|
UnregisterListener(io::Error),
|
||||||
|
/// Error writing to the TAP device
|
||||||
|
WriteTap(io::Error),
|
||||||
/// Error reading from the TAP device
|
/// Error reading from the TAP device
|
||||||
FailedReadTap,
|
ReadTap(io::Error),
|
||||||
|
/// Error related to guest memory
|
||||||
|
GuestMemory(vm_memory::GuestMemoryError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NetQueuePair {
|
pub struct NetQueuePair {
|
||||||
@@ -220,128 +212,15 @@ pub struct NetQueuePair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NetQueuePair {
|
impl NetQueuePair {
|
||||||
// Copies a single frame from `self.rx.frame_buf` into the guest. Returns true
|
|
||||||
// if a buffer was used, and false if the frame must be deferred until a buffer
|
|
||||||
// is made available by the driver.
|
|
||||||
fn rx_single_frame(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
|
||||||
let mem = self
|
|
||||||
.mem
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
|
||||||
.map(|m| m.memory())?;
|
|
||||||
let next_desc = queue.iter(&mem).next();
|
|
||||||
|
|
||||||
if next_desc.is_none() {
|
|
||||||
// Queue has no available descriptors
|
|
||||||
if self.rx_tap_listening {
|
|
||||||
unregister_listener(
|
|
||||||
self.epoll_fd.unwrap(),
|
|
||||||
self.tap.as_raw_fd(),
|
|
||||||
epoll::Events::EPOLLIN,
|
|
||||||
u64::from(self.tap_event_id),
|
|
||||||
)
|
|
||||||
.map_err(NetQueuePairError::UnregisterListener)?;
|
|
||||||
self.rx_tap_listening = false;
|
|
||||||
info!("Listener unregistered");
|
|
||||||
}
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self.rx.process_desc_chain(&mem, next_desc, &mut queue))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_rx(&mut self, queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
|
||||||
// Read as many frames as possible.
|
|
||||||
loop {
|
|
||||||
match self.read_tap() {
|
|
||||||
Ok(count) => {
|
|
||||||
self.rx.bytes_read = count;
|
|
||||||
if !self.rx_single_frame(queue)? {
|
|
||||||
self.rx.deferred_frame = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
// The tap device is non-blocking, so any error aside from EAGAIN is
|
|
||||||
// unexpected.
|
|
||||||
match e.raw_os_error() {
|
|
||||||
Some(err) if err == EAGAIN => (),
|
|
||||||
_ => {
|
|
||||||
error!("Failed to read tap: {:?}", e);
|
|
||||||
return Err(NetQueuePairError::FailedReadTap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume the counters from the Rx/Tx queues and accumulate into
|
|
||||||
// the counters for the device as whole. This consumption is needed
|
|
||||||
// to handle MQ.
|
|
||||||
self.counters
|
|
||||||
.rx_bytes
|
|
||||||
.fetch_add(self.rx.counter_bytes.0, Ordering::AcqRel);
|
|
||||||
self.counters
|
|
||||||
.rx_frames
|
|
||||||
.fetch_add(self.rx.counter_frames.0, Ordering::AcqRel);
|
|
||||||
self.rx.counter_bytes = Wrapping(0);
|
|
||||||
self.rx.counter_frames = Wrapping(0);
|
|
||||||
|
|
||||||
if self.rx.deferred_irqs {
|
|
||||||
self.rx.deferred_irqs = false;
|
|
||||||
let mem = self
|
|
||||||
.mem
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
|
||||||
.map(|m| m.memory())?;
|
|
||||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resume_rx(&mut self, queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
|
||||||
if !self.rx_tap_listening {
|
|
||||||
register_listener(
|
|
||||||
self.epoll_fd.unwrap(),
|
|
||||||
self.tap.as_raw_fd(),
|
|
||||||
epoll::Events::EPOLLIN,
|
|
||||||
u64::from(self.tap_event_id),
|
|
||||||
)
|
|
||||||
.map_err(NetQueuePairError::RegisterListener)?;
|
|
||||||
self.rx_tap_listening = true;
|
|
||||||
info!("Listener registered");
|
|
||||||
}
|
|
||||||
if self.rx.deferred_frame {
|
|
||||||
if self.rx_single_frame(queue)? {
|
|
||||||
self.rx.deferred_frame = false;
|
|
||||||
// process_rx() was interrupted possibly before consuming all
|
|
||||||
// packets in the tap; try continuing now.
|
|
||||||
self.process_rx(queue)
|
|
||||||
} else if self.rx.deferred_irqs {
|
|
||||||
self.rx.deferred_irqs = false;
|
|
||||||
let mem = self
|
|
||||||
.mem
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
|
||||||
.map(|m| m.memory())?;
|
|
||||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_tx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
pub fn process_tx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
||||||
let mem = self
|
let mem = self
|
||||||
.mem
|
.mem
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
||||||
.map(|m| m.memory())?;
|
.map(|m| m.memory())?;
|
||||||
self.tx.process_desc_chain(&mem, &mut self.tap, &mut queue);
|
|
||||||
|
self.tx
|
||||||
|
.process_desc_chain(&mem, &mut self.tap, &mut queue)?;
|
||||||
|
|
||||||
self.counters
|
self.counters
|
||||||
.tx_bytes
|
.tx_bytes
|
||||||
@@ -355,26 +234,37 @@ impl NetQueuePair {
|
|||||||
Ok(queue.needs_notification(&mem, queue.next_used))
|
Ok(queue.needs_notification(&mem, queue.next_used))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_rx_tap(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
pub fn process_rx(&mut self, mut queue: &mut Queue) -> Result<bool, NetQueuePairError> {
|
||||||
if self.rx.deferred_frame
|
let mem = self
|
||||||
// Process a deferred frame first if available. Don't read from tap again
|
.mem
|
||||||
// until we manage to receive this deferred frame.
|
.as_ref()
|
||||||
{
|
.ok_or(NetQueuePairError::NoMemoryConfigured)
|
||||||
if self.rx_single_frame(&mut queue)? {
|
.map(|m| m.memory())?;
|
||||||
self.rx.deferred_frame = false;
|
|
||||||
self.process_rx(&mut queue)
|
|
||||||
} else if self.rx.deferred_irqs {
|
|
||||||
self.rx.deferred_irqs = false;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.process_rx(&mut queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_tap(&mut self) -> io::Result<usize> {
|
if self
|
||||||
self.tap.read(&mut self.rx.frame_buf)
|
.rx
|
||||||
|
.process_desc_chain(&mem, &mut self.tap, &mut queue)?
|
||||||
|
&& self.rx_tap_listening
|
||||||
|
{
|
||||||
|
unregister_listener(
|
||||||
|
self.epoll_fd.unwrap(),
|
||||||
|
self.tap.as_raw_fd(),
|
||||||
|
epoll::Events::EPOLLIN,
|
||||||
|
u64::from(self.tap_event_id),
|
||||||
|
)
|
||||||
|
.map_err(NetQueuePairError::UnregisterListener)?;
|
||||||
|
self.rx_tap_listening = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.counters
|
||||||
|
.rx_bytes
|
||||||
|
.fetch_add(self.rx.counter_bytes.0, Ordering::AcqRel);
|
||||||
|
self.counters
|
||||||
|
.rx_frames
|
||||||
|
.fetch_add(self.rx.counter_frames.0, Ordering::AcqRel);
|
||||||
|
self.rx.counter_bytes = Wrapping(0);
|
||||||
|
self.rx.counter_frames = Wrapping(0);
|
||||||
|
|
||||||
|
Ok(queue.needs_notification(&mem, queue.next_used))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.4.3"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "master" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
serde = {version = ">=1.0.27", features = ["rc"] }
|
serde = {version = ">=1.0.27", features = ["rc"] }
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = ">=1.0.27"
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ impl PciBus {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for (address, size, type_) in bars {
|
for (address, size, type_) in bars {
|
||||||
match type_ {
|
match type_ {
|
||||||
PciBarRegionType::IORegion => {
|
PciBarRegionType::IoRegion => {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
io_bus
|
io_bus
|
||||||
.insert(dev.clone(), address.raw_value(), size)
|
.insert(dev.clone(), address.raw_value(), size)
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ pub enum PciBridgeSubclass {
|
|||||||
PcmciaBridge = 0x05,
|
PcmciaBridge = 0x05,
|
||||||
NuBusBridge = 0x06,
|
NuBusBridge = 0x06,
|
||||||
CardBusBridge = 0x07,
|
CardBusBridge = 0x07,
|
||||||
RACEwayBridge = 0x08,
|
RacEwayBridge = 0x08,
|
||||||
PciToPciSemiTransparentBridge = 0x09,
|
PciToPciSemiTransparentBridge = 0x09,
|
||||||
InfiniBrandToPciHostBridge = 0x0a,
|
InfiniBrandToPciHostBridge = 0x0a,
|
||||||
OtherBridgeDevice = 0x80,
|
OtherBridgeDevice = 0x80,
|
||||||
@@ -117,9 +117,9 @@ impl PciSubclass for PciBridgeSubclass {
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum PciSerialBusSubClass {
|
pub enum PciSerialBusSubClass {
|
||||||
Firewire = 0x00,
|
Firewire = 0x00,
|
||||||
ACCESSbus = 0x01,
|
Accessbus = 0x01,
|
||||||
SSA = 0x02,
|
Ssa = 0x02,
|
||||||
USB = 0x03,
|
Usb = 0x03,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PciSubclass for PciSerialBusSubClass {
|
impl PciSubclass for PciSerialBusSubClass {
|
||||||
@@ -132,15 +132,15 @@ impl PciSubclass for PciSerialBusSubClass {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum PciMassStorageSubclass {
|
pub enum PciMassStorageSubclass {
|
||||||
SCSIStorage = 0x00,
|
ScsiStorage = 0x00,
|
||||||
IDEInterface = 0x01,
|
IdeInterface = 0x01,
|
||||||
FloppyController = 0x02,
|
FloppyController = 0x02,
|
||||||
IPIController = 0x03,
|
IpiController = 0x03,
|
||||||
RAIDController = 0x04,
|
RaidController = 0x04,
|
||||||
ATAController = 0x05,
|
AtaController = 0x05,
|
||||||
SATAController = 0x06,
|
SataController = 0x06,
|
||||||
SerialSCSIController = 0x07,
|
SerialScsiController = 0x07,
|
||||||
NVMController = 0x08,
|
NvmController = 0x08,
|
||||||
MassStorage = 0x80,
|
MassStorage = 0x80,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +156,11 @@ impl PciSubclass for PciMassStorageSubclass {
|
|||||||
pub enum PciNetworkControllerSubclass {
|
pub enum PciNetworkControllerSubclass {
|
||||||
EthernetController = 0x00,
|
EthernetController = 0x00,
|
||||||
TokenRingController = 0x01,
|
TokenRingController = 0x01,
|
||||||
FDDIController = 0x02,
|
FddiController = 0x02,
|
||||||
ATMController = 0x03,
|
AtmController = 0x03,
|
||||||
ISDNController = 0x04,
|
IsdnController = 0x04,
|
||||||
WorldFipController = 0x05,
|
WorldFipController = 0x05,
|
||||||
PICMGController = 0x06,
|
PicmgController = 0x06,
|
||||||
InfinibandController = 0x07,
|
InfinibandController = 0x07,
|
||||||
FabricController = 0x08,
|
FabricController = 0x08,
|
||||||
NetworkController = 0x80,
|
NetworkController = 0x80,
|
||||||
@@ -186,55 +186,55 @@ pub trait PciProgrammingInterface {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum PciCapabilityID {
|
pub enum PciCapabilityId {
|
||||||
ListID = 0,
|
ListId = 0,
|
||||||
PowerManagement = 0x01,
|
PowerManagement = 0x01,
|
||||||
AcceleratedGraphicsPort = 0x02,
|
AcceleratedGraphicsPort = 0x02,
|
||||||
VitalProductData = 0x03,
|
VitalProductData = 0x03,
|
||||||
SlotIdentification = 0x04,
|
SlotIdentification = 0x04,
|
||||||
MessageSignalledInterrupts = 0x05,
|
MessageSignalledInterrupts = 0x05,
|
||||||
CompactPCIHotSwap = 0x06,
|
CompactPciHotSwap = 0x06,
|
||||||
PCIX = 0x07,
|
PciX = 0x07,
|
||||||
HyperTransport = 0x08,
|
HyperTransport = 0x08,
|
||||||
VendorSpecific = 0x09,
|
VendorSpecific = 0x09,
|
||||||
Debugport = 0x0A,
|
Debugport = 0x0A,
|
||||||
CompactPCICentralResourceControl = 0x0B,
|
CompactPciCentralResourceControl = 0x0B,
|
||||||
PCIStandardHotPlugController = 0x0C,
|
PciStandardHotPlugController = 0x0C,
|
||||||
BridgeSubsystemVendorDeviceID = 0x0D,
|
BridgeSubsystemVendorDeviceId = 0x0D,
|
||||||
AGPTargetPCIPCIbridge = 0x0E,
|
AgpTargetPciPcibridge = 0x0E,
|
||||||
SecureDevice = 0x0F,
|
SecureDevice = 0x0F,
|
||||||
PCIExpress = 0x10,
|
PciExpress = 0x10,
|
||||||
MSIX = 0x11,
|
MsiX = 0x11,
|
||||||
SATADataIndexConf = 0x12,
|
SataDataIndexConf = 0x12,
|
||||||
PCIAdvancedFeatures = 0x13,
|
PciAdvancedFeatures = 0x13,
|
||||||
PCIEnhancedAllocation = 0x14,
|
PciEnhancedAllocation = 0x14,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for PciCapabilityID {
|
impl From<u8> for PciCapabilityId {
|
||||||
fn from(c: u8) -> Self {
|
fn from(c: u8) -> Self {
|
||||||
match c {
|
match c {
|
||||||
0 => PciCapabilityID::ListID,
|
0 => PciCapabilityId::ListId,
|
||||||
0x01 => PciCapabilityID::PowerManagement,
|
0x01 => PciCapabilityId::PowerManagement,
|
||||||
0x02 => PciCapabilityID::AcceleratedGraphicsPort,
|
0x02 => PciCapabilityId::AcceleratedGraphicsPort,
|
||||||
0x03 => PciCapabilityID::VitalProductData,
|
0x03 => PciCapabilityId::VitalProductData,
|
||||||
0x04 => PciCapabilityID::SlotIdentification,
|
0x04 => PciCapabilityId::SlotIdentification,
|
||||||
0x05 => PciCapabilityID::MessageSignalledInterrupts,
|
0x05 => PciCapabilityId::MessageSignalledInterrupts,
|
||||||
0x06 => PciCapabilityID::CompactPCIHotSwap,
|
0x06 => PciCapabilityId::CompactPciHotSwap,
|
||||||
0x07 => PciCapabilityID::PCIX,
|
0x07 => PciCapabilityId::PciX,
|
||||||
0x08 => PciCapabilityID::HyperTransport,
|
0x08 => PciCapabilityId::HyperTransport,
|
||||||
0x09 => PciCapabilityID::VendorSpecific,
|
0x09 => PciCapabilityId::VendorSpecific,
|
||||||
0x0A => PciCapabilityID::Debugport,
|
0x0A => PciCapabilityId::Debugport,
|
||||||
0x0B => PciCapabilityID::CompactPCICentralResourceControl,
|
0x0B => PciCapabilityId::CompactPciCentralResourceControl,
|
||||||
0x0C => PciCapabilityID::PCIStandardHotPlugController,
|
0x0C => PciCapabilityId::PciStandardHotPlugController,
|
||||||
0x0D => PciCapabilityID::BridgeSubsystemVendorDeviceID,
|
0x0D => PciCapabilityId::BridgeSubsystemVendorDeviceId,
|
||||||
0x0E => PciCapabilityID::AGPTargetPCIPCIbridge,
|
0x0E => PciCapabilityId::AgpTargetPciPcibridge,
|
||||||
0x0F => PciCapabilityID::SecureDevice,
|
0x0F => PciCapabilityId::SecureDevice,
|
||||||
0x10 => PciCapabilityID::PCIExpress,
|
0x10 => PciCapabilityId::PciExpress,
|
||||||
0x11 => PciCapabilityID::MSIX,
|
0x11 => PciCapabilityId::MsiX,
|
||||||
0x12 => PciCapabilityID::SATADataIndexConf,
|
0x12 => PciCapabilityId::SataDataIndexConf,
|
||||||
0x13 => PciCapabilityID::PCIAdvancedFeatures,
|
0x13 => PciCapabilityId::PciAdvancedFeatures,
|
||||||
0x14 => PciCapabilityID::PCIEnhancedAllocation,
|
0x14 => PciCapabilityId::PciEnhancedAllocation,
|
||||||
_ => PciCapabilityID::ListID,
|
_ => PciCapabilityId::ListId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +242,7 @@ impl From<u8> for PciCapabilityID {
|
|||||||
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
|
/// A PCI capability list. Devices can optionally specify capabilities in their configuration space.
|
||||||
pub trait PciCapability {
|
pub trait PciCapability {
|
||||||
fn bytes(&self) -> &[u8];
|
fn bytes(&self) -> &[u8];
|
||||||
fn id(&self) -> PciCapabilityID;
|
fn id(&self) -> PciCapabilityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
|
fn encode_32_bits_bar_size(bar_size: u32) -> Option<u32> {
|
||||||
@@ -317,7 +317,7 @@ pub struct PciConfiguration {
|
|||||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum PciBarRegionType {
|
pub enum PciBarRegionType {
|
||||||
Memory32BitRegion = 0,
|
Memory32BitRegion = 0,
|
||||||
IORegion = 0x01,
|
IoRegion = 0x01,
|
||||||
Memory64BitRegion = 0x04,
|
Memory64BitRegion = 0x04,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,7 +572,7 @@ impl PciConfiguration {
|
|||||||
.checked_add(config.size - 1)
|
.checked_add(config.size - 1)
|
||||||
.ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
|
.ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
|
||||||
match config.region_type {
|
match config.region_type {
|
||||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IORegion => {
|
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
|
||||||
if end_addr > u64::from(u32::max_value()) {
|
if end_addr > u64::from(u32::max_value()) {
|
||||||
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
||||||
}
|
}
|
||||||
@@ -614,7 +614,7 @@ impl PciConfiguration {
|
|||||||
BAR_MEM_ADDR_MASK,
|
BAR_MEM_ADDR_MASK,
|
||||||
config.prefetchable as u32 | config.region_type as u32,
|
config.prefetchable as u32 | config.region_type as u32,
|
||||||
),
|
),
|
||||||
PciBarRegionType::IORegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
|
PciBarRegionType::IoRegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||||
@@ -711,7 +711,7 @@ impl PciConfiguration {
|
|||||||
}
|
}
|
||||||
self.last_capability = Some((cap_offset, total_len));
|
self.last_capability = Some((cap_offset, total_len));
|
||||||
|
|
||||||
if cap_data.id() == PciCapabilityID::MSIX {
|
if cap_data.id() == PciCapabilityId::MsiX {
|
||||||
self.msix_cap_reg_idx = Some(cap_offset / 4);
|
self.msix_cap_reg_idx = Some(cap_offset / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,8 +1005,8 @@ mod tests {
|
|||||||
self.as_slice()
|
self.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id(&self) -> PciCapabilityID {
|
fn id(&self) -> PciCapabilityId {
|
||||||
PciCapabilityID::VendorSpecific
|
PciCapabilityId::VendorSpecific
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1056,11 +1056,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum TestPI {
|
enum TestPi {
|
||||||
Test = 0x5a,
|
Test = 0x5a,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PciProgrammingInterface for TestPI {
|
impl PciProgrammingInterface for TestPi {
|
||||||
fn get_register_value(&self) -> u8 {
|
fn get_register_value(&self) -> u8 {
|
||||||
*self as u8
|
*self as u8
|
||||||
}
|
}
|
||||||
@@ -1074,7 +1074,7 @@ mod tests {
|
|||||||
0x1,
|
0x1,
|
||||||
PciClassCode::MultimediaController,
|
PciClassCode::MultimediaController,
|
||||||
&PciMultimediaSubclass::AudioController,
|
&PciMultimediaSubclass::AudioController,
|
||||||
Some(&TestPI::Test),
|
Some(&TestPi::Test),
|
||||||
PciHeaderType::Device,
|
PciHeaderType::Device,
|
||||||
0xABCD,
|
0xABCD,
|
||||||
0x2468,
|
0x2468,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ mod vfio;
|
|||||||
|
|
||||||
pub use self::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
|
pub use self::bus::{PciBus, PciConfigIo, PciConfigMmio, PciRoot, PciRootError};
|
||||||
pub use self::configuration::{
|
pub use self::configuration::{
|
||||||
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityID,
|
PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability, PciCapabilityId,
|
||||||
PciClassCode, PciConfiguration, PciHeaderType, PciMassStorageSubclass,
|
PciClassCode, PciConfiguration, PciHeaderType, PciMassStorageSubclass,
|
||||||
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
|
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate vm_memory;
|
extern crate vm_memory;
|
||||||
|
|
||||||
use crate::{PciCapability, PciCapabilityID};
|
use crate::{PciCapability, PciCapabilityId};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -498,8 +498,8 @@ impl PciCapability for MsixCap {
|
|||||||
self.as_slice()
|
self.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id(&self) -> PciCapabilityID {
|
fn id(&self) -> PciCapabilityId {
|
||||||
PciCapabilityID::MSIX
|
PciCapabilityId::MsiX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,32 +7,30 @@ extern crate vm_allocator;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
msi_num_enabled_vectors, BarReprogrammingParams, MsiConfig, MsixCap, MsixConfig,
|
msi_num_enabled_vectors, BarReprogrammingParams, MsiConfig, MsixCap, MsixConfig,
|
||||||
PciBarConfiguration, PciBarRegionType, PciCapabilityID, PciClassCode, PciConfiguration,
|
PciBarConfiguration, PciBarRegionType, PciCapabilityId, PciClassCode, PciConfiguration,
|
||||||
PciDevice, PciDeviceError, PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE,
|
PciDevice, PciDeviceError, PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE,
|
||||||
};
|
};
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::{fmt, io, result};
|
use std::{fmt, io, result};
|
||||||
use vfio_bindings::bindings::vfio::*;
|
use vfio_bindings::bindings::vfio::*;
|
||||||
use vfio_ioctls::{VfioDevice, VfioError};
|
use vfio_ioctls::{VfioContainer, VfioDevice, VfioError};
|
||||||
use vm_allocator::SystemAllocator;
|
use vm_allocator::SystemAllocator;
|
||||||
use vm_device::interrupt::{
|
use vm_device::interrupt::{
|
||||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||||
};
|
};
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_memory::{
|
use vm_memory::{Address, GuestAddress, GuestUsize};
|
||||||
Address, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap, GuestRegionMmap,
|
|
||||||
GuestUsize,
|
|
||||||
};
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VfioPciError {
|
pub enum VfioPciError {
|
||||||
AllocateGsi,
|
AllocateGsi,
|
||||||
|
DmaMap(VfioError),
|
||||||
|
DmaUnmap(VfioError),
|
||||||
EnableIntx(VfioError),
|
EnableIntx(VfioError),
|
||||||
EnableMsi(VfioError),
|
EnableMsi(VfioError),
|
||||||
EnableMsix(VfioError),
|
EnableMsix(VfioError),
|
||||||
@@ -45,7 +43,6 @@ pub enum VfioPciError {
|
|||||||
MsixNotConfigured,
|
MsixNotConfigured,
|
||||||
NewVfioPciDevice,
|
NewVfioPciDevice,
|
||||||
SetGsiRouting(hypervisor::HypervisorVmError),
|
SetGsiRouting(hypervisor::HypervisorVmError),
|
||||||
UpdateMemory(VfioError),
|
|
||||||
}
|
}
|
||||||
pub type Result<T> = std::result::Result<T, VfioPciError>;
|
pub type Result<T> = std::result::Result<T, VfioPciError>;
|
||||||
|
|
||||||
@@ -53,6 +50,8 @@ impl fmt::Display for VfioPciError {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
VfioPciError::AllocateGsi => write!(f, "failed to allocate GSI"),
|
VfioPciError::AllocateGsi => write!(f, "failed to allocate GSI"),
|
||||||
|
VfioPciError::DmaMap(e) => write!(f, "failed to DMA map: {}", e),
|
||||||
|
VfioPciError::DmaUnmap(e) => write!(f, "failed to DMA unmap: {}", e),
|
||||||
VfioPciError::EnableIntx(e) => write!(f, "failed to enable INTx: {}", e),
|
VfioPciError::EnableIntx(e) => write!(f, "failed to enable INTx: {}", e),
|
||||||
VfioPciError::EnableMsi(e) => write!(f, "failed to enable MSI: {}", e),
|
VfioPciError::EnableMsi(e) => write!(f, "failed to enable MSI: {}", e),
|
||||||
VfioPciError::EnableMsix(e) => write!(f, "failed to enable MSI-X: {}", e),
|
VfioPciError::EnableMsix(e) => write!(f, "failed to enable MSI-X: {}", e),
|
||||||
@@ -69,7 +68,6 @@ impl fmt::Display for VfioPciError {
|
|||||||
VfioPciError::MsixNotConfigured => write!(f, "MSI-X interrupt not yet configured"),
|
VfioPciError::MsixNotConfigured => write!(f, "MSI-X interrupt not yet configured"),
|
||||||
VfioPciError::NewVfioPciDevice => write!(f, "failed to create VFIO PCI device"),
|
VfioPciError::NewVfioPciDevice => write!(f, "failed to create VFIO PCI device"),
|
||||||
VfioPciError::SetGsiRouting(e) => write!(f, "failed to set GSI routes: {}", e),
|
VfioPciError::SetGsiRouting(e) => write!(f, "failed to set GSI routes: {}", e),
|
||||||
VfioPciError::UpdateMemory(e) => write!(f, "failed to update memory: {}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,13 +184,13 @@ impl Interrupt {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accessed(&self, offset: u64) -> Option<(PciCapabilityID, u64)> {
|
fn accessed(&self, offset: u64) -> Option<(PciCapabilityId, u64)> {
|
||||||
if let Some(msi) = &self.msi {
|
if let Some(msi) = &self.msi {
|
||||||
if offset >= u64::from(msi.cap_offset)
|
if offset >= u64::from(msi.cap_offset)
|
||||||
&& offset < u64::from(msi.cap_offset) + msi.cfg.size()
|
&& offset < u64::from(msi.cap_offset) + msi.cfg.size()
|
||||||
{
|
{
|
||||||
return Some((
|
return Some((
|
||||||
PciCapabilityID::MessageSignalledInterrupts,
|
PciCapabilityId::MessageSignalledInterrupts,
|
||||||
u64::from(msi.cap_offset),
|
u64::from(msi.cap_offset),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -200,7 +198,7 @@ impl Interrupt {
|
|||||||
|
|
||||||
if let Some(msix) = &self.msix {
|
if let Some(msix) = &self.msix {
|
||||||
if offset == u64::from(msix.cap_offset) {
|
if offset == u64::from(msix.cap_offset) {
|
||||||
return Some((PciCapabilityID::MSIX, u64::from(msix.cap_offset)));
|
return Some((PciCapabilityId::MsiX, u64::from(msix.cap_offset)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,11 +296,12 @@ impl VfioPciConfig {
|
|||||||
pub struct VfioPciDevice {
|
pub struct VfioPciDevice {
|
||||||
vm: Arc<dyn hypervisor::Vm>,
|
vm: Arc<dyn hypervisor::Vm>,
|
||||||
device: Arc<VfioDevice>,
|
device: Arc<VfioDevice>,
|
||||||
|
container: Arc<VfioContainer>,
|
||||||
vfio_pci_configuration: VfioPciConfig,
|
vfio_pci_configuration: VfioPciConfig,
|
||||||
configuration: PciConfiguration,
|
configuration: PciConfiguration,
|
||||||
mmio_regions: Vec<MmioRegion>,
|
mmio_regions: Vec<MmioRegion>,
|
||||||
interrupt: Interrupt,
|
interrupt: Interrupt,
|
||||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
iommu_attached: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VfioPciDevice {
|
impl VfioPciDevice {
|
||||||
@@ -310,9 +309,10 @@ impl VfioPciDevice {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
device: VfioDevice,
|
device: VfioDevice,
|
||||||
|
container: Arc<VfioContainer>,
|
||||||
msi_interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
msi_interrupt_manager: &Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||||
legacy_interrupt_group: Option<Arc<Box<dyn InterruptSourceGroup>>>,
|
legacy_interrupt_group: Option<Arc<Box<dyn InterruptSourceGroup>>>,
|
||||||
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
iommu_attached: bool,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let device = Arc::new(device);
|
let device = Arc::new(device);
|
||||||
device.reset();
|
device.reset();
|
||||||
@@ -335,6 +335,7 @@ impl VfioPciDevice {
|
|||||||
let mut vfio_pci_device = VfioPciDevice {
|
let mut vfio_pci_device = VfioPciDevice {
|
||||||
vm: vm.clone(),
|
vm: vm.clone(),
|
||||||
device,
|
device,
|
||||||
|
container,
|
||||||
configuration,
|
configuration,
|
||||||
vfio_pci_configuration,
|
vfio_pci_configuration,
|
||||||
mmio_regions: Vec::new(),
|
mmio_regions: Vec::new(),
|
||||||
@@ -343,7 +344,7 @@ impl VfioPciDevice {
|
|||||||
msi: None,
|
msi: None,
|
||||||
msix: None,
|
msix: None,
|
||||||
},
|
},
|
||||||
mem,
|
iommu_attached,
|
||||||
};
|
};
|
||||||
|
|
||||||
vfio_pci_device.parse_capabilities(msi_interrupt_manager);
|
vfio_pci_device.parse_capabilities(msi_interrupt_manager);
|
||||||
@@ -353,6 +354,10 @@ impl VfioPciDevice {
|
|||||||
Ok(vfio_pci_device)
|
Ok(vfio_pci_device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iommu_attached(&self) -> bool {
|
||||||
|
self.iommu_attached
|
||||||
|
}
|
||||||
|
|
||||||
fn enable_intx(&mut self) -> Result<()> {
|
fn enable_intx(&mut self) -> Result<()> {
|
||||||
if let Some(intx) = &mut self.interrupt.intx {
|
if let Some(intx) = &mut self.interrupt.intx {
|
||||||
if !intx.enabled {
|
if !intx.enabled {
|
||||||
@@ -421,7 +426,7 @@ impl VfioPciDevice {
|
|||||||
|
|
||||||
self.device
|
self.device
|
||||||
.enable_msix(irq_fds.iter().collect())
|
.enable_msix(irq_fds.iter().collect())
|
||||||
.map_err(VfioPciError::EnableMsi)?;
|
.map_err(VfioPciError::EnableMsix)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -535,8 +540,8 @@ impl VfioPciDevice {
|
|||||||
.vfio_pci_configuration
|
.vfio_pci_configuration
|
||||||
.read_config_byte(cap_next.into());
|
.read_config_byte(cap_next.into());
|
||||||
|
|
||||||
match PciCapabilityID::from(cap_id) {
|
match PciCapabilityId::from(cap_id) {
|
||||||
PciCapabilityID::MessageSignalledInterrupts => {
|
PciCapabilityId::MessageSignalledInterrupts => {
|
||||||
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_MSI_IRQ_INDEX) {
|
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_MSI_IRQ_INDEX) {
|
||||||
if irq_info.count > 0 {
|
if irq_info.count > 0 {
|
||||||
// Parse capability only if the VFIO device
|
// Parse capability only if the VFIO device
|
||||||
@@ -545,7 +550,7 @@ impl VfioPciDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PciCapabilityID::MSIX => {
|
PciCapabilityId::MsiX => {
|
||||||
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_MSIX_IRQ_INDEX) {
|
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_MSIX_IRQ_INDEX) {
|
||||||
if irq_info.count > 0 {
|
if irq_info.count > 0 {
|
||||||
// Parse capability only if the VFIO device
|
// Parse capability only if the VFIO device
|
||||||
@@ -722,10 +727,24 @@ impl VfioPciDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_memory(&self, new_region: &Arc<GuestRegionMmap>) -> Result<()> {
|
pub fn dma_map(&self, iova: u64, size: u64, user_addr: u64) -> Result<()> {
|
||||||
self.device
|
if !self.iommu_attached {
|
||||||
.extend_dma_map(new_region)
|
self.container
|
||||||
.map_err(VfioPciError::UpdateMemory)
|
.vfio_dma_map(iova, size, user_addr)
|
||||||
|
.map_err(VfioPciError::DmaMap)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dma_unmap(&self, iova: u64, size: u64) -> Result<()> {
|
||||||
|
if !self.iommu_attached {
|
||||||
|
self.container
|
||||||
|
.vfio_dma_unmap(iova, size)
|
||||||
|
.map_err(VfioPciError::DmaUnmap)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mmio_regions(&self) -> Vec<MmioRegion> {
|
pub fn mmio_regions(&self) -> Vec<MmioRegion> {
|
||||||
@@ -752,14 +771,6 @@ impl Drop for VfioPciDevice {
|
|||||||
if self.interrupt.intx_in_use() {
|
if self.interrupt.intx_in_use() {
|
||||||
self.disable_intx();
|
self.disable_intx();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
|
||||||
.device
|
|
||||||
.unset_dma_map(self.mem.memory().deref())
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
error!("failed to remove all guest memory regions from iommu table");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,7 +867,7 @@ impl PciDevice for VfioPciDevice {
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
{
|
{
|
||||||
// IO BAR
|
// IO BAR
|
||||||
region_type = PciBarRegionType::IORegion;
|
region_type = PciBarRegionType::IoRegion;
|
||||||
|
|
||||||
// Clear first bit.
|
// Clear first bit.
|
||||||
lsb_size &= 0xffff_fffc;
|
lsb_size &= 0xffff_fffc;
|
||||||
@@ -963,14 +974,6 @@ impl PciDevice for VfioPciDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
|
||||||
.device
|
|
||||||
.setup_dma_map(self.mem.memory().deref())
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
error!("failed to add all guest memory regions into iommu table");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ranges)
|
Ok(ranges)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,7 +983,7 @@ impl PciDevice for VfioPciDevice {
|
|||||||
) -> std::result::Result<(), PciDeviceError> {
|
) -> std::result::Result<(), PciDeviceError> {
|
||||||
for region in self.mmio_regions.iter() {
|
for region in self.mmio_regions.iter() {
|
||||||
match region.type_ {
|
match region.type_ {
|
||||||
PciBarRegionType::IORegion => {
|
PciBarRegionType::IoRegion => {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
allocator.free_io_addresses(region.start, region.length);
|
allocator.free_io_addresses(region.start, region.length);
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
@@ -1026,12 +1029,12 @@ impl PciDevice for VfioPciDevice {
|
|||||||
if let Some((cap_id, cap_base)) = self.interrupt.accessed(reg) {
|
if let Some((cap_id, cap_base)) = self.interrupt.accessed(reg) {
|
||||||
let cap_offset: u64 = reg - cap_base + offset;
|
let cap_offset: u64 = reg - cap_base + offset;
|
||||||
match cap_id {
|
match cap_id {
|
||||||
PciCapabilityID::MessageSignalledInterrupts => {
|
PciCapabilityId::MessageSignalledInterrupts => {
|
||||||
if let Err(e) = self.update_msi_capabilities(cap_offset, data) {
|
if let Err(e) = self.update_msi_capabilities(cap_offset, data) {
|
||||||
error!("Could not update MSI capabilities: {}", e);
|
error!("Could not update MSI capabilities: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PciCapabilityID::MSIX => {
|
PciCapabilityId::MsiX => {
|
||||||
if let Err(e) = self.update_msix_capabilities(cap_offset, data) {
|
if let Err(e) = self.update_msix_capabilities(cap_offset, data) {
|
||||||
error!("Could not update MSI-X capabilities: {}", e);
|
error!("Could not update MSI-X capabilities: {}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,8 @@ license = "BSD-3-Clause"
|
|||||||
path = "src/qcow.rs"
|
path = "src/qcow.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.4.3"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
remain = "0.2.2"
|
remain = "0.2.2"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tempfile = "3.2.0"
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
|
|||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use vmm_sys_util::{
|
use vmm_sys_util::{
|
||||||
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
|
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
|
||||||
write_zeroes::WriteZeroes,
|
write_zeroes::WriteZeroesAt,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::raw_file::RawFile;
|
pub use crate::raw_file::RawFile;
|
||||||
@@ -1274,8 +1274,7 @@ impl QcowFile {
|
|||||||
// unallocated clusters already read back as zeroes.
|
// unallocated clusters already read back as zeroes.
|
||||||
if let Some(offset) = self.file_offset_read(curr_addr)? {
|
if let Some(offset) = self.file_offset_read(curr_addr)? {
|
||||||
// Partial cluster - zero it out.
|
// Partial cluster - zero it out.
|
||||||
self.raw_file.file_mut().seek(SeekFrom::Start(offset))?;
|
self.raw_file.file_mut().write_zeroes_at(offset, count)?;
|
||||||
self.raw_file.file_mut().write_zeroes(count)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1528,6 +1527,13 @@ impl PunchHole for QcowFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteZeroesAt for QcowFile {
|
||||||
|
fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize> {
|
||||||
|
self.punch_hole(offset, length as u64)?;
|
||||||
|
Ok(length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SeekHole for QcowFile {
|
impl SeekHole for QcowFile {
|
||||||
fn seek_hole(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
fn seek_hole(&mut self, offset: u64) -> io::Result<Option<u64>> {
|
||||||
match self.find_allocated_cluster(offset, false) {
|
match self.find_allocated_cluster(offset, false) {
|
||||||
@@ -1704,7 +1710,8 @@ pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use tempfile::tempfile;
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
|
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||||
|
|
||||||
fn valid_header_v3() -> Vec<u8> {
|
fn valid_header_v3() -> Vec<u8> {
|
||||||
vec![
|
vec![
|
||||||
@@ -1775,7 +1782,7 @@ mod tests {
|
|||||||
where
|
where
|
||||||
F: FnMut(RawFile),
|
F: FnMut(RawFile),
|
||||||
{
|
{
|
||||||
let mut disk_file: RawFile = RawFile::new(tempfile().unwrap(), false);
|
let mut disk_file: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), false);
|
||||||
disk_file.write_all(&header).unwrap();
|
disk_file.write_all(&header).unwrap();
|
||||||
disk_file.set_len(0x1_0000_0000).unwrap();
|
disk_file.set_len(0x1_0000_0000).unwrap();
|
||||||
disk_file.seek(SeekFrom::Start(0)).unwrap();
|
disk_file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
@@ -1787,7 +1794,7 @@ mod tests {
|
|||||||
where
|
where
|
||||||
F: FnMut(QcowFile),
|
F: FnMut(QcowFile),
|
||||||
{
|
{
|
||||||
let tmp: RawFile = RawFile::new(tempfile().unwrap(), direct);
|
let tmp: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), direct);
|
||||||
let qcow_file = QcowFile::new(tmp, 3, file_size).unwrap();
|
let qcow_file = QcowFile::new(tmp, 3, file_size).unwrap();
|
||||||
|
|
||||||
testfn(qcow_file); // File closed when the function exits.
|
testfn(qcow_file); // File closed when the function exits.
|
||||||
@@ -1796,7 +1803,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn default_header_v2() {
|
fn default_header_v2() {
|
||||||
let header = QcowHeader::create_for_size(2, 0x10_0000);
|
let header = QcowHeader::create_for_size(2, 0x10_0000);
|
||||||
let mut disk_file: RawFile = RawFile::new(tempfile().unwrap(), false);
|
let mut disk_file: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), false);
|
||||||
header
|
header
|
||||||
.write_to(&mut disk_file)
|
.write_to(&mut disk_file)
|
||||||
.expect("Failed to write header to temporary file.");
|
.expect("Failed to write header to temporary file.");
|
||||||
@@ -1807,7 +1814,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn default_header_v3() {
|
fn default_header_v3() {
|
||||||
let header = QcowHeader::create_for_size(3, 0x10_0000);
|
let header = QcowHeader::create_for_size(3, 0x10_0000);
|
||||||
let mut disk_file: RawFile = RawFile::new(tempfile().unwrap(), false);
|
let mut disk_file: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), false);
|
||||||
header
|
header
|
||||||
.write_to(&mut disk_file)
|
.write_to(&mut disk_file)
|
||||||
.expect("Failed to write header to temporary file.");
|
.expect("Failed to write header to temporary file.");
|
||||||
@@ -2040,7 +2047,7 @@ mod tests {
|
|||||||
struct Transfer {
|
struct Transfer {
|
||||||
pub write: bool,
|
pub write: bool,
|
||||||
pub addr: u64,
|
pub addr: u64,
|
||||||
};
|
}
|
||||||
|
|
||||||
// Write transactions from mkfs.ext4.
|
// Write transactions from mkfs.ext4.
|
||||||
let xfers: Vec<Transfer> = vec![
|
let xfers: Vec<Transfer> = vec![
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::fs::{File, Metadata};
|
|||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use vmm_sys_util::{seek_hole::SeekHole, write_zeroes::PunchHole};
|
use vmm_sys_util::{seek_hole::SeekHole, write_zeroes::PunchHole, write_zeroes::WriteZeroesAt};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RawFile {
|
pub struct RawFile {
|
||||||
@@ -281,6 +281,12 @@ impl Seek for RawFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WriteZeroesAt for RawFile {
|
||||||
|
fn write_zeroes_at(&mut self, offset: u64, length: usize) -> std::io::Result<usize> {
|
||||||
|
self.file.write_zeroes_at(offset, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PunchHole for RawFile {
|
impl PunchHole for RawFile {
|
||||||
fn punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()> {
|
fn punch_hole(&mut self, offset: u64, length: u64) -> std::io::Result<()> {
|
||||||
self.file.punch_hole(offset, length)
|
self.file.punch_hole(offset, length)
|
||||||
|
|||||||
105
release-notes.md
105
release-notes.md
@@ -1,17 +1,26 @@
|
|||||||
|
- [v0.14.0](#v0140)
|
||||||
|
- [Structured event monitoring](#structured-event-monitoring)
|
||||||
|
- [MSHV improvements](#mshv-improvements)
|
||||||
|
- [Improved aarch64 platform](#improved-aarch64-platform)
|
||||||
|
- [Updated hotplug documentation](#updated-hotplug-documentation)
|
||||||
|
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||||
|
- [Block device rate limiting](#block-device-rate-limiting)
|
||||||
|
- [Deprecations](#deprecations)
|
||||||
|
- [Contributors](#contributors)
|
||||||
- [v0.13.0](#v0130)
|
- [v0.13.0](#v0130)
|
||||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||||
- [Improve huge page support](#improve-huge-page-support)
|
- [Improved huge page support](#improved-huge-page-support)
|
||||||
- [MACvTAP support](#macvtap-support)
|
- [MACvTAP support](#macvtap-support)
|
||||||
- [VHD disk image support](#vhd-disk-image-support)
|
- [VHD disk image support](#vhd-disk-image-support)
|
||||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||||
- [Contributors](#contributors)
|
- [Contributors](#contributors-1)
|
||||||
- [v0.12.0](#v0120)
|
- [v0.12.0](#v0120)
|
||||||
- [ARM64 enhancements](#arm64-enhancements)
|
- [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)
|
- [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)
|
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||||
- [Enhanced "info" API](#enhanced-info-api)
|
- [Enhanced "info" API](#enhanced-info-api)
|
||||||
- [Contributors](#contributors-1)
|
- [Contributors](#contributors-2)
|
||||||
- [v0.11.0](#v0110)
|
- [v0.11.0](#v0110)
|
||||||
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
||||||
- [Windows Guest Support](#windows-guest-support)
|
- [Windows Guest Support](#windows-guest-support)
|
||||||
@@ -24,14 +33,14 @@
|
|||||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||||
- [Contributors](#contributors-2)
|
- [Contributors](#contributors-3)
|
||||||
- [v0.10.0](#v0100)
|
- [v0.10.0](#v0100)
|
||||||
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
||||||
- [Memory Zones](#memory-zones)
|
- [Memory Zones](#memory-zones)
|
||||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
||||||
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||||
- [Contributors](#contributors-3)
|
- [Contributors](#contributors-4)
|
||||||
- [v0.9.0](#v090)
|
- [v0.9.0](#v090)
|
||||||
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
||||||
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
||||||
@@ -45,7 +54,7 @@
|
|||||||
- [Intel SGX Support](#intel-sgx-support)
|
- [Intel SGX Support](#intel-sgx-support)
|
||||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||||
- [Contributors](#contributors-4)
|
- [Contributors](#contributors-5)
|
||||||
- [v0.8.0](#v080)
|
- [v0.8.0](#v080)
|
||||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||||
@@ -54,7 +63,7 @@
|
|||||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||||
- [Contributors](#contributors-5)
|
- [Contributors](#contributors-6)
|
||||||
- [v0.7.0](#v070)
|
- [v0.7.0](#v070)
|
||||||
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
||||||
- [Alternative `libc` Support](#alternative-libc-support)
|
- [Alternative `libc` Support](#alternative-libc-support)
|
||||||
@@ -64,14 +73,14 @@
|
|||||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||||
- [Updated Distribution Support](#updated-distribution-support)
|
- [Updated Distribution Support](#updated-distribution-support)
|
||||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||||
- [Contributors](#contributors-6)
|
- [Contributors](#contributors-7)
|
||||||
- [v0.6.0](#v060)
|
- [v0.6.0](#v060)
|
||||||
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
||||||
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
||||||
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
||||||
- [Command Line Interface](#command-line-interface)
|
- [Command Line Interface](#command-line-interface)
|
||||||
- [PVH Boot](#pvh-boot)
|
- [PVH Boot](#pvh-boot)
|
||||||
- [Contributors](#contributors-7)
|
- [Contributors](#contributors-8)
|
||||||
- [v0.5.1](#v051)
|
- [v0.5.1](#v051)
|
||||||
- [v0.5.0](#v050)
|
- [v0.5.0](#v050)
|
||||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||||
@@ -79,7 +88,7 @@
|
|||||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||||
- [Development Tools](#development-tools)
|
- [Development Tools](#development-tools)
|
||||||
- [Kata Containers Integration](#kata-containers-integration)
|
- [Kata Containers Integration](#kata-containers-integration)
|
||||||
- [Contributors](#contributors-8)
|
- [Contributors](#contributors-9)
|
||||||
- [v0.4.0](#v040)
|
- [v0.4.0](#v040)
|
||||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||||
@@ -88,7 +97,7 @@
|
|||||||
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
||||||
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
||||||
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
||||||
- [Contributors](#contributors-9)
|
- [Contributors](#contributors-10)
|
||||||
- [v0.3.0](#v030)
|
- [v0.3.0](#v030)
|
||||||
- [Block device offloading](#block-device-offloading)
|
- [Block device offloading](#block-device-offloading)
|
||||||
- [Network device backend](#network-device-backend)
|
- [Network device backend](#network-device-backend)
|
||||||
@@ -115,6 +124,78 @@
|
|||||||
- [Unit testing](#unit-testing)
|
- [Unit testing](#unit-testing)
|
||||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||||
|
|
||||||
|
# v0.14.0
|
||||||
|
|
||||||
|
This release has been tracked through the [0.14.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/17).
|
||||||
|
|
||||||
|
Highlights for `cloud-hypervisor` version 0.14.0 include:
|
||||||
|
|
||||||
|
### Structured event monitoring
|
||||||
|
|
||||||
|
A new option was added to the VMM `--event-monitor` which reports structured
|
||||||
|
events (JSON) over a file or file descriptor at key events in the lifecycle of
|
||||||
|
the VM. The list of events is limited at the moment but will be further
|
||||||
|
extended over subsequent releases. The events exposed form part of the Cloud
|
||||||
|
Hypervisor API surface.
|
||||||
|
|
||||||
|
### MSHV improvements
|
||||||
|
|
||||||
|
Basic support has been added for running Windows guests atop the MSHV
|
||||||
|
hypervisor as an alternative to KVM and further improvements have been made to
|
||||||
|
the MSHV support.
|
||||||
|
|
||||||
|
### Improved aarch64 platform
|
||||||
|
|
||||||
|
The aarch64 platform has been enhanced with more devices exposed to the running
|
||||||
|
VM including an enhanced serial UART.
|
||||||
|
|
||||||
|
### Updated hotplug documentation
|
||||||
|
|
||||||
|
The documentation for the hotplug support has been updated to reflect the use
|
||||||
|
of the `ch-remote` tool and to include details of `virtio-mem` based hotplug as
|
||||||
|
well as documenting hotplug of paravirtualised and VFIO devices.
|
||||||
|
|
||||||
|
### PTY control for serial and `virtio-console`
|
||||||
|
|
||||||
|
The `--serial` and `--console` parameters can now direct the console to a PTY
|
||||||
|
allowing programmatic control of the console from another process through the
|
||||||
|
PTY subsystem.
|
||||||
|
|
||||||
|
### Block device rate limiting
|
||||||
|
|
||||||
|
The block device performance can now be constrained as part of the VM
|
||||||
|
configuration allowing rate limiting. Full details of the controls are in the
|
||||||
|
[IO throttling doumentation.](docs/io_throttling.md)
|
||||||
|
|
||||||
|
|
||||||
|
### Deprecations
|
||||||
|
|
||||||
|
Deprecated features will be removed in a subsequent release and users should plan to use alternatives
|
||||||
|
|
||||||
|
* Support for booting with the "LinuxBoot" protocol for ELF and `bzImage`
|
||||||
|
binaries has been deprecated. When using direct boot users should configure
|
||||||
|
their kernel with `CONFIG_PVH=y`.
|
||||||
|
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
Many thanks to everyone who has contributed to our 0.14.0 release including
|
||||||
|
some new faces.
|
||||||
|
|
||||||
|
Bo Chen <chen.bo@intel.com>
|
||||||
|
Henry Wang <Henry.Wang@arm.com>
|
||||||
|
Iggy Jackson <iggy@theiggy.com>
|
||||||
|
Jiachen Zhang <zhangjiachen.jaycee@bytedance.com>
|
||||||
|
Michael Zhao <michael.zhao@arm.com>
|
||||||
|
Muminul Islam <muislam@microsoft.com>
|
||||||
|
Penny Zheng <Penny.Zheng@arm.com>
|
||||||
|
Rob Bradford <robert.bradford@intel.com>
|
||||||
|
Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||||
|
Vineeth Pillai <viremana@linux.microsoft.com>
|
||||||
|
Wei Liu <liuwe@microsoft.com>
|
||||||
|
William Douglas <william.r.douglas@gmail.com>
|
||||||
|
Zide Chen <zide.chen@intel.com>
|
||||||
|
|
||||||
# v0.13.0
|
# v0.13.0
|
||||||
|
|
||||||
This release has been tracked through the [0.13.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/16).
|
This release has been tracked through the [0.13.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/16).
|
||||||
@@ -128,7 +209,7 @@ devices that do not support MSI or MSI-X and instead rely on INTx interrupts.
|
|||||||
Most notably this widens the support to most NVIDIA cards with the proprietary
|
Most notably this widens the support to most NVIDIA cards with the proprietary
|
||||||
drivers.
|
drivers.
|
||||||
|
|
||||||
### Improve huge page support
|
### Improved huge page support
|
||||||
|
|
||||||
Through the addition of `hugepage_size` on `--memory` it is now possible to
|
Through the addition of `hugepage_size` on `--memory` it is now possible to
|
||||||
specify the desired size of the huge pages used when allocating the guest
|
specify the desired size of the huge pages used when allocating the guest
|
||||||
|
|||||||
@@ -1383,6 +1383,8 @@ CONFIG_INPUT_EVDEV=y
|
|||||||
CONFIG_INPUT_KEYBOARD=y
|
CONFIG_INPUT_KEYBOARD=y
|
||||||
# CONFIG_KEYBOARD_ATKBD is not set
|
# CONFIG_KEYBOARD_ATKBD is not set
|
||||||
# CONFIG_KEYBOARD_LKKBD is not set
|
# CONFIG_KEYBOARD_LKKBD is not set
|
||||||
|
CONFIG_KEYBOARD_GPIO=y
|
||||||
|
CONFIG_KEYBOARD_GPIO_POLLED=y
|
||||||
# CONFIG_KEYBOARD_NEWTON is not set
|
# CONFIG_KEYBOARD_NEWTON is not set
|
||||||
# CONFIG_KEYBOARD_OPENCORES is not set
|
# CONFIG_KEYBOARD_OPENCORES is not set
|
||||||
# CONFIG_KEYBOARD_SAMSUNG is not set
|
# CONFIG_KEYBOARD_SAMSUNG is not set
|
||||||
@@ -1444,13 +1446,14 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=1
|
|||||||
CONFIG_SERIAL_8250_FSL=y
|
CONFIG_SERIAL_8250_FSL=y
|
||||||
# CONFIG_SERIAL_8250_DW is not set
|
# CONFIG_SERIAL_8250_DW is not set
|
||||||
# CONFIG_SERIAL_8250_RT288X is not set
|
# CONFIG_SERIAL_8250_RT288X is not set
|
||||||
# CONFIG_SERIAL_OF_PLATFORM is not set
|
CONFIG_SERIAL_OF_PLATFORM=y
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-8250 serial port support
|
# Non-8250 serial port support
|
||||||
#
|
#
|
||||||
# CONFIG_SERIAL_AMBA_PL010 is not set
|
# CONFIG_SERIAL_AMBA_PL010 is not set
|
||||||
# CONFIG_SERIAL_AMBA_PL011 is not set
|
CONFIG_SERIAL_AMBA_PL011=y
|
||||||
|
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||||
# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set
|
# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set
|
||||||
# CONFIG_SERIAL_UARTLITE is not set
|
# CONFIG_SERIAL_UARTLITE is not set
|
||||||
CONFIG_SERIAL_CORE=y
|
CONFIG_SERIAL_CORE=y
|
||||||
@@ -1538,7 +1541,12 @@ CONFIG_PTP_1588_CLOCK=y
|
|||||||
# end of PTP clock support
|
# end of PTP clock support
|
||||||
|
|
||||||
# CONFIG_PINCTRL is not set
|
# CONFIG_PINCTRL is not set
|
||||||
# CONFIG_GPIOLIB is not set
|
CONFIG_GPIOLIB=y
|
||||||
|
CONFIG_GPIOLIB_FASTPATH_LIMIT=512
|
||||||
|
CONFIG_OF_GPIO=y
|
||||||
|
CONFIG_GPIO_ACPI=y
|
||||||
|
CONFIG_GPIOLIB_IRQCHIP=y
|
||||||
|
CONFIG_GPIO_PL061=y
|
||||||
# CONFIG_W1 is not set
|
# CONFIG_W1 is not set
|
||||||
CONFIG_POWER_RESET=y
|
CONFIG_POWER_RESET=y
|
||||||
# CONFIG_POWER_RESET_RESTART is not set
|
# CONFIG_POWER_RESET_RESTART is not set
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ cmd_help() {
|
|||||||
echo " --cargo Run the cargo tests."
|
echo " --cargo Run the cargo tests."
|
||||||
echo " --integration Run the integration tests."
|
echo " --integration Run the integration tests."
|
||||||
echo " --integration-sgx Run the SGX integration tests."
|
echo " --integration-sgx Run the SGX integration tests."
|
||||||
|
echo " --integration-vfio Run the VFIO integration tests."
|
||||||
echo " --integration-windows Run the Windows guest integration tests."
|
echo " --integration-windows Run the Windows guest integration tests."
|
||||||
echo " --libc Select the C library Cloud Hypervisor will be built against. Default is gnu"
|
echo " --libc Select the C library Cloud Hypervisor will be built against. Default is gnu"
|
||||||
echo " --volumes Hash separated volumes to be exported. Example --volumes /mnt:/mnt#/myvol:/myvol"
|
echo " --volumes Hash separated volumes to be exported. Example --volumes /mnt:/mnt#/myvol:/myvol"
|
||||||
@@ -298,6 +299,7 @@ cmd_tests() {
|
|||||||
cargo=false
|
cargo=false
|
||||||
integration=false
|
integration=false
|
||||||
integration_sgx=false
|
integration_sgx=false
|
||||||
|
integration_vfio=false
|
||||||
integration_windows=false
|
integration_windows=false
|
||||||
libc="gnu"
|
libc="gnu"
|
||||||
arg_vols=""
|
arg_vols=""
|
||||||
@@ -310,6 +312,7 @@ cmd_tests() {
|
|||||||
"--cargo") { cargo=true; } ;;
|
"--cargo") { cargo=true; } ;;
|
||||||
"--integration") { integration=true; } ;;
|
"--integration") { integration=true; } ;;
|
||||||
"--integration-sgx") { integration_sgx=true; } ;;
|
"--integration-sgx") { integration_sgx=true; } ;;
|
||||||
|
"--integration-vfio") { integration_vfio=true; } ;;
|
||||||
"--integration-windows") { integration_windows=true; } ;;
|
"--integration-windows") { integration_windows=true; } ;;
|
||||||
"--libc")
|
"--libc")
|
||||||
shift
|
shift
|
||||||
@@ -419,6 +422,25 @@ cmd_tests() {
|
|||||||
./scripts/run_integration_tests_sgx.sh "$@" || fix_dir_perms $? || exit $?
|
./scripts/run_integration_tests_sgx.sh "$@" || fix_dir_perms $? || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$integration_vfio" = true ] ; then
|
||||||
|
say "Running VFIO integration tests for $target..."
|
||||||
|
$DOCKER_RUNTIME run \
|
||||||
|
--workdir "$CTR_CLH_ROOT_DIR" \
|
||||||
|
--rm \
|
||||||
|
--privileged \
|
||||||
|
--security-opt seccomp=unconfined \
|
||||||
|
--ipc=host \
|
||||||
|
--net="$CTR_CLH_NET" \
|
||||||
|
--mount type=tmpfs,destination=/tmp \
|
||||||
|
--volume /dev:/dev \
|
||||||
|
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||||
|
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||||
|
--env USER="root" \
|
||||||
|
--env CH_LIBC="${libc}" \
|
||||||
|
"$CTR_IMAGE" \
|
||||||
|
./scripts/run_integration_tests_vfio.sh "$@" || fix_dir_perms $? || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$integration_windows" = true ] ; then
|
if [ "$integration_windows" = true ] ; then
|
||||||
say "Running Windows integration tests for $target..."
|
say "Running Windows integration tests for $target..."
|
||||||
$DOCKER_RUNTIME run \
|
$DOCKER_RUNTIME run \
|
||||||
|
|||||||
@@ -203,11 +203,6 @@ if [ $RES -ne 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create tap interface without multiple queues support for vhost_user_net test.
|
|
||||||
sudo ip tuntap add name vunet-tap0 mode tap
|
|
||||||
# Create tap interface with multiple queues support for vhost_user_net test.
|
|
||||||
sudo ip tuntap add name vunet-tap1 mode tap multi_queue
|
|
||||||
|
|
||||||
BUILD_TARGET="aarch64-unknown-linux-${CH_LIBC}"
|
BUILD_TARGET="aarch64-unknown-linux-${CH_LIBC}"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
TARGET_CC=""
|
TARGET_CC=""
|
||||||
@@ -231,8 +226,4 @@ export RUST_BACKTRACE=1
|
|||||||
time cargo test $features_test "tests::parallel::$test_filter"
|
time cargo test $features_test "tests::parallel::$test_filter"
|
||||||
RES=$?
|
RES=$?
|
||||||
|
|
||||||
# Tear vhost_user_net test network down
|
|
||||||
sudo ip link del vunet-tap0
|
|
||||||
sudo ip link del vunet-tap1
|
|
||||||
|
|
||||||
exit $RES
|
exit $RES
|
||||||
|
|||||||
28
scripts/run_integration_tests_vfio.sh
Executable file
28
scripts/run_integration_tests_vfio.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -x
|
||||||
|
|
||||||
|
source $HOME/.cargo/env
|
||||||
|
source $(dirname "$0")/test-util.sh
|
||||||
|
|
||||||
|
process_common_args "$@"
|
||||||
|
# For now these values are deafult for kvm
|
||||||
|
features_build=""
|
||||||
|
features_test="--features integration_tests"
|
||||||
|
|
||||||
|
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||||
|
CFLAGS=""
|
||||||
|
TARGET_CC=""
|
||||||
|
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||||
|
TARGET_CC="musl-gcc"
|
||||||
|
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cargo build --all --release $features_build --target $BUILD_TARGET
|
||||||
|
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||||
|
|
||||||
|
export RUST_BACKTRACE=1
|
||||||
|
|
||||||
|
time cargo test $features_test "tests::vfio::$test_filter"
|
||||||
|
RES=$?
|
||||||
|
|
||||||
|
exit $RES
|
||||||
@@ -9,6 +9,12 @@ process_common_args "$@"
|
|||||||
features_build=""
|
features_build=""
|
||||||
features_test="--features integration_tests"
|
features_test="--features integration_tests"
|
||||||
|
|
||||||
|
if [ "$hypervisor" = "mshv" ] ; then
|
||||||
|
features_build="--no-default-features --features mshv,common"
|
||||||
|
features_test="--no-default-features --features mshv,common,integration_tests"
|
||||||
|
fi
|
||||||
|
WIN_IMAGE_FILE="/root/workloads/windows-server-2019.raw"
|
||||||
|
OVMF_FW_FILE="/root/workloads/OVMF-4b47d0c6c8.fd"
|
||||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
TARGET_CC=""
|
TARGET_CC=""
|
||||||
@@ -17,9 +23,15 @@ TARGET_CC="musl-gcc"
|
|||||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if the images are present
|
||||||
|
if [[ ! -f ${WIN_IMAGE_FILE} || ! -f ${OVMF_FW_FILE} ]]; then
|
||||||
|
echo "Windows image/firmware not present in the host"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Use device mapper to create a snapshot of the Windows image
|
# Use device mapper to create a snapshot of the Windows image
|
||||||
img_blk_size=$(du -b -B 512 /root/workloads/windows-server-2019.raw | awk '{print $1;}')
|
img_blk_size=$(du -b -B 512 ${WIN_IMAGE_FILE} | awk '{print $1;}')
|
||||||
loop_device=$(losetup --find --show --read-only /root/workloads/windows-server-2019.raw)
|
loop_device=$(losetup --find --show --read-only ${WIN_IMAGE_FILE})
|
||||||
dmsetup create windows-base --table "0 $img_blk_size linear $loop_device 0"
|
dmsetup create windows-base --table "0 $img_blk_size linear $loop_device 0"
|
||||||
dmsetup mknodes
|
dmsetup mknodes
|
||||||
dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
|
dmsetup create windows-snapshot-base --table "0 $img_blk_size snapshot-origin /dev/mapper/windows-base"
|
||||||
|
|||||||
@@ -187,33 +187,6 @@ cp $FOCAL_OS_IMAGE $VFIO_DIR
|
|||||||
cp $FW $VFIO_DIR
|
cp $FW $VFIO_DIR
|
||||||
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
||||||
|
|
||||||
# VFIO test network setup.
|
|
||||||
# We reserve a different IP class for it: 172.18.0.0/24.
|
|
||||||
sudo ip link add name vfio-br0 type bridge
|
|
||||||
sudo ip link set vfio-br0 up
|
|
||||||
sudo ip addr add 172.18.0.1/24 dev vfio-br0
|
|
||||||
|
|
||||||
sudo ip tuntap add vfio-tap0 mode tap
|
|
||||||
sudo ip link set vfio-tap0 master vfio-br0
|
|
||||||
sudo ip link set vfio-tap0 up
|
|
||||||
|
|
||||||
sudo ip tuntap add vfio-tap1 mode tap
|
|
||||||
sudo ip link set vfio-tap1 master vfio-br0
|
|
||||||
sudo ip link set vfio-tap1 up
|
|
||||||
|
|
||||||
sudo ip tuntap add vfio-tap2 mode tap
|
|
||||||
sudo ip link set vfio-tap2 master vfio-br0
|
|
||||||
sudo ip link set vfio-tap2 up
|
|
||||||
|
|
||||||
sudo ip tuntap add vfio-tap3 mode tap
|
|
||||||
sudo ip link set vfio-tap3 master vfio-br0
|
|
||||||
sudo ip link set vfio-tap3 up
|
|
||||||
|
|
||||||
# Create tap interface without multiple queues support for vhost_user_net test.
|
|
||||||
sudo ip tuntap add name vunet-tap0 mode tap
|
|
||||||
# Create tap interface with multiple queues support for vhost_user_net test.
|
|
||||||
sudo ip tuntap add name vunet-tap1 mode tap multi_queue
|
|
||||||
|
|
||||||
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
TARGET_CC=""
|
TARGET_CC=""
|
||||||
@@ -253,15 +226,4 @@ if [ $RES -eq 0 ]; then
|
|||||||
RES=$?
|
RES=$?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Tear VFIO test network down
|
|
||||||
sudo ip link del vfio-br0
|
|
||||||
sudo ip link del vfio-tap0
|
|
||||||
sudo ip link del vfio-tap1
|
|
||||||
sudo ip link del vfio-tap2
|
|
||||||
sudo ip link del vfio-tap3
|
|
||||||
|
|
||||||
# Tear vhost_user_net test network down
|
|
||||||
sudo ip link del vunet-tap0
|
|
||||||
sudo ip link del vunet-tap1
|
|
||||||
|
|
||||||
exit $RES
|
exit $RES
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use std::process;
|
|||||||
enum Error {
|
enum Error {
|
||||||
Connect(std::io::Error),
|
Connect(std::io::Error),
|
||||||
ApiClient(ApiClientError),
|
ApiClient(ApiClientError),
|
||||||
InvalidCPUCount(std::num::ParseIntError),
|
InvalidCpuCount(std::num::ParseIntError),
|
||||||
InvalidMemorySize(ByteSizedParseError),
|
InvalidMemorySize(ByteSizedParseError),
|
||||||
InvalidBalloonSize(ByteSizedParseError),
|
InvalidBalloonSize(ByteSizedParseError),
|
||||||
AddDeviceConfig(vmm::config::Error),
|
AddDeviceConfig(vmm::config::Error),
|
||||||
@@ -39,7 +39,7 @@ impl fmt::Display for Error {
|
|||||||
match self {
|
match self {
|
||||||
ApiClient(e) => e.fmt(f),
|
ApiClient(e) => e.fmt(f),
|
||||||
Connect(e) => write!(f, "Error opening HTTP socket: {}", e),
|
Connect(e) => write!(f, "Error opening HTTP socket: {}", e),
|
||||||
InvalidCPUCount(e) => write!(f, "Error parsing CPU count: {}", e),
|
InvalidCpuCount(e) => write!(f, "Error parsing CPU count: {}", e),
|
||||||
InvalidMemorySize(e) => write!(f, "Error parsing memory size: {:?}", e),
|
InvalidMemorySize(e) => write!(f, "Error parsing memory size: {:?}", e),
|
||||||
InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {:?}", e),
|
InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {:?}", e),
|
||||||
AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {}", e),
|
AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {}", e),
|
||||||
@@ -60,7 +60,7 @@ fn resize_api_command(
|
|||||||
balloon: Option<&str>,
|
balloon: Option<&str>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let desired_vcpus: Option<u8> = if let Some(cpus) = cpus {
|
let desired_vcpus: Option<u8> = if let Some(cpus) = cpus {
|
||||||
Some(cpus.parse().map_err(Error::InvalidCPUCount)?)
|
Some(cpus.parse().map_err(Error::InvalidCpuCount)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|||||||
133
src/main.rs
133
src/main.rs
@@ -11,15 +11,21 @@ extern crate vmm_sys_util;
|
|||||||
#[macro_use(crate_authors)]
|
#[macro_use(crate_authors)]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate event_monitor;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgGroup, ArgMatches};
|
use clap::{App, Arg, ArgGroup, ArgMatches};
|
||||||
use libc::EFD_NONBLOCK;
|
use libc::EFD_NONBLOCK;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
use option_parser::OptionParser;
|
||||||
use seccomp::SeccompAction;
|
use seccomp::SeccompAction;
|
||||||
use signal_hook::{
|
use signal_hook::{
|
||||||
consts::SIGSYS,
|
consts::SIGSYS,
|
||||||
iterator::{exfiltrator::WithRawSiginfo, SignalsInfo},
|
iterator::{exfiltrator::WithRawSiginfo, SignalsInfo},
|
||||||
};
|
};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::unix::io::FromRawFd;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@@ -30,7 +36,7 @@ use vmm_sys_util::eventfd::EventFd;
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
#[error("Failed to create API EventFd: {0}")]
|
#[error("Failed to create API EventFd: {0}")]
|
||||||
CreateAPIEventFd(#[source] std::io::Error),
|
CreateApiEventFd(#[source] std::io::Error),
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "kvm",
|
feature = "kvm",
|
||||||
error("Failed to open hypervisor interface (is /dev/kvm available?): {0}")
|
error("Failed to open hypervisor interface (is /dev/kvm available?): {0}")
|
||||||
@@ -41,7 +47,7 @@ enum Error {
|
|||||||
)]
|
)]
|
||||||
CreateHypervisor(#[source] hypervisor::HypervisorError),
|
CreateHypervisor(#[source] hypervisor::HypervisorError),
|
||||||
#[error("Failed to start the VMM thread: {0}")]
|
#[error("Failed to start the VMM thread: {0}")]
|
||||||
StartVMMThread(#[source] vmm::Error),
|
StartVmmThread(#[source] vmm::Error),
|
||||||
#[error("Error parsing config: {0}")]
|
#[error("Error parsing config: {0}")]
|
||||||
ParsingConfig(vmm::config::Error),
|
ParsingConfig(vmm::config::Error),
|
||||||
#[error("Error creating VM: {0:?}")]
|
#[error("Error creating VM: {0:?}")]
|
||||||
@@ -115,7 +121,6 @@ fn create_app<'a, 'b>(
|
|||||||
default_vcpus: &'a str,
|
default_vcpus: &'a str,
|
||||||
default_memory: &'a str,
|
default_memory: &'a str,
|
||||||
default_rng: &'a str,
|
default_rng: &'a str,
|
||||||
api_server_path: &'a str,
|
|
||||||
) -> App<'a, 'b> {
|
) -> App<'a, 'b> {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
let mut app: App;
|
let mut app: App;
|
||||||
@@ -312,7 +317,14 @@ fn create_app<'a, 'b>(
|
|||||||
.help("HTTP API socket path (UNIX domain socket).")
|
.help("HTTP API socket path (UNIX domain socket).")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.min_values(1)
|
.min_values(1)
|
||||||
.default_value(&api_server_path)
|
.group("vmm-config"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("event-monitor")
|
||||||
|
.long("event-monitor")
|
||||||
|
.help("File to report events on: path=</path/to/a/file> or fd=<fd>")
|
||||||
|
.takes_value(true)
|
||||||
|
.min_values(1)
|
||||||
.group("vmm-config"),
|
.group("vmm-config"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@@ -343,12 +355,23 @@ fn create_app<'a, 'b>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
{
|
||||||
|
app = app.arg(
|
||||||
|
Arg::with_name("tdx")
|
||||||
|
.long("tdx")
|
||||||
|
.help("TDX Support: firmware=<tdvf path>")
|
||||||
|
.takes_value(true)
|
||||||
|
.group("vm-config"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &str) -> Result<(), Error> {
|
fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &Option<String>) -> Result<(), Error> {
|
||||||
let (api_request_sender, api_request_receiver) = channel();
|
let (api_request_sender, api_request_receiver) = channel();
|
||||||
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateAPIEventFd)?;
|
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
|
||||||
|
|
||||||
let http_sender = api_request_sender.clone();
|
let http_sender = api_request_sender.clone();
|
||||||
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.value_of("seccomp") {
|
let seccomp_action = if let Some(seccomp_value) = cmd_arguments.value_of("seccomp") {
|
||||||
@@ -389,6 +412,8 @@ fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &str) -> Result<(), Err
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event!("vmm", "starting");
|
||||||
|
|
||||||
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?;
|
||||||
let vmm_thread = vmm::start_vmm_thread(
|
let vmm_thread = vmm::start_vmm_thread(
|
||||||
env!("CARGO_PKG_VERSION").to_string(),
|
env!("CARGO_PKG_VERSION").to_string(),
|
||||||
@@ -399,26 +424,14 @@ fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &str) -> Result<(), Err
|
|||||||
&seccomp_action,
|
&seccomp_action,
|
||||||
hypervisor,
|
hypervisor,
|
||||||
)
|
)
|
||||||
.map_err(Error::StartVMMThread)?;
|
.map_err(Error::StartVmmThread)?;
|
||||||
|
|
||||||
// Can't test for "vm-config" group as some have default values. The kernel
|
// Can't test for "vm-config" group as some have default values. The kernel
|
||||||
// is the only required option for booting the VM.
|
// is the only required option for booting the VM.
|
||||||
if cmd_arguments.is_present("kernel") {
|
if cmd_arguments.is_present("kernel") || cmd_arguments.is_present("tdx") {
|
||||||
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
let vm_params = config::VmParams::from_arg_matches(&cmd_arguments);
|
||||||
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||||
|
|
||||||
println!(
|
|
||||||
"Cloud Hypervisor Guest\n\tAPI server: {}\n\tvCPUs: {}\n\tMemory: {} MB\n\tKernel: \
|
|
||||||
{:?}\n\tInitramfs: {:?}\n\tKernel cmdline: {}\n\tDisk(s): {:?}",
|
|
||||||
api_socket_path,
|
|
||||||
vm_config.cpus.boot_vcpus,
|
|
||||||
vm_config.memory.size >> 20,
|
|
||||||
vm_config.kernel,
|
|
||||||
vm_config.initramfs,
|
|
||||||
vm_config.cmdline.args.as_str(),
|
|
||||||
vm_config.disks,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create and boot the VM based off the VM config we just built.
|
// Create and boot the VM based off the VM config we just built.
|
||||||
let sender = api_request_sender.clone();
|
let sender = api_request_sender.clone();
|
||||||
vmm::api::vm_create(
|
vmm::api::vm_create(
|
||||||
@@ -447,31 +460,9 @@ fn main() {
|
|||||||
// Ensure all created files (.e.g sockets) are only accessible by this user
|
// Ensure all created files (.e.g sockets) are only accessible by this user
|
||||||
let _ = unsafe { libc::umask(0o077) };
|
let _ = unsafe { libc::umask(0o077) };
|
||||||
|
|
||||||
let pid = unsafe { libc::getpid() };
|
|
||||||
let uid = unsafe { libc::getuid() };
|
|
||||||
|
|
||||||
let mut api_server_path = format! {"/run/user/{}/cloud-hypervisor.{}", uid, pid};
|
|
||||||
if uid == 0 {
|
|
||||||
// If we're running as root, we try to get the real user ID if we've been sudo'ed
|
|
||||||
// or else create our socket directly under /run.
|
|
||||||
let key = "SUDO_UID";
|
|
||||||
match env::var(key) {
|
|
||||||
Ok(sudo_uid) => {
|
|
||||||
api_server_path = format! {"/run/user/{}/cloud-hypervisor.{}", sudo_uid, pid}
|
|
||||||
}
|
|
||||||
Err(_) => api_server_path = format! {"/run/cloud-hypervisor.{}", pid},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||||
|
|
||||||
let cmd_arguments = create_app(
|
let cmd_arguments = create_app(&default_vcpus, &default_memory, &default_rng).get_matches();
|
||||||
&default_vcpus,
|
|
||||||
&default_memory,
|
|
||||||
&default_rng,
|
|
||||||
&api_server_path,
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let log_level = match cmd_arguments.occurrences_of("v") {
|
let log_level = match cmd_arguments.occurrences_of("v") {
|
||||||
0 => LevelFilter::Warn,
|
0 => LevelFilter::Warn,
|
||||||
@@ -496,17 +487,42 @@ fn main() {
|
|||||||
.map(|()| log::set_max_level(log_level))
|
.map(|()| log::set_max_level(log_level))
|
||||||
.expect("Expected to be able to setup logger");
|
.expect("Expected to be able to setup logger");
|
||||||
|
|
||||||
let api_socket_path = cmd_arguments
|
let api_socket_path = cmd_arguments.value_of("api-socket").map(|s| s.to_string());
|
||||||
.value_of("api-socket")
|
|
||||||
.expect("Missing argument: api-socket")
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
if let Err(e) = start_vmm(cmd_arguments, &api_socket_path) {
|
if let Some(monitor_config) = cmd_arguments.value_of("event-monitor") {
|
||||||
eprintln!("{}", e);
|
let mut parser = OptionParser::new();
|
||||||
std::fs::remove_file(api_socket_path).ok();
|
parser.add("path").add("fd");
|
||||||
std::process::exit(1);
|
parser
|
||||||
|
.parse(monitor_config)
|
||||||
|
.expect("Error parsing event monitor config");
|
||||||
|
|
||||||
|
let file = if parser.is_set("fd") {
|
||||||
|
let fd = parser
|
||||||
|
.convert("fd")
|
||||||
|
.expect("Integral file descriptor expected")
|
||||||
|
.unwrap();
|
||||||
|
unsafe { File::from_raw_fd(fd) }
|
||||||
|
} else if parser.is_set("path") {
|
||||||
|
std::fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(parser.get("path").unwrap())
|
||||||
|
.expect("Error opening event monitor path")
|
||||||
|
} else {
|
||||||
|
panic!("--event-monitor requires either a fd or path provided")
|
||||||
|
};
|
||||||
|
event_monitor::set_monitor(file).expect("Expected setting monitor to succeed");
|
||||||
}
|
}
|
||||||
std::fs::remove_file(api_socket_path).ok();
|
|
||||||
|
let exit_code = if let Err(e) = start_vmm(cmd_arguments, &api_socket_path) {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
api_socket_path.map(|s| std::fs::remove_file(s).ok());
|
||||||
|
std::process::exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -525,15 +541,8 @@ mod unit_tests {
|
|||||||
|
|
||||||
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
fn get_vm_config_from_vec(args: &[&str]) -> VmConfig {
|
||||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||||
let api_server_path = "";
|
let cmd_arguments =
|
||||||
|
create_app(&default_vcpus, &default_memory, &default_rng).get_matches_from(args);
|
||||||
let cmd_arguments = create_app(
|
|
||||||
&default_vcpus,
|
|
||||||
&default_memory,
|
|
||||||
&default_rng,
|
|
||||||
&api_server_path,
|
|
||||||
)
|
|
||||||
.get_matches_from(args);
|
|
||||||
|
|
||||||
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
||||||
|
|
||||||
@@ -623,6 +632,8 @@ mod unit_tests {
|
|||||||
sgx_epc: None,
|
sgx_epc: None,
|
||||||
numa: None,
|
numa: None,
|
||||||
watchdog: false,
|
watchdog: false,
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
tdx: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
aver_eq!(tb, expected_vm_config, result_vm_config);
|
aver_eq!(tb, expected_vm_config, result_vm_config);
|
||||||
|
|||||||
13
test_infra/Cargo.toml
Normal file
13
test_infra/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "test_infra"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dirs = "3.0.1"
|
||||||
|
epoll = "4.3.1"
|
||||||
|
libc = "0.2.91"
|
||||||
|
ssh2 = "0.9.1"
|
||||||
|
vmm-sys-util = "0.8.0"
|
||||||
|
wait-timeout = "0.2.0"
|
||||||
582
test_infra/src/lib.rs
Normal file
582
test_infra/src/lib.rs
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use ssh2::Session;
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::net::TcpListener;
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::thread;
|
||||||
|
use vmm_sys_util::tempdir::TempDir;
|
||||||
|
|
||||||
|
pub const DEFAULT_TCP_LISTENER_MESSAGE: &str = "booted";
|
||||||
|
|
||||||
|
pub struct GuestNetworkConfig {
|
||||||
|
pub guest_ip: String,
|
||||||
|
pub l2_guest_ip1: String,
|
||||||
|
pub l2_guest_ip2: String,
|
||||||
|
pub l2_guest_ip3: String,
|
||||||
|
pub host_ip: String,
|
||||||
|
pub guest_mac: String,
|
||||||
|
pub l2_guest_mac1: String,
|
||||||
|
pub l2_guest_mac2: String,
|
||||||
|
pub l2_guest_mac3: String,
|
||||||
|
pub tcp_listener_port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DEFAULT_TCP_LISTENER_PORT: u16 = 8000;
|
||||||
|
pub const DEFAULT_TCP_LISTENER_TIMEOUT: i32 = 80;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WaitForBootError {
|
||||||
|
EpollWait(std::io::Error),
|
||||||
|
Listen(std::io::Error),
|
||||||
|
EpollWaitTimeout,
|
||||||
|
WrongGuestAddr,
|
||||||
|
Accept(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestNetworkConfig {
|
||||||
|
pub fn wait_vm_boot(&self, custom_timeout: Option<i32>) -> Result<(), WaitForBootError> {
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
// The 'port' is unique per 'GUEST' and listening to wild-card ip avoids retrying on 'TcpListener::bind()'
|
||||||
|
let listen_addr = format!("0.0.0.0:{}", self.tcp_listener_port);
|
||||||
|
let expected_guest_addr = self.guest_ip.as_str();
|
||||||
|
let mut s = String::new();
|
||||||
|
let timeout = match custom_timeout {
|
||||||
|
Some(t) => t,
|
||||||
|
None => DEFAULT_TCP_LISTENER_TIMEOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
match (|| -> Result<(), WaitForBootError> {
|
||||||
|
let listener =
|
||||||
|
TcpListener::bind(&listen_addr.as_str()).map_err(WaitForBootError::Listen)?;
|
||||||
|
listener
|
||||||
|
.set_nonblocking(true)
|
||||||
|
.expect("Cannot set non-blocking for tcp listener");
|
||||||
|
|
||||||
|
// Reply on epoll w/ timeout to wait for guest connections faithfully
|
||||||
|
let epoll_fd = epoll::create(true).expect("Cannot create epoll fd");
|
||||||
|
epoll::ctl(
|
||||||
|
epoll_fd,
|
||||||
|
epoll::ControlOptions::EPOLL_CTL_ADD,
|
||||||
|
listener.as_raw_fd(),
|
||||||
|
epoll::Event::new(epoll::Events::EPOLLIN, 0),
|
||||||
|
)
|
||||||
|
.expect("Cannot add 'tcp_listener' event to epoll");
|
||||||
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 1];
|
||||||
|
loop {
|
||||||
|
let num_events = match epoll::wait(epoll_fd, timeout * 1000_i32, &mut events[..]) {
|
||||||
|
Ok(num_events) => Ok(num_events),
|
||||||
|
Err(e) => match e.raw_os_error() {
|
||||||
|
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.map_err(WaitForBootError::EpollWait)?;
|
||||||
|
if num_events == 0 {
|
||||||
|
return Err(WaitForBootError::EpollWaitTimeout);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match listener.accept() {
|
||||||
|
Ok((_, addr)) => {
|
||||||
|
// Make sure the connection is from the expected 'guest_addr'
|
||||||
|
if addr.ip() != std::net::IpAddr::from_str(expected_guest_addr).unwrap() {
|
||||||
|
s = format!(
|
||||||
|
"Expecting the guest ip '{}' while being connected with ip '{}'",
|
||||||
|
expected_guest_addr,
|
||||||
|
addr.ip()
|
||||||
|
);
|
||||||
|
return Err(WaitForBootError::WrongGuestAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
s = "TcpListener::accept() failed".to_string();
|
||||||
|
Err(WaitForBootError::Accept(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})() {
|
||||||
|
Err(e) => {
|
||||||
|
let duration = start.elapsed();
|
||||||
|
eprintln!(
|
||||||
|
"\n\n==== Start 'wait_vm_boot' (FAILED) ====\n\n\
|
||||||
|
duration =\"{:?}, timeout = {}s\"\n\
|
||||||
|
listen_addr=\"{}\"\n\
|
||||||
|
expected_guest_addr=\"{}\"\n\
|
||||||
|
message=\"{}\"\n\
|
||||||
|
error=\"{:?}\"\n\
|
||||||
|
\n==== End 'wait_vm_boot' outout ====\n\n",
|
||||||
|
duration, timeout, listen_addr, expected_guest_addr, s, e
|
||||||
|
);
|
||||||
|
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DiskType {
|
||||||
|
OperatingSystem,
|
||||||
|
CloudInit,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DiskConfig {
|
||||||
|
fn prepare_files(&mut self, tmp_dir: &TempDir, network: &GuestNetworkConfig);
|
||||||
|
fn prepare_cloudinit(&self, tmp_dir: &TempDir, network: &GuestNetworkConfig) -> String;
|
||||||
|
fn disk(&self, disk_type: DiskType) -> Option<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UbuntuDiskConfig {
|
||||||
|
osdisk_path: String,
|
||||||
|
cloudinit_path: String,
|
||||||
|
image_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UbuntuDiskConfig {
|
||||||
|
pub fn new(image_name: String) -> Self {
|
||||||
|
UbuntuDiskConfig {
|
||||||
|
image_name,
|
||||||
|
osdisk_path: String::new(),
|
||||||
|
cloudinit_path: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowsDiskConfig {
|
||||||
|
image_name: String,
|
||||||
|
osdisk_path: String,
|
||||||
|
loopback_device: String,
|
||||||
|
windows_snapshot_cow: String,
|
||||||
|
windows_snapshot: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowsDiskConfig {
|
||||||
|
pub fn new(image_name: String) -> Self {
|
||||||
|
WindowsDiskConfig {
|
||||||
|
image_name,
|
||||||
|
osdisk_path: String::new(),
|
||||||
|
loopback_device: String::new(),
|
||||||
|
windows_snapshot_cow: String::new(),
|
||||||
|
windows_snapshot: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WindowsDiskConfig {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// dmsetup remove windows-snapshot-1
|
||||||
|
std::process::Command::new("dmsetup")
|
||||||
|
.arg("remove")
|
||||||
|
.arg(self.windows_snapshot.as_str())
|
||||||
|
.output()
|
||||||
|
.expect("Expect removing Windows snapshot with 'dmsetup' to succeed");
|
||||||
|
|
||||||
|
// dmsetup remove windows-snapshot-cow-1
|
||||||
|
std::process::Command::new("dmsetup")
|
||||||
|
.arg("remove")
|
||||||
|
.arg(self.windows_snapshot_cow.as_str())
|
||||||
|
.output()
|
||||||
|
.expect("Expect removing Windows snapshot CoW with 'dmsetup' to succeed");
|
||||||
|
|
||||||
|
// losetup -d <loopback_device>
|
||||||
|
std::process::Command::new("losetup")
|
||||||
|
.args(&["-d", self.loopback_device.as_str()])
|
||||||
|
.output()
|
||||||
|
.expect("Expect removing loopback device to succeed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskConfig for UbuntuDiskConfig {
|
||||||
|
fn prepare_cloudinit(&self, tmp_dir: &TempDir, network: &GuestNetworkConfig) -> String {
|
||||||
|
let cloudinit_file_path =
|
||||||
|
String::from(tmp_dir.as_path().join("cloudinit").to_str().unwrap());
|
||||||
|
|
||||||
|
let cloud_init_directory = tmp_dir.as_path().join("cloud-init").join("ubuntu");
|
||||||
|
|
||||||
|
fs::create_dir_all(&cloud_init_directory)
|
||||||
|
.expect("Expect creating cloud-init directory to succeed");
|
||||||
|
|
||||||
|
let source_file_dir = std::env::current_dir()
|
||||||
|
.unwrap()
|
||||||
|
.join("test_data")
|
||||||
|
.join("cloud-init")
|
||||||
|
.join("ubuntu");
|
||||||
|
|
||||||
|
vec!["meta-data"].iter().for_each(|x| {
|
||||||
|
rate_limited_copy(source_file_dir.join(x), cloud_init_directory.join(x))
|
||||||
|
.expect("Expect copying cloud-init meta-data to succeed");
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut user_data_string = String::new();
|
||||||
|
fs::File::open(source_file_dir.join("user-data"))
|
||||||
|
.unwrap()
|
||||||
|
.read_to_string(&mut user_data_string)
|
||||||
|
.expect("Expected reading user-data file in to succeed");
|
||||||
|
user_data_string = user_data_string.replace(
|
||||||
|
"@DEFAULT_TCP_LISTENER_MESSAGE",
|
||||||
|
&DEFAULT_TCP_LISTENER_MESSAGE,
|
||||||
|
);
|
||||||
|
user_data_string = user_data_string.replace("@HOST_IP", &network.host_ip);
|
||||||
|
user_data_string =
|
||||||
|
user_data_string.replace("@TCP_LISTENER_PORT", &network.tcp_listener_port.to_string());
|
||||||
|
|
||||||
|
fs::File::create(cloud_init_directory.join("user-data"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(&user_data_string.as_bytes())
|
||||||
|
.expect("Expected writing out user-data to succeed");
|
||||||
|
|
||||||
|
let mut network_config_string = String::new();
|
||||||
|
|
||||||
|
fs::File::open(source_file_dir.join("network-config"))
|
||||||
|
.unwrap()
|
||||||
|
.read_to_string(&mut network_config_string)
|
||||||
|
.expect("Expected reading network-config file in to succeed");
|
||||||
|
|
||||||
|
network_config_string = network_config_string.replace("192.168.2.1", &network.host_ip);
|
||||||
|
network_config_string = network_config_string.replace("192.168.2.2", &network.guest_ip);
|
||||||
|
network_config_string = network_config_string.replace("192.168.2.3", &network.l2_guest_ip1);
|
||||||
|
network_config_string = network_config_string.replace("192.168.2.4", &network.l2_guest_ip2);
|
||||||
|
network_config_string = network_config_string.replace("192.168.2.5", &network.l2_guest_ip3);
|
||||||
|
network_config_string =
|
||||||
|
network_config_string.replace("12:34:56:78:90:ab", &network.guest_mac);
|
||||||
|
network_config_string =
|
||||||
|
network_config_string.replace("de:ad:be:ef:12:34", &network.l2_guest_mac1);
|
||||||
|
network_config_string =
|
||||||
|
network_config_string.replace("de:ad:be:ef:34:56", &network.l2_guest_mac2);
|
||||||
|
network_config_string =
|
||||||
|
network_config_string.replace("de:ad:be:ef:56:78", &network.l2_guest_mac3);
|
||||||
|
|
||||||
|
fs::File::create(cloud_init_directory.join("network-config"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(&network_config_string.as_bytes())
|
||||||
|
.expect("Expected writing out network-config to succeed");
|
||||||
|
|
||||||
|
std::process::Command::new("mkdosfs")
|
||||||
|
.args(&["-n", "cidata"])
|
||||||
|
.args(&["-C", cloudinit_file_path.as_str()])
|
||||||
|
.arg("8192")
|
||||||
|
.output()
|
||||||
|
.expect("Expect creating disk image to succeed");
|
||||||
|
|
||||||
|
vec!["user-data", "meta-data", "network-config"]
|
||||||
|
.iter()
|
||||||
|
.for_each(|x| {
|
||||||
|
std::process::Command::new("mcopy")
|
||||||
|
.arg("-o")
|
||||||
|
.args(&["-i", cloudinit_file_path.as_str()])
|
||||||
|
.args(&["-s", cloud_init_directory.join(x).to_str().unwrap(), "::"])
|
||||||
|
.output()
|
||||||
|
.expect("Expect copying files to disk image to succeed");
|
||||||
|
});
|
||||||
|
|
||||||
|
cloudinit_file_path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_files(&mut self, tmp_dir: &TempDir, network: &GuestNetworkConfig) {
|
||||||
|
let mut workload_path = dirs::home_dir().unwrap();
|
||||||
|
workload_path.push("workloads");
|
||||||
|
|
||||||
|
let mut osdisk_base_path = workload_path;
|
||||||
|
osdisk_base_path.push(&self.image_name);
|
||||||
|
|
||||||
|
let osdisk_path = String::from(tmp_dir.as_path().join("osdisk.img").to_str().unwrap());
|
||||||
|
let cloudinit_path = self.prepare_cloudinit(tmp_dir, network);
|
||||||
|
|
||||||
|
rate_limited_copy(osdisk_base_path, &osdisk_path)
|
||||||
|
.expect("copying of OS source disk image failed");
|
||||||
|
|
||||||
|
self.cloudinit_path = cloudinit_path;
|
||||||
|
self.osdisk_path = osdisk_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disk(&self, disk_type: DiskType) -> Option<String> {
|
||||||
|
match disk_type {
|
||||||
|
DiskType::OperatingSystem => Some(self.osdisk_path.clone()),
|
||||||
|
DiskType::CloudInit => Some(self.cloudinit_path.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskConfig for WindowsDiskConfig {
|
||||||
|
fn prepare_cloudinit(&self, _tmp_dir: &TempDir, _network: &GuestNetworkConfig) -> String {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_files(&mut self, tmp_dir: &TempDir, _network: &GuestNetworkConfig) {
|
||||||
|
let mut workload_path = dirs::home_dir().unwrap();
|
||||||
|
workload_path.push("workloads");
|
||||||
|
|
||||||
|
let mut osdisk_path = workload_path;
|
||||||
|
osdisk_path.push(&self.image_name);
|
||||||
|
|
||||||
|
let osdisk_blk_size = fs::metadata(osdisk_path)
|
||||||
|
.expect("Expect retrieving Windows image metadata")
|
||||||
|
.len()
|
||||||
|
>> 9;
|
||||||
|
|
||||||
|
let snapshot_cow_path =
|
||||||
|
String::from(tmp_dir.as_path().join("snapshot_cow").to_str().unwrap());
|
||||||
|
|
||||||
|
// Create and truncate CoW file for device mapper
|
||||||
|
let cow_file_size: u64 = 1 << 30;
|
||||||
|
let cow_file_blk_size = cow_file_size >> 9;
|
||||||
|
let cow_file = std::fs::File::create(snapshot_cow_path.as_str())
|
||||||
|
.expect("Expect creating CoW image to succeed");
|
||||||
|
cow_file
|
||||||
|
.set_len(cow_file_size)
|
||||||
|
.expect("Expect truncating CoW image to succeed");
|
||||||
|
|
||||||
|
// losetup --find --show /tmp/snapshot_cow
|
||||||
|
let loopback_device = std::process::Command::new("losetup")
|
||||||
|
.arg("--find")
|
||||||
|
.arg("--show")
|
||||||
|
.arg(snapshot_cow_path.as_str())
|
||||||
|
.output()
|
||||||
|
.expect("Expect creating loopback device from snapshot CoW image to succeed");
|
||||||
|
|
||||||
|
self.loopback_device = String::from_utf8_lossy(&loopback_device.stdout)
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let random_extension = tmp_dir.as_path().file_name().unwrap();
|
||||||
|
let windows_snapshot_cow = format!(
|
||||||
|
"windows-snapshot-cow-{}",
|
||||||
|
random_extension.to_str().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// dmsetup create windows-snapshot-cow-1 --table '0 2097152 linear /dev/loop1 0'
|
||||||
|
std::process::Command::new("dmsetup")
|
||||||
|
.arg("create")
|
||||||
|
.arg(windows_snapshot_cow.as_str())
|
||||||
|
.args(&[
|
||||||
|
"--table",
|
||||||
|
format!("0 {} linear {} 0", cow_file_blk_size, self.loopback_device).as_str(),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.expect("Expect creating Windows snapshot CoW with 'dmsetup' to succeed");
|
||||||
|
|
||||||
|
let windows_snapshot = format!("windows-snapshot-{}", random_extension.to_str().unwrap());
|
||||||
|
|
||||||
|
// dmsetup mknodes
|
||||||
|
std::process::Command::new("dmsetup")
|
||||||
|
.arg("mknodes")
|
||||||
|
.output()
|
||||||
|
.expect("Expect device mapper nodes to be ready");
|
||||||
|
|
||||||
|
// dmsetup create windows-snapshot-1 --table '0 41943040 snapshot /dev/mapper/windows-base /dev/mapper/windows-snapshot-cow-1 P 8'
|
||||||
|
std::process::Command::new("dmsetup")
|
||||||
|
.arg("create")
|
||||||
|
.arg(windows_snapshot.as_str())
|
||||||
|
.args(&[
|
||||||
|
"--table",
|
||||||
|
format!(
|
||||||
|
"0 {} snapshot /dev/mapper/windows-base /dev/mapper/{} P 8",
|
||||||
|
osdisk_blk_size,
|
||||||
|
windows_snapshot_cow.as_str()
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.expect("Expect creating Windows snapshot with 'dmsetup' to succeed");
|
||||||
|
|
||||||
|
// dmsetup mknodes
|
||||||
|
std::process::Command::new("dmsetup")
|
||||||
|
.arg("mknodes")
|
||||||
|
.output()
|
||||||
|
.expect("Expect device mapper nodes to be ready");
|
||||||
|
|
||||||
|
self.osdisk_path = format!("/dev/mapper/{}", windows_snapshot);
|
||||||
|
self.windows_snapshot_cow = windows_snapshot_cow;
|
||||||
|
self.windows_snapshot = windows_snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disk(&self, disk_type: DiskType) -> Option<String> {
|
||||||
|
match disk_type {
|
||||||
|
DiskType::OperatingSystem => Some(self.osdisk_path.clone()),
|
||||||
|
DiskType::CloudInit => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rate_limited_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
||||||
|
for i in 0..10 {
|
||||||
|
let free_bytes = unsafe {
|
||||||
|
let mut stats = std::mem::MaybeUninit::zeroed();
|
||||||
|
let fs_name = std::ffi::CString::new("/tmp").unwrap();
|
||||||
|
libc::statvfs(fs_name.as_ptr(), stats.as_mut_ptr());
|
||||||
|
|
||||||
|
let free_blocks = stats.assume_init().f_bfree;
|
||||||
|
let block_size = stats.assume_init().f_bsize;
|
||||||
|
|
||||||
|
free_blocks * block_size
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure there is at least 6 GiB of space
|
||||||
|
if free_bytes < 6 << 30 {
|
||||||
|
eprintln!(
|
||||||
|
"Not enough space on disk ({}). Attempt {} of 10. Sleeping.",
|
||||||
|
free_bytes, i
|
||||||
|
);
|
||||||
|
thread::sleep(std::time::Duration::new(60, 0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match fs::copy(&from, &to) {
|
||||||
|
Err(e) => {
|
||||||
|
if let Some(errno) = e.raw_os_error() {
|
||||||
|
if errno == libc::ENOSPC {
|
||||||
|
eprintln!("Copy returned ENOSPC. Attempt {} of 10. Sleeping.", i);
|
||||||
|
thread::sleep(std::time::Duration::new(60, 0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
Ok(i) => return Ok(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_child_output(
|
||||||
|
r: Result<(), std::boxed::Box<dyn std::any::Any + std::marker::Send>>,
|
||||||
|
output: &std::process::Output,
|
||||||
|
) {
|
||||||
|
use std::os::unix::process::ExitStatusExt;
|
||||||
|
if r.is_ok() && output.status.success() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match output.status.code() {
|
||||||
|
None => {
|
||||||
|
// Don't treat child.kill() as a problem
|
||||||
|
if output.status.signal() == Some(9) && r.is_ok() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"==== child killed by signal: {} ====",
|
||||||
|
output.status.signal().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(code) => {
|
||||||
|
eprintln!("\n\n==== child exit code: {} ====", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====",
|
||||||
|
String::from_utf8_lossy(&output.stdout)
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
|
||||||
|
panic!("Test failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PasswordAuth {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DEFAULT_SSH_RETRIES: u8 = 6;
|
||||||
|
pub const DEFAULT_SSH_TIMEOUT: u8 = 10;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SshCommandError {
|
||||||
|
Connection(std::io::Error),
|
||||||
|
Handshake(ssh2::Error),
|
||||||
|
Authentication(ssh2::Error),
|
||||||
|
ChannelSession(ssh2::Error),
|
||||||
|
Command(ssh2::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ssh_command_ip_with_auth(
|
||||||
|
command: &str,
|
||||||
|
auth: &PasswordAuth,
|
||||||
|
ip: &str,
|
||||||
|
retries: u8,
|
||||||
|
timeout: u8,
|
||||||
|
) -> Result<String, SshCommandError> {
|
||||||
|
let mut s = String::new();
|
||||||
|
|
||||||
|
let mut counter = 0;
|
||||||
|
loop {
|
||||||
|
match (|| -> Result<(), SshCommandError> {
|
||||||
|
let tcp =
|
||||||
|
TcpStream::connect(format!("{}:22", ip)).map_err(SshCommandError::Connection)?;
|
||||||
|
let mut sess = Session::new().unwrap();
|
||||||
|
sess.set_tcp_stream(tcp);
|
||||||
|
sess.handshake().map_err(SshCommandError::Handshake)?;
|
||||||
|
|
||||||
|
sess.userauth_password(&auth.username, &auth.password)
|
||||||
|
.map_err(SshCommandError::Authentication)?;
|
||||||
|
assert!(sess.authenticated());
|
||||||
|
|
||||||
|
let mut channel = sess
|
||||||
|
.channel_session()
|
||||||
|
.map_err(SshCommandError::ChannelSession)?;
|
||||||
|
channel.exec(command).map_err(SshCommandError::Command)?;
|
||||||
|
|
||||||
|
// Intentionally ignore these results here as their failure
|
||||||
|
// does not precipitate a repeat
|
||||||
|
let _ = channel.read_to_string(&mut s);
|
||||||
|
let _ = channel.close();
|
||||||
|
let _ = channel.wait_close();
|
||||||
|
Ok(())
|
||||||
|
})() {
|
||||||
|
Ok(_) => break,
|
||||||
|
Err(e) => {
|
||||||
|
counter += 1;
|
||||||
|
if counter >= retries {
|
||||||
|
eprintln!(
|
||||||
|
"\n\n==== Start ssh command output (FAILED) ====\n\n\
|
||||||
|
command=\"{}\"\n\
|
||||||
|
auth=\"{:#?}\"\n\
|
||||||
|
ip=\"{}\"\n\
|
||||||
|
output=\"{}\"\n\
|
||||||
|
error=\"{:?}\"\n\
|
||||||
|
\n==== End ssh command outout ====\n\n",
|
||||||
|
command, auth, ip, s, e
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
thread::sleep(std::time::Duration::new((timeout * counter).into(), 0));
|
||||||
|
}
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ssh_command_ip(
|
||||||
|
command: &str,
|
||||||
|
ip: &str,
|
||||||
|
retries: u8,
|
||||||
|
timeout: u8,
|
||||||
|
) -> Result<String, SshCommandError> {
|
||||||
|
ssh_command_ip_with_auth(
|
||||||
|
command,
|
||||||
|
&PasswordAuth {
|
||||||
|
username: String::from("cloud"),
|
||||||
|
password: String::from("cloud123"),
|
||||||
|
},
|
||||||
|
ip,
|
||||||
|
retries,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,10 @@ default = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.5.0"
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||||
|
|||||||
@@ -15,20 +15,23 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
|||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use vhost_rs::vhost_user::message::{
|
use vhost::vhost_user::message::{
|
||||||
VhostUserConfigFlags, VhostUserMemoryRegion, VhostUserProtocolFeatures,
|
VhostUserConfigFlags, VhostUserMemoryRegion, VhostUserProtocolFeatures,
|
||||||
VhostUserVirtioFeatures, VhostUserVringAddrFlags, VhostUserVringState,
|
VhostUserSingleMemoryRegion, VhostUserVirtioFeatures, VhostUserVringAddrFlags,
|
||||||
|
VhostUserVringState,
|
||||||
};
|
};
|
||||||
use vhost_rs::vhost_user::{
|
use vhost::vhost_user::{
|
||||||
Error as VhostUserError, Listener, Result as VhostUserResult, SlaveFsCacheReq, SlaveListener,
|
Error as VhostUserError, Listener, Result as VhostUserResult, SlaveFsCacheReq, SlaveListener,
|
||||||
VhostUserSlaveReqHandler,
|
VhostUserSlaveReqHandlerMut,
|
||||||
};
|
};
|
||||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||||
use vm_memory::guest_memory::FileOffset;
|
use vm_memory::guest_memory::FileOffset;
|
||||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
use vm_memory::{GuestAddress, GuestMemoryMmap, GuestRegionMmap, MmapRegion};
|
||||||
use vm_virtio::Queue;
|
use vm_virtio::Queue;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
|
const MAX_MEM_SLOTS: u64 = 32;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Errors related to vhost-user daemon.
|
/// Errors related to vhost-user daemon.
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -203,10 +206,6 @@ struct AddrMapping {
|
|||||||
gpa_base: u64,
|
gpa_base: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Memory {
|
|
||||||
mappings: Vec<AddrMapping>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Vring {
|
pub struct Vring {
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
kick: Option<EventFd>,
|
kick: Option<EventFd>,
|
||||||
@@ -444,7 +443,8 @@ struct VhostUserHandler<S: VhostUserBackend> {
|
|||||||
num_queues: usize,
|
num_queues: usize,
|
||||||
max_queue_size: usize,
|
max_queue_size: usize,
|
||||||
queues_per_thread: Vec<u64>,
|
queues_per_thread: Vec<u64>,
|
||||||
memory: Option<Memory>,
|
mappings: Vec<AddrMapping>,
|
||||||
|
guest_memory: Option<GuestMemoryMmap>,
|
||||||
vrings: Vec<Arc<RwLock<Vring>>>,
|
vrings: Vec<Arc<RwLock<Vring>>>,
|
||||||
worker_threads: Vec<thread::JoinHandle<VringWorkerResult<()>>>,
|
worker_threads: Vec<thread::JoinHandle<VringWorkerResult<()>>>,
|
||||||
}
|
}
|
||||||
@@ -521,7 +521,8 @@ impl<S: VhostUserBackend> VhostUserHandler<S> {
|
|||||||
num_queues,
|
num_queues,
|
||||||
max_queue_size,
|
max_queue_size,
|
||||||
queues_per_thread,
|
queues_per_thread,
|
||||||
memory: None,
|
mappings: Vec::new(),
|
||||||
|
guest_memory: None,
|
||||||
vrings,
|
vrings,
|
||||||
worker_threads,
|
worker_threads,
|
||||||
})
|
})
|
||||||
@@ -532,11 +533,9 @@ impl<S: VhostUserBackend> VhostUserHandler<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vmm_va_to_gpa(&self, vmm_va: u64) -> VhostUserHandlerResult<u64> {
|
fn vmm_va_to_gpa(&self, vmm_va: u64) -> VhostUserHandlerResult<u64> {
|
||||||
if let Some(memory) = &self.memory {
|
for mapping in self.mappings.iter() {
|
||||||
for mapping in memory.mappings.iter() {
|
if vmm_va >= mapping.vmm_addr && vmm_va < mapping.vmm_addr + mapping.size {
|
||||||
if vmm_va >= mapping.vmm_addr && vmm_va < mapping.vmm_addr + mapping.size {
|
return Ok(vmm_va - mapping.vmm_addr + mapping.gpa_base);
|
||||||
return Ok(vmm_va - mapping.vmm_addr + mapping.gpa_base);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,7 +543,7 @@ impl<S: VhostUserBackend> VhostUserHandler<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: VhostUserBackend> VhostUserSlaveReqHandler for VhostUserHandler<S> {
|
impl<S: VhostUserBackend> VhostUserSlaveReqHandlerMut for VhostUserHandler<S> {
|
||||||
fn set_owner(&mut self) -> VhostUserResult<()> {
|
fn set_owner(&mut self) -> VhostUserResult<()> {
|
||||||
if self.owned {
|
if self.owned {
|
||||||
return Err(VhostUserError::InvalidOperation);
|
return Err(VhostUserError::InvalidOperation);
|
||||||
@@ -636,11 +635,13 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandler for VhostUserHandler<S> {
|
|||||||
self.backend
|
self.backend
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.update_memory(mem)
|
.update_memory(mem.clone())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||||
})?;
|
})?;
|
||||||
self.memory = Some(Memory { mappings });
|
|
||||||
|
self.guest_memory = Some(mem);
|
||||||
|
self.mappings = mappings;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -670,7 +671,7 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandler for VhostUserHandler<S> {
|
|||||||
return Err(VhostUserError::InvalidParam);
|
return Err(VhostUserError::InvalidParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.memory.is_some() {
|
if !self.mappings.is_empty() {
|
||||||
let desc_table = self.vmm_va_to_gpa(descriptor).map_err(|e| {
|
let desc_table = self.vmm_va_to_gpa(descriptor).map_err(|e| {
|
||||||
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||||
})?;
|
})?;
|
||||||
@@ -863,6 +864,84 @@ impl<S: VhostUserBackend> VhostUserSlaveReqHandler for VhostUserHandler<S> {
|
|||||||
fn set_slave_req_fd(&mut self, vu_req: SlaveFsCacheReq) {
|
fn set_slave_req_fd(&mut self, vu_req: SlaveFsCacheReq) {
|
||||||
self.backend.write().unwrap().set_slave_req_fd(vu_req);
|
self.backend.write().unwrap().set_slave_req_fd(vu_req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_max_mem_slots(&mut self) -> VhostUserResult<u64> {
|
||||||
|
Ok(MAX_MEM_SLOTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_mem_region(
|
||||||
|
&mut self,
|
||||||
|
region: &VhostUserSingleMemoryRegion,
|
||||||
|
fd: RawFd,
|
||||||
|
) -> VhostUserResult<()> {
|
||||||
|
let file = unsafe { File::from_raw_fd(fd) };
|
||||||
|
let mmap_region = MmapRegion::from_file(
|
||||||
|
FileOffset::new(file, region.mmap_offset),
|
||||||
|
region.memory_size as usize,
|
||||||
|
)
|
||||||
|
.map_err(|e| VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e)))?;
|
||||||
|
let guest_region = Arc::new(
|
||||||
|
GuestRegionMmap::new(mmap_region, GuestAddress(region.guest_phys_addr)).map_err(
|
||||||
|
|e| VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e)),
|
||||||
|
)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
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())
|
||||||
|
.map_err(|e| {
|
||||||
|
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.guest_memory = Some(guest_memory);
|
||||||
|
|
||||||
|
self.mappings.push(AddrMapping {
|
||||||
|
vmm_addr: region.user_addr,
|
||||||
|
size: region.memory_size,
|
||||||
|
gpa_base: region.guest_phys_addr,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
.map_err(|e| {
|
||||||
|
VhostUserError::ReqHandlerError(io::Error::new(io::ErrorKind::Other, e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
self.guest_memory = Some(guest_memory);
|
||||||
|
|
||||||
|
self.mappings
|
||||||
|
.retain(|mapping| mapping.gpa_base != region.guest_phys_addr);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: VhostUserBackend> Drop for VhostUserHandler<S> {
|
impl<S: VhostUserBackend> Drop for VhostUserHandler<S> {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ edition = "2018"
|
|||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
clap = { version = "2.33.3", features=["wrap_help"] }
|
clap = { version = "2.33.3", features=["wrap_help"] }
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
option_parser = { path = "../option_parser" }
|
option_parser = { path = "../option_parser" }
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
vhost_user_backend = { path = "../vhost_user_backend" }
|
vhost_user_backend = { path = "../vhost_user_backend" }
|
||||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.5.0"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
extern crate block_util;
|
extern crate block_util;
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate vhost_rs;
|
extern crate vhost;
|
||||||
extern crate vhost_user_backend;
|
extern crate vhost_user_backend;
|
||||||
|
|
||||||
use block_util::{build_disk_image_id, Request, VirtioBlockConfig};
|
use block_util::{build_disk_image_id, Request, VirtioBlockConfig};
|
||||||
@@ -33,8 +33,8 @@ use std::sync::{Arc, Mutex, RwLock};
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::{convert, error, fmt, io};
|
use std::{convert, error, fmt, io};
|
||||||
use vhost_rs::vhost_user::message::*;
|
use vhost::vhost_user::message::*;
|
||||||
use vhost_rs::vhost_user::Listener;
|
use vhost::vhost_user::Listener;
|
||||||
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring};
|
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring};
|
||||||
use virtio_bindings::bindings::virtio_blk::*;
|
use virtio_bindings::bindings::virtio_blk::*;
|
||||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||||
@@ -308,6 +308,8 @@ impl VhostUserBackend for VhostUserBlkBackend {
|
|||||||
|
|
||||||
fn protocol_features(&self) -> VhostUserProtocolFeatures {
|
fn protocol_features(&self) -> VhostUserProtocolFeatures {
|
||||||
VhostUserProtocolFeatures::CONFIG
|
VhostUserProtocolFeatures::CONFIG
|
||||||
|
| VhostUserProtocolFeatures::MQ
|
||||||
|
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_event_idx(&mut self, enabled: bool) {
|
fn set_event_idx(&mut self, enabled: bool) {
|
||||||
@@ -391,7 +393,7 @@ impl VhostUserBackend for VhostUserBlkBackend {
|
|||||||
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
return Err(io::Error::from_raw_os_error(libc::EINVAL));
|
||||||
}
|
}
|
||||||
let (_, right) = config_slice.split_at_mut(offset as usize);
|
let (_, right) = config_slice.split_at_mut(offset as usize);
|
||||||
right.copy_from_slice(&data[..]);
|
right.copy_from_slice(&data);
|
||||||
self.update_writeback();
|
self.update_writeback();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "2.33.3", features=["wrap_help"] }
|
clap = { version = "2.33.3", features=["wrap_help"] }
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
option_parser = { path = "../option_parser" }
|
option_parser = { path = "../option_parser" }
|
||||||
vhost_user_backend = { path = "../vhost_user_backend" }
|
vhost_user_backend = { path = "../vhost_user_backend" }
|
||||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = "0.5.0"
|
vm-memory = "0.5.0"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate net_util;
|
extern crate net_util;
|
||||||
extern crate vhost_rs;
|
extern crate vhost;
|
||||||
extern crate vhost_user_backend;
|
extern crate vhost_user_backend;
|
||||||
|
|
||||||
use libc::{self, EFD_NONBLOCK};
|
use libc::{self, EFD_NONBLOCK};
|
||||||
@@ -24,8 +24,8 @@ use std::os::unix::io::AsRawFd;
|
|||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use vhost_rs::vhost_user::message::*;
|
use vhost::vhost_user::message::*;
|
||||||
use vhost_rs::vhost_user::{Error as VhostUserError, Listener};
|
use vhost::vhost_user::{Error as VhostUserError, Listener};
|
||||||
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring, VringWorker};
|
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring, VringWorker};
|
||||||
use virtio_bindings::bindings::virtio_net::*;
|
use virtio_bindings::bindings::virtio_net::*;
|
||||||
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||||
@@ -70,6 +70,8 @@ pub enum Error {
|
|||||||
SocketParameterMissing,
|
SocketParameterMissing,
|
||||||
/// Underlying QueuePair error
|
/// Underlying QueuePair error
|
||||||
NetQueuePair(net_util::NetQueuePairError),
|
NetQueuePair(net_util::NetQueuePairError),
|
||||||
|
/// Failed registering the TAP listener
|
||||||
|
RegisterTapListener(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SYNTAX: &str = "vhost-user-net backend parameters \
|
pub const SYNTAX: &str = "vhost-user-net backend parameters \
|
||||||
@@ -186,7 +188,9 @@ impl VhostUserBackend for VhostUserNetBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn protocol_features(&self) -> VhostUserProtocolFeatures {
|
fn protocol_features(&self) -> VhostUserProtocolFeatures {
|
||||||
VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::REPLY_ACK
|
VhostUserProtocolFeatures::MQ
|
||||||
|
| VhostUserProtocolFeatures::REPLY_ACK
|
||||||
|
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_event_idx(&mut self, _enabled: bool) {}
|
fn set_event_idx(&mut self, _enabled: bool) {}
|
||||||
@@ -212,15 +216,15 @@ impl VhostUserBackend for VhostUserNetBackend {
|
|||||||
let mut thread = self.threads[thread_id].lock().unwrap();
|
let mut thread = self.threads[thread_id].lock().unwrap();
|
||||||
match device_event {
|
match device_event {
|
||||||
0 => {
|
0 => {
|
||||||
let mut vring = vrings[0].write().unwrap();
|
if !thread.net.rx_tap_listening {
|
||||||
if thread
|
net_util::register_listener(
|
||||||
.net
|
thread.net.epoll_fd.unwrap(),
|
||||||
.resume_rx(&mut vring.mut_queue())
|
thread.net.tap.as_raw_fd(),
|
||||||
.map_err(Error::NetQueuePair)?
|
epoll::Events::EPOLLIN,
|
||||||
{
|
u64::from(thread.net.tap_event_id),
|
||||||
vring
|
)
|
||||||
.signal_used_queue()
|
.map_err(Error::RegisterTapListener)?;
|
||||||
.map_err(Error::FailedSignalingUsedQueue)?
|
thread.net.rx_tap_listening = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
@@ -239,7 +243,7 @@ impl VhostUserBackend for VhostUserNetBackend {
|
|||||||
let mut vring = vrings[0].write().unwrap();
|
let mut vring = vrings[0].write().unwrap();
|
||||||
if thread
|
if thread
|
||||||
.net
|
.net
|
||||||
.process_rx_tap(&mut vring.mut_queue())
|
.process_rx(&mut vring.mut_queue())
|
||||||
.map_err(Error::NetQueuePair)?
|
.map_err(Error::NetQueuePair)?
|
||||||
{
|
{
|
||||||
vring
|
vring
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ io_uring = ["block_util/io_uring"]
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
arc-swap = ">=1.0.0"
|
arc-swap = ">=1.0.0"
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.4.3"
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
|
event_monitor = { path = "../event_monitor" }
|
||||||
io-uring = ">=0.4.0"
|
io-uring = ">=0.4.0"
|
||||||
libc = "0.2.86"
|
libc = "0.2.91"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
@@ -24,9 +25,7 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v
|
|||||||
serde = ">=1.0.27"
|
serde = ">=1.0.27"
|
||||||
serde_derive = ">=1.0.27"
|
serde_derive = ">=1.0.27"
|
||||||
serde_json = ">=1.0.9"
|
serde_json = ">=1.0.9"
|
||||||
tempfile = "3.2.0"
|
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
||||||
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
|
||||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
|
||||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ impl Balloon {
|
|||||||
|
|
||||||
Ok(Balloon {
|
Ok(Balloon {
|
||||||
common: VirtioCommon {
|
common: VirtioCommon {
|
||||||
device_type: VirtioDeviceType::TYPE_BALLOON as u32,
|
device_type: VirtioDeviceType::Balloon as u32,
|
||||||
avail_features,
|
avail_features,
|
||||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||||
@@ -462,11 +462,14 @@ impl VirtioDevice for Balloon {
|
|||||||
})?;
|
})?;
|
||||||
self.common.epoll_threads = Some(epoll_threads);
|
self.common.epoll_threads = Some(epoll_threads);
|
||||||
|
|
||||||
|
event!("virtio-device", "activated", "id", &self.id);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||||
self.common.reset()
|
let result = self.common.reset();
|
||||||
|
event!("virtio-device", "reset", "id", &self.id);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
use super::Error as DeviceError;
|
use super::Error as DeviceError;
|
||||||
use super::{
|
use super::{
|
||||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
|
||||||
VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
|
RateLimiterConfig, VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType,
|
||||||
|
EPOLL_HELPER_EVENT_LAST,
|
||||||
};
|
};
|
||||||
|
use crate::rate_limiter::{RateLimiter, TokenType};
|
||||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||||
use crate::VirtioInterrupt;
|
use crate::VirtioInterrupt;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
@@ -21,7 +23,6 @@ use block_util::{
|
|||||||
RequestType, VirtioBlockConfig,
|
RequestType, VirtioBlockConfig,
|
||||||
};
|
};
|
||||||
use seccomp::{SeccompAction, SeccompFilter};
|
use seccomp::{SeccompAction, SeccompFilter};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
@@ -30,11 +31,9 @@ use std::result;
|
|||||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::{collections::HashMap, convert::TryInto};
|
||||||
use virtio_bindings::bindings::virtio_blk::*;
|
use virtio_bindings::bindings::virtio_blk::*;
|
||||||
use vm_memory::{
|
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
|
||||||
ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError,
|
|
||||||
GuestMemoryMmap,
|
|
||||||
};
|
|
||||||
use vm_migration::{
|
use vm_migration::{
|
||||||
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||||
Transportable,
|
Transportable,
|
||||||
@@ -48,27 +47,11 @@ pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
|||||||
const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||||
// New completed tasks are pending on the completion ring.
|
// New completed tasks are pending on the completion ring.
|
||||||
const COMPLETION_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
const COMPLETION_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||||
|
// New 'wake up' event from the rate limiter
|
||||||
|
const RATE_LIMITER_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Guest gave us bad memory addresses.
|
|
||||||
GuestMemory(GuestMemoryError),
|
|
||||||
/// Guest gave us offsets that would have overflowed a usize.
|
|
||||||
CheckedOffset(GuestAddress, usize),
|
|
||||||
/// Guest gave us a write only descriptor that protocol says to read from.
|
|
||||||
UnexpectedWriteOnlyDescriptor,
|
|
||||||
/// Guest gave us a read only descriptor that protocol says to write to.
|
|
||||||
UnexpectedReadOnlyDescriptor,
|
|
||||||
/// Guest gave us too few descriptors in a descriptor chain.
|
|
||||||
DescriptorChainTooShort,
|
|
||||||
/// Guest gave us a descriptor that was too short to use.
|
|
||||||
DescriptorLengthTooSmall,
|
|
||||||
/// Getting a block's metadata fails for any reason.
|
|
||||||
GetFileMetadata,
|
|
||||||
/// The requested operation would cause a seek beyond disk end.
|
|
||||||
InvalidOffset,
|
|
||||||
/// Unsupported operation on the disk.
|
|
||||||
Unsupported(u32),
|
|
||||||
/// Failed to parse the request.
|
/// Failed to parse the request.
|
||||||
RequestParsing(block_util::Error),
|
RequestParsing(block_util::Error),
|
||||||
/// Failed to execute the request.
|
/// Failed to execute the request.
|
||||||
@@ -104,6 +87,7 @@ struct BlockEpollHandler {
|
|||||||
counters: BlockCounters,
|
counters: BlockCounters,
|
||||||
queue_evt: EventFd,
|
queue_evt: EventFd,
|
||||||
request_list: HashMap<u16, Request>,
|
request_list: HashMap<u16, Request>,
|
||||||
|
rate_limiter: Option<RateLimiter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockEpollHandler {
|
impl BlockEpollHandler {
|
||||||
@@ -116,6 +100,38 @@ impl BlockEpollHandler {
|
|||||||
|
|
||||||
for avail_desc in queue.iter(&mem) {
|
for avail_desc in queue.iter(&mem) {
|
||||||
let mut request = Request::parse(&avail_desc, &mem).map_err(Error::RequestParsing)?;
|
let mut request = Request::parse(&avail_desc, &mem).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
|
||||||
|
// budget and rate limiting is in effect.
|
||||||
|
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();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Exercise the rate limiter only if this request is of data transfer type.
|
||||||
|
if request.request_type == RequestType::In
|
||||||
|
|| request.request_type == RequestType::Out
|
||||||
|
{
|
||||||
|
let mut bytes = Wrapping(0);
|
||||||
|
for (_, data_len) in &request.data_descriptors {
|
||||||
|
bytes += Wrapping(*data_len as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If limiter.consume() fails it means there is no more TokenType::Bytes
|
||||||
|
// budget and rate limiting is in effect.
|
||||||
|
if !rate_limiter.consume(bytes.0, TokenType::Bytes) {
|
||||||
|
// Revert the OPS consume().
|
||||||
|
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();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||||
|
|
||||||
if request
|
if request
|
||||||
@@ -242,6 +258,9 @@ impl BlockEpollHandler {
|
|||||||
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
||||||
helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?;
|
helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?;
|
||||||
helper.add_event(self.disk_image.notifier().as_raw_fd(), COMPLETION_EVENT)?;
|
helper.add_event(self.disk_image.notifier().as_raw_fd(), COMPLETION_EVENT)?;
|
||||||
|
if let Some(rate_limiter) = &self.rate_limiter {
|
||||||
|
helper.add_event(rate_limiter.as_raw_fd(), RATE_LIMITER_EVENT)?;
|
||||||
|
}
|
||||||
helper.run(paused, paused_sync, self)?;
|
helper.run(paused, paused_sync, self)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -258,18 +277,24 @@ impl EpollHelperHandler for BlockEpollHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.process_queue_submit() {
|
let rate_limit_reached =
|
||||||
Ok(needs_notification) => {
|
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
|
||||||
if needs_notification {
|
|
||||||
if let Err(e) = self.signal_used_queue() {
|
// Process the queue only when the rate limit is not reached
|
||||||
error!("Failed to signal used queue: {:?}", e);
|
if !rate_limit_reached {
|
||||||
return true;
|
match self.process_queue_submit() {
|
||||||
|
Ok(needs_notification) => {
|
||||||
|
if needs_notification {
|
||||||
|
if let Err(e) = self.signal_used_queue() {
|
||||||
|
error!("Failed to signal used queue: {:?}", e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Err(e) => {
|
||||||
Err(e) => {
|
error!("Failed to process queue (submit): {:?}", e);
|
||||||
error!("Failed to process queue (submit): {:?}", e);
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,6 +319,31 @@ impl EpollHelperHandler for BlockEpollHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RATE_LIMITER_EVENT => {
|
||||||
|
if let Some(rate_limiter) = &mut self.rate_limiter {
|
||||||
|
// Upon rate limiter event, call the rate limiter handler
|
||||||
|
// and restart processing the queue.
|
||||||
|
if rate_limiter.event_handler().is_ok() {
|
||||||
|
match self.process_queue_submit() {
|
||||||
|
Ok(needs_notification) => {
|
||||||
|
if needs_notification {
|
||||||
|
if let Err(e) = self.signal_used_queue() {
|
||||||
|
error!("Failed to signal used queue: {:?}", e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to process queue (submit): {:?}", e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Unexpected 'RATE_LIMITER_EVENT' when rate_limiter is not enabled.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Unexpected event: {}", ev_type);
|
error!("Unexpected event: {}", ev_type);
|
||||||
return true;
|
return true;
|
||||||
@@ -314,6 +364,7 @@ pub struct Block {
|
|||||||
writeback: Arc<AtomicBool>,
|
writeback: Arc<AtomicBool>,
|
||||||
counters: BlockCounters,
|
counters: BlockCounters,
|
||||||
seccomp_action: SeccompAction,
|
seccomp_action: SeccompAction,
|
||||||
|
rate_limiter_config: Option<RateLimiterConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -337,6 +388,7 @@ impl Block {
|
|||||||
num_queues: usize,
|
num_queues: usize,
|
||||||
queue_size: u16,
|
queue_size: u16,
|
||||||
seccomp_action: SeccompAction,
|
seccomp_action: SeccompAction,
|
||||||
|
rate_limiter_config: Option<RateLimiterConfig>,
|
||||||
) -> io::Result<Self> {
|
) -> io::Result<Self> {
|
||||||
let disk_size = disk_image.size().map_err(|e| {
|
let disk_size = disk_image.size().map_err(|e| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
@@ -378,7 +430,7 @@ impl Block {
|
|||||||
|
|
||||||
Ok(Block {
|
Ok(Block {
|
||||||
common: VirtioCommon {
|
common: VirtioCommon {
|
||||||
device_type: VirtioDeviceType::TYPE_BLOCK as u32,
|
device_type: VirtioDeviceType::Block as u32,
|
||||||
avail_features,
|
avail_features,
|
||||||
paused_sync: Some(Arc::new(Barrier::new(num_queues + 1))),
|
paused_sync: Some(Arc::new(Barrier::new(num_queues + 1))),
|
||||||
queue_sizes: vec![queue_size; num_queues],
|
queue_sizes: vec![queue_size; num_queues],
|
||||||
@@ -393,6 +445,7 @@ impl Block {
|
|||||||
writeback: Arc::new(AtomicBool::new(true)),
|
writeback: Arc::new(AtomicBool::new(true)),
|
||||||
counters: BlockCounters::default(),
|
counters: BlockCounters::default(),
|
||||||
seccomp_action,
|
seccomp_action,
|
||||||
|
rate_limiter_config,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,6 +574,12 @@ impl VirtioDevice for Block {
|
|||||||
ActivateError::BadActivate
|
ActivateError::BadActivate
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let rate_limiter: Option<RateLimiter> = self
|
||||||
|
.rate_limiter_config
|
||||||
|
.map(RateLimiterConfig::try_into)
|
||||||
|
.transpose()
|
||||||
|
.map_err(ActivateError::CreateRateLimiter)?;
|
||||||
|
|
||||||
let mut handler = BlockEpollHandler {
|
let mut handler = BlockEpollHandler {
|
||||||
queue,
|
queue,
|
||||||
mem: mem.clone(),
|
mem: mem.clone(),
|
||||||
@@ -540,6 +599,7 @@ impl VirtioDevice for Block {
|
|||||||
counters: self.counters.clone(),
|
counters: self.counters.clone(),
|
||||||
queue_evt,
|
queue_evt,
|
||||||
request_list: HashMap::with_capacity(queue_size.into()),
|
request_list: HashMap::with_capacity(queue_size.into()),
|
||||||
|
rate_limiter,
|
||||||
};
|
};
|
||||||
|
|
||||||
let paused = self.common.paused.clone();
|
let paused = self.common.paused.clone();
|
||||||
@@ -567,12 +627,15 @@ impl VirtioDevice for Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.common.epoll_threads = Some(epoll_threads);
|
self.common.epoll_threads = Some(epoll_threads);
|
||||||
|
event!("virtio-device", "activated", "id", &self.id);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||||
self.common.reset()
|
let result = self.common.reset();
|
||||||
|
event!("virtio-device", "reset", "id", &self.id);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn counters(&self) -> Option<HashMap<&'static str, Wrapping<u64>>> {
|
fn counters(&self) -> Option<HashMap<&'static str, Wrapping<u64>>> {
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ impl Console {
|
|||||||
Ok((
|
Ok((
|
||||||
Console {
|
Console {
|
||||||
common: VirtioCommon {
|
common: VirtioCommon {
|
||||||
device_type: VirtioDeviceType::TYPE_CONSOLE as u32,
|
device_type: VirtioDeviceType::Console as u32,
|
||||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||||
avail_features,
|
avail_features,
|
||||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||||
@@ -485,11 +485,14 @@ impl VirtioDevice for Console {
|
|||||||
|
|
||||||
self.common.epoll_threads = Some(epoll_threads);
|
self.common.epoll_threads = Some(epoll_threads);
|
||||||
|
|
||||||
|
event!("virtio-device", "activated", "id", &self.id);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||||
self.common.reset()
|
let result = self.common.reset();
|
||||||
|
event!("virtio-device", "reset", "id", &self.id);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use std::sync::{
|
|||||||
Arc, Barrier,
|
Arc, Barrier,
|
||||||
};
|
};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, GuestUsize};
|
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, GuestRegionMmap, GuestUsize};
|
||||||
use vm_migration::{MigratableError, Pausable};
|
use vm_migration::{MigratableError, Pausable};
|
||||||
use vm_virtio::VirtioDeviceType;
|
use vm_virtio::VirtioDeviceType;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
@@ -139,7 +139,10 @@ pub trait VirtioDevice: Send {
|
|||||||
/// after a shutdown() can lead to unpredictable results.
|
/// after a shutdown() can lead to unpredictable results.
|
||||||
fn shutdown(&mut self) {}
|
fn shutdown(&mut self) {}
|
||||||
|
|
||||||
fn update_memory(&mut self, _mem: &GuestMemoryMmap) -> std::result::Result<(), Error> {
|
fn add_memory_region(
|
||||||
|
&mut self,
|
||||||
|
_region: &Arc<GuestRegionMmap>,
|
||||||
|
) -> std::result::Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user