mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
166 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eb9e61a32 | ||
|
|
d42b50845b | ||
|
|
06f391e022 | ||
|
|
60d2469e7a | ||
|
|
c4f8e4b000 | ||
|
|
c32959261e | ||
|
|
ef3fad8388 | ||
|
|
2b42f6c835 | ||
|
|
7acb5c6437 | ||
|
|
63f356b75b | ||
|
|
fd0ef6cfb5 | ||
|
|
286a23fbd4 | ||
|
|
3a93487b82 | ||
|
|
23c46b162e | ||
|
|
f4af668d76 | ||
|
|
7fe5d276a3 | ||
|
|
e664fa386b | ||
|
|
9919dec1d9 | ||
|
|
9ce6c3b75c | ||
|
|
47f59409db | ||
|
|
b51f112629 | ||
|
|
d158fa8de2 | ||
|
|
c64de38140 | ||
|
|
9594ecec0d | ||
|
|
6c0c47a530 | ||
|
|
6d38612f6c | ||
|
|
a44d96c9cc | ||
|
|
93b7dcac12 | ||
|
|
dd3844a892 | ||
|
|
0c3ef986fa | ||
|
|
53c3b00fb0 | ||
|
|
03b824be0b | ||
|
|
558975aa51 | ||
|
|
d78b9bd287 | ||
|
|
365d39ca2c | ||
|
|
d79594aa3f | ||
|
|
27b5f8d7e3 | ||
|
|
2cc312b65c | ||
|
|
d416271457 | ||
|
|
9e6a023f7f | ||
|
|
90ae4676c8 | ||
|
|
89bc413add | ||
|
|
c352b59857 | ||
|
|
ab89b48143 | ||
|
|
ffaab46934 | ||
|
|
280d4fb245 | ||
|
|
15f0451c34 | ||
|
|
3fa6e17cc4 | ||
|
|
e2c81f9ed8 | ||
|
|
ead8453120 | ||
|
|
c4ebe83576 | ||
|
|
1908f488c3 | ||
|
|
593a958fe5 | ||
|
|
ba71390d6a | ||
|
|
f6892f2ede | ||
|
|
251434862f | ||
|
|
1ac4c42a84 | ||
|
|
7dc92d1dac | ||
|
|
7a4edecd29 | ||
|
|
d419e30df1 | ||
|
|
3faffcb087 | ||
|
|
b3a1f5f1be | ||
|
|
2bc5119730 | ||
|
|
5100bc94cc | ||
|
|
6bf9fe487b | ||
|
|
d1a23d9085 | ||
|
|
05f785fa88 | ||
|
|
a3d957ba4d | ||
|
|
5bd63efa45 | ||
|
|
66b001665f | ||
|
|
c6aea5af5d | ||
|
|
c8b655490e | ||
|
|
72741c557a | ||
|
|
b2608ca285 | ||
|
|
74fe032827 | ||
|
|
40a2e3b462 | ||
|
|
abc6672f21 | ||
|
|
053c9e4d73 | ||
|
|
9ea19852b5 | ||
|
|
9871ff454b | ||
|
|
e817b4f36c | ||
|
|
6f63cd00eb | ||
|
|
a6ad85dc40 | ||
|
|
0f4f30dbde | ||
|
|
fe5401223b | ||
|
|
12b7a498d9 | ||
|
|
1fc97e91a4 | ||
|
|
fc5d6c96be | ||
|
|
546778ebfb | ||
|
|
f0360aff83 | ||
|
|
ae96aeda65 | ||
|
|
b5b97f7b05 | ||
|
|
9fc2613b41 | ||
|
|
44b5a19209 | ||
|
|
df6b52924f | ||
|
|
1ab1341775 | ||
|
|
a29f7ffcc7 | ||
|
|
eda39d0469 | ||
|
|
de0c24810e | ||
|
|
fadeb98c67 | ||
|
|
1fc0b94fcd | ||
|
|
ef4c896bbf | ||
|
|
72bb255ff6 | ||
|
|
a406d90059 | ||
|
|
0dfffee6ba | ||
|
|
0fec326582 | ||
|
|
6a591ca81d | ||
|
|
a525f8365f | ||
|
|
f271378811 | ||
|
|
78b9edf848 | ||
|
|
d0aebfb922 | ||
|
|
f913dd6150 | ||
|
|
c622278030 | ||
|
|
d1d0421103 | ||
|
|
fb70baece0 | ||
|
|
0159ab93e1 | ||
|
|
609d704168 | ||
|
|
3ac9b6c404 | ||
|
|
b34703d29f | ||
|
|
cf6763dfdb | ||
|
|
11a69450ba | ||
|
|
c62e409827 | ||
|
|
041724a7cf | ||
|
|
8baa244ec1 | ||
|
|
b399287430 | ||
|
|
c0827e01b1 | ||
|
|
46e736973e | ||
|
|
096d99664d | ||
|
|
f7c42dc7d2 | ||
|
|
37f947d614 | ||
|
|
47d2421e39 | ||
|
|
04419065a3 | ||
|
|
ca60adda70 | ||
|
|
dfe2dadb3e | ||
|
|
523029449e | ||
|
|
7ac764518c | ||
|
|
aa98589bb4 | ||
|
|
ca5f1d6de8 | ||
|
|
270631922d | ||
|
|
465ef3f7ad | ||
|
|
50e0423304 | ||
|
|
906a1eb18d | ||
|
|
7b77f1ef90 | ||
|
|
0005d11e32 | ||
|
|
ec84abc5c7 | ||
|
|
2ea0d8eb91 | ||
|
|
6a25aaf6b9 | ||
|
|
1b6b45bf2f | ||
|
|
ecda97f759 | ||
|
|
5566992422 | ||
|
|
cc637f12f6 | ||
|
|
592babaadd | ||
|
|
138a8156c8 | ||
|
|
7a008e66a4 | ||
|
|
164d481683 | ||
|
|
52f60188ca | ||
|
|
8cb29af712 | ||
|
|
60768a84c5 | ||
|
|
3266eb7cce | ||
|
|
c9a28312a9 | ||
|
|
a278704ae0 | ||
|
|
093a581ee1 | ||
|
|
69394c9c35 | ||
|
|
0f5e5d9e6d | ||
|
|
4606f0f28a | ||
|
|
cb88ceeae8 |
1
.github/workflows/build.yaml
vendored
1
.github/workflows/build.yaml
vendored
@@ -7,6 +7,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
|
|||||||
25
.github/workflows/quality.yaml
vendored
25
.github/workflows/quality.yaml
vendored
@@ -18,19 +18,28 @@ jobs:
|
|||||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- name: Formatting (rustfmt)
|
- name: Formatting (rustfmt)
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
- name: Clippy (all features)
|
- name: Clippy (all features,kvm)
|
||||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (all features,mshv)
|
||||||
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "common,mshv" -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (acpi,kvm)
|
- name: Clippy (acpi,kvm)
|
||||||
run: cargo clippy --all-targets --no-default-features --features "acpi,kvm" -- -D warnings
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,kvm" -- -D warnings
|
||||||
|
|
||||||
- name: Clippy (kvm)
|
- name: Clippy (kvm)
|
||||||
run: cargo clippy --all-targets --no-default-features --features "kvm" -- -D warnings
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "kvm" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (acpi,mshv)
|
||||||
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "acpi,mshv" -- -D warnings
|
||||||
|
|
||||||
|
- name: Clippy (mshv)
|
||||||
|
run: cargo clippy --all --all-targets --no-default-features --tests --features "mshv" -- -D warnings
|
||||||
|
|||||||
512
Cargo.lock
generated
512
Cargo.lock
generated
@@ -9,9 +9,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
|
checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
@@ -24,9 +24,9 @@ checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.14"
|
version = "0.7.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
|
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -42,9 +42,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.33"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
|
checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api_client"
|
name = "api_client"
|
||||||
@@ -52,9 +52,9 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "0.4.7"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
|
checksum = "0688b520bcc7498f6ca8fa006e8031d353e3fd4f51bd4a50fb03cc4230b28bd2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arch"
|
name = "arch"
|
||||||
@@ -111,9 +111,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.53"
|
version = "0.3.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e"
|
checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
@@ -125,9 +125,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.12.3"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@@ -143,9 +143,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2b_simd"
|
||||||
version = "0.5.10"
|
version = "0.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
|
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
@@ -176,9 +176,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.61"
|
version = "1.0.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -210,7 +210,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api_client",
|
"api_client",
|
||||||
@@ -230,8 +230,7 @@ dependencies = [
|
|||||||
"tempdir",
|
"tempdir",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"vhost_user_block",
|
"vm-memory",
|
||||||
"vhost_user_net",
|
|
||||||
"vmm",
|
"vmm",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
@@ -264,12 +263,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.7.2"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cfg-if 0.1.10",
|
"cfg-if 1.0.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -314,6 +313,19 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log 0.4.11",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epoll"
|
name = "epoll"
|
||||||
version = "4.3.1"
|
version = "4.3.1"
|
||||||
@@ -346,108 +358,22 @@ 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]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-channel"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-executor"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
"num_cpus",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-io"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-macro"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-hack",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-sink"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"futures-macro",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"memchr",
|
|
||||||
"pin-project",
|
|
||||||
"pin-utils",
|
|
||||||
"proc-macro-hack",
|
|
||||||
"proc-macro-nested",
|
|
||||||
"slab",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
@@ -461,9 +387,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.22.0"
|
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 = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
|
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
@@ -480,17 +406,27 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hypervisor"
|
name = "hypervisor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"env_logger",
|
||||||
|
"epoll",
|
||||||
|
"iced-x86",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"kvm-ioctls",
|
"kvm-ioctls",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
|
"mshv-bindings",
|
||||||
|
"mshv-ioctls",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -499,6 +435,17 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iced-x86"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "248ce8ff0784d2b15f3c3d8b01f529be0e18aa693a2ba7415df76857967c8fc3"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"rustc_version",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -522,9 +469,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnetwork"
|
name = "ipnetwork"
|
||||||
version = "0.16.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e"
|
checksum = "02c3eaab3ac0ede60ffa41add21970a7df7d91772c03383aac6c2c3d53cc716b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -573,15 +520,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.80"
|
version = "0.2.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libssh2-sys"
|
name = "libssh2-sys"
|
||||||
version = "0.2.19"
|
version = "0.2.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca46220853ba1c512fc82826d0834d87b06bcd3c2a42241b7de72f3d2fe17056"
|
checksum = "df40b13fe7ea1be9b9dffa365a51273816c345fc1811478b57ed7d964fbfc4ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -606,7 +553,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-loader"
|
name = "linux-loader"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/rust-vmm/linux-loader#3cf96c53ed6bfed9c7ca7dcfdbbcd04c86b6dd09"
|
source = "git+https://github.com/rust-vmm/linux-loader#acf9c21591813c87dc77f667618d433f62c821af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
]
|
]
|
||||||
@@ -653,7 +600,7 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "micro_http"
|
name = "micro_http"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/firecracker-microvm/micro-http#40309b72ee084b0a26d60208f0872bd7c986de45"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=master#59ab64440a95f7b16253fef67b5201c2ec4adaf7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"epoll",
|
"epoll",
|
||||||
]
|
]
|
||||||
@@ -668,6 +615,28 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mshv-bindings"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#2ded6daf97976aa27c1aca47964672c201a7d17d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"vmm-sys-util",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mshv-ioctls"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/cloud-hypervisor/mshv?branch=master#2ded6daf97976aa27c1aca47964672c201a7d17d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mshv-bindings",
|
||||||
|
"vmm-sys-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "net_gen"
|
name = "net_gen"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -694,33 +663,17 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_cpus"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.21.1"
|
version = "0.22.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
|
checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.58"
|
version = "0.9.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
|
checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -784,32 +737,6 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841"
|
|
||||||
dependencies = [
|
|
||||||
"pin-project-internal",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-internal"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
@@ -818,9 +745,9 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet"
|
name = "pnet"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c62df42dcd72f6f2a658bcf38509f1027df1440ac85f1af4badbe034418302dc"
|
checksum = "b657d5b9a98a2c81b82549922b8b15984e49f8120cd130b11a09f81b9b55d633"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
"pnet_base",
|
"pnet_base",
|
||||||
@@ -832,28 +759,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_base"
|
name = "pnet_base"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7cd5f7e15220afa66b0a9a62841ea10089f39dcaa1c29752c0b22dfc03111b5"
|
checksum = "4e4688aa497ef62129f302a5800ebde67825f8ff129f43690ca84099f6620bed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_datalink"
|
name = "pnet_datalink"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7318ae1d6e0b7fa1e49933233c9473f2b72d3d18b97e70e2716c6415dde5f915"
|
checksum = "59001c9c4d9d23bf2f61afaaf134a766fd6932ba2557c606b9112157053b9ac7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
"libc",
|
"libc",
|
||||||
"pnet_base",
|
"pnet_base",
|
||||||
"pnet_sys",
|
"pnet_sys",
|
||||||
"winapi 0.2.8",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_macros"
|
name = "pnet_macros"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbbd5c52c6e04aa720400f9c71cd0e8bcb38cd13421d5caabd9035e9efa47de9"
|
checksum = "d894a90dbdbe976e624453fc31b1912f658083778329442dda1cca94f76a3e76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"syntex",
|
"syntex",
|
||||||
@@ -862,18 +789,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_macros_support"
|
name = "pnet_macros_support"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "daf9c5c0c36766d0a4da9ab268c0700771b8ec367b9463fd678109fa28463c5b"
|
checksum = "4b99269a458570bc06a9132254349f6543d9abc92e88b68d8de934aac9481f6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pnet_base",
|
"pnet_base",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_packet"
|
name = "pnet_packet"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89e26a864d71d0ac51a549cf40283c44ed1b8f98168545638a4730ef9f560283"
|
checksum = "33f8238f4eb897a55ca06510cd71afb5b5ca7b4ff2d7188f1ca855fc1710133e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glob",
|
"glob",
|
||||||
"pnet_base",
|
"pnet_base",
|
||||||
@@ -884,20 +811,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_sys"
|
name = "pnet_sys"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73f0de0c52609f157b25d79ce24d9016ab1bbf10cde761397200d634a833872c"
|
checksum = "7589e4c4e7ed72a3ffdff8a65d3bea84e8c3a23e19d0a10e8f45efdf632fff15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"winapi 0.2.8",
|
"winapi 0.3.9",
|
||||||
"ws2_32-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnet_transport"
|
name = "pnet_transport"
|
||||||
version = "0.26.0"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6712ab76534340494d849e3c51c64a6261e4b451337b7c05bd3681e384c48b10"
|
checksum = "326abdfd2e70e8e943bd58087b59686de170cac050a3b19c9fcc84db01690af5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"pnet_base",
|
"pnet_base",
|
||||||
@@ -907,21 +833,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.9"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-hack"
|
|
||||||
version = "0.5.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-nested"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
@@ -1050,9 +964,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
|
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -1062,9 +976,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.20"
|
version = "0.6.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
|
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remain"
|
name = "remain"
|
||||||
@@ -1088,9 +1002,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-argon2"
|
name = "rust-argon2"
|
||||||
version = "0.8.2"
|
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 = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
|
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
@@ -1110,6 +1024,15 @@ 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"
|
||||||
@@ -1131,16 +1054,31 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "semver"
|
||||||
version = "1.0.117"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
|
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]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.118"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.117"
|
version = "1.0.118"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
|
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1149,9 +1087,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.59"
|
version = "1.0.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
|
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -1160,9 +1098,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.1.16"
|
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 = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
|
checksum = "cbae917d5719a9884206132a443899e8c9056b48a156233a823417b39f34044f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@@ -1170,31 +1108,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.2.1"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
|
checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "slab"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.4.2"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
|
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ssh2"
|
name = "ssh2"
|
||||||
version = "0.8.2"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba56d741dab9a295bcb131ebfbe57f8fea2e1b7ae203e9184f5d7648213e4460"
|
checksum = "ed024de0a5e6944fe3080a3745e7a5649c5517ea2a6cfb16333f94f89c248985"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1202,6 +1133,12 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -1210,9 +1147,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.48"
|
version = "1.0.54"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
|
checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1324,6 +1261,15 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -1336,18 +1282,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.21"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
|
checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.21"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
|
checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1365,9 +1311,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
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 = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
|
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
@@ -1380,9 +1335,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.13"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
|
checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
@@ -1407,10 +1362,11 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.1.1"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"matches",
|
"matches",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
@@ -1493,27 +1449,6 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vhost_user_fs"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.2.1",
|
|
||||||
"clap",
|
|
||||||
"epoll",
|
|
||||||
"futures",
|
|
||||||
"libc",
|
|
||||||
"log 0.4.11",
|
|
||||||
"seccomp",
|
|
||||||
"tempdir",
|
|
||||||
"vhost",
|
|
||||||
"vhost_user_backend",
|
|
||||||
"virtio-bindings",
|
|
||||||
"virtio-devices",
|
|
||||||
"vm-memory",
|
|
||||||
"vm-virtio",
|
|
||||||
"vmm-sys-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost_user_net"
|
name = "vhost_user_net"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1592,8 +1527,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vm-memory"
|
name = "vm-memory"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/cloud-hypervisor/vm-memory?branch=ch#6f2f7107f29a8f2b66b64a849aeace20783ec8f3"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45b5b0a6f371f8147143b1adb95edddafc9cb9e40adaf94edb6f93a1d04b0330"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1667,9 +1603,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vmm-sys-util"
|
name = "vmm-sys-util"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "183d25b56a61a6f518ef464ac578e790f04added34dfaab59a453d8a03cb7bd0"
|
checksum = "d1cdd1d72e262bbfb014de65ada24c1ac50e10a2e3b1e8ec052df188c2ee5dfa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.1",
|
"bitflags 1.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1720,6 +1656,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -1727,11 +1672,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws2_32-sys"
|
name = "zerocopy"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.2.8",
|
"byteorder",
|
||||||
"winapi-build",
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|||||||
25
Cargo.toml
25
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor"
|
name = "cloud-hypervisor"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
default-run = "cloud-hypervisor"
|
default-run = "cloud-hypervisor"
|
||||||
@@ -18,47 +18,43 @@ 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.0.1"
|
||||||
hypervisor = { path = "hypervisor" }
|
hypervisor = { path = "hypervisor" }
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = { version = "0.4.11", features = ["std"] }
|
log = { version = "0.4.11", 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.59"
|
serde_json = "1.0.60"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
vhost_user_block = { path = "vhost_user_block"}
|
|
||||||
vhost_user_net = { path = "vhost_user_net"}
|
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.6.1"
|
vmm-sys-util = "0.7.0"
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
vm-memory = "0.4.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.crates-io]
|
|
||||||
vm-memory = { git = "https://github.com/cloud-hypervisor/vm-memory", branch = "ch" }
|
|
||||||
[patch.'https://github.com/rust-vmm/vhost']
|
[patch.'https://github.com/rust-vmm/vhost']
|
||||||
vhost_rs = { git = "https://github.com/cloud-hypervisor/vhost", branch = "ch", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
vhost_rs = { git = "https://github.com/cloud-hypervisor/vhost", branch = "ch", package = "vhost", features = ["vhost-user-master", "vhost-user-slave"] }
|
||||||
[patch.'https://github.com/rust-vmm/kvm-ioctls']
|
|
||||||
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
|
|
||||||
[patch.'https://github.com/rust-vmm/kvm-bindings']
|
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ssh2 = "0.8.2"
|
ssh2 = "0.9.0"
|
||||||
dirs = "3.0.1"
|
dirs = "3.0.1"
|
||||||
credibility = "0.1.3"
|
credibility = "0.1.3"
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
lazy_static= "1.4.0"
|
lazy_static= "1.4.0"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
serde_json = "1.0.59"
|
serde_json = "1.0.60"
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["acpi", "cmos", "io_uring", "kvm"]
|
default = ["acpi", "cmos", "io_uring", "kvm"]
|
||||||
|
# Common features for all hypervisors
|
||||||
|
common = ["acpi", "cmos", "fwdebug", "io_uring"]
|
||||||
acpi = ["vmm/acpi"]
|
acpi = ["vmm/acpi"]
|
||||||
cmos = ["vmm/cmos"]
|
cmos = ["vmm/cmos"]
|
||||||
fwdebug = ["vmm/fwdebug"]
|
fwdebug = ["vmm/fwdebug"]
|
||||||
kvm = ["vmm/kvm"]
|
kvm = ["vmm/kvm"]
|
||||||
|
mshv = ["vmm/mshv"]
|
||||||
io_uring = ["vmm/io_uring"]
|
io_uring = ["vmm/io_uring"]
|
||||||
|
|
||||||
# Integration tests require a special environment to run in
|
# Integration tests require a special environment to run in
|
||||||
@@ -80,7 +76,6 @@ members = [
|
|||||||
"qcow",
|
"qcow",
|
||||||
"vhost_user_backend",
|
"vhost_user_backend",
|
||||||
"vhost_user_block",
|
"vhost_user_block",
|
||||||
"vhost_user_fs",
|
|
||||||
"vhost_user_net",
|
"vhost_user_net",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
"vmm",
|
"vmm",
|
||||||
|
|||||||
36
Jenkinsfile
vendored
36
Jenkinsfile
vendored
@@ -26,9 +26,6 @@ pipeline{
|
|||||||
parallel {
|
parallel {
|
||||||
stage ('Worker build') {
|
stage ('Worker build') {
|
||||||
agent { node { label 'groovy' } }
|
agent { node { label 'groovy' } }
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -46,6 +43,9 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run integration tests') {
|
stage ('Run integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration"
|
sh "scripts/dev_cli.sh tests --integration"
|
||||||
}
|
}
|
||||||
@@ -54,9 +54,6 @@ pipeline{
|
|||||||
}
|
}
|
||||||
stage ('AArch64 worker build') {
|
stage ('AArch64 worker build') {
|
||||||
agent { node { label 'bionic-arm64' } }
|
agent { node { label 'bionic-arm64' } }
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -69,6 +66,9 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run integration tests') {
|
stage ('Run integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration"
|
sh "scripts/dev_cli.sh tests --integration"
|
||||||
}
|
}
|
||||||
@@ -83,9 +83,6 @@ pipeline{
|
|||||||
}
|
}
|
||||||
stage ('Worker build (musl)') {
|
stage ('Worker build (musl)') {
|
||||||
agent { node { label 'groovy' } }
|
agent { node { label 'groovy' } }
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -98,6 +95,9 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run integration tests for musl') {
|
stage ('Run integration tests for musl') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
sh "scripts/dev_cli.sh tests --integration --libc musl"
|
||||||
}
|
}
|
||||||
@@ -106,9 +106,6 @@ pipeline{
|
|||||||
}
|
}
|
||||||
stage ('Worker build SGX') {
|
stage ('Worker build SGX') {
|
||||||
agent { node { label 'bionic-sgx' } }
|
agent { node { label 'bionic-sgx' } }
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
when { branch 'master' }
|
when { branch 'master' }
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
@@ -117,11 +114,17 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run SGX integration tests') {
|
stage ('Run SGX integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration-sgx"
|
sh "scripts/dev_cli.sh tests --integration-sgx"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run SGX integration tests for musl') {
|
stage ('Run SGX integration tests for musl') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
sh "scripts/dev_cli.sh tests --integration-sgx --libc musl"
|
||||||
}
|
}
|
||||||
@@ -136,9 +139,6 @@ pipeline{
|
|||||||
}
|
}
|
||||||
stage ('Worker build - Windows guest') {
|
stage ('Worker build - Windows guest') {
|
||||||
agent { node { label 'groovy-win' } }
|
agent { node { label 'groovy-win' } }
|
||||||
options {
|
|
||||||
timeout(time: 1, unit: 'HOURS')
|
|
||||||
}
|
|
||||||
stages {
|
stages {
|
||||||
stage ('Checkout') {
|
stage ('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
@@ -161,11 +161,17 @@ pipeline{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run Windows guest integration tests') {
|
stage ('Run Windows guest integration tests') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration-windows"
|
sh "scripts/dev_cli.sh tests --integration-windows"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage ('Run Windows guest integration tests for musl') {
|
stage ('Run Windows guest integration tests for musl') {
|
||||||
|
options {
|
||||||
|
timeout(time: 1, unit: 'HOURS')
|
||||||
|
}
|
||||||
steps {
|
steps {
|
||||||
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
sh "scripts/dev_cli.sh tests --integration-windows --libc musl"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ authors = ["The Cloud Hypervisor Authors"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vm-memory = "0.3.0"
|
vm-memory = "0.4.0"
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ acpi = ["acpi_tables"]
|
|||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
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"
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap"] }
|
vm-memory = { version = "0.4.0", features = ["backend-mmap"] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
arch_gen = { path = "../arch_gen" }
|
arch_gen = { path = "../arch_gen" }
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Method to initialize the GIC device
|
/// Method to initialize the GIC device
|
||||||
|
#[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)?;
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
use vm_memory::{GuestAddress, GuestUsize};
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
|
/// Below this address will reside the GIC, above this address will reside the MMIO devices.
|
||||||
pub const MAPPED_IO_START: u64 = 0x0900_0000;
|
pub const MAPPED_IO_START: u64 = 0x0900_0000;
|
||||||
@@ -61,7 +61,7 @@ pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x3000_0000;
|
|||||||
|
|
||||||
/// PCI MMCONFIG space (start: after the device space at 1 GiB, length: 256MiB)
|
/// PCI MMCONFIG space (start: after the device space at 1 GiB, length: 256MiB)
|
||||||
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x4000_0000);
|
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x4000_0000);
|
||||||
pub const PCI_MMCONFIG_SIZE: GuestUsize = 256 << 20;
|
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||||
|
|
||||||
/// Start of RAM on 64 bit ARM.
|
/// Start of RAM on 64 bit ARM.
|
||||||
pub const RAM_64BIT_START: u64 = 0x8000_0000;
|
pub const RAM_64BIT_START: u64 = 0x8000_0000;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// 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 vm_memory::{GuestAddress, GuestUsize};
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@@ -81,22 +81,22 @@ pub const HIGH_RAM_START: GuestAddress = GuestAddress(0x100000);
|
|||||||
|
|
||||||
// ** 32-bit reserved area (start: 3GiB, length: 1GiB) **
|
// ** 32-bit reserved area (start: 3GiB, length: 1GiB) **
|
||||||
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000);
|
pub const MEM_32BIT_RESERVED_START: GuestAddress = GuestAddress(0xc000_0000);
|
||||||
pub const MEM_32BIT_RESERVED_SIZE: GuestUsize = 1024 << 20;
|
pub const MEM_32BIT_RESERVED_SIZE: u64 = 1024 << 20;
|
||||||
|
|
||||||
// == Fixed constants within the "32-bit reserved" range ==
|
// == Fixed constants within the "32-bit reserved" range ==
|
||||||
|
|
||||||
// Sub range: 32-bit PCI devices (start: 3GiB, length: 640Mib)
|
// Sub range: 32-bit PCI devices (start: 3GiB, length: 640Mib)
|
||||||
pub const MEM_32BIT_DEVICES_START: GuestAddress = MEM_32BIT_RESERVED_START;
|
pub const MEM_32BIT_DEVICES_START: GuestAddress = MEM_32BIT_RESERVED_START;
|
||||||
pub const MEM_32BIT_DEVICES_SIZE: GuestUsize = 640 << 20;
|
pub const MEM_32BIT_DEVICES_SIZE: u64 = 640 << 20;
|
||||||
|
|
||||||
// PCI MMCONFIG space (start: after the device space, length: 256MiB)
|
// PCI MMCONFIG space (start: after the device space, length: 256MiB)
|
||||||
pub const PCI_MMCONFIG_START: GuestAddress =
|
pub const PCI_MMCONFIG_START: GuestAddress =
|
||||||
GuestAddress(MEM_32BIT_DEVICES_START.0 + MEM_32BIT_DEVICES_SIZE);
|
GuestAddress(MEM_32BIT_DEVICES_START.0 + MEM_32BIT_DEVICES_SIZE);
|
||||||
pub const PCI_MMCONFIG_SIZE: GuestUsize = 256 << 20;
|
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||||
|
|
||||||
// IOAPIC
|
// IOAPIC
|
||||||
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
pub const IOAPIC_START: GuestAddress = GuestAddress(0xfec0_0000);
|
||||||
pub const IOAPIC_SIZE: GuestUsize = 0x20;
|
pub const IOAPIC_SIZE: u64 = 0x20;
|
||||||
|
|
||||||
// APIC
|
// APIC
|
||||||
pub const APIC_START: GuestAddress = GuestAddress(0xfee0_0000);
|
pub const APIC_START: GuestAddress = GuestAddress(0xfee0_0000);
|
||||||
|
|||||||
@@ -7,7 +7,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 LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
mod gdt;
|
|
||||||
pub mod interrupts;
|
pub mod interrupts;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
mod mptable;
|
mod mptable;
|
||||||
@@ -992,7 +991,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_system_configuration() {
|
fn test_system_configuration() {
|
||||||
let no_vcpus = 4;
|
let no_vcpus = 4;
|
||||||
let gm = GuestMemoryMmap::from_ranges(&vec![(GuestAddress(0), 0x10000)]).unwrap();
|
let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
|
||||||
let config_err = configure_system(
|
let config_err = configure_system(
|
||||||
&gm,
|
&gm,
|
||||||
GuestAddress(0),
|
GuestAddress(0),
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, result};
|
use std::{mem, result};
|
||||||
|
|
||||||
use super::gdt::{gdt_entry, segment_from_gdt};
|
|
||||||
use super::BootProtocol;
|
use super::BootProtocol;
|
||||||
|
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
||||||
|
use hypervisor::arch::x86::regs::*;
|
||||||
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
use hypervisor::x86_64::{FpuState, SpecialRegisters, StandardRegisters};
|
||||||
use layout::{
|
use layout::{
|
||||||
BOOT_GDT_START, BOOT_IDT_START, PDE_START, PDPTE_START, PML4_START, PML5_START, PVH_INFO_START,
|
BOOT_GDT_START, BOOT_IDT_START, PDE_START, PDPTE_START, PML4_START, PML5_START, PVH_INFO_START,
|
||||||
@@ -134,14 +135,6 @@ pub fn setup_sregs(
|
|||||||
|
|
||||||
const BOOT_GDT_MAX: usize = 4;
|
const BOOT_GDT_MAX: usize = 4;
|
||||||
|
|
||||||
const EFER_LMA: u64 = 0x400;
|
|
||||||
const EFER_LME: u64 = 0x100;
|
|
||||||
|
|
||||||
const X86_CR0_PE: u64 = 0x1;
|
|
||||||
const X86_CR0_PG: u64 = 0x80000000;
|
|
||||||
const X86_CR4_PAE: u64 = 0x20;
|
|
||||||
const X86_CR4_LA57: u64 = 0x1000;
|
|
||||||
|
|
||||||
fn write_gdt_table(table: &[u64], guest_mem: &GuestMemoryMmap) -> Result<()> {
|
fn write_gdt_table(table: &[u64], guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||||
let boot_gdt_addr = BOOT_GDT_START;
|
let boot_gdt_addr = BOOT_GDT_START;
|
||||||
for (index, entry) in table.iter().enumerate() {
|
for (index, entry) in table.iter().enumerate() {
|
||||||
@@ -209,12 +202,12 @@ pub fn configure_segments_and_sregs(
|
|||||||
|
|
||||||
match boot_prot {
|
match boot_prot {
|
||||||
BootProtocol::PvhBoot => {
|
BootProtocol::PvhBoot => {
|
||||||
sregs.cr0 = X86_CR0_PE;
|
sregs.cr0 = CR0_PE;
|
||||||
sregs.cr4 = 0;
|
sregs.cr4 = 0;
|
||||||
}
|
}
|
||||||
BootProtocol::LinuxBoot => {
|
BootProtocol::LinuxBoot => {
|
||||||
/* 64-bit protected mode */
|
/* 64-bit protected mode */
|
||||||
sregs.cr0 |= X86_CR0_PE;
|
sregs.cr0 |= CR0_PE;
|
||||||
sregs.efer |= EFER_LME | EFER_LMA;
|
sregs.efer |= EFER_LME | EFER_LMA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +223,7 @@ pub fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) ->
|
|||||||
.map_err(Error::WritePML5Address)?;
|
.map_err(Error::WritePML5Address)?;
|
||||||
|
|
||||||
sregs.cr3 = PML5_START.raw_value();
|
sregs.cr3 = PML5_START.raw_value();
|
||||||
sregs.cr4 |= X86_CR4_LA57;
|
sregs.cr4 |= CR4_LA57;
|
||||||
} else {
|
} else {
|
||||||
sregs.cr3 = PML4_START.raw_value();
|
sregs.cr3 = PML4_START.raw_value();
|
||||||
}
|
}
|
||||||
@@ -250,8 +243,8 @@ pub fn setup_page_tables(mem: &GuestMemoryMmap, sregs: &mut SpecialRegisters) ->
|
|||||||
.map_err(Error::WritePDEAddress)?;
|
.map_err(Error::WritePDEAddress)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
sregs.cr4 |= X86_CR4_PAE;
|
sregs.cr4 |= CR4_PAE;
|
||||||
sregs.cr0 |= X86_CR0_PG;
|
sregs.cr0 |= CR0_PG;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -264,7 +257,7 @@ mod tests {
|
|||||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
||||||
|
|
||||||
fn create_guest_mem() -> GuestMemoryMmap {
|
fn create_guest_mem() -> GuestMemoryMmap {
|
||||||
GuestMemoryMmap::from_ranges(&vec![(GuestAddress(0), 0x10000)]).unwrap()
|
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_u64(gm: &GuestMemoryMmap, offset: GuestAddress) -> u64 {
|
fn read_u64(gm: &GuestMemoryMmap, offset: GuestAddress) -> u64 {
|
||||||
@@ -301,7 +294,7 @@ mod tests {
|
|||||||
assert_eq!(0, sregs.tr.base);
|
assert_eq!(0, sregs.tr.base);
|
||||||
assert_eq!(0xffffffff, sregs.tr.limit);
|
assert_eq!(0xffffffff, sregs.tr.limit);
|
||||||
assert_eq!(0, sregs.tr.avl);
|
assert_eq!(0, sregs.tr.avl);
|
||||||
assert_eq!(X86_CR0_PE, sregs.cr0);
|
assert_eq!(CR0_PE, sregs.cr0);
|
||||||
assert_eq!(EFER_LME | EFER_LMA, sregs.efer);
|
assert_eq!(EFER_LME | EFER_LMA, sregs.efer);
|
||||||
|
|
||||||
configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::PvhBoot).unwrap();
|
configure_segments_and_sregs(&gm, &mut sregs, BootProtocol::PvhBoot).unwrap();
|
||||||
@@ -331,7 +324,7 @@ mod tests {
|
|||||||
assert_eq!(0x67, sregs.tr.limit);
|
assert_eq!(0x67, sregs.tr.limit);
|
||||||
assert_eq!(0xb, sregs.tr.type_);
|
assert_eq!(0xb, sregs.tr.type_);
|
||||||
assert_eq!(0, sregs.tr.avl);
|
assert_eq!(0, sregs.tr.avl);
|
||||||
assert_eq!(X86_CR0_PE, sregs.cr0);
|
assert_eq!(CR0_PE, sregs.cr0);
|
||||||
assert_eq!(0, sregs.cr4);
|
assert_eq!(0, sregs.cr4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +351,7 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
assert_eq!(PML4_START.raw_value(), sregs.cr3);
|
assert_eq!(PML4_START.raw_value(), sregs.cr3);
|
||||||
}
|
}
|
||||||
assert_eq!(X86_CR4_PAE, sregs.cr4);
|
assert_eq!(CR4_PAE, sregs.cr4);
|
||||||
assert_eq!(X86_CR0_PG, sregs.cr0);
|
assert_eq!(CR0_PG, sregs.cr0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
concat!("Alignment of ", stringify!(mpf_intel))
|
concat!("Alignment of ", stringify!(mpf_intel))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).signature as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).signature as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -77,7 +77,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).physptr as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).physptr as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -87,7 +87,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).length as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).length as *const _ as usize },
|
||||||
8usize,
|
8usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -97,7 +97,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).specification as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).specification as *const _ as usize },
|
||||||
9usize,
|
9usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -107,7 +107,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).checksum as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).checksum as *const _ as usize },
|
||||||
10usize,
|
10usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -117,7 +117,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).feature1 as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).feature1 as *const _ as usize },
|
||||||
11usize,
|
11usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -127,7 +127,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).feature2 as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).feature2 as *const _ as usize },
|
||||||
12usize,
|
12usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -137,7 +137,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).feature3 as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).feature3 as *const _ as usize },
|
||||||
13usize,
|
13usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -147,7 +147,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).feature4 as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).feature4 as *const _ as usize },
|
||||||
14usize,
|
14usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -157,7 +157,7 @@ fn bindgen_test_layout_mpf_intel() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpf_intel)).feature5 as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpf_intel>()).feature5 as *const _ as usize },
|
||||||
15usize,
|
15usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -200,7 +200,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_table))
|
concat!("Alignment of ", stringify!(mpc_table))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).signature as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).signature as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -210,7 +210,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).length as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).length as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -220,7 +220,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).spec as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).spec as *const _ as usize },
|
||||||
6usize,
|
6usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -230,7 +230,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).checksum as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).checksum as *const _ as usize },
|
||||||
7usize,
|
7usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -240,7 +240,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).oem as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).oem as *const _ as usize },
|
||||||
8usize,
|
8usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -250,7 +250,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).productid as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).productid as *const _ as usize },
|
||||||
16usize,
|
16usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -260,7 +260,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).oemptr as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).oemptr as *const _ as usize },
|
||||||
28usize,
|
28usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -270,7 +270,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).oemsize as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).oemsize as *const _ as usize },
|
||||||
32usize,
|
32usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -280,7 +280,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).oemcount as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).oemcount as *const _ as usize },
|
||||||
34usize,
|
34usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -290,7 +290,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).lapic as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).lapic as *const _ as usize },
|
||||||
36usize,
|
36usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -300,7 +300,7 @@ fn bindgen_test_layout_mpc_table() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_table)).reserved as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_table>()).reserved as *const _ as usize },
|
||||||
40usize,
|
40usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -339,7 +339,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_cpu))
|
concat!("Alignment of ", stringify!(mpc_cpu))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).type_ as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).type_ as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -349,7 +349,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).apicid as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).apicid as *const _ as usize },
|
||||||
1usize,
|
1usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -359,7 +359,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).apicver as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).apicver as *const _ as usize },
|
||||||
2usize,
|
2usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -369,7 +369,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).cpuflag as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).cpuflag as *const _ as usize },
|
||||||
3usize,
|
3usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -379,7 +379,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).cpufeature as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).cpufeature as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -389,7 +389,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).featureflag as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).featureflag as *const _ as usize },
|
||||||
8usize,
|
8usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -399,7 +399,7 @@ fn bindgen_test_layout_mpc_cpu() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_cpu)).reserved as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_cpu>()).reserved as *const _ as usize },
|
||||||
12usize,
|
12usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -434,7 +434,7 @@ fn bindgen_test_layout_mpc_bus() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_bus))
|
concat!("Alignment of ", stringify!(mpc_bus))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_bus)).type_ as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_bus>()).type_ as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -444,7 +444,7 @@ fn bindgen_test_layout_mpc_bus() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_bus)).busid as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_bus>()).busid as *const _ as usize },
|
||||||
1usize,
|
1usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -454,7 +454,7 @@ fn bindgen_test_layout_mpc_bus() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_bus)).bustype as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_bus>()).bustype as *const _ as usize },
|
||||||
2usize,
|
2usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -491,7 +491,7 @@ fn bindgen_test_layout_mpc_ioapic() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_ioapic))
|
concat!("Alignment of ", stringify!(mpc_ioapic))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_ioapic)).type_ as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_ioapic>()).type_ as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -501,7 +501,7 @@ fn bindgen_test_layout_mpc_ioapic() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_ioapic)).apicid as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_ioapic>()).apicid as *const _ as usize },
|
||||||
1usize,
|
1usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -511,7 +511,7 @@ fn bindgen_test_layout_mpc_ioapic() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_ioapic)).apicver as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_ioapic>()).apicver as *const _ as usize },
|
||||||
2usize,
|
2usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -521,7 +521,7 @@ fn bindgen_test_layout_mpc_ioapic() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_ioapic)).flags as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_ioapic>()).flags as *const _ as usize },
|
||||||
3usize,
|
3usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -531,7 +531,7 @@ fn bindgen_test_layout_mpc_ioapic() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_ioapic)).apicaddr as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_ioapic>()).apicaddr as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -570,7 +570,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_intsrc))
|
concat!("Alignment of ", stringify!(mpc_intsrc))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).type_ as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).type_ as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -580,7 +580,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).irqtype as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).irqtype as *const _ as usize },
|
||||||
1usize,
|
1usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -590,7 +590,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).irqflag as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).irqflag as *const _ as usize },
|
||||||
2usize,
|
2usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -600,7 +600,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).srcbus as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).srcbus as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -610,7 +610,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).srcbusirq as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).srcbusirq as *const _ as usize },
|
||||||
5usize,
|
5usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -620,7 +620,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).dstapic as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).dstapic as *const _ as usize },
|
||||||
6usize,
|
6usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -630,7 +630,7 @@ fn bindgen_test_layout_mpc_intsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_intsrc)).dstirq as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_intsrc>()).dstirq as *const _ as usize },
|
||||||
7usize,
|
7usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -674,7 +674,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_lintsrc))
|
concat!("Alignment of ", stringify!(mpc_lintsrc))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).type_ as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).type_ as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -684,7 +684,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).irqtype as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).irqtype as *const _ as usize },
|
||||||
1usize,
|
1usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -694,7 +694,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).irqflag as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).irqflag as *const _ as usize },
|
||||||
2usize,
|
2usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -704,7 +704,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).srcbusid as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).srcbusid as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -714,7 +714,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).srcbusirq as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).srcbusirq as *const _ as usize },
|
||||||
5usize,
|
5usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -724,7 +724,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).destapic as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).destapic as *const _ as usize },
|
||||||
6usize,
|
6usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -734,7 +734,7 @@ fn bindgen_test_layout_mpc_lintsrc() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_lintsrc)).destapiclint as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_lintsrc>()).destapiclint as *const _ as usize },
|
||||||
7usize,
|
7usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -771,7 +771,7 @@ fn bindgen_test_layout_mpc_oemtable() {
|
|||||||
concat!("Alignment of ", stringify!(mpc_oemtable))
|
concat!("Alignment of ", stringify!(mpc_oemtable))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_oemtable)).signature as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_oemtable>()).signature as *const _ as usize },
|
||||||
0usize,
|
0usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -781,7 +781,7 @@ fn bindgen_test_layout_mpc_oemtable() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_oemtable)).length as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_oemtable>()).length as *const _ as usize },
|
||||||
4usize,
|
4usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -791,7 +791,7 @@ fn bindgen_test_layout_mpc_oemtable() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_oemtable)).rev as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_oemtable>()).rev as *const _ as usize },
|
||||||
6usize,
|
6usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -801,7 +801,7 @@ fn bindgen_test_layout_mpc_oemtable() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_oemtable)).checksum as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_oemtable>()).checksum as *const _ as usize },
|
||||||
7usize,
|
7usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
@@ -811,7 +811,7 @@ fn bindgen_test_layout_mpc_oemtable() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unsafe { &(*(0 as *const mpc_oemtable)).mpc as *const _ as usize },
|
unsafe { &(*std::ptr::null::<mpc_oemtable>()).mpc as *const _ as usize },
|
||||||
8usize,
|
8usize,
|
||||||
concat!(
|
concat!(
|
||||||
"Alignment of field: ",
|
"Alignment of field: ",
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ io_uring = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
io-uring = ">=0.4.0"
|
io-uring = ">=0.4.0"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
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"
|
||||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.4.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"
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ anyhow = "1.0"
|
|||||||
bitflags = ">=1.2.1"
|
bitflags = ">=1.2.1"
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
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"
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
vm-memory = "0.3.0"
|
vm-memory = "0.4.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|
||||||
|
|||||||
@@ -394,13 +394,13 @@ mod tests {
|
|||||||
Box::new(serial_out.clone()),
|
Box::new(serial_out.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, DATA as u64, &['x' as u8, 'y' as u8]);
|
serial.write(0, DATA as u64, &[b'x', b'y']);
|
||||||
serial.write(0, DATA as u64, &['a' as u8]);
|
serial.write(0, DATA as u64, &[b'a']);
|
||||||
serial.write(0, DATA as u64, &['b' as u8]);
|
serial.write(0, DATA as u64, &[b'b']);
|
||||||
serial.write(0, DATA as u64, &['c' as u8]);
|
serial.write(0, DATA as u64, &[b'c']);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serial_out.buf.lock().unwrap().as_slice(),
|
serial_out.buf.lock().unwrap().as_slice(),
|
||||||
&['a' as u8, 'b' as u8, 'c' as u8]
|
&[b'a', b'b', b'c']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,16 +411,14 @@ mod tests {
|
|||||||
let mut serial = Serial::new_out(
|
let mut serial = Serial::new_out(
|
||||||
String::from(SERIAL_NAME),
|
String::from(SERIAL_NAME),
|
||||||
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))),
|
||||||
Box::new(serial_out.clone()),
|
Box::new(serial_out),
|
||||||
);
|
);
|
||||||
|
|
||||||
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
|
// 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)
|
// counter doesn't change (for 0 it blocks)
|
||||||
assert!(intr_evt.write(1).is_ok());
|
assert!(intr_evt.write(1).is_ok());
|
||||||
serial.write(0, IER as u64, &[IER_RECV_BIT]);
|
serial.write(0, IER as u64, &[IER_RECV_BIT]);
|
||||||
serial
|
serial.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
|
||||||
.queue_input_bytes(&['a' as u8, 'b' as u8, 'c' as u8])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
|
|
||||||
@@ -433,11 +431,11 @@ mod tests {
|
|||||||
serial.read(0, LSR as u64, &mut data[..]);
|
serial.read(0, LSR as u64, &mut data[..]);
|
||||||
assert_ne!(data[0] & LSR_DATA_BIT, 0);
|
assert_ne!(data[0] & LSR_DATA_BIT, 0);
|
||||||
serial.read(0, DATA as u64, &mut data[..]);
|
serial.read(0, DATA as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], 'a' as u8);
|
assert_eq!(data[0], b'a');
|
||||||
serial.read(0, DATA as u64, &mut data[..]);
|
serial.read(0, DATA as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], 'b' as u8);
|
assert_eq!(data[0], b'b');
|
||||||
serial.read(0, DATA as u64, &mut data[..]);
|
serial.read(0, DATA as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], 'c' as u8);
|
assert_eq!(data[0], b'c');
|
||||||
|
|
||||||
// check if reading from the largest u8 offset returns 0
|
// check if reading from the largest u8 offset returns 0
|
||||||
serial.read(0, 0xff, &mut data[..]);
|
serial.read(0, 0xff, &mut data[..]);
|
||||||
@@ -456,7 +454,7 @@ mod tests {
|
|||||||
// counter doesn't change (for 0 it blocks)
|
// counter doesn't change (for 0 it blocks)
|
||||||
assert!(intr_evt.write(1).is_ok());
|
assert!(intr_evt.write(1).is_ok());
|
||||||
serial.write(0, IER as u64, &[IER_THR_BIT]);
|
serial.write(0, IER as u64, &[IER_THR_BIT]);
|
||||||
serial.write(0, DATA as u64, &['a' as u8]);
|
serial.write(0, DATA as u64, &[b'a']);
|
||||||
|
|
||||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
let mut data = [0u8];
|
let mut data = [0u8];
|
||||||
@@ -496,9 +494,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT as u8]);
|
serial.write(0, MCR as u64, &[MCR_LOOP_BIT as u8]);
|
||||||
serial.write(0, DATA as u64, &['a' as u8]);
|
serial.write(0, DATA as u64, &[b'a']);
|
||||||
serial.write(0, DATA as u64, &['b' as u8]);
|
serial.write(0, DATA as u64, &[b'b']);
|
||||||
serial.write(0, DATA as u64, &['c' as u8]);
|
serial.write(0, DATA as u64, &[b'c']);
|
||||||
|
|
||||||
let mut data = [0u8];
|
let mut data = [0u8];
|
||||||
serial.read(0, MSR as u64, &mut data[..]);
|
serial.read(0, MSR as u64, &mut data[..]);
|
||||||
@@ -506,11 +504,11 @@ mod tests {
|
|||||||
serial.read(0, MCR as u64, &mut data[..]);
|
serial.read(0, MCR as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], MCR_LOOP_BIT as u8);
|
assert_eq!(data[0], MCR_LOOP_BIT as u8);
|
||||||
serial.read(0, DATA as u64, &mut data[..]);
|
serial.read(0, DATA as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], 'a' as u8);
|
assert_eq!(data[0], b'a');
|
||||||
serial.read(0, DATA as u64, &mut data[..]);
|
serial.read(0, DATA as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], 'b' as u8);
|
assert_eq!(data[0], b'b');
|
||||||
serial.read(0, DATA as u64, &mut data[..]);
|
serial.read(0, DATA as u64, &mut data[..]);
|
||||||
assert_eq!(data[0], 'c' as u8);
|
assert_eq!(data[0], b'c');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
45
docs/profiling.md
Normal file
45
docs/profiling.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Profiling
|
||||||
|
|
||||||
|
`perf` can be used to profile the `cloud-hypervisor` binary but it is necessary to make some modifications to the the build in order to produce a binary that gives useful results.
|
||||||
|
|
||||||
|
## Building a suitable binary
|
||||||
|
|
||||||
|
Modify the `Cargo.toml` file to add `debug = 1` to the `[profile.release]` block. It should look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
debug = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
This adds the symbol information to the release binary but does not otherwise affect the performance.
|
||||||
|
|
||||||
|
The binary must also be built with frame pointers included so that the call graph can be captured by the profiler.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cargo clean && RUSTFLAGS='-C force-frame-pointers=y' cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Profiling
|
||||||
|
|
||||||
|
`perf` may then be used in the usual manner:
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ perf record -g target/release/cloud-hypervisor \
|
||||||
|
--kernel ~/src/linux/vmlinux \
|
||||||
|
--pmem file=~/workloads/focal.raw \
|
||||||
|
--cpus boot=1 --memory size=1G \
|
||||||
|
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
|
||||||
|
--serial tty --console off \
|
||||||
|
--api-socket=/tmp/api1
|
||||||
|
```
|
||||||
|
|
||||||
|
For analysing the samples:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ perf report -g
|
||||||
|
```
|
||||||
|
|
||||||
|
If profiling with a network device attached either the TAP device must be already created and configured or the profiling must be done as root so that the TAP device can be created.
|
||||||
35
docs/uefi.md
35
docs/uefi.md
@@ -35,8 +35,41 @@ To make Cloud Hypervisor use UEFI boot, pass the `OVMF.fd` file path as an argum
|
|||||||
|
|
||||||
The same firmware can be used with Cloud Hypervisor or with QEMU. This is particularly useful if using QEMU for the preparation phase.
|
The same firmware can be used with Cloud Hypervisor or with QEMU. This is particularly useful if using QEMU for the preparation phase.
|
||||||
|
|
||||||
|
## Building UEFI Firmware with Compatibility Support Module (CSM)
|
||||||
|
|
||||||
|
CSM is a module that allows to boot legacy operating systems using the OVMF firmware. OVMF can embed a CSM build of SeaBIOS. To build the SeaBIOS with CSM support, add `CONFIG_CSM=y` to `.config` before the build. The outcome `out/Csm16.bin` is to be moved into `OvmfPkg/Csm/Csm16/Csm16.bin` before OVMF is built. Then, the OVMF build will have to be passed the `-D CSM_ENABLE` option in order to generate a legacy aware UEFI firmware. At the current stage, all the necessary patches are included in the Cloud Hypervisor specific [SeaBIOS branch](https://github.com/cloud-hypervisor/seabios/tree/ch). Taking into account the previous instructions, the modified command sequence to compile an OVMF binary with CSM support is the following one:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install uuid-dev nasm iasl build-essential python3-distutils git
|
||||||
|
|
||||||
|
git checkout https://github.com/cloud-hypervisor/seabios -b ch
|
||||||
|
cd seabios
|
||||||
|
make menuconfig
|
||||||
|
# Enable `CONFIG_CSM` and `CONFIG_QEMU_HARDWARE`
|
||||||
|
make CONFIG_CSM=y CONFIG_QEMU_HARDWARE=y
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
git clone https://github.com/cloud-hypervisor/edk2 -b ch
|
||||||
|
cd edk2
|
||||||
|
. edksetup.sh
|
||||||
|
git submodule update --init
|
||||||
|
cp ../seabios/out/Csm16.bin OvmfPkg/Csm/Csm16/
|
||||||
|
|
||||||
|
echo "ACTIVE_PLATFORM=OvmfPkg/OvmfCh.dsc" >> Conf/target.txt
|
||||||
|
echo "TARGET_ARCH=X64" >> Conf/target.txt
|
||||||
|
echo "TOOL_CHAIN_TAG=GCC5" >> Conf/target.txt
|
||||||
|
|
||||||
|
make -C ./BaseTools
|
||||||
|
build
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note, that the CSM support has currently only been tested with Linux guests. There are no plans to provide legacy support for other OSes (e.g. Windows).
|
||||||
|
|
||||||
# Links
|
# Links
|
||||||
|
|
||||||
- [OVMF wiki](https://github.com/tianocore/tianocore.github.io/wiki/OVMF)
|
- [OVMF wiki](https://github.com/tianocore/tianocore.github.io/wiki/OVMF)
|
||||||
- [Cloud Hypervisor specific tree](https://github.com/cloud-hypervisor/edk2/tree/ch)
|
- [Cloud Hypervisor specific tree](https://github.com/cloud-hypervisor/edk2/tree/ch)
|
||||||
|
- [Redhat OVMF Status Report](https://access.redhat.com/sites/default/files/attachments/ovmf-whtepaper-031815.pdf)
|
||||||
|
- [SeaBIOS Build Overview](https://www.seabios.org/Build_overview#Build_as_a_UEFI_Compatibility_Support_Module_.28CSM.29)
|
||||||
|
|||||||
@@ -282,6 +282,17 @@ Note, this VM has the networking enabled. It is needed, because symbols and sour
|
|||||||
|
|
||||||
Also, notice the `-serial` parameter - that's what does the magic on exposing the serial port to the guest while connecting the debugger VM with a client VM through the network. SAC/EMS needs to be disabled in the debugger VM, as otherwise the COM device might be blocked.
|
Also, notice the `-serial` parameter - that's what does the magic on exposing the serial port to the guest while connecting the debugger VM with a client VM through the network. SAC/EMS needs to be disabled in the debugger VM, as otherwise the COM device might be blocked.
|
||||||
|
|
||||||
|
Hereafter, WinDbg can be started using a command below:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
set _NT_DEBUG_PORT=com1
|
||||||
|
set _NT_DEBUG_BAUD_RATE=115200
|
||||||
|
|
||||||
|
windbg -v -d -k
|
||||||
|
```
|
||||||
|
|
||||||
|
Once started, WinDbg will wait for an incoming connection which is going to be initialized by the debuggee VM started in the next section.
|
||||||
|
|
||||||
#### Invoke the Debuggee VM
|
#### Invoke the Debuggee VM
|
||||||
|
|
||||||
##### Under QEMU
|
##### Under QEMU
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor-fuzz"
|
name = "cloud-hypervisor-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@@ -10,7 +9,6 @@ edition = "2018"
|
|||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "master" }
|
|
||||||
libc = "0.2.72"
|
libc = "0.2.72"
|
||||||
libfuzzer-sys = "0.3"
|
libfuzzer-sys = "0.3"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
@@ -18,18 +16,11 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v
|
|||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vm-memory = "0.3.0"
|
vm-memory = "0.4.0"
|
||||||
|
|
||||||
[dependencies.cloud-hypervisor]
|
[dependencies.cloud-hypervisor]
|
||||||
path = ".."
|
path = ".."
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
vm-memory = { git = "https://github.com/cloud-hypervisor/vm-memory", branch = "ch" }
|
|
||||||
[patch.'https://github.com/rust-vmm/kvm-ioctls']
|
|
||||||
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
|
|
||||||
[patch.'https://github.com/rust-vmm/kvm-bindings']
|
|
||||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
|
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["."]
|
members = ["."]
|
||||||
|
|||||||
@@ -7,21 +7,34 @@ license = "Apache-2.0 OR BSD-3-Clause"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
kvm = []
|
kvm = []
|
||||||
|
mshv = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
arc-swap = ">=0.4.4"
|
epoll = ">=4.0.1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "master" }
|
kvm-ioctls = { git = "https://github.com/cloud-hypervisor/kvm-ioctls", branch = "ch" }
|
||||||
kvm-bindings = { git = "https://github.com/rust-vmm/kvm-bindings", branch = "master", features = ["with-serde", "fam-wrappers"] }
|
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch", features = ["with-serde", "fam-wrappers"] }
|
||||||
|
|
||||||
|
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" }
|
||||||
|
|
||||||
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"
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.4.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"] }
|
||||||
|
|
||||||
[dependencies.linux-loader]
|
[dependencies.linux-loader]
|
||||||
git = "https://github.com/rust-vmm/linux-loader"
|
git = "https://github.com/rust-vmm/linux-loader"
|
||||||
features = ["elf", "bzimage"]
|
features = ["elf", "bzimage"]
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||||
|
version = "1.9.1"
|
||||||
|
default-features = false
|
||||||
|
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.8.2"
|
||||||
|
|||||||
148
hypervisor/src/arch/emulator/mod.rs
Normal file
148
hypervisor/src/arch/emulator/mod.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate thiserror;
|
||||||
|
|
||||||
|
use core::fmt::Debug;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Error, Debug)]
|
||||||
|
pub struct Exception<T: Debug> {
|
||||||
|
vector: T,
|
||||||
|
ip: u64,
|
||||||
|
error: Option<u32>,
|
||||||
|
payload: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Display for Exception<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Exception {:?} at IP {:#x}", self.vector, self.ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PlatformError {
|
||||||
|
#[error("Invalid address: {0}")]
|
||||||
|
InvalidAddress(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Invalid register: {0}")]
|
||||||
|
InvalidRegister(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Invalid state: {0}")]
|
||||||
|
InvalidState(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Memory read failure: {0}")]
|
||||||
|
MemoryReadFailure(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Memory write failure: {0}")]
|
||||||
|
MemoryWriteFailure(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Get CPU state failure: {0}")]
|
||||||
|
GetCpuStateFailure(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Set CPU state failure: {0}")]
|
||||||
|
SetCpuStateFailure(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Unmapped virtual address: {0}")]
|
||||||
|
UnmappedGVA(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Unsupported CPU Mode: {0}")]
|
||||||
|
UnsupportedCpuMode(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Invalid instruction operand: {0}")]
|
||||||
|
InvalidOperand(#[source] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum EmulationError<T: Debug> {
|
||||||
|
#[error("Unsupported instruction: {0}")]
|
||||||
|
UnsupportedInstruction(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Unsupported memory size: {0}")]
|
||||||
|
UnsupportedMemorySize(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Invalid operand: {0}")]
|
||||||
|
InvalidOperand(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Wrong number of operands: {0}")]
|
||||||
|
WrongNumberOperands(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Instruction Exception: {0}")]
|
||||||
|
InstructionException(Exception<T>),
|
||||||
|
|
||||||
|
#[error("Instruction fetching error: {0}")]
|
||||||
|
InstructionFetchingError(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Platform emulation error: {0}")]
|
||||||
|
PlatformEmulationError(PlatformError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
EmulationError(#[from] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The PlatformEmulator trait emulates a guest platform.
|
||||||
|
/// It's mostly a guest resources (memory and CPU state) getter and setter.
|
||||||
|
///
|
||||||
|
/// A CpuState is an architecture specific type, representing a CPU state.
|
||||||
|
/// The emulator and its instruction handlers modify a given CPU state and
|
||||||
|
/// eventually ask the platform to commit it back through `set_cpu_state`.
|
||||||
|
pub trait PlatformEmulator: Send + Sync {
|
||||||
|
type CpuState: Clone;
|
||||||
|
|
||||||
|
/// Read guest memory into a u8 slice.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `gva` - Guest virtual address to read from.
|
||||||
|
/// * `data` - Data slice to read into.
|
||||||
|
///
|
||||||
|
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError>;
|
||||||
|
|
||||||
|
/// Write a u8 slice into guest memory.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `gva` - Guest virtual address to write into.
|
||||||
|
/// * `data` - Data slice to be written.
|
||||||
|
///
|
||||||
|
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError>;
|
||||||
|
|
||||||
|
/// Get a CPU state from the guest.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `cpu_id` - Logical CPU ID.
|
||||||
|
///
|
||||||
|
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError>;
|
||||||
|
|
||||||
|
/// Set a guest CPU state.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `cpu_id` - Logical CPU ID.
|
||||||
|
/// * `state` - State to set the CPU into.
|
||||||
|
///
|
||||||
|
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError>;
|
||||||
|
|
||||||
|
/// Translate a guest virtual address into a physical one
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `gva` - Guest virtual address to translate.
|
||||||
|
///
|
||||||
|
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError>;
|
||||||
|
|
||||||
|
/// Fetch instruction bytes from memory.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ip` - Instruction pointer virtual address to start fetching instructions from.
|
||||||
|
///
|
||||||
|
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type EmulationResult<S, E> = std::result::Result<S, EmulationError<E>>;
|
||||||
@@ -11,5 +11,7 @@
|
|||||||
// Copyright © 2020, Microsoft Corporation
|
// Copyright © 2020, Microsoft Corporation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
pub mod emulator;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86;
|
pub mod x86;
|
||||||
|
|||||||
355
hypervisor/src/arch/x86/emulator/instructions/cmp.rs
Normal file
355
hypervisor/src/arch/x86/emulator/instructions/cmp.rs
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Microsoft
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
//
|
||||||
|
// CMP-Compare Two Operands
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate iced_x86;
|
||||||
|
|
||||||
|
use crate::arch::emulator::{EmulationError, PlatformEmulator};
|
||||||
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
|
use crate::arch::x86::regs::*;
|
||||||
|
use crate::arch::x86::Exception;
|
||||||
|
|
||||||
|
// CMP affects OF, SF, ZF, AF, PF and CF
|
||||||
|
const FLAGS_MASK: u64 = CF | PF | AF | ZF | SF | OF;
|
||||||
|
|
||||||
|
// TODO: Switch to inline asm when that's stable. Executing CMP (or any arthimetic instructions)
|
||||||
|
// natively and extracting RFLAGS will be much faster and make the code simpler.
|
||||||
|
fn calc_rflags_cpazso(op0: u64, op1: u64, op_size: usize) -> u64 {
|
||||||
|
let op_bits = op_size * 8;
|
||||||
|
let msb_shift = op_bits - 1;
|
||||||
|
// CMP is the same as SUB.
|
||||||
|
let result = op0.wrapping_sub(op1);
|
||||||
|
|
||||||
|
// Carry-out vector for SUB.
|
||||||
|
let cout = (!op0 & op1) | ((!op0 ^ op1) & result);
|
||||||
|
|
||||||
|
let cf = ((cout >> msb_shift) & 0x1) << CF_SHIFT;
|
||||||
|
|
||||||
|
// PF only needs the least significant byte. XOR its higher 4 bits with its lower 4 bits then
|
||||||
|
// use the value directly.
|
||||||
|
let pf = ((0x9669 >> ((result ^ (result >> 4)) & 0xf)) & 0x1) << PF_SHIFT;
|
||||||
|
|
||||||
|
// AF cares about the lowest 4 bits (nibble). msb_shift is 3 in this case.
|
||||||
|
let af = ((cout >> 3) & 0x1) << AF_SHIFT;
|
||||||
|
|
||||||
|
let zf = if result & (!0u64 >> (63 - msb_shift)) == 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} << ZF_SHIFT;
|
||||||
|
|
||||||
|
let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT;
|
||||||
|
|
||||||
|
// Overflow happens when two operands have the same sign but the result has a different sign.
|
||||||
|
let of = ((((op0 ^ op1) & (op0 ^ result)) >> msb_shift) & 0x1) << OF_SHIFT;
|
||||||
|
|
||||||
|
cf | pf | af | zf | sf | of
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cmp_rm_r {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>());
|
||||||
|
|
||||||
|
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cmp_r_rm {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>());
|
||||||
|
|
||||||
|
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cmp_rm_imm {
|
||||||
|
($imm:ty, $bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
let op1_value = get_op(&insn, 1, std::mem::size_of::<$imm>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>());
|
||||||
|
|
||||||
|
state.set_flags((state.flags() & !FLAGS_MASK) | cpazso);
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm64_r64;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_r64 {
|
||||||
|
cmp_rm_r!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm32_r32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_r32 {
|
||||||
|
cmp_rm_r!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm16_r16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_r16 {
|
||||||
|
cmp_rm_r!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm8_r8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_r8 {
|
||||||
|
cmp_rm_r!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_r64_rm64;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r64_rm64 {
|
||||||
|
cmp_r_rm!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_r32_rm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r32_rm32 {
|
||||||
|
cmp_r_rm!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_r16_rm16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r16_rm16 {
|
||||||
|
cmp_r_rm!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_r8_rm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r8_rm8 {
|
||||||
|
cmp_r_rm!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_AL_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AL_imm8 {
|
||||||
|
cmp_rm_imm!(u8, u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_AX_imm16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AX_imm16 {
|
||||||
|
cmp_rm_imm!(u16, u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_EAX_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_EAX_imm32 {
|
||||||
|
cmp_rm_imm!(u32, u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_RAX_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_RAX_imm32 {
|
||||||
|
cmp_rm_imm!(u32, u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm8_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_imm8 {
|
||||||
|
cmp_rm_imm!(u8, u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm16_imm16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm16 {
|
||||||
|
cmp_rm_imm!(u16, u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm32_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm32 {
|
||||||
|
cmp_rm_imm!(u32, u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm64_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm32 {
|
||||||
|
cmp_rm_imm!(u32, u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm16_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm8 {
|
||||||
|
cmp_rm_imm!(u8, u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm32_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm8 {
|
||||||
|
cmp_rm_imm!(u8, u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cmp_rm64_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm8 {
|
||||||
|
cmp_rm_imm!(u8, u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// cmp ah,al
|
||||||
|
fn test_cmp_rm8_r8_1() -> MockResult {
|
||||||
|
let rax: u64 = 0x0;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x38, 0xc4]; // cmp ah,al
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
|
assert_eq!(0b1000100, rflags);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// cmp eax,100
|
||||||
|
fn test_cmp_rm32_imm8_1() -> MockResult {
|
||||||
|
let rax: u64 = 0xabcdef;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x83, 0xf8, 0x64]; // cmp eax,100
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
|
assert_eq!(0b100, rflags);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// cmp eax,-1
|
||||||
|
fn test_cmp_rm32_imm8_2() -> MockResult {
|
||||||
|
let rax: u64 = 0xabcdef;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x83, 0xf8, 0xff]; // cmp eax,-1
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
|
assert_eq!(0b101, rflags);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// cmp rax,rbx
|
||||||
|
fn test_cmp_rm64_r64() -> MockResult {
|
||||||
|
let rax: u64 = 0xabcdef;
|
||||||
|
let rbx: u64 = 0x1234;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
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);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK;
|
||||||
|
assert_eq!(0b100, rflags);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmp_64() -> MockResult {
|
||||||
|
let data = [
|
||||||
|
(0xabcdef, 0x1234, 0b100),
|
||||||
|
(0x0, 0x101, 0b1001_0101),
|
||||||
|
(0x0, 0x8000_0000_0000_0000, 0b1000_1000_0101),
|
||||||
|
(0x1234abcd, 0x1234abcd, 0b100_0100),
|
||||||
|
(0x1234abcd, 0xdeadbeef, 0b1001_0101),
|
||||||
|
(0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b1000_0000),
|
||||||
|
(0xffff_ffff_ffff_ffff, 0x0, 0b1000_0100),
|
||||||
|
];
|
||||||
|
|
||||||
|
for d in data.iter() {
|
||||||
|
let rax = d.0;
|
||||||
|
let rbx = d.1;
|
||||||
|
let insn = [0x48, 0x39, 0xd8]; // cmp rax,rbx
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
0x1000,
|
||||||
|
vec![(Register::RAX, rax), (Register::RBX, rbx)],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK;
|
||||||
|
assert_eq!(d.2, rflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cmp_32() -> MockResult {
|
||||||
|
let data = [
|
||||||
|
(0xabcdef, 0x1234, 0b100),
|
||||||
|
(0x0, 0x101, 0b1001_0101),
|
||||||
|
(0x0, 0x8000_0000_0000_0000, 0b100_0100), // Same as cmp 0,0 due to truncation
|
||||||
|
(0x1234abcd, 0x1234abcd, 0b100_0100),
|
||||||
|
(0x1234abcd, 0xdeadbeef, 0b1_0101),
|
||||||
|
(0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b0), // Same as cmp 0xffffffff,0xdeadbeef
|
||||||
|
(0xffff_ffff, 0x0, 0b1000_0100),
|
||||||
|
];
|
||||||
|
|
||||||
|
for d in data.iter() {
|
||||||
|
let rax = d.0;
|
||||||
|
let rbx = d.1;
|
||||||
|
let insn = [0x39, 0xd8]; // cmp eax,ebx
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
0x1000,
|
||||||
|
vec![(Register::RAX, rax), (Register::RBX, rbx)],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK;
|
||||||
|
assert_eq!(d.2, rflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
146
hypervisor/src/arch/x86/emulator/instructions/mod.rs
Normal file
146
hypervisor/src/arch/x86/emulator/instructions/mod.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate iced_x86;
|
||||||
|
|
||||||
|
use crate::arch::emulator::{EmulationError, PlatformEmulator, PlatformError};
|
||||||
|
use crate::arch::x86::emulator::CpuStateManager;
|
||||||
|
use crate::arch::x86::Exception;
|
||||||
|
use iced_x86::*;
|
||||||
|
|
||||||
|
pub mod cmp;
|
||||||
|
pub mod mov;
|
||||||
|
|
||||||
|
fn get_op<T: CpuStateManager>(
|
||||||
|
insn: &Instruction,
|
||||||
|
op_index: u32,
|
||||||
|
op_size: usize,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<u64, PlatformError> {
|
||||||
|
if insn.op_count() < op_index + 1 {
|
||||||
|
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||||
|
"Invalid operand {:?}",
|
||||||
|
op_index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
match op_size {
|
||||||
|
1 | 2 | 4 | 8 => {}
|
||||||
|
_ => {
|
||||||
|
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||||
|
"Invalid operand size {:?}",
|
||||||
|
op_size
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = match insn.op_kind(op_index) {
|
||||||
|
OpKind::Register => state.read_reg(insn.op_register(op_index))?,
|
||||||
|
OpKind::Memory => {
|
||||||
|
let addr = memory_operand_address(insn, state, false)?;
|
||||||
|
let mut memory: [u8; 8] = [0; 8];
|
||||||
|
platform.read_memory(addr, &mut memory[0..op_size])?;
|
||||||
|
<u64>::from_le_bytes(memory)
|
||||||
|
}
|
||||||
|
OpKind::Immediate8 => insn.immediate8() as u64,
|
||||||
|
OpKind::Immediate8to16 => insn.immediate8to16() as u64,
|
||||||
|
OpKind::Immediate8to32 => insn.immediate8to32() as u64,
|
||||||
|
OpKind::Immediate8to64 => insn.immediate8to64() as u64,
|
||||||
|
OpKind::Immediate16 => insn.immediate16() as u64,
|
||||||
|
OpKind::Immediate32 => insn.immediate32() as u64,
|
||||||
|
OpKind::Immediate32to64 => insn.immediate32to64() as u64,
|
||||||
|
OpKind::Immediate64 => insn.immediate64() as u64,
|
||||||
|
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_op<T: CpuStateManager>(
|
||||||
|
insn: &Instruction,
|
||||||
|
op_index: u32,
|
||||||
|
op_size: usize,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
value: u64,
|
||||||
|
) -> Result<(), PlatformError> {
|
||||||
|
if insn.op_count() < op_index + 1 {
|
||||||
|
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||||
|
"Invalid operand {:?}",
|
||||||
|
op_index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
match op_size {
|
||||||
|
1 | 2 | 4 | 8 => {}
|
||||||
|
_ => {
|
||||||
|
return Err(PlatformError::InvalidOperand(anyhow!(
|
||||||
|
"Invalid operand size {:?}",
|
||||||
|
op_size
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match insn.op_kind(op_index) {
|
||||||
|
OpKind::Register => state.write_reg(insn.op_register(op_index), value)?,
|
||||||
|
OpKind::Memory => {
|
||||||
|
let addr = memory_operand_address(insn, state, true)?;
|
||||||
|
platform.write_memory(addr, &value.to_le_bytes()[..op_size])?;
|
||||||
|
}
|
||||||
|
k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the linear a.k.a. virtual address for a memory operand.
|
||||||
|
fn memory_operand_address<T: CpuStateManager>(
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &T,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<u64, PlatformError> {
|
||||||
|
let mut address: u64 = 0;
|
||||||
|
|
||||||
|
if insn.memory_base() != iced_x86::Register::None {
|
||||||
|
let base: u64 = state.read_reg(insn.memory_base())?;
|
||||||
|
address += base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if insn.memory_index() != iced_x86::Register::None {
|
||||||
|
let mut index: u64 = state.read_reg(insn.memory_index())?;
|
||||||
|
index *= insn.memory_index_scale() as u64;
|
||||||
|
|
||||||
|
address += index;
|
||||||
|
}
|
||||||
|
|
||||||
|
address += insn.memory_displacement() as u64;
|
||||||
|
|
||||||
|
// Translate to a linear address.
|
||||||
|
state.linearize(insn.memory_segment(), address, write)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InstructionHandler<T: CpuStateManager> {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! insn_format {
|
||||||
|
($insn:ident) => {{
|
||||||
|
let mut output = String::new();
|
||||||
|
let mut formatter = FastFormatter::new();
|
||||||
|
formatter
|
||||||
|
.options_mut()
|
||||||
|
.set_space_after_operand_separator(true);
|
||||||
|
formatter.format(&$insn, &mut output);
|
||||||
|
|
||||||
|
output
|
||||||
|
}};
|
||||||
|
}
|
||||||
563
hypervisor/src/arch/x86/emulator/instructions/mov.rs
Normal file
563
hypervisor/src/arch/x86/emulator/instructions/mov.rs
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
//
|
||||||
|
// MOV-Move
|
||||||
|
// SDM Volume 1, Chapter 4.3
|
||||||
|
// Copies the second operand (source operand) to the first operand (destination operand).
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate iced_x86;
|
||||||
|
|
||||||
|
use crate::arch::emulator::{EmulationError, PlatformEmulator};
|
||||||
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
|
use crate::arch::x86::Exception;
|
||||||
|
|
||||||
|
macro_rules! mov_rm_r {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let src_reg_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
set_op(
|
||||||
|
&insn,
|
||||||
|
0,
|
||||||
|
std::mem::size_of::<$bound>(),
|
||||||
|
state,
|
||||||
|
platform,
|
||||||
|
src_reg_value,
|
||||||
|
)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! mov_rm_imm {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
set_op(
|
||||||
|
&insn,
|
||||||
|
0,
|
||||||
|
std::mem::size_of::<$bound>(),
|
||||||
|
state,
|
||||||
|
platform,
|
||||||
|
imm,
|
||||||
|
)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! mov_r_rm {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let src_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
set_op(
|
||||||
|
&insn,
|
||||||
|
0,
|
||||||
|
std::mem::size_of::<$bound>(),
|
||||||
|
state,
|
||||||
|
platform,
|
||||||
|
src_value,
|
||||||
|
)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! mov_r_imm {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
set_op(
|
||||||
|
&insn,
|
||||||
|
0,
|
||||||
|
std::mem::size_of::<$bound>(),
|
||||||
|
state,
|
||||||
|
platform,
|
||||||
|
imm,
|
||||||
|
)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
state.set_ip(insn.ip());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r8_rm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_rm8 {
|
||||||
|
mov_r_rm!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r8_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_imm8 {
|
||||||
|
mov_r_imm!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r16_rm16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_rm16 {
|
||||||
|
mov_r_rm!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r16_imm16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_imm16 {
|
||||||
|
mov_r_imm!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r32_rm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_rm32 {
|
||||||
|
mov_r_rm!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r32_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_imm32 {
|
||||||
|
mov_r_imm!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r64_rm64;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_rm64 {
|
||||||
|
mov_r_rm!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_r64_imm64;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_imm64 {
|
||||||
|
mov_r_imm!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm8_imm8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_imm8 {
|
||||||
|
mov_rm_imm!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm8_r8;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_r8 {
|
||||||
|
mov_rm_r!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm16_imm16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_imm16 {
|
||||||
|
mov_rm_imm!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm16_r16;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_r16 {
|
||||||
|
mov_rm_r!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm32_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_imm32 {
|
||||||
|
mov_rm_imm!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm32_r32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_r32 {
|
||||||
|
mov_rm_r!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm64_imm32;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_imm32 {
|
||||||
|
mov_rm_imm!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mov_rm64_r64;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_r64 {
|
||||||
|
mov_rm_r!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
use super::*;
|
||||||
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax,rbx
|
||||||
|
fn test_mov_r64_r64() -> MockResult {
|
||||||
|
let rbx: u64 = 0x8899aabbccddeeff;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x48, 0x89, 0xd8];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![(Register::RBX, rbx)], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, rbx);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax,0x1122334411223344
|
||||||
|
fn test_mov_r64_imm64() -> MockResult {
|
||||||
|
let imm64: u64 = 0x1122334411223344;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, imm64);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax, [rax+rax]
|
||||||
|
fn test_mov_r64_m64() -> MockResult {
|
||||||
|
let target_rax: u64 = 0x1234567812345678;
|
||||||
|
let mut rax: u64 = 0x100;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory: [u8; 8] = target_rax.to_le_bytes();
|
||||||
|
let insn = [0x48, 0x8b, 0x04, 0x00];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![(Register::RAX, rax)], Some((rax + rax, &memory)));
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
rax = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, target_rax);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov al,0x11
|
||||||
|
fn test_mov_r8_imm8() -> MockResult {
|
||||||
|
let imm8: u8 = 0x11;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0xb0, 0x11];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let al = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::AL)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(al as u8, imm8);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov eax,0x11
|
||||||
|
fn test_mov_r32_imm8() -> MockResult {
|
||||||
|
let imm8: u8 = 0x11;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let eax = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::EAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(eax as u8, imm8);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax,0x11223344
|
||||||
|
fn test_mov_r64_imm32() -> MockResult {
|
||||||
|
let imm32: u32 = 0x11223344;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], None);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, imm32 as u64);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov byte ptr [rax],dh
|
||||||
|
fn test_mov_m8_r8() -> MockResult {
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let dh: u8 = 0x99;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x88, 0x30];
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
ip,
|
||||||
|
vec![(Register::RAX, rax), (Register::DH, dh.into())],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let mut memory: [u8; 1] = [0; 1];
|
||||||
|
vmm.read_memory(rax, &mut memory).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(u8::from_le_bytes(memory), dh);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov dword ptr [rax],esi
|
||||||
|
fn test_mov_m32_r32() -> MockResult {
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let esi: u32 = 0x8899;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x89, 0x30];
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
ip,
|
||||||
|
vec![(Register::RAX, rax), (Register::ESI, esi.into())],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let mut memory: [u8; 4] = [0; 4];
|
||||||
|
vmm.read_memory(rax, &mut memory).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(u32::from_le_bytes(memory), esi);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov dword ptr [rax+0x00000001],edi
|
||||||
|
fn test_mov_m32imm32_r32() -> MockResult {
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let displacement: u64 = 0x1;
|
||||||
|
let edi: u32 = 0x8899;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
ip,
|
||||||
|
vec![(Register::RAX, rax), (Register::EDI, edi.into())],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let mut memory: [u8; 4] = [0; 4];
|
||||||
|
vmm.read_memory(rax + displacement, &mut memory).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(u32::from_le_bytes(memory), edi);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov eax,dword ptr [rax+10h]
|
||||||
|
fn test_mov_r32_m32imm32() -> MockResult {
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let displacement: u64 = 0x10;
|
||||||
|
let eax: u32 = 0xaabbccdd;
|
||||||
|
let memory: [u8; 4] = eax.to_le_bytes();
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x8b, 0x40, 0x10];
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
ip,
|
||||||
|
vec![(Register::RAX, rax)],
|
||||||
|
Some((rax + displacement, &memory)),
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let new_eax = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::EAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(new_eax, eax as u64);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov al,byte ptr [rax+10h]
|
||||||
|
fn test_mov_r8_m32imm32() -> MockResult {
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let displacement: u64 = 0x10;
|
||||||
|
let al: u8 = 0xaa;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let insn = [0x8a, 0x40, 0x10];
|
||||||
|
let memory: [u8; 1] = al.to_le_bytes();
|
||||||
|
let mut vmm = MockVMM::new(
|
||||||
|
ip,
|
||||||
|
vec![(Register::RAX, rax)],
|
||||||
|
Some((rax + displacement, &memory)),
|
||||||
|
);
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
let new_al = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::AL)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(new_al, al as u64);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax, 0x100
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
fn test_mov_r64_imm64_and_r64_m64() -> MockResult {
|
||||||
|
let target_rax: u64 = 0x1234567812345678;
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let displacement: u64 = 0x10;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory: [u8; 8] = target_rax.to_le_bytes();
|
||||||
|
let insn = [
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
|
||||||
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
|
];
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], Some((rax + displacement, &memory)));
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
|
let rbx: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RBX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rbx, target_rax);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax, 0x100
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
fn test_mov_r64_imm64_and_r64_m64_first_insn() -> MockResult {
|
||||||
|
let target_rax: u64 = 0x1234567812345678;
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let displacement: u64 = 0x10;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory: [u8; 8] = target_rax.to_le_bytes();
|
||||||
|
let insn = [
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
|
||||||
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], Some((rax + displacement, &memory)));
|
||||||
|
// Only run the first instruction.
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
|
||||||
|
|
||||||
|
assert_eq!(ip + 7 as u64, vmm.cpu_state(cpu_id).unwrap().ip());
|
||||||
|
|
||||||
|
let new_rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, new_rax);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// mov rax, 0x100
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
// mov rax, 0x200
|
||||||
|
fn test_mov_r64_imm64_and_r64_m64_two_insns() -> MockResult {
|
||||||
|
let target_rax: u64 = 0x1234567812345678;
|
||||||
|
let rax: u64 = 0x100;
|
||||||
|
let displacement: u64 = 0x10;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory: [u8; 8] = target_rax.to_le_bytes();
|
||||||
|
let insn = [
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
|
||||||
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], Some((rax + displacement, &memory)));
|
||||||
|
// Run the 2 first instructions.
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
|
assert_eq!(ip + 7 + 4 as u64, vmm.cpu_state(cpu_id).unwrap().ip());
|
||||||
|
|
||||||
|
let rbx: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RBX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rbx, target_rax);
|
||||||
|
|
||||||
|
// Check that rax is still at 0x100
|
||||||
|
let new_rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, new_rax);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
859
hypervisor/src/arch/x86/emulator/mod.rs
Normal file
859
hypervisor/src/arch/x86/emulator/mod.rs
Normal file
@@ -0,0 +1,859 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate iced_x86;
|
||||||
|
|
||||||
|
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
||||||
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
|
use crate::arch::x86::regs::*;
|
||||||
|
use crate::arch::x86::*;
|
||||||
|
use crate::arch::x86::{Exception, SegmentRegisterOps};
|
||||||
|
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
||||||
|
use anyhow::Context;
|
||||||
|
use iced_x86::*;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod instructions;
|
||||||
|
|
||||||
|
/// x86 CPU modes
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CpuMode {
|
||||||
|
/// Real mode
|
||||||
|
Real,
|
||||||
|
|
||||||
|
/// Virtual 8086 mode
|
||||||
|
Virtual8086,
|
||||||
|
|
||||||
|
/// 16-bit protected mode
|
||||||
|
Protected16,
|
||||||
|
|
||||||
|
/// 32-bit protected mode
|
||||||
|
Protected,
|
||||||
|
|
||||||
|
/// 64-bit mode, a.k.a. long mode
|
||||||
|
Long,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CpuStateManager manages an x86 CPU state.
|
||||||
|
///
|
||||||
|
/// Instruction emulation handlers get a mutable reference to
|
||||||
|
/// a `CpuStateManager` implementation, representing the current state of the
|
||||||
|
/// CPU they have to emulate an instruction stream against. Usually those
|
||||||
|
/// handlers will modify the CPU state by modifying `CpuState` and it is up to
|
||||||
|
/// the handler caller to commit those changes back by invoking a
|
||||||
|
/// `PlatformEmulator` implementation `set_state()` method.
|
||||||
|
///
|
||||||
|
pub trait CpuStateManager: Clone {
|
||||||
|
/// Reads a CPU register.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `reg` - A general purpose, control or debug register.
|
||||||
|
fn read_reg(&self, reg: Register) -> Result<u64, PlatformError>;
|
||||||
|
|
||||||
|
/// Write to a CPU register.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `reg` - A general purpose, control or debug register.
|
||||||
|
/// * `val` - The value to load.
|
||||||
|
fn write_reg(&mut self, reg: Register, val: u64) -> Result<(), PlatformError>;
|
||||||
|
|
||||||
|
/// Reads a segment register.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `reg` - A segment register.
|
||||||
|
fn read_segment(&self, reg: Register) -> Result<SegmentRegister, PlatformError>;
|
||||||
|
|
||||||
|
/// Write to a segment register.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `reg` - A segment register.
|
||||||
|
/// * `segment_reg` - The segment register value to load.
|
||||||
|
fn write_segment(
|
||||||
|
&mut self,
|
||||||
|
reg: Register,
|
||||||
|
segment_reg: SegmentRegister,
|
||||||
|
) -> Result<(), PlatformError>;
|
||||||
|
|
||||||
|
/// Get the CPU instruction pointer.
|
||||||
|
fn ip(&self) -> u64;
|
||||||
|
|
||||||
|
/// Set the CPU instruction pointer.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `ip` - The CPU instruction pointer.
|
||||||
|
fn set_ip(&mut self, ip: u64);
|
||||||
|
|
||||||
|
/// Get the CPU Extended Feature Enable Register.
|
||||||
|
fn efer(&self) -> u64;
|
||||||
|
|
||||||
|
/// Set the CPU Extended Feature Enable Register.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `efer` - The CPU EFER value.
|
||||||
|
fn set_efer(&mut self, efer: u64);
|
||||||
|
|
||||||
|
/// Get the CPU flags.
|
||||||
|
fn flags(&self) -> u64;
|
||||||
|
|
||||||
|
/// Set the CPU flags.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `flags` - The CPU flags
|
||||||
|
fn set_flags(&mut self, flags: u64);
|
||||||
|
|
||||||
|
/// Get the CPU mode.
|
||||||
|
fn mode(&self) -> Result<CpuMode, PlatformError>;
|
||||||
|
|
||||||
|
/// Translate a logical (segmented) address into a linear (virtual) one.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `segment` - Which segment to use for linearization
|
||||||
|
/// * `logical_addr` - The logical address to be translated
|
||||||
|
fn linearize(
|
||||||
|
&self,
|
||||||
|
segment: Register,
|
||||||
|
logical_addr: u64,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<u64, PlatformError> {
|
||||||
|
let segment_register = self.read_segment(segment)?;
|
||||||
|
let mode = self.mode()?;
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
CpuMode::Long => {
|
||||||
|
// TODO Check that we got a canonical address.
|
||||||
|
Ok(logical_addr
|
||||||
|
.checked_add(segment_register.base)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
PlatformError::InvalidAddress(anyhow!(
|
||||||
|
"Logical address {:#x} can not be linearized with segment {:#x?}",
|
||||||
|
logical_addr,
|
||||||
|
segment_register
|
||||||
|
))
|
||||||
|
})?)
|
||||||
|
}
|
||||||
|
|
||||||
|
CpuMode::Protected | CpuMode::Real => {
|
||||||
|
let segment_type = segment_register.segment_type();
|
||||||
|
|
||||||
|
// Must not write to a read-only segment.
|
||||||
|
if segment_type_ro(segment_type) && write {
|
||||||
|
return Err(PlatformError::InvalidAddress(anyhow!(
|
||||||
|
"Can not write to a read-only segment"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let logical_addr = logical_addr & 0xffff_ffffu64;
|
||||||
|
let mut segment_limit: u32 = if segment_register.granularity() != 0 {
|
||||||
|
(segment_register.limit << 12) | 0xfff
|
||||||
|
} else {
|
||||||
|
segment_register.limit
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expand-down segment
|
||||||
|
if segment_type_expand_down(segment_type) {
|
||||||
|
if logical_addr >= segment_limit.into() {
|
||||||
|
return Err(PlatformError::InvalidAddress(anyhow!(
|
||||||
|
"{:#x} is off limits {:#x} (expand down)",
|
||||||
|
logical_addr,
|
||||||
|
segment_limit
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if segment_register.db() != 0 {
|
||||||
|
segment_limit = 0xffffffff
|
||||||
|
} else {
|
||||||
|
segment_limit = 0xffff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if logical_addr > segment_limit.into() {
|
||||||
|
return Err(PlatformError::InvalidAddress(anyhow!(
|
||||||
|
"{:#x} is off limits {:#x}",
|
||||||
|
logical_addr,
|
||||||
|
segment_limit
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(logical_addr + segment_register.base)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Err(PlatformError::UnsupportedCpuMode(anyhow!("{:?}", mode))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const REGISTER_MASK_64: u64 = 0xffff_ffff_ffff_ffffu64;
|
||||||
|
const REGISTER_MASK_32: u64 = 0xffff_ffffu64;
|
||||||
|
const REGISTER_MASK_16: u64 = 0xffffu64;
|
||||||
|
const REGISTER_MASK_8: u64 = 0xffu64;
|
||||||
|
|
||||||
|
macro_rules! set_reg {
|
||||||
|
($reg:expr, $mask:expr, $value:expr) => {
|
||||||
|
$reg = ($reg & $mask) | $value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
/// A minimal, emulated CPU state.
|
||||||
|
///
|
||||||
|
/// Hypervisors needing x86 emulation can choose to either use their own
|
||||||
|
/// CPU state structures and implement the CpuStateManager interface for it,
|
||||||
|
/// or use `EmulatorCpuState`. The latter implies creating a new state
|
||||||
|
/// `EmulatorCpuState` instance for each platform `cpu_state()` call, which
|
||||||
|
/// might be less efficient.
|
||||||
|
pub struct EmulatorCpuState {
|
||||||
|
pub regs: StandardRegisters,
|
||||||
|
pub sregs: SpecialRegisters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuStateManager for EmulatorCpuState {
|
||||||
|
fn read_reg(&self, reg: Register) -> Result<u64, PlatformError> {
|
||||||
|
let mut reg_value: u64 = match reg {
|
||||||
|
Register::RAX | Register::EAX | Register::AX | Register::AL | Register::AH => {
|
||||||
|
self.regs.rax
|
||||||
|
}
|
||||||
|
Register::RBX | Register::EBX | Register::BX | Register::BL | Register::BH => {
|
||||||
|
self.regs.rbx
|
||||||
|
}
|
||||||
|
Register::RCX | Register::ECX | Register::CX | Register::CL | Register::CH => {
|
||||||
|
self.regs.rcx
|
||||||
|
}
|
||||||
|
Register::RDX | Register::EDX | Register::DX | Register::DL | Register::DH => {
|
||||||
|
self.regs.rdx
|
||||||
|
}
|
||||||
|
Register::RSP | Register::ESP | Register::SP => self.regs.rsp,
|
||||||
|
Register::RBP | Register::EBP | Register::BP => self.regs.rbp,
|
||||||
|
Register::RSI | Register::ESI | Register::SI | Register::SIL => self.regs.rsi,
|
||||||
|
Register::RDI | Register::EDI | Register::DI | Register::DIL => self.regs.rdi,
|
||||||
|
Register::R8 | Register::R8D | Register::R8W | Register::R8L => self.regs.r8,
|
||||||
|
Register::R9 | Register::R9D | Register::R9W | Register::R9L => self.regs.r9,
|
||||||
|
Register::R10 | Register::R10D | Register::R10W | Register::R10L => self.regs.r10,
|
||||||
|
Register::R11 | Register::R11D | Register::R11W | Register::R11L => self.regs.r11,
|
||||||
|
Register::R12 | Register::R12D | Register::R12W | Register::R12L => self.regs.r12,
|
||||||
|
Register::R13 | Register::R13D | Register::R13W | Register::R13L => self.regs.r13,
|
||||||
|
Register::R14 | Register::R14D | Register::R14W | Register::R14L => self.regs.r14,
|
||||||
|
Register::R15 | Register::R15D | Register::R15W | Register::R15L => self.regs.r15,
|
||||||
|
Register::CR0 => self.sregs.cr0,
|
||||||
|
Register::CR2 => self.sregs.cr2,
|
||||||
|
Register::CR3 => self.sregs.cr3,
|
||||||
|
Register::CR4 => self.sregs.cr4,
|
||||||
|
Register::CR8 => self.sregs.cr8,
|
||||||
|
|
||||||
|
r => {
|
||||||
|
return Err(PlatformError::InvalidRegister(anyhow!(
|
||||||
|
"read_reg invalid GPR {:?}",
|
||||||
|
r
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reg_value = if reg.is_gpr64() || reg.is_cr() {
|
||||||
|
reg_value
|
||||||
|
} else if reg.is_gpr32() {
|
||||||
|
reg_value & REGISTER_MASK_32
|
||||||
|
} else if reg.is_gpr16() {
|
||||||
|
reg_value & REGISTER_MASK_16
|
||||||
|
} else if reg.is_gpr8() {
|
||||||
|
if reg == Register::AH
|
||||||
|
|| reg == Register::BH
|
||||||
|
|| reg == Register::CH
|
||||||
|
|| reg == Register::DH
|
||||||
|
{
|
||||||
|
(reg_value >> 8) & REGISTER_MASK_8
|
||||||
|
} else {
|
||||||
|
reg_value & REGISTER_MASK_8
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(PlatformError::InvalidRegister(anyhow!(
|
||||||
|
"read_reg invalid GPR {:?}",
|
||||||
|
reg
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Register read: {:#x} from {:?}", reg_value, reg);
|
||||||
|
|
||||||
|
Ok(reg_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_reg(&mut self, reg: Register, val: u64) -> Result<(), PlatformError> {
|
||||||
|
debug!("Register write: {:#x} to {:?}", val, reg);
|
||||||
|
|
||||||
|
// SDM Vol 1 - 3.4.1.1
|
||||||
|
//
|
||||||
|
// 8-bit and 16-bit operands generate an 8-bit or 16-bit result.
|
||||||
|
// The upper 56 bits or 48 bits (respectively) of the destination
|
||||||
|
// general-purpose register are not modified by the operation.
|
||||||
|
let (reg_value, mask): (u64, u64) = if reg.is_gpr64() || reg.is_cr() {
|
||||||
|
(val, !REGISTER_MASK_64)
|
||||||
|
} else if reg.is_gpr32() {
|
||||||
|
(val & REGISTER_MASK_32, !REGISTER_MASK_64)
|
||||||
|
} else if reg.is_gpr16() {
|
||||||
|
(val & REGISTER_MASK_16, !REGISTER_MASK_16)
|
||||||
|
} else if reg.is_gpr8() {
|
||||||
|
if reg == Register::AH
|
||||||
|
|| reg == Register::BH
|
||||||
|
|| reg == Register::CH
|
||||||
|
|| reg == Register::DH
|
||||||
|
{
|
||||||
|
((val & REGISTER_MASK_8) << 8, !(REGISTER_MASK_8 << 8))
|
||||||
|
} else {
|
||||||
|
(val & REGISTER_MASK_8, !REGISTER_MASK_8)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(PlatformError::InvalidRegister(anyhow!(
|
||||||
|
"write_reg invalid register {:?}",
|
||||||
|
reg
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
match reg {
|
||||||
|
Register::RAX | Register::EAX | Register::AX | Register::AL | Register::AH => {
|
||||||
|
set_reg!(self.regs.rax, mask, reg_value);
|
||||||
|
}
|
||||||
|
Register::RBX | Register::EBX | Register::BX | Register::BL | Register::BH => {
|
||||||
|
set_reg!(self.regs.rbx, mask, reg_value);
|
||||||
|
}
|
||||||
|
Register::RCX | Register::ECX | Register::CX | Register::CL | Register::CH => {
|
||||||
|
set_reg!(self.regs.rcx, mask, reg_value);
|
||||||
|
}
|
||||||
|
Register::RDX | Register::EDX | Register::DX | Register::DL | Register::DH => {
|
||||||
|
set_reg!(self.regs.rdx, mask, reg_value);
|
||||||
|
}
|
||||||
|
Register::RSP | Register::ESP | Register::SP => {
|
||||||
|
set_reg!(self.regs.rsp, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::RBP | Register::EBP | Register::BP => {
|
||||||
|
set_reg!(self.regs.rbp, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::RSI | Register::ESI | Register::SI | Register::SIL => {
|
||||||
|
set_reg!(self.regs.rsi, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::RDI | Register::EDI | Register::DI | Register::DIL => {
|
||||||
|
set_reg!(self.regs.rdi, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R8 | Register::R8D | Register::R8W | Register::R8L => {
|
||||||
|
set_reg!(self.regs.r8, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R9 | Register::R9D | Register::R9W | Register::R9L => {
|
||||||
|
set_reg!(self.regs.r9, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R10 | Register::R10D | Register::R10W | Register::R10L => {
|
||||||
|
set_reg!(self.regs.r10, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R11 | Register::R11D | Register::R11W | Register::R11L => {
|
||||||
|
set_reg!(self.regs.r11, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R12 | Register::R12D | Register::R12W | Register::R12L => {
|
||||||
|
set_reg!(self.regs.r12, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R13 | Register::R13D | Register::R13W | Register::R13L => {
|
||||||
|
set_reg!(self.regs.r13, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R14 | Register::R14D | Register::R14W | Register::R14L => {
|
||||||
|
set_reg!(self.regs.r14, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::R15 | Register::R15D | Register::R15W | Register::R15L => {
|
||||||
|
set_reg!(self.regs.r15, mask, reg_value)
|
||||||
|
}
|
||||||
|
Register::CR0 => set_reg!(self.sregs.cr0, mask, reg_value),
|
||||||
|
Register::CR2 => set_reg!(self.sregs.cr2, mask, reg_value),
|
||||||
|
Register::CR3 => set_reg!(self.sregs.cr3, mask, reg_value),
|
||||||
|
Register::CR4 => set_reg!(self.sregs.cr4, mask, reg_value),
|
||||||
|
Register::CR8 => set_reg!(self.sregs.cr8, mask, reg_value),
|
||||||
|
_ => {
|
||||||
|
return Err(PlatformError::InvalidRegister(anyhow!(
|
||||||
|
"write_reg invalid register {:?}",
|
||||||
|
reg
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_segment(&self, reg: Register) -> Result<SegmentRegister, PlatformError> {
|
||||||
|
if !reg.is_segment_register() {
|
||||||
|
return Err(PlatformError::InvalidRegister(anyhow!(
|
||||||
|
"read_segment {:?} is not a segment register",
|
||||||
|
reg
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
match reg {
|
||||||
|
Register::CS => Ok(self.sregs.cs),
|
||||||
|
Register::DS => Ok(self.sregs.ds),
|
||||||
|
Register::ES => Ok(self.sregs.es),
|
||||||
|
Register::FS => Ok(self.sregs.fs),
|
||||||
|
Register::GS => Ok(self.sregs.gs),
|
||||||
|
Register::SS => Ok(self.sregs.ss),
|
||||||
|
r => Err(PlatformError::InvalidRegister(anyhow!(
|
||||||
|
"read_segment invalid register {:?}",
|
||||||
|
r
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_segment(
|
||||||
|
&mut self,
|
||||||
|
reg: Register,
|
||||||
|
segment_register: SegmentRegister,
|
||||||
|
) -> Result<(), PlatformError> {
|
||||||
|
if !reg.is_segment_register() {
|
||||||
|
return Err(PlatformError::InvalidRegister(anyhow!("{:?}", reg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
match reg {
|
||||||
|
Register::CS => self.sregs.cs = segment_register,
|
||||||
|
Register::DS => self.sregs.ds = segment_register,
|
||||||
|
Register::ES => self.sregs.es = segment_register,
|
||||||
|
Register::FS => self.sregs.fs = segment_register,
|
||||||
|
Register::GS => self.sregs.gs = segment_register,
|
||||||
|
Register::SS => self.sregs.ss = segment_register,
|
||||||
|
r => return Err(PlatformError::InvalidRegister(anyhow!("{:?}", r))),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ip(&self) -> u64 {
|
||||||
|
self.regs.rip
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ip(&mut self, ip: u64) {
|
||||||
|
self.regs.rip = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn efer(&self) -> u64 {
|
||||||
|
self.sregs.efer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_efer(&mut self, efer: u64) {
|
||||||
|
self.sregs.efer = efer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flags(&self) -> u64 {
|
||||||
|
self.regs.rflags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flags(&mut self, flags: u64) {
|
||||||
|
self.regs.rflags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mode(&self) -> Result<CpuMode, PlatformError> {
|
||||||
|
let efer = self.efer();
|
||||||
|
let cr0 = self.read_reg(Register::CR0)?;
|
||||||
|
let mut mode = CpuMode::Real;
|
||||||
|
|
||||||
|
if (cr0 & CR0_PE) == CR0_PE {
|
||||||
|
mode = CpuMode::Protected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (efer & EFER_LMA) == EFER_LMA {
|
||||||
|
if mode != CpuMode::Protected {
|
||||||
|
return Err(PlatformError::InvalidState(anyhow!(
|
||||||
|
"Protection must be enabled in long mode"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = CpuMode::Long;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Emulator<'a, T: CpuStateManager> {
|
||||||
|
platform: &'a mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce repetition, see its invocation in get_handler().
|
||||||
|
macro_rules! gen_handler_match {
|
||||||
|
($value: ident, $( ($module:ident, $code:ident) ),* ) => {
|
||||||
|
match $value {
|
||||||
|
$(
|
||||||
|
Code::$code => Some(Box::new($module::$code)),
|
||||||
|
)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
||||||
|
pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<T> {
|
||||||
|
Emulator { platform }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_handler(code: Code) -> Option<Box<dyn InstructionHandler<T>>> {
|
||||||
|
let handler: Option<Box<dyn InstructionHandler<T>>> = gen_handler_match!(
|
||||||
|
code,
|
||||||
|
// CMP
|
||||||
|
(cmp, Cmp_rm32_r32),
|
||||||
|
(cmp, Cmp_rm8_r8),
|
||||||
|
(cmp, Cmp_rm32_imm8),
|
||||||
|
(cmp, Cmp_rm64_r64),
|
||||||
|
// MOV
|
||||||
|
(mov, Mov_r8_rm8),
|
||||||
|
(mov, Mov_r8_imm8),
|
||||||
|
(mov, Mov_r16_imm16),
|
||||||
|
(mov, Mov_r16_rm16),
|
||||||
|
(mov, Mov_r32_imm32),
|
||||||
|
(mov, Mov_r32_rm32),
|
||||||
|
(mov, Mov_r64_imm64),
|
||||||
|
(mov, Mov_r64_rm64),
|
||||||
|
(mov, Mov_rm8_imm8),
|
||||||
|
(mov, Mov_rm8_r8),
|
||||||
|
(mov, Mov_rm16_imm16),
|
||||||
|
(mov, Mov_rm16_r16),
|
||||||
|
(mov, Mov_rm32_imm32),
|
||||||
|
(mov, Mov_rm32_r32),
|
||||||
|
(mov, Mov_rm64_imm32),
|
||||||
|
(mov, Mov_rm64_r64)
|
||||||
|
);
|
||||||
|
|
||||||
|
handler
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emulate_insn_stream(
|
||||||
|
&mut self,
|
||||||
|
cpu_id: usize,
|
||||||
|
insn_stream: &[u8],
|
||||||
|
num_insn: Option<usize>,
|
||||||
|
) -> EmulationResult<T, Exception> {
|
||||||
|
let mut state = self
|
||||||
|
.platform
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
let mut decoder = Decoder::new(64, insn_stream, DecoderOptions::NONE);
|
||||||
|
let mut insn = Instruction::default();
|
||||||
|
let mut num_insn_emulated: usize = 0;
|
||||||
|
let mut fetched_insn_stream: [u8; 16] = [0; 16];
|
||||||
|
let mut last_decoded_ip: u64 = state.ip();
|
||||||
|
let mut stop_emulation: bool = false;
|
||||||
|
|
||||||
|
decoder.set_ip(state.ip());
|
||||||
|
|
||||||
|
while decoder.can_decode() && !stop_emulation {
|
||||||
|
decoder.decode_out(&mut insn);
|
||||||
|
|
||||||
|
if decoder.last_error() == DecoderError::NoMoreBytes {
|
||||||
|
// The decoder is missing some bytes to decode the current
|
||||||
|
// instruction, for example because the instruction stream
|
||||||
|
// crosses a page boundary.
|
||||||
|
// We fetch 16 more bytes from the instruction segment,
|
||||||
|
// decode and emulate the failing instruction and terminate
|
||||||
|
// the emulation loop.
|
||||||
|
debug!(
|
||||||
|
"Fetching {} bytes from {:#x}",
|
||||||
|
fetched_insn_stream.len(),
|
||||||
|
last_decoded_ip
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetched_insn_stream is 16 bytes long, enough to contain
|
||||||
|
// any complete x86 instruction.
|
||||||
|
self.platform
|
||||||
|
.fetch(last_decoded_ip, &mut fetched_insn_stream)
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
debug!("Fetched {:x?}", fetched_insn_stream);
|
||||||
|
|
||||||
|
// Once we have the new stream, we must create a new decoder
|
||||||
|
// and emulate one last instruction from the last decoded IP.
|
||||||
|
decoder = Decoder::new(64, &fetched_insn_stream, DecoderOptions::NONE);
|
||||||
|
decoder.decode_out(&mut insn);
|
||||||
|
if decoder.last_error() != DecoderError::None {
|
||||||
|
return Err(EmulationError::InstructionFetchingError(anyhow!(
|
||||||
|
"{:#x?}",
|
||||||
|
insn_format!(insn)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_emulation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulate the decoded instruction
|
||||||
|
Emulator::get_handler(insn.code())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
EmulationError::UnsupportedInstruction(anyhow!(
|
||||||
|
"{:#x?} {:?} {:?}",
|
||||||
|
insn_format!(insn),
|
||||||
|
insn.mnemonic(),
|
||||||
|
insn.code()
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.emulate(&insn, &mut state, self.platform)
|
||||||
|
.context(anyhow!("Failed to emulate {:#x?}", insn_format!(insn)))?;
|
||||||
|
|
||||||
|
last_decoded_ip = decoder.ip();
|
||||||
|
num_insn_emulated += 1;
|
||||||
|
|
||||||
|
if let Some(num_insn) = num_insn {
|
||||||
|
if num_insn_emulated >= num_insn {
|
||||||
|
// Exit the decoding loop, do not decode the next instruction.
|
||||||
|
stop_emulation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.set_ip(decoder.ip());
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emulate all instructions from the instructions stream.
|
||||||
|
pub fn emulate(&mut self, cpu_id: usize, insn_stream: &[u8]) -> EmulationResult<T, Exception> {
|
||||||
|
self.emulate_insn_stream(cpu_id, insn_stream, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only emulate the first instruction from the stream.
|
||||||
|
///
|
||||||
|
/// This is useful for cases where we get readahead instruction stream
|
||||||
|
/// but implicitly must only emulate the first instruction, and then return
|
||||||
|
/// to the guest.
|
||||||
|
pub fn emulate_first_insn(
|
||||||
|
&mut self,
|
||||||
|
cpu_id: usize,
|
||||||
|
insn_stream: &[u8],
|
||||||
|
) -> EmulationResult<T, Exception> {
|
||||||
|
self.emulate_insn_stream(cpu_id, insn_stream, Some(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod mock_vmm {
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
|
||||||
|
extern crate env_logger;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::arch::emulator::{EmulationError, PlatformEmulator};
|
||||||
|
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState as CpuState};
|
||||||
|
use crate::arch::x86::gdt::{gdt_entry, segment_from_gdt};
|
||||||
|
use crate::arch::x86::Exception;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MockVMM {
|
||||||
|
memory: Vec<u8>,
|
||||||
|
state: Arc<Mutex<CpuState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for MockVMM {}
|
||||||
|
|
||||||
|
pub type MockResult = Result<(), EmulationError<Exception>>;
|
||||||
|
|
||||||
|
impl MockVMM {
|
||||||
|
pub fn new(ip: u64, regs: Vec<(Register, u64)>, memory: Option<(u64, &[u8])>) -> MockVMM {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
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 mut initial_state = CpuState::default();
|
||||||
|
initial_state.set_ip(ip);
|
||||||
|
initial_state.write_segment(Register::CS, cs_reg).unwrap();
|
||||||
|
initial_state.write_segment(Register::DS, ds_reg).unwrap();
|
||||||
|
for (reg, value) in regs {
|
||||||
|
initial_state.write_reg(reg, value).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vmm = MockVMM {
|
||||||
|
memory: vec![0; 8192],
|
||||||
|
state: Arc::new(Mutex::new(initial_state)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mem) = memory {
|
||||||
|
vmm.write_memory(mem.0, &mem.1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
vmm
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emulate_insn(
|
||||||
|
&mut self,
|
||||||
|
cpu_id: usize,
|
||||||
|
insn: &[u8],
|
||||||
|
num_insn: Option<usize>,
|
||||||
|
) -> MockResult {
|
||||||
|
let ip = self.cpu_state(cpu_id).unwrap().ip();
|
||||||
|
let mut emulator = Emulator::new(self);
|
||||||
|
|
||||||
|
let new_state = emulator.emulate_insn_stream(cpu_id, &insn, num_insn)?;
|
||||||
|
if num_insn.is_none() {
|
||||||
|
assert_eq!(ip + insn.len() as u64, new_state.ip());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_cpu_state(cpu_id, new_state).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emulate_first_insn(&mut self, cpu_id: usize, insn: &[u8]) -> MockResult {
|
||||||
|
self.emulate_insn(cpu_id, insn, Some(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformEmulator for MockVMM {
|
||||||
|
type CpuState = CpuState;
|
||||||
|
|
||||||
|
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
debug!(
|
||||||
|
"Memory read {} bytes from [{:#x} -> {:#x}]",
|
||||||
|
data.len(),
|
||||||
|
gva,
|
||||||
|
gva
|
||||||
|
);
|
||||||
|
data.copy_from_slice(&self.memory[gva as usize..gva as usize + data.len()]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||||
|
debug!(
|
||||||
|
"Memory write {} bytes at [{:#x} -> {:#x}]",
|
||||||
|
data.len(),
|
||||||
|
gva,
|
||||||
|
gva
|
||||||
|
);
|
||||||
|
self.memory[gva as usize..gva as usize + data.len()].copy_from_slice(data);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_state(&self, _cpu_id: usize) -> Result<CpuState, PlatformError> {
|
||||||
|
Ok(self.state.lock().unwrap().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cpu_state(
|
||||||
|
&self,
|
||||||
|
_cpu_id: usize,
|
||||||
|
state: Self::CpuState,
|
||||||
|
) -> Result<(), PlatformError> {
|
||||||
|
*self.state.lock().unwrap() = state;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
|
||||||
|
Ok(gva)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
let rip = self
|
||||||
|
.state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.linearize(Register::CS, ip, false)?;
|
||||||
|
self.read_memory(rip, instruction_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
use super::*;
|
||||||
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Emulate truncated instruction stream, which should cause a fetch.
|
||||||
|
//
|
||||||
|
// mov rax, 0x1000
|
||||||
|
// Test with a first instruction truncated.
|
||||||
|
fn test_fetch_first_instruction() -> MockResult {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory = [
|
||||||
|
// Code at IP
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
|
||||||
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
|
// Padding
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, // Padding is all zeroes
|
||||||
|
// Data at IP + 0x10 (0x1234567812345678 in LE)
|
||||||
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
|
||||||
|
];
|
||||||
|
let insn = [
|
||||||
|
// First instruction is truncated
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
|
let rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, ip);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Emulate truncated instruction stream, which should cause a fetch.
|
||||||
|
//
|
||||||
|
// mov rax, 0x1000
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
// Test with a 2nd instruction truncated.
|
||||||
|
fn test_fetch_second_instruction() -> MockResult {
|
||||||
|
let target_rax: u64 = 0x1234567812345678;
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory = [
|
||||||
|
// Code at IP
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
|
||||||
|
0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
|
// Padding
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, // Padding is all zeroes
|
||||||
|
// Data at IP + 0x10 (0x1234567812345678 in LE)
|
||||||
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
|
||||||
|
];
|
||||||
|
let insn = [
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, 0x10, 0x00, 0x00, // mov rax, 0x1000
|
||||||
|
0x48, 0x8b, // Truncated mov rbx, qword ptr [rax+10h] -- missing [0x58, 0x10]
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
|
||||||
|
|
||||||
|
let rbx: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RBX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rbx, target_rax);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Emulate truncated instruction stream, which should cause a fetch.
|
||||||
|
//
|
||||||
|
// mov rax, 0x1000
|
||||||
|
// Test with a first instruction truncated and a bad fetched instruction.
|
||||||
|
// Verify that the instruction emulation returns an error.
|
||||||
|
fn test_fetch_bad_insn() -> MockResult {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let cpu_id = 0;
|
||||||
|
let memory = [
|
||||||
|
// Code at IP
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
];
|
||||||
|
let insn = [
|
||||||
|
// First instruction is truncated
|
||||||
|
0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVMM::new(ip, vec![], Some((ip, &memory)));
|
||||||
|
assert!(vmm.emulate_first_insn(cpu_id, &insn).is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
// For GDT details see arch/x86/include/asm/segment.h
|
// For GDT details see arch/x86/include/asm/segment.h
|
||||||
use hypervisor::x86_64::SegmentRegister;
|
use crate::x86_64::SegmentRegister;
|
||||||
|
|
||||||
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
|
||||||
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
// Copyright © 2020, Microsoft Corporation
|
// Copyright © 2020, Microsoft Corporation
|
||||||
//
|
//
|
||||||
|
|
||||||
|
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)]
|
||||||
@@ -28,9 +30,95 @@
|
|||||||
)]
|
)]
|
||||||
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;
|
||||||
|
|
||||||
// IOAPIC pins
|
// IOAPIC pins
|
||||||
pub const NUM_IOAPIC_PINS: usize = 24;
|
pub const NUM_IOAPIC_PINS: usize = 24;
|
||||||
|
|
||||||
|
// X86 Exceptions
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Exception {
|
||||||
|
DE = 1, // Divide Error
|
||||||
|
DB = 2, // Debug Exception
|
||||||
|
BP = 3, // Breakpoint
|
||||||
|
OF = 4, // Overflow
|
||||||
|
BR = 5, // BOUND Range Exceeded
|
||||||
|
UD = 6, // Invalid/Undefined Opcode
|
||||||
|
NM = 7, // No Math Coprocessor
|
||||||
|
DF = 8, // Double Fault
|
||||||
|
TS = 10, // Invalid TSS
|
||||||
|
NP = 11, // Segment Not Present
|
||||||
|
SS = 12, // Stack Segment Fault
|
||||||
|
GP = 13, // General Protection
|
||||||
|
PF = 14, // Page Fault
|
||||||
|
MF = 16, // Math Fault
|
||||||
|
AC = 17, // Alignment Check
|
||||||
|
MC = 18, // Machine Check
|
||||||
|
XM = 19, // SIMD Floating-Point Exception
|
||||||
|
VE = 20, // Virtualization Exception
|
||||||
|
CP = 21, // Control Protection Exception
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod regs;
|
||||||
|
|
||||||
|
// Abstracted segment register ops.
|
||||||
|
// Each x86 hypervisor should implement those.
|
||||||
|
pub trait SegmentRegisterOps {
|
||||||
|
// Segment type
|
||||||
|
fn segment_type(&self) -> u8;
|
||||||
|
fn set_segment_type(&mut self, val: u8);
|
||||||
|
|
||||||
|
// Descriptor Privilege Level (DPL)
|
||||||
|
fn dpl(&self) -> u8;
|
||||||
|
fn set_dpl(&mut self, val: u8);
|
||||||
|
|
||||||
|
// Granularity
|
||||||
|
fn granularity(&self) -> u8;
|
||||||
|
fn set_granularity(&mut self, val: u8);
|
||||||
|
|
||||||
|
// Memory Presence
|
||||||
|
fn present(&self) -> u8;
|
||||||
|
fn set_present(&mut self, val: u8);
|
||||||
|
|
||||||
|
// Long mode
|
||||||
|
fn long(&self) -> u8;
|
||||||
|
fn set_long(&mut self, val: u8);
|
||||||
|
|
||||||
|
// Available for system use (AVL)
|
||||||
|
fn avl(&self) -> u8;
|
||||||
|
fn set_avl(&mut self, val: u8);
|
||||||
|
|
||||||
|
// Descriptor type (System or code/data)
|
||||||
|
fn desc_type(&self) -> u8;
|
||||||
|
fn set_desc_type(&mut self, val: u8);
|
||||||
|
|
||||||
|
// D/B
|
||||||
|
fn db(&self) -> u8;
|
||||||
|
fn set_db(&mut self, val: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code segment
|
||||||
|
pub const CODE_SEGMENT_TYPE: u8 = 0x8;
|
||||||
|
|
||||||
|
// Read/Write or Read/Exec segment
|
||||||
|
pub const RWRX_SEGMENT_TYPE: u8 = 0x2;
|
||||||
|
|
||||||
|
// Expand down segment
|
||||||
|
pub const EXPAND_DOWN_SEGMENT_TYPE: u8 = 0x4;
|
||||||
|
|
||||||
|
pub fn segment_type_code(t: u8) -> bool {
|
||||||
|
t & CODE_SEGMENT_TYPE != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn segment_type_ro(t: u8) -> bool {
|
||||||
|
t & !RWRX_SEGMENT_TYPE == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn segment_type_expand_down(t: u8) -> bool {
|
||||||
|
!segment_type_code(t) && (t & EXPAND_DOWN_SEGMENT_TYPE != 0)
|
||||||
|
}
|
||||||
|
|||||||
32
hypervisor/src/arch/x86/regs.rs
Normal file
32
hypervisor/src/arch/x86/regs.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
// EFER (technically not a register) bits
|
||||||
|
pub const EFER_LMA: u64 = 0x400;
|
||||||
|
pub const EFER_LME: u64 = 0x100;
|
||||||
|
|
||||||
|
// CR0 bits
|
||||||
|
pub const CR0_PE: u64 = 0x1;
|
||||||
|
pub const CR0_PG: u64 = 0x80000000;
|
||||||
|
|
||||||
|
// CR4 bits
|
||||||
|
pub const CR4_PAE: u64 = 0x20;
|
||||||
|
pub const CR4_LA57: u64 = 0x1000;
|
||||||
|
|
||||||
|
// RFlags bits
|
||||||
|
pub const CF_SHIFT: usize = 0;
|
||||||
|
pub const PF_SHIFT: usize = 2;
|
||||||
|
pub const AF_SHIFT: usize = 4;
|
||||||
|
pub const ZF_SHIFT: usize = 6;
|
||||||
|
pub const SF_SHIFT: usize = 7;
|
||||||
|
pub const OF_SHIFT: usize = 11;
|
||||||
|
|
||||||
|
pub const CF: u64 = 1 << CF_SHIFT;
|
||||||
|
pub const PF: u64 = 1 << PF_SHIFT;
|
||||||
|
pub const AF: u64 = 1 << AF_SHIFT;
|
||||||
|
pub const ZF: u64 = 1 << ZF_SHIFT;
|
||||||
|
pub const SF: u64 = 1 << SF_SHIFT;
|
||||||
|
pub const OF: u64 = 1 << OF_SHIFT;
|
||||||
@@ -12,13 +12,17 @@
|
|||||||
use crate::aarch64::VcpuInit;
|
use crate::aarch64::VcpuInit;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use crate::aarch64::{RegList, Register, StandardRegisters};
|
use crate::aarch64::{RegList, Register, StandardRegisters};
|
||||||
use crate::{CpuState, MpState};
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::x86_64::{CpuId, LapicState};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::x86_64::{
|
use crate::x86_64::{
|
||||||
CpuId, ExtendedControlRegisters, FpuState, LapicState, MsrEntries, SpecialRegisters,
|
ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents,
|
||||||
StandardRegisters, VcpuEvents, Xsave,
|
|
||||||
};
|
};
|
||||||
|
use crate::CpuState;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
use crate::MpState;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::Xsave;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -151,6 +155,20 @@ pub enum HypervisorCpuError {
|
|||||||
#[error("Failed to notify guest its clock was paused: {0}")]
|
#[error("Failed to notify guest its clock was paused: {0}")]
|
||||||
NotifyGuestClockPaused(#[source] anyhow::Error),
|
NotifyGuestClockPaused(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
|
/// Setting debug register error
|
||||||
|
///
|
||||||
|
#[error("Failed to set debug registers: {0}")]
|
||||||
|
SetDebugRegs(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Getting debug register error
|
||||||
|
///
|
||||||
|
#[error("Failed to get debug registers: {0}")]
|
||||||
|
GetDebugRegs(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Write to Guest Mem
|
||||||
|
///
|
||||||
|
#[error("Failed to write to Guest Mem at: {0}")]
|
||||||
|
GuestMemWrite(#[source] anyhow::Error),
|
||||||
/// Enabling HyperV SynIC error
|
/// Enabling HyperV SynIC error
|
||||||
///
|
///
|
||||||
#[error("Failed to enable HyperV SynIC")]
|
#[error("Failed to enable HyperV SynIC")]
|
||||||
@@ -194,6 +212,7 @@ pub enum VmExit<'a> {
|
|||||||
MmioWrite(u64 /* address */, &'a [u8]),
|
MmioWrite(u64 /* address */, &'a [u8]),
|
||||||
Ignore,
|
Ignore,
|
||||||
Reset,
|
Reset,
|
||||||
|
Shutdown,
|
||||||
Hyperv,
|
Hyperv,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,10 +290,12 @@ pub trait Vcpu: Send + Sync {
|
|||||||
/// Setup the model-specific registers (MSR) for this vCPU.
|
/// Setup the model-specific registers (MSR) for this vCPU.
|
||||||
///
|
///
|
||||||
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
|
fn set_msrs(&self, msrs: &MsrEntries) -> Result<usize>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
///
|
///
|
||||||
/// Returns the vcpu's current "multiprocessing state".
|
/// Returns the vcpu's current "multiprocessing state".
|
||||||
///
|
///
|
||||||
fn get_mp_state(&self) -> Result<MpState>;
|
fn get_mp_state(&self) -> Result<MpState>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
///
|
///
|
||||||
/// Sets the vcpu's current "multiprocessing state".
|
/// Sets the vcpu's current "multiprocessing state".
|
||||||
///
|
///
|
||||||
@@ -311,7 +332,7 @@ pub trait Vcpu: Send + Sync {
|
|||||||
/// of the vcpu.
|
/// of the vcpu.
|
||||||
///
|
///
|
||||||
fn set_vcpu_events(&self, events: &VcpuEvents) -> Result<()>;
|
fn set_vcpu_events(&self, events: &VcpuEvents) -> Result<()>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
///
|
///
|
||||||
/// Let the guest know that it has been paused, which prevents from
|
/// Let the guest know that it has been paused, which prevents from
|
||||||
/// potential soft lockups when being resumed.
|
/// potential soft lockups when being resumed.
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
//
|
//
|
||||||
use crate::vm::Vm;
|
use crate::vm::Vm;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::x86_64::{CpuId, MsrList};
|
use crate::x86_64::CpuId;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use crate::x86_64::MsrList;
|
||||||
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
use kvm_ioctls::Cap;
|
use kvm_ioctls::Cap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -20,6 +22,11 @@ use thiserror::Error;
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub enum HypervisorError {
|
pub enum HypervisorError {
|
||||||
|
///
|
||||||
|
/// hypervisor creation error
|
||||||
|
///
|
||||||
|
#[error("Failed to create the hypervisor: {0}")]
|
||||||
|
HypervisorCreate(#[source] anyhow::Error),
|
||||||
///
|
///
|
||||||
/// Vm creation failure
|
/// Vm creation failure
|
||||||
///
|
///
|
||||||
@@ -78,19 +85,22 @@ pub trait Hypervisor: Send + Sync {
|
|||||||
/// Return a hypervisor-agnostic Vm trait object
|
/// Return a hypervisor-agnostic Vm trait object
|
||||||
///
|
///
|
||||||
fn create_vm(&self) -> Result<Arc<dyn Vm>>;
|
fn create_vm(&self) -> 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>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
///
|
///
|
||||||
/// Gets the recommended maximum number of VCPUs per VM.
|
/// Gets the recommended maximum number of VCPUs per VM.
|
||||||
///
|
///
|
||||||
fn get_max_vcpus(&self) -> Result<usize>;
|
fn get_max_vcpus(&self) -> Result<usize>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
///
|
///
|
||||||
/// Gets the recommended number of VCPUs per VM.
|
/// Gets the recommended number of VCPUs per VM.
|
||||||
///
|
///
|
||||||
fn get_nr_vcpus(&self) -> Result<usize>;
|
fn get_nr_vcpus(&self) -> Result<usize>;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
///
|
///
|
||||||
/// Checks if a particular `Cap` is available.
|
/// Checks if a particular `Cap` is available.
|
||||||
///
|
///
|
||||||
@@ -100,6 +110,7 @@ pub trait Hypervisor: Send + Sync {
|
|||||||
/// Get the supported CpuID
|
/// Get the supported CpuID
|
||||||
///
|
///
|
||||||
fn get_cpuid(&self) -> Result<CpuId>;
|
fn get_cpuid(&self) -> Result<CpuId>;
|
||||||
|
#[cfg(not(feature = "mshv"))]
|
||||||
///
|
///
|
||||||
/// Check particular extensions if any
|
/// Check particular extensions if any
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ use crate::hypervisor;
|
|||||||
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};
|
||||||
use arc_swap::ArcSwapOption;
|
|
||||||
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
@@ -70,7 +69,8 @@ use std::mem;
|
|||||||
pub use kvm_bindings;
|
pub use kvm_bindings;
|
||||||
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_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_READONLY, KVM_MSI_VALID_DEVID,
|
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};
|
||||||
@@ -96,7 +96,6 @@ pub struct KvmVm {
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
state: KvmVmState,
|
state: KvmVmState,
|
||||||
vmmops: ArcSwapOption<Box<dyn vm::VmmOps>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
||||||
@@ -176,7 +175,11 @@ impl vm::Vm for KvmVm {
|
|||||||
///
|
///
|
||||||
/// Creates a VcpuFd object from a vcpu RawFd.
|
/// Creates a VcpuFd object from a vcpu RawFd.
|
||||||
///
|
///
|
||||||
fn create_vcpu(&self, id: u8) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
fn create_vcpu(
|
||||||
|
&self,
|
||||||
|
id: u8,
|
||||||
|
vmmops: Option<Arc<Box<dyn VmmOps>>>,
|
||||||
|
) -> vm::Result<Arc<dyn cpu::Vcpu>> {
|
||||||
let vc = self
|
let vc = self
|
||||||
.fd
|
.fd
|
||||||
.create_vcpu(id)
|
.create_vcpu(id)
|
||||||
@@ -185,7 +188,7 @@ impl vm::Vm for KvmVm {
|
|||||||
fd: vc,
|
fd: vc,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
msrs: self.msrs.clone(),
|
msrs: self.msrs.clone(),
|
||||||
vmmops: self.vmmops.clone(),
|
vmmops,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
hyperv_synic: AtomicBool::new(false),
|
hyperv_synic: AtomicBool::new(false),
|
||||||
};
|
};
|
||||||
@@ -255,13 +258,19 @@ impl vm::Vm for KvmVm {
|
|||||||
memory_size: u64,
|
memory_size: u64,
|
||||||
userspace_addr: u64,
|
userspace_addr: u64,
|
||||||
readonly: bool,
|
readonly: bool,
|
||||||
|
log_dirty_pages: bool,
|
||||||
) -> MemoryRegion {
|
) -> MemoryRegion {
|
||||||
MemoryRegion {
|
MemoryRegion {
|
||||||
slot,
|
slot,
|
||||||
guest_phys_addr,
|
guest_phys_addr,
|
||||||
memory_size,
|
memory_size,
|
||||||
userspace_addr,
|
userspace_addr,
|
||||||
flags: if readonly { KVM_MEM_READONLY } else { 0 },
|
flags: if readonly { KVM_MEM_READONLY } else { 0 }
|
||||||
|
| if log_dirty_pages {
|
||||||
|
KVM_MEM_LOG_DIRTY_PAGES
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
@@ -356,11 +365,12 @@ impl vm::Vm for KvmVm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Set the VmmOps interface
|
/// Get dirty pages bitmap (one bit per page)
|
||||||
///
|
///
|
||||||
fn set_vmmops(&self, vmmops: Box<dyn VmmOps>) -> vm::Result<()> {
|
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> vm::Result<Vec<u64>> {
|
||||||
self.vmmops.store(Some(Arc::new(vmmops)));
|
self.fd
|
||||||
Ok(())
|
.get_dirty_log(slot, memory_size as usize)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Wrapper over KVM system ioctls.
|
/// Wrapper over KVM system ioctls.
|
||||||
@@ -439,7 +449,6 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
msrs,
|
msrs,
|
||||||
state: VmState {},
|
state: VmState {},
|
||||||
vmmops: ArcSwapOption::from(None),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +457,6 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
|||||||
Ok(Arc::new(KvmVm {
|
Ok(Arc::new(KvmVm {
|
||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
state: VmState {},
|
state: VmState {},
|
||||||
vmmops: ArcSwapOption::from(None),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,7 +517,7 @@ pub struct KvmVcpu {
|
|||||||
fd: VcpuFd,
|
fd: VcpuFd,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
vmmops: ArcSwapOption<Box<dyn vm::VmmOps>>,
|
vmmops: Option<Arc<Box<dyn vm::VmmOps>>>,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
hyperv_synic: AtomicBool,
|
hyperv_synic: AtomicBool,
|
||||||
}
|
}
|
||||||
@@ -520,7 +528,7 @@ pub struct KvmVcpu {
|
|||||||
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
/// let kvm = hypervisor::kvm::KvmHypervisor::new().unwrap();
|
||||||
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
/// let hypervisor: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||||
/// let vcpu = vm.create_vcpu(0).unwrap();
|
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
/// vcpu.get/set().unwrap()
|
/// vcpu.get/set().unwrap()
|
||||||
///
|
///
|
||||||
impl cpu::Vcpu for KvmVcpu {
|
impl cpu::Vcpu for KvmVcpu {
|
||||||
@@ -594,7 +602,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
fn enable_hyperv_synic(&self) -> cpu::Result<()> {
|
fn enable_hyperv_synic(&self) -> cpu::Result<()> {
|
||||||
// Update the information about Hyper-V SynIC being enabled and
|
// Update the information about Hyper-V SynIC being enabled and
|
||||||
// emulated as it will influence later which MSRs should be saved.
|
// emulated as it will influence later which MSRs should be saved.
|
||||||
self.hyperv_synic.store(true, Ordering::SeqCst);
|
self.hyperv_synic.store(true, Ordering::Release);
|
||||||
|
|
||||||
let mut cap: kvm_enable_cap = Default::default();
|
let mut cap: kvm_enable_cap = Default::default();
|
||||||
cap.cap = KVM_CAP_HYPERV_SYNIC;
|
cap.cap = KVM_CAP_HYPERV_SYNIC;
|
||||||
@@ -708,7 +716,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
Ok(run) => match run {
|
Ok(run) => match run {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
VcpuExit::IoIn(addr, data) => {
|
VcpuExit::IoIn(addr, data) => {
|
||||||
if let Some(vmmops) = self.vmmops.load_full() {
|
if let Some(vmmops) = &self.vmmops {
|
||||||
return vmmops
|
return vmmops
|
||||||
.pio_read(addr.into(), data)
|
.pio_read(addr.into(), data)
|
||||||
.map(|_| cpu::VmExit::Ignore)
|
.map(|_| cpu::VmExit::Ignore)
|
||||||
@@ -719,7 +727,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
VcpuExit::IoOut(addr, data) => {
|
VcpuExit::IoOut(addr, data) => {
|
||||||
if let Some(vmmops) = self.vmmops.load_full() {
|
if let Some(vmmops) = &self.vmmops {
|
||||||
return vmmops
|
return vmmops
|
||||||
.pio_write(addr.into(), data)
|
.pio_write(addr.into(), data)
|
||||||
.map(|_| cpu::VmExit::Ignore)
|
.map(|_| cpu::VmExit::Ignore)
|
||||||
@@ -738,10 +746,10 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
use kvm_bindings::{KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN};
|
use kvm_bindings::{KVM_SYSTEM_EVENT_RESET, KVM_SYSTEM_EVENT_SHUTDOWN};
|
||||||
// On Aarch64, when the VM is shutdown, run() returns
|
// On Aarch64, when the VM is shutdown, run() returns
|
||||||
// VcpuExit::SystemEvent with reason KVM_SYSTEM_EVENT_SHUTDOWN
|
// VcpuExit::SystemEvent with reason KVM_SYSTEM_EVENT_SHUTDOWN
|
||||||
if event_type == KVM_SYSTEM_EVENT_SHUTDOWN
|
if event_type == KVM_SYSTEM_EVENT_RESET {
|
||||||
|| event_type == KVM_SYSTEM_EVENT_RESET
|
|
||||||
{
|
|
||||||
Ok(cpu::VmExit::Reset)
|
Ok(cpu::VmExit::Reset)
|
||||||
|
} else if event_type == KVM_SYSTEM_EVENT_SHUTDOWN {
|
||||||
|
Ok(cpu::VmExit::Shutdown)
|
||||||
} else {
|
} else {
|
||||||
Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||||
"Unexpected system event with type 0x{:x}, flags 0x{:x}",
|
"Unexpected system event with type 0x{:x}, flags 0x{:x}",
|
||||||
@@ -752,7 +760,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VcpuExit::MmioRead(addr, data) => {
|
VcpuExit::MmioRead(addr, data) => {
|
||||||
if let Some(vmmops) = self.vmmops.load_full() {
|
if let Some(vmmops) = &self.vmmops {
|
||||||
return vmmops
|
return vmmops
|
||||||
.mmio_read(addr, data)
|
.mmio_read(addr, data)
|
||||||
.map(|_| cpu::VmExit::Ignore)
|
.map(|_| cpu::VmExit::Ignore)
|
||||||
@@ -762,7 +770,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
Ok(cpu::VmExit::MmioRead(addr, data))
|
Ok(cpu::VmExit::MmioRead(addr, data))
|
||||||
}
|
}
|
||||||
VcpuExit::MmioWrite(addr, data) => {
|
VcpuExit::MmioWrite(addr, data) => {
|
||||||
if let Some(vmmops) = self.vmmops.load_full() {
|
if let Some(vmmops) = &self.vmmops {
|
||||||
return vmmops
|
return vmmops
|
||||||
.mmio_write(addr, data)
|
.mmio_write(addr, data)
|
||||||
.map(|_| cpu::VmExit::Ignore)
|
.map(|_| cpu::VmExit::Ignore)
|
||||||
@@ -1121,7 +1129,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||||
/// let vm = hv.create_vm().expect("new VM fd creation failed");
|
/// let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||||
/// vm.enable_split_irq().unwrap();
|
/// vm.enable_split_irq().unwrap();
|
||||||
/// let vcpu = vm.create_vcpu(0).unwrap();
|
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
/// let state = vcpu.state().unwrap();
|
/// let state = vcpu.state().unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
fn state(&self) -> cpu::Result<CpuState> {
|
fn state(&self) -> cpu::Result<CpuState> {
|
||||||
@@ -1143,7 +1151,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
|
|
||||||
// Save extra MSRs if the Hyper-V synthetic interrupt controller is
|
// Save extra MSRs if the Hyper-V synthetic interrupt controller is
|
||||||
// emulated.
|
// emulated.
|
||||||
if self.hyperv_synic.load(Ordering::SeqCst) {
|
if self.hyperv_synic.load(Ordering::Acquire) {
|
||||||
let hyperv_synic_msrs = vec![
|
let hyperv_synic_msrs = vec![
|
||||||
0x40000020, 0x40000021, 0x40000080, 0x40000081, 0x40000082, 0x40000083, 0x40000084,
|
0x40000020, 0x40000021, 0x40000080, 0x40000081, 0x40000082, 0x40000083, 0x40000084,
|
||||||
0x40000090, 0x40000091, 0x40000092, 0x40000093, 0x40000094, 0x40000095, 0x40000096,
|
0x40000090, 0x40000091, 0x40000092, 0x40000093, 0x40000094, 0x40000095, 0x40000096,
|
||||||
@@ -1267,7 +1275,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
/// let hv: Arc<dyn hypervisor::Hypervisor> = Arc::new(kvm);
|
||||||
/// let vm = hv.create_vm().expect("new VM fd creation failed");
|
/// let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||||
/// vm.enable_split_irq().unwrap();
|
/// vm.enable_split_irq().unwrap();
|
||||||
/// let vcpu = vm.create_vcpu(0).unwrap();
|
/// let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
/// let state = vcpu.state().unwrap();
|
/// let state = vcpu.state().unwrap();
|
||||||
/// vcpu.set_state(&state).unwrap();
|
/// vcpu.set_state(&state).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use vm_memory::GuestAddress;
|
use vm_memory::GuestAddress;
|
||||||
|
|
||||||
use crate::arch::x86::{msr_index, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||||
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
use crate::kvm::{Cap, Kvm, KvmError, KvmResult};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -28,6 +28,71 @@ pub use {
|
|||||||
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
|
kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_VALID_INDEX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
impl SegmentRegisterOps for SegmentRegister {
|
||||||
|
fn segment_type(&self) -> u8 {
|
||||||
|
self.type_
|
||||||
|
}
|
||||||
|
fn set_segment_type(&mut self, val: u8) {
|
||||||
|
self.type_ = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dpl(&self) -> u8 {
|
||||||
|
self.dpl
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_dpl(&mut self, val: u8) {
|
||||||
|
self.dpl = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn present(&self) -> u8 {
|
||||||
|
self.present
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_present(&mut self, val: u8) {
|
||||||
|
self.present = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn long(&self) -> u8 {
|
||||||
|
self.l
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_long(&mut self, val: u8) {
|
||||||
|
self.l = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avl(&self) -> u8 {
|
||||||
|
self.avl
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_avl(&mut self, val: u8) {
|
||||||
|
self.avl = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desc_type(&self) -> u8 {
|
||||||
|
self.s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_desc_type(&mut self, val: u8) {
|
||||||
|
self.s = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn granularity(&self) -> u8 {
|
||||||
|
self.g
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_granularity(&mut self, val: u8) {
|
||||||
|
self.g = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn db(&self) -> u8 {
|
||||||
|
self.db
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_db(&mut self, val: u8) {
|
||||||
|
self.db = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
pub const KVM_TSS_ADDRESS: GuestAddress = GuestAddress(0xfffb_d000);
|
||||||
|
|
||||||
macro_rules! msr {
|
macro_rules! msr {
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ extern crate serde_derive;
|
|||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate thiserror;
|
extern crate thiserror;
|
||||||
|
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
/// KVM implementation module
|
/// KVM implementation module
|
||||||
pub mod kvm;
|
pub mod kvm;
|
||||||
|
|
||||||
|
/// Microsoft Hypervisor implementation module
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
pub mod mshv;
|
||||||
|
|
||||||
/// Hypevisor related module
|
/// Hypevisor related module
|
||||||
pub mod hypervisor;
|
pub mod hypervisor;
|
||||||
|
|
||||||
@@ -49,7 +54,10 @@ mod device;
|
|||||||
pub use crate::hypervisor::{Hypervisor, HypervisorError};
|
pub use crate::hypervisor::{Hypervisor, HypervisorError};
|
||||||
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||||
pub use device::{Device, HypervisorDeviceError};
|
pub use device::{Device, HypervisorDeviceError};
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
pub use kvm::*;
|
pub use kvm::*;
|
||||||
|
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||||
|
pub use mshv::*;
|
||||||
pub use vm::{DataMatch, HypervisorVmError, Vm};
|
pub use vm::{DataMatch, HypervisorVmError, Vm};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -58,5 +66,8 @@ pub fn new() -> std::result::Result<Arc<dyn Hypervisor>, HypervisorError> {
|
|||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
let hv = kvm::KvmHypervisor::new()?;
|
let hv = kvm::KvmHypervisor::new()?;
|
||||||
|
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
let hv = mshv::MshvHypervisor::new()?;
|
||||||
|
|
||||||
Ok(Arc::new(hv))
|
Ok(Arc::new(hv))
|
||||||
}
|
}
|
||||||
|
|||||||
1082
hypervisor/src/mshv/mod.rs
Normal file
1082
hypervisor/src/mshv/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
148
hypervisor/src/mshv/x86_64/mod.rs
Normal file
148
hypervisor/src/mshv/x86_64/mod.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// Copyright © 2019 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||||
|
//
|
||||||
|
// Copyright © 2020, Microsoft Corporation
|
||||||
|
//
|
||||||
|
// Copyright 2018-2019 CrowdStrike, Inc.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::arch::x86::{msr_index, SegmentRegisterOps, MTRR_ENABLE, MTRR_MEM_TYPE_WB};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
///
|
||||||
|
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||||
|
///
|
||||||
|
pub use {
|
||||||
|
mshv_bindings::mshv_user_mem_region as MemoryRegion, mshv_bindings::msr_entry as MsrEntry,
|
||||||
|
mshv_bindings::CpuId, mshv_bindings::DebugRegisters,
|
||||||
|
mshv_bindings::FloatingPointUnit as FpuState, mshv_bindings::LapicState,
|
||||||
|
mshv_bindings::MsrList, mshv_bindings::Msrs as MsrEntries, mshv_bindings::Msrs,
|
||||||
|
mshv_bindings::SegmentRegister, mshv_bindings::SpecialRegisters,
|
||||||
|
mshv_bindings::StandardRegisters, mshv_bindings::VcpuEvents, mshv_bindings::XSave as Xsave,
|
||||||
|
mshv_bindings::Xcrs as ExtendedControlRegisters,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct VcpuMshvState {
|
||||||
|
pub msrs: MsrEntries,
|
||||||
|
pub vcpu_events: VcpuEvents,
|
||||||
|
pub regs: StandardRegisters,
|
||||||
|
pub sregs: SpecialRegisters,
|
||||||
|
pub fpu: FpuState,
|
||||||
|
pub xcrs: ExtendedControlRegisters,
|
||||||
|
pub lapic: LapicState,
|
||||||
|
pub dbg: DebugRegisters,
|
||||||
|
pub xsave: Xsave,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CreateDevice {}
|
||||||
|
pub struct DeviceAttr {}
|
||||||
|
pub struct IrqRouting {}
|
||||||
|
pub enum VcpuExit {}
|
||||||
|
pub struct MpState {}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, Clone, Debug, Copy)]
|
||||||
|
pub enum IoEventAddress {
|
||||||
|
/// Representation of an programmable I/O address.
|
||||||
|
Pio(u64),
|
||||||
|
/// Representation of an memory mapped I/O address.
|
||||||
|
Mmio(u64),
|
||||||
|
}
|
||||||
|
macro_rules! msr {
|
||||||
|
($msr:expr) => {
|
||||||
|
MsrEntry {
|
||||||
|
index: $msr,
|
||||||
|
data: 0x0,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! msr_data {
|
||||||
|
($msr:expr, $data:expr) => {
|
||||||
|
MsrEntry {
|
||||||
|
index: $msr,
|
||||||
|
data: $data,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SegmentRegisterOps for SegmentRegister {
|
||||||
|
fn segment_type(&self) -> u8 {
|
||||||
|
self.type_
|
||||||
|
}
|
||||||
|
fn set_segment_type(&mut self, val: u8) {
|
||||||
|
self.type_ = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dpl(&self) -> u8 {
|
||||||
|
self.dpl
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_dpl(&mut self, val: u8) {
|
||||||
|
self.dpl = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn present(&self) -> u8 {
|
||||||
|
self.present
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_present(&mut self, val: u8) {
|
||||||
|
self.present = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn long(&self) -> u8 {
|
||||||
|
self.l
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_long(&mut self, val: u8) {
|
||||||
|
self.l = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avl(&self) -> u8 {
|
||||||
|
self.avl
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_avl(&mut self, val: u8) {
|
||||||
|
self.avl = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desc_type(&self) -> u8 {
|
||||||
|
self.s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_desc_type(&mut self, val: u8) {
|
||||||
|
self.s = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn granularity(&self) -> u8 {
|
||||||
|
self.g
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_granularity(&mut self, val: u8) {
|
||||||
|
self.g = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn db(&self) -> u8 {
|
||||||
|
self.db
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_db(&mut self, val: u8) {
|
||||||
|
self.db = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn boot_msr_entries() -> MsrEntries {
|
||||||
|
MsrEntries::from_entries(&[
|
||||||
|
msr!(msr_index::MSR_IA32_SYSENTER_CS),
|
||||||
|
msr!(msr_index::MSR_IA32_SYSENTER_ESP),
|
||||||
|
msr!(msr_index::MSR_IA32_SYSENTER_EIP),
|
||||||
|
msr!(msr_index::MSR_STAR),
|
||||||
|
msr!(msr_index::MSR_CSTAR),
|
||||||
|
msr!(msr_index::MSR_LSTAR),
|
||||||
|
msr!(msr_index::MSR_KERNEL_GS_BASE),
|
||||||
|
msr!(msr_index::MSR_SYSCALL_MASK),
|
||||||
|
msr!(msr_index::MSR_IA32_TSC),
|
||||||
|
])
|
||||||
|
}
|
||||||
@@ -12,10 +12,16 @@
|
|||||||
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(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
use crate::ClockData;
|
use crate::ClockData;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
use crate::CreateDevice;
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
use crate::HvState as VmState;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
use crate::KvmVmState as VmState;
|
use crate::KvmVmState as VmState;
|
||||||
use crate::{CreateDevice, IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion};
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
use kvm_ioctls::Cap;
|
use kvm_ioctls::Cap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -24,6 +30,7 @@ use vmm_sys_util::eventfd::EventFd;
|
|||||||
///
|
///
|
||||||
/// I/O events data matches (32 or 64 bits).
|
/// I/O events data matches (32 or 64 bits).
|
||||||
///
|
///
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum DataMatch {
|
pub enum DataMatch {
|
||||||
DataMatch32(u32),
|
DataMatch32(u32),
|
||||||
DataMatch64(u64),
|
DataMatch64(u64),
|
||||||
@@ -117,7 +124,6 @@ pub enum HypervisorVmError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to create passthrough device: {0}")]
|
#[error("Failed to create passthrough device: {0}")]
|
||||||
CreatePassthroughDevice(#[source] anyhow::Error),
|
CreatePassthroughDevice(#[source] anyhow::Error),
|
||||||
///
|
|
||||||
/// Write to Guest memory
|
/// Write to Guest memory
|
||||||
///
|
///
|
||||||
#[error("Failed to write to guest memory: {0}")]
|
#[error("Failed to write to guest memory: {0}")]
|
||||||
@@ -147,6 +153,16 @@ pub enum HypervisorVmError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to write to IO Bus: {0}")]
|
#[error("Failed to write to IO Bus: {0}")]
|
||||||
IoBusWrite(#[source] anyhow::Error),
|
IoBusWrite(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Get dirty log error
|
||||||
|
///
|
||||||
|
#[error("Failed to get dirty log: {0}")]
|
||||||
|
GetDirtyLog(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Assert virtual interrupt error
|
||||||
|
///
|
||||||
|
#[error("Failed to assert virtual Interrupt: {0}")]
|
||||||
|
AsserttVirtualInterrupt(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Result type for returning from a function
|
/// Result type for returning from a function
|
||||||
@@ -169,7 +185,7 @@ pub trait Vm: Send + Sync {
|
|||||||
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
|
/// Unregister an event that will, when signaled, trigger the `gsi` IRQ.
|
||||||
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
|
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>;
|
||||||
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
/// Creates a new KVM vCPU file descriptor and maps the memory corresponding
|
||||||
fn create_vcpu(&self, id: u8) -> Result<Arc<dyn Vcpu>>;
|
fn create_vcpu(&self, id: u8, vmmops: Option<Arc<Box<dyn VmmOps>>>) -> Result<Arc<dyn Vcpu>>;
|
||||||
/// Registers an event to be signaled whenever a certain address is written to.
|
/// Registers an event to be signaled whenever a certain address is written to.
|
||||||
fn register_ioevent(
|
fn register_ioevent(
|
||||||
&self,
|
&self,
|
||||||
@@ -189,9 +205,11 @@ pub trait Vm: Send + Sync {
|
|||||||
memory_size: u64,
|
memory_size: u64,
|
||||||
userspace_addr: u64,
|
userspace_addr: u64,
|
||||||
readonly: bool,
|
readonly: bool,
|
||||||
|
log_dirty_pages: bool,
|
||||||
) -> MemoryRegion;
|
) -> MemoryRegion;
|
||||||
/// Creates/modifies a guest physical memory slot.
|
/// Creates/modifies a guest physical memory slot.
|
||||||
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> Result<()>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
/// Creates an emulated device in the kernel.
|
/// Creates an emulated device in the kernel.
|
||||||
fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
|
fn create_device(&self, device: &mut CreateDevice) -> Result<Arc<dyn Device>>;
|
||||||
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
/// Returns the preferred CPU target type which can be emulated by KVM on underlying host.
|
||||||
@@ -201,11 +219,12 @@ pub trait Vm: Send + Sync {
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn enable_split_irq(&self) -> Result<()>;
|
fn enable_split_irq(&self) -> Result<()>;
|
||||||
/// Retrieve guest clock.
|
/// Retrieve guest clock.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
fn get_clock(&self) -> Result<ClockData>;
|
fn get_clock(&self) -> Result<ClockData>;
|
||||||
/// Set guest clock.
|
/// Set guest clock.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
fn set_clock(&self, data: &ClockData) -> Result<()>;
|
fn set_clock(&self, data: &ClockData) -> Result<()>;
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
/// Checks if a particular `Cap` is available.
|
/// Checks if a particular `Cap` is available.
|
||||||
fn check_extension(&self, c: Cap) -> bool;
|
fn check_extension(&self, c: Cap) -> bool;
|
||||||
/// Create a device that is used for passthrough
|
/// Create a device that is used for passthrough
|
||||||
@@ -214,8 +233,8 @@ pub trait Vm: Send + Sync {
|
|||||||
fn state(&self) -> Result<VmState>;
|
fn state(&self) -> Result<VmState>;
|
||||||
/// Set the VM state
|
/// Set the VM state
|
||||||
fn set_state(&self, state: VmState) -> Result<()>;
|
fn set_state(&self, state: VmState) -> Result<()>;
|
||||||
/// Set VmmOps interface
|
/// Get dirty pages bitmap
|
||||||
fn set_vmmops(&self, vmmops: Box<dyn VmmOps>) -> Result<()>;
|
fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result<Vec<u64>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VmmOps: Send + Sync {
|
pub trait VmmOps: Send + Sync {
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ authors = ["The Chromium OS Authors"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
serde = "1.0.117"
|
serde = "1.0.118"
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.4.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]
|
[dev-dependencies]
|
||||||
lazy_static = "1.3.0"
|
lazy_static = "1.3.0"
|
||||||
pnet = "0.26.0"
|
pnet = "0.27.2"
|
||||||
serde_json = "1.0.59"
|
serde_json = "1.0.60"
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ byteorder = "1.3.4"
|
|||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
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"
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = "0.3.0"
|
vm-memory = "0.4.0"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
|
|
||||||
[dependencies.vfio-bindings]
|
[dependencies.vfio-bindings]
|
||||||
|
|||||||
@@ -570,7 +570,7 @@ impl PciConfiguration {
|
|||||||
let end_addr = config
|
let end_addr = config
|
||||||
.addr
|
.addr
|
||||||
.checked_add(config.size - 1)
|
.checked_add(config.size - 1)
|
||||||
.ok_or_else(|| 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()) {
|
||||||
@@ -642,7 +642,7 @@ impl PciConfiguration {
|
|||||||
let end_addr = config
|
let end_addr = config
|
||||||
.addr
|
.addr
|
||||||
.checked_add(config.size - 1)
|
.checked_add(config.size - 1)
|
||||||
.ok_or_else(|| Error::RomBarAddressInvalid(config.addr, config.size))?;
|
.ok_or(Error::RomBarAddressInvalid(config.addr, config.size))?;
|
||||||
|
|
||||||
if end_addr > u64::from(u32::max_value()) {
|
if end_addr > u64::from(u32::max_value()) {
|
||||||
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
||||||
@@ -698,7 +698,7 @@ impl PciConfiguration {
|
|||||||
};
|
};
|
||||||
let end_offset = cap_offset
|
let end_offset = cap_offset
|
||||||
.checked_add(total_len)
|
.checked_add(total_len)
|
||||||
.ok_or_else(|| Error::CapabilitySpaceFull(total_len))?;
|
.ok_or(Error::CapabilitySpaceFull(total_len))?;
|
||||||
if end_offset > CAPABILITY_MAX_OFFSET {
|
if end_offset > CAPABILITY_MAX_OFFSET {
|
||||||
return Err(Error::CapabilitySpaceFull(total_len));
|
return Err(Error::CapabilitySpaceFull(total_len));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,9 +221,9 @@ impl Interrupt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct MmioRegion {
|
pub struct MmioRegion {
|
||||||
start: GuestAddress,
|
pub start: GuestAddress,
|
||||||
length: GuestUsize,
|
pub length: GuestUsize,
|
||||||
type_: PciBarRegionType,
|
type_: PciBarRegionType,
|
||||||
index: u32,
|
index: u32,
|
||||||
mem_slot: Option<u32>,
|
mem_slot: Option<u32>,
|
||||||
@@ -561,6 +561,7 @@ impl VfioPciDevice {
|
|||||||
mmap_size as u64,
|
mmap_size as u64,
|
||||||
host_addr as u64,
|
host_addr as u64,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
vm.set_user_memory_region(mem_region)
|
vm.set_user_memory_region(mem_region)
|
||||||
@@ -590,6 +591,7 @@ impl VfioPciDevice {
|
|||||||
0,
|
0,
|
||||||
host_addr as u64,
|
host_addr as u64,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(e) = self.vm.set_user_memory_region(r) {
|
if let Err(e) = self.vm.set_user_memory_region(r) {
|
||||||
@@ -613,6 +615,10 @@ impl VfioPciDevice {
|
|||||||
.extend_dma_map(new_region)
|
.extend_dma_map(new_region)
|
||||||
.map_err(VfioPciError::UpdateMemory)
|
.map_err(VfioPciError::UpdateMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mmio_regions(&self) -> Vec<MmioRegion> {
|
||||||
|
self.mmio_regions.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for VfioPciDevice {
|
impl Drop for VfioPciDevice {
|
||||||
@@ -748,7 +754,7 @@ impl PciDevice for VfioPciDevice {
|
|||||||
// The address needs to be 4 bytes aligned.
|
// The address needs to be 4 bytes aligned.
|
||||||
bar_addr = allocator
|
bar_addr = allocator
|
||||||
.allocate_io_addresses(None, region_size, Some(0x4))
|
.allocate_io_addresses(None, region_size, Some(0x4))
|
||||||
.ok_or_else(|| PciDeviceError::IoAllocationFailed(region_size))?;
|
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
@@ -795,11 +801,11 @@ impl PciDevice for VfioPciDevice {
|
|||||||
if is_64bit_bar {
|
if is_64bit_bar {
|
||||||
bar_addr = allocator
|
bar_addr = allocator
|
||||||
.allocate_mmio_addresses(None, region_size, Some(bar_alignment))
|
.allocate_mmio_addresses(None, region_size, Some(bar_alignment))
|
||||||
.ok_or_else(|| PciDeviceError::IoAllocationFailed(region_size))?;
|
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
|
||||||
} else {
|
} else {
|
||||||
bar_addr = allocator
|
bar_addr = allocator
|
||||||
.allocate_mmio_hole_addresses(None, region_size, Some(bar_alignment))
|
.allocate_mmio_hole_addresses(None, region_size, Some(bar_alignment))
|
||||||
.ok_or_else(|| PciDeviceError::IoAllocationFailed(region_size))?;
|
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,6 +1018,7 @@ impl PciDevice for VfioPciDevice {
|
|||||||
0,
|
0,
|
||||||
host_addr as u64,
|
host_addr as u64,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.vm
|
self.vm
|
||||||
@@ -1025,6 +1032,7 @@ impl PciDevice for VfioPciDevice {
|
|||||||
mmap_size as u64,
|
mmap_size as u64,
|
||||||
host_addr as u64,
|
host_addr as u64,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.vm
|
self.vm
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ path = "src/qcow.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
remain = "0.2.2"
|
remain = "0.2.2"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|||||||
@@ -1868,7 +1868,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_header_crazy_file_size_rejected() {
|
fn test_header_crazy_file_size_rejected() {
|
||||||
let mut header = valid_header_v3();
|
let mut header = valid_header_v3();
|
||||||
&mut header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
|
header[24..32].copy_from_slice(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e]);
|
||||||
with_basic_file(&header, |disk_file: RawFile| {
|
with_basic_file(&header, |disk_file: RawFile| {
|
||||||
QcowFile::from(disk_file).expect_err("Failed to create file.");
|
QcowFile::from(disk_file).expect_err("Failed to create file.");
|
||||||
});
|
});
|
||||||
@@ -1917,7 +1917,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_header_huge_num_refcounts() {
|
fn test_header_huge_num_refcounts() {
|
||||||
let mut header = valid_header_v3();
|
let mut header = valid_header_v3();
|
||||||
&mut header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
|
header[56..60].copy_from_slice(&[0x02, 0x00, 0xe8, 0xff]);
|
||||||
with_basic_file(&header, |disk_file: RawFile| {
|
with_basic_file(&header, |disk_file: RawFile| {
|
||||||
QcowFile::from(disk_file).expect_err("Created disk with crazy refcount clusters");
|
QcowFile::from(disk_file).expect_err("Created disk with crazy refcount clusters");
|
||||||
});
|
});
|
||||||
@@ -1926,7 +1926,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_header_huge_refcount_offset() {
|
fn test_header_huge_refcount_offset() {
|
||||||
let mut header = valid_header_v3();
|
let mut header = valid_header_v3();
|
||||||
&mut header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
|
header[48..56].copy_from_slice(&[0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00]);
|
||||||
with_basic_file(&header, |disk_file: RawFile| {
|
with_basic_file(&header, |disk_file: RawFile| {
|
||||||
QcowFile::from(disk_file).expect_err("Created disk with crazy refcount offset");
|
QcowFile::from(disk_file).expect_err("Created disk with crazy refcount offset");
|
||||||
});
|
});
|
||||||
@@ -1936,11 +1936,11 @@ mod tests {
|
|||||||
fn write_read_start() {
|
fn write_read_start() {
|
||||||
with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
|
with_basic_file(&valid_header_v3(), |disk_file: RawFile| {
|
||||||
let mut q = QcowFile::from(disk_file).unwrap();
|
let mut q = QcowFile::from(disk_file).unwrap();
|
||||||
q.write(b"test first bytes")
|
q.write_all(b"test first bytes")
|
||||||
.expect("Failed to write test string.");
|
.expect("Failed to write test string.");
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||||
q.read(&mut buf).expect("Failed to read.");
|
q.read_exact(&mut buf).expect("Failed to read.");
|
||||||
assert_eq!(&buf, b"test");
|
assert_eq!(&buf, b"test");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1951,10 +1951,10 @@ mod tests {
|
|||||||
let mut q = QcowFile::from(disk_file).unwrap();
|
let mut q = QcowFile::from(disk_file).unwrap();
|
||||||
let b = [0x55u8; 0x1000];
|
let b = [0x55u8; 0x1000];
|
||||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||||
q.write(&b).expect("Failed to write test string.");
|
q.write_all(&b).expect("Failed to write test string.");
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||||
q.read(&mut buf).expect("Failed to read.");
|
q.read_exact(&mut buf).expect("Failed to read.");
|
||||||
assert_eq!(buf[0], 0x55);
|
assert_eq!(buf[0], 0x55);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1966,7 +1966,7 @@ mod tests {
|
|||||||
// Write some test data.
|
// Write some test data.
|
||||||
let b = [0x55u8; 0x1000];
|
let b = [0x55u8; 0x1000];
|
||||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||||
q.write(&b).expect("Failed to write test string.");
|
q.write_all(&b).expect("Failed to write test string.");
|
||||||
// Overwrite the test data with zeroes.
|
// Overwrite the test data with zeroes.
|
||||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||||
let nwritten = q.write_zeroes(0x200).expect("Failed to write zeroes.");
|
let nwritten = q.write_zeroes(0x200).expect("Failed to write zeroes.");
|
||||||
@@ -1974,7 +1974,7 @@ mod tests {
|
|||||||
// Verify that the correct part of the data was zeroed out.
|
// Verify that the correct part of the data was zeroed out.
|
||||||
let mut buf = [0u8; 0x1000];
|
let mut buf = [0u8; 0x1000];
|
||||||
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0xfff2000)).expect("Failed to seek.");
|
||||||
q.read(&mut buf).expect("Failed to read.");
|
q.read_exact(&mut buf).expect("Failed to read.");
|
||||||
assert_eq!(buf[0], 0);
|
assert_eq!(buf[0], 0);
|
||||||
assert_eq!(buf[0x1FF], 0);
|
assert_eq!(buf[0x1FF], 0);
|
||||||
assert_eq!(buf[0x200], 0x55);
|
assert_eq!(buf[0x200], 0x55);
|
||||||
@@ -1992,7 +1992,7 @@ mod tests {
|
|||||||
// Write some test data.
|
// Write some test data.
|
||||||
let b = [0x55u8; CHUNK_SIZE];
|
let b = [0x55u8; CHUNK_SIZE];
|
||||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||||
q.write(&b).expect("Failed to write test string.");
|
q.write_all(&b).expect("Failed to write test string.");
|
||||||
// Overwrite the full cluster with zeroes.
|
// Overwrite the full cluster with zeroes.
|
||||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||||
let nwritten = q.write_zeroes(CHUNK_SIZE).expect("Failed to write zeroes.");
|
let nwritten = q.write_zeroes(CHUNK_SIZE).expect("Failed to write zeroes.");
|
||||||
@@ -2000,7 +2000,7 @@ mod tests {
|
|||||||
// Verify that the data was zeroed out.
|
// Verify that the data was zeroed out.
|
||||||
let mut buf = [0u8; CHUNK_SIZE];
|
let mut buf = [0u8; CHUNK_SIZE];
|
||||||
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(0)).expect("Failed to seek.");
|
||||||
q.read(&mut buf).expect("Failed to read.");
|
q.read_exact(&mut buf).expect("Failed to read.");
|
||||||
assert_eq!(buf[0], 0);
|
assert_eq!(buf[0], 0);
|
||||||
assert_eq!(buf[CHUNK_SIZE - 1], 0);
|
assert_eq!(buf[CHUNK_SIZE - 1], 0);
|
||||||
});
|
});
|
||||||
@@ -2024,7 +2024,7 @@ mod tests {
|
|||||||
let mut q = QcowFile::from(disk_file).unwrap();
|
let mut q = QcowFile::from(disk_file).unwrap();
|
||||||
let mut b = [5u8; 16];
|
let mut b = [5u8; 16];
|
||||||
q.seek(SeekFrom::Start(1000)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(1000)).expect("Failed to seek.");
|
||||||
q.read(&mut b).expect("Failed to read.");
|
q.read_exact(&mut b).expect("Failed to read.");
|
||||||
assert_eq!(0, b[0]);
|
assert_eq!(0, b[0]);
|
||||||
assert_eq!(0, b[15]);
|
assert_eq!(0, b[15]);
|
||||||
});
|
});
|
||||||
@@ -2397,7 +2397,7 @@ mod tests {
|
|||||||
for xfer in &xfers {
|
for xfer in &xfers {
|
||||||
q.seek(SeekFrom::Start(xfer.addr)).expect("Failed to seek.");
|
q.seek(SeekFrom::Start(xfer.addr)).expect("Failed to seek.");
|
||||||
if xfer.write {
|
if xfer.write {
|
||||||
q.write(&b).expect("Failed to write.");
|
q.write_all(&b).expect("Failed to write.");
|
||||||
} else {
|
} else {
|
||||||
let read_count: usize = q.read(&mut b).expect("Failed to read.");
|
let read_count: usize = q.read(&mut b).expect("Failed to read.");
|
||||||
assert_eq!(read_count, BUF_SIZE);
|
assert_eq!(read_count, BUF_SIZE);
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
- [v0.12.0](#v0120)
|
||||||
|
- [ARM64 enhancements](#arm64-enhancements)
|
||||||
|
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||||
|
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||||
|
- [Enhanced "info" API](#enhanced-info-api)
|
||||||
|
- [Contributors](#contributors)
|
||||||
- [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)
|
||||||
@@ -10,14 +16,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)
|
- [Contributors](#contributors-1)
|
||||||
- [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-1)
|
- [Contributors](#contributors-2)
|
||||||
- [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)
|
||||||
@@ -31,7 +37,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-2)
|
- [Contributors](#contributors-3)
|
||||||
- [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)
|
||||||
@@ -40,7 +46,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-3)
|
- [Contributors](#contributors-4)
|
||||||
- [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)
|
||||||
@@ -50,14 +56,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-4)
|
- [Contributors](#contributors-5)
|
||||||
- [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-5)
|
- [Contributors](#contributors-6)
|
||||||
- [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)
|
||||||
@@ -65,7 +71,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-6)
|
- [Contributors](#contributors-7)
|
||||||
- [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)
|
||||||
@@ -74,7 +80,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-7)
|
- [Contributors](#contributors-8)
|
||||||
- [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)
|
||||||
@@ -101,6 +107,47 @@
|
|||||||
- [Unit testing](#unit-testing)
|
- [Unit testing](#unit-testing)
|
||||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||||
|
|
||||||
|
# v0.12.0
|
||||||
|
|
||||||
|
This release has been tracked through the [0.12.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/15).
|
||||||
|
|
||||||
|
Highlights for `cloud-hypervisor` version 0.12.0 include:
|
||||||
|
|
||||||
|
### ARM64 enhancements
|
||||||
|
|
||||||
|
The use of `--watchdog` is now fully supported as is the ability to reboot the
|
||||||
|
VM from within the guest when running Cloud Hypervisor on an ARM64 system.
|
||||||
|
|
||||||
|
### Removal of `vhost-user-net` and `vhost-user-block` self spawning
|
||||||
|
|
||||||
|
In order to use `vhost-user-net` or `vhost-user-block` backends the user is now
|
||||||
|
responsible for starting the backend and providing the socket for the VMM to
|
||||||
|
use. This functionality was deprecated in the last release and how now been
|
||||||
|
removed.
|
||||||
|
|
||||||
|
### Migration of `vhost-user-fs` backend
|
||||||
|
|
||||||
|
The `vhost-user-fs` backend is no longer included in Cloud Hypervisor and it is
|
||||||
|
instead hosted in [it's own
|
||||||
|
repository](https://gitlab.com/virtio-fs/virtiofsd-rs)
|
||||||
|
|
||||||
|
### Enhanced "info" API
|
||||||
|
|
||||||
|
The `vm.info` HTTP API endpoint has been extended to include the details of the
|
||||||
|
devices used by the VM including any VFIO devices used.
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
Many thanks to everyone who has contributed to our 0.12.0 release:
|
||||||
|
|
||||||
|
* Anatol Belski <anbelski@linux.microsoft.com>
|
||||||
|
* Julio Montes <julio.montes@intel.com>
|
||||||
|
* Michael Zhao <michael.zhao@arm.com>
|
||||||
|
* Muminul Islam <muislam@microsoft.com>
|
||||||
|
* Rob Bradford <robert.bradford@intel.com>
|
||||||
|
* Samuel Ortiz <sameo@linux.intel.com>
|
||||||
|
* Wei Liu <liuwe@microsoft.com>
|
||||||
|
|
||||||
# v0.11.0
|
# v0.11.0
|
||||||
|
|
||||||
This release has been tracked through the [0.11.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/14).
|
This release has been tracked through the [0.11.0 project](https://github.com/cloud-hypervisor/cloud-hypervisor/projects/14).
|
||||||
|
|||||||
@@ -1543,7 +1543,39 @@ CONFIG_THERMAL_GOV_USER_SPACE=y
|
|||||||
# CONFIG_CPU_THERMAL is not set
|
# CONFIG_CPU_THERMAL is not set
|
||||||
# CONFIG_THERMAL_EMULATION is not set
|
# CONFIG_THERMAL_EMULATION is not set
|
||||||
# CONFIG_THERMAL_MMIO is not set
|
# CONFIG_THERMAL_MMIO is not set
|
||||||
# CONFIG_WATCHDOG is not set
|
CONFIG_WATCHDOG=y
|
||||||
|
CONFIG_WATCHDOG_CORE=y
|
||||||
|
# CONFIG_WATCHDOG_NOWAYOUT is not set
|
||||||
|
CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
|
||||||
|
CONFIG_WATCHDOG_OPEN_TIMEOUT=0
|
||||||
|
# CONFIG_WATCHDOG_SYSFS is not set
|
||||||
|
|
||||||
|
#
|
||||||
|
# Watchdog Pretimeout Governors
|
||||||
|
#
|
||||||
|
# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set
|
||||||
|
|
||||||
|
#
|
||||||
|
# Watchdog Device Drivers
|
||||||
|
#
|
||||||
|
# CONFIG_SOFT_WATCHDOG is not set
|
||||||
|
# CONFIG_WDAT_WDT is not set
|
||||||
|
# CONFIG_XILINX_WATCHDOG is not set
|
||||||
|
# CONFIG_ARM_SP805_WATCHDOG is not set
|
||||||
|
# CONFIG_ARM_SBSA_WATCHDOG is not set
|
||||||
|
# CONFIG_CADENCE_WATCHDOG is not set
|
||||||
|
# CONFIG_DW_WATCHDOG is not set
|
||||||
|
# CONFIG_MAX63XX_WATCHDOG is not set
|
||||||
|
# CONFIG_ARM_SMC_WATCHDOG is not set
|
||||||
|
# CONFIG_ALIM7101_WDT is not set
|
||||||
|
# CONFIG_I6300ESB_WDT is not set
|
||||||
|
CONFIG_VIRTIO_WDT=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# PCI-based Watchdog Cards
|
||||||
|
#
|
||||||
|
# CONFIG_PCIPCWATCHDOG is not set
|
||||||
|
# CONFIG_WDTPCI is not set
|
||||||
CONFIG_SSB_POSSIBLE=y
|
CONFIG_SSB_POSSIBLE=y
|
||||||
# CONFIG_SSB is not set
|
# CONFIG_SSB is not set
|
||||||
CONFIG_BCMA_POSSIBLE=y
|
CONFIG_BCMA_POSSIBLE=y
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: cloud-hypervisor
|
Name: cloud-hypervisor
|
||||||
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
Summary: Cloud Hypervisor is an open source Virtual Machine Monitor (VMM) that runs on top of KVM.
|
||||||
Version: 0.8.0
|
Version: 0.12.0
|
||||||
Release: 0%{?dist}
|
Release: 0%{?dist}
|
||||||
License: ASL 2.0 or BSD-3-clause
|
License: ASL 2.0 or BSD-3-clause
|
||||||
Group: Applications/System
|
Group: Applications/System
|
||||||
@@ -36,14 +36,12 @@ install -D -m755 ./target/x86_64-unknown-linux-gnu/release/cloud-hypervisor %{b
|
|||||||
install -D -m755 ./target/x86_64-unknown-linux-gnu/release/ch-remote %{buildroot}%{_bindir}
|
install -D -m755 ./target/x86_64-unknown-linux-gnu/release/ch-remote %{buildroot}%{_bindir}
|
||||||
install -d %{buildroot}%{_libdir}
|
install -d %{buildroot}%{_libdir}
|
||||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor
|
install -d %{buildroot}%{_libdir}/cloud-hypervisor
|
||||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_blk %{buildroot}%{_libdir}/cloud-hypervisor
|
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor
|
||||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_fs %{buildroot}%{_libdir}/cloud-hypervisor
|
|
||||||
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
|
install -D -m755 target/x86_64-unknown-linux-gnu/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor
|
||||||
|
|
||||||
install -d %{buildroot}%{_libdir}/cloud-hypervisor/static
|
install -d %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||||
install -D -m755 target/x86_64-unknown-linux-musl/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
|
install -D -m755 target/x86_64-unknown-linux-musl/release/cloud-hypervisor %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_blk %{buildroot}%{_libdir}/cloud-hypervisor/static
|
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_block %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_fs %{buildroot}%{_libdir}/cloud-hypervisor/static
|
|
||||||
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
|
install -D -m755 target/x86_64-unknown-linux-musl/release/vhost_user_net %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||||
install -D -m755 target/x86_64-unknown-linux-musl/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
|
install -D -m755 target/x86_64-unknown-linux-musl/release/ch-remote %{buildroot}%{_libdir}/cloud-hypervisor/static
|
||||||
|
|
||||||
@@ -87,8 +85,6 @@ setcap cap_net_admin+ep %{_bindir}/cloud-hypervisor
|
|||||||
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/vhost_user_net
|
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/vhost_user_net
|
||||||
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/cloud-hypervisor
|
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/cloud-hypervisor
|
||||||
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/vhost_user_net
|
setcap cap_net_admin+ep %{_libdir}/cloud-hypervisor/static/vhost_user_net
|
||||||
setcap cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_mknod,cap_setfcap,cap_sys_admin+epi %{_libdir}/cloud-hypervisor/vhost_user_fs
|
|
||||||
setcap cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_mknod,cap_setfcap,cap_sys_admin+epi %{_libdir}/cloud-hypervisor/static/vhost_user_fs
|
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ cmd_help() {
|
|||||||
echo " --release Build the release binaries."
|
echo " --release Build the release binaries."
|
||||||
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"
|
||||||
|
echo " --hypervisor Underlying hypervisor. Options kvm, mshv"
|
||||||
echo ""
|
echo ""
|
||||||
echo " tests [--unit|--cargo|--all] [--libc musl|gnu] [-- [<cargo test args>]]"
|
echo " tests [--unit|--cargo|--all] [--libc musl|gnu] [-- [<cargo test args>]]"
|
||||||
echo " Run the Cloud Hypervisor tests."
|
echo " Run the Cloud Hypervisor tests."
|
||||||
@@ -185,6 +186,7 @@ cmd_help() {
|
|||||||
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"
|
||||||
|
echo " --hypervisor Underlying hypervisor. Options kvm, mshv"
|
||||||
echo " --all Run all tests."
|
echo " --all Run all tests."
|
||||||
echo ""
|
echo ""
|
||||||
echo " build-container [--type]"
|
echo " build-container [--type]"
|
||||||
@@ -205,12 +207,14 @@ cmd_help() {
|
|||||||
cmd_build() {
|
cmd_build() {
|
||||||
build="debug"
|
build="debug"
|
||||||
libc="gnu"
|
libc="gnu"
|
||||||
|
hypervisor="kvm"
|
||||||
|
features_build=""
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"-h"|"--help") { cmd_help; exit 1; } ;;
|
"-h"|"--help") { cmd_help; exit 1; } ;;
|
||||||
"--debug") { build="debug"; } ;;
|
"--debug") { build="debug"; } ;;
|
||||||
"--release") { build="release"; } ;;
|
"--release") { build="release"; } ;;
|
||||||
"--libc")
|
"--libc")
|
||||||
shift
|
shift
|
||||||
[[ "$1" =~ ^(musl|gnu)$ ]] || \
|
[[ "$1" =~ ^(musl|gnu)$ ]] || \
|
||||||
@@ -221,7 +225,11 @@ cmd_build() {
|
|||||||
shift
|
shift
|
||||||
arg_vols="$1"
|
arg_vols="$1"
|
||||||
;;
|
;;
|
||||||
"--") { shift; break; } ;;
|
"--hypervisor")
|
||||||
|
shift
|
||||||
|
hypervisor="$1"
|
||||||
|
;;
|
||||||
|
"--") { shift; break; } ;;
|
||||||
*)
|
*)
|
||||||
die "Unknown build argument: $1. Please use --help for help."
|
die "Unknown build argument: $1. Please use --help for help."
|
||||||
;;
|
;;
|
||||||
@@ -229,6 +237,9 @@ cmd_build() {
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
process_volumes_args
|
process_volumes_args
|
||||||
|
if [[ "$hypervisor" != "kvm" ]]; then
|
||||||
|
die "Hypervisor value must be kvm"
|
||||||
|
fi
|
||||||
|
|
||||||
target="$(uname -m)-unknown-linux-${libc}"
|
target="$(uname -m)-unknown-linux-${libc}"
|
||||||
|
|
||||||
@@ -251,7 +262,7 @@ cmd_build() {
|
|||||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||||
--env RUSTFLAGS="$rustflags" \
|
--env RUSTFLAGS="$rustflags" \
|
||||||
"$CTR_IMAGE" \
|
"$CTR_IMAGE" \
|
||||||
cargo build --all \
|
cargo build --all $features_build \
|
||||||
--target-dir "$CTR_CLH_CARGO_TARGET" \
|
--target-dir "$CTR_CLH_CARGO_TARGET" \
|
||||||
"${cargo_args[@]}" && say "Binaries placed under $CLH_CARGO_TARGET/$target/$build"
|
"${cargo_args[@]}" && say "Binaries placed under $CLH_CARGO_TARGET/$target/$build"
|
||||||
}
|
}
|
||||||
@@ -278,15 +289,15 @@ cmd_tests() {
|
|||||||
integration_windows=false
|
integration_windows=false
|
||||||
libc="gnu"
|
libc="gnu"
|
||||||
arg_vols=""
|
arg_vols=""
|
||||||
|
hypervisor="kvm"
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"-h"|"--help") { cmd_help; exit 1; } ;;
|
"-h"|"--help") { cmd_help; exit 1; } ;;
|
||||||
"--unit") { unit=true; } ;;
|
"--unit") { unit=true; } ;;
|
||||||
"--cargo") { cargo=true; } ;;
|
"--cargo") { cargo=true; } ;;
|
||||||
"--integration") { integration=true; } ;;
|
"--integration") { integration=true; } ;;
|
||||||
"--integration-sgx") { integration_sgx=true; } ;;
|
"--integration-sgx") { integration_sgx=true; } ;;
|
||||||
"--integration-windows") { integration_windows=true; } ;;
|
"--integration-windows") { integration_windows=true; } ;;
|
||||||
"--libc")
|
"--libc")
|
||||||
shift
|
shift
|
||||||
[[ "$1" =~ ^(musl|gnu)$ ]] || \
|
[[ "$1" =~ ^(musl|gnu)$ ]] || \
|
||||||
@@ -297,14 +308,22 @@ cmd_tests() {
|
|||||||
shift
|
shift
|
||||||
arg_vols="$1"
|
arg_vols="$1"
|
||||||
;;
|
;;
|
||||||
"--all") { cargo=true; unit=true; integration=true; } ;;
|
"--hypervisor")
|
||||||
"--") { shift; break; } ;;
|
shift
|
||||||
|
hypervisor="$1"
|
||||||
|
;;
|
||||||
|
"--all") { cargo=true; unit=true; integration=true; } ;;
|
||||||
|
"--") { shift; break; } ;;
|
||||||
*)
|
*)
|
||||||
die "Unknown tests argument: $1. Please use --help for help."
|
die "Unknown tests argument: $1. Please use --help for help."
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
if [[ "$hypervisor" != "kvm" ]]; then
|
||||||
|
die "Hypervisor value must be kvm"
|
||||||
|
fi
|
||||||
|
set -- "$@" '--hypervisor' $hypervisor
|
||||||
|
|
||||||
process_volumes_args
|
process_volumes_args
|
||||||
target="$(uname -m)-unknown-linux-${libc}"
|
target="$(uname -m)-unknown-linux-${libc}"
|
||||||
@@ -315,7 +334,7 @@ cmd_tests() {
|
|||||||
cflags="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
cflags="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$unit" = true ] ; then
|
if [[ "$unit" = true && $hypervisor = "kvm" ]] ; then
|
||||||
say "Running unit tests for $target..."
|
say "Running unit tests for $target..."
|
||||||
$DOCKER_RUNTIME run \
|
$DOCKER_RUNTIME run \
|
||||||
--workdir "$CTR_CLH_ROOT_DIR" \
|
--workdir "$CTR_CLH_ROOT_DIR" \
|
||||||
@@ -338,7 +357,7 @@ cmd_tests() {
|
|||||||
--rm \
|
--rm \
|
||||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||||
"$CTR_IMAGE" \
|
"$CTR_IMAGE" \
|
||||||
./scripts/run_cargo_tests.sh || fix_dir_perms $? || exit $?
|
./scripts/run_cargo_tests.sh "$@" || fix_dir_perms $? || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$integration" = true ] ; then
|
if [ "$integration" = true ] ; then
|
||||||
@@ -390,7 +409,7 @@ cmd_tests() {
|
|||||||
--net="$CTR_CLH_NET" \
|
--net="$CTR_CLH_NET" \
|
||||||
--mount type=tmpfs,destination=/tmp \
|
--mount type=tmpfs,destination=/tmp \
|
||||||
--volume /dev:/dev \
|
--volume /dev:/dev \
|
||||||
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" \
|
--volume "$CLH_ROOT_DIR:$CTR_CLH_ROOT_DIR" $exported_volumes \
|
||||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||||
--env USER="root" \
|
--env USER="root" \
|
||||||
--env CH_LIBC="${libc}" \
|
--env CH_LIBC="${libc}" \
|
||||||
@@ -405,9 +424,9 @@ cmd_build-container() {
|
|||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"-h"|"--help") { cmd_help; exit 1; } ;;
|
"-h"|"--help") { cmd_help; exit 1; } ;;
|
||||||
"--dev") { container_type="dev"; } ;;
|
"--dev") { container_type="dev"; } ;;
|
||||||
"--") { shift; break; } ;;
|
"--") { shift; break; } ;;
|
||||||
*)
|
*)
|
||||||
die "Unknown build-container argument: $1. Please use --help for help."
|
die "Unknown build-container argument: $1. Please use --help for help."
|
||||||
;;
|
;;
|
||||||
@@ -457,8 +476,8 @@ cmd_shell() {
|
|||||||
#
|
#
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-h|--help) { cmd_help; exit 1; } ;;
|
-h|--help) { cmd_help; exit 1; } ;;
|
||||||
-y|--unattended) { OPT_UNATTENDED=true; } ;;
|
-y|--unattended) { OPT_UNATTENDED=true; } ;;
|
||||||
-*)
|
-*)
|
||||||
die "Unknown arg: $1. Please use \`$0 help\` for help."
|
die "Unknown arg: $1. Please use \`$0 help\` for help."
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set -e
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
|
source $(dirname "$0")/test-util.sh
|
||||||
|
|
||||||
|
process_common_args "$@"
|
||||||
|
|
||||||
# Install cargo components
|
# Install cargo components
|
||||||
time rustup component add clippy
|
time rustup component add clippy
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
|
source $(dirname "$0")/test-util.sh
|
||||||
|
|
||||||
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
|
export BUILD_TARGET=${BUILD_TARGET-aarch64-unknown-linux-gnu}
|
||||||
|
|
||||||
@@ -106,19 +107,25 @@ update_workloads() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SRCDIR=$PWD
|
SRCDIR=$PWD
|
||||||
if [ ! -d "$LINUX_CUSTOM_DIR" ]; then
|
LINUX_CUSTOM_BRANCH="virtio-fs-virtio-iommu-5.8-rc4"
|
||||||
pushd $WORKLOADS_DIR
|
|
||||||
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b "virtio-fs-virtio-iommu-5.8-rc4" $LINUX_CUSTOM_DIR
|
# Check whether the local HEAD commit same as the remote HEAD or not. Remove the folder if they are different.
|
||||||
cp $SRCDIR/resources/linux-config-aarch64 $LINUX_CUSTOM_DIR/.config
|
if [ -d "$LINUX_CUSTOM_DIR" ]; then
|
||||||
popd
|
|
||||||
else
|
|
||||||
pushd $LINUX_CUSTOM_DIR
|
pushd $LINUX_CUSTOM_DIR
|
||||||
git fetch
|
git fetch
|
||||||
git checkout -f "virtio-fs-virtio-iommu-5.8-rc4"
|
LINUX_CUSTOM_LOCAL_HEAD=$(git rev-parse HEAD)
|
||||||
cp $SRCDIR/resources/linux-config-aarch64 $LINUX_CUSTOM_DIR/.config
|
LINUX_CUSTOM_REMOTE_HEAD=$(git rev-parse remotes/origin/$LINUX_CUSTOM_BRANCH)
|
||||||
popd
|
popd
|
||||||
|
if [ "$LINUX_CUSTOM_LOCAL_HEAD" != "$LINUX_CUSTOM_REMOTE_HEAD" ]; then
|
||||||
|
rm -rf "$LINUX_CUSTOM_DIR"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$LINUX_CUSTOM_DIR" ]; then
|
||||||
|
time git clone --depth 1 "https://github.com/cloud-hypervisor/linux.git" -b $LINUX_CUSTOM_BRANCH $LINUX_CUSTOM_DIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp $SRCDIR/resources/linux-config-aarch64 $LINUX_CUSTOM_DIR/.config
|
||||||
build_custom_linux_kernel
|
build_custom_linux_kernel
|
||||||
|
|
||||||
VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
|
VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
|
||||||
@@ -136,6 +143,19 @@ update_workloads() {
|
|||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
VIRTIOFSD_RS="$WORKLOADS_DIR/virtiofsd-rs"
|
||||||
|
VIRTIOFSD_RS_DIR="virtiofsd_rs_build"
|
||||||
|
if [ ! -f "$VIRTIOFSD_RS" ]; then
|
||||||
|
pushd $WORKLOADS_DIR
|
||||||
|
git clone --depth 1 "https://gitlab.com/virtio-fs/virtiofsd-rs.git" $VIRTIOFSD_RS_DIR
|
||||||
|
pushd $VIRTIOFSD_RS_DIR
|
||||||
|
time cargo build --release
|
||||||
|
cp target/release/virtiofsd-rs $VIRTIOFSD_RS || exit 1
|
||||||
|
popd
|
||||||
|
rm -rf $VIRTIOFSD_RS_DIR
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
||||||
MNT_DIR="mount_image"
|
MNT_DIR="mount_image"
|
||||||
if [ ! -f "$BLK_IMAGE" ]; then
|
if [ ! -f "$BLK_IMAGE" ]; then
|
||||||
@@ -158,6 +178,10 @@ update_workloads() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_common_args "$@"
|
||||||
|
features_build="--no-default-features --features kvm "
|
||||||
|
features_test="--no-default-features --features integration_tests,kvm"
|
||||||
|
|
||||||
# lock the workloads folder to avoid parallel updating by different containers
|
# lock the workloads folder to avoid parallel updating by different containers
|
||||||
(
|
(
|
||||||
echo "try to lock $WORKLOADS_DIR folder and update"
|
echo "try to lock $WORKLOADS_DIR folder and update"
|
||||||
@@ -185,7 +209,7 @@ TARGET_CC="musl-gcc"
|
|||||||
CFLAGS="-I /usr/include/aarch64-linux-musl/ -idirafter /usr/include/"
|
CFLAGS="-I /usr/include/aarch64-linux-musl/ -idirafter /usr/include/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cargo build --all --release --no-default-features --features kvm --target $BUILD_TARGET
|
cargo build --all --release $features_build --target $BUILD_TARGET
|
||||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||||
strip target/$BUILD_TARGET/release/ch-remote
|
strip target/$BUILD_TARGET/release/ch-remote
|
||||||
@@ -197,7 +221,7 @@ sudo bash -c "echo 10 > /sys/kernel/mm/ksm/sleep_millisecs"
|
|||||||
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
|
sudo bash -c "echo 1 > /sys/kernel/mm/ksm/run"
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
time cargo test --no-default-features --features "integration_tests,kvm" "tests::parallel::$@" -- --skip test_snapshot_restore
|
time cargo test $features_test "tests::parallel::"
|
||||||
RES=$?
|
RES=$?
|
||||||
|
|
||||||
# Tear vhost_user_net test network down
|
# Tear vhost_user_net test network down
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
source $HOME/.cargo/env
|
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}"
|
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
@@ -11,12 +17,12 @@ 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
|
||||||
|
|
||||||
cargo build --all --release --target $BUILD_TARGET
|
cargo build --all --release $features_build --target $BUILD_TARGET
|
||||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
|
|
||||||
time cargo test --features "integration_tests" "tests::sgx::$@"
|
time cargo test $features_test "tests::sgx::"
|
||||||
RES=$?
|
RES=$?
|
||||||
|
|
||||||
exit $RES
|
exit $RES
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
source $HOME/.cargo/env
|
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}"
|
BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||||
CFLAGS=""
|
CFLAGS=""
|
||||||
@@ -11,14 +17,14 @@ 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
|
||||||
|
|
||||||
cargo build --all --release --target $BUILD_TARGET
|
cargo build --all --release $features_build --target $BUILD_TARGET
|
||||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
|
|
||||||
# Only run with 1 thread to avoid tests interfering with one another because
|
# Only run with 1 thread to avoid tests interfering with one another because
|
||||||
# Windows has a static IP configured
|
# Windows has a static IP configured
|
||||||
time cargo test --features "integration_tests" "tests::windows::$@" -- --test-threads=1
|
time cargo test $features_test "tests::windows::" -- --test-threads=1
|
||||||
RES=$?
|
RES=$?
|
||||||
|
|
||||||
exit $RES
|
exit $RES
|
||||||
|
|||||||
@@ -2,12 +2,20 @@
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
|
source $(dirname "$0")/test-util.sh
|
||||||
|
|
||||||
export BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
export BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
||||||
|
|
||||||
WORKLOADS_DIR="$HOME/workloads"
|
WORKLOADS_DIR="$HOME/workloads"
|
||||||
mkdir -p "$WORKLOADS_DIR"
|
mkdir -p "$WORKLOADS_DIR"
|
||||||
|
|
||||||
|
|
||||||
|
process_common_args "$@"
|
||||||
|
|
||||||
|
# For now these values are deafult for kvm
|
||||||
|
features_build=""
|
||||||
|
features_test="--features integration_tests"
|
||||||
|
|
||||||
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
|
||||||
|
|
||||||
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
|
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
|
||||||
@@ -137,6 +145,20 @@ if [ ! -f "$VIRTIOFSD" ]; then
|
|||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
VIRTIOFSD_RS="$WORKLOADS_DIR/virtiofsd-rs"
|
||||||
|
VIRTIOFSD_RS_DIR="virtiofsd_rs_build"
|
||||||
|
if [ ! -f "$VIRTIOFSD_RS" ]; then
|
||||||
|
pushd $WORKLOADS_DIR
|
||||||
|
git clone --depth 1 "https://gitlab.com/virtio-fs/virtiofsd-rs.git" $VIRTIOFSD_RS_DIR
|
||||||
|
pushd $VIRTIOFSD_RS_DIR
|
||||||
|
time cargo build --release
|
||||||
|
cp target/release/virtiofsd-rs $VIRTIOFSD_RS || exit 1
|
||||||
|
popd
|
||||||
|
rm -rf $VIRTIOFSD_RS_DIR
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
BLK_IMAGE="$WORKLOADS_DIR/blk.img"
|
||||||
MNT_DIR="mount_image"
|
MNT_DIR="mount_image"
|
||||||
if [ ! -f "$BLK_IMAGE" ]; then
|
if [ ! -f "$BLK_IMAGE" ]; then
|
||||||
@@ -201,7 +223,7 @@ 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
|
||||||
|
|
||||||
cargo build --all --release --target $BUILD_TARGET
|
cargo build --all --release $features_build --target $BUILD_TARGET
|
||||||
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
strip target/$BUILD_TARGET/release/cloud-hypervisor
|
||||||
strip target/$BUILD_TARGET/release/vhost_user_net
|
strip target/$BUILD_TARGET/release/vhost_user_net
|
||||||
strip target/$BUILD_TARGET/release/ch-remote
|
strip target/$BUILD_TARGET/release/ch-remote
|
||||||
@@ -221,14 +243,14 @@ echo 4096 | sudo tee /proc/sys/vm/nr_hugepages
|
|||||||
sudo chmod a+rwX /dev/hugepages
|
sudo chmod a+rwX /dev/hugepages
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
time cargo test --features "integration_tests" "tests::parallel::$@"
|
time cargo test $features_test "tests::parallel::"
|
||||||
RES=$?
|
RES=$?
|
||||||
|
|
||||||
# Run some tests in sequence since the result could be affected by other tests
|
# Run some tests in sequence since the result could be affected by other tests
|
||||||
# running in parallel.
|
# running in parallel.
|
||||||
if [ $RES -eq 0 ]; then
|
if [ $RES -eq 0 ]; then
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
time cargo test --features "integration_tests" "tests::sequential::$@" -- --test-threads=1
|
time cargo test $features_test "tests::sequential::" -- --test-threads=1
|
||||||
RES=$?
|
RES=$?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
|
source $(dirname "$0")/test-util.sh
|
||||||
|
|
||||||
|
process_common_args "$@"
|
||||||
|
|
||||||
BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
BUILD_TARGET=${BUILD_TARGET-x86_64-unknown-linux-gnu}
|
||||||
cargo_args=("$@")
|
cargo_args=("")
|
||||||
[ $(uname -m) = "aarch64" ] && cargo_args+=("--no-default-features")
|
[ $(uname -m) = "aarch64" ] && cargo_args+=("--no-default-features")
|
||||||
[ $(uname -m) = "aarch64" ] && cargo_args+=("--features kvm")
|
[ $(uname -m) = "aarch64" ] && cargo_args+=("--features kvm")
|
||||||
|
|
||||||
|
|||||||
34
scripts/test-util.sh
Normal file
34
scripts/test-util.sh
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
hypervisor="kvm"
|
||||||
|
cmd_help() {
|
||||||
|
echo ""
|
||||||
|
echo "Cloud Hypervisor $(basename $0)"
|
||||||
|
echo "Usage: $(basename $0) [<args>]"
|
||||||
|
echo ""
|
||||||
|
echo "Available arguments:"
|
||||||
|
echo ""
|
||||||
|
echo " --hypervisor Underlying hypervisor. Options kvm, mshv"
|
||||||
|
echo ""
|
||||||
|
echo " --help Display this help message."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
process_common_args() {
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
"-h"|"--help") { cmd_help; exit 1; } ;;
|
||||||
|
"--hypervisor")
|
||||||
|
shift
|
||||||
|
hypervisor="$1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# We only care about hypervisor , do nothing for other arguments
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
if [[ "$hypervisor" != "kvm" ]]; then
|
||||||
|
echo "Hypervisor value must be kvm"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ impl fmt::Display for Error {
|
|||||||
use Error::*;
|
use Error::*;
|
||||||
match self {
|
match self {
|
||||||
ApiClient(e) => e.fmt(f),
|
ApiClient(e) => e.fmt(f),
|
||||||
Connect(e) => write!(f, "Error openning 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),
|
||||||
@@ -230,6 +230,32 @@ fn restore_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Erro
|
|||||||
.map_err(Error::ApiClient)
|
.map_err(Error::ApiClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
|
||||||
|
let receive_migration_data = vmm::api::VmReceiveMigrationData {
|
||||||
|
receiver_url: url.to_owned(),
|
||||||
|
};
|
||||||
|
simple_api_command(
|
||||||
|
socket,
|
||||||
|
"PUT",
|
||||||
|
"receive-migration",
|
||||||
|
Some(&serde_json::to_string(&receive_migration_data).unwrap()),
|
||||||
|
)
|
||||||
|
.map_err(Error::ApiClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
|
||||||
|
let send_migration_data = vmm::api::VmSendMigrationData {
|
||||||
|
destination_url: url.to_owned(),
|
||||||
|
};
|
||||||
|
simple_api_command(
|
||||||
|
socket,
|
||||||
|
"PUT",
|
||||||
|
"send-migration",
|
||||||
|
Some(&serde_json::to_string(&send_migration_data).unwrap()),
|
||||||
|
)
|
||||||
|
.map_err(Error::ApiClient)
|
||||||
|
}
|
||||||
|
|
||||||
fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
||||||
let mut socket =
|
let mut socket =
|
||||||
UnixStream::connect(matches.value_of("api-socket").unwrap()).map_err(Error::Connect)?;
|
UnixStream::connect(matches.value_of("api-socket").unwrap()).map_err(Error::Connect)?;
|
||||||
@@ -341,6 +367,22 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> {
|
|||||||
.value_of("restore_config")
|
.value_of("restore_config")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
|
Some("send-migration") => send_migration_api_command(
|
||||||
|
&mut socket,
|
||||||
|
matches
|
||||||
|
.subcommand_matches("send-migration")
|
||||||
|
.unwrap()
|
||||||
|
.value_of("send_migration_config")
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
Some("receive-migration") => receive_migration_api_command(
|
||||||
|
&mut socket,
|
||||||
|
matches
|
||||||
|
.subcommand_matches("receive-migration")
|
||||||
|
.unwrap()
|
||||||
|
.value_of("receive_migration_config")
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
Some(c) => simple_api_command(&mut socket, "PUT", c, None).map_err(Error::ApiClient),
|
Some(c) => simple_api_command(&mut socket, "PUT", c, None).map_err(Error::ApiClient),
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
}
|
}
|
||||||
@@ -484,6 +526,24 @@ fn main() {
|
|||||||
.index(1)
|
.index(1)
|
||||||
.help(vmm::config::RestoreConfig::SYNTAX),
|
.help(vmm::config::RestoreConfig::SYNTAX),
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("send-migration")
|
||||||
|
.about("Initiate a VM migration")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("send_migration_config")
|
||||||
|
.index(1)
|
||||||
|
.help("<destination_url>"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("receive-migration")
|
||||||
|
.about("Receive a VM migration")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("receive_migration_config")
|
||||||
|
.index(1)
|
||||||
|
.help("<receiver_url>"),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let matches = app.get_matches();
|
let matches = app.get_matches();
|
||||||
|
|||||||
57
src/main.rs
57
src/main.rs
@@ -18,8 +18,6 @@ use std::env;
|
|||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vhost_user_block::start_block_backend;
|
|
||||||
use vhost_user_net::start_net_backend;
|
|
||||||
use vmm::config;
|
use vmm::config;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -27,7 +25,14 @@ use vmm_sys_util::eventfd::EventFd;
|
|||||||
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),
|
||||||
#[error("Failed to open hypervisor interface (is /dev/kvm available?): {0}")]
|
#[cfg_attr(
|
||||||
|
feature = "kvm",
|
||||||
|
error("Failed to open hypervisor interface (is /dev/kvm available?): {0}")
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "mshv",
|
||||||
|
error("Failed to open hypervisor interface (is /dev/mshv available?): {0}")
|
||||||
|
)]
|
||||||
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),
|
||||||
@@ -307,22 +312,6 @@ fn create_app<'a, 'b>(
|
|||||||
.min_values(1)
|
.min_values(1)
|
||||||
.group("vmm-config"),
|
.group("vmm-config"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name("net-backend")
|
|
||||||
.long("net-backend")
|
|
||||||
.help(vhost_user_net::SYNTAX)
|
|
||||||
.takes_value(true)
|
|
||||||
.conflicts_with_all(&["block-backend", "kernel"])
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("block-backend")
|
|
||||||
.long("block-backend")
|
|
||||||
.help(vhost_user_block::SYNTAX)
|
|
||||||
.takes_value(true)
|
|
||||||
.conflicts_with_all(&["net-backend", "kernel"])
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("seccomp")
|
Arg::with_name("seccomp")
|
||||||
.long("seccomp")
|
.long("seccomp")
|
||||||
@@ -471,23 +460,17 @@ 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");
|
||||||
|
|
||||||
if let Some(backend_command) = cmd_arguments.value_of("net-backend") {
|
let api_socket_path = cmd_arguments
|
||||||
start_net_backend(backend_command);
|
.value_of("api-socket")
|
||||||
} else if let Some(backend_command) = cmd_arguments.value_of("block-backend") {
|
.expect("Missing argument: api-socket")
|
||||||
start_block_backend(backend_command);
|
.to_string();
|
||||||
} else {
|
|
||||||
let api_socket_path = cmd_arguments
|
|
||||||
.value_of("api-socket")
|
|
||||||
.expect("Missing argument: api-socket")
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
if let Err(e) = start_vmm(cmd_arguments, &api_socket_path) {
|
if let Err(e) = start_vmm(cmd_arguments, &api_socket_path) {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
std::fs::remove_file(api_socket_path).ok();
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
std::fs::remove_file(api_socket_path).ok();
|
std::fs::remove_file(api_socket_path).ok();
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
std::fs::remove_file(api_socket_path).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -812,14 +795,12 @@ mod unit_tests {
|
|||||||
"shared=true",
|
"shared=true",
|
||||||
"--disk",
|
"--disk",
|
||||||
"vhost_user=true,socket=/tmp/sock1",
|
"vhost_user=true,socket=/tmp/sock1",
|
||||||
"path=/path/to/disk/2",
|
|
||||||
],
|
],
|
||||||
r#"{
|
r#"{
|
||||||
"kernel": {"path": "/path/to/kernel"},
|
"kernel": {"path": "/path/to/kernel"},
|
||||||
"memory" : { "shared": true, "size": 536870912 },
|
"memory" : { "shared": true, "size": 536870912 },
|
||||||
"disks": [
|
"disks": [
|
||||||
{"vhost_user":true, "vhost_socket":"/tmp/sock1"},
|
{"vhost_user":true, "vhost_socket":"/tmp/sock1"}
|
||||||
{"path": "/path/to/disk/2"}
|
|
||||||
]
|
]
|
||||||
}"#,
|
}"#,
|
||||||
true,
|
true,
|
||||||
@@ -833,14 +814,12 @@ mod unit_tests {
|
|||||||
"shared=true",
|
"shared=true",
|
||||||
"--disk",
|
"--disk",
|
||||||
"vhost_user=true,socket=/tmp/sock1",
|
"vhost_user=true,socket=/tmp/sock1",
|
||||||
"path=/path/to/disk/2",
|
|
||||||
],
|
],
|
||||||
r#"{
|
r#"{
|
||||||
"kernel": {"path": "/path/to/kernel"},
|
"kernel": {"path": "/path/to/kernel"},
|
||||||
"memory" : { "shared": true, "size": 536870912 },
|
"memory" : { "shared": true, "size": 536870912 },
|
||||||
"disks": [
|
"disks": [
|
||||||
{"vhost_user":true, "vhost_socket":"/tmp/sock1"},
|
{"vhost_user":true, "vhost_socket":"/tmp/sock1"}
|
||||||
{"path": "/path/to/disk/2"}
|
|
||||||
]
|
]
|
||||||
}"#,
|
}"#,
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ mod tests {
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
#[cfg_attr(target_arch = "aarch64", allow(unused_imports))]
|
||||||
use wait_timeout::ChildExt;
|
use wait_timeout::ChildExt;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -348,16 +349,23 @@ mod tests {
|
|||||||
(child, virtiofsd_socket_path)
|
(child, virtiofsd_socket_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_vhost_user_fs_daemon(
|
fn prepare_virtofsd_rs_daemon(
|
||||||
tmp_dir: &TempDir,
|
tmp_dir: &TempDir,
|
||||||
shared_dir: &str,
|
shared_dir: &str,
|
||||||
_cache: &str,
|
_cache: &str,
|
||||||
) -> (std::process::Child, String) {
|
) -> (std::process::Child, String) {
|
||||||
|
let mut workload_path = dirs::home_dir().unwrap();
|
||||||
|
workload_path.push("workloads");
|
||||||
|
|
||||||
|
let mut virtiofsd_path = workload_path;
|
||||||
|
virtiofsd_path.push("virtiofsd-rs");
|
||||||
|
let virtiofsd_path = String::from(virtiofsd_path.to_str().unwrap());
|
||||||
|
|
||||||
let virtiofsd_socket_path =
|
let virtiofsd_socket_path =
|
||||||
String::from(tmp_dir.path().join("virtiofs.sock").to_str().unwrap());
|
String::from(tmp_dir.path().join("virtiofs.sock").to_str().unwrap());
|
||||||
|
|
||||||
// Start the daemon
|
// Start the daemon
|
||||||
let child = Command::new(clh_command("vhost_user_fs"))
|
let child = Command::new(virtiofsd_path.as_str())
|
||||||
.args(&["--shared-dir", shared_dir])
|
.args(&["--shared-dir", shared_dir])
|
||||||
.args(&["--socket", virtiofsd_socket_path.as_str()])
|
.args(&["--socket", virtiofsd_socket_path.as_str()])
|
||||||
.spawn()
|
.spawn()
|
||||||
@@ -385,7 +393,7 @@ mod tests {
|
|||||||
let vubd_socket_path = String::from(tmp_dir.path().join("vub.sock").to_str().unwrap());
|
let vubd_socket_path = String::from(tmp_dir.path().join("vub.sock").to_str().unwrap());
|
||||||
|
|
||||||
// Start the daemon
|
// Start the daemon
|
||||||
let child = Command::new(clh_command("cloud-hypervisor"))
|
let child = Command::new(clh_command("vhost_user_block"))
|
||||||
.args(&[
|
.args(&[
|
||||||
"--block-backend",
|
"--block-backend",
|
||||||
format!(
|
format!(
|
||||||
@@ -460,7 +468,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let child = Command::new(clh_command("cloud-hypervisor"))
|
let child = Command::new(clh_command("vhost_user_net"))
|
||||||
.args(&["--net-backend", net_params.as_str()])
|
.args(&["--net-backend", net_params.as_str()])
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -856,10 +864,20 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.expect("Cannot add 'tcp_listener' event to epoll");
|
.expect("Cannot add 'tcp_listener' event to epoll");
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 1];
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 1];
|
||||||
let num_events = epoll::wait(epoll_fd, timeout * 1000 as i32, &mut events[..])
|
loop {
|
||||||
.map_err(Error::EpollWait)?;
|
let num_events =
|
||||||
if num_events == 0 {
|
match epoll::wait(epoll_fd, timeout * 1000 as i32, &mut events[..]) {
|
||||||
return Err(Error::EpollWaitTimeout);
|
Ok(num_events) => Ok(num_events),
|
||||||
|
Err(e) => match e.raw_os_error() {
|
||||||
|
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.map_err(Error::EpollWait)?;
|
||||||
|
if num_events == 0 {
|
||||||
|
return Err(Error::EpollWaitTimeout);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
match listener.accept() {
|
match listener.accept() {
|
||||||
@@ -1289,7 +1307,6 @@ mod tests {
|
|||||||
tap: Option<&str>,
|
tap: Option<&str>,
|
||||||
num_queues: usize,
|
num_queues: usize,
|
||||||
prepare_vhost_user_net_daemon: Option<&PrepareNetDaemon>,
|
prepare_vhost_user_net_daemon: Option<&PrepareNetDaemon>,
|
||||||
self_spawned: bool,
|
|
||||||
generate_host_mac: bool,
|
generate_host_mac: bool,
|
||||||
) {
|
) {
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
@@ -1307,20 +1324,7 @@ mod tests {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let (net_params, daemon_child) = if self_spawned {
|
let (net_params, daemon_child) = {
|
||||||
(
|
|
||||||
format!(
|
|
||||||
"vhost_user=true,mac={},ip={},mask=255.255.255.0,num_queues={},queue_size=1024{}",
|
|
||||||
guest.network.guest_mac, guest.network.host_ip, num_queues,
|
|
||||||
if let Some(host_mac) =host_mac {
|
|
||||||
format!(",host_mac={}", host_mac)
|
|
||||||
} else {
|
|
||||||
"".to_owned()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let prepare_daemon = prepare_vhost_user_net_daemon.unwrap();
|
let prepare_daemon = prepare_vhost_user_net_daemon.unwrap();
|
||||||
// Start the daemon
|
// Start the daemon
|
||||||
let (daemon_child, vunet_socket_path) =
|
let (daemon_child, vunet_socket_path) =
|
||||||
@@ -1456,7 +1460,6 @@ mod tests {
|
|||||||
readonly: bool,
|
readonly: bool,
|
||||||
direct: bool,
|
direct: bool,
|
||||||
prepare_vhost_user_blk_daemon: Option<&PrepareBlkDaemon>,
|
prepare_vhost_user_blk_daemon: Option<&PrepareBlkDaemon>,
|
||||||
self_spawned: bool,
|
|
||||||
) {
|
) {
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(&mut focal);
|
let guest = Guest::new(&mut focal);
|
||||||
@@ -1467,19 +1470,7 @@ mod tests {
|
|||||||
|
|
||||||
let kernel_path = direct_kernel_boot_path().unwrap();
|
let kernel_path = direct_kernel_boot_path().unwrap();
|
||||||
|
|
||||||
let (blk_params, daemon_child) = if self_spawned {
|
let (blk_params, daemon_child) = {
|
||||||
let mut blk_file_path = workload_path;
|
|
||||||
blk_file_path.push("blk.img");
|
|
||||||
let blk_file_path = String::from(blk_file_path.to_str().unwrap());
|
|
||||||
|
|
||||||
(
|
|
||||||
format!(
|
|
||||||
"vhost_user=true,path={},num_queues={},queue_size=128",
|
|
||||||
blk_file_path, num_queues,
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let prepare_daemon = prepare_vhost_user_blk_daemon.unwrap();
|
let prepare_daemon = prepare_vhost_user_blk_daemon.unwrap();
|
||||||
// Start the daemon
|
// Start the daemon
|
||||||
let (daemon_child, vubd_socket_path) =
|
let (daemon_child, vubd_socket_path) =
|
||||||
@@ -1629,7 +1620,6 @@ mod tests {
|
|||||||
readonly: bool,
|
readonly: bool,
|
||||||
direct: bool,
|
direct: bool,
|
||||||
prepare_vhost_user_blk_daemon: Option<&PrepareBlkDaemon>,
|
prepare_vhost_user_blk_daemon: Option<&PrepareBlkDaemon>,
|
||||||
self_spawned: bool,
|
|
||||||
) {
|
) {
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(&mut focal);
|
let guest = Guest::new(&mut focal);
|
||||||
@@ -1644,15 +1634,7 @@ mod tests {
|
|||||||
.disk(DiskType::RawOperatingSystem)
|
.disk(DiskType::RawOperatingSystem)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (blk_boot_params, daemon_child) = if self_spawned {
|
let (blk_boot_params, daemon_child) = {
|
||||||
(
|
|
||||||
format!(
|
|
||||||
"vhost_user=true,path={},num_queues={},queue_size=128",
|
|
||||||
disk_path, num_queues,
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let prepare_daemon = prepare_vhost_user_blk_daemon.unwrap();
|
let prepare_daemon = prepare_vhost_user_blk_daemon.unwrap();
|
||||||
// Start the daemon
|
// Start the daemon
|
||||||
let (daemon_child, vubd_socket_path) = prepare_daemon(
|
let (daemon_child, vubd_socket_path) = prepare_daemon(
|
||||||
@@ -1697,28 +1679,6 @@ mod tests {
|
|||||||
// Just check the VM booted correctly.
|
// Just check the VM booted correctly.
|
||||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), num_queues as u32);
|
assert_eq!(guest.get_cpu_count().unwrap_or_default(), num_queues as u32);
|
||||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||||
|
|
||||||
if self_spawned {
|
|
||||||
let reboot_count = guest
|
|
||||||
.ssh_command("sudo journalctl | grep -c -- \"-- Reboot --\"")
|
|
||||||
.unwrap_or_default()
|
|
||||||
.trim()
|
|
||||||
.parse::<u32>()
|
|
||||||
.unwrap_or(1);
|
|
||||||
|
|
||||||
assert_eq!(reboot_count, 0);
|
|
||||||
guest.ssh_command("sudo reboot").unwrap_or_default();
|
|
||||||
|
|
||||||
guest.wait_vm_boot(None).unwrap();
|
|
||||||
|
|
||||||
let reboot_count = guest
|
|
||||||
.ssh_command("sudo journalctl | grep -c -- \"-- Reboot --\"")
|
|
||||||
.unwrap_or_default()
|
|
||||||
.trim()
|
|
||||||
.parse::<u32>()
|
|
||||||
.unwrap_or_default();
|
|
||||||
assert_eq!(reboot_count, 1);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
let output = child.wait_with_output().unwrap();
|
let output = child.wait_with_output().unwrap();
|
||||||
@@ -1855,7 +1815,7 @@ mod tests {
|
|||||||
let desired_ram = 1024 << 20;
|
let desired_ram = 1024 << 20;
|
||||||
resize_command(&api_socket, None, Some(desired_ram), None);
|
resize_command(&api_socket, None, Some(desired_ram), None);
|
||||||
|
|
||||||
thread::sleep(std::time::Duration::new(10, 0));
|
thread::sleep(std::time::Duration::new(30, 0));
|
||||||
assert!(guest.get_total_memory().unwrap_or_default() > 960_000);
|
assert!(guest.get_total_memory().unwrap_or_default() > 960_000);
|
||||||
|
|
||||||
// After the resize, check again that file1 exists and its
|
// After the resize, check again that file1 exists and its
|
||||||
@@ -2468,6 +2428,8 @@ mod tests {
|
|||||||
cmd.args(&["--cpus", "boot=48"])
|
cmd.args(&["--cpus", "boot=48"])
|
||||||
.args(&["--memory", "size=5120M"])
|
.args(&["--memory", "size=5120M"])
|
||||||
.args(&["--kernel", guest.fw_path.as_str()])
|
.args(&["--kernel", guest.fw_path.as_str()])
|
||||||
|
.args(&["--serial", "tty"])
|
||||||
|
.args(&["--console", "off"])
|
||||||
.capture_output()
|
.capture_output()
|
||||||
.default_disks()
|
.default_disks()
|
||||||
.default_net();
|
.default_net();
|
||||||
@@ -2910,8 +2872,7 @@ mod tests {
|
|||||||
handle_child_output(r, &output);
|
handle_child_output(r, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn _test_virtio_block(disable_io_uring: bool) {
|
||||||
fn test_virtio_blk() {
|
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(&mut focal);
|
let guest = Guest::new(&mut focal);
|
||||||
|
|
||||||
@@ -2941,8 +2902,9 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
format!(
|
format!(
|
||||||
"path={},readonly=on,direct=on,num_queues=4",
|
"path={},readonly=on,direct=on,num_queues=4,_disable_io_uring={}",
|
||||||
blk_file_path.to_str().unwrap()
|
blk_file_path.to_str().unwrap(),
|
||||||
|
disable_io_uring
|
||||||
)
|
)
|
||||||
.as_str(),
|
.as_str(),
|
||||||
])
|
])
|
||||||
@@ -2994,9 +2956,19 @@ mod tests {
|
|||||||
handle_child_output(r, &output);
|
handle_child_output(r, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_virtio_block() {
|
||||||
|
_test_virtio_block(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_virtio_block_disable_io_uring() {
|
||||||
|
_test_virtio_block(false)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vhost_user_net_default() {
|
fn test_vhost_user_net_default() {
|
||||||
test_vhost_user_net(None, 2, Some(&prepare_vhost_user_net_daemon), false, false)
|
test_vhost_user_net(None, 2, Some(&prepare_vhost_user_net_daemon), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3006,7 +2978,6 @@ mod tests {
|
|||||||
2,
|
2,
|
||||||
Some(&prepare_vhost_user_net_daemon),
|
Some(&prepare_vhost_user_net_daemon),
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3017,13 +2988,12 @@ mod tests {
|
|||||||
2,
|
2,
|
||||||
Some(&prepare_vhost_user_net_daemon),
|
Some(&prepare_vhost_user_net_daemon),
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vhost_user_net_multiple_queues() {
|
fn test_vhost_user_net_multiple_queues() {
|
||||||
test_vhost_user_net(None, 4, Some(&prepare_vhost_user_net_daemon), false, false)
|
test_vhost_user_net(None, 4, Some(&prepare_vhost_user_net_daemon), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3033,60 +3003,37 @@ mod tests {
|
|||||||
4,
|
4,
|
||||||
Some(&prepare_vhost_user_net_daemon),
|
Some(&prepare_vhost_user_net_daemon),
|
||||||
false,
|
false,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vhost_user_net_self_spawning() {
|
|
||||||
test_vhost_user_net(None, 4, None, true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vhost_user_net_self_spawning_host_mac() {
|
|
||||||
test_vhost_user_net(None, 2, None, true, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_vhost_user_net_host_mac() {
|
fn test_vhost_user_net_host_mac() {
|
||||||
test_vhost_user_net(None, 2, None, true, true)
|
test_vhost_user_net(None, 2, Some(&prepare_vhost_user_net_daemon), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_vhost_user_blk_default() {
|
fn test_vhost_user_blk_default() {
|
||||||
test_vhost_user_blk(2, false, false, Some(&prepare_vubd), false)
|
test_vhost_user_blk(2, false, false, Some(&prepare_vubd))
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_vhost_user_blk_self_spawning() {
|
|
||||||
test_vhost_user_blk(1, false, false, None, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_vhost_user_blk_readonly() {
|
fn test_vhost_user_blk_readonly() {
|
||||||
test_vhost_user_blk(1, true, false, Some(&prepare_vubd), false)
|
test_vhost_user_blk(1, true, false, Some(&prepare_vubd))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_vhost_user_blk_direct() {
|
fn test_vhost_user_blk_direct() {
|
||||||
test_vhost_user_blk(1, false, true, Some(&prepare_vubd), false)
|
test_vhost_user_blk(1, false, true, Some(&prepare_vubd))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_boot_from_vhost_user_blk_default() {
|
fn test_boot_from_vhost_user_blk_default() {
|
||||||
test_boot_from_vhost_user_blk(1, false, false, Some(&prepare_vubd), false)
|
test_boot_from_vhost_user_blk(1, false, false, Some(&prepare_vubd))
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_boot_from_vhost_user_blk_self_spawning() {
|
|
||||||
test_boot_from_vhost_user_blk(1, false, false, None, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3150,24 +3097,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_virtio_fs_dax_on_default_cache_size_w_vhost_user_fs_daemon() {
|
fn test_virtio_fs_dax_on_default_cache_size_w_virtiofsd_rs_daemon() {
|
||||||
test_virtio_fs(true, None, "none", &prepare_vhost_user_fs_daemon, false)
|
test_virtio_fs(true, None, "none", &prepare_virtofsd_rs_daemon, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_virtio_fs_dax_on_cache_size_1_gib_w_vhost_user_fs_daemon() {
|
fn test_virtio_fs_dax_on_cache_size_1_gib_w_virtiofsd_rs_daemon() {
|
||||||
test_virtio_fs(
|
test_virtio_fs(
|
||||||
true,
|
true,
|
||||||
Some(0x4000_0000),
|
Some(0x4000_0000),
|
||||||
"none",
|
"none",
|
||||||
&prepare_vhost_user_fs_daemon,
|
&prepare_virtofsd_rs_daemon,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_virtio_fs_dax_off_w_vhost_user_fs_daemon() {
|
fn test_virtio_fs_dax_off_w_virtiofsd_rs_daemon() {
|
||||||
test_virtio_fs(false, None, "none", &prepare_vhost_user_fs_daemon, false)
|
test_virtio_fs(false, None, "none", &prepare_virtofsd_rs_daemon, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3184,14 +3131,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_virtio_fs_hotplug_dax_on_w_vhost_user_fs_daemon() {
|
fn test_virtio_fs_hotplug_dax_on_w_virtiofsd_rs_daemon() {
|
||||||
test_virtio_fs(true, None, "none", &prepare_vhost_user_fs_daemon, true)
|
test_virtio_fs(true, None, "none", &prepare_virtofsd_rs_daemon, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_virtio_fs_hotplug_dax_off_w_vhost_user_fs_daemon() {
|
fn test_virtio_fs_hotplug_dax_off_w_virtiofsd_rs_daemon() {
|
||||||
test_virtio_fs(false, None, "none", &prepare_vhost_user_fs_daemon, true)
|
test_virtio_fs(false, None, "none", &prepare_virtofsd_rs_daemon, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3913,7 +3860,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_reboot() {
|
fn test_reboot() {
|
||||||
let mut bionic = UbuntuDiskConfig::new(BIONIC_IMAGE_NAME.to_string());
|
let mut bionic = UbuntuDiskConfig::new(BIONIC_IMAGE_NAME.to_string());
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
@@ -3926,16 +3872,18 @@ mod tests {
|
|||||||
.for_each(|disk_config| {
|
.for_each(|disk_config| {
|
||||||
let guest = Guest::new(*disk_config);
|
let guest = Guest::new(*disk_config);
|
||||||
|
|
||||||
let mut child = GuestCommand::new(&guest)
|
let mut cmd = GuestCommand::new(&guest);
|
||||||
.args(&["--cpus", "boot=1"])
|
cmd.args(&["--cpus", "boot=1"])
|
||||||
.args(&["--memory", "size=512M"])
|
.args(&["--memory", "size=512M"])
|
||||||
.args(&["--kernel", guest.fw_path.as_str()])
|
.args(&["--kernel", guest.fw_path.as_str()])
|
||||||
.default_raw_disks()
|
.default_raw_disks()
|
||||||
.default_net()
|
.default_net()
|
||||||
.args(&["--serial", "tty", "--console", "off"])
|
.capture_output();
|
||||||
.capture_output()
|
|
||||||
.spawn()
|
#[cfg(target_arch = "aarch64")]
|
||||||
.unwrap();
|
cmd.args(&["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE]);
|
||||||
|
|
||||||
|
let mut child = cmd.spawn().unwrap();
|
||||||
|
|
||||||
let r = std::panic::catch_unwind(|| {
|
let r = std::panic::catch_unwind(|| {
|
||||||
guest.wait_vm_boot(Some(120)).unwrap();
|
guest.wait_vm_boot(Some(120)).unwrap();
|
||||||
@@ -5247,6 +5195,7 @@ mod tests {
|
|||||||
// through each ssh command. There's no need to perform a dedicated test to
|
// through each ssh command. There's no need to perform a dedicated test to
|
||||||
// verify the migration went well for virtio-net.
|
// verify the migration went well for virtio-net.
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn test_snapshot_restore() {
|
fn test_snapshot_restore() {
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(&mut focal);
|
let guest = Guest::new(&mut focal);
|
||||||
@@ -5254,10 +5203,7 @@ mod tests {
|
|||||||
workload_path.push("workloads");
|
workload_path.push("workloads");
|
||||||
|
|
||||||
let mut kernel_path = workload_path;
|
let mut kernel_path = workload_path;
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
kernel_path.push("bzImage");
|
kernel_path.push("bzImage");
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
kernel_path.push("Image");
|
|
||||||
|
|
||||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||||
|
|
||||||
@@ -5459,7 +5405,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_watchdog() {
|
fn test_watchdog() {
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(&mut focal);
|
let guest = Guest::new(&mut focal);
|
||||||
@@ -5539,7 +5484,7 @@ mod tests {
|
|||||||
guest.ssh_command("screen -dmS reboot sh -c \"sleep 5; echo s | tee /proc/sysrq-trigger; echo c | sudo tee /proc/sysrq-trigger\"").unwrap();
|
guest.ssh_command("screen -dmS reboot sh -c \"sleep 5; echo s | tee /proc/sysrq-trigger; echo c | sudo tee /proc/sysrq-trigger\"").unwrap();
|
||||||
|
|
||||||
// Allow some time for the watchdog to trigger (max 30s) and reboot to happen
|
// Allow some time for the watchdog to trigger (max 30s) and reboot to happen
|
||||||
thread::sleep(std::time::Duration::new(50, 0));
|
guest.wait_vm_boot(Some(50)).unwrap();
|
||||||
|
|
||||||
// Check that watchdog triggered reboot
|
// Check that watchdog triggered reboot
|
||||||
let boot_count = guest
|
let boot_count = guest
|
||||||
@@ -5550,19 +5495,22 @@ mod tests {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
assert_eq!(boot_count, 3);
|
assert_eq!(boot_count, 3);
|
||||||
|
|
||||||
// Now pause the VM and remain offline for 30s
|
#[cfg(target_arch = "x86_64")]
|
||||||
assert!(remote_command(&api_socket, "pause", None));
|
{
|
||||||
thread::sleep(std::time::Duration::new(30, 0));
|
// Now pause the VM and remain offline for 30s
|
||||||
assert!(remote_command(&api_socket, "resume", None));
|
assert!(remote_command(&api_socket, "pause", None));
|
||||||
|
thread::sleep(std::time::Duration::new(30, 0));
|
||||||
|
assert!(remote_command(&api_socket, "resume", None));
|
||||||
|
|
||||||
// Check no reboot
|
// Check no reboot
|
||||||
let boot_count = guest
|
let boot_count = guest
|
||||||
.ssh_command("sudo journalctl --list-boots | wc -l")
|
.ssh_command("sudo journalctl --list-boots | wc -l")
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.trim()
|
.trim()
|
||||||
.parse::<u32>()
|
.parse::<u32>()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
assert_eq!(boot_count, 3);
|
assert_eq!(boot_count, 3);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
@@ -5581,10 +5529,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod windows {
|
mod windows {
|
||||||
use crate::tests::*;
|
use crate::tests::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn windows_auth() -> PasswordAuth {
|
fn windows_auth() -> PasswordAuth {
|
||||||
PasswordAuth {
|
PasswordAuth {
|
||||||
username: String::from("administrator"),
|
username: String::from("administrator"),
|
||||||
@@ -5593,7 +5541,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_windows_guest() {
|
fn test_windows_guest() {
|
||||||
let mut workload_path = dirs::home_dir().unwrap();
|
let mut workload_path = dirs::home_dir().unwrap();
|
||||||
workload_path.push("workloads");
|
workload_path.push("workloads");
|
||||||
@@ -5605,7 +5552,7 @@ mod tests {
|
|||||||
osdisk_path.push("windows-server-2019.raw");
|
osdisk_path.push("windows-server-2019.raw");
|
||||||
|
|
||||||
let mut child = Command::new(clh_command("cloud-hypervisor"))
|
let mut child = Command::new(clh_command("cloud-hypervisor"))
|
||||||
.args(&["--cpus", "boot=2,kvm_hyperv=on,max_phys_bits=39"])
|
.args(&["--cpus", "boot=2,kvm_hyperv=on"])
|
||||||
.args(&["--memory", "size=4G"])
|
.args(&["--memory", "size=4G"])
|
||||||
.args(&["--kernel", ovmf_path.to_str().unwrap()])
|
.args(&["--kernel", ovmf_path.to_str().unwrap()])
|
||||||
.args(&["--disk", &format!("path={}", osdisk_path.to_str().unwrap())])
|
.args(&["--disk", &format!("path={}", osdisk_path.to_str().unwrap())])
|
||||||
@@ -5645,7 +5592,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_windows_guest_snapshot_restore() {
|
fn test_windows_guest_snapshot_restore() {
|
||||||
let tmp_dir = TempDir::new("ch").unwrap();
|
let tmp_dir = TempDir::new("ch").unwrap();
|
||||||
let mut workload_path = dirs::home_dir().unwrap();
|
let mut workload_path = dirs::home_dir().unwrap();
|
||||||
@@ -5661,7 +5607,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut child = Command::new(clh_command("cloud-hypervisor"))
|
let mut child = Command::new(clh_command("cloud-hypervisor"))
|
||||||
.args(&["--api-socket", &api_socket])
|
.args(&["--api-socket", &api_socket])
|
||||||
.args(&["--cpus", "boot=2,kvm_hyperv=on,max_phys_bits=39"])
|
.args(&["--cpus", "boot=2,kvm_hyperv=on"])
|
||||||
.args(&["--memory", "size=4G"])
|
.args(&["--memory", "size=4G"])
|
||||||
.args(&["--kernel", ovmf_path.to_str().unwrap()])
|
.args(&["--kernel", ovmf_path.to_str().unwrap()])
|
||||||
.args(&["--disk", &format!("path={}", osdisk_path.to_str().unwrap())])
|
.args(&["--disk", &format!("path={}", osdisk_path.to_str().unwrap())])
|
||||||
@@ -5740,11 +5686,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod sgx {
|
mod sgx {
|
||||||
use crate::tests::*;
|
use crate::tests::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
fn test_sgx() {
|
fn test_sgx() {
|
||||||
let mut focal = UbuntuDiskConfig::new(FOCAL_SGX_IMAGE_NAME.to_string());
|
let mut focal = UbuntuDiskConfig::new(FOCAL_SGX_IMAGE_NAME.to_string());
|
||||||
let guest = Guest::new(&mut focal);
|
let guest = Guest::new(&mut focal);
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ default = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
virtio-bindings = "0.1.0"
|
virtio-bindings = "0.1.0"
|
||||||
vm-memory = "0.3.0"
|
vm-memory = "0.4.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_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
||||||
|
|||||||
@@ -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.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
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_rs = { 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.3.0"
|
vm-memory = "0.4.0"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ impl VhostUserBlkThread {
|
|||||||
match Request::parse(&head, mem) {
|
match Request::parse(&head, mem) {
|
||||||
Ok(mut request) => {
|
Ok(mut request) => {
|
||||||
debug!("element is a valid request");
|
debug!("element is a valid request");
|
||||||
request.set_writeback(self.writeback.load(Ordering::SeqCst));
|
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||||
let status = match request.execute(
|
let status = match request.execute(
|
||||||
&mut self.disk_image.lock().unwrap().deref_mut(),
|
&mut self.disk_image.lock().unwrap().deref_mut(),
|
||||||
self.disk_nsectors,
|
self.disk_nsectors,
|
||||||
@@ -273,7 +273,7 @@ impl VhostUserBlkBackend {
|
|||||||
"writethrough"
|
"writethrough"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.writeback.store(writeback, Ordering::SeqCst);
|
self.writeback.store(writeback, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +455,7 @@ impl VhostUserBlkBackendConfig {
|
|||||||
let poll_queue = parser
|
let poll_queue = parser
|
||||||
.convert::<Toggle>("poll_queue")
|
.convert::<Toggle>("poll_queue")
|
||||||
.map_err(Error::FailedConfigParse)?
|
.map_err(Error::FailedConfigParse)?
|
||||||
.unwrap_or_else(|| Toggle(true))
|
.unwrap_or(Toggle(true))
|
||||||
.0;
|
.0;
|
||||||
let queue_size = parser
|
let queue_size = parser
|
||||||
.convert("queue_size")
|
.convert("queue_size")
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "vhost_user_fs"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitflags = "1.1.0"
|
|
||||||
clap = { version = "2.33.3", features=["wrap_help"] }
|
|
||||||
epoll = ">=4.0.1"
|
|
||||||
futures = { version = "0.3.7", features = ["thread-pool"] }
|
|
||||||
libc = "0.2.80"
|
|
||||||
log = "0.4.11"
|
|
||||||
# Match the version in vmm
|
|
||||||
seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.22.0" }
|
|
||||||
tempdir = "0.3.7"
|
|
||||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
|
||||||
vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", package = "vhost", features = ["vhost-user-slave"] }
|
|
||||||
vhost_user_backend = { path = "../vhost_user_backend"}
|
|
||||||
vm-memory = "0.3.0"
|
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
|
||||||
vmm-sys-util = "0.6.1"
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,409 +0,0 @@
|
|||||||
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
use std::os::unix::io::AsRawFd;
|
|
||||||
|
|
||||||
use vm_memory::VolatileSlice;
|
|
||||||
|
|
||||||
use libc::{
|
|
||||||
c_int, c_void, off64_t, pread64, preadv64, pwrite64, pwritev64, read, readv, size_t, write,
|
|
||||||
writev,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A trait for setting the size of a file.
|
|
||||||
/// This is equivalent to File's `set_len` method, but
|
|
||||||
/// wrapped in a trait so that it can be implemented for
|
|
||||||
/// other types.
|
|
||||||
pub trait FileSetLen {
|
|
||||||
// Set the size of this file.
|
|
||||||
// This is the moral equivalent of `ftruncate()`.
|
|
||||||
fn set_len(&self, _len: u64) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileSetLen for File {
|
|
||||||
fn set_len(&self, len: u64) -> Result<()> {
|
|
||||||
File::set_len(self, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait similar to `Read` and `Write`, but uses volatile memory as buffers.
|
|
||||||
pub trait FileReadWriteVolatile {
|
|
||||||
/// Read bytes from this file into the given slice, returning the number of bytes read on
|
|
||||||
/// success.
|
|
||||||
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize>;
|
|
||||||
|
|
||||||
/// Like `read_volatile`, except it reads to a slice of buffers. Data is copied to fill each
|
|
||||||
/// buffer in order, with the final buffer written to possibly being only partially filled. This
|
|
||||||
/// method must behave as a single call to `read_volatile` with the buffers concatenated would.
|
|
||||||
/// The default implementation calls `read_volatile` with either the first nonempty buffer
|
|
||||||
/// provided, or returns `Ok(0)` if none exists.
|
|
||||||
fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
||||||
bufs.iter()
|
|
||||||
.find(|b| !b.is_empty())
|
|
||||||
.map(|&b| self.read_volatile(b))
|
|
||||||
.unwrap_or(Ok(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads bytes from this into the given slice until all bytes in the slice are written, or an
|
|
||||||
/// error is returned.
|
|
||||||
fn read_exact_volatile(&mut self, mut slice: VolatileSlice) -> Result<()> {
|
|
||||||
while !slice.is_empty() {
|
|
||||||
let bytes_read = self.read_volatile(slice)?;
|
|
||||||
if bytes_read == 0 {
|
|
||||||
return Err(Error::from(ErrorKind::UnexpectedEof));
|
|
||||||
}
|
|
||||||
// Will panic if read_volatile read more bytes than we gave it, which would be worthy of
|
|
||||||
// a panic.
|
|
||||||
slice = slice.offset(bytes_read).unwrap();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write bytes from the slice to the given file, returning the number of bytes written on
|
|
||||||
/// success.
|
|
||||||
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize>;
|
|
||||||
|
|
||||||
/// Like `write_volatile`, except that it writes from a slice of buffers. Data is copied from
|
|
||||||
/// each buffer in order, with the final buffer read from possibly being only partially
|
|
||||||
/// consumed. This method must behave as a call to `write_volatile` with the buffers
|
|
||||||
/// concatenated would. The default implementation calls `write_volatile` with either the first
|
|
||||||
/// nonempty buffer provided, or returns `Ok(0)` if none exists.
|
|
||||||
fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
||||||
bufs.iter()
|
|
||||||
.find(|b| !b.is_empty())
|
|
||||||
.map(|&b| self.write_volatile(b))
|
|
||||||
.unwrap_or(Ok(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write bytes from the slice to the given file until all the bytes from the slice have been
|
|
||||||
/// written, or an error is returned.
|
|
||||||
fn write_all_volatile(&mut self, mut slice: VolatileSlice) -> Result<()> {
|
|
||||||
while !slice.is_empty() {
|
|
||||||
let bytes_written = self.write_volatile(slice)?;
|
|
||||||
if bytes_written == 0 {
|
|
||||||
return Err(Error::from(ErrorKind::WriteZero));
|
|
||||||
}
|
|
||||||
// Will panic if read_volatile read more bytes than we gave it, which would be worthy of
|
|
||||||
// a panic.
|
|
||||||
slice = slice.offset(bytes_written).unwrap();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: FileReadWriteVolatile + ?Sized> FileReadWriteVolatile for &'a mut T {
|
|
||||||
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
||||||
(**self).read_volatile(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
||||||
(**self).read_vectored_volatile(bufs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_exact_volatile(&mut self, slice: VolatileSlice) -> Result<()> {
|
|
||||||
(**self).read_exact_volatile(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
||||||
(**self).write_volatile(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
||||||
(**self).write_vectored_volatile(bufs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_all_volatile(&mut self, slice: VolatileSlice) -> Result<()> {
|
|
||||||
(**self).write_all_volatile(slice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait similar to the unix `ReadExt` and `WriteExt` traits, but for volatile memory.
|
|
||||||
pub trait FileReadWriteAtVolatile {
|
|
||||||
/// Reads bytes from this file at `offset` into the given slice, returning the number of bytes
|
|
||||||
/// read on success.
|
|
||||||
fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize>;
|
|
||||||
|
|
||||||
/// Like `read_at_volatile`, except it reads to a slice of buffers. Data is copied to fill each
|
|
||||||
/// buffer in order, with the final buffer written to possibly being only partially filled. This
|
|
||||||
/// method must behave as a single call to `read_at_volatile` with the buffers concatenated
|
|
||||||
/// would. The default implementation calls `read_at_volatile` with either the first nonempty
|
|
||||||
/// buffer provided, or returns `Ok(0)` if none exists.
|
|
||||||
fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
||||||
if let Some(&slice) = bufs.first() {
|
|
||||||
self.read_at_volatile(slice, offset)
|
|
||||||
} else {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads bytes from this file at `offset` into the given slice until all bytes in the slice are
|
|
||||||
/// read, or an error is returned.
|
|
||||||
fn read_exact_at_volatile(&mut self, mut slice: VolatileSlice, mut offset: u64) -> Result<()> {
|
|
||||||
while !slice.is_empty() {
|
|
||||||
match self.read_at_volatile(slice, offset) {
|
|
||||||
Ok(0) => return Err(Error::from(ErrorKind::UnexpectedEof)),
|
|
||||||
Ok(n) => {
|
|
||||||
slice = slice.offset(n).unwrap();
|
|
||||||
offset = offset.checked_add(n as u64).unwrap();
|
|
||||||
}
|
|
||||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes bytes from this file at `offset` into the given slice, returning the number of bytes
|
|
||||||
/// written on success.
|
|
||||||
fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize>;
|
|
||||||
|
|
||||||
/// Like `write_at_at_volatile`, except that it writes from a slice of buffers. Data is copied
|
|
||||||
/// from each buffer in order, with the final buffer read from possibly being only partially
|
|
||||||
/// consumed. This method must behave as a call to `write_at_volatile` with the buffers
|
|
||||||
/// concatenated would. The default implementation calls `write_at_volatile` with either the
|
|
||||||
/// first nonempty buffer provided, or returns `Ok(0)` if none exists.
|
|
||||||
fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
||||||
if let Some(&slice) = bufs.first() {
|
|
||||||
self.write_at_volatile(slice, offset)
|
|
||||||
} else {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes bytes from this file at `offset` into the given slice until all bytes in the slice
|
|
||||||
/// are written, or an error is returned.
|
|
||||||
fn write_all_at_volatile(&mut self, mut slice: VolatileSlice, mut offset: u64) -> Result<()> {
|
|
||||||
while !slice.is_empty() {
|
|
||||||
match self.write_at_volatile(slice, offset) {
|
|
||||||
Ok(0) => return Err(Error::from(ErrorKind::WriteZero)),
|
|
||||||
Ok(n) => {
|
|
||||||
slice = slice.offset(n).unwrap();
|
|
||||||
offset = offset.checked_add(n as u64).unwrap();
|
|
||||||
}
|
|
||||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: FileReadWriteAtVolatile + ?Sized> FileReadWriteAtVolatile for &'a mut T {
|
|
||||||
fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
||||||
(**self).read_at_volatile(slice, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
||||||
(**self).read_vectored_at_volatile(bufs, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_exact_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<()> {
|
|
||||||
(**self).read_exact_at_volatile(slice, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
||||||
(**self).write_at_volatile(slice, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
|
||||||
(**self).write_vectored_at_volatile(bufs, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_all_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<()> {
|
|
||||||
(**self).write_all_at_volatile(slice, offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! volatile_impl {
|
|
||||||
($ty:ty) => {
|
|
||||||
impl FileReadWriteVolatile for $ty {
|
|
||||||
fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
||||||
// to handle arbitrary memory for I/O.
|
|
||||||
let ret =
|
|
||||||
unsafe { read(self.as_raw_fd(), slice.as_ptr() as *mut c_void, slice.len()) };
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
||||||
let iovecs: Vec<libc::iovec> = bufs
|
|
||||||
.iter()
|
|
||||||
.map(|s| libc::iovec {
|
|
||||||
iov_base: s.as_ptr() as *mut c_void,
|
|
||||||
iov_len: s.len() as size_t,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if iovecs.is_empty() {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
||||||
// expected to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe { readv(self.as_raw_fd(), &iovecs[0], iovecs.len() as c_int) };
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
|
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
||||||
// to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe {
|
|
||||||
write(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
slice.as_ptr() as *const c_void,
|
|
||||||
slice.len(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored_volatile(&mut self, bufs: &[VolatileSlice]) -> Result<usize> {
|
|
||||||
let iovecs: Vec<libc::iovec> = bufs
|
|
||||||
.iter()
|
|
||||||
.map(|s| libc::iovec {
|
|
||||||
iov_base: s.as_ptr() as *mut c_void,
|
|
||||||
iov_len: s.len() as size_t,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if iovecs.is_empty() {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
||||||
// expected to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe { writev(self.as_raw_fd(), &iovecs[0], iovecs.len() as c_int) };
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileReadWriteAtVolatile for $ty {
|
|
||||||
fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
||||||
// to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe {
|
|
||||||
pread64(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
slice.as_ptr() as *mut c_void,
|
|
||||||
slice.len(),
|
|
||||||
offset as off64_t,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_vectored_at_volatile(
|
|
||||||
&mut self,
|
|
||||||
bufs: &[VolatileSlice],
|
|
||||||
offset: u64,
|
|
||||||
) -> Result<usize> {
|
|
||||||
let iovecs: Vec<libc::iovec> = bufs
|
|
||||||
.iter()
|
|
||||||
.map(|s| libc::iovec {
|
|
||||||
iov_base: s.as_ptr() as *mut c_void,
|
|
||||||
iov_len: s.len() as size_t,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if iovecs.is_empty() {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
||||||
// expected to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe {
|
|
||||||
preadv64(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
&iovecs[0],
|
|
||||||
iovecs.len() as c_int,
|
|
||||||
offset as off64_t,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
|
|
||||||
// Safe because only bytes inside the slice are accessed and the kernel is expected
|
|
||||||
// to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe {
|
|
||||||
pwrite64(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
slice.as_ptr() as *const c_void,
|
|
||||||
slice.len(),
|
|
||||||
offset as off64_t,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored_at_volatile(
|
|
||||||
&mut self,
|
|
||||||
bufs: &[VolatileSlice],
|
|
||||||
offset: u64,
|
|
||||||
) -> Result<usize> {
|
|
||||||
let iovecs: Vec<libc::iovec> = bufs
|
|
||||||
.iter()
|
|
||||||
.map(|s| libc::iovec {
|
|
||||||
iov_base: s.as_ptr() as *mut c_void,
|
|
||||||
iov_len: s.len() as size_t,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if iovecs.is_empty() {
|
|
||||||
return Ok(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe because only bytes inside the buffers are accessed and the kernel is
|
|
||||||
// expected to handle arbitrary memory for I/O.
|
|
||||||
let ret = unsafe {
|
|
||||||
pwritev64(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
&iovecs[0],
|
|
||||||
iovecs.len() as c_int,
|
|
||||||
offset as off64_t,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret >= 0 {
|
|
||||||
Ok(ret as usize)
|
|
||||||
} else {
|
|
||||||
Err(Error::last_os_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
volatile_impl!(File);
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
|||||||
use crate::fuse;
|
|
||||||
use std::io;
|
|
||||||
use std::os::unix::io::RawFd;
|
|
||||||
use vhost_rs::vhost_user::message::{
|
|
||||||
VhostUserFSSlaveMsg, VhostUserFSSlaveMsgFlags, VHOST_USER_FS_SLAVE_ENTRIES,
|
|
||||||
};
|
|
||||||
use vhost_rs::vhost_user::{SlaveFsCacheReq, VhostUserMasterReqHandler};
|
|
||||||
|
|
||||||
/// Trait for virtio-fs cache requests operations. This is mainly used to hide
|
|
||||||
/// vhost-user details from virtio-fs's fuse part.
|
|
||||||
pub trait FsCacheReqHandler: Send + Sync + 'static {
|
|
||||||
/// Setup a dedicated mapping so that guest can access file data in DAX style.
|
|
||||||
fn map(
|
|
||||||
&mut self,
|
|
||||||
foffset: u64,
|
|
||||||
moffset: u64,
|
|
||||||
len: u64,
|
|
||||||
flags: u64,
|
|
||||||
fd: RawFd,
|
|
||||||
) -> io::Result<()>;
|
|
||||||
|
|
||||||
/// Remove those mappings that provide the access to file data.
|
|
||||||
fn unmap(&mut self, requests: Vec<fuse::RemovemappingOne>) -> io::Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FsCacheReqHandler for SlaveFsCacheReq {
|
|
||||||
fn map(
|
|
||||||
&mut self,
|
|
||||||
foffset: u64,
|
|
||||||
moffset: u64,
|
|
||||||
len: u64,
|
|
||||||
flags: u64,
|
|
||||||
fd: RawFd,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
let mut msg: VhostUserFSSlaveMsg = Default::default();
|
|
||||||
msg.fd_offset[0] = foffset;
|
|
||||||
msg.cache_offset[0] = moffset;
|
|
||||||
msg.len[0] = len;
|
|
||||||
msg.flags[0] = if (flags & fuse::SetupmappingFlags::WRITE.bits()) != 0 {
|
|
||||||
VhostUserFSSlaveMsgFlags::MAP_W | VhostUserFSSlaveMsgFlags::MAP_R
|
|
||||||
} else {
|
|
||||||
VhostUserFSSlaveMsgFlags::MAP_R
|
|
||||||
};
|
|
||||||
|
|
||||||
self.fs_slave_map(&msg, fd)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unmap(&mut self, requests: Vec<fuse::RemovemappingOne>) -> io::Result<()> {
|
|
||||||
for chunk in requests.chunks(VHOST_USER_FS_SLAVE_ENTRIES) {
|
|
||||||
let mut msg: VhostUserFSSlaveMsg = Default::default();
|
|
||||||
|
|
||||||
for (ind, req) in chunk.iter().enumerate() {
|
|
||||||
msg.len[ind] = req.len;
|
|
||||||
msg.cache_offset[ind] = req.moffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fs_slave_unmap(&msg)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,60 +0,0 @@
|
|||||||
// Copyright © 2019 Intel Corporation
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
pub mod descriptor_utils;
|
|
||||||
pub mod file_traits;
|
|
||||||
pub mod filesystem;
|
|
||||||
pub mod fs_cache_req_handler;
|
|
||||||
pub mod fuse;
|
|
||||||
pub mod multikey;
|
|
||||||
pub mod passthrough;
|
|
||||||
pub mod sandbox;
|
|
||||||
pub mod seccomp;
|
|
||||||
pub mod server;
|
|
||||||
|
|
||||||
use std::ffi::FromBytesWithNulError;
|
|
||||||
use std::{error, fmt, io};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Failed to decode protocol messages.
|
|
||||||
DecodeMessage(io::Error),
|
|
||||||
/// Failed to encode protocol messages.
|
|
||||||
EncodeMessage(io::Error),
|
|
||||||
/// One or more parameters are missing.
|
|
||||||
MissingParameter,
|
|
||||||
/// A C string parameter is invalid.
|
|
||||||
InvalidCString(FromBytesWithNulError),
|
|
||||||
/// The `len` field of the header is too small.
|
|
||||||
InvalidHeaderLength,
|
|
||||||
/// The `size` field of the `SetxattrIn` message does not match the length
|
|
||||||
/// of the decoded value.
|
|
||||||
InvalidXattrSize((u32, usize)),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for Error {}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use Error::*;
|
|
||||||
match self {
|
|
||||||
DecodeMessage(err) => write!(f, "failed to decode fuse message: {}", err),
|
|
||||||
EncodeMessage(err) => write!(f, "failed to encode fuse message: {}", err),
|
|
||||||
MissingParameter => write!(f, "one or more parameters are missing"),
|
|
||||||
InvalidHeaderLength => write!(f, "the `len` field of the header is too small"),
|
|
||||||
InvalidCString(err) => write!(f, "a c string parameter is invalid: {}", err),
|
|
||||||
InvalidXattrSize((size, len)) => write!(
|
|
||||||
f,
|
|
||||||
"The `size` field of the `SetxattrIn` message does not match the length of the\
|
|
||||||
decoded value: size = {}, value.len() = {}",
|
|
||||||
size, len
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
|
||||||
@@ -1,423 +0,0 @@
|
|||||||
// Copyright 2019 Intel Corporation. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause)
|
|
||||||
|
|
||||||
#[macro_use(crate_version, crate_authors)]
|
|
||||||
extern crate clap;
|
|
||||||
extern crate log;
|
|
||||||
extern crate vhost_rs;
|
|
||||||
extern crate vhost_user_backend;
|
|
||||||
extern crate virtio_devices;
|
|
||||||
|
|
||||||
use clap::{App, Arg};
|
|
||||||
use futures::executor::{ThreadPool, ThreadPoolBuilder};
|
|
||||||
use libc::EFD_NONBLOCK;
|
|
||||||
use log::*;
|
|
||||||
use seccomp::SeccompAction;
|
|
||||||
use std::num::Wrapping;
|
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
|
||||||
use std::{convert, error, fmt, io, process};
|
|
||||||
|
|
||||||
use vhost_rs::vhost_user::message::*;
|
|
||||||
use vhost_rs::vhost_user::{Listener, SlaveFsCacheReq};
|
|
||||||
use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring};
|
|
||||||
use vhost_user_fs::descriptor_utils::Error as VufDescriptorError;
|
|
||||||
use vhost_user_fs::descriptor_utils::{Reader, Writer};
|
|
||||||
use vhost_user_fs::filesystem::FileSystem;
|
|
||||||
use vhost_user_fs::passthrough::{self, PassthroughFs};
|
|
||||||
use vhost_user_fs::sandbox::Sandbox;
|
|
||||||
use vhost_user_fs::seccomp::enable_seccomp;
|
|
||||||
use vhost_user_fs::server::Server;
|
|
||||||
use vhost_user_fs::Error as VhostUserFsError;
|
|
||||||
use virtio_bindings::bindings::virtio_net::*;
|
|
||||||
use virtio_bindings::bindings::virtio_ring::{
|
|
||||||
VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
|
|
||||||
};
|
|
||||||
use vm_memory::{GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
|
|
||||||
use vm_virtio::queue::DescriptorChain;
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
|
||||||
|
|
||||||
const QUEUE_SIZE: usize = 1024;
|
|
||||||
const NUM_QUEUES: usize = 2;
|
|
||||||
const THREAD_POOL_SIZE: usize = 64;
|
|
||||||
|
|
||||||
// The guest queued an available buffer for the high priority queue.
|
|
||||||
const HIPRIO_QUEUE_EVENT: u16 = 0;
|
|
||||||
// The guest queued an available buffer for the request queue.
|
|
||||||
const REQ_QUEUE_EVENT: u16 = 1;
|
|
||||||
// The device has been dropped.
|
|
||||||
const KILL_EVENT: u16 = 2;
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
|
||||||
type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Error {
|
|
||||||
/// Failed to create kill eventfd.
|
|
||||||
CreateKillEventFd(io::Error),
|
|
||||||
/// Failed to create thread pool.
|
|
||||||
CreateThreadPool(io::Error),
|
|
||||||
/// Failed to handle event other than input event.
|
|
||||||
HandleEventNotEpollIn,
|
|
||||||
/// Failed to handle unknown event.
|
|
||||||
HandleEventUnknownEvent,
|
|
||||||
/// No memory configured.
|
|
||||||
NoMemoryConfigured,
|
|
||||||
/// Processing queue failed.
|
|
||||||
ProcessQueue(VhostUserFsError),
|
|
||||||
/// Creating a queue reader failed.
|
|
||||||
QueueReader(VufDescriptorError),
|
|
||||||
/// Creating a queue writer failed.
|
|
||||||
QueueWriter(VufDescriptorError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "vhost_user_fs_error: {:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for Error {}
|
|
||||||
|
|
||||||
impl convert::From<Error> for io::Error {
|
|
||||||
fn from(e: Error) -> Self {
|
|
||||||
io::Error::new(io::ErrorKind::Other, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VhostUserFsThread<F: FileSystem + Send + Sync + 'static> {
|
|
||||||
mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
|
|
||||||
kill_evt: EventFd,
|
|
||||||
server: Arc<Server<F>>,
|
|
||||||
// handle request from slave to master
|
|
||||||
vu_req: Option<SlaveFsCacheReq>,
|
|
||||||
event_idx: bool,
|
|
||||||
pool: ThreadPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FileSystem + Send + Sync + 'static> Clone for VhostUserFsThread<F> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
VhostUserFsThread {
|
|
||||||
mem: self.mem.clone(),
|
|
||||||
kill_evt: self.kill_evt.try_clone().unwrap(),
|
|
||||||
server: self.server.clone(),
|
|
||||||
vu_req: self.vu_req.clone(),
|
|
||||||
event_idx: self.event_idx,
|
|
||||||
pool: self.pool.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FileSystem + Send + Sync + 'static> VhostUserFsThread<F> {
|
|
||||||
fn new(fs: F, thread_pool_size: usize) -> Result<Self> {
|
|
||||||
Ok(VhostUserFsThread {
|
|
||||||
mem: None,
|
|
||||||
kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?,
|
|
||||||
server: Arc::new(Server::new(fs)),
|
|
||||||
vu_req: None,
|
|
||||||
event_idx: false,
|
|
||||||
pool: ThreadPoolBuilder::new()
|
|
||||||
.pool_size(thread_pool_size)
|
|
||||||
.create()
|
|
||||||
.map_err(Error::CreateThreadPool)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_queue(&mut self, vring_lock: Arc<RwLock<Vring>>) -> Result<bool> {
|
|
||||||
let mut used_any = false;
|
|
||||||
let (atomic_mem, mem) = match &self.mem {
|
|
||||||
Some(m) => (m, m.memory()),
|
|
||||||
None => return Err(Error::NoMemoryConfigured),
|
|
||||||
};
|
|
||||||
let mut vring = vring_lock.write().unwrap();
|
|
||||||
|
|
||||||
while let Some(avail_desc) = vring.mut_queue().iter(&mem).next() {
|
|
||||||
used_any = true;
|
|
||||||
|
|
||||||
// Prepare a set of objects that can be moved to the worker thread.
|
|
||||||
let desc_head = avail_desc.get_head();
|
|
||||||
let atomic_mem = atomic_mem.clone();
|
|
||||||
let server = self.server.clone();
|
|
||||||
let mut vu_req = self.vu_req.clone();
|
|
||||||
let event_idx = self.event_idx;
|
|
||||||
let vring_lock = vring_lock.clone();
|
|
||||||
|
|
||||||
self.pool.spawn_ok(async move {
|
|
||||||
let mem = atomic_mem.memory();
|
|
||||||
let desc = DescriptorChain::new_from_head(&mem, desc_head).unwrap();
|
|
||||||
let head_index = desc.index;
|
|
||||||
|
|
||||||
let reader = Reader::new(&mem, desc.clone())
|
|
||||||
.map_err(Error::QueueReader)
|
|
||||||
.unwrap();
|
|
||||||
let writer = Writer::new(&mem, desc.clone())
|
|
||||||
.map_err(Error::QueueWriter)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
server
|
|
||||||
.handle_message(reader, writer, vu_req.as_mut())
|
|
||||||
.map_err(Error::ProcessQueue)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut vring = vring_lock.write().unwrap();
|
|
||||||
|
|
||||||
if event_idx {
|
|
||||||
let queue = vring.mut_queue();
|
|
||||||
if let Some(used_idx) = queue.add_used(&mem, head_index, 0) {
|
|
||||||
if queue.needs_notification(&mem, Wrapping(used_idx)) {
|
|
||||||
vring.signal_used_queue().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vring.mut_queue().add_used(&mem, head_index, 0);
|
|
||||||
vring.signal_used_queue().unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(used_any)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VhostUserFsBackend<F: FileSystem + Send + Sync + 'static> {
|
|
||||||
thread: Mutex<VhostUserFsThread<F>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FileSystem + Send + Sync + 'static> VhostUserFsBackend<F> {
|
|
||||||
fn new(fs: F, thread_pool_size: usize) -> Result<Self> {
|
|
||||||
let thread = Mutex::new(VhostUserFsThread::new(fs, thread_pool_size)?);
|
|
||||||
Ok(VhostUserFsBackend { thread })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FileSystem + Send + Sync + 'static> VhostUserBackend for VhostUserFsBackend<F> {
|
|
||||||
fn num_queues(&self) -> usize {
|
|
||||||
NUM_QUEUES
|
|
||||||
}
|
|
||||||
|
|
||||||
fn max_queue_size(&self) -> usize {
|
|
||||||
QUEUE_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
fn features(&self) -> u64 {
|
|
||||||
1 << VIRTIO_F_VERSION_1
|
|
||||||
| 1 << VIRTIO_RING_F_INDIRECT_DESC
|
|
||||||
| 1 << VIRTIO_RING_F_EVENT_IDX
|
|
||||||
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn protocol_features(&self) -> VhostUserProtocolFeatures {
|
|
||||||
VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::SLAVE_REQ
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_event_idx(&mut self, enabled: bool) {
|
|
||||||
self.thread.lock().unwrap().event_idx = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_memory(&mut self, mem: GuestMemoryMmap) -> VhostUserBackendResult<()> {
|
|
||||||
self.thread.lock().unwrap().mem = Some(GuestMemoryAtomic::new(mem));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_event(
|
|
||||||
&self,
|
|
||||||
device_event: u16,
|
|
||||||
evset: epoll::Events,
|
|
||||||
vrings: &[Arc<RwLock<Vring>>],
|
|
||||||
_thread_id: usize,
|
|
||||||
) -> VhostUserBackendResult<bool> {
|
|
||||||
if evset != epoll::Events::EPOLLIN {
|
|
||||||
return Err(Error::HandleEventNotEpollIn.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut thread = self.thread.lock().unwrap();
|
|
||||||
let mem = match &thread.mem {
|
|
||||||
Some(m) => m.memory(),
|
|
||||||
None => return Err(Error::NoMemoryConfigured.into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let vring_lock = match device_event {
|
|
||||||
HIPRIO_QUEUE_EVENT => {
|
|
||||||
debug!("HIPRIO_QUEUE_EVENT");
|
|
||||||
vrings[0].clone()
|
|
||||||
}
|
|
||||||
REQ_QUEUE_EVENT => {
|
|
||||||
debug!("QUEUE_EVENT");
|
|
||||||
vrings[1].clone()
|
|
||||||
}
|
|
||||||
_ => return Err(Error::HandleEventUnknownEvent.into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if thread.event_idx {
|
|
||||||
// vm-virtio's Queue implementation only checks avail_index
|
|
||||||
// once, so to properly support EVENT_IDX we need to keep
|
|
||||||
// calling process_queue() until it stops finding new
|
|
||||||
// requests on the queue.
|
|
||||||
loop {
|
|
||||||
{
|
|
||||||
let mut vring = vring_lock.write().unwrap();
|
|
||||||
vring.mut_queue().update_avail_event(&mem);
|
|
||||||
}
|
|
||||||
if !thread.process_queue(vring_lock.clone())? {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Without EVENT_IDX, a single call is enough.
|
|
||||||
thread.process_queue(vring_lock)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_event(&self, _thread_index: usize) -> Option<(EventFd, Option<u16>)> {
|
|
||||||
Some((
|
|
||||||
self.thread.lock().unwrap().kill_evt.try_clone().unwrap(),
|
|
||||||
Some(KILL_EVENT),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_slave_req_fd(&mut self, vu_req: SlaveFsCacheReq) {
|
|
||||||
self.thread.lock().unwrap().vu_req = Some(vu_req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let cmd_arguments = App::new("vhost-user-fs backend")
|
|
||||||
.version(crate_version!())
|
|
||||||
.author(crate_authors!())
|
|
||||||
.about("Launch a vhost-user-fs backend.")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("shared-dir")
|
|
||||||
.long("shared-dir")
|
|
||||||
.help("Shared directory path")
|
|
||||||
.takes_value(true)
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("sock")
|
|
||||||
.long("sock")
|
|
||||||
.help("vhost-user socket path (deprecated)")
|
|
||||||
.takes_value(true)
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("socket")
|
|
||||||
.long("socket")
|
|
||||||
.help("vhost-user socket path")
|
|
||||||
.takes_value(true)
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("thread-pool-size")
|
|
||||||
.long("thread-pool-size")
|
|
||||||
.help("thread pool size (default 64)")
|
|
||||||
.takes_value(true)
|
|
||||||
.min_values(1),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("disable-xattr")
|
|
||||||
.long("disable-xattr")
|
|
||||||
.help("Disable support for extended attributes"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("disable-sandbox")
|
|
||||||
.long("disable-sandbox")
|
|
||||||
.help("Don't set up a sandbox for the daemon"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("seccomp")
|
|
||||||
.long("seccomp")
|
|
||||||
.help("Disable/debug seccomp security")
|
|
||||||
.possible_values(&["kill", "log", "trap", "none"])
|
|
||||||
.default_value("kill"),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// Retrieve arguments
|
|
||||||
let shared_dir = cmd_arguments
|
|
||||||
.value_of("shared-dir")
|
|
||||||
.expect("Failed to retrieve shared directory path");
|
|
||||||
let socket = match cmd_arguments.value_of("socket") {
|
|
||||||
Some(path) => path,
|
|
||||||
None => {
|
|
||||||
println!("warning: use of deprecated parameter '--sock': Please use the '--socket' option instead.");
|
|
||||||
cmd_arguments
|
|
||||||
.value_of("sock")
|
|
||||||
.expect("Failed to retrieve vhost-user socket path")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let thread_pool_size: usize = match cmd_arguments.value_of("thread-pool-size") {
|
|
||||||
Some(size) => size.parse().expect("Invalid argument for thread-pool-size"),
|
|
||||||
None => THREAD_POOL_SIZE,
|
|
||||||
};
|
|
||||||
let xattr: bool = !cmd_arguments.is_present("disable-xattr");
|
|
||||||
let create_sandbox: bool = !cmd_arguments.is_present("disable-sandbox");
|
|
||||||
let seccomp_mode: SeccompAction = match cmd_arguments.value_of("seccomp").unwrap() {
|
|
||||||
"none" => SeccompAction::Allow, // i.e. no seccomp
|
|
||||||
"kill" => SeccompAction::Kill,
|
|
||||||
"log" => SeccompAction::Log,
|
|
||||||
"trap" => SeccompAction::Trap,
|
|
||||||
_ => unreachable!(), // We told Arg possible_values
|
|
||||||
};
|
|
||||||
|
|
||||||
let listener = Listener::new(socket, true).unwrap();
|
|
||||||
|
|
||||||
let fs_cfg = if create_sandbox {
|
|
||||||
let mut sandbox = Sandbox::new(shared_dir.to_string());
|
|
||||||
match sandbox.enter().unwrap() {
|
|
||||||
Some(child_pid) => {
|
|
||||||
unsafe { libc::waitpid(child_pid, std::ptr::null_mut(), 0) };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
None => passthrough::Config {
|
|
||||||
root_dir: "/".to_string(),
|
|
||||||
xattr,
|
|
||||||
proc_sfd_rawfd: sandbox.get_proc_self_fd(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
passthrough::Config {
|
|
||||||
root_dir: shared_dir.to_string(),
|
|
||||||
xattr,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Must happen before we start the thread pool
|
|
||||||
if seccomp_mode != SeccompAction::Allow {
|
|
||||||
enable_seccomp(seccomp_mode).unwrap();
|
|
||||||
};
|
|
||||||
|
|
||||||
let fs = PassthroughFs::new(fs_cfg).unwrap();
|
|
||||||
let fs_backend = Arc::new(RwLock::new(
|
|
||||||
VhostUserFsBackend::new(fs, thread_pool_size).unwrap(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut daemon =
|
|
||||||
VhostUserDaemon::new(String::from("vhost-user-fs-backend"), fs_backend.clone()).unwrap();
|
|
||||||
|
|
||||||
if let Err(e) = daemon.start(listener) {
|
|
||||||
error!("Failed to start daemon: {:?}", e);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = daemon.wait() {
|
|
||||||
error!("Waiting for daemon failed: {:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let kill_evt = fs_backend
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.thread
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.kill_evt
|
|
||||||
.try_clone()
|
|
||||||
.unwrap();
|
|
||||||
if let Err(e) = kill_evt.write(1) {
|
|
||||||
error!("Error shutting down worker thread: {:?}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,274 +0,0 @@
|
|||||||
// Copyright 2019 The Chromium OS Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
/// A BTreeMap that supports 2 types of keys per value. All the usual restrictions and warnings for
|
|
||||||
/// `std::collections::BTreeMap` also apply to this struct. Additionally, there is a 1:1
|
|
||||||
/// relationship between the 2 key types. In other words, for each `K1` in the map, there is exactly
|
|
||||||
/// one `K2` in the map and vice versa.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MultikeyBTreeMap<K1, K2, V>
|
|
||||||
where
|
|
||||||
K1: Ord,
|
|
||||||
K2: Ord,
|
|
||||||
{
|
|
||||||
// We need to keep a copy of the second key in the main map so that we can remove entries using
|
|
||||||
// just the main key. Otherwise we would require the caller to provide both keys when calling
|
|
||||||
// `remove`.
|
|
||||||
main: BTreeMap<K1, (K2, V)>,
|
|
||||||
alt: BTreeMap<K2, K1>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K1, K2, V> MultikeyBTreeMap<K1, K2, V>
|
|
||||||
where
|
|
||||||
K1: Clone + Ord,
|
|
||||||
K2: Clone + Ord,
|
|
||||||
{
|
|
||||||
/// Create a new empty MultikeyBTreeMap.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
MultikeyBTreeMap {
|
|
||||||
main: BTreeMap::default(),
|
|
||||||
alt: BTreeMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the value corresponding to the key.
|
|
||||||
///
|
|
||||||
/// The key may be any borrowed form of `K1``, but the ordering on the borrowed form must match
|
|
||||||
/// the ordering on `K1`.
|
|
||||||
pub fn get<Q>(&self, key: &Q) -> Option<&V>
|
|
||||||
where
|
|
||||||
K1: Borrow<Q>,
|
|
||||||
Q: Ord + ?Sized,
|
|
||||||
{
|
|
||||||
self.main.get(key).map(|(_, v)| v)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the value corresponding to the alternate key.
|
|
||||||
///
|
|
||||||
/// The key may be any borrowed form of the `K2``, but the ordering on the borrowed form must
|
|
||||||
/// match the ordering on `K2`.
|
|
||||||
///
|
|
||||||
/// Note that this method performs 2 lookups: one to get the main key and another to get the
|
|
||||||
/// value associated with that key. For best performance callers should prefer the `get` method
|
|
||||||
/// over this method whenever possible as `get` only needs to perform one lookup.
|
|
||||||
pub fn get_alt<Q2>(&self, key: &Q2) -> Option<&V>
|
|
||||||
where
|
|
||||||
K2: Borrow<Q2>,
|
|
||||||
Q2: Ord + ?Sized,
|
|
||||||
{
|
|
||||||
if let Some(k) = self.alt.get(key) {
|
|
||||||
self.get(k)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a new entry into the map with the given keys and value.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the map did not have an entry with `k1` or `k2` present. If exactly one
|
|
||||||
/// key was present, then the value associated with that key is updated, the other key is
|
|
||||||
/// removed, and the old value is returned. If **both** keys were present then the value
|
|
||||||
/// associated with the main key is updated, the value associated with the alternate key is
|
|
||||||
/// removed, and the old value associated with the main key is returned.
|
|
||||||
pub fn insert(&mut self, k1: K1, k2: K2, v: V) -> Option<V> {
|
|
||||||
let oldval = if let Some(oldkey) = self.alt.insert(k2.clone(), k1.clone()) {
|
|
||||||
self.main.remove(&oldkey)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
self.main
|
|
||||||
.insert(k1, (k2.clone(), v))
|
|
||||||
.or(oldval)
|
|
||||||
.map(|(oldk2, v)| {
|
|
||||||
if oldk2 != k2 {
|
|
||||||
self.alt.remove(&oldk2);
|
|
||||||
}
|
|
||||||
v
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a key from the map, returning the value associated with that key if it was previously
|
|
||||||
/// in the map.
|
|
||||||
///
|
|
||||||
/// The key may be any borrowed form of `K1``, but the ordering on the borrowed form must match
|
|
||||||
/// the ordering on `K1`.
|
|
||||||
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
|
|
||||||
where
|
|
||||||
K1: Borrow<Q>,
|
|
||||||
Q: Ord + ?Sized,
|
|
||||||
{
|
|
||||||
self.main.remove(key).map(|(k2, v)| {
|
|
||||||
self.alt.remove(&k2);
|
|
||||||
v
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears the map, removing all values.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.alt.clear();
|
|
||||||
self.main.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
assert_eq!(*m.get(&k1).expect("failed to look up main key"), val);
|
|
||||||
assert_eq!(*m.get_alt(&k2).expect("failed to look up alt key"), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn update_main_key() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
let new_k1 = 0x3add_f8f8_c7c5_df5e;
|
|
||||||
let val2 = 0x7389_f8a7;
|
|
||||||
assert_eq!(
|
|
||||||
m.insert(new_k1, k2, val2)
|
|
||||||
.expect("failed to update main key"),
|
|
||||||
val
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(m.get(&k1).is_none());
|
|
||||||
assert_eq!(*m.get(&new_k1).expect("failed to look up main key"), val2);
|
|
||||||
assert_eq!(*m.get_alt(&k2).expect("failed to look up alt key"), val2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn update_alt_key() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
let new_k2 = 0x6825_a60b_61ac_b333;
|
|
||||||
let val2 = 0xbb14_8f2c;
|
|
||||||
assert_eq!(
|
|
||||||
m.insert(k1, new_k2, val2)
|
|
||||||
.expect("failed to update alt key"),
|
|
||||||
val
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(m.get_alt(&k2).is_none());
|
|
||||||
assert_eq!(*m.get(&k1).expect("failed to look up main key"), val2);
|
|
||||||
assert_eq!(
|
|
||||||
*m.get_alt(&new_k2).expect("failed to look up alt key"),
|
|
||||||
val2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn update_value() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
let val2 = 0xe42d_79ba;
|
|
||||||
assert_eq!(
|
|
||||||
m.insert(k1, k2, val2).expect("failed to update alt key"),
|
|
||||||
val
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(*m.get(&k1).expect("failed to look up main key"), val2);
|
|
||||||
assert_eq!(*m.get_alt(&k2).expect("failed to look up alt key"), val2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn update_both_keys_main() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
let new_k1 = 0xc980_587a_24b3_ae30;
|
|
||||||
let new_k2 = 0x2773_c5ee_8239_45a2;
|
|
||||||
let val2 = 0x31f4_33f9;
|
|
||||||
assert!(m.insert(new_k1, new_k2, val2).is_none());
|
|
||||||
|
|
||||||
let val3 = 0x8da1_9cf7;
|
|
||||||
assert_eq!(
|
|
||||||
m.insert(k1, new_k2, val3)
|
|
||||||
.expect("failed to update main key"),
|
|
||||||
val
|
|
||||||
);
|
|
||||||
|
|
||||||
// Both new_k1 and k2 should now be gone from the map.
|
|
||||||
assert!(m.get(&new_k1).is_none());
|
|
||||||
assert!(m.get_alt(&k2).is_none());
|
|
||||||
|
|
||||||
assert_eq!(*m.get(&k1).expect("failed to look up main key"), val3);
|
|
||||||
assert_eq!(
|
|
||||||
*m.get_alt(&new_k2).expect("failed to look up alt key"),
|
|
||||||
val3
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn update_both_keys_alt() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
let new_k1 = 0xc980_587a_24b3_ae30;
|
|
||||||
let new_k2 = 0x2773_c5ee_8239_45a2;
|
|
||||||
let val2 = 0x31f4_33f9;
|
|
||||||
assert!(m.insert(new_k1, new_k2, val2).is_none());
|
|
||||||
|
|
||||||
let val3 = 0x8da1_9cf7;
|
|
||||||
assert_eq!(
|
|
||||||
m.insert(new_k1, k2, val3)
|
|
||||||
.expect("failed to update main key"),
|
|
||||||
val2
|
|
||||||
);
|
|
||||||
|
|
||||||
// Both k1 and new_k2 should now be gone from the map.
|
|
||||||
assert!(m.get(&k1).is_none());
|
|
||||||
assert!(m.get_alt(&new_k2).is_none());
|
|
||||||
|
|
||||||
assert_eq!(*m.get(&new_k1).expect("failed to look up main key"), val3);
|
|
||||||
assert_eq!(*m.get_alt(&k2).expect("failed to look up alt key"), val3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove() {
|
|
||||||
let mut m = MultikeyBTreeMap::<u64, i64, u32>::new();
|
|
||||||
|
|
||||||
let k1 = 0xc6c8_f5e0_b13e_ed40;
|
|
||||||
let k2 = 0x1a04_ce4b_8329_14fe;
|
|
||||||
let val = 0xf4e3_c360;
|
|
||||||
assert!(m.insert(k1, k2, val).is_none());
|
|
||||||
|
|
||||||
assert_eq!(m.remove(&k1).expect("failed to remove entry"), val);
|
|
||||||
assert!(m.get(&k1).is_none());
|
|
||||||
assert!(m.get_alt(&k2).is_none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,319 +0,0 @@
|
|||||||
// Copyright 2020 Red Hat, Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::os::unix::io::RawFd;
|
|
||||||
use std::{fmt, fs, io};
|
|
||||||
|
|
||||||
use tempdir::TempDir;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Failed to bind mount `/proc/self/fd` into a temporary directory.
|
|
||||||
BindMountProcSelfFd(io::Error),
|
|
||||||
/// Failed to bind mount shared directory.
|
|
||||||
BindMountSharedDir(io::Error),
|
|
||||||
/// Failed to change to the old root directory.
|
|
||||||
ChdirOldRoot(io::Error),
|
|
||||||
/// Failed to change to the new root directory.
|
|
||||||
ChdirNewRoot(io::Error),
|
|
||||||
/// Failed to clean the properties of the mount point.
|
|
||||||
CleanMount(io::Error),
|
|
||||||
/// Failed to create a temporary directory.
|
|
||||||
CreateTempDir(io::Error),
|
|
||||||
/// Call to libc::fork returned an error.
|
|
||||||
Fork(io::Error),
|
|
||||||
/// Error bind-mounting a directory.
|
|
||||||
MountBind(io::Error),
|
|
||||||
/// Failed to mount old root.
|
|
||||||
MountOldRoot(io::Error),
|
|
||||||
/// Error mounting proc.
|
|
||||||
MountProc(io::Error),
|
|
||||||
/// Failed to mount new root.
|
|
||||||
MountNewRoot(io::Error),
|
|
||||||
/// Error mounting target directory.
|
|
||||||
MountTarget(io::Error),
|
|
||||||
/// Failed to open new root.
|
|
||||||
OpenNewRoot(io::Error),
|
|
||||||
/// Failed to open old root.
|
|
||||||
OpenOldRoot(io::Error),
|
|
||||||
/// Failed to open `/proc/self/fd`.
|
|
||||||
OpenProcSelfFd(io::Error),
|
|
||||||
/// Error switching root directory.
|
|
||||||
PivotRoot(io::Error),
|
|
||||||
/// Failed to remove temporary directory.
|
|
||||||
RmdirTempDir(io::Error),
|
|
||||||
/// Failed to lazily unmount old root.
|
|
||||||
UmountOldRoot(io::Error),
|
|
||||||
/// Failed to lazily unmount temporary directory.
|
|
||||||
UmountTempDir(io::Error),
|
|
||||||
/// Call to libc::unshare returned an error.
|
|
||||||
Unshare(io::Error),
|
|
||||||
/// Failed to read from procfs.
|
|
||||||
ReadProc(io::Error),
|
|
||||||
/// Failed to parse `/proc/sys/fs/nr_open`.
|
|
||||||
InvalidNrOpen(std::num::ParseIntError),
|
|
||||||
/// Failed to set rlimit.
|
|
||||||
SetRlimit(io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "vhost_user_fs_sandbox_error: {:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper for creating a sandbox for isolating the service.
|
|
||||||
pub struct Sandbox {
|
|
||||||
/// The directory that is going to be shared with the VM. The sandbox will be constructed on top
|
|
||||||
/// of this directory.
|
|
||||||
shared_dir: String,
|
|
||||||
/// A file descriptor for `/proc/self/fd` obtained from the sandboxed context.
|
|
||||||
proc_self_fd: Option<RawFd>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sandbox {
|
|
||||||
pub fn new(shared_dir: String) -> Self {
|
|
||||||
Sandbox {
|
|
||||||
shared_dir,
|
|
||||||
proc_self_fd: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make `self.shared_dir` our root directory, and get an isolated file descriptor for
|
|
||||||
// `/proc/self/fd`.
|
|
||||||
//
|
|
||||||
// This is based on virtiofsd's setup_namespaces() and setup_mounts(), and it's very similar to
|
|
||||||
// the strategy used in containers. Consists on a careful sequence of mounts and bind-mounts to
|
|
||||||
// ensure it's not possible to escape the sandbox through `self.shared_dir` nor the file
|
|
||||||
// descriptor obtained for `/proc/self/fd`.
|
|
||||||
//
|
|
||||||
// It's ugly, but it's the only way until Linux implements a proper containerization API.
|
|
||||||
fn setup_mounts(&mut self) -> Result<(), Error> {
|
|
||||||
// Ensure our mount changes don't affect the parent mount namespace.
|
|
||||||
let c_root_dir = CString::new("/").unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::mount(
|
|
||||||
std::ptr::null(),
|
|
||||||
c_root_dir.as_ptr(),
|
|
||||||
std::ptr::null(),
|
|
||||||
libc::MS_SLAVE | libc::MS_REC,
|
|
||||||
std::ptr::null(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret != 0 {
|
|
||||||
return Err(Error::CleanMount(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount `/proc` in this context.
|
|
||||||
let c_proc_dir = CString::new("/proc").unwrap();
|
|
||||||
let c_proc_fs = CString::new("proc").unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::mount(
|
|
||||||
c_proc_fs.as_ptr(),
|
|
||||||
c_proc_dir.as_ptr(),
|
|
||||||
c_proc_fs.as_ptr(),
|
|
||||||
libc::MS_NODEV | libc::MS_NOEXEC | libc::MS_NOSUID | libc::MS_RELATIME,
|
|
||||||
std::ptr::null(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret != 0 {
|
|
||||||
return Err(Error::MountProc(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind-mount `/proc/self/fd` info a temporary directory, preventing access to ancestor
|
|
||||||
// directories.
|
|
||||||
let c_proc_self_fd = CString::new("/proc/self/fd").unwrap();
|
|
||||||
let tmp_dir = TempDir::new("vhostuserfs-")
|
|
||||||
.map_err(|_| Error::CreateTempDir(std::io::Error::last_os_error()))?;
|
|
||||||
let c_tmp_dir = CString::new(tmp_dir.into_path().to_str().unwrap()).unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::mount(
|
|
||||||
c_proc_self_fd.as_ptr(),
|
|
||||||
c_tmp_dir.as_ptr(),
|
|
||||||
std::ptr::null(),
|
|
||||||
libc::MS_BIND,
|
|
||||||
std::ptr::null(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::BindMountProcSelfFd(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain a file descriptor for `/proc/self/fd` through the bind-mounted temporary directory.
|
|
||||||
let proc_self_fd = unsafe { libc::open(c_tmp_dir.as_ptr(), libc::O_PATH) };
|
|
||||||
if proc_self_fd < 0 {
|
|
||||||
return Err(Error::OpenProcSelfFd(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
self.proc_self_fd = Some(proc_self_fd);
|
|
||||||
|
|
||||||
// Now that we have a file descriptor for `/proc/self/fd`, we no longer need the bind-mount.
|
|
||||||
// Unmount it and remove the temporary directory.
|
|
||||||
let ret = unsafe { libc::umount2(c_tmp_dir.as_ptr(), libc::MNT_DETACH) };
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::UmountTempDir(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
let ret = unsafe { libc::rmdir(c_tmp_dir.as_ptr()) };
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::RmdirTempDir(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind-mount `self.shared_dir` on itself so we can use as new root on `pivot_root` syscall.
|
|
||||||
let c_shared_dir = CString::new(self.shared_dir.clone()).unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::mount(
|
|
||||||
c_shared_dir.as_ptr(),
|
|
||||||
c_shared_dir.as_ptr(),
|
|
||||||
std::ptr::null(),
|
|
||||||
libc::MS_BIND | libc::MS_REC,
|
|
||||||
std::ptr::null(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::BindMountSharedDir(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a file descriptor to our old root so we can reference it after switching root.
|
|
||||||
let oldroot_fd = unsafe {
|
|
||||||
libc::open(
|
|
||||||
c_root_dir.as_ptr(),
|
|
||||||
libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if oldroot_fd < 0 {
|
|
||||||
return Err(Error::OpenOldRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a file descriptor to the new root so we can reference it after switching root.
|
|
||||||
let newroot_fd = unsafe {
|
|
||||||
libc::open(
|
|
||||||
c_shared_dir.as_ptr(),
|
|
||||||
libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if newroot_fd < 0 {
|
|
||||||
return Err(Error::OpenNewRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change to new root directory to prepare for `pivot_root` syscall.
|
|
||||||
let ret = unsafe { libc::fchdir(newroot_fd) };
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::ChdirNewRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call to `pivot_root` using `.` as both new and old root.
|
|
||||||
let c_current_dir = CString::new(".").unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::syscall(
|
|
||||||
libc::SYS_pivot_root,
|
|
||||||
c_current_dir.as_ptr(),
|
|
||||||
c_current_dir.as_ptr(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::PivotRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change to old root directory to prepare for cleaning up and unmounting it.
|
|
||||||
let ret = unsafe { libc::fchdir(oldroot_fd) };
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::ChdirOldRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up old root to avoid mount namespace propagation.
|
|
||||||
let c_empty = CString::new("").unwrap();
|
|
||||||
let ret = unsafe {
|
|
||||||
libc::mount(
|
|
||||||
c_empty.as_ptr(),
|
|
||||||
c_current_dir.as_ptr(),
|
|
||||||
c_empty.as_ptr(),
|
|
||||||
libc::MS_SLAVE | libc::MS_REC,
|
|
||||||
std::ptr::null(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if ret != 0 {
|
|
||||||
return Err(Error::CleanMount(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lazily unmount old root.
|
|
||||||
let ret = unsafe { libc::umount2(c_current_dir.as_ptr(), libc::MNT_DETACH) };
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::UmountOldRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change to new root.
|
|
||||||
let ret = unsafe { libc::fchdir(newroot_fd) };
|
|
||||||
if ret < 0 {
|
|
||||||
return Err(Error::ChdirNewRoot(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We no longer need these file descriptors, so close them.
|
|
||||||
unsafe { libc::close(newroot_fd) };
|
|
||||||
unsafe { libc::close(oldroot_fd) };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the limit of open files to the max possible.
|
|
||||||
fn setup_nofile_rlimit(&self) -> Result<(), Error> {
|
|
||||||
// /proc/sys/fs/nr_open is a sysctl file that shows the maximum number
|
|
||||||
// of file-handles a process can allocate.
|
|
||||||
let path = "/proc/sys/fs/nr_open";
|
|
||||||
let max_str = fs::read_to_string(path).map_err(Error::ReadProc)?;
|
|
||||||
let max = max_str.trim().parse().map_err(Error::InvalidNrOpen)?;
|
|
||||||
|
|
||||||
let limit = libc::rlimit {
|
|
||||||
rlim_cur: max,
|
|
||||||
rlim_max: max,
|
|
||||||
};
|
|
||||||
let ret = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &limit) };
|
|
||||||
if ret < 0 {
|
|
||||||
Err(Error::SetRlimit(std::io::Error::last_os_error()))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set up sandbox, fork and jump into it.
|
|
||||||
///
|
|
||||||
/// On success, the returned value will be the PID of the child for the parent and `None` for
|
|
||||||
/// the child itself, with the latter running isolated in `self.shared_dir`.
|
|
||||||
pub fn enter(&mut self) -> Result<Option<i32>, Error> {
|
|
||||||
let uid = unsafe { libc::geteuid() };
|
|
||||||
|
|
||||||
let flags = if uid == 0 {
|
|
||||||
libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::CLONE_NEWNET
|
|
||||||
} else {
|
|
||||||
// If running as an unprivileged user, rely on user_namespaces(7) for isolation. The
|
|
||||||
// main limitation of this strategy is that only the current uid/gid are mapped into
|
|
||||||
// the new namespace, so most operations on permissions will fail.
|
|
||||||
libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::CLONE_NEWNET | libc::CLONE_NEWUSER
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret = unsafe { libc::unshare(flags) };
|
|
||||||
if ret != 0 {
|
|
||||||
return Err(Error::Unshare(std::io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = unsafe { libc::fork() };
|
|
||||||
match child {
|
|
||||||
0 => {
|
|
||||||
// This is the child. Request to receive SIGTERM on parent's death.
|
|
||||||
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) };
|
|
||||||
self.setup_nofile_rlimit()?;
|
|
||||||
self.setup_mounts()?;
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
x if x > 0 => {
|
|
||||||
// This is the parent.
|
|
||||||
Ok(Some(child))
|
|
||||||
}
|
|
||||||
_ => Err(Error::Fork(std::io::Error::last_os_error())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_proc_self_fd(&self) -> Option<RawFd> {
|
|
||||||
self.proc_self_fd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// Copyright 2020 Red Hat, Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
use seccomp::{allow_syscall, BpfProgram, SeccompAction, SeccompFilter};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::{convert, fmt};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Cannot create seccomp filter
|
|
||||||
CreateSeccompFilter(seccomp::SeccompError),
|
|
||||||
|
|
||||||
/// Cannot apply seccomp filter
|
|
||||||
ApplySeccompFilter(seccomp::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl convert::From<seccomp::Error> for Error {
|
|
||||||
fn from(e: seccomp::Error) -> Self {
|
|
||||||
Error::ApplySeccompFilter(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "vhost_user_fs_seccomp_error: {:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vuf_filter(action: SeccompAction) -> Result<SeccompFilter, Error> {
|
|
||||||
Ok(SeccompFilter::new(
|
|
||||||
vec![
|
|
||||||
allow_syscall(libc::SYS_accept4),
|
|
||||||
allow_syscall(libc::SYS_brk),
|
|
||||||
allow_syscall(libc::SYS_capget), // For CAP_FSETID
|
|
||||||
allow_syscall(libc::SYS_capset),
|
|
||||||
allow_syscall(libc::SYS_clock_gettime),
|
|
||||||
allow_syscall(libc::SYS_clone),
|
|
||||||
allow_syscall(libc::SYS_close),
|
|
||||||
allow_syscall(libc::SYS_copy_file_range),
|
|
||||||
allow_syscall(libc::SYS_dup),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_epoll_create),
|
|
||||||
allow_syscall(libc::SYS_epoll_create1),
|
|
||||||
allow_syscall(libc::SYS_epoll_ctl),
|
|
||||||
allow_syscall(libc::SYS_epoll_pwait),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_epoll_wait),
|
|
||||||
allow_syscall(libc::SYS_eventfd2),
|
|
||||||
allow_syscall(libc::SYS_exit),
|
|
||||||
allow_syscall(libc::SYS_exit_group),
|
|
||||||
allow_syscall(libc::SYS_fallocate),
|
|
||||||
allow_syscall(libc::SYS_fchdir),
|
|
||||||
allow_syscall(libc::SYS_fchmod),
|
|
||||||
allow_syscall(libc::SYS_fchmodat),
|
|
||||||
allow_syscall(libc::SYS_fchownat),
|
|
||||||
allow_syscall(libc::SYS_fcntl),
|
|
||||||
allow_syscall(libc::SYS_fdatasync),
|
|
||||||
allow_syscall(libc::SYS_fgetxattr),
|
|
||||||
allow_syscall(libc::SYS_flistxattr),
|
|
||||||
allow_syscall(libc::SYS_flock),
|
|
||||||
allow_syscall(libc::SYS_fremovexattr),
|
|
||||||
allow_syscall(libc::SYS_fsetxattr),
|
|
||||||
allow_syscall(libc::SYS_fstat),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_fstatfs),
|
|
||||||
allow_syscall(libc::SYS_fsync),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_ftruncate),
|
|
||||||
allow_syscall(libc::SYS_futex),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_getdents),
|
|
||||||
allow_syscall(libc::SYS_getdents64),
|
|
||||||
allow_syscall(libc::SYS_getegid),
|
|
||||||
allow_syscall(libc::SYS_geteuid),
|
|
||||||
allow_syscall(libc::SYS_getpid),
|
|
||||||
allow_syscall(libc::SYS_gettid),
|
|
||||||
allow_syscall(libc::SYS_gettimeofday),
|
|
||||||
allow_syscall(libc::SYS_getxattr),
|
|
||||||
allow_syscall(libc::SYS_linkat),
|
|
||||||
allow_syscall(libc::SYS_listxattr),
|
|
||||||
allow_syscall(libc::SYS_lseek),
|
|
||||||
allow_syscall(libc::SYS_madvise),
|
|
||||||
allow_syscall(libc::SYS_mkdirat),
|
|
||||||
allow_syscall(libc::SYS_mknodat),
|
|
||||||
allow_syscall(libc::SYS_mmap),
|
|
||||||
allow_syscall(libc::SYS_mprotect),
|
|
||||||
allow_syscall(libc::SYS_mremap),
|
|
||||||
allow_syscall(libc::SYS_munmap),
|
|
||||||
allow_syscall(libc::SYS_newfstatat),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_open),
|
|
||||||
allow_syscall(libc::SYS_openat),
|
|
||||||
allow_syscall(libc::SYS_prctl), // TODO restrict to just PR_SET_NAME?
|
|
||||||
allow_syscall(libc::SYS_preadv),
|
|
||||||
allow_syscall(libc::SYS_pread64),
|
|
||||||
allow_syscall(libc::SYS_pwritev),
|
|
||||||
allow_syscall(libc::SYS_pwrite64),
|
|
||||||
allow_syscall(libc::SYS_read),
|
|
||||||
allow_syscall(libc::SYS_readlinkat),
|
|
||||||
allow_syscall(libc::SYS_recvmsg),
|
|
||||||
allow_syscall(libc::SYS_renameat),
|
|
||||||
allow_syscall(libc::SYS_renameat2),
|
|
||||||
allow_syscall(libc::SYS_removexattr),
|
|
||||||
allow_syscall(libc::SYS_rt_sigaction),
|
|
||||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
|
||||||
allow_syscall(libc::SYS_rt_sigreturn),
|
|
||||||
allow_syscall(libc::SYS_sched_getaffinity), // used by thread_pool
|
|
||||||
allow_syscall(libc::SYS_sendmsg),
|
|
||||||
allow_syscall(libc::SYS_setresgid),
|
|
||||||
allow_syscall(libc::SYS_setresuid),
|
|
||||||
//allow_syscall(libc::SYS_setresgid32), Needed on some platforms,
|
|
||||||
//allow_syscall(libc::SYS_setresuid32), Needed on some platforms
|
|
||||||
allow_syscall(libc::SYS_set_robust_list),
|
|
||||||
allow_syscall(libc::SYS_setxattr),
|
|
||||||
allow_syscall(libc::SYS_sigaltstack),
|
|
||||||
allow_syscall(libc::SYS_statx),
|
|
||||||
allow_syscall(libc::SYS_symlinkat),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_time), // Rarely needed, except on static builds
|
|
||||||
allow_syscall(libc::SYS_tgkill),
|
|
||||||
allow_syscall(libc::SYS_umask),
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
allow_syscall(libc::SYS_unlink),
|
|
||||||
allow_syscall(libc::SYS_unlinkat),
|
|
||||||
allow_syscall(libc::SYS_unshare),
|
|
||||||
allow_syscall(libc::SYS_utimensat),
|
|
||||||
allow_syscall(libc::SYS_write),
|
|
||||||
allow_syscall(libc::SYS_writev),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
action,
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_seccomp(action: SeccompAction) -> Result<(), Error> {
|
|
||||||
let scfilter = vuf_filter(action)?;
|
|
||||||
let bpfprog: BpfProgram = scfilter.try_into()?;
|
|
||||||
SeccompFilter::apply(bpfprog).map_err(Error::ApplySeccompFilter)
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
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_rs = { 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.3.0"
|
vm-memory = "0.4.0"
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ io_uring = ["block_util/io_uring"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
arc-swap = ">=0.4.4"
|
arc-swap = ">=1.0.0"
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
io-uring = ">=0.4.0"
|
io-uring = ">=0.4.0"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
@@ -30,7 +30,7 @@ vhost_rs = { git = "https://github.com/rust-vmm/vhost", branch = "master", packa
|
|||||||
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" }
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ struct VirtioBalloonResizeReceiver {
|
|||||||
|
|
||||||
impl VirtioBalloonResizeReceiver {
|
impl VirtioBalloonResizeReceiver {
|
||||||
fn get_size(&self) -> u64 {
|
fn get_size(&self) -> u64 {
|
||||||
self.size.load(Ordering::SeqCst)
|
self.size.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
|
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
|
||||||
@@ -134,7 +134,7 @@ impl VirtioBalloonResize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn work(&self, size: u64) -> Result<(), Error> {
|
pub fn work(&self, size: u64) -> Result<(), Error> {
|
||||||
self.size.store(size, Ordering::SeqCst);
|
self.size.store(size, Ordering::Release);
|
||||||
self.evt.write(1).map_err(Error::EventFdWriteFail)?;
|
self.evt.write(1).map_err(Error::EventFdWriteFail)?;
|
||||||
self.rx.recv().map_err(Error::MpscRecvFail)?
|
self.rx.recv().map_err(Error::MpscRecvFail)?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
|||||||
let len;
|
let len;
|
||||||
match Request::parse(&avail_desc, &mem) {
|
match Request::parse(&avail_desc, &mem) {
|
||||||
Ok(mut request) => {
|
Ok(mut request) => {
|
||||||
request.set_writeback(self.writeback.load(Ordering::SeqCst));
|
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||||
|
|
||||||
let mut disk_image_locked = self.disk_image.lock().unwrap();
|
let mut disk_image_locked = self.disk_image.lock().unwrap();
|
||||||
let mut disk_image = disk_image_locked.deref_mut();
|
let mut disk_image = disk_image_locked.deref_mut();
|
||||||
@@ -387,7 +387,7 @@ impl<T: DiskFile> Block<T> {
|
|||||||
"writethrough"
|
"writethrough"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.writeback.store(writeback, Ordering::SeqCst);
|
self.writeback.store(writeback, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ impl BlockIoUringEpollHandler {
|
|||||||
|
|
||||||
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)?;
|
||||||
request.set_writeback(self.writeback.load(Ordering::SeqCst));
|
request.set_writeback(self.writeback.load(Ordering::Acquire));
|
||||||
if request
|
if request
|
||||||
.execute_io_uring(
|
.execute_io_uring(
|
||||||
&mem,
|
&mem,
|
||||||
@@ -428,7 +428,7 @@ impl BlockIoUring {
|
|||||||
"writethrough"
|
"writethrough"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
self.writeback.store(writeback, Ordering::SeqCst);
|
self.writeback.store(writeback, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ impl ConsoleInput {
|
|||||||
pub fn update_console_size(&self, cols: u16, rows: u16) {
|
pub fn update_console_size(&self, cols: u16, rows: u16) {
|
||||||
if self
|
if self
|
||||||
.acked_features
|
.acked_features
|
||||||
.fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::SeqCst)
|
.fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
|
||||||
!= 0
|
!= 0
|
||||||
{
|
{
|
||||||
self.config.lock().unwrap().update_console_size(cols, rows);
|
self.config.lock().unwrap().update_console_size(cols, rows);
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ impl Resize {
|
|||||||
|
|
||||||
pub fn work(&self, size: u64) -> Result<(), Error> {
|
pub fn work(&self, size: u64) -> Result<(), Error> {
|
||||||
if let Some(rx) = &self.rx {
|
if let Some(rx) = &self.rx {
|
||||||
self.size.store(size, Ordering::SeqCst);
|
self.size.store(size, Ordering::Release);
|
||||||
self.evt.write(1).map_err(Error::EventFdWriteFail)?;
|
self.evt.write(1).map_err(Error::EventFdWriteFail)?;
|
||||||
rx.recv().map_err(Error::MpscRecvFail)?
|
rx.recv().map_err(Error::MpscRecvFail)?
|
||||||
} else {
|
} else {
|
||||||
@@ -293,7 +293,7 @@ impl Resize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_size(&self) -> u64 {
|
fn get_size(&self) -> u64 {
|
||||||
self.size.load(Ordering::SeqCst)
|
self.size.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
|
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl VirtioPciCommonConfig {
|
|||||||
device_feature_select: self.device_feature_select,
|
device_feature_select: self.device_feature_select,
|
||||||
driver_feature_select: self.driver_feature_select,
|
driver_feature_select: self.driver_feature_select,
|
||||||
queue_select: self.queue_select,
|
queue_select: self.queue_select,
|
||||||
msix_config: self.msix_config.load(Ordering::SeqCst),
|
msix_config: self.msix_config.load(Ordering::Acquire),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ impl VirtioPciCommonConfig {
|
|||||||
self.device_feature_select = state.device_feature_select;
|
self.device_feature_select = state.device_feature_select;
|
||||||
self.driver_feature_select = state.driver_feature_select;
|
self.driver_feature_select = state.driver_feature_select;
|
||||||
self.queue_select = state.queue_select;
|
self.queue_select = state.queue_select;
|
||||||
self.msix_config.store(state.msix_config, Ordering::SeqCst);
|
self.msix_config.store(state.msix_config, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(
|
pub fn read(
|
||||||
@@ -153,7 +153,7 @@ impl VirtioPciCommonConfig {
|
|||||||
fn read_common_config_word(&self, offset: u64, queues: &[Queue]) -> u16 {
|
fn read_common_config_word(&self, offset: u64, queues: &[Queue]) -> u16 {
|
||||||
debug!("read_common_config_word: offset 0x{:x}", offset);
|
debug!("read_common_config_word: offset 0x{:x}", offset);
|
||||||
match offset {
|
match offset {
|
||||||
0x10 => self.msix_config.load(Ordering::SeqCst),
|
0x10 => self.msix_config.load(Ordering::Acquire),
|
||||||
0x12 => queues.len() as u16, // num_queues
|
0x12 => queues.len() as u16, // num_queues
|
||||||
0x16 => self.queue_select,
|
0x16 => self.queue_select,
|
||||||
0x18 => self.with_queue(queues, |q| q.size).unwrap_or(0),
|
0x18 => self.with_queue(queues, |q| q.size).unwrap_or(0),
|
||||||
@@ -176,7 +176,7 @@ impl VirtioPciCommonConfig {
|
|||||||
fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut Vec<Queue>) {
|
fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut Vec<Queue>) {
|
||||||
debug!("write_common_config_word: offset 0x{:x}", offset);
|
debug!("write_common_config_word: offset 0x{:x}", offset);
|
||||||
match offset {
|
match offset {
|
||||||
0x10 => self.msix_config.store(value, Ordering::SeqCst),
|
0x10 => self.msix_config.store(value, Ordering::Release),
|
||||||
0x16 => self.queue_select = value,
|
0x16 => self.queue_select = value,
|
||||||
0x18 => self.with_queue_mut(queues, |q| q.size = value),
|
0x18 => self.with_queue_mut(queues, |q| q.size = value),
|
||||||
0x1a => self.with_queue_mut(queues, |q| q.vector = value),
|
0x1a => self.with_queue_mut(queues, |q| q.vector = value),
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ impl VirtioPciDevice {
|
|||||||
fn state(&self) -> VirtioPciDeviceState {
|
fn state(&self) -> VirtioPciDeviceState {
|
||||||
VirtioPciDeviceState {
|
VirtioPciDeviceState {
|
||||||
device_activated: self.device_activated,
|
device_activated: self.device_activated,
|
||||||
interrupt_status: self.interrupt_status.load(Ordering::SeqCst),
|
interrupt_status: self.interrupt_status.load(Ordering::Acquire),
|
||||||
queues: self.queues.clone(),
|
queues: self.queues.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,7 +456,7 @@ impl VirtioPciDevice {
|
|||||||
fn set_state(&mut self, state: &VirtioPciDeviceState) -> std::result::Result<(), Error> {
|
fn set_state(&mut self, state: &VirtioPciDeviceState) -> std::result::Result<(), Error> {
|
||||||
self.device_activated = state.device_activated;
|
self.device_activated = state.device_activated;
|
||||||
self.interrupt_status
|
self.interrupt_status
|
||||||
.store(state.interrupt_status, Ordering::SeqCst);
|
.store(state.interrupt_status, Ordering::Release);
|
||||||
|
|
||||||
// Update virtqueues indexes for both available and used rings.
|
// Update virtqueues indexes for both available and used rings.
|
||||||
if let Some(mem) = self.memory.as_ref() {
|
if let Some(mem) = self.memory.as_ref() {
|
||||||
@@ -685,7 +685,7 @@ impl VirtioInterrupt for VirtioInterruptMsix {
|
|||||||
queue: Option<&Queue>,
|
queue: Option<&Queue>,
|
||||||
) -> std::result::Result<(), std::io::Error> {
|
) -> std::result::Result<(), std::io::Error> {
|
||||||
let vector = match int_type {
|
let vector = match int_type {
|
||||||
VirtioInterruptType::Config => self.config_vector.load(Ordering::SeqCst),
|
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
|
||||||
VirtioInterruptType::Queue => {
|
VirtioInterruptType::Queue => {
|
||||||
if let Some(q) = queue {
|
if let Some(q) = queue {
|
||||||
q.vector
|
q.vector
|
||||||
@@ -717,7 +717,7 @@ impl VirtioInterrupt for VirtioInterruptMsix {
|
|||||||
|
|
||||||
fn notifier(&self, int_type: &VirtioInterruptType, queue: Option<&Queue>) -> Option<&EventFd> {
|
fn notifier(&self, int_type: &VirtioInterruptType, queue: Option<&Queue>) -> Option<&EventFd> {
|
||||||
let vector = match int_type {
|
let vector = match int_type {
|
||||||
VirtioInterruptType::Config => self.config_vector.load(Ordering::SeqCst),
|
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
|
||||||
VirtioInterruptType::Queue => {
|
VirtioInterruptType::Queue => {
|
||||||
if let Some(q) = queue {
|
if let Some(q) = queue {
|
||||||
q.vector
|
q.vector
|
||||||
@@ -891,7 +891,7 @@ impl PciDevice for VirtioPciDevice {
|
|||||||
o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => {
|
o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => {
|
||||||
if let Some(v) = data.get_mut(0) {
|
if let Some(v) = data.get_mut(0) {
|
||||||
// Reading this register resets it to 0.
|
// Reading this register resets it to 0.
|
||||||
*v = self.interrupt_status.swap(0, Ordering::SeqCst) as u8;
|
*v = self.interrupt_status.swap(0, Ordering::AcqRel) as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o if DEVICE_CONFIG_BAR_OFFSET <= o
|
o if DEVICE_CONFIG_BAR_OFFSET <= o
|
||||||
@@ -936,7 +936,7 @@ impl PciDevice for VirtioPciDevice {
|
|||||||
o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => {
|
o if ISR_CONFIG_BAR_OFFSET <= o && o < ISR_CONFIG_BAR_OFFSET + ISR_CONFIG_SIZE => {
|
||||||
if let Some(v) = data.get(0) {
|
if let Some(v) = data.get(0) {
|
||||||
self.interrupt_status
|
self.interrupt_status
|
||||||
.fetch_and(!(*v as usize), Ordering::SeqCst);
|
.fetch_and(!(*v as usize), Ordering::AcqRel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o if DEVICE_CONFIG_BAR_OFFSET <= o
|
o if DEVICE_CONFIG_BAR_OFFSET <= o
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ pub fn setup_vhost_user_vring(
|
|||||||
queue.desc_table,
|
queue.desc_table,
|
||||||
actual_size * std::mem::size_of::<Descriptor>(),
|
actual_size * std::mem::size_of::<Descriptor>(),
|
||||||
)
|
)
|
||||||
.ok_or_else(|| Error::DescriptorTableAddress)? as u64,
|
.ok_or(Error::DescriptorTableAddress)? as u64,
|
||||||
// The used ring is {flags: u16; idx: u16; virtq_used_elem [{id: u16, len: u16}; actual_size]},
|
// The used ring is {flags: u16; idx: u16; virtq_used_elem [{id: u16, len: u16}; actual_size]},
|
||||||
// i.e. 4 + (4 + 4) * actual_size.
|
// i.e. 4 + (4 + 4) * actual_size.
|
||||||
used_ring_addr: get_host_address_range(mem, queue.used_ring, 4 + actual_size * 8)
|
used_ring_addr: get_host_address_range(mem, queue.used_ring, 4 + actual_size * 8)
|
||||||
.ok_or_else(|| Error::UsedAddress)? as u64,
|
.ok_or(Error::UsedAddress)? as u64,
|
||||||
// The used ring is {flags: u16; idx: u16; elem [u16; actual_size]},
|
// The used ring is {flags: u16; idx: u16; elem [u16; actual_size]},
|
||||||
// i.e. 4 + (2) * actual_size.
|
// i.e. 4 + (2) * actual_size.
|
||||||
avail_ring_addr: get_host_address_range(mem, queue.avail_ring, 4 + actual_size * 2)
|
avail_ring_addr: get_host_address_range(mem, queue.avail_ring, 4 + actual_size * 2)
|
||||||
.ok_or_else(|| Error::AvailAddress)? as u64,
|
.ok_or(Error::AvailAddress)? as u64,
|
||||||
log_addr: None,
|
log_addr: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ impl VsockPacket {
|
|||||||
|
|
||||||
let mut pkt = Self {
|
let mut pkt = Self {
|
||||||
hdr: get_host_address_range(head.mem, head.addr, VSOCK_PKT_HDR_SIZE)
|
hdr: get_host_address_range(head.mem, head.addr, VSOCK_PKT_HDR_SIZE)
|
||||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
.ok_or(VsockError::GuestMemory)? as *mut u8,
|
||||||
buf: None,
|
buf: None,
|
||||||
buf_size: 0,
|
buf_size: 0,
|
||||||
};
|
};
|
||||||
@@ -151,7 +151,7 @@ impl VsockPacket {
|
|||||||
pkt.buf_size = buf_desc.len as usize;
|
pkt.buf_size = buf_desc.len as usize;
|
||||||
pkt.buf = Some(
|
pkt.buf = Some(
|
||||||
get_host_address_range(buf_desc.mem, buf_desc.addr, pkt.buf_size)
|
get_host_address_range(buf_desc.mem, buf_desc.addr, pkt.buf_size)
|
||||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
.ok_or(VsockError::GuestMemory)? as *mut u8,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(pkt)
|
Ok(pkt)
|
||||||
@@ -183,10 +183,10 @@ impl VsockPacket {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
hdr: get_host_address_range(head.mem, head.addr, VSOCK_PKT_HDR_SIZE)
|
hdr: get_host_address_range(head.mem, head.addr, VSOCK_PKT_HDR_SIZE)
|
||||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
.ok_or(VsockError::GuestMemory)? as *mut u8,
|
||||||
buf: Some(
|
buf: Some(
|
||||||
get_host_address_range(buf_desc.mem, buf_desc.addr, buf_size)
|
get_host_address_range(buf_desc.mem, buf_desc.addr, buf_size)
|
||||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
.ok_or(VsockError::GuestMemory)? as *mut u8,
|
||||||
),
|
),
|
||||||
buf_size,
|
buf_size,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ authors = ["The Chromium OS Authors"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
vm-memory = "0.3.0"
|
vm-memory = "0.4.0"
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ thiserror = "1.0"
|
|||||||
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"
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap"] }
|
vm-memory = { version = "0.4.0", features = ["backend-mmap"] }
|
||||||
vmm-sys-util = ">=0.3.1"
|
vmm-sys-util = ">=0.3.1"
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ impl Bus {
|
|||||||
.range(..=BusRange { base: addr, len: 1 })
|
.range(..=BusRange { base: addr, len: 1 })
|
||||||
.rev()
|
.rev()
|
||||||
.next()?;
|
.next()?;
|
||||||
Some((*range, dev.upgrade().unwrap().clone()))
|
dev.upgrade().map(|d| (*range, d.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
@@ -282,10 +282,11 @@ mod tests {
|
|||||||
assert!(bus.insert(dummy.clone(), 0x0, 0x20).is_err());
|
assert!(bus.insert(dummy.clone(), 0x0, 0x20).is_err());
|
||||||
assert!(bus.insert(dummy.clone(), 0x20, 0x05).is_ok());
|
assert!(bus.insert(dummy.clone(), 0x20, 0x05).is_ok());
|
||||||
assert!(bus.insert(dummy.clone(), 0x25, 0x05).is_ok());
|
assert!(bus.insert(dummy.clone(), 0x25, 0x05).is_ok());
|
||||||
assert!(bus.insert(dummy.clone(), 0x0, 0x10).is_ok());
|
assert!(bus.insert(dummy, 0x0, 0x10).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
fn bus_read_write() {
|
fn bus_read_write() {
|
||||||
let bus = Bus::new();
|
let bus = Bus::new();
|
||||||
let dummy = Arc::new(Mutex::new(DummyDevice));
|
let dummy = Arc::new(Mutex::new(DummyDevice));
|
||||||
@@ -297,12 +298,13 @@ mod tests {
|
|||||||
assert!(bus.read(0x16, &mut [0, 0, 0, 0]).is_ok());
|
assert!(bus.read(0x16, &mut [0, 0, 0, 0]).is_ok());
|
||||||
assert!(bus.write(0x16, &[0, 0, 0, 0]).is_ok());
|
assert!(bus.write(0x16, &[0, 0, 0, 0]).is_ok());
|
||||||
assert!(bus.read(0x20, &mut [0, 0, 0, 0]).is_err());
|
assert!(bus.read(0x20, &mut [0, 0, 0, 0]).is_err());
|
||||||
assert!(bus.write(0x20, &mut [0, 0, 0, 0]).is_err());
|
assert!(bus.write(0x20, &[0, 0, 0, 0]).is_err());
|
||||||
assert!(bus.read(0x06, &mut [0, 0, 0, 0]).is_err());
|
assert!(bus.read(0x06, &mut [0, 0, 0, 0]).is_err());
|
||||||
assert!(bus.write(0x06, &mut [0, 0, 0, 0]).is_err());
|
assert!(bus.write(0x06, &[0, 0, 0, 0]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
fn bus_read_write_values() {
|
fn bus_read_write_values() {
|
||||||
let bus = Bus::new();
|
let bus = Bus::new();
|
||||||
let dummy = Arc::new(Mutex::new(ConstantDevice));
|
let dummy = Arc::new(Mutex::new(ConstantDevice));
|
||||||
@@ -318,6 +320,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::redundant_clone)]
|
||||||
fn busrange_cmp() {
|
fn busrange_cmp() {
|
||||||
let range = BusRange { base: 0x10, len: 2 };
|
let range = BusRange { base: 0x10, len: 2 };
|
||||||
assert_eq!(range, BusRange { base: 0x10, len: 3 });
|
assert_eq!(range, BusRange { base: 0x10, len: 3 });
|
||||||
@@ -332,7 +335,7 @@ mod tests {
|
|||||||
let mut data = [1, 2, 3, 4];
|
let mut data = [1, 2, 3, 4];
|
||||||
let device = Arc::new(Mutex::new(DummyDevice));
|
let device = Arc::new(Mutex::new(DummyDevice));
|
||||||
assert!(bus.insert(device.clone(), 0x10, 0x10).is_ok());
|
assert!(bus.insert(device.clone(), 0x10, 0x10).is_ok());
|
||||||
assert!(bus.write(0x10, &mut data).is_ok());
|
assert!(bus.write(0x10, &data).is_ok());
|
||||||
assert!(bus.read(0x10, &mut data).is_ok());
|
assert!(bus.read(0x10, &mut data).is_ok());
|
||||||
assert_eq!(data, [1, 2, 3, 4]);
|
assert_eq!(data, [1, 2, 3, 4]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ extern crate serde_derive;
|
|||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub mod protocol;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum MigratableError {
|
pub enum MigratableError {
|
||||||
#[error("Failed to pause migratable component: {0}")]
|
#[error("Failed to pause migratable component: {0}")]
|
||||||
@@ -29,6 +31,9 @@ pub enum MigratableError {
|
|||||||
|
|
||||||
#[error("Failed to receive migratable component snapshot: {0}")]
|
#[error("Failed to receive migratable component snapshot: {0}")]
|
||||||
MigrateReceive(#[source] anyhow::Error),
|
MigrateReceive(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Socket error: {0}")]
|
||||||
|
MigrateSocket(#[source] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Pausable component can be paused and resumed.
|
/// A Pausable component can be paused and resumed.
|
||||||
|
|||||||
239
vm-migration/src/protocol.rs
Normal file
239
vm-migration/src/protocol.rs
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
// Copyright © 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::MigratableError;
|
||||||
|
|
||||||
|
// Migration protocol
|
||||||
|
// 1: Source establishes communication with destination (file socket or TCP connection.)
|
||||||
|
// (The establishment is out of scope.)
|
||||||
|
// 2: Source -> Dest : send "start command"
|
||||||
|
// 3: Dest -> Source : sends "ok response" when read to accept state data
|
||||||
|
// 4: Source -> Dest : sends "config command" followed by config data, length
|
||||||
|
// in command is length of config data
|
||||||
|
// 5: Dest -> Source : sends "ok response" when ready to accept memory data
|
||||||
|
// 6: Source -> Dest : send "memory command" followed by table of u64 pairs (GPA, size)
|
||||||
|
// followed by the memory described in those pairs.
|
||||||
|
// !! length is size of table i.e. 16 * number of ranges !!
|
||||||
|
// 7: Dest -> Source : sends "ok response" when ready to accept more memory data
|
||||||
|
// 8..(n-4): Repeat steps 6 and 7 until source has no more memory to send
|
||||||
|
// (n-3): Source -> Dest : sends "state command" followed by state data, length
|
||||||
|
// in command is length of config data
|
||||||
|
// (n-2): Dest -> Source : sends "ok response"
|
||||||
|
// (n-1): Source -> Dest : send "complete command"
|
||||||
|
// n: Dest -> Source: sends "ok response"
|
||||||
|
|
||||||
|
// The destination can at any time send an "error response" to cancel
|
||||||
|
// The source can at any time send an "abandon request" to cancel
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Command {
|
||||||
|
Invalid,
|
||||||
|
Start,
|
||||||
|
Config,
|
||||||
|
State,
|
||||||
|
Memory,
|
||||||
|
Complete,
|
||||||
|
Abandon,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Command {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AsBytes {
|
||||||
|
fn as_bytes<T: Sized>(p: &T) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
std::slice::from_raw_parts((p as *const T) as *const u8, std::mem::size_of::<T>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_mut_bytes<T: Sized>(p: &mut T) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
std::slice::from_raw_parts_mut((p as *mut T) as *mut u8, std::mem::size_of::<T>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Request {
|
||||||
|
command: Command,
|
||||||
|
padding: [u8; 6],
|
||||||
|
length: u64, // Length of payload for command excluding the Request struct
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub fn new(command: Command, length: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
command,
|
||||||
|
length,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start() -> Self {
|
||||||
|
Self::new(Command::Start, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state(length: u64) -> Self {
|
||||||
|
Self::new(Command::State, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config(length: u64) -> Self {
|
||||||
|
Self::new(Command::Config, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory(length: u64) -> Self {
|
||||||
|
Self::new(Command::Memory, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete() -> Self {
|
||||||
|
Self::new(Command::Complete, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn abandon() -> Self {
|
||||||
|
Self::new(Command::Abandon, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn command(&self) -> Command {
|
||||||
|
self.command
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> u64 {
|
||||||
|
self.length
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from(fd: &mut dyn Read) -> Result<Request, MigratableError> {
|
||||||
|
let mut request = Request::default();
|
||||||
|
fd.read_exact(Self::as_mut_bytes(&mut request))
|
||||||
|
.map_err(MigratableError::MigrateSocket)?;
|
||||||
|
|
||||||
|
Ok(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to(&self, fd: &mut dyn Write) -> Result<(), MigratableError> {
|
||||||
|
fd.write_all(Self::as_bytes(self))
|
||||||
|
.map_err(MigratableError::MigrateSocket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsBytes for Request {}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
pub enum Status {
|
||||||
|
Invalid,
|
||||||
|
Ok,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Status {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Response {
|
||||||
|
status: Status,
|
||||||
|
padding: [u8; 6],
|
||||||
|
length: u64, // Length of payload for command excluding the Response struct
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn new(status: Status, length: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
status,
|
||||||
|
length,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ok() -> Self {
|
||||||
|
Self::new(Status::Ok, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error() -> Self {
|
||||||
|
Self::new(Status::Error, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(&self) -> Status {
|
||||||
|
self.status
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from(fd: &mut dyn Read) -> Result<Response, MigratableError> {
|
||||||
|
let mut response = Response::default();
|
||||||
|
fd.read_exact(Self::as_mut_bytes(&mut response))
|
||||||
|
.map_err(MigratableError::MigrateSocket)?;
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to(&self, fd: &mut dyn Write) -> Result<(), MigratableError> {
|
||||||
|
fd.write_all(Self::as_bytes(self))
|
||||||
|
.map_err(MigratableError::MigrateSocket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsBytes for Response {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MemoryRange {
|
||||||
|
pub gpa: u64,
|
||||||
|
pub length: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MemoryRangeTable {
|
||||||
|
data: Vec<MemoryRange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryRangeTable {
|
||||||
|
pub fn regions(&self) -> &[MemoryRange] {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, range: MemoryRange) {
|
||||||
|
self.data.push(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from(fd: &mut dyn Read, length: u64) -> Result<MemoryRangeTable, MigratableError> {
|
||||||
|
assert!(length as usize % std::mem::size_of::<MemoryRange>() == 0);
|
||||||
|
|
||||||
|
let mut data = Vec::with_capacity(length as usize / (std::mem::size_of::<MemoryRange>()));
|
||||||
|
unsafe {
|
||||||
|
data.set_len(length as usize / (std::mem::size_of::<MemoryRange>()));
|
||||||
|
}
|
||||||
|
fd.read_exact(unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(
|
||||||
|
data.as_ptr() as *mut MemoryRange as *mut u8,
|
||||||
|
length as usize,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(MigratableError::MigrateSocket)?;
|
||||||
|
|
||||||
|
Ok(Self { data })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> u64 {
|
||||||
|
(std::mem::size_of::<MemoryRange>() * self.data.len()) as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to(&self, fd: &mut dyn Write) -> Result<(), MigratableError> {
|
||||||
|
fd.write_all(unsafe {
|
||||||
|
std::slice::from_raw_parts(
|
||||||
|
self.data.as_ptr() as *const MemoryRange as *const u8,
|
||||||
|
self.length() as usize,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(MigratableError::MigrateSocket)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,4 +13,4 @@ serde = ">=1.0.27"
|
|||||||
serde_derive = ">=1.0.27"
|
serde_derive = ">=1.0.27"
|
||||||
serde_json = ">=1.0.9"
|
serde_json = ">=1.0.9"
|
||||||
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
virtio-bindings = { version = "0.1", features = ["virtio-v5_0_0"]}
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
@@ -10,12 +10,13 @@ acpi = ["acpi_tables","devices/acpi", "arch/acpi"]
|
|||||||
cmos = ["devices/cmos"]
|
cmos = ["devices/cmos"]
|
||||||
fwdebug = ["devices/fwdebug"]
|
fwdebug = ["devices/fwdebug"]
|
||||||
kvm = ["hypervisor/kvm"]
|
kvm = ["hypervisor/kvm"]
|
||||||
|
mshv = ["hypervisor/mshv"]
|
||||||
io_uring = ["virtio-devices/io_uring"]
|
io_uring = ["virtio-devices/io_uring"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
arc-swap = ">=0.4.4"
|
arc-swap = ">=1.0.0"
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
bitflags = ">=1.2.1"
|
bitflags = ">=1.2.1"
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
@@ -24,7 +25,7 @@ devices = { path = "../devices" }
|
|||||||
epoll = ">=4.0.1"
|
epoll = ">=4.0.1"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2.80"
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "master" }
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "master" }
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
@@ -35,15 +36,15 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v
|
|||||||
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"
|
||||||
signal-hook = "0.1.16"
|
signal-hook = "0.2.1"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
url = "2.1.1"
|
url = "2.2.0"
|
||||||
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
vfio-ioctls = { git = "https://github.com/cloud-hypervisor/vfio-ioctls", branch = "ch" }
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = { version = "0.3.0", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.4.0", features = ["backend-mmap", "backend-atomic"] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
|
vmm-sys-util = { version = ">=0.5.0", features = ["with-serde"] }
|
||||||
|
|||||||
@@ -100,6 +100,12 @@ pub enum HttpError {
|
|||||||
|
|
||||||
/// Could not get counters from VM
|
/// Could not get counters from VM
|
||||||
VmCounters(ApiError),
|
VmCounters(ApiError),
|
||||||
|
|
||||||
|
/// Error setting up migration received
|
||||||
|
VmReceiveMigration(ApiError),
|
||||||
|
|
||||||
|
/// Error setting up migration sender
|
||||||
|
VmSendMigration(ApiError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_json::Error> for HttpError {
|
impl From<serde_json::Error> for HttpError {
|
||||||
@@ -205,11 +211,13 @@ lazy_static! {
|
|||||||
r.routes.insert(endpoint!("/vm.info"), Box::new(VmInfo {}));
|
r.routes.insert(endpoint!("/vm.info"), Box::new(VmInfo {}));
|
||||||
r.routes.insert(endpoint!("/vm.pause"), Box::new(VmActionHandler::new(VmAction::Pause)));
|
r.routes.insert(endpoint!("/vm.pause"), Box::new(VmActionHandler::new(VmAction::Pause)));
|
||||||
r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot)));
|
r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot)));
|
||||||
|
r.routes.insert(endpoint!("/vm.receive-migration"), Box::new(VmActionHandler::new(VmAction::ReceiveMigration(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vm.remove-device"), Box::new(VmActionHandler::new(VmAction::RemoveDevice(Arc::default()))));
|
r.routes.insert(endpoint!("/vm.remove-device"), Box::new(VmActionHandler::new(VmAction::RemoveDevice(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vm.resize"), Box::new(VmActionHandler::new(VmAction::Resize(Arc::default()))));
|
r.routes.insert(endpoint!("/vm.resize"), Box::new(VmActionHandler::new(VmAction::Resize(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vm.resize-zone"), Box::new(VmActionHandler::new(VmAction::ResizeZone(Arc::default()))));
|
r.routes.insert(endpoint!("/vm.resize-zone"), Box::new(VmActionHandler::new(VmAction::ResizeZone(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vm.restore"), Box::new(VmActionHandler::new(VmAction::Restore(Arc::default()))));
|
r.routes.insert(endpoint!("/vm.restore"), Box::new(VmActionHandler::new(VmAction::Restore(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vm.resume"), Box::new(VmActionHandler::new(VmAction::Resume)));
|
r.routes.insert(endpoint!("/vm.resume"), Box::new(VmActionHandler::new(VmAction::Resume)));
|
||||||
|
r.routes.insert(endpoint!("/vm.send-migration"), Box::new(VmActionHandler::new(VmAction::SendMigration(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown)));
|
r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown)));
|
||||||
r.routes.insert(endpoint!("/vm.snapshot"), Box::new(VmActionHandler::new(VmAction::Snapshot(Arc::default()))));
|
r.routes.insert(endpoint!("/vm.snapshot"), Box::new(VmActionHandler::new(VmAction::Snapshot(Arc::default()))));
|
||||||
r.routes.insert(endpoint!("/vmm.ping"), Box::new(VmmPing {}));
|
r.routes.insert(endpoint!("/vmm.ping"), Box::new(VmmPing {}));
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
use crate::api::http::{error_response, EndpointHandler, HttpError};
|
use crate::api::http::{error_response, EndpointHandler, HttpError};
|
||||||
use crate::api::{
|
use crate::api::{
|
||||||
vm_add_device, vm_add_disk, vm_add_fs, vm_add_net, vm_add_pmem, vm_add_vsock, vm_boot,
|
vm_add_device, vm_add_disk, vm_add_fs, vm_add_net, vm_add_pmem, vm_add_vsock, vm_boot,
|
||||||
vm_counters, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_remove_device, vm_resize,
|
vm_counters, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_receive_migration,
|
||||||
vm_resize_zone, vm_restore, vm_resume, vm_shutdown, vm_snapshot, vmm_ping, vmm_shutdown,
|
vm_remove_device, vm_resize, vm_resize_zone, vm_restore, vm_resume, vm_send_migration,
|
||||||
ApiRequest, VmAction, VmConfig,
|
vm_shutdown, vm_snapshot, vmm_ping, vmm_shutdown, ApiRequest, VmAction, VmConfig,
|
||||||
};
|
};
|
||||||
use micro_http::{Body, Method, Request, Response, StatusCode, Version};
|
use micro_http::{Body, Method, Request, Response, StatusCode, Version};
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
@@ -153,6 +153,20 @@ impl EndpointHandler for VmActionHandler {
|
|||||||
)
|
)
|
||||||
.map_err(HttpError::VmSnapshot),
|
.map_err(HttpError::VmSnapshot),
|
||||||
|
|
||||||
|
ReceiveMigration(_) => vm_receive_migration(
|
||||||
|
api_notifier,
|
||||||
|
api_sender,
|
||||||
|
Arc::new(serde_json::from_slice(body.raw())?),
|
||||||
|
)
|
||||||
|
.map_err(HttpError::VmReceiveMigration),
|
||||||
|
|
||||||
|
SendMigration(_) => vm_send_migration(
|
||||||
|
api_notifier,
|
||||||
|
api_sender,
|
||||||
|
Arc::new(serde_json::from_slice(body.raw())?),
|
||||||
|
)
|
||||||
|
.map_err(HttpError::VmSendMigration),
|
||||||
|
|
||||||
_ => Err(HttpError::BadRequest),
|
_ => Err(HttpError::BadRequest),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ pub mod http_endpoint;
|
|||||||
use crate::config::{
|
use crate::config::{
|
||||||
DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, VsockConfig,
|
DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, VmConfig, VsockConfig,
|
||||||
};
|
};
|
||||||
|
use crate::device_tree::DeviceTree;
|
||||||
use crate::vm::{Error as VmError, VmState};
|
use crate::vm::{Error as VmError, VmState};
|
||||||
use micro_http::Body;
|
use micro_http::Body;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::mpsc::{channel, RecvError, SendError, Sender};
|
use std::sync::mpsc::{channel, RecvError, SendError, Sender};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use vm_migration::MigratableError;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
/// API errors are sent back from the VMM API server through the ApiResponse.
|
/// API errors are sent back from the VMM API server through the ApiResponse.
|
||||||
@@ -138,6 +140,12 @@ pub enum ApiError {
|
|||||||
|
|
||||||
/// The vsock device could not be added to the VM.
|
/// The vsock device could not be added to the VM.
|
||||||
VmAddVsock(VmError),
|
VmAddVsock(VmError),
|
||||||
|
|
||||||
|
/// Error starting migration receiever
|
||||||
|
VmReceiveMigration(MigratableError),
|
||||||
|
|
||||||
|
/// Error starting migration sender
|
||||||
|
VmSendMigration(MigratableError),
|
||||||
}
|
}
|
||||||
pub type ApiResult<T> = std::result::Result<T, ApiError>;
|
pub type ApiResult<T> = std::result::Result<T, ApiError>;
|
||||||
|
|
||||||
@@ -146,6 +154,7 @@ pub struct VmInfo {
|
|||||||
pub config: Arc<Mutex<VmConfig>>,
|
pub config: Arc<Mutex<VmConfig>>,
|
||||||
pub state: VmState,
|
pub state: VmState,
|
||||||
pub memory_actual_size: u64,
|
pub memory_actual_size: u64,
|
||||||
|
pub device_tree: Option<Arc<Mutex<DeviceTree>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
@@ -177,6 +186,18 @@ pub struct VmSnapshotConfig {
|
|||||||
pub destination_url: String,
|
pub destination_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Serialize, Default)]
|
||||||
|
pub struct VmReceiveMigrationData {
|
||||||
|
/// URL for the reception of migration state
|
||||||
|
pub receiver_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Serialize, Default)]
|
||||||
|
pub struct VmSendMigrationData {
|
||||||
|
/// URL to migrate the VM to
|
||||||
|
pub destination_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum ApiResponsePayload {
|
pub enum ApiResponsePayload {
|
||||||
/// No data is sent on the channel.
|
/// No data is sent on the channel.
|
||||||
Empty,
|
Empty,
|
||||||
@@ -275,6 +296,12 @@ pub enum ApiRequest {
|
|||||||
|
|
||||||
/// Restore from a VM snapshot
|
/// Restore from a VM snapshot
|
||||||
VmRestore(Arc<RestoreConfig>, Sender<ApiResponse>),
|
VmRestore(Arc<RestoreConfig>, Sender<ApiResponse>),
|
||||||
|
|
||||||
|
/// Incoming migration
|
||||||
|
VmReceiveMigration(Arc<VmReceiveMigrationData>, Sender<ApiResponse>),
|
||||||
|
|
||||||
|
/// Outgoing migration
|
||||||
|
VmSendMigration(Arc<VmSendMigrationData>, Sender<ApiResponse>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vm_create(
|
pub fn vm_create(
|
||||||
@@ -352,6 +379,12 @@ pub enum VmAction {
|
|||||||
|
|
||||||
/// Snapshot VM
|
/// Snapshot VM
|
||||||
Snapshot(Arc<VmSnapshotConfig>),
|
Snapshot(Arc<VmSnapshotConfig>),
|
||||||
|
|
||||||
|
/// Incoming migration
|
||||||
|
ReceiveMigration(Arc<VmReceiveMigrationData>),
|
||||||
|
|
||||||
|
/// Outgoing migration
|
||||||
|
SendMigration(Arc<VmSendMigrationData>),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vm_action(
|
fn vm_action(
|
||||||
@@ -381,6 +414,8 @@ fn vm_action(
|
|||||||
ResizeZone(v) => ApiRequest::VmResizeZone(v, response_sender),
|
ResizeZone(v) => ApiRequest::VmResizeZone(v, response_sender),
|
||||||
Restore(v) => ApiRequest::VmRestore(v, response_sender),
|
Restore(v) => ApiRequest::VmRestore(v, response_sender),
|
||||||
Snapshot(v) => ApiRequest::VmSnapshot(v, response_sender),
|
Snapshot(v) => ApiRequest::VmSnapshot(v, response_sender),
|
||||||
|
ReceiveMigration(v) => ApiRequest::VmReceiveMigration(v, response_sender),
|
||||||
|
SendMigration(v) => ApiRequest::VmSendMigration(v, response_sender),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the VM request.
|
// Send the VM request.
|
||||||
@@ -424,6 +459,22 @@ pub fn vm_counters(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> ApiResul
|
|||||||
vm_action(api_evt, api_sender, VmAction::Counters)
|
vm_action(api_evt, api_sender, VmAction::Counters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn vm_receive_migration(
|
||||||
|
api_evt: EventFd,
|
||||||
|
api_sender: Sender<ApiRequest>,
|
||||||
|
data: Arc<VmReceiveMigrationData>,
|
||||||
|
) -> ApiResult<Option<Body>> {
|
||||||
|
vm_action(api_evt, api_sender, VmAction::ReceiveMigration(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vm_send_migration(
|
||||||
|
api_evt: EventFd,
|
||||||
|
api_sender: Sender<ApiRequest>,
|
||||||
|
data: Arc<VmSendMigrationData>,
|
||||||
|
) -> ApiResult<Option<Body>> {
|
||||||
|
vm_action(api_evt, api_sender, VmAction::SendMigration(data))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn vm_snapshot(
|
pub fn vm_snapshot(
|
||||||
api_evt: EventFd,
|
api_evt: EventFd,
|
||||||
api_sender: Sender<ApiRequest>,
|
api_sender: Sender<ApiRequest>,
|
||||||
|
|||||||
@@ -363,8 +363,30 @@ components:
|
|||||||
memory_actual_size:
|
memory_actual_size:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
|
device_tree:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
$ref: '#/components/schemas/DeviceNode'
|
||||||
description: Virtual Machine information
|
description: Virtual Machine information
|
||||||
|
|
||||||
|
DeviceNode:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
resources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
# Rust enum type (with data) which can't be better represented here
|
||||||
|
type: object
|
||||||
|
children:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
pci_bdf:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
|
||||||
VmCounters:
|
VmCounters:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
@@ -654,8 +676,12 @@ components:
|
|||||||
|
|
||||||
FsConfig:
|
FsConfig:
|
||||||
required:
|
required:
|
||||||
- tag
|
- cache_size
|
||||||
|
- dax
|
||||||
|
- num_queues
|
||||||
|
- queue_size
|
||||||
- socket
|
- socket
|
||||||
|
- tag
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
tag:
|
tag:
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ pub enum ValidationError {
|
|||||||
DiskSocketAndPath,
|
DiskSocketAndPath,
|
||||||
/// Using vhost user requires shared memory
|
/// Using vhost user requires shared memory
|
||||||
VhostUserRequiresSharedMemory,
|
VhostUserRequiresSharedMemory,
|
||||||
|
/// No socket provided for vhost_use
|
||||||
|
VhostUserMissingSocket,
|
||||||
/// Trying to use IOMMU without PCI
|
/// Trying to use IOMMU without PCI
|
||||||
IommuUnsupported,
|
IommuUnsupported,
|
||||||
/// Trying to use VFIO without PCI
|
/// Trying to use VFIO without PCI
|
||||||
@@ -121,6 +123,7 @@ impl fmt::Display for ValidationError {
|
|||||||
VhostUserRequiresSharedMemory => {
|
VhostUserRequiresSharedMemory => {
|
||||||
write!(f, "Using vhost-user requires using shared memory")
|
write!(f, "Using vhost-user requires using shared memory")
|
||||||
}
|
}
|
||||||
|
VhostUserMissingSocket => write!(f, "No socket provided when using vhost-user"),
|
||||||
IommuUnsupported => write!(f, "Using an IOMMU without PCI support is unsupported"),
|
IommuUnsupported => write!(f, "Using an IOMMU without PCI support is unsupported"),
|
||||||
VfioUnsupported => write!(f, "Using VFIO without PCI support is unsupported"),
|
VfioUnsupported => write!(f, "Using VFIO without PCI support is unsupported"),
|
||||||
CpuTopologyZeroPart => write!(f, "No part of the CPU topology can be zero"),
|
CpuTopologyZeroPart => write!(f, "No part of the CPU topology can be zero"),
|
||||||
@@ -619,6 +622,9 @@ pub struct DiskConfig {
|
|||||||
pub poll_queue: bool,
|
pub poll_queue: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub id: Option<String>,
|
pub id: Option<String>,
|
||||||
|
// For testing use only. Not exposed in API.
|
||||||
|
#[serde(default)]
|
||||||
|
pub disable_io_uring: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_diskconfig_num_queues() -> usize {
|
fn default_diskconfig_num_queues() -> usize {
|
||||||
@@ -646,6 +652,7 @@ impl Default for DiskConfig {
|
|||||||
vhost_socket: None,
|
vhost_socket: None,
|
||||||
poll_queue: default_diskconfig_poll_queue(),
|
poll_queue: default_diskconfig_poll_queue(),
|
||||||
id: None,
|
id: None,
|
||||||
|
disable_io_uring: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -668,7 +675,8 @@ impl DiskConfig {
|
|||||||
.add("vhost_user")
|
.add("vhost_user")
|
||||||
.add("socket")
|
.add("socket")
|
||||||
.add("poll_queue")
|
.add("poll_queue")
|
||||||
.add("id");
|
.add("id")
|
||||||
|
.add("_disable_io_uring");
|
||||||
parser.parse(disk).map_err(Error::ParseDisk)?;
|
parser.parse(disk).map_err(Error::ParseDisk)?;
|
||||||
|
|
||||||
let path = parser.get("path").map(PathBuf::from);
|
let path = parser.get("path").map(PathBuf::from);
|
||||||
@@ -707,6 +715,11 @@ impl DiskConfig {
|
|||||||
.unwrap_or_else(|| Toggle(default_diskconfig_poll_queue()))
|
.unwrap_or_else(|| Toggle(default_diskconfig_poll_queue()))
|
||||||
.0;
|
.0;
|
||||||
let id = parser.get("id");
|
let id = parser.get("id");
|
||||||
|
let disable_io_uring = parser
|
||||||
|
.convert::<Toggle>("_disable_io_uring")
|
||||||
|
.map_err(Error::ParseDisk)?
|
||||||
|
.unwrap_or(Toggle(false))
|
||||||
|
.0;
|
||||||
|
|
||||||
if parser.is_set("poll_queue") && !vhost_user {
|
if parser.is_set("poll_queue") && !vhost_user {
|
||||||
warn!("poll_queue parameter currently only has effect when used vhost_user=true");
|
warn!("poll_queue parameter currently only has effect when used vhost_user=true");
|
||||||
@@ -723,6 +736,7 @@ impl DiskConfig {
|
|||||||
vhost_user,
|
vhost_user,
|
||||||
poll_queue,
|
poll_queue,
|
||||||
id,
|
id,
|
||||||
|
disable_io_uring,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1453,6 +1467,9 @@ impl VmConfig {
|
|||||||
if disk.vhost_user && !self.memory.shared {
|
if disk.vhost_user && !self.memory.shared {
|
||||||
return Err(ValidationError::VhostUserRequiresSharedMemory);
|
return Err(ValidationError::VhostUserRequiresSharedMemory);
|
||||||
}
|
}
|
||||||
|
if disk.vhost_user && disk.vhost_socket.is_none() {
|
||||||
|
return Err(ValidationError::VhostUserMissingSocket);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1788,9 +1805,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DiskConfig::parse("path=/path/to_file,vhost_user=true")?,
|
DiskConfig::parse("vhost_user=true,socket=/tmp/sock")?,
|
||||||
DiskConfig {
|
DiskConfig {
|
||||||
path: Some(PathBuf::from("/path/to_file")),
|
vhost_socket: Some(String::from("/tmp/sock")),
|
||||||
vhost_user: true,
|
vhost_user: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@@ -2264,9 +2281,18 @@ mod tests {
|
|||||||
}]);
|
}]);
|
||||||
assert!(invalid_config.validate().is_err());
|
assert!(invalid_config.validate().is_err());
|
||||||
|
|
||||||
|
let mut invalid_config = valid_config.clone();
|
||||||
|
invalid_config.disks = Some(vec![DiskConfig {
|
||||||
|
vhost_user: true,
|
||||||
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
}]);
|
||||||
|
assert!(invalid_config.validate().is_err());
|
||||||
|
|
||||||
let mut still_valid_config = valid_config.clone();
|
let mut still_valid_config = valid_config.clone();
|
||||||
still_valid_config.disks = Some(vec![DiskConfig {
|
still_valid_config.disks = Some(vec![DiskConfig {
|
||||||
vhost_user: true,
|
vhost_user: true,
|
||||||
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}]);
|
}]);
|
||||||
still_valid_config.memory.shared = true;
|
still_valid_config.memory.shared = true;
|
||||||
@@ -2282,6 +2308,7 @@ mod tests {
|
|||||||
let mut still_valid_config = valid_config.clone();
|
let mut still_valid_config = valid_config.clone();
|
||||||
still_valid_config.net = Some(vec![NetConfig {
|
still_valid_config.net = Some(vec![NetConfig {
|
||||||
vhost_user: true,
|
vhost_user: true,
|
||||||
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}]);
|
}]);
|
||||||
still_valid_config.memory.shared = true;
|
still_valid_config.memory.shared = true;
|
||||||
@@ -2293,7 +2320,7 @@ mod tests {
|
|||||||
}]);
|
}]);
|
||||||
assert!(invalid_config.validate().is_err());
|
assert!(invalid_config.validate().is_err());
|
||||||
|
|
||||||
let mut still_valid_config = valid_config.clone();
|
let mut still_valid_config = valid_config;
|
||||||
invalid_config.fs = Some(vec![FsConfig {
|
invalid_config.fs = Some(vec![FsConfig {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
131
vmm/src/cpu.rs
131
vmm/src/cpu.rs
@@ -34,10 +34,9 @@ use devices::interrupt_controller::InterruptController;
|
|||||||
use hypervisor::kvm::kvm_bindings;
|
use hypervisor::kvm::kvm_bindings;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use hypervisor::CpuId;
|
use hypervisor::CpuId;
|
||||||
use hypervisor::{CpuState, VmExit};
|
use hypervisor::{vm::VmmOps, CpuState, HypervisorCpuError, VmExit};
|
||||||
use seccomp::{SeccompAction, SeccompFilter};
|
|
||||||
|
|
||||||
use libc::{c_void, siginfo_t};
|
use libc::{c_void, siginfo_t};
|
||||||
|
use seccomp::{SeccompAction, SeccompFilter};
|
||||||
use std::os::unix::thread::JoinHandleExt;
|
use std::os::unix::thread::JoinHandleExt;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Barrier, Mutex};
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
@@ -213,8 +212,6 @@ pub struct Vcpu {
|
|||||||
// The hypervisor abstracted CPU.
|
// The hypervisor abstracted CPU.
|
||||||
vcpu: Arc<dyn hypervisor::Vcpu>,
|
vcpu: Arc<dyn hypervisor::Vcpu>,
|
||||||
id: u8,
|
id: u8,
|
||||||
#[cfg_attr(target_arch = "aarch64", allow(dead_code))]
|
|
||||||
interrupt_controller: Option<Arc<Mutex<dyn InterruptController>>>,
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mpidr: u64,
|
mpidr: u64,
|
||||||
saved_state: Option<CpuState>,
|
saved_state: Option<CpuState>,
|
||||||
@@ -227,19 +224,19 @@ impl Vcpu {
|
|||||||
///
|
///
|
||||||
/// * `id` - Represents the CPU number between [0, max vcpus).
|
/// * `id` - Represents the CPU number between [0, max vcpus).
|
||||||
/// * `vm` - The virtual machine this vcpu will get attached to.
|
/// * `vm` - The virtual machine this vcpu will get attached to.
|
||||||
|
/// * `vmmops` - Optional object for exit handling.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: u8,
|
id: u8,
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
interrupt_controller: Option<Arc<Mutex<dyn InterruptController>>>,
|
vmmops: Option<Arc<Box<dyn VmmOps>>>,
|
||||||
) -> Result<Arc<Mutex<Self>>> {
|
) -> Result<Arc<Mutex<Self>>> {
|
||||||
let vcpu = vm
|
let vcpu = vm
|
||||||
.create_vcpu(id)
|
.create_vcpu(id, vmmops)
|
||||||
.map_err(|e| Error::VcpuCreate(e.into()))?;
|
.map_err(|e| Error::VcpuCreate(e.into()))?;
|
||||||
// Initially the cpuid per vCPU is the one supported by this VM.
|
// Initially the cpuid per vCPU is the one supported by this VM.
|
||||||
Ok(Arc::new(Mutex::new(Vcpu {
|
Ok(Arc::new(Mutex::new(Vcpu {
|
||||||
vcpu,
|
vcpu,
|
||||||
id,
|
id,
|
||||||
interrupt_controller,
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mpidr: 0,
|
mpidr: 0,
|
||||||
saved_state: None,
|
saved_state: None,
|
||||||
@@ -323,28 +320,8 @@ impl Vcpu {
|
|||||||
///
|
///
|
||||||
/// Note that the state of the VCPU and associated VM must be setup first for this to do
|
/// Note that the state of the VCPU and associated VM must be setup first for this to do
|
||||||
/// anything useful.
|
/// anything useful.
|
||||||
pub fn run(&self) -> Result<bool> {
|
pub fn run(&self) -> std::result::Result<VmExit, HypervisorCpuError> {
|
||||||
match self.vcpu.run() {
|
self.vcpu.run()
|
||||||
Ok(run) => match run {
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
VmExit::IoapicEoi(vector) => {
|
|
||||||
if let Some(interrupt_controller) = &self.interrupt_controller {
|
|
||||||
interrupt_controller
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.end_of_interrupt(vector);
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
VmExit::Ignore => Ok(true),
|
|
||||||
VmExit::Reset => Ok(false),
|
|
||||||
// No need to handle anything from a KVM HyperV exit
|
|
||||||
VmExit::Hyperv => Ok(true),
|
|
||||||
_ => Err(Error::UnexpectedVmExit),
|
|
||||||
},
|
|
||||||
|
|
||||||
Err(e) => Err(Error::VcpuRun(e.into())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,12 +402,14 @@ pub struct CpuManager {
|
|||||||
vm: Arc<dyn hypervisor::Vm>,
|
vm: Arc<dyn hypervisor::Vm>,
|
||||||
vcpus_kill_signalled: Arc<AtomicBool>,
|
vcpus_kill_signalled: Arc<AtomicBool>,
|
||||||
vcpus_pause_signalled: Arc<AtomicBool>,
|
vcpus_pause_signalled: Arc<AtomicBool>,
|
||||||
|
exit_evt: EventFd,
|
||||||
#[cfg_attr(target_arch = "aarch64", allow(dead_code))]
|
#[cfg_attr(target_arch = "aarch64", allow(dead_code))]
|
||||||
reset_evt: EventFd,
|
reset_evt: EventFd,
|
||||||
vcpu_states: Vec<VcpuState>,
|
vcpu_states: Vec<VcpuState>,
|
||||||
selected_cpu: u8,
|
selected_cpu: u8,
|
||||||
vcpus: Vec<Arc<Mutex<Vcpu>>>,
|
vcpus: Vec<Arc<Mutex<Vcpu>>>,
|
||||||
seccomp_action: SeccompAction,
|
seccomp_action: SeccompAction,
|
||||||
|
vmmops: Arc<Box<dyn VmmOps>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CPU_ENABLE_FLAG: usize = 0;
|
const CPU_ENABLE_FLAG: usize = 0;
|
||||||
@@ -550,14 +529,17 @@ impl VcpuState {
|
|||||||
|
|
||||||
impl CpuManager {
|
impl CpuManager {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: &CpusConfig,
|
config: &CpusConfig,
|
||||||
device_manager: &Arc<Mutex<DeviceManager>>,
|
device_manager: &Arc<Mutex<DeviceManager>>,
|
||||||
memory_manager: &Arc<Mutex<MemoryManager>>,
|
memory_manager: &Arc<Mutex<MemoryManager>>,
|
||||||
vm: Arc<dyn hypervisor::Vm>,
|
vm: Arc<dyn hypervisor::Vm>,
|
||||||
|
exit_evt: EventFd,
|
||||||
reset_evt: EventFd,
|
reset_evt: EventFd,
|
||||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||||
seccomp_action: SeccompAction,
|
seccomp_action: SeccompAction,
|
||||||
|
vmmops: Arc<Box<dyn VmmOps>>,
|
||||||
) -> Result<Arc<Mutex<CpuManager>>> {
|
) -> Result<Arc<Mutex<CpuManager>>> {
|
||||||
let guest_memory = memory_manager.lock().unwrap().guest_memory();
|
let guest_memory = memory_manager.lock().unwrap().guest_memory();
|
||||||
let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus));
|
let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus));
|
||||||
@@ -584,10 +566,12 @@ impl CpuManager {
|
|||||||
vcpus_kill_signalled: Arc::new(AtomicBool::new(false)),
|
vcpus_kill_signalled: Arc::new(AtomicBool::new(false)),
|
||||||
vcpus_pause_signalled: Arc::new(AtomicBool::new(false)),
|
vcpus_pause_signalled: Arc::new(AtomicBool::new(false)),
|
||||||
vcpu_states,
|
vcpu_states,
|
||||||
|
exit_evt,
|
||||||
reset_evt,
|
reset_evt,
|
||||||
selected_cpu: 0,
|
selected_cpu: 0,
|
||||||
vcpus: Vec::with_capacity(usize::from(config.max_vcpus)),
|
vcpus: Vec::with_capacity(usize::from(config.max_vcpus)),
|
||||||
seccomp_action,
|
seccomp_action,
|
||||||
|
vmmops,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -679,13 +663,7 @@ impl CpuManager {
|
|||||||
) -> Result<Arc<Mutex<Vcpu>>> {
|
) -> Result<Arc<Mutex<Vcpu>>> {
|
||||||
info!("Creating vCPU: cpu_id = {}", cpu_id);
|
info!("Creating vCPU: cpu_id = {}", cpu_id);
|
||||||
|
|
||||||
let interrupt_controller = if let Some(interrupt_controller) = &self.interrupt_controller {
|
let vcpu = Vcpu::new(cpu_id, &self.vm, Some(self.vmmops.clone()))?;
|
||||||
Some(interrupt_controller.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let vcpu = Vcpu::new(cpu_id, &self.vm, interrupt_controller)?;
|
|
||||||
|
|
||||||
if let Some(snapshot) = snapshot {
|
if let Some(snapshot) = snapshot {
|
||||||
// AArch64 vCPUs should be initialized after created.
|
// AArch64 vCPUs should be initialized after created.
|
||||||
@@ -756,6 +734,7 @@ impl CpuManager {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cpu_id = vcpu.lock().unwrap().id;
|
let cpu_id = vcpu.lock().unwrap().id;
|
||||||
let reset_evt = self.reset_evt.try_clone().unwrap();
|
let reset_evt = self.reset_evt.try_clone().unwrap();
|
||||||
|
let exit_evt = self.exit_evt.try_clone().unwrap();
|
||||||
let vcpu_kill_signalled = self.vcpus_kill_signalled.clone();
|
let vcpu_kill_signalled = self.vcpus_kill_signalled.clone();
|
||||||
let vcpu_pause_signalled = self.vcpus_pause_signalled.clone();
|
let vcpu_pause_signalled = self.vcpus_pause_signalled.clone();
|
||||||
|
|
||||||
@@ -770,6 +749,14 @@ impl CpuManager {
|
|||||||
let vcpu_seccomp_filter = get_seccomp_filter(&self.seccomp_action, Thread::Vcpu)
|
let vcpu_seccomp_filter = get_seccomp_filter(&self.seccomp_action, Thread::Vcpu)
|
||||||
.map_err(Error::CreateSeccompFilter)?;
|
.map_err(Error::CreateSeccompFilter)?;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
let interrupt_controller_clone =
|
||||||
|
if let Some(interrupt_controller) = &self.interrupt_controller {
|
||||||
|
Some(interrupt_controller.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let handle = Some(
|
let handle = Some(
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(format!("vcpu{}", cpu_id))
|
.name(format!("vcpu{}", cpu_id))
|
||||||
@@ -798,6 +785,11 @@ impl CpuManager {
|
|||||||
// We enter a loop because park() could spuriously
|
// We enter a loop because park() could spuriously
|
||||||
// return. We will then park() again unless the
|
// return. We will then park() again unless the
|
||||||
// pause boolean has been toggled.
|
// pause boolean has been toggled.
|
||||||
|
|
||||||
|
// Need to use Ordering::SeqCst as we have multiple
|
||||||
|
// loads and stores to different atomics and we need
|
||||||
|
// to see them in a consistent order in all threads
|
||||||
|
|
||||||
if vcpu_pause_signalled.load(Ordering::SeqCst) {
|
if vcpu_pause_signalled.load(Ordering::SeqCst) {
|
||||||
vcpu_run_interrupted.store(true, Ordering::SeqCst);
|
vcpu_run_interrupted.store(true, Ordering::SeqCst);
|
||||||
while vcpu_pause_signalled.load(Ordering::SeqCst) {
|
while vcpu_pause_signalled.load(Ordering::SeqCst) {
|
||||||
@@ -816,14 +808,39 @@ impl CpuManager {
|
|||||||
|
|
||||||
// vcpu.run() returns false on a triple-fault so trigger a reset
|
// vcpu.run() returns false on a triple-fault so trigger a reset
|
||||||
match vcpu.lock().unwrap().run() {
|
match vcpu.lock().unwrap().run() {
|
||||||
|
Ok(run) => match run {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
VmExit::IoapicEoi(vector) => {
|
||||||
|
if let Some(interrupt_controller) = &interrupt_controller_clone
|
||||||
|
{
|
||||||
|
interrupt_controller
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.end_of_interrupt(vector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VmExit::Ignore => {}
|
||||||
|
VmExit::Hyperv => {}
|
||||||
|
VmExit::Reset => {
|
||||||
|
debug!("VmExit::Reset");
|
||||||
|
vcpu_run_interrupted.store(true, Ordering::SeqCst);
|
||||||
|
reset_evt.write(1).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
VmExit::Shutdown => {
|
||||||
|
debug!("VmExit::Shutdown");
|
||||||
|
vcpu_run_interrupted.store(true, Ordering::SeqCst);
|
||||||
|
exit_evt.write(1).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("VCPU generated error: {:?}", Error::UnexpectedVmExit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("VCPU generated error: {:?}", e);
|
error!("VCPU generated error: {:?}", Error::VcpuRun(e.into()));
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(true) => {}
|
|
||||||
Ok(false) => {
|
|
||||||
vcpu_run_interrupted.store(true, Ordering::SeqCst);
|
|
||||||
reset_evt.write(1).unwrap();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1343,7 +1360,7 @@ impl Pausable for CpuManager {
|
|||||||
for vcpu in self.vcpus.iter() {
|
for vcpu in self.vcpus.iter() {
|
||||||
let mut vcpu = vcpu.lock().unwrap();
|
let mut vcpu = vcpu.lock().unwrap();
|
||||||
vcpu.pause()?;
|
vcpu.pause()?;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
if !self.config.kvm_hyperv {
|
if !self.config.kvm_hyperv {
|
||||||
vcpu.vcpu.notify_guest_clock_paused().map_err(|e| {
|
vcpu.vcpu.notify_guest_clock_paused().map_err(|e| {
|
||||||
MigratableError::Pause(anyhow!(
|
MigratableError::Pause(anyhow!(
|
||||||
@@ -1407,7 +1424,7 @@ impl Snapshottable for CpuManager {
|
|||||||
impl Transportable for CpuManager {}
|
impl Transportable for CpuManager {}
|
||||||
impl Migratable for CpuManager {}
|
impl Migratable for CpuManager {}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
@@ -1424,7 +1441,7 @@ mod tests {
|
|||||||
assert!(hv.check_capability(hypervisor::kvm::Cap::Irqchip));
|
assert!(hv.check_capability(hypervisor::kvm::Cap::Irqchip));
|
||||||
// Calling get_lapic will fail if there is no irqchip before hand.
|
// Calling get_lapic will fail if there is no irqchip before hand.
|
||||||
assert!(vm.create_irq_chip().is_ok());
|
assert!(vm.create_irq_chip().is_ok());
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
let klapic_before: LapicState = vcpu.get_lapic().unwrap();
|
let klapic_before: LapicState = vcpu.get_lapic().unwrap();
|
||||||
|
|
||||||
// Compute the value that is expected to represent LVT0 and LVT1.
|
// Compute the value that is expected to represent LVT0 and LVT1.
|
||||||
@@ -1447,7 +1464,7 @@ mod tests {
|
|||||||
fn test_setup_fpu() {
|
fn test_setup_fpu() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
setup_fpu(&vcpu).unwrap();
|
setup_fpu(&vcpu).unwrap();
|
||||||
|
|
||||||
let expected_fpu: FpuState = FpuState {
|
let expected_fpu: FpuState = FpuState {
|
||||||
@@ -1472,7 +1489,7 @@ mod tests {
|
|||||||
|
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
setup_msrs(&vcpu).unwrap();
|
setup_msrs(&vcpu).unwrap();
|
||||||
|
|
||||||
// This test will check against the last MSR entry configured (the tenth one).
|
// This test will check against the last MSR entry configured (the tenth one).
|
||||||
@@ -1498,7 +1515,7 @@ mod tests {
|
|||||||
fn test_setup_regs() {
|
fn test_setup_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
|
|
||||||
let expected_regs: StandardRegisters = StandardRegisters {
|
let expected_regs: StandardRegisters = StandardRegisters {
|
||||||
rflags: 0x0000000000000002u64,
|
rflags: 0x0000000000000002u64,
|
||||||
@@ -1526,10 +1543,10 @@ mod tests {
|
|||||||
fn test_setup_sregs() {
|
fn test_setup_sregs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().expect("new VM fd creation failed");
|
let vm = hv.create_vm().expect("new VM fd creation failed");
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
|
|
||||||
let mut expected_sregs: SpecialRegisters = vcpu.get_sregs().unwrap();
|
let mut expected_sregs: SpecialRegisters = vcpu.get_sregs().unwrap();
|
||||||
let gm = GuestMemoryMmap::from_ranges(&vec![(GuestAddress(0), 0x10000)]).unwrap();
|
let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
|
||||||
configure_segments_and_sregs(&gm, &mut expected_sregs, BootProtocol::LinuxBoot).unwrap();
|
configure_segments_and_sregs(&gm, &mut expected_sregs, BootProtocol::LinuxBoot).unwrap();
|
||||||
setup_page_tables(&gm, &mut expected_sregs).unwrap();
|
setup_page_tables(&gm, &mut expected_sregs).unwrap();
|
||||||
|
|
||||||
@@ -1557,7 +1574,7 @@ mod tests {
|
|||||||
fn test_setup_regs() {
|
fn test_setup_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
let mut regions = Vec::new();
|
let mut regions = Vec::new();
|
||||||
regions.push((
|
regions.push((
|
||||||
GuestAddress(layout::RAM_64BIT_START),
|
GuestAddress(layout::RAM_64BIT_START),
|
||||||
@@ -1580,7 +1597,7 @@ mod tests {
|
|||||||
fn test_read_mpidr() {
|
fn test_read_mpidr() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
||||||
vm.get_preferred_target(&mut kvi).unwrap();
|
vm.get_preferred_target(&mut kvi).unwrap();
|
||||||
|
|
||||||
@@ -1604,7 +1621,7 @@ mod tests {
|
|||||||
fn test_save_restore_core_regs() {
|
fn test_save_restore_core_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
||||||
vm.get_preferred_target(&mut kvi).unwrap();
|
vm.get_preferred_target(&mut kvi).unwrap();
|
||||||
|
|
||||||
@@ -1640,7 +1657,7 @@ mod tests {
|
|||||||
fn test_save_restore_system_regs() {
|
fn test_save_restore_system_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
||||||
vm.get_preferred_target(&mut kvi).unwrap();
|
vm.get_preferred_target(&mut kvi).unwrap();
|
||||||
|
|
||||||
@@ -1681,7 +1698,7 @@ mod tests {
|
|||||||
fn test_get_set_mpstate() {
|
fn test_get_set_mpstate() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let vcpu = vm.create_vcpu(0).unwrap();
|
let vcpu = vm.create_vcpu(0, None).unwrap();
|
||||||
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
let mut kvi: kvm_vcpu_init = kvm_vcpu_init::default();
|
||||||
vm.get_preferred_target(&mut kvi).unwrap();
|
vm.get_preferred_target(&mut kvi).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ use crate::config::{DiskConfig, FsConfig, NetConfig, PmemConfig, VmConfig, Vsock
|
|||||||
use crate::device_tree::{DeviceNode, DeviceTree};
|
use crate::device_tree::{DeviceNode, DeviceTree};
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
use crate::interrupt::kvm::KvmMsiInterruptManager as MsiInterruptManager;
|
use crate::interrupt::kvm::KvmMsiInterruptManager as MsiInterruptManager;
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
use crate::interrupt::mshv::MshvMsiInterruptManager as MsiInterruptManager;
|
||||||
use crate::interrupt::LegacyUserspaceInterruptManager;
|
use crate::interrupt::LegacyUserspaceInterruptManager;
|
||||||
use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
|
use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
@@ -43,10 +45,12 @@ use devices::{
|
|||||||
interrupt_controller, interrupt_controller::InterruptController, legacy::Serial,
|
interrupt_controller, interrupt_controller::InterruptController, legacy::Serial,
|
||||||
HotPlugNotificationFlags,
|
HotPlugNotificationFlags,
|
||||||
};
|
};
|
||||||
use hypervisor::kvm_ioctls;
|
#[cfg(feature = "kvm")]
|
||||||
use hypervisor::kvm_ioctls::*;
|
use hypervisor::kvm_ioctls::*;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use hypervisor::CpuState;
|
use hypervisor::CpuState;
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
use hypervisor::IoEventAddress;
|
||||||
use libc::TIOCGWINSZ;
|
use libc::TIOCGWINSZ;
|
||||||
use libc::{MAP_NORESERVE, MAP_PRIVATE, MAP_SHARED, O_TMPFILE, PROT_READ, PROT_WRITE};
|
use libc::{MAP_NORESERVE, MAP_PRIVATE, MAP_SHARED, O_TMPFILE, PROT_READ, PROT_WRITE};
|
||||||
use pci::{
|
use pci::{
|
||||||
@@ -63,10 +67,8 @@ use std::num::Wrapping;
|
|||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
use std::os::unix::io::FromRawFd;
|
use std::os::unix::io::FromRawFd;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
use vfio_ioctls::{VfioContainer, VfioDevice, VfioDmaMapping};
|
use vfio_ioctls::{VfioContainer, VfioDevice, VfioDmaMapping};
|
||||||
use virtio_devices::transport::VirtioPciDevice;
|
use virtio_devices::transport::VirtioPciDevice;
|
||||||
@@ -183,7 +185,7 @@ pub enum DeviceManagerError {
|
|||||||
AllocateIrq,
|
AllocateIrq,
|
||||||
|
|
||||||
/// Cannot configure the IRQ.
|
/// Cannot configure the IRQ.
|
||||||
Irq(kvm_ioctls::Error),
|
Irq(vmm_sys_util::errno::Error),
|
||||||
|
|
||||||
/// Cannot allocate PCI BARs
|
/// Cannot allocate PCI BARs
|
||||||
AllocateBars(pci::PciDeviceError),
|
AllocateBars(pci::PciDeviceError),
|
||||||
@@ -614,6 +616,7 @@ impl DeviceRelocation for AddressManager {
|
|||||||
0,
|
0,
|
||||||
shm_regions.host_addr,
|
shm_regions.host_addr,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.vm.set_user_memory_region(mem_region).map_err(|e| {
|
self.vm.set_user_memory_region(mem_region).map_err(|e| {
|
||||||
@@ -630,6 +633,7 @@ impl DeviceRelocation for AddressManager {
|
|||||||
shm_regions.len,
|
shm_regions.len,
|
||||||
shm_regions.host_addr,
|
shm_regions.host_addr,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.vm.set_user_memory_region(mem_region).map_err(|e| {
|
self.vm.set_user_memory_region(mem_region).map_err(|e| {
|
||||||
@@ -656,17 +660,6 @@ impl DeviceRelocation for AddressManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ActivatedBackend {
|
|
||||||
_socket_file: tempfile::NamedTempFile,
|
|
||||||
child: std::process::Child,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ActivatedBackend {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.child.wait().ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct DeviceManagerState {
|
struct DeviceManagerState {
|
||||||
device_tree: DeviceTree,
|
device_tree: DeviceTree,
|
||||||
@@ -733,12 +726,6 @@ pub struct DeviceManager {
|
|||||||
// which prevents cyclic dependencies.
|
// which prevents cyclic dependencies.
|
||||||
bus_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
|
bus_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
|
||||||
|
|
||||||
// The path to the VMM for self spawning
|
|
||||||
vmm_path: PathBuf,
|
|
||||||
|
|
||||||
// Backends that have been spawned
|
|
||||||
vhost_user_backends: Vec<ActivatedBackend>,
|
|
||||||
|
|
||||||
// Counter to keep track of the consumed device IDs.
|
// Counter to keep track of the consumed device IDs.
|
||||||
device_id_cnt: Wrapping<usize>,
|
device_id_cnt: Wrapping<usize>,
|
||||||
|
|
||||||
@@ -799,7 +786,6 @@ impl DeviceManager {
|
|||||||
memory_manager: Arc<Mutex<MemoryManager>>,
|
memory_manager: Arc<Mutex<MemoryManager>>,
|
||||||
_exit_evt: &EventFd,
|
_exit_evt: &EventFd,
|
||||||
reset_evt: &EventFd,
|
reset_evt: &EventFd,
|
||||||
vmm_path: PathBuf,
|
|
||||||
seccomp_action: SeccompAction,
|
seccomp_action: SeccompAction,
|
||||||
#[cfg(feature = "acpi")] numa_nodes: NumaNodes,
|
#[cfg(feature = "acpi")] numa_nodes: NumaNodes,
|
||||||
) -> DeviceManagerResult<Arc<Mutex<Self>>> {
|
) -> DeviceManagerResult<Arc<Mutex<Self>>> {
|
||||||
@@ -839,8 +825,6 @@ impl DeviceManager {
|
|||||||
memory_manager,
|
memory_manager,
|
||||||
virtio_devices: Vec::new(),
|
virtio_devices: Vec::new(),
|
||||||
bus_devices: Vec::new(),
|
bus_devices: Vec::new(),
|
||||||
vmm_path,
|
|
||||||
vhost_user_backends: Vec::new(),
|
|
||||||
device_id_cnt: Wrapping(0),
|
device_id_cnt: Wrapping(0),
|
||||||
pci_bus: None,
|
pci_bus: None,
|
||||||
msi_interrupt_manager,
|
msi_interrupt_manager,
|
||||||
@@ -1580,39 +1564,6 @@ impl DeviceManager {
|
|||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launch block backend
|
|
||||||
fn start_block_backend(&mut self, disk_cfg: &DiskConfig) -> DeviceManagerResult<String> {
|
|
||||||
let _socket_file = NamedTempFile::new().map_err(DeviceManagerError::CreateSocketFile)?;
|
|
||||||
let socket = _socket_file.path().to_str().unwrap().to_owned();
|
|
||||||
|
|
||||||
let child = std::process::Command::new(&self.vmm_path)
|
|
||||||
.args(&[
|
|
||||||
"--block-backend",
|
|
||||||
&format!(
|
|
||||||
"path={},socket={},num_queues={},queue_size={}",
|
|
||||||
disk_cfg
|
|
||||||
.path
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(DeviceManagerError::NoDiskPath)?
|
|
||||||
.to_str()
|
|
||||||
.unwrap(),
|
|
||||||
&socket,
|
|
||||||
disk_cfg.num_queues,
|
|
||||||
disk_cfg.queue_size
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.spawn()
|
|
||||||
.map_err(DeviceManagerError::SpawnBlockBackend)?;
|
|
||||||
|
|
||||||
// The ActivatedBackend::drop() will automatically reap the child
|
|
||||||
self.vhost_user_backends.push(ActivatedBackend {
|
|
||||||
child,
|
|
||||||
_socket_file,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(socket)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_virtio_block_device(
|
fn make_virtio_block_device(
|
||||||
&mut self,
|
&mut self,
|
||||||
disk_cfg: &mut DiskConfig,
|
disk_cfg: &mut DiskConfig,
|
||||||
@@ -1626,14 +1577,9 @@ impl DeviceManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if disk_cfg.vhost_user {
|
if disk_cfg.vhost_user {
|
||||||
let socket = if let Some(socket) = disk_cfg.vhost_socket.clone() {
|
let socket = disk_cfg.vhost_socket.as_ref().unwrap().clone();
|
||||||
socket
|
|
||||||
} else {
|
|
||||||
warn!("Self-spawning of vhost-user block backend is deprecated and will be removed in a future release.");
|
|
||||||
self.start_block_backend(disk_cfg)?
|
|
||||||
};
|
|
||||||
let vu_cfg = VhostUserConfig {
|
let vu_cfg = VhostUserConfig {
|
||||||
socket: socket.clone(),
|
socket,
|
||||||
num_queues: disk_cfg.num_queues,
|
num_queues: disk_cfg.num_queues,
|
||||||
queue_size: disk_cfg.queue_size,
|
queue_size: disk_cfg.queue_size,
|
||||||
};
|
};
|
||||||
@@ -1645,11 +1591,6 @@ impl DeviceManager {
|
|||||||
) {
|
) {
|
||||||
Ok(vub_device) => vub_device,
|
Ok(vub_device) => vub_device,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
for vub in self.vhost_user_backends.iter_mut() {
|
|
||||||
if vub._socket_file.path().to_str().unwrap() == socket {
|
|
||||||
let _ = vub.child.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Err(DeviceManagerError::CreateVhostUserBlk(e));
|
return Err(DeviceManagerError::CreateVhostUserBlk(e));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1694,7 +1635,7 @@ impl DeviceManager {
|
|||||||
ImageType::Raw => {
|
ImageType::Raw => {
|
||||||
// Use asynchronous backend relying on io_uring if the
|
// Use asynchronous backend relying on io_uring if the
|
||||||
// syscalls are supported.
|
// syscalls are supported.
|
||||||
if block_io_uring_is_supported() {
|
if block_io_uring_is_supported() && !disk_cfg.disable_io_uring {
|
||||||
let dev = Arc::new(Mutex::new(
|
let dev = Arc::new(Mutex::new(
|
||||||
virtio_devices::BlockIoUring::new(
|
virtio_devices::BlockIoUring::new(
|
||||||
id.clone(),
|
id.clone(),
|
||||||
@@ -1797,40 +1738,6 @@ impl DeviceManager {
|
|||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launch network backend
|
|
||||||
fn start_net_backend(&mut self, net_cfg: &NetConfig) -> DeviceManagerResult<String> {
|
|
||||||
let _socket_file = NamedTempFile::new().map_err(DeviceManagerError::CreateSocketFile)?;
|
|
||||||
let socket = _socket_file.path().to_str().unwrap().to_owned();
|
|
||||||
|
|
||||||
let child = std::process::Command::new(&self.vmm_path)
|
|
||||||
.args(&[
|
|
||||||
"--net-backend",
|
|
||||||
&format!(
|
|
||||||
"ip={},mask={},socket={},num_queues={},queue_size={}{}",
|
|
||||||
net_cfg.ip,
|
|
||||||
net_cfg.mask,
|
|
||||||
&socket,
|
|
||||||
net_cfg.num_queues,
|
|
||||||
net_cfg.queue_size,
|
|
||||||
if let Some(mac) = net_cfg.host_mac {
|
|
||||||
format!(",host_mac={:}", mac)
|
|
||||||
} else {
|
|
||||||
"".to_owned()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.spawn()
|
|
||||||
.map_err(DeviceManagerError::SpawnNetBackend)?;
|
|
||||||
|
|
||||||
// The ActivatedBackend::drop() will automatically reap the child
|
|
||||||
self.vhost_user_backends.push(ActivatedBackend {
|
|
||||||
child,
|
|
||||||
_socket_file,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(socket)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_virtio_net_device(
|
fn make_virtio_net_device(
|
||||||
&mut self,
|
&mut self,
|
||||||
net_cfg: &mut NetConfig,
|
net_cfg: &mut NetConfig,
|
||||||
@@ -1844,14 +1751,9 @@ impl DeviceManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if net_cfg.vhost_user {
|
if net_cfg.vhost_user {
|
||||||
let socket = if let Some(socket) = net_cfg.vhost_socket.clone() {
|
let socket = net_cfg.vhost_socket.as_ref().unwrap().clone();
|
||||||
socket
|
|
||||||
} else {
|
|
||||||
warn!("Self-spawning of vhost-user net backend is deprecated and will be removed in a future release.");
|
|
||||||
self.start_net_backend(net_cfg)?
|
|
||||||
};
|
|
||||||
let vu_cfg = VhostUserConfig {
|
let vu_cfg = VhostUserConfig {
|
||||||
socket: socket.clone(),
|
socket,
|
||||||
num_queues: net_cfg.num_queues,
|
num_queues: net_cfg.num_queues,
|
||||||
queue_size: net_cfg.queue_size,
|
queue_size: net_cfg.queue_size,
|
||||||
};
|
};
|
||||||
@@ -1864,11 +1766,6 @@ impl DeviceManager {
|
|||||||
) {
|
) {
|
||||||
Ok(vun_device) => vun_device,
|
Ok(vun_device) => vun_device,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
for vun in self.vhost_user_backends.iter_mut() {
|
|
||||||
if vun._socket_file.path().to_str().unwrap() == socket {
|
|
||||||
let _ = vun.child.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Err(DeviceManagerError::CreateVhostUserNet(e));
|
return Err(DeviceManagerError::CreateVhostUserNet(e));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2086,7 +1983,9 @@ impl DeviceManager {
|
|||||||
.memory_manager
|
.memory_manager
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.create_userspace_mapping(cache_base, cache_size, host_addr, false, false)
|
.create_userspace_mapping(
|
||||||
|
cache_base, cache_size, host_addr, false, false, false,
|
||||||
|
)
|
||||||
.map_err(DeviceManagerError::MemoryManager)?;
|
.map_err(DeviceManagerError::MemoryManager)?;
|
||||||
|
|
||||||
let mut region_list = Vec::new();
|
let mut region_list = Vec::new();
|
||||||
@@ -2277,6 +2176,7 @@ impl DeviceManager {
|
|||||||
host_addr,
|
host_addr,
|
||||||
pmem_cfg.mergeable,
|
pmem_cfg.mergeable,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.map_err(DeviceManagerError::MemoryManager)?;
|
.map_err(DeviceManagerError::MemoryManager)?;
|
||||||
|
|
||||||
@@ -2643,6 +2543,15 @@ impl DeviceManager {
|
|||||||
})
|
})
|
||||||
.map_err(DeviceManagerError::VfioMapRegion)?;
|
.map_err(DeviceManagerError::VfioMapRegion)?;
|
||||||
|
|
||||||
|
let mut node = device_node!(vfio_name);
|
||||||
|
|
||||||
|
for region in vfio_pci_device.mmio_regions() {
|
||||||
|
node.resources.push(Resource::MmioAddressRange {
|
||||||
|
base: region.start.0,
|
||||||
|
size: region.length as u64,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let vfio_pci_device = Arc::new(Mutex::new(vfio_pci_device));
|
let vfio_pci_device = Arc::new(Mutex::new(vfio_pci_device));
|
||||||
|
|
||||||
self.add_pci_device(
|
self.add_pci_device(
|
||||||
@@ -2653,6 +2562,10 @@ impl DeviceManager {
|
|||||||
pci_device_bdf,
|
pci_device_bdf,
|
||||||
vfio_name.clone(),
|
vfio_name.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
self.device_tree
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(vfio_name.clone(), node);
|
||||||
|
|
||||||
Ok((pci_device_bdf, vfio_name))
|
Ok((pci_device_bdf, vfio_name))
|
||||||
}
|
}
|
||||||
@@ -3225,6 +3138,10 @@ impl DeviceManager {
|
|||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn device_tree(&self) -> Arc<Mutex<DeviceTree>> {
|
||||||
|
self.device_tree.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ impl InterruptRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
|
pub fn enable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
|
||||||
if !self.registered.load(Ordering::SeqCst) {
|
if !self.registered.load(Ordering::Acquire) {
|
||||||
vm.register_irqfd(&self.irq_fd, self.gsi).map_err(|e| {
|
vm.register_irqfd(&self.irq_fd, self.gsi).map_err(|e| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
@@ -48,14 +48,14 @@ impl InterruptRoute {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Update internals to track the irq_fd as "registered".
|
// Update internals to track the irq_fd as "registered".
|
||||||
self.registered.store(true, Ordering::SeqCst);
|
self.registered.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
|
pub fn disable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
|
||||||
if self.registered.load(Ordering::SeqCst) {
|
if self.registered.load(Ordering::Acquire) {
|
||||||
vm.unregister_irqfd(&self.irq_fd, self.gsi).map_err(|e| {
|
vm.unregister_irqfd(&self.irq_fd, self.gsi).map_err(|e| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
@@ -64,7 +64,7 @@ impl InterruptRoute {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Update internals to track the irq_fd as "unregistered".
|
// Update internals to track the irq_fd as "unregistered".
|
||||||
self.registered.store(false, Ordering::SeqCst);
|
self.registered.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -325,6 +325,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
pub mod kvm {
|
pub mod kvm {
|
||||||
use super::*;
|
use super::*;
|
||||||
use hypervisor::kvm::KVM_MSI_VALID_DEVID;
|
use hypervisor::kvm::KVM_MSI_VALID_DEVID;
|
||||||
@@ -395,6 +396,69 @@ pub mod kvm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mshv")]
|
||||||
|
pub mod mshv {
|
||||||
|
use super::*;
|
||||||
|
use hypervisor::mshv::*;
|
||||||
|
|
||||||
|
type MshvMsiInterruptGroup = MsiInterruptGroup<MshvIrqRoutingEntry>;
|
||||||
|
type MshvRoutingEntry = RoutingEntry<MshvIrqRoutingEntry>;
|
||||||
|
pub type MshvMsiInterruptManager = MsiInterruptManager<MshvIrqRoutingEntry>;
|
||||||
|
|
||||||
|
impl RoutingEntryExt for MshvRoutingEntry {
|
||||||
|
fn make_entry(
|
||||||
|
_vm: &Arc<dyn hypervisor::Vm>,
|
||||||
|
gsi: u32,
|
||||||
|
config: &InterruptSourceConfig,
|
||||||
|
) -> Result<Box<Self>> {
|
||||||
|
if let InterruptSourceConfig::MsiIrq(cfg) = &config {
|
||||||
|
let route = MshvIrqRoutingEntry {
|
||||||
|
gsi,
|
||||||
|
route: MshvIrqRouting::Msi(MshvIrqRoutingMsi {
|
||||||
|
address_lo: cfg.low_addr,
|
||||||
|
address_hi: cfg.high_addr,
|
||||||
|
data: cfg.data,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let entry = MshvRoutingEntry {
|
||||||
|
route,
|
||||||
|
masked: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(Box::new(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Interrupt config type not supported",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MsiInterruptGroupOps<MshvIrqRoutingEntry> for MshvMsiInterruptGroup {
|
||||||
|
fn set_gsi_routes(
|
||||||
|
&self,
|
||||||
|
routes: &HashMap<u32, RoutingEntry<MshvIrqRoutingEntry>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut entry_vec: Vec<MshvIrqRoutingEntry> = Vec::new();
|
||||||
|
for (_, entry) in routes.iter() {
|
||||||
|
if entry.masked {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_vec.push(entry.route);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vm.set_gsi_routing(&entry_vec).map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("Failed setting GSI routing: {}", e),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -415,7 +479,7 @@ mod tests {
|
|||||||
fn test_get_set_dist_regs() {
|
fn test_get_set_dist_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
||||||
|
|
||||||
let res = get_dist_regs(gic.device());
|
let res = get_dist_regs(gic.device());
|
||||||
@@ -431,7 +495,7 @@ mod tests {
|
|||||||
fn test_get_set_redist_regs() {
|
fn test_get_set_redist_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
||||||
|
|
||||||
let mut gicr_typer = Vec::new();
|
let mut gicr_typer = Vec::new();
|
||||||
@@ -449,7 +513,7 @@ mod tests {
|
|||||||
fn test_get_set_icc_regs() {
|
fn test_get_set_icc_regs() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
||||||
|
|
||||||
let mut gicr_typer = Vec::new();
|
let mut gicr_typer = Vec::new();
|
||||||
@@ -467,7 +531,7 @@ mod tests {
|
|||||||
fn test_save_pending_tables() {
|
fn test_save_pending_tables() {
|
||||||
let hv = hypervisor::new().unwrap();
|
let hv = hypervisor::new().unwrap();
|
||||||
let vm = hv.create_vm().unwrap();
|
let vm = hv.create_vm().unwrap();
|
||||||
let _ = vm.create_vcpu(0).unwrap();
|
let _ = vm.create_vcpu(0, None).unwrap();
|
||||||
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
let gic = create_gic(&vm, 1).expect("Cannot create gic");
|
||||||
|
|
||||||
assert!(save_pending_tables(gic.device()).is_ok());
|
assert!(save_pending_tables(gic.device()).is_ok());
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user