mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
292 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea3e2ff625 | ||
|
|
3320015793 | ||
|
|
9113544e21 | ||
|
|
8c526891ac | ||
|
|
78a30012fb | ||
|
|
c5c751c478 | ||
|
|
f92cce888b | ||
|
|
00b4d97826 | ||
|
|
3fe7d6d904 | ||
|
|
02f146fef8 | ||
|
|
36cdd67b9c | ||
|
|
92b12ce2f4 | ||
|
|
cbb588c380 | ||
|
|
1c7997c5c3 | ||
|
|
fee769bed4 | ||
|
|
6b2c2c938a | ||
|
|
1e967697c2 | ||
|
|
959ea7115f | ||
|
|
398e9338a7 | ||
|
|
5c4b5c0e40 | ||
|
|
f9bd2aaf9f | ||
|
|
f126288159 | ||
|
|
9f9a4e657f | ||
|
|
3097d78c6e | ||
|
|
7c2a252c02 | ||
|
|
a87b25a962 | ||
|
|
24ed063dbe | ||
|
|
fbac81843f | ||
|
|
610a2234dc | ||
|
|
5f18ac3bc0 | ||
|
|
4bf2d4f7dd | ||
|
|
954f3dd057 | ||
|
|
026e2c6aa8 | ||
|
|
be9f57dcf9 | ||
|
|
bd180bc3eb | ||
|
|
b9f086bcb3 | ||
|
|
d2f0e8aebb | ||
|
|
513973873c | ||
|
|
4c52fa344e | ||
|
|
e18d32bac2 | ||
|
|
59185050d5 | ||
|
|
dc72ef42dc | ||
|
|
68bb32a3e7 | ||
|
|
0f89387475 | ||
|
|
f5b2eb5c76 | ||
|
|
82ac114b8a | ||
|
|
2ffd5df018 | ||
|
|
006a12d911 | ||
|
|
bb55976454 | ||
|
|
a15e041138 | ||
|
|
0fa96402b8 | ||
|
|
bc0ef1d8ba | ||
|
|
86153225cf | ||
|
|
ddc3f194aa | ||
|
|
c6cc3d3ba9 | ||
|
|
0fbb3e2c2c | ||
|
|
330e1aac36 | ||
|
|
5fddf76a3b | ||
|
|
b50dfb3538 | ||
|
|
502995746a | ||
|
|
8aa2d7ab2a | ||
|
|
bd8c28d341 | ||
|
|
422906a0c7 | ||
|
|
b7512263be | ||
|
|
7e749c0390 | ||
|
|
61a5bae25c | ||
|
|
a2438700e4 | ||
|
|
bb4af57219 | ||
|
|
824e83ab0d | ||
|
|
29675cfe68 | ||
|
|
43632f255b | ||
|
|
a1941ad10b | ||
|
|
70db454b90 | ||
|
|
7b3ffd89a5 | ||
|
|
c1f18fa634 | ||
|
|
efb92d409f | ||
|
|
e4aa3617c7 | ||
|
|
d7e8cd8258 | ||
|
|
8803e4a2e7 | ||
|
|
d2a01f7cec | ||
|
|
dec4a82058 | ||
|
|
5fc143205c | ||
|
|
b73d94f969 | ||
|
|
ead4f767ff | ||
|
|
bc6acb842f | ||
|
|
d89f1f4f21 | ||
|
|
dc90744ec3 | ||
|
|
18340d9761 | ||
|
|
cfaa192eb4 | ||
|
|
c67f799717 | ||
|
|
b47efc3bc2 | ||
|
|
498f35a1ab | ||
|
|
2d61bc36ed | ||
|
|
46447962b8 | ||
|
|
9a6bc025a7 | ||
|
|
de1abe0e30 | ||
|
|
e97cee99ef | ||
|
|
3103526153 | ||
|
|
08ff89ba6e | ||
|
|
514f36fb5a | ||
|
|
fe3506976e | ||
|
|
94929889ac | ||
|
|
8452edfcc7 | ||
|
|
466cc5e043 | ||
|
|
034c674c4c | ||
|
|
b785e00317 | ||
|
|
7f6731cd05 | ||
|
|
457fd9ef96 | ||
|
|
eea45a2c78 | ||
|
|
11c17ca319 | ||
|
|
249e362c70 | ||
|
|
b3e5738b40 | ||
|
|
1dd53c3d24 | ||
|
|
130c988380 | ||
|
|
8c76a3e4b5 | ||
|
|
af5a9677c8 | ||
|
|
1d89f98edf | ||
|
|
287dbd4fc9 | ||
|
|
c50ea2c708 | ||
|
|
14b45e4d2e | ||
|
|
519476e842 | ||
|
|
5fec858130 | ||
|
|
67f22b6aa4 | ||
|
|
1eb4133034 | ||
|
|
9f3bea3e3f | ||
|
|
e7c7a304e8 | ||
|
|
b5cce0d371 | ||
|
|
7c608f6380 | ||
|
|
3ad8d24943 | ||
|
|
56c6c02724 | ||
|
|
19b0ea842b | ||
|
|
16bd88b4b5 | ||
|
|
09136c50b5 | ||
|
|
380b7d398c | ||
|
|
764959c9a8 | ||
|
|
7d84654a79 | ||
|
|
246cb94dc5 | ||
|
|
165c2c476f | ||
|
|
5e8f380698 | ||
|
|
e9b2639757 | ||
|
|
98d0aabb99 | ||
|
|
d206586683 | ||
|
|
19f2800d9e | ||
|
|
08cf983d42 | ||
|
|
243dc5918c | ||
|
|
544de7d000 | ||
|
|
796db588ea | ||
|
|
397c76dd20 | ||
|
|
cdd3ff5e5a | ||
|
|
6cb76abbf1 | ||
|
|
3a5e5364b9 | ||
|
|
f2b2d033f7 | ||
|
|
c41461c9c2 | ||
|
|
9257322330 | ||
|
|
220455caaf | ||
|
|
c206c14318 | ||
|
|
777757254b | ||
|
|
9c42d98e6a | ||
|
|
254db7b96a | ||
|
|
b3a73d6634 | ||
|
|
9cd214b0a1 | ||
|
|
9f969ee18d | ||
|
|
c3fcddf830 | ||
|
|
11d98fccac | ||
|
|
a8fa2af64b | ||
|
|
dc723171a7 | ||
|
|
52eebaf6b2 | ||
|
|
380ba564f4 | ||
|
|
385f9a9aa9 | ||
|
|
d784bf0c75 | ||
|
|
cf6115a73c | ||
|
|
0991b881de | ||
|
|
e048da6f73 | ||
|
|
6a08133efb | ||
|
|
2e50f5769f | ||
|
|
42e9632c53 | ||
|
|
46c5fb5f2c | ||
|
|
81b41b55fd | ||
|
|
112a971c56 | ||
|
|
9b87a7e699 | ||
|
|
1bd8afeffa | ||
|
|
3414586995 | ||
|
|
06e8d1c40c | ||
|
|
b70b8dfa4c | ||
|
|
57e450a7a8 | ||
|
|
d82846c954 | ||
|
|
714b61846c | ||
|
|
6bb3ad1b96 | ||
|
|
dd820d69ba | ||
|
|
77c80f67ed | ||
|
|
eb4dd90532 | ||
|
|
d3cd25566b | ||
|
|
925ab090af | ||
|
|
8caf29ec01 | ||
|
|
091ce85473 | ||
|
|
133ac463c2 | ||
|
|
400837ff99 | ||
|
|
a9e41c417a | ||
|
|
1845e63e7c | ||
|
|
f13d8f1412 | ||
|
|
bfe0106566 | ||
|
|
7c46e610e7 | ||
|
|
b13de200f5 | ||
|
|
c3d69a9bac | ||
|
|
51a9f78625 | ||
|
|
89b429c768 | ||
|
|
f25315d151 | ||
|
|
193c006669 | ||
|
|
5d0d56f50b | ||
|
|
30b6e412af | ||
|
|
6c90623c8e | ||
|
|
94fe22da62 | ||
|
|
036e7e3797 | ||
|
|
5a811af099 | ||
|
|
7ac2e56fa6 | ||
|
|
eb8f959361 | ||
|
|
27fd114cbf | ||
|
|
ba488f5c33 | ||
|
|
af1f94e5b8 | ||
|
|
22621df084 | ||
|
|
972b4bf248 | ||
|
|
f4aa3de335 | ||
|
|
b2d25a582d | ||
|
|
a3262ad570 | ||
|
|
034283fc83 | ||
|
|
860939d677 | ||
|
|
a5a41bf797 | ||
|
|
aabfc9513e | ||
|
|
3fe9b87736 | ||
|
|
9e338d86f5 | ||
|
|
5f9c6bc84f | ||
|
|
de38e4b777 | ||
|
|
6c4947e9ba | ||
|
|
63df8599dc | ||
|
|
b6003cb428 | ||
|
|
340a51d08e | ||
|
|
507a03e7ee | ||
|
|
fe86f90ba6 | ||
|
|
3769b18a70 | ||
|
|
6d74393833 | ||
|
|
26670e4778 | ||
|
|
555c4c41ab | ||
|
|
b82f25572b | ||
|
|
584784a0f8 | ||
|
|
4fd5070f5d | ||
|
|
241d1d5cdb | ||
|
|
c07671edb4 | ||
|
|
8093820965 | ||
|
|
86cf50565e | ||
|
|
95fd684ad7 | ||
|
|
ce8e76cf94 | ||
|
|
3f8cd52ffd | ||
|
|
f9d3c73c15 | ||
|
|
7e25cc2aa0 | ||
|
|
8b86c7724b | ||
|
|
ea23c16c5a | ||
|
|
3def18f502 | ||
|
|
2bf6f9300a | ||
|
|
fd43b79f96 | ||
|
|
a70808bae9 | ||
|
|
97d617f071 | ||
|
|
0194a635f7 | ||
|
|
e5d0acf0a9 | ||
|
|
622a1a3735 | ||
|
|
f5abb168e3 | ||
|
|
1e3ef1a548 | ||
|
|
275a63655b | ||
|
|
16651db712 | ||
|
|
d03e43facc | ||
|
|
c2ed82f3af | ||
|
|
5fc71dec34 | ||
|
|
d10f20eb71 | ||
|
|
6c0dedd560 | ||
|
|
b29edfbee8 | ||
|
|
75e1dc2bce | ||
|
|
6ba4f6de95 | ||
|
|
de1f055481 | ||
|
|
1a94c6c932 | ||
|
|
1aeaee221d | ||
|
|
862a056105 | ||
|
|
fe704bd44a | ||
|
|
f6cd3bd86d | ||
|
|
0816c8292f | ||
|
|
319538fbed | ||
|
|
4f1e74b553 | ||
|
|
24f8d15b14 | ||
|
|
f4b0443489 | ||
|
|
030d84eb08 | ||
|
|
4847f5c4f6 | ||
|
|
bf6c9e6447 | ||
|
|
f6d99d9a9b | ||
|
|
e38a69f0d2 |
5
.github/workflows/build.yaml
vendored
5
.github/workflows/build.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- nightly
|
- nightly
|
||||||
- "1.74.1"
|
- "1.77.0"
|
||||||
target:
|
target:
|
||||||
- x86_64-unknown-linux-gnu
|
- x86_64-unknown-linux-gnu
|
||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
@@ -49,6 +49,9 @@ jobs:
|
|||||||
- name: Build (default features + guest_debug)
|
- name: Build (default features + guest_debug)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
- name: Build (default features + pvmemcontrol)
|
||||||
|
run: cargo rustc --locked --bin cloud-hypervisor --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Build (mshv)
|
- name: Build (mshv)
|
||||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/docker-image.yaml
vendored
2
.github/workflows/docker-image.yaml
vendored
@@ -41,7 +41,7 @@ jobs:
|
|||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
# generate Docker tags based on the following events/attributes
|
# generate Docker tags based on the following events/attributes
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=20240407-0
|
type=raw,value=20240507-0
|
||||||
type=sha
|
type=sha
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
|
|||||||
7
.github/workflows/quality.yaml
vendored
7
.github/workflows/quality.yaml
vendored
@@ -80,6 +80,13 @@ jobs:
|
|||||||
command: clippy
|
command: clippy
|
||||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
|
- name: Clippy (default features + pvmemcontrol)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||||
|
command: clippy
|
||||||
|
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks
|
||||||
|
|
||||||
- name: Clippy (default features + tracing)
|
- name: Clippy (default features + tracing)
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
3
.github/workflows/release.yaml
vendored
3
.github/workflows/release.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
target: ${{ matrix.platform.target }}
|
target: ${{ matrix.platform.target }}
|
||||||
args: ${{ matrix.platform.args }}
|
args: ${{ matrix.platform.args }}
|
||||||
strip: true
|
strip: true
|
||||||
toolchain: 1.74.1
|
toolchain: "1.77.0"
|
||||||
- name: Copy Release Binaries
|
- name: Copy Release Binaries
|
||||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -88,6 +88,7 @@ jobs:
|
|||||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
./${{ matrix.platform.name_ch }}
|
./${{ matrix.platform.name_ch }}
|
||||||
./${{ matrix.platform.name_ch_remote }}
|
./${{ matrix.platform.name_ch_remote }}
|
||||||
|
|||||||
21
.github/workflows/taplo.yaml
vendored
Normal file
21
.github/workflows/taplo.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Cargo.toml Formatting (taplo)
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**/Cargo.toml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cargo_toml_format:
|
||||||
|
name: Cargo.toml Formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Code checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get -yqq install build-essential libssl-dev
|
||||||
|
- name: Install taplo
|
||||||
|
run: cargo install taplo-cli --locked
|
||||||
|
- name: Check formatting
|
||||||
|
run: taplo fmt --check
|
||||||
5
.taplo.toml
Normal file
5
.taplo.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
include = ["**/Cargo.toml"]
|
||||||
|
|
||||||
|
[formatting]
|
||||||
|
reorder_arrays = true
|
||||||
|
reorder_keys = true
|
||||||
@@ -112,5 +112,5 @@ Fixes #88
|
|||||||
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
|
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, after the corresponding PR is merged, Github will automatically close that issue when parsing the
|
Then, after the corresponding PR is merged, GitHub will automatically close that issue when parsing the
|
||||||
[commit message](https://help.github.com/articles/closing-issues-via-commit-messages/).
|
[commit message](https://help.github.com/articles/closing-issues-via-commit-messages/).
|
||||||
|
|||||||
715
Cargo.lock
generated
715
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
95
Cargo.toml
95
Cargo.toml
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor"
|
|
||||||
version = "39.0.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
|
||||||
default-run = "cloud-hypervisor"
|
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
license = "LICENSE-APACHE & LICENSE-BSD-3-Clause"
|
default-run = "cloud-hypervisor"
|
||||||
description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM"
|
description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM & MSHV"
|
||||||
|
edition = "2021"
|
||||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||||
|
license = "Apache-2.0 AND BSD-3-Clause"
|
||||||
|
name = "cloud-hypervisor"
|
||||||
|
version = "41.0.0"
|
||||||
# Minimum buildable version:
|
# Minimum buildable version:
|
||||||
# Keep in sync with version in .github/workflows/build.yaml
|
# Keep in sync with version in .github/workflows/build.yaml
|
||||||
# Policy on MSRV (see #4318):
|
# Policy on MSRV (see #4318):
|
||||||
@@ -15,87 +15,88 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
|||||||
# a.) A dependency requires it,
|
# a.) A dependency requires it,
|
||||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||||
# c.) There is a security issue that is addressed by the toolchain update.
|
# c.) There is a security issue that is addressed by the toolchain update.
|
||||||
rust-version = "1.74.1"
|
rust-version = "1.77.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
[profile.profiling]
|
[profile.profiling]
|
||||||
|
debug = true
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
strip = false
|
strip = false
|
||||||
debug = true
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.86"
|
||||||
api_client = { path = "api_client" }
|
api_client = { path = "api_client" }
|
||||||
clap = { version = "4.5.4", features = ["string"] }
|
clap = { version = "4.5.4", features = ["string"] }
|
||||||
dhat = { version = "0.3.3", optional = true }
|
dhat = { version = "0.3.3", optional = true }
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
event_monitor = { path = "event_monitor" }
|
event_monitor = { path = "event_monitor" }
|
||||||
hypervisor = { path = "hypervisor" }
|
hypervisor = { path = "hypervisor" }
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
log = { version = "0.4.21", features = ["std"] }
|
log = { version = "0.4.22", features = ["std"] }
|
||||||
option_parser = { path = "option_parser" }
|
option_parser = { path = "option_parser" }
|
||||||
seccompiler = "0.4.0"
|
seccompiler = "0.4.0"
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.120"
|
||||||
signal-hook = "0.3.17"
|
signal-hook = "0.3.17"
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
tpm = { path = "tpm"}
|
tpm = { path = "tpm" }
|
||||||
tracer = { path = "tracer" }
|
tracer = { path = "tracer" }
|
||||||
|
vm-memory = "0.14.1"
|
||||||
vmm = { path = "vmm" }
|
vmm = { path = "vmm" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
vm-memory = "0.14.1"
|
zbus = { version = "4.1.2", optional = true }
|
||||||
zbus = { version = "3.15.2", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
net_util = { path = "net_util" }
|
net_util = { path = "net_util" }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.120"
|
||||||
test_infra = { path = "test_infra" }
|
test_infra = { path = "test_infra" }
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
|
||||||
# Please adjust `vmm::feature_list()` accordingly when changing the
|
# Please adjust `vmm::feature_list()` accordingly when changing the
|
||||||
# feature list below
|
# feature list below
|
||||||
[features]
|
[features]
|
||||||
default = ["kvm", "io_uring"]
|
dbus_api = ["vmm/dbus_api", "zbus"]
|
||||||
dbus_api = ["zbus", "vmm/dbus_api"]
|
default = ["io_uring", "kvm"]
|
||||||
dhat-heap = ["dhat"] # For heap profiling
|
dhat-heap = ["dhat", "vmm/dhat-heap"] # For heap profiling
|
||||||
guest_debug = ["vmm/guest_debug"]
|
guest_debug = ["vmm/guest_debug"]
|
||||||
igvm = ["vmm/igvm", "mshv"]
|
igvm = ["mshv", "vmm/igvm"]
|
||||||
io_uring = ["vmm/io_uring"]
|
io_uring = ["vmm/io_uring"]
|
||||||
kvm = ["vmm/kvm"]
|
kvm = ["vmm/kvm"]
|
||||||
mshv = ["vmm/mshv"]
|
mshv = ["vmm/mshv"]
|
||||||
sev_snp = ["igvm", "vmm/sev_snp", "mshv"]
|
pvmemcontrol = ["vmm/pvmemcontrol"]
|
||||||
|
sev_snp = ["igvm", "mshv", "vmm/sev_snp"]
|
||||||
tdx = ["vmm/tdx"]
|
tdx = ["vmm/tdx"]
|
||||||
tracing = ["vmm/tracing", "tracer/tracing"]
|
tracing = ["tracer/tracing", "vmm/tracing"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"api_client",
|
"api_client",
|
||||||
"arch",
|
"arch",
|
||||||
"block",
|
"block",
|
||||||
"devices",
|
"devices",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"net_gen",
|
"net_gen",
|
||||||
"net_util",
|
"net_util",
|
||||||
"option_parser",
|
"option_parser",
|
||||||
"pci",
|
"pci",
|
||||||
"performance-metrics",
|
"performance-metrics",
|
||||||
"rate_limiter",
|
"rate_limiter",
|
||||||
"serial_buffer",
|
"serial_buffer",
|
||||||
"test_infra",
|
"test_infra",
|
||||||
"tracer",
|
"tracer",
|
||||||
"vhost_user_block",
|
"vhost_user_block",
|
||||||
"vhost_user_net",
|
"vhost_user_net",
|
||||||
"virtio-devices",
|
"virtio-devices",
|
||||||
"vmm",
|
"vm-allocator",
|
||||||
"vm-allocator",
|
"vm-device",
|
||||||
"vm-device",
|
"vm-migration",
|
||||||
"vm-migration",
|
"vm-virtio",
|
||||||
"vm-virtio"
|
"vmm",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "api_client"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "api_client"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
thiserror = "1.0.62"
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
|
|||||||
@@ -3,41 +3,27 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
use thiserror::Error;
|
||||||
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Error writing to or reading from HTTP socket: {0}")]
|
||||||
Socket(std::io::Error),
|
Socket(std::io::Error),
|
||||||
|
#[error("Error sending file descriptors: {0}")]
|
||||||
SocketSendFds(vmm_sys_util::errno::Error),
|
SocketSendFds(vmm_sys_util::errno::Error),
|
||||||
|
#[error("Error parsing HTTP status code: {0}")]
|
||||||
StatusCodeParsing(std::num::ParseIntError),
|
StatusCodeParsing(std::num::ParseIntError),
|
||||||
|
#[error("HTTP output is missing protocol statement")]
|
||||||
MissingProtocol,
|
MissingProtocol,
|
||||||
|
#[error("Error parsing HTTP Content-Length field: {0}")]
|
||||||
ContentLengthParsing(std::num::ParseIntError),
|
ContentLengthParsing(std::num::ParseIntError),
|
||||||
|
#[error("Server responded with an error: {0:?}")]
|
||||||
ServerResponse(StatusCode, Option<String>),
|
ServerResponse(StatusCode, Option<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use Error::*;
|
|
||||||
match self {
|
|
||||||
Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {e}"),
|
|
||||||
SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {e}"),
|
|
||||||
StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {e}"),
|
|
||||||
MissingProtocol => write!(f, "HTTP output is missing protocol statement"),
|
|
||||||
ContentLengthParsing(e) => write!(f, "Error parsing HTTP Content-Length field: {e}"),
|
|
||||||
ServerResponse(s, o) => {
|
|
||||||
if let Some(o) = o {
|
|
||||||
write!(f, "Server responded with an error: {s:?}: {o}")
|
|
||||||
} else {
|
|
||||||
write!(f, "Server responded with an error: {s:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum StatusCode {
|
pub enum StatusCode {
|
||||||
Continue,
|
Continue,
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "arch"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Chromium OS Authors"]
|
authors = ["The Chromium OS Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "arch"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
kvm = []
|
||||||
sev_snp = []
|
sev_snp = []
|
||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.86"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
linux-loader = { version = "0.11.0", features = ["elf", "bzimage", "pe"] }
|
linux-loader = { version = "0.11.0", features = ["bzimage", "elf", "pe"] }
|
||||||
log = "0.4.21"
|
log = "0.4.22"
|
||||||
serde = { version = "1.0.197", features = ["rc", "derive"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
uuid = "1.8.0"
|
uuid = "1.8.0"
|
||||||
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-bitmap"] }
|
vm-memory = { version = "0.14.1", features = [
|
||||||
|
"backend-bitmap",
|
||||||
|
"backend-mmap",
|
||||||
|
] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
|
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use super::layout::{
|
|||||||
};
|
};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use thiserror::Error;
|
||||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||||
|
|
||||||
@@ -80,9 +81,10 @@ pub trait DeviceInfoForFdt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Errors thrown while configuring the Flattened Device Tree for aarch64.
|
/// Errors thrown while configuring the Flattened Device Tree for aarch64.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failure in writing FDT in memory.
|
/// Failure in writing FDT in memory.
|
||||||
|
#[error("Failure in writing FDT in memory: {0}")]
|
||||||
WriteFdtToMemory(GuestMemoryError),
|
WriteFdtToMemory(GuestMemoryError),
|
||||||
}
|
}
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
@@ -523,7 +525,7 @@ fn create_memory_node(
|
|||||||
let memory_region_size: u64 = memory_region.size() as u64;
|
let memory_region_size: u64 = memory_region.size() as u64;
|
||||||
mem_reg_prop.push(memory_region_start_addr);
|
mem_reg_prop.push(memory_region_start_addr);
|
||||||
mem_reg_prop.push(memory_region_size);
|
mem_reg_prop.push(memory_region_size);
|
||||||
// Set the node address the first non-zero regison address
|
// Set the node address the first non-zero region address
|
||||||
if node_memory_addr == 0 {
|
if node_memory_addr == 0 {
|
||||||
node_memory_addr = memory_region_start_addr;
|
node_memory_addr = memory_region_start_addr;
|
||||||
}
|
}
|
||||||
@@ -569,7 +571,7 @@ fn create_memory_node(
|
|||||||
|
|
||||||
if ram_regions.len() > 2 {
|
if ram_regions.len() > 2 {
|
||||||
panic!(
|
panic!(
|
||||||
"There should be up to two non-continuous regions, devidided by the
|
"There should be up to two non-continuous regions, divided by the
|
||||||
gap at the end of 32bit address space."
|
gap at the end of 32bit address space."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -894,7 +896,7 @@ fn create_pci_nodes(
|
|||||||
for pci_device_info_elem in pci_device_info.iter() {
|
for pci_device_info_elem in pci_device_info.iter() {
|
||||||
// EDK2 requires the PCIe high space above 4G address.
|
// EDK2 requires the PCIe high space above 4G address.
|
||||||
// The actual space in CLH follows the RAM. If the RAM space is small, the PCIe high space
|
// The actual space in CLH follows the RAM. If the RAM space is small, the PCIe high space
|
||||||
// could fall bellow 4G.
|
// could fall below 4G.
|
||||||
// Here we cut off PCI device space below 8G in FDT to workaround the EDK2 check.
|
// Here we cut off PCI device space below 8G in FDT to workaround the EDK2 check.
|
||||||
// But the address written in ACPI is not impacted.
|
// But the address written in ACPI is not impacted.
|
||||||
let (pci_device_base_64bit, pci_device_size_64bit) =
|
let (pci_device_base_64bit, pci_device_size_64bit) =
|
||||||
|
|||||||
@@ -18,32 +18,40 @@ use log::{log_enabled, Level};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use thiserror::Error;
|
||||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||||
|
|
||||||
pub const _NSIG: i32 = 65;
|
pub const _NSIG: i32 = 65;
|
||||||
|
|
||||||
/// Errors thrown while configuring aarch64 system.
|
/// Errors thrown while configuring aarch64 system.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to create a FDT.
|
/// Failed to create a FDT.
|
||||||
|
#[error("Failed to create a FDT")]
|
||||||
SetupFdt,
|
SetupFdt,
|
||||||
|
|
||||||
/// Failed to write FDT to memory.
|
/// Failed to write FDT to memory.
|
||||||
|
#[error("Failed to write FDT to memory: {0}")]
|
||||||
WriteFdtToMemory(fdt::Error),
|
WriteFdtToMemory(fdt::Error),
|
||||||
|
|
||||||
/// Failed to create a GIC.
|
/// Failed to create a GIC.
|
||||||
|
#[error("Failed to create a GIC")]
|
||||||
SetupGic,
|
SetupGic,
|
||||||
|
|
||||||
/// Failed to compute the initramfs address.
|
/// Failed to compute the initramfs address.
|
||||||
|
#[error("Failed to compute the initramfs address")]
|
||||||
InitramfsAddress,
|
InitramfsAddress,
|
||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
|
#[error("Error configuring the general purpose registers: {0}")]
|
||||||
RegsConfiguration(hypervisor::HypervisorCpuError),
|
RegsConfiguration(hypervisor::HypervisorCpuError),
|
||||||
|
|
||||||
/// Error configuring the MPIDR register
|
/// Error configuring the MPIDR register
|
||||||
|
#[error("Error configuring the MPIDR register: {0}")]
|
||||||
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
VcpuRegMpidr(hypervisor::HypervisorCpuError),
|
||||||
|
|
||||||
/// Error initializing PMU for vcpu
|
/// Error initializing PMU for vcpu
|
||||||
|
#[error("Error initializing PMU for vcpu")]
|
||||||
VcpuInitPmu,
|
VcpuInitPmu,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,18 +5,23 @@
|
|||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::os::fd::AsFd;
|
use std::os::fd::AsFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use thiserror::Error;
|
||||||
use vm_memory::{GuestAddress, GuestMemory};
|
use vm_memory::{GuestAddress, GuestMemory};
|
||||||
|
|
||||||
/// Errors thrown while loading UEFI binary
|
/// Errors thrown while loading UEFI binary
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Unable to seek to UEFI image start.
|
/// Unable to seek to UEFI image start.
|
||||||
|
#[error("Unable to seek to UEFI image start")]
|
||||||
SeekUefiStart,
|
SeekUefiStart,
|
||||||
/// Unable to seek to UEFI image end.
|
/// Unable to seek to UEFI image end.
|
||||||
|
#[error("Unable to seek to UEFI image end")]
|
||||||
SeekUefiEnd,
|
SeekUefiEnd,
|
||||||
/// UEFI image too big.
|
/// UEFI image too big.
|
||||||
|
#[error("UEFI image too big")]
|
||||||
UefiTooBig,
|
UefiTooBig,
|
||||||
/// Unable to read UEFI image
|
/// Unable to read UEFI image
|
||||||
|
#[error("Unable to read UEFI image")]
|
||||||
ReadUefiImage,
|
ReadUefiImage,
|
||||||
}
|
}
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use linux_loader::loader::elf::start_info::{
|
|||||||
};
|
};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use thiserror::Error;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||||
GuestMemoryRegion, GuestUsize,
|
GuestMemoryRegion, GuestUsize,
|
||||||
@@ -33,6 +34,7 @@ use std::arch::x86_64;
|
|||||||
pub mod tdx;
|
pub mod tdx;
|
||||||
|
|
||||||
// CPUID feature bits
|
// CPUID feature bits
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
||||||
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
||||||
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
||||||
@@ -127,64 +129,83 @@ pub struct CpuidConfig {
|
|||||||
pub amx: bool,
|
pub amx: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error writing MP table to memory.
|
/// Error writing MP table to memory.
|
||||||
|
#[error("Error writing MP table to memory: {0}")]
|
||||||
MpTableSetup(mptable::Error),
|
MpTableSetup(mptable::Error),
|
||||||
|
|
||||||
/// Error configuring the general purpose registers
|
/// Error configuring the general purpose registers
|
||||||
|
#[error("Error configuring the general purpose registers: {0}")]
|
||||||
RegsConfiguration(regs::Error),
|
RegsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the special registers
|
/// Error configuring the special registers
|
||||||
|
#[error("Error configuring the special registers: {0}")]
|
||||||
SregsConfiguration(regs::Error),
|
SregsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the floating point related registers
|
/// Error configuring the floating point related registers
|
||||||
|
#[error("Error configuring the floating point related registers: {0}")]
|
||||||
FpuConfiguration(regs::Error),
|
FpuConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Error configuring the MSR registers
|
/// Error configuring the MSR registers
|
||||||
|
#[error("Error configuring the MSR registers: {0}")]
|
||||||
MsrsConfiguration(regs::Error),
|
MsrsConfiguration(regs::Error),
|
||||||
|
|
||||||
/// Failed to set supported CPUs.
|
/// Failed to set supported CPUs.
|
||||||
|
#[error("Failed to set supported CPUs: {0}")]
|
||||||
SetSupportedCpusFailed(anyhow::Error),
|
SetSupportedCpusFailed(anyhow::Error),
|
||||||
|
|
||||||
/// Cannot set the local interruption due to bad configuration.
|
/// Cannot set the local interruption due to bad configuration.
|
||||||
|
#[error("Cannot set the local interruption due to bad configuration: {0}")]
|
||||||
LocalIntConfiguration(anyhow::Error),
|
LocalIntConfiguration(anyhow::Error),
|
||||||
|
|
||||||
/// Error setting up SMBIOS table
|
/// Error setting up SMBIOS table
|
||||||
|
#[error("Error setting up SMBIOS table: {0}")]
|
||||||
SmbiosSetup(smbios::Error),
|
SmbiosSetup(smbios::Error),
|
||||||
|
|
||||||
/// Could not find any SGX EPC section
|
/// Could not find any SGX EPC section
|
||||||
|
#[error("Could not find any SGX EPC section")]
|
||||||
NoSgxEpcSection,
|
NoSgxEpcSection,
|
||||||
|
|
||||||
/// Missing SGX CPU feature
|
/// Missing SGX CPU feature
|
||||||
|
#[error("Missing SGX CPU feature")]
|
||||||
MissingSgxFeature,
|
MissingSgxFeature,
|
||||||
|
|
||||||
/// Missing SGX_LC CPU feature
|
/// Missing SGX_LC CPU feature
|
||||||
|
#[error("Missing SGX_LC CPU feature")]
|
||||||
MissingSgxLaunchControlFeature,
|
MissingSgxLaunchControlFeature,
|
||||||
|
|
||||||
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
|
/// Error getting supported CPUID through the hypervisor (kvm/mshv) API
|
||||||
|
#[error("Error getting supported CPUID through the hypervisor API: {0}")]
|
||||||
CpuidGetSupported(HypervisorError),
|
CpuidGetSupported(HypervisorError),
|
||||||
|
|
||||||
/// Error populating CPUID with KVM HyperV emulation details
|
/// Error populating CPUID with KVM HyperV emulation details
|
||||||
|
#[error("Error populating CPUID with KVM HyperV emulation details: {0}")]
|
||||||
CpuidKvmHyperV(vmm_sys_util::fam::Error),
|
CpuidKvmHyperV(vmm_sys_util::fam::Error),
|
||||||
|
|
||||||
/// Error populating CPUID with CPU identification
|
/// Error populating CPUID with CPU identification
|
||||||
|
#[error("Error populating CPUID with CPU identification: {0}")]
|
||||||
CpuidIdentification(vmm_sys_util::fam::Error),
|
CpuidIdentification(vmm_sys_util::fam::Error),
|
||||||
|
|
||||||
/// Error checking CPUID compatibility
|
/// Error checking CPUID compatibility
|
||||||
|
#[error("Error checking CPUID compatibility")]
|
||||||
CpuidCheckCompatibility,
|
CpuidCheckCompatibility,
|
||||||
|
|
||||||
// Error writing EBDA address
|
// Error writing EBDA address
|
||||||
|
#[error("Error writing EBDA address: {0}")]
|
||||||
EbdaSetup(vm_memory::GuestMemoryError),
|
EbdaSetup(vm_memory::GuestMemoryError),
|
||||||
|
|
||||||
// Error getting CPU TSC frequency
|
// Error getting CPU TSC frequency
|
||||||
|
#[error("Error getting CPU TSC frequency: {0}")]
|
||||||
GetTscFrequency(HypervisorCpuError),
|
GetTscFrequency(HypervisorCpuError),
|
||||||
|
|
||||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
|
#[error("Error retrieving TDX capabilities through the hypervisor API: {0}")]
|
||||||
TdxCapabilities(HypervisorError),
|
TdxCapabilities(HypervisorError),
|
||||||
|
|
||||||
/// Failed to configure E820 map for bzImage
|
/// Failed to configure E820 map for bzImage
|
||||||
|
#[error("Failed to configure E820 map for bzImage")]
|
||||||
E820Configuration,
|
E820Configuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +578,7 @@ impl CpuidFeatureEntry {
|
|||||||
if !entry_compatible {
|
if !entry_compatible {
|
||||||
error!(
|
error!(
|
||||||
"Detected incompatible CPUID entry: leaf={:#02x} (subleaf={:#02x}), register='{:?}', \
|
"Detected incompatible CPUID entry: leaf={:#02x} (subleaf={:#02x}), register='{:?}', \
|
||||||
compatilbe_check='{:?}', source VM feature='{:#04x}', destination VM feature'{:#04x}'.",
|
compatible_check='{:?}', source VM feature='{:#04x}', destination VM feature'{:#04x}'.",
|
||||||
entry.function, entry.index, entry.feature_reg,
|
entry.function, entry.index, entry.feature_reg,
|
||||||
entry.compatible_check, src_vm_feature, dest_vm_feature
|
entry.compatible_check, src_vm_feature, dest_vm_feature
|
||||||
);
|
);
|
||||||
@@ -599,17 +620,8 @@ pub fn generate_common_cpuid(
|
|||||||
"Generating guest CPUID for with physical address size: {}",
|
"Generating guest CPUID for with physical address size: {}",
|
||||||
config.phys_bits
|
config.phys_bits
|
||||||
);
|
);
|
||||||
let cpuid_patches = vec![
|
#[allow(unused_mut)]
|
||||||
// Patch tsc deadline timer bit
|
let mut cpuid_patches = vec![
|
||||||
CpuidPatch {
|
|
||||||
function: 1,
|
|
||||||
index: 0,
|
|
||||||
flags_bit: None,
|
|
||||||
eax_bit: None,
|
|
||||||
ebx_bit: None,
|
|
||||||
ecx_bit: Some(TSC_DEADLINE_TIMER_ECX_BIT),
|
|
||||||
edx_bit: None,
|
|
||||||
},
|
|
||||||
// Patch hypervisor bit
|
// Patch hypervisor bit
|
||||||
CpuidPatch {
|
CpuidPatch {
|
||||||
function: 1,
|
function: 1,
|
||||||
@@ -632,6 +644,23 @@ pub fn generate_common_cpuid(
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
if matches!(
|
||||||
|
hypervisor.hypervisor_type(),
|
||||||
|
hypervisor::HypervisorType::Kvm
|
||||||
|
) {
|
||||||
|
// Patch tsc deadline timer bit
|
||||||
|
cpuid_patches.push(CpuidPatch {
|
||||||
|
function: 1,
|
||||||
|
index: 0,
|
||||||
|
flags_bit: None,
|
||||||
|
eax_bit: None,
|
||||||
|
ebx_bit: None,
|
||||||
|
ecx_bit: Some(TSC_DEADLINE_TIMER_ECX_BIT),
|
||||||
|
edx_bit: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Supported CPUID
|
// Supported CPUID
|
||||||
let mut cpuid = hypervisor
|
let mut cpuid = hypervisor
|
||||||
.get_supported_cpuid()
|
.get_supported_cpuid()
|
||||||
@@ -682,6 +711,20 @@ pub fn generate_common_cpuid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Copy host L1 cache details if not populated by KVM
|
||||||
|
0x8000_0005 => {
|
||||||
|
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||||
|
// SAFETY: cpuid called with valid leaves
|
||||||
|
if unsafe { std::arch::x86_64::__cpuid(0x8000_0000).eax } >= 0x8000_0005 {
|
||||||
|
// SAFETY: cpuid called with valid leaves
|
||||||
|
let leaf = unsafe { std::arch::x86_64::__cpuid(0x8000_0005) };
|
||||||
|
entry.eax = leaf.eax;
|
||||||
|
entry.ebx = leaf.ebx;
|
||||||
|
entry.ecx = leaf.ecx;
|
||||||
|
entry.edx = leaf.edx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Copy host L2 cache details if not populated by KVM
|
// Copy host L2 cache details if not populated by KVM
|
||||||
0x8000_0006 => {
|
0x8000_0006 => {
|
||||||
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||||
@@ -800,12 +843,17 @@ pub fn configure_vcpu(
|
|||||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x8000_001e, Some(0), CpuidReg::EAX, x2apic_id);
|
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x8000_001e, Some(0), CpuidReg::EAX, x2apic_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set ApicId in cpuid for each vcpu
|
// Set ApicId in cpuid for each vcpu - found in cpuid ebx when eax = 1
|
||||||
// SAFETY: get host cpuid when eax=1
|
let mut apic_id_patched = false;
|
||||||
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
|
for entry in &mut cpuid {
|
||||||
cpu_ebx &= 0xffffff;
|
if entry.function == 1 {
|
||||||
cpu_ebx |= x2apic_id << 24;
|
entry.ebx &= 0xffffff;
|
||||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
entry.ebx |= x2apic_id << 24;
|
||||||
|
apic_id_patched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(apic_id_patched);
|
||||||
|
|
||||||
if let Some(t) = topology {
|
if let Some(t) = topology {
|
||||||
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2, cpu_vendor, id);
|
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2, cpu_vendor, id);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub const MP_IRQDIR_DEFAULT: ::std::os::raw::c_uint = 0;
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct mpf_intel {
|
pub struct mpf_intel {
|
||||||
pub signature: [::std::os::raw::c_char; 4usize],
|
pub signature: [::std::os::raw::c_uchar; 4usize],
|
||||||
pub physptr: ::std::os::raw::c_uint,
|
pub physptr: ::std::os::raw::c_uint,
|
||||||
pub length: ::std::os::raw::c_uchar,
|
pub length: ::std::os::raw::c_uchar,
|
||||||
pub specification: ::std::os::raw::c_uchar,
|
pub specification: ::std::os::raw::c_uchar,
|
||||||
@@ -32,12 +32,12 @@ pub struct mpf_intel {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct mpc_table {
|
pub struct mpc_table {
|
||||||
pub signature: [::std::os::raw::c_char; 4usize],
|
pub signature: [::std::os::raw::c_uchar; 4usize],
|
||||||
pub length: ::std::os::raw::c_ushort,
|
pub length: ::std::os::raw::c_ushort,
|
||||||
pub spec: ::std::os::raw::c_char,
|
pub spec: ::std::os::raw::c_uchar,
|
||||||
pub checksum: ::std::os::raw::c_char,
|
pub checksum: ::std::os::raw::c_uchar,
|
||||||
pub oem: [::std::os::raw::c_char; 8usize],
|
pub oem: [::std::os::raw::c_uchar; 8usize],
|
||||||
pub productid: [::std::os::raw::c_char; 12usize],
|
pub productid: [::std::os::raw::c_uchar; 12usize],
|
||||||
pub oemptr: ::std::os::raw::c_uint,
|
pub oemptr: ::std::os::raw::c_uint,
|
||||||
pub oemsize: ::std::os::raw::c_ushort,
|
pub oemsize: ::std::os::raw::c_ushort,
|
||||||
pub oemcount: ::std::os::raw::c_ushort,
|
pub oemcount: ::std::os::raw::c_ushort,
|
||||||
@@ -106,9 +106,9 @@ pub struct mpc_lintsrc {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct mpc_oemtable {
|
pub struct mpc_oemtable {
|
||||||
pub signature: [::std::os::raw::c_char; 4usize],
|
pub signature: [::std::os::raw::c_uchar; 4usize],
|
||||||
pub length: ::std::os::raw::c_ushort,
|
pub length: ::std::os::raw::c_ushort,
|
||||||
pub rev: ::std::os::raw::c_char,
|
pub rev: ::std::os::raw::c_uchar,
|
||||||
pub checksum: ::std::os::raw::c_char,
|
pub checksum: ::std::os::raw::c_uchar,
|
||||||
pub mpc: [::std::os::raw::c_char; 8usize],
|
pub mpc: [::std::os::raw::c_uchar; 8usize],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START};
|
use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START};
|
||||||
use crate::x86_64::{get_x2apic_id, mpspec};
|
use crate::x86_64::{get_x2apic_id, mpspec};
|
||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use libc::c_char;
|
use libc::c_uchar;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
use thiserror::Error;
|
||||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError};
|
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError};
|
||||||
|
|
||||||
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
|
||||||
@@ -49,29 +50,40 @@ unsafe impl ByteValued for MpcLintsrcWrapper {}
|
|||||||
// SAFETY: see above
|
// SAFETY: see above
|
||||||
unsafe impl ByteValued for MpfIntelWrapper {}
|
unsafe impl ByteValued for MpfIntelWrapper {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// There was too little guest memory to store the entire MP table.
|
/// There was too little guest memory to store the entire MP table.
|
||||||
|
#[error("There was too little guest memory to store the entire MP table")]
|
||||||
NotEnoughMemory,
|
NotEnoughMemory,
|
||||||
/// The MP table has too little address space to be stored.
|
/// The MP table has too little address space to be stored.
|
||||||
|
#[error("The MP table has too little address space to be stored")]
|
||||||
AddressOverflow,
|
AddressOverflow,
|
||||||
/// Failure while zeroing out the memory for the MP table.
|
/// Failure while zeroing out the memory for the MP table.
|
||||||
|
#[error("Failure while zeroing out the memory for the MP table: {0}")]
|
||||||
Clear(GuestMemoryError),
|
Clear(GuestMemoryError),
|
||||||
/// Number of CPUs exceeds the maximum supported CPUs
|
/// Number of CPUs exceeds the maximum supported CPUs
|
||||||
|
#[error("Number of CPUs exceeds the maximum supported CPUs")]
|
||||||
TooManyCpus,
|
TooManyCpus,
|
||||||
/// Failure to write the MP floating pointer.
|
/// Failure to write the MP floating pointer.
|
||||||
|
#[error("Failure to write the MP floating pointer: {0}")]
|
||||||
WriteMpfIntel(GuestMemoryError),
|
WriteMpfIntel(GuestMemoryError),
|
||||||
/// Failure to write MP CPU entry.
|
/// Failure to write MP CPU entry.
|
||||||
|
#[error("Failure to write MP CPU entry: {0}")]
|
||||||
WriteMpcCpu(GuestMemoryError),
|
WriteMpcCpu(GuestMemoryError),
|
||||||
/// Failure to write MP ioapic entry.
|
/// Failure to write MP ioapic entry.
|
||||||
|
#[error("Failure to write MP ioapic entry: {0}")]
|
||||||
WriteMpcIoapic(GuestMemoryError),
|
WriteMpcIoapic(GuestMemoryError),
|
||||||
/// Failure to write MP bus entry.
|
/// Failure to write MP bus entry.
|
||||||
|
#[error("Failure to write MP bus entry: {0}")]
|
||||||
WriteMpcBus(GuestMemoryError),
|
WriteMpcBus(GuestMemoryError),
|
||||||
/// Failure to write MP interrupt source entry.
|
/// Failure to write MP interrupt source entry.
|
||||||
|
#[error("Failure to write MP interrupt source entry: {0}")]
|
||||||
WriteMpcIntsrc(GuestMemoryError),
|
WriteMpcIntsrc(GuestMemoryError),
|
||||||
/// Failure to write MP local interrupt source entry.
|
/// Failure to write MP local interrupt source entry.
|
||||||
|
#[error("Failure to write MP local interrupt source entry: {0}")]
|
||||||
WriteMpcLintsrc(GuestMemoryError),
|
WriteMpcLintsrc(GuestMemoryError),
|
||||||
/// Failure to write MP table header.
|
/// Failure to write MP table header.
|
||||||
|
#[error("Failure to write MP table header: {0}")]
|
||||||
WriteMpcTable(GuestMemoryError),
|
WriteMpcTable(GuestMemoryError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,18 +94,13 @@ pub type Result<T> = result::Result<T, Error>;
|
|||||||
// a large number for FC usecases.
|
// a large number for FC usecases.
|
||||||
pub const MAX_SUPPORTED_CPUS: u32 = 254;
|
pub const MAX_SUPPORTED_CPUS: u32 = 254;
|
||||||
|
|
||||||
// Convenience macro for making arrays of diverse character types.
|
|
||||||
macro_rules! char_array {
|
|
||||||
($t:ty; $( $c:expr ),*) => ( [ $( $c as $t ),* ] )
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most of these variables are sourced from the Intel MP Spec 1.4.
|
// Most of these variables are sourced from the Intel MP Spec 1.4.
|
||||||
const SMP_MAGIC_IDENT: [c_char; 4] = char_array!(c_char; '_', 'M', 'P', '_');
|
const SMP_MAGIC_IDENT: &[c_uchar; 4] = b"_MP_";
|
||||||
const MPC_SIGNATURE: [c_char; 4] = char_array!(c_char; 'P', 'C', 'M', 'P');
|
const MPC_SIGNATURE: &[c_uchar; 4] = b"PCMP";
|
||||||
const MPC_SPEC: i8 = 4;
|
const MPC_SPEC: u8 = 4;
|
||||||
const MPC_OEM: [c_char; 8] = char_array!(c_char; 'F', 'C', ' ', ' ', ' ', ' ', ' ', ' ');
|
const MPC_OEM: &[c_uchar; 8] = b"FC ";
|
||||||
const MPC_PRODUCT_ID: [c_char; 12] = ['0' as c_char; 12];
|
const MPC_PRODUCT_ID: &[c_uchar; 12] = &[b'0'; 12];
|
||||||
const BUS_TYPE_ISA: [u8; 6] = char_array!(u8; 'I', 'S', 'A', ' ', ' ', ' ');
|
const BUS_TYPE_ISA: &[c_uchar; 6] = b"ISA ";
|
||||||
const APIC_VERSION: u8 = 0x14;
|
const APIC_VERSION: u8 = 0x14;
|
||||||
const CPU_STEPPING: u32 = 0x600;
|
const CPU_STEPPING: u32 = 0x600;
|
||||||
const CPU_FEATURE_APIC: u32 = 0x200;
|
const CPU_FEATURE_APIC: u32 = 0x200;
|
||||||
@@ -168,7 +175,7 @@ pub fn setup_mptable(
|
|||||||
{
|
{
|
||||||
let mut mpf_intel = MpfIntelWrapper(mpspec::mpf_intel::default());
|
let mut mpf_intel = MpfIntelWrapper(mpspec::mpf_intel::default());
|
||||||
let size = mem::size_of::<MpfIntelWrapper>() as u64;
|
let size = mem::size_of::<MpfIntelWrapper>() as u64;
|
||||||
mpf_intel.0.signature = SMP_MAGIC_IDENT;
|
mpf_intel.0.signature = *SMP_MAGIC_IDENT;
|
||||||
mpf_intel.0.length = 1;
|
mpf_intel.0.length = 1;
|
||||||
mpf_intel.0.specification = 4;
|
mpf_intel.0.specification = 4;
|
||||||
mpf_intel.0.physptr = (base_mp.raw_value() + size) as u32;
|
mpf_intel.0.physptr = (base_mp.raw_value() + size) as u32;
|
||||||
@@ -209,7 +216,7 @@ pub fn setup_mptable(
|
|||||||
let mut mpc_bus = MpcBusWrapper(mpspec::mpc_bus::default());
|
let mut mpc_bus = MpcBusWrapper(mpspec::mpc_bus::default());
|
||||||
mpc_bus.0.type_ = mpspec::MP_BUS as u8;
|
mpc_bus.0.type_ = mpspec::MP_BUS as u8;
|
||||||
mpc_bus.0.busid = 0;
|
mpc_bus.0.busid = 0;
|
||||||
mpc_bus.0.bustype = BUS_TYPE_ISA;
|
mpc_bus.0.bustype = *BUS_TYPE_ISA;
|
||||||
mem.write_obj(mpc_bus, base_mp)
|
mem.write_obj(mpc_bus, base_mp)
|
||||||
.map_err(Error::WriteMpcBus)?;
|
.map_err(Error::WriteMpcBus)?;
|
||||||
base_mp = base_mp.unchecked_add(size as u64);
|
base_mp = base_mp.unchecked_add(size as u64);
|
||||||
@@ -280,14 +287,14 @@ pub fn setup_mptable(
|
|||||||
|
|
||||||
{
|
{
|
||||||
let mut mpc_table = MpcTableWrapper(mpspec::mpc_table::default());
|
let mut mpc_table = MpcTableWrapper(mpspec::mpc_table::default());
|
||||||
mpc_table.0.signature = MPC_SIGNATURE;
|
mpc_table.0.signature = *MPC_SIGNATURE;
|
||||||
mpc_table.0.length = table_end.unchecked_offset_from(table_base) as u16;
|
mpc_table.0.length = table_end.unchecked_offset_from(table_base) as u16;
|
||||||
mpc_table.0.spec = MPC_SPEC;
|
mpc_table.0.spec = MPC_SPEC;
|
||||||
mpc_table.0.oem = MPC_OEM;
|
mpc_table.0.oem = *MPC_OEM;
|
||||||
mpc_table.0.productid = MPC_PRODUCT_ID;
|
mpc_table.0.productid = *MPC_PRODUCT_ID;
|
||||||
mpc_table.0.lapic = APIC_START.0 as u32;
|
mpc_table.0.lapic = APIC_START.0 as u32;
|
||||||
checksum = checksum.wrapping_add(compute_checksum(&mpc_table.0));
|
checksum = checksum.wrapping_add(compute_checksum(&mpc_table.0));
|
||||||
mpc_table.0.checksum = (!checksum).wrapping_add(1) as i8;
|
mpc_table.0.checksum = (!checksum).wrapping_add(1);
|
||||||
mem.write_obj(mpc_table, table_base)
|
mem.write_obj(mpc_table, table_base)
|
||||||
.map_err(Error::WriteMpcTable)?;
|
.map_err(Error::WriteMpcTable)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,33 +15,46 @@ use hypervisor::arch::x86::regs::CR0_PE;
|
|||||||
use hypervisor::arch::x86::{FpuState, SpecialRegisters, StandardRegisters};
|
use hypervisor::arch::x86::{FpuState, SpecialRegisters, StandardRegisters};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, result};
|
use std::{mem, result};
|
||||||
|
use thiserror::Error;
|
||||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Failed to get SREGs for this CPU.
|
/// Failed to get SREGs for this CPU.
|
||||||
|
#[error("Failed to get SREGs for this CPU: {0}")]
|
||||||
GetStatusRegisters(hypervisor::HypervisorCpuError),
|
GetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Failed to set base registers for this CPU.
|
/// Failed to set base registers for this CPU.
|
||||||
|
#[error("Failed to set base registers for this CPU: {0}")]
|
||||||
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
SetBaseRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Failed to configure the FPU.
|
/// Failed to configure the FPU.
|
||||||
|
#[error("Failed to configure the FPU: {0}")]
|
||||||
SetFpuRegisters(hypervisor::HypervisorCpuError),
|
SetFpuRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Setting up MSRs failed.
|
/// Setting up MSRs failed.
|
||||||
|
#[error("Setting up MSRs failed: {0}")]
|
||||||
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Failed to set SREGs for this CPU.
|
/// Failed to set SREGs for this CPU.
|
||||||
|
#[error("Failed to set SREGs for this CPU: {0}")]
|
||||||
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
SetStatusRegisters(hypervisor::HypervisorCpuError),
|
||||||
/// Checking the GDT address failed.
|
/// Checking the GDT address failed.
|
||||||
|
#[error("Checking the GDT address failed")]
|
||||||
CheckGdtAddr,
|
CheckGdtAddr,
|
||||||
/// Writing the GDT to RAM failed.
|
/// Writing the GDT to RAM failed.
|
||||||
|
#[error("Writing the GDT to RAM failed: {0}")]
|
||||||
WriteGdt(GuestMemoryError),
|
WriteGdt(GuestMemoryError),
|
||||||
/// Writing the IDT to RAM failed.
|
/// Writing the IDT to RAM failed.
|
||||||
|
#[error("Writing the IDT to RAM failed: {0}")]
|
||||||
WriteIdt(GuestMemoryError),
|
WriteIdt(GuestMemoryError),
|
||||||
/// Writing PDPTE to RAM failed.
|
/// Writing PDPTE to RAM failed.
|
||||||
|
#[error("Writing PDPTE to RAM failed: {0}")]
|
||||||
WritePdpteAddress(GuestMemoryError),
|
WritePdpteAddress(GuestMemoryError),
|
||||||
/// Writing PDE to RAM failed.
|
/// Writing PDE to RAM failed.
|
||||||
|
#[error("Writing PDE to RAM failed: {0}")]
|
||||||
WritePdeAddress(GuestMemoryError),
|
WritePdeAddress(GuestMemoryError),
|
||||||
/// Writing PML4 to RAM failed.
|
/// Writing PML4 to RAM failed.
|
||||||
|
#[error("Writing PML4 to RAM failed: {0}")]
|
||||||
WritePml4Address(GuestMemoryError),
|
WritePml4Address(GuestMemoryError),
|
||||||
/// Writing PML5 to RAM failed.
|
/// Writing PML5 to RAM failed.
|
||||||
|
#[error("Writing PML5 to RAM failed: {0}")]
|
||||||
WritePml5Address(GuestMemoryError),
|
WritePml5Address(GuestMemoryError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,53 +8,36 @@
|
|||||||
|
|
||||||
use crate::layout::SMBIOS_START;
|
use crate::layout::SMBIOS_START;
|
||||||
use crate::GuestMemoryMmap;
|
use crate::GuestMemoryMmap;
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vm_memory::ByteValued;
|
use vm_memory::ByteValued;
|
||||||
use vm_memory::{Address, Bytes, GuestAddress};
|
use vm_memory::{Address, Bytes, GuestAddress};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// There was too little guest memory to store the entire SMBIOS table.
|
/// There was too little guest memory to store the entire SMBIOS table.
|
||||||
|
#[error("There was too little guest memory to store the SMBIOS table")]
|
||||||
NotEnoughMemory,
|
NotEnoughMemory,
|
||||||
/// The SMBIOS table has too little address space to be stored.
|
/// The SMBIOS table has too little address space to be stored.
|
||||||
|
#[error("The SMBIOS table has too little address space to be stored")]
|
||||||
AddressOverflow,
|
AddressOverflow,
|
||||||
/// Failure while zeroing out the memory for the SMBIOS table.
|
/// Failure while zeroing out the memory for the SMBIOS table.
|
||||||
|
#[error("Failure while zeroing out the memory for the SMBIOS table")]
|
||||||
Clear,
|
Clear,
|
||||||
/// Failure to write SMBIOS entrypoint structure
|
/// Failure to write SMBIOS entrypoint structure
|
||||||
|
#[error("Failure to write SMBIOS entrypoint structure")]
|
||||||
WriteSmbiosEp,
|
WriteSmbiosEp,
|
||||||
/// Failure to write additional data to memory
|
/// Failure to write additional data to memory
|
||||||
|
#[error("Failure to write additional data to memory")]
|
||||||
WriteData,
|
WriteData,
|
||||||
/// Failure to parse uuid, uuid format may be error
|
/// Failure to parse uuid, uuid format may be error
|
||||||
|
#[error("Failure to parse uuid: {0}")]
|
||||||
ParseUuid(uuid::Error),
|
ParseUuid(uuid::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use self::Error::*;
|
|
||||||
|
|
||||||
let description = match self {
|
|
||||||
NotEnoughMemory => {
|
|
||||||
"There was too little guest memory to store the SMBIOS table".to_string()
|
|
||||||
}
|
|
||||||
AddressOverflow => {
|
|
||||||
"The SMBIOS table has too little address space to be stored".to_string()
|
|
||||||
}
|
|
||||||
Clear => "Failure while zeroing out the memory for the SMBIOS table".to_string(),
|
|
||||||
WriteSmbiosEp => "Failure to write SMBIOS entrypoint structure".to_string(),
|
|
||||||
WriteData => "Failure to write additional data to memory".to_string(),
|
|
||||||
ParseUuid(e) => format!("Failure to parse uuid: {e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "SMBIOS error: {description}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
// Constants sourced from SMBIOS Spec 3.2.0.
|
// Constants sourced from SMBIOS Spec 3.2.0.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
|
authors = ["The Chromium OS Authors", "The Cloud Hypervisor Authors"]
|
||||||
|
edition = "2021"
|
||||||
name = "block"
|
name = "block"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
|
||||||
authors = ["The Cloud Hypervisor Authors", "The Chromium OS Authors"]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
@@ -12,15 +12,19 @@ io_uring = ["dep:io-uring"]
|
|||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
crc-any = "2.4.4"
|
crc-any = "2.4.4"
|
||||||
io-uring = { version = "0.6.3", optional = true }
|
io-uring = { version = "0.6.3", optional = true }
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
log = "0.4.21"
|
log = "0.4.22"
|
||||||
remain = "0.2.13"
|
remain = "0.2.14"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
smallvec = "1.13.2"
|
smallvec = "1.13.2"
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
virtio-bindings = { version = "0.2.2", features = ["virtio-v5_0_0"] }
|
virtio-bindings = { version = "0.2.2", features = ["virtio-v5_0_0"] }
|
||||||
virtio-queue = "0.11.0"
|
virtio-queue = "0.12.0"
|
||||||
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
vm-memory = { version = "0.14.1", features = [
|
||||||
|
"backend-atomic",
|
||||||
|
"backend-bitmap",
|
||||||
|
"backend-mmap",
|
||||||
|
] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
|
|||||||
@@ -56,16 +56,14 @@ use thiserror::Error;
|
|||||||
use virtio_bindings::virtio_blk::*;
|
use virtio_bindings::virtio_blk::*;
|
||||||
use virtio_queue::DescriptorChain;
|
use virtio_queue::DescriptorChain;
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
bitmap::AtomicBitmap, bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory,
|
bitmap::Bitmap, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError,
|
||||||
GuestMemoryError, GuestMemoryLoadGuard,
|
GuestMemoryLoadGuard,
|
||||||
};
|
};
|
||||||
use vm_virtio::{AccessPlatform, Translatable};
|
use vm_virtio::{AccessPlatform, Translatable};
|
||||||
use vmm_sys_util::aio;
|
use vmm_sys_util::aio;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
|
||||||
|
|
||||||
const SECTOR_SHIFT: u8 = 9;
|
const SECTOR_SHIFT: u8 = 9;
|
||||||
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
|
||||||
|
|
||||||
@@ -166,8 +164,8 @@ pub enum ExecuteError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ExecuteError {
|
impl ExecuteError {
|
||||||
pub fn status(&self) -> u32 {
|
pub fn status(&self) -> u8 {
|
||||||
match *self {
|
let status = match *self {
|
||||||
ExecuteError::BadRequest(_) => VIRTIO_BLK_S_IOERR,
|
ExecuteError::BadRequest(_) => VIRTIO_BLK_S_IOERR,
|
||||||
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
|
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
|
||||||
ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
|
ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
|
||||||
@@ -182,7 +180,8 @@ impl ExecuteError {
|
|||||||
ExecuteError::AsyncWrite(_) => VIRTIO_BLK_S_IOERR,
|
ExecuteError::AsyncWrite(_) => VIRTIO_BLK_S_IOERR,
|
||||||
ExecuteError::AsyncFlush(_) => VIRTIO_BLK_S_IOERR,
|
ExecuteError::AsyncFlush(_) => VIRTIO_BLK_S_IOERR,
|
||||||
ExecuteError::TemporaryBufferAllocation(_) => VIRTIO_BLK_S_IOERR,
|
ExecuteError::TemporaryBufferAllocation(_) => VIRTIO_BLK_S_IOERR,
|
||||||
}
|
};
|
||||||
|
status as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,8 +194,8 @@ pub enum RequestType {
|
|||||||
Unsupported(u32),
|
Unsupported(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_type(
|
pub fn request_type<B: Bitmap + 'static>(
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
desc_addr: GuestAddress,
|
desc_addr: GuestAddress,
|
||||||
) -> result::Result<RequestType, Error> {
|
) -> result::Result<RequestType, Error> {
|
||||||
let type_ = mem.read_obj(desc_addr).map_err(Error::GuestMemory)?;
|
let type_ = mem.read_obj(desc_addr).map_err(Error::GuestMemory)?;
|
||||||
@@ -209,7 +208,10 @@ pub fn request_type(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sector(mem: &GuestMemoryMmap, desc_addr: GuestAddress) -> result::Result<u64, Error> {
|
fn sector<B: Bitmap + 'static>(
|
||||||
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
|
desc_addr: GuestAddress,
|
||||||
|
) -> result::Result<u64, Error> {
|
||||||
const SECTOR_OFFSET: usize = 8;
|
const SECTOR_OFFSET: usize = 8;
|
||||||
let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
|
let addr = match mem.checked_offset(desc_addr, SECTOR_OFFSET) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
@@ -239,16 +241,15 @@ pub struct Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
pub fn parse(
|
pub fn parse<B: Bitmap + 'static>(
|
||||||
desc_chain: &mut DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap>>,
|
desc_chain: &mut DescriptorChain<GuestMemoryLoadGuard<vm_memory::GuestMemoryMmap<B>>>,
|
||||||
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
access_platform: Option<&Arc<dyn AccessPlatform>>,
|
||||||
) -> result::Result<Request, Error> {
|
) -> result::Result<Request, Error> {
|
||||||
let hdr_desc = desc_chain
|
let hdr_desc = desc_chain
|
||||||
.next()
|
.next()
|
||||||
.ok_or(Error::DescriptorChainTooShort)
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
.map_err(|e| {
|
.inspect_err(|_| {
|
||||||
error!("Missing head descriptor");
|
error!("Missing head descriptor");
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// The head contains the request type which MUST be readable.
|
// The head contains the request type which MUST be readable.
|
||||||
@@ -274,9 +275,8 @@ impl Request {
|
|||||||
let mut desc = desc_chain
|
let mut desc = desc_chain
|
||||||
.next()
|
.next()
|
||||||
.ok_or(Error::DescriptorChainTooShort)
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
.map_err(|e| {
|
.inspect_err(|_| {
|
||||||
error!("Only head descriptor present: request = {:?}", req);
|
error!("Only head descriptor present: request = {:?}", req);
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !desc.has_next() {
|
if !desc.has_next() {
|
||||||
@@ -307,9 +307,8 @@ impl Request {
|
|||||||
desc = desc_chain
|
desc = desc_chain
|
||||||
.next()
|
.next()
|
||||||
.ok_or(Error::DescriptorChainTooShort)
|
.ok_or(Error::DescriptorChainTooShort)
|
||||||
.map_err(|e| {
|
.inspect_err(|_| {
|
||||||
error!("DescriptorChain corrupted: request = {:?}", req);
|
error!("DescriptorChain corrupted: request = {:?}", req);
|
||||||
e
|
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
status_desc = desc;
|
status_desc = desc;
|
||||||
@@ -331,11 +330,11 @@ impl Request {
|
|||||||
Ok(req)
|
Ok(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute<T: Seek + Read + Write>(
|
pub fn execute<T: Seek + Read + Write, B: Bitmap + 'static>(
|
||||||
&self,
|
&self,
|
||||||
disk: &mut T,
|
disk: &mut T,
|
||||||
disk_nsectors: u64,
|
disk_nsectors: u64,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
serial: &[u8],
|
serial: &[u8],
|
||||||
) -> result::Result<u32, ExecuteError> {
|
) -> result::Result<u32, ExecuteError> {
|
||||||
disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
|
disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
|
||||||
@@ -388,9 +387,9 @@ impl Request {
|
|||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_async(
|
pub fn execute_async<B: Bitmap + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
disk_nsectors: u64,
|
disk_nsectors: u64,
|
||||||
disk_image: &mut dyn AsyncIo,
|
disk_image: &mut dyn AsyncIo,
|
||||||
serial: &[u8],
|
serial: &[u8],
|
||||||
@@ -658,7 +657,9 @@ where
|
|||||||
let mut slices: SmallVec<[IoSliceMut; 1]> = SmallVec::with_capacity(iovecs.len());
|
let mut slices: SmallVec<[IoSliceMut; 1]> = SmallVec::with_capacity(iovecs.len());
|
||||||
for iovec in iovecs.iter() {
|
for iovec in iovecs.iter() {
|
||||||
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
// SAFETY: on Linux IoSliceMut wraps around libc::iovec
|
||||||
slices.push(IoSliceMut::new(unsafe { std::mem::transmute(*iovec) }));
|
slices.push(IoSliceMut::new(unsafe {
|
||||||
|
std::mem::transmute::<libc::iovec, &mut [u8]>(*iovec)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
@@ -691,7 +692,9 @@ where
|
|||||||
let mut slices: SmallVec<[IoSlice; 1]> = SmallVec::with_capacity(iovecs.len());
|
let mut slices: SmallVec<[IoSlice; 1]> = SmallVec::with_capacity(iovecs.len());
|
||||||
for iovec in iovecs.iter() {
|
for iovec in iovecs.iter() {
|
||||||
// SAFETY: on Linux IoSlice wraps around libc::iovec
|
// SAFETY: on Linux IoSlice wraps around libc::iovec
|
||||||
slices.push(IoSlice::new(unsafe { std::mem::transmute(*iovec) }));
|
slices.push(IoSlice::new(unsafe {
|
||||||
|
std::mem::transmute::<libc::iovec, &mut [u8]>(*iovec)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
|||||||
use libc::{EINVAL, ENOSPC, ENOTSUP};
|
use libc::{EINVAL, ENOSPC, ENOTSUP};
|
||||||
use remain::sorted;
|
use remain::sorted;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use thiserror::Error;
|
||||||
use vmm_sys_util::{
|
use vmm_sys_util::{
|
||||||
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
|
file_traits::FileSetLen, file_traits::FileSync, seek_hole::SeekHole, write_zeroes::PunchHole,
|
||||||
write_zeroes::WriteZeroesAt,
|
write_zeroes::WriteZeroesAt,
|
||||||
@@ -31,106 +31,98 @@ use vmm_sys_util::{
|
|||||||
|
|
||||||
pub use crate::qcow::raw_file::RawFile;
|
pub use crate::qcow::raw_file::RawFile;
|
||||||
|
|
||||||
|
/// Nesting depth limit for disk formats that can open other disk files.
|
||||||
|
const MAX_NESTING_DEPTH: u32 = 10;
|
||||||
|
|
||||||
#[sorted]
|
#[sorted]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Backing file io error: {0}")]
|
||||||
BackingFileIo(io::Error),
|
BackingFileIo(io::Error),
|
||||||
BackingFileOpen(Box<crate::Error>),
|
#[error("Backing file open error: {0}")]
|
||||||
|
BackingFileOpen(Box<Error>),
|
||||||
|
#[error("Backing file name is too long: {0} bytes over")]
|
||||||
BackingFileTooLong(usize),
|
BackingFileTooLong(usize),
|
||||||
|
#[error("Compressed blocks not supported")]
|
||||||
CompressedBlocksNotSupported,
|
CompressedBlocksNotSupported,
|
||||||
|
#[error("Failed to evict cache: {0}")]
|
||||||
EvictingCache(io::Error),
|
EvictingCache(io::Error),
|
||||||
|
#[error("File larger than max of {}: {0}", MAX_QCOW_FILE_SIZE)]
|
||||||
FileTooBig(u64),
|
FileTooBig(u64),
|
||||||
|
#[error("Failed to get file size: {0}")]
|
||||||
GettingFileSize(io::Error),
|
GettingFileSize(io::Error),
|
||||||
|
#[error("Failed to get refcount: {0}")]
|
||||||
GettingRefcount(refcount::Error),
|
GettingRefcount(refcount::Error),
|
||||||
|
#[error("Failed to parse filename: {0}")]
|
||||||
InvalidBackingFileName(str::Utf8Error),
|
InvalidBackingFileName(str::Utf8Error),
|
||||||
|
#[error("Invalid cluster index")]
|
||||||
InvalidClusterIndex,
|
InvalidClusterIndex,
|
||||||
|
#[error("Invalid cluster size")]
|
||||||
InvalidClusterSize,
|
InvalidClusterSize,
|
||||||
|
#[error("Invalid index")]
|
||||||
InvalidIndex,
|
InvalidIndex,
|
||||||
|
#[error("Invalid L1 table offset")]
|
||||||
InvalidL1TableOffset,
|
InvalidL1TableOffset,
|
||||||
|
#[error("Invalid L1 table size: {0}")]
|
||||||
InvalidL1TableSize(u32),
|
InvalidL1TableSize(u32),
|
||||||
|
#[error("Invalid magic")]
|
||||||
InvalidMagic,
|
InvalidMagic,
|
||||||
|
#[error("Invalid offset: {0}")]
|
||||||
InvalidOffset(u64),
|
InvalidOffset(u64),
|
||||||
|
#[error("Invalid refcount table offset")]
|
||||||
InvalidRefcountTableOffset,
|
InvalidRefcountTableOffset,
|
||||||
|
#[error("Invalid refcount table size: {0}")]
|
||||||
InvalidRefcountTableSize(u64),
|
InvalidRefcountTableSize(u64),
|
||||||
|
#[error("Maximum disk nesting depth exceeded")]
|
||||||
|
MaxNestingDepthExceeded,
|
||||||
|
#[error("No free clusters")]
|
||||||
NoFreeClusters,
|
NoFreeClusters,
|
||||||
|
#[error("No refcount clusters")]
|
||||||
NoRefcountClusters,
|
NoRefcountClusters,
|
||||||
|
#[error("Not enough space for refcounts")]
|
||||||
NotEnoughSpaceForRefcounts,
|
NotEnoughSpaceForRefcounts,
|
||||||
|
#[error("Failed to open file {0}")]
|
||||||
OpeningFile(io::Error),
|
OpeningFile(io::Error),
|
||||||
|
#[error("Failed to read data: {0}")]
|
||||||
ReadingData(io::Error),
|
ReadingData(io::Error),
|
||||||
|
#[error("Failed to read header: {0}")]
|
||||||
ReadingHeader(io::Error),
|
ReadingHeader(io::Error),
|
||||||
|
#[error("Failed to read pointers: {0}")]
|
||||||
ReadingPointers(io::Error),
|
ReadingPointers(io::Error),
|
||||||
|
#[error("Failed to read ref count block: {0}")]
|
||||||
ReadingRefCountBlock(refcount::Error),
|
ReadingRefCountBlock(refcount::Error),
|
||||||
|
#[error("Failed to read ref counts: {0}")]
|
||||||
ReadingRefCounts(io::Error),
|
ReadingRefCounts(io::Error),
|
||||||
|
#[error("Failed to rebuild ref counts: {0}")]
|
||||||
RebuildingRefCounts(io::Error),
|
RebuildingRefCounts(io::Error),
|
||||||
|
#[error("Refcount table offset past file end")]
|
||||||
RefcountTableOffEnd,
|
RefcountTableOffEnd,
|
||||||
|
#[error("Too many clusters specified for refcount")]
|
||||||
RefcountTableTooLarge,
|
RefcountTableTooLarge,
|
||||||
|
#[error("Failed to seek file: {0}")]
|
||||||
SeekingFile(io::Error),
|
SeekingFile(io::Error),
|
||||||
|
#[error("Failed to set file size: {0}")]
|
||||||
SettingFileSize(io::Error),
|
SettingFileSize(io::Error),
|
||||||
|
#[error("Failed to set refcount refcount: {0}")]
|
||||||
SettingRefcountRefcount(io::Error),
|
SettingRefcountRefcount(io::Error),
|
||||||
|
#[error("Size too small for number of clusters")]
|
||||||
SizeTooSmallForNumberOfClusters,
|
SizeTooSmallForNumberOfClusters,
|
||||||
|
#[error("L1 entry table too large: {0}")]
|
||||||
TooManyL1Entries(u64),
|
TooManyL1Entries(u64),
|
||||||
|
#[error("Ref count table too large: {0}")]
|
||||||
TooManyRefcounts(u64),
|
TooManyRefcounts(u64),
|
||||||
|
#[error("Unsupported refcount order")]
|
||||||
UnsupportedRefcountOrder,
|
UnsupportedRefcountOrder,
|
||||||
|
#[error("Unsupported version: {0}")]
|
||||||
UnsupportedVersion(u32),
|
UnsupportedVersion(u32),
|
||||||
|
#[error("Failed to write data: {0}")]
|
||||||
WritingData(io::Error),
|
WritingData(io::Error),
|
||||||
|
#[error("Failed to write header: {0}")]
|
||||||
WritingHeader(io::Error),
|
WritingHeader(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
#[remain::check]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use self::Error::*;
|
|
||||||
|
|
||||||
#[sorted]
|
|
||||||
match self {
|
|
||||||
BackingFileIo(e) => write!(f, "backing file io error: {}", e),
|
|
||||||
BackingFileOpen(e) => write!(f, "backing file open error: {}", *e),
|
|
||||||
BackingFileTooLong(len) => {
|
|
||||||
write!(f, "backing file name is too long: {} bytes over", len)
|
|
||||||
}
|
|
||||||
CompressedBlocksNotSupported => write!(f, "compressed blocks not supported"),
|
|
||||||
EvictingCache(e) => write!(f, "failed to evict cache: {e}"),
|
|
||||||
FileTooBig(size) => write!(f, "file larger than max of {MAX_QCOW_FILE_SIZE}: {size}"),
|
|
||||||
GettingFileSize(e) => write!(f, "failed to get file size: {e}"),
|
|
||||||
GettingRefcount(e) => write!(f, "failed to get refcount: {e}"),
|
|
||||||
InvalidBackingFileName(e) => write!(f, "failed to parse filename: {}", e),
|
|
||||||
InvalidClusterIndex => write!(f, "invalid cluster index"),
|
|
||||||
InvalidClusterSize => write!(f, "invalid cluster size"),
|
|
||||||
InvalidIndex => write!(f, "invalid index"),
|
|
||||||
InvalidL1TableOffset => write!(f, "invalid L1 table offset"),
|
|
||||||
InvalidL1TableSize(size) => write!(f, "invalid L1 table size {size}"),
|
|
||||||
InvalidMagic => write!(f, "invalid magic"),
|
|
||||||
InvalidOffset(_) => write!(f, "invalid offset"),
|
|
||||||
InvalidRefcountTableOffset => write!(f, "invalid refcount table offset"),
|
|
||||||
InvalidRefcountTableSize(size) => write!(f, "invalid refcount table size: {size}"),
|
|
||||||
NoFreeClusters => write!(f, "no free clusters"),
|
|
||||||
NoRefcountClusters => write!(f, "no refcount clusters"),
|
|
||||||
NotEnoughSpaceForRefcounts => write!(f, "not enough space for refcounts"),
|
|
||||||
OpeningFile(e) => write!(f, "failed to open file: {e}"),
|
|
||||||
ReadingData(e) => write!(f, "failed to read data: {e}"),
|
|
||||||
ReadingHeader(e) => write!(f, "failed to read header: {e}"),
|
|
||||||
ReadingPointers(e) => write!(f, "failed to read pointers: {e}"),
|
|
||||||
ReadingRefCountBlock(e) => write!(f, "failed to read ref count block: {e}"),
|
|
||||||
ReadingRefCounts(e) => write!(f, "failed to read ref counts: {e}"),
|
|
||||||
RebuildingRefCounts(e) => write!(f, "failed to rebuild ref counts: {e}"),
|
|
||||||
RefcountTableOffEnd => write!(f, "refcount table offset past file end"),
|
|
||||||
RefcountTableTooLarge => write!(f, "too many clusters specified for refcount table"),
|
|
||||||
SeekingFile(e) => write!(f, "failed to seek file: {e}"),
|
|
||||||
SettingFileSize(e) => write!(f, "failed to set file size: {e}"),
|
|
||||||
SettingRefcountRefcount(e) => write!(f, "failed to set refcount refcount: {e}"),
|
|
||||||
SizeTooSmallForNumberOfClusters => write!(f, "size too small for number of clusters"),
|
|
||||||
TooManyL1Entries(count) => write!(f, "l1 entry table too large: {count}"),
|
|
||||||
TooManyRefcounts(count) => write!(f, "ref count table too large: {count}"),
|
|
||||||
UnsupportedRefcountOrder => write!(f, "unsupported refcount order"),
|
|
||||||
UnsupportedVersion(v) => write!(f, "unsupported version: {v}"),
|
|
||||||
WritingData(e) => write!(f, "failed to write data: {e}"),
|
|
||||||
WritingHeader(e) => write!(f, "failed to write header: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ImageType {
|
pub enum ImageType {
|
||||||
Raw,
|
Raw,
|
||||||
Qcow2,
|
Qcow2,
|
||||||
@@ -447,12 +439,20 @@ pub struct QcowFile {
|
|||||||
// List of unreferenced clusters available to be used. unref clusters become available once the
|
// List of unreferenced clusters available to be used. unref clusters become available once the
|
||||||
// removal of references to them have been synced to disk.
|
// removal of references to them have been synced to disk.
|
||||||
avail_clusters: Vec<u64>,
|
avail_clusters: Vec<u64>,
|
||||||
backing_file: Option<Box<dyn BlockBackend>>,
|
backing_file: Option<Box<Self>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QcowFile {
|
impl QcowFile {
|
||||||
/// Creates a QcowFile from `file`. File must be a valid qcow2 image.
|
/// Creates a QcowFile from `file`. File must be a valid qcow2 image.
|
||||||
pub fn from(mut file: RawFile) -> Result<QcowFile> {
|
///
|
||||||
|
/// Additionally, max nesting depth of this qcow2 image will be set to default value 10.
|
||||||
|
pub fn from(file: RawFile) -> Result<QcowFile> {
|
||||||
|
Self::from_with_nesting_depth(file, MAX_NESTING_DEPTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a QcowFile from `file` and with a max nesting depth. File must be a valid qcow2
|
||||||
|
/// image.
|
||||||
|
pub fn from_with_nesting_depth(mut file: RawFile, max_nesting_depth: u32) -> Result<QcowFile> {
|
||||||
let header = QcowHeader::new(&mut file)?;
|
let header = QcowHeader::new(&mut file)?;
|
||||||
|
|
||||||
// Only v2 and v3 files are supported.
|
// Only v2 and v3 files are supported.
|
||||||
@@ -479,14 +479,20 @@ impl QcowFile {
|
|||||||
let direct_io = file.is_direct();
|
let direct_io = file.is_direct();
|
||||||
|
|
||||||
let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
|
let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
|
||||||
|
if max_nesting_depth == 0 {
|
||||||
|
return Err(Error::MaxNestingDepthExceeded);
|
||||||
|
}
|
||||||
let path = backing_file_path.clone();
|
let path = backing_file_path.clone();
|
||||||
let backing_raw_file = OpenOptions::new()
|
let backing_raw_file = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(path)
|
.open(path)
|
||||||
.map_err(Error::BackingFileIo)?;
|
.map_err(Error::BackingFileIo)?;
|
||||||
let backing_file = crate::create_disk_file(backing_raw_file, direct_io)
|
let backing_file = Self::from_with_nesting_depth(
|
||||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
RawFile::new(backing_raw_file, direct_io),
|
||||||
Some(backing_file)
|
max_nesting_depth - 1,
|
||||||
|
)
|
||||||
|
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||||
|
Some(Box::new(backing_file))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -634,20 +640,22 @@ impl QcowFile {
|
|||||||
file: RawFile,
|
file: RawFile,
|
||||||
version: u32,
|
version: u32,
|
||||||
backing_file_name: &str,
|
backing_file_name: &str,
|
||||||
|
backing_file_max_nesting_depth: u32,
|
||||||
) -> Result<QcowFile> {
|
) -> Result<QcowFile> {
|
||||||
let direct_io = file.is_direct();
|
let direct_io = file.is_direct();
|
||||||
let backing_raw_file = OpenOptions::new()
|
let backing_raw_file = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(backing_file_name)
|
.open(backing_file_name)
|
||||||
.map_err(Error::BackingFileIo)?;
|
.map_err(Error::BackingFileIo)?;
|
||||||
let backing_file = crate::create_disk_file(backing_raw_file, direct_io)
|
let backing_file = Self::from_with_nesting_depth(
|
||||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
RawFile::new(backing_raw_file, direct_io),
|
||||||
let size = backing_file
|
backing_file_max_nesting_depth,
|
||||||
.size()
|
)
|
||||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||||
|
let size = backing_file.virtual_size();
|
||||||
let header = QcowHeader::create_for_size_and_path(version, size, Some(backing_file_name))?;
|
let header = QcowHeader::create_for_size_and_path(version, size, Some(backing_file_name))?;
|
||||||
let mut result = QcowFile::new_from_header(file, header)?;
|
let mut result = QcowFile::new_from_header(file, header)?;
|
||||||
result.backing_file = Some(backing_file);
|
result.backing_file = Some(Box::new(backing_file));
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,7 +683,7 @@ impl QcowFile {
|
|||||||
Ok(qcow)
|
Ok(qcow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_backing_file(&mut self, backing: Option<Box<dyn BlockBackend>>) {
|
pub fn set_backing_file(&mut self, backing: Option<Box<Self>>) {
|
||||||
self.backing_file = backing;
|
self.backing_file = backing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1213,7 +1221,7 @@ impl QcowFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate and initialize a new data cluster. Returns the offset of the
|
// Allocate and initialize a new data cluster. Returns the offset of the
|
||||||
// cluster in to the file on success.
|
// cluster into the file on success.
|
||||||
fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
|
fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
|
||||||
let new_addr: u64 = self.get_new_cluster(initial_data)?;
|
let new_addr: u64 = self.get_new_cluster(initial_data)?;
|
||||||
// The cluster refcount starts at one indicating it is used but doesn't need COW.
|
// The cluster refcount starts at one indicating it is used but doesn't need COW.
|
||||||
@@ -1631,7 +1639,7 @@ impl PunchHole for QcowFile {
|
|||||||
let mut remaining = length;
|
let mut remaining = length;
|
||||||
let mut offset = offset;
|
let mut offset = offset;
|
||||||
while remaining > 0 {
|
while remaining > 0 {
|
||||||
let chunk_length = min(remaining, std::usize::MAX as u64) as usize;
|
let chunk_length = min(remaining, usize::MAX as u64) as usize;
|
||||||
self.deallocate_bytes(offset, chunk_length)?;
|
self.deallocate_bytes(offset, chunk_length)?;
|
||||||
remaining -= chunk_length as u64;
|
remaining -= chunk_length as u64;
|
||||||
offset += chunk_length as u64;
|
offset += chunk_length as u64;
|
||||||
@@ -1791,11 +1799,17 @@ where
|
|||||||
/// Copy the contents of a disk image in `src_file` into `dst_file`.
|
/// Copy the contents of a disk image in `src_file` into `dst_file`.
|
||||||
/// The type of `src_file` is automatically detected, and the output file type is
|
/// The type of `src_file` is automatically detected, and the output file type is
|
||||||
/// determined by `dst_type`.
|
/// determined by `dst_type`.
|
||||||
pub fn convert(mut src_file: RawFile, dst_file: RawFile, dst_type: ImageType) -> Result<()> {
|
pub fn convert(
|
||||||
|
mut src_file: RawFile,
|
||||||
|
dst_file: RawFile,
|
||||||
|
dst_type: ImageType,
|
||||||
|
src_max_nesting_depth: u32,
|
||||||
|
) -> Result<()> {
|
||||||
let src_type = detect_image_type(&mut src_file)?;
|
let src_type = detect_image_type(&mut src_file)?;
|
||||||
match src_type {
|
match src_type {
|
||||||
ImageType::Qcow2 => {
|
ImageType::Qcow2 => {
|
||||||
let mut src_reader = QcowFile::from(src_file)?;
|
let mut src_reader =
|
||||||
|
QcowFile::from_with_nesting_depth(src_file, src_max_nesting_depth)?;
|
||||||
convert_reader(&mut src_reader, dst_file, dst_type)
|
convert_reader(&mut src_reader, dst_file, dst_type)
|
||||||
}
|
}
|
||||||
ImageType::Raw => {
|
ImageType::Raw => {
|
||||||
@@ -1824,6 +1838,9 @@ pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::Path;
|
||||||
|
use vmm_sys_util::tempdir::TempDir;
|
||||||
use vmm_sys_util::tempfile::TempFile;
|
use vmm_sys_util::tempfile::TempFile;
|
||||||
use vmm_sys_util::write_zeroes::WriteZeroes;
|
use vmm_sys_util::write_zeroes::WriteZeroes;
|
||||||
|
|
||||||
@@ -2023,6 +2040,82 @@ mod tests {
|
|||||||
assert_eq!(read_header.backing_file_path, header.backing_file_path);
|
assert_eq!(read_header.backing_file_path, header.backing_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_backing_file() {
|
||||||
|
// `backing_file` is `None`
|
||||||
|
let header = QcowHeader::create_for_size_and_path(3, 0x10_0000, None)
|
||||||
|
.expect("Failed to create header.");
|
||||||
|
let mut disk_file: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), false);
|
||||||
|
header
|
||||||
|
.write_to(&mut disk_file)
|
||||||
|
.expect("Failed to write header to shm.");
|
||||||
|
disk_file.rewind().unwrap();
|
||||||
|
// The maximum nesting depth is 0, which means backing file is not allowed.
|
||||||
|
let res = QcowFile::from_with_nesting_depth(disk_file, 0);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disable_backing_file() {
|
||||||
|
// `backing_file` is `Some`
|
||||||
|
let header =
|
||||||
|
QcowHeader::create_for_size_and_path(3, 0x10_0000, Some("/path/to/backing/file"))
|
||||||
|
.expect("Failed to create header.");
|
||||||
|
let mut disk_file: RawFile = RawFile::new(TempFile::new().unwrap().into_file(), false);
|
||||||
|
header
|
||||||
|
.write_to(&mut disk_file)
|
||||||
|
.expect("Failed to write header to shm.");
|
||||||
|
disk_file.rewind().unwrap();
|
||||||
|
// The maximum nesting depth is 0, which means backing file is not allowed.
|
||||||
|
let res = QcowFile::from_with_nesting_depth(disk_file, 0);
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(matches!(res.unwrap_err(), Error::MaxNestingDepthExceeded));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a qcow2 file with itself as its backing file.
|
||||||
|
///
|
||||||
|
/// Without configuration `max_nesting_depth`, this will cause infinite recursion when loading
|
||||||
|
/// the file until stack overflow.
|
||||||
|
fn new_self_referential_qcow(path: &Path) -> Result<()> {
|
||||||
|
let header = QcowHeader::create_for_size_and_path(3, 0x10_0000, path.to_str())?;
|
||||||
|
let mut disk_file = RawFile::new(
|
||||||
|
File::create(path).expect("Failed to create image file."),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
header.write_to(&mut disk_file)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn max_nesting_backing() {
|
||||||
|
let test_dir = TempDir::new_with_prefix("/tmp/ch").unwrap();
|
||||||
|
let img_path = test_dir.as_path().join("test.img");
|
||||||
|
|
||||||
|
new_self_referential_qcow(img_path.as_path()).unwrap();
|
||||||
|
|
||||||
|
let err = QcowFile::from_with_nesting_depth(
|
||||||
|
RawFile::new(
|
||||||
|
File::open(img_path.as_path()).expect("Failed to open qcow image file"),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
MAX_NESTING_DEPTH,
|
||||||
|
)
|
||||||
|
.expect_err("Opening qcow file with itself as backing file should fail.");
|
||||||
|
|
||||||
|
// This type of error is complex. For comparing easily, we can check if it contains the
|
||||||
|
// type name after formatting.
|
||||||
|
assert!(format!("{err:?}").contains(&format!("{:?}", Error::MaxNestingDepthExceeded)));
|
||||||
|
// This should recursively call the function ten times before throwing an error, and the
|
||||||
|
// error `BackingFileOpen` should also be repeated ten times.
|
||||||
|
assert_eq!(
|
||||||
|
format!("{err:?}")
|
||||||
|
.matches("BackingFileOpen")
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.len() as u32,
|
||||||
|
MAX_NESTING_DEPTH,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_magic() {
|
fn invalid_magic() {
|
||||||
let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
|
let invalid_header = vec![0x51u8, 0x46, 0x4a, 0xfb];
|
||||||
|
|||||||
@@ -4,50 +4,37 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use libc::EINVAL;
|
use libc::EINVAL;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::qcow::{
|
use crate::qcow::{
|
||||||
qcow_raw_file::QcowRawFile,
|
qcow_raw_file::QcowRawFile,
|
||||||
vec_cache::{CacheMap, Cacheable, VecCache},
|
vec_cache::{CacheMap, Cacheable, VecCache},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// `EvictingCache` - Error writing a refblock from the cache to disk.
|
/// `EvictingCache` - Error writing a refblock from the cache to disk.
|
||||||
|
#[error("Failed to write a refblock from the cache to disk: {0}")]
|
||||||
EvictingRefCounts(io::Error),
|
EvictingRefCounts(io::Error),
|
||||||
/// `InvalidIndex` - Address requested isn't within the range of the disk.
|
/// `InvalidIndex` - Address requested isn't within the range of the disk.
|
||||||
|
#[error("Address requested is not within the range of the disk")]
|
||||||
InvalidIndex,
|
InvalidIndex,
|
||||||
/// `NeedCluster` - Handle this error by reading the cluster and calling the function again.
|
/// `NeedCluster` - Handle this error by reading the cluster and calling the function again.
|
||||||
|
#[error("Cluster with addr={0} needs to be read")]
|
||||||
NeedCluster(u64),
|
NeedCluster(u64),
|
||||||
/// `NeedNewCluster` - Handle this error by allocating a cluster and calling the function again.
|
/// `NeedNewCluster` - Handle this error by allocating a cluster and calling the function again.
|
||||||
|
#[error("New cluster needs to be allocated for refcounts")]
|
||||||
NeedNewCluster,
|
NeedNewCluster,
|
||||||
/// `ReadingRefCounts` - Error reading the file in to the refcount cache.
|
/// `ReadingRefCounts` - Error reading the file into the refcount cache.
|
||||||
|
#[error("Failed to read the file into the refcount cache: {0}")]
|
||||||
ReadingRefCounts(io::Error),
|
ReadingRefCounts(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
impl Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use self::Error::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
EvictingRefCounts(e) => {
|
|
||||||
write!(f, "failed to write a refblock from the cache to disk: {e}")
|
|
||||||
}
|
|
||||||
InvalidIndex => write!(f, "address requested is not within the range of the disk"),
|
|
||||||
NeedCluster(addr) => write!(f, "cluster with addr={addr} needs to be read"),
|
|
||||||
NeedNewCluster => write!(f, "new cluster needs to be allocated for refcounts"),
|
|
||||||
ReadingRefCounts(e) => {
|
|
||||||
write!(f, "failed to read the file into the refcount cache: {e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the refcount entries for an open qcow file.
|
/// Represents the refcount entries for an open qcow file.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RefCount {
|
pub struct RefCount {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ pub struct Header {
|
|||||||
impl Header {
|
impl Header {
|
||||||
/// Reads the Header structure from a reference VHDx file
|
/// Reads the Header structure from a reference VHDx file
|
||||||
pub fn new(f: &mut File, start: u64) -> Result<Header> {
|
pub fn new(f: &mut File, start: u64) -> Result<Header> {
|
||||||
// Read the whole header in to a buffer. We will need it for
|
// Read the whole header into a buffer. We will need it for
|
||||||
// calculating checksum.
|
// calculating checksum.
|
||||||
let mut buffer = [0; HEADER_SIZE as usize];
|
let mut buffer = [0; HEADER_SIZE as usize];
|
||||||
f.seek(SeekFrom::Start(start))
|
f.seek(SeekFrom::Start(start))
|
||||||
@@ -470,7 +470,7 @@ impl VhdxHeader {
|
|||||||
pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32> {
|
pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32> {
|
||||||
// Read the checksum into a mutable slice
|
// Read the checksum into a mutable slice
|
||||||
let csum_buf = &mut buffer[csum_offset..csum_offset + 4];
|
let csum_buf = &mut buffer[csum_offset..csum_offset + 4];
|
||||||
// Convert the checksum chunk in to a u32 integer
|
// Convert the checksum chunk into a u32 integer
|
||||||
let orig_csum = LittleEndian::read_u32(csum_buf);
|
let orig_csum = LittleEndian::read_u32(csum_buf);
|
||||||
// Zero the checksum in the buffer
|
// Zero the checksum in the buffer
|
||||||
LittleEndian::write_u32(csum_buf, 0);
|
LittleEndian::write_u32(csum_buf, 0);
|
||||||
|
|||||||
@@ -1,26 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "devices"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Chromium OS Authors"]
|
authors = ["The Chromium OS Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "devices"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.86"
|
||||||
arch = { path = "../arch" }
|
arch = { path = "../arch" }
|
||||||
bitflags = "2.5.0"
|
bitflags = "2.6.0"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
event_monitor = { path = "../event_monitor" }
|
event_monitor = { path = "../event_monitor" }
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
log = "0.4.21"
|
log = "0.4.22"
|
||||||
|
num_enum = "0.7.2"
|
||||||
pci = { path = "../pci" }
|
pci = { path = "../pci" }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
tpm = { path = "../tpm" }
|
tpm = { path = "../tpm" }
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = "0.14.1"
|
vm-memory = { version = "0.14.1", features = [
|
||||||
|
"backend-atomic",
|
||||||
|
"backend-bitmap",
|
||||||
|
"backend-mmap",
|
||||||
|
] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
|
|
||||||
@@ -29,3 +34,4 @@ arch = { path = "../arch" }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
pvmemcontrol = []
|
||||||
|
|||||||
@@ -4,31 +4,42 @@
|
|||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use thiserror::Error;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Invalid trigger mode.
|
/// Invalid trigger mode.
|
||||||
|
#[error("Invalid trigger mode")]
|
||||||
InvalidTriggerMode,
|
InvalidTriggerMode,
|
||||||
/// Invalid delivery mode.
|
/// Invalid delivery mode.
|
||||||
|
#[error("Invalid delivery mode")]
|
||||||
InvalidDeliveryMode,
|
InvalidDeliveryMode,
|
||||||
/// Failed creating the interrupt source group.
|
/// Failed creating the interrupt source group.
|
||||||
|
#[error("Failed creating the interrupt source group: {0}")]
|
||||||
CreateInterruptSourceGroup(io::Error),
|
CreateInterruptSourceGroup(io::Error),
|
||||||
/// Failed triggering the interrupt.
|
/// Failed triggering the interrupt.
|
||||||
|
#[error("Failed triggering the interrupt: {0}")]
|
||||||
TriggerInterrupt(io::Error),
|
TriggerInterrupt(io::Error),
|
||||||
/// Failed masking the interrupt.
|
/// Failed masking the interrupt.
|
||||||
|
#[error("Failed masking the interrupt: {0}")]
|
||||||
MaskInterrupt(io::Error),
|
MaskInterrupt(io::Error),
|
||||||
/// Failed unmasking the interrupt.
|
/// Failed unmasking the interrupt.
|
||||||
|
#[error("Failed unmasking the interrupt: {0}")]
|
||||||
UnmaskInterrupt(io::Error),
|
UnmaskInterrupt(io::Error),
|
||||||
/// Failed updating the interrupt.
|
/// Failed updating the interrupt.
|
||||||
|
#[error("Failed updating the interrupt: {0}")]
|
||||||
UpdateInterrupt(io::Error),
|
UpdateInterrupt(io::Error),
|
||||||
/// Failed enabling the interrupt.
|
/// Failed enabling the interrupt.
|
||||||
|
#[error("Failed enabling the interrupt: {0}")]
|
||||||
EnableInterrupt(io::Error),
|
EnableInterrupt(io::Error),
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
/// Failed creating GIC device.
|
/// Failed creating GIC device.
|
||||||
|
#[error("Failed creating GIC device: {0}")]
|
||||||
CreateGic(hypervisor::HypervisorVmError),
|
CreateGic(hypervisor::HypervisorVmError),
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
/// Failed restoring GIC device.
|
/// Failed restoring GIC device.
|
||||||
|
#[error("Failed restoring GIC device: {0}")]
|
||||||
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ impl InterruptController for Ioapic {
|
|||||||
self.interrupt_source_group
|
self.interrupt_source_group
|
||||||
.trigger(irq as InterruptIndex)
|
.trigger(irq as InterruptIndex)
|
||||||
.map_err(Error::TriggerInterrupt)?;
|
.map_err(Error::TriggerInterrupt)?;
|
||||||
debug!("Interrupt successfully delivered");
|
debug!("Interrupt {irq} successfully delivered");
|
||||||
|
|
||||||
// If trigger mode is level sensitive, set the Remote IRR bit.
|
// If trigger mode is level sensitive, set the Remote IRR bit.
|
||||||
// It will be cleared when the EOI is received.
|
// It will be cleared when the EOI is received.
|
||||||
|
|||||||
@@ -9,9 +9,10 @@
|
|||||||
|
|
||||||
use crate::{read_le_u32, write_le_u32};
|
use crate::{read_le_u32, write_le_u32};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::{fmt, io};
|
use thiserror::Error;
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
@@ -37,29 +38,18 @@ const GPIO_ID_HIGH: u64 = 0x1000;
|
|||||||
|
|
||||||
const N_GPIOS: u32 = 8;
|
const N_GPIOS: u32 = 8;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Bad Write Offset: {0}")]
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
|
#[error("GPIO interrupt disabled by guest driver.")]
|
||||||
GpioInterruptDisabled,
|
GpioInterruptDisabled,
|
||||||
|
#[error("Could not trigger GPIO interrupt: {0}.")]
|
||||||
GpioInterruptFailure(io::Error),
|
GpioInterruptFailure(io::Error),
|
||||||
|
#[error("Invalid GPIO Input key triggered: {0}.")]
|
||||||
GpioTriggerKeyFailure(u32),
|
GpioTriggerKeyFailure(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {offset}"),
|
|
||||||
Error::GpioInterruptDisabled => write!(f, "GPIO interrupt disabled by guest driver.",),
|
|
||||||
Error::GpioInterruptFailure(ref e) => {
|
|
||||||
write!(f, "Could not trigger GPIO interrupt: {e}.")
|
|
||||||
}
|
|
||||||
Error::GpioTriggerKeyFailure(key) => {
|
|
||||||
write!(f, "Invalid GPIO Input key triggered: {key}.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// A GPIO device following the PL061 specification.
|
/// A GPIO device following the PL061 specification.
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
//! a real-time clock input.
|
//! a real-time clock input.
|
||||||
//!
|
//!
|
||||||
use crate::{read_le_u32, write_le_u32};
|
use crate::{read_le_u32, write_le_u32};
|
||||||
use std::fmt;
|
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
|
use thiserror::Error;
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
|
|
||||||
@@ -39,21 +39,14 @@ const AMBA_ID_HIGH: u64 = 0x1000;
|
|||||||
/// Constant to convert seconds to nanoseconds.
|
/// Constant to convert seconds to nanoseconds.
|
||||||
pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
|
pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Bad Write Offset: {0}")]
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
|
#[error("Failed to trigger interrupt: {0}")]
|
||||||
InterruptFailure(io::Error),
|
InterruptFailure(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {offset}"),
|
|
||||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// Wrapper over `libc::clockid_t` to specify Linux Kernel clock source.
|
/// Wrapper over `libc::clockid_t` to specify Linux Kernel clock source.
|
||||||
@@ -79,98 +72,6 @@ impl From<ClockType> for libc::clockid_t {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure representing the date in local time with nanosecond precision.
|
|
||||||
pub struct LocalTime {
|
|
||||||
/// Seconds in current minute.
|
|
||||||
sec: i32,
|
|
||||||
/// Minutes in current hour.
|
|
||||||
min: i32,
|
|
||||||
/// Hours in current day, 24H format.
|
|
||||||
hour: i32,
|
|
||||||
/// Days in current month.
|
|
||||||
mday: i32,
|
|
||||||
/// Months in current year.
|
|
||||||
mon: i32,
|
|
||||||
/// Years passed since 1900 BC.
|
|
||||||
year: i32,
|
|
||||||
/// Nanoseconds in current second.
|
|
||||||
nsec: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LocalTime {
|
|
||||||
/// Returns the [LocalTime](struct.LocalTime.html) structure for the calling moment.
|
|
||||||
#[cfg(test)]
|
|
||||||
pub fn now() -> LocalTime {
|
|
||||||
let mut timespec = libc::timespec {
|
|
||||||
tv_sec: 0,
|
|
||||||
tv_nsec: 0,
|
|
||||||
};
|
|
||||||
let mut tm: libc::tm = libc::tm {
|
|
||||||
tm_sec: 0,
|
|
||||||
tm_min: 0,
|
|
||||||
tm_hour: 0,
|
|
||||||
tm_mday: 0,
|
|
||||||
tm_mon: 0,
|
|
||||||
tm_year: 0,
|
|
||||||
tm_wday: 0,
|
|
||||||
tm_yday: 0,
|
|
||||||
tm_isdst: 0,
|
|
||||||
tm_gmtoff: 0,
|
|
||||||
tm_zone: std::ptr::null(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// SAFETY: the parameters are valid.
|
|
||||||
unsafe {
|
|
||||||
libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
|
|
||||||
libc::localtime_r(×pec.tv_sec, &mut tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalTime {
|
|
||||||
sec: tm.tm_sec,
|
|
||||||
min: tm.tm_min,
|
|
||||||
hour: tm.tm_hour,
|
|
||||||
mday: tm.tm_mday,
|
|
||||||
mon: tm.tm_mon,
|
|
||||||
year: tm.tm_year,
|
|
||||||
nsec: timespec.tv_nsec,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for LocalTime {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}",
|
|
||||||
self.year + 1900,
|
|
||||||
self.mon + 1,
|
|
||||||
self.mday,
|
|
||||||
self.hour,
|
|
||||||
self.min,
|
|
||||||
self.sec,
|
|
||||||
self.nsec
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Holds a micro-second resolution timestamp with both the real time and cpu time.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TimestampUs {
|
|
||||||
/// Real time in microseconds.
|
|
||||||
pub time_us: u64,
|
|
||||||
/// Cpu time in microseconds.
|
|
||||||
pub cputime_us: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TimestampUs {
|
|
||||||
fn default() -> TimestampUs {
|
|
||||||
TimestampUs {
|
|
||||||
time_us: get_time(ClockType::Monotonic) / 1000,
|
|
||||||
cputime_us: get_time(ClockType::ProcessCpu) / 1000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a timestamp in nanoseconds based on the provided clock type.
|
/// Returns a timestamp in nanoseconds based on the provided clock type.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -337,6 +238,70 @@ mod tests {
|
|||||||
|
|
||||||
const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
|
||||||
|
|
||||||
|
struct LocalTime {
|
||||||
|
sec: i32,
|
||||||
|
min: i32,
|
||||||
|
hour: i32,
|
||||||
|
mday: i32,
|
||||||
|
mon: i32,
|
||||||
|
year: i32,
|
||||||
|
nsec: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalTime {
|
||||||
|
fn now() -> LocalTime {
|
||||||
|
let mut timespec = libc::timespec {
|
||||||
|
tv_sec: 0,
|
||||||
|
tv_nsec: 0,
|
||||||
|
};
|
||||||
|
let mut tm: libc::tm = libc::tm {
|
||||||
|
tm_sec: 0,
|
||||||
|
tm_min: 0,
|
||||||
|
tm_hour: 0,
|
||||||
|
tm_mday: 0,
|
||||||
|
tm_mon: 0,
|
||||||
|
tm_year: 0,
|
||||||
|
tm_wday: 0,
|
||||||
|
tm_yday: 0,
|
||||||
|
tm_isdst: 0,
|
||||||
|
tm_gmtoff: 0,
|
||||||
|
tm_zone: std::ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: the parameters are valid.
|
||||||
|
unsafe {
|
||||||
|
libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
|
||||||
|
libc::localtime_r(×pec.tv_sec, &mut tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalTime {
|
||||||
|
sec: tm.tm_sec,
|
||||||
|
min: tm.tm_min,
|
||||||
|
hour: tm.tm_hour,
|
||||||
|
mday: tm.tm_mday,
|
||||||
|
mon: tm.tm_mon,
|
||||||
|
year: tm.tm_year,
|
||||||
|
nsec: timespec.tv_nsec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for LocalTime {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}",
|
||||||
|
self.year + 1900,
|
||||||
|
self.mon + 1,
|
||||||
|
self.mday,
|
||||||
|
self.hour,
|
||||||
|
self.min,
|
||||||
|
self.sec,
|
||||||
|
self.nsec
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_time() {
|
fn test_get_time() {
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
|
|||||||
@@ -411,14 +411,11 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, DATA as u64, &[b'x', b'y']);
|
serial.write(0, DATA as u64, b"xy");
|
||||||
serial.write(0, DATA as u64, &[b'a']);
|
serial.write(0, DATA as u64, b"a");
|
||||||
serial.write(0, DATA as u64, &[b'b']);
|
serial.write(0, DATA as u64, b"b");
|
||||||
serial.write(0, DATA as u64, &[b'c']);
|
serial.write(0, DATA as u64, b"c");
|
||||||
assert_eq!(
|
assert_eq!(serial_out.buf.lock().unwrap().as_slice(), b"abc");
|
||||||
serial_out.buf.lock().unwrap().as_slice(),
|
|
||||||
&[b'a', b'b', b'c']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -436,7 +433,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_RECV_BIT]);
|
serial.write(0, IER as u64, &[IER_RECV_BIT]);
|
||||||
serial.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
|
serial.queue_input_bytes(b"abc").unwrap();
|
||||||
|
|
||||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
|
|
||||||
@@ -473,7 +470,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, &[b'a']);
|
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];
|
||||||
@@ -515,9 +512,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
serial.write(0, MCR as u64, &[MCR_LOOP_BIT]);
|
||||||
serial.write(0, DATA as u64, &[b'a']);
|
serial.write(0, DATA as u64, b"a");
|
||||||
serial.write(0, DATA as u64, &[b'b']);
|
serial.write(0, DATA as u64, b"b");
|
||||||
serial.write(0, DATA as u64, &[b'c']);
|
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[..]);
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
use crate::{read_le_u32, write_le_u32};
|
use crate::{read_le_u32, write_le_u32};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
|
||||||
use std::sync::{Arc, Barrier};
|
use std::sync::{Arc, Barrier};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
|
use thiserror::Error;
|
||||||
use vm_device::interrupt::InterruptSourceGroup;
|
use vm_device::interrupt::InterruptSourceGroup;
|
||||||
use vm_device::BusDevice;
|
use vm_device::BusDevice;
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
@@ -45,27 +45,20 @@ const PL011_ID: [u8; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
|||||||
const AMBA_ID_LOW: u64 = 0x3f8;
|
const AMBA_ID_LOW: u64 = 0x3f8;
|
||||||
const AMBA_ID_HIGH: u64 = 0x401;
|
const AMBA_ID_HIGH: u64 = 0x401;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("pl011_write: Bad Write Offset: {0}")]
|
||||||
BadWriteOffset(u64),
|
BadWriteOffset(u64),
|
||||||
|
#[error("pl011: DMA not implemented.")]
|
||||||
DmaNotImplemented,
|
DmaNotImplemented,
|
||||||
|
#[error("Failed to trigger interrupt: {0}")]
|
||||||
InterruptFailure(io::Error),
|
InterruptFailure(io::Error),
|
||||||
|
#[error("Failed to write: {0}")]
|
||||||
WriteAllFailure(io::Error),
|
WriteAllFailure(io::Error),
|
||||||
|
#[error("Failed to flush: {0}")]
|
||||||
FlushFailure(io::Error),
|
FlushFailure(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::BadWriteOffset(offset) => write!(f, "pl011_write: Bad Write Offset: {offset}"),
|
|
||||||
Error::DmaNotImplemented => write!(f, "pl011: DMA not implemented."),
|
|
||||||
Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {e}"),
|
|
||||||
Error::WriteAllFailure(e) => write!(f, "Failed to write: {e}"),
|
|
||||||
Error::FlushFailure(e) => write!(f, "Failed to flush: {e}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
/// A PL011 device following the PL011 specification.
|
/// A PL011 device following the PL011 specification.
|
||||||
@@ -531,14 +524,11 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
pl011.write(0, UARTDR, &[b'x', b'y']);
|
pl011.write(0, UARTDR, b"xy");
|
||||||
pl011.write(0, UARTDR, &[b'a']);
|
pl011.write(0, UARTDR, b"a");
|
||||||
pl011.write(0, UARTDR, &[b'b']);
|
pl011.write(0, UARTDR, b"b");
|
||||||
pl011.write(0, UARTDR, &[b'c']);
|
pl011.write(0, UARTDR, b"c");
|
||||||
assert_eq!(
|
assert_eq!(pl011_out.buf.lock().unwrap().as_slice(), b"xabc");
|
||||||
pl011_out.buf.lock().unwrap().as_slice(),
|
|
||||||
&[b'x', b'a', b'b', b'c']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -556,7 +546,7 @@ mod tests {
|
|||||||
// 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());
|
||||||
pl011.queue_input_bytes(&[b'a', b'b', b'c']).unwrap();
|
pl011.queue_input_bytes(b"abc").unwrap();
|
||||||
|
|
||||||
assert_eq!(intr_evt.read().unwrap(), 2);
|
assert_eq!(intr_evt.read().unwrap(), 2);
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ pub mod interrupt_controller;
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod ioapic;
|
pub mod ioapic;
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
|
#[cfg(feature = "pvmemcontrol")]
|
||||||
|
pub mod pvmemcontrol;
|
||||||
pub mod pvpanic;
|
pub mod pvpanic;
|
||||||
pub mod tpm;
|
pub mod tpm;
|
||||||
|
|
||||||
|
|||||||
819
devices/src/pvmemcontrol.rs
Normal file
819
devices/src/pvmemcontrol.rs
Normal file
@@ -0,0 +1,819 @@
|
|||||||
|
// Copyright © 2024 Google LLC
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use pci::{
|
||||||
|
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
||||||
|
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ffi::CString,
|
||||||
|
io, result,
|
||||||
|
sync::{Arc, Barrier, Mutex, RwLock},
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
use vm_allocator::{page_size::get_page_size, AddressAllocator, SystemAllocator};
|
||||||
|
use vm_device::{BusDeviceSync, Resource};
|
||||||
|
use vm_memory::{
|
||||||
|
bitmap::AtomicBitmap, Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory,
|
||||||
|
GuestMemoryAtomic, GuestMemoryError, GuestMemoryMmap, Le32, Le64,
|
||||||
|
};
|
||||||
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
|
|
||||||
|
const PVMEMCONTROL_VENDOR_ID: u16 = 0x1ae0;
|
||||||
|
const PVMEMCONTROL_DEVICE_ID: u16 = 0x0087;
|
||||||
|
|
||||||
|
const PVMEMCONTROL_SUBSYSTEM_VENDOR_ID: u16 = 0x1ae0;
|
||||||
|
const PVMEMCONTROL_SUBSYSTEM_ID: u16 = 0x011F;
|
||||||
|
|
||||||
|
const MAJOR_VERSION: u64 = 1;
|
||||||
|
const MINOR_VERSION: u64 = 0;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
// device errors
|
||||||
|
#[error("Guest gave us bad memory addresses: {0}")]
|
||||||
|
GuestMemory(#[source] GuestMemoryError),
|
||||||
|
#[error("Guest sent us invalid request")]
|
||||||
|
InvalidRequest,
|
||||||
|
|
||||||
|
#[error("Guest sent us invalid command: {0}")]
|
||||||
|
InvalidCommand(u32),
|
||||||
|
#[error("Guest sent us invalid connection: {0}")]
|
||||||
|
InvalidConnection(u32),
|
||||||
|
|
||||||
|
// pvmemcontrol errors
|
||||||
|
#[error("Request contains invalid arguments: {0}")]
|
||||||
|
InvalidArgument(u64),
|
||||||
|
#[error("Unknown function code: {0}")]
|
||||||
|
UnknownFunctionCode(u64),
|
||||||
|
#[error("Libc call fail: {0}")]
|
||||||
|
LibcFail(#[source] std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum PvmemcontrolSubclass {
|
||||||
|
Other = 0x80,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciSubclass for PvmemcontrolSubclass {
|
||||||
|
fn get_register_value(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// commands have 0 as the most significant byte
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, TryFromPrimitive)]
|
||||||
|
enum PvmemcontrolTransportCommand {
|
||||||
|
Reset = 0x060f_e6d2,
|
||||||
|
Register = 0x0e35_9539,
|
||||||
|
Ready = 0x0ca8_d227,
|
||||||
|
Disconnect = 0x030f_5da0,
|
||||||
|
Ack = 0x03cf_5196,
|
||||||
|
Error = 0x01fb_a249,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct PvmemcontrolTransportRegister {
|
||||||
|
buf_phys_addr: Le64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct PvmemcontrolTransportRegisterResponse {
|
||||||
|
command: Le32,
|
||||||
|
_padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
union PvmemcontrolTransportUnion {
|
||||||
|
register: PvmemcontrolTransportRegister,
|
||||||
|
register_response: PvmemcontrolTransportRegisterResponse,
|
||||||
|
unit: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct PvmemcontrolTransport {
|
||||||
|
payload: PvmemcontrolTransportUnion,
|
||||||
|
command: PvmemcontrolTransportCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
const PVMEMCONTROL_DEVICE_MMIO_SIZE: u64 = std::mem::size_of::<PvmemcontrolTransport>() as u64;
|
||||||
|
const PVMEMCONTROL_DEVICE_MMIO_ALIGN: u64 = std::mem::align_of::<PvmemcontrolTransport>() as u64;
|
||||||
|
|
||||||
|
impl PvmemcontrolTransport {
|
||||||
|
fn ack() -> Self {
|
||||||
|
PvmemcontrolTransport {
|
||||||
|
payload: PvmemcontrolTransportUnion { unit: () },
|
||||||
|
command: PvmemcontrolTransportCommand::Ack,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error() -> Self {
|
||||||
|
PvmemcontrolTransport {
|
||||||
|
payload: PvmemcontrolTransportUnion { unit: () },
|
||||||
|
command: PvmemcontrolTransportCommand::Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_response(command: u32) -> Self {
|
||||||
|
PvmemcontrolTransport {
|
||||||
|
payload: PvmemcontrolTransportUnion {
|
||||||
|
register_response: PvmemcontrolTransportRegisterResponse {
|
||||||
|
command: command.into(),
|
||||||
|
_padding: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
command: PvmemcontrolTransportCommand::Ack,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn as_register(self) -> PvmemcontrolTransportRegister {
|
||||||
|
self.payload.register
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Contains no references and does not have compiler-inserted padding
|
||||||
|
unsafe impl ByteValued for PvmemcontrolTransportUnion {}
|
||||||
|
// SAFETY: Contains no references and does not have compiler-inserted padding
|
||||||
|
unsafe impl ByteValued for PvmemcontrolTransport {}
|
||||||
|
|
||||||
|
#[repr(u64)]
|
||||||
|
#[derive(Copy, Clone, TryFromPrimitive, Debug)]
|
||||||
|
enum FunctionCode {
|
||||||
|
Info = 0,
|
||||||
|
Dontneed = 1,
|
||||||
|
Remove = 2,
|
||||||
|
Free = 3,
|
||||||
|
Pageout = 4,
|
||||||
|
Dontdump = 5,
|
||||||
|
SetVMAAnonName = 6,
|
||||||
|
Mlock = 7,
|
||||||
|
Munlock = 8,
|
||||||
|
MprotectNone = 9,
|
||||||
|
MprotectR = 10,
|
||||||
|
MprotectW = 11,
|
||||||
|
MprotectRW = 12,
|
||||||
|
Mergeable = 13,
|
||||||
|
Unmergeable = 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
struct PvmemcontrolReq {
|
||||||
|
func_code: Le64,
|
||||||
|
addr: Le64,
|
||||||
|
length: Le64,
|
||||||
|
arg: Le64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: it only has data and has no implicit padding.
|
||||||
|
unsafe impl ByteValued for PvmemcontrolReq {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
struct PvmemcontrolResp {
|
||||||
|
ret_errno: Le32,
|
||||||
|
ret_code: Le32,
|
||||||
|
ret_value: Le64,
|
||||||
|
arg0: Le64,
|
||||||
|
arg1: Le64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for PvmemcontrolResp {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let PvmemcontrolResp {
|
||||||
|
ret_errno,
|
||||||
|
ret_code,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"PvmemcontrolResp {{ ret_errno: {}, ret_code: {}, .. }}",
|
||||||
|
ret_errno.to_native(),
|
||||||
|
ret_code.to_native()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: it only has data and has no implicit padding.
|
||||||
|
unsafe impl ByteValued for PvmemcontrolResp {}
|
||||||
|
|
||||||
|
/// The guest connections start at 0x8000_0000, which has a leading 1 in
|
||||||
|
/// the most significant byte, this ensures it does not conflict with
|
||||||
|
/// any of the transport commands
|
||||||
|
#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
pub struct GuestConnection {
|
||||||
|
command: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GuestConnection {
|
||||||
|
fn default() -> Self {
|
||||||
|
GuestConnection::new(0x8000_0000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuestConnection {
|
||||||
|
fn new(command: u32) -> Self {
|
||||||
|
Self { command }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self) -> Self {
|
||||||
|
let GuestConnection { command } = *self;
|
||||||
|
|
||||||
|
if command == u32::MAX {
|
||||||
|
GuestConnection::default()
|
||||||
|
} else {
|
||||||
|
GuestConnection::new(command + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for GuestConnection {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
if (value & 0x8000_0000) != 0 {
|
||||||
|
Ok(GuestConnection::new(value))
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidConnection(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PercpuInitState {
|
||||||
|
port_buf_map: HashMap<GuestConnection, GuestAddress>,
|
||||||
|
next_conn: GuestConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PercpuInitState {
|
||||||
|
fn new() -> Self {
|
||||||
|
PercpuInitState {
|
||||||
|
port_buf_map: HashMap::new(),
|
||||||
|
next_conn: GuestConnection::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PvmemcontrolState {
|
||||||
|
PercpuInit(PercpuInitState),
|
||||||
|
Ready(HashMap<GuestConnection, GuestAddress>),
|
||||||
|
Broken,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PvmemcontrolDevice {
|
||||||
|
transport: PvmemcontrolTransport,
|
||||||
|
state: PvmemcontrolState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PvmemcontrolDevice {
|
||||||
|
fn new(transport: PvmemcontrolTransport, state: PvmemcontrolState) -> Self {
|
||||||
|
PvmemcontrolDevice { transport, state }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PvmemcontrolDevice {
|
||||||
|
fn register_percpu_buf(
|
||||||
|
guest_memory: &GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
|
||||||
|
mut state: PercpuInitState,
|
||||||
|
PvmemcontrolTransportRegister { buf_phys_addr }: PvmemcontrolTransportRegister,
|
||||||
|
) -> Self {
|
||||||
|
// access to this address is checked
|
||||||
|
let buf_phys_addr = GuestAddress(buf_phys_addr.into());
|
||||||
|
if !guest_memory.memory().check_range(
|
||||||
|
buf_phys_addr,
|
||||||
|
std::mem::size_of::<PvmemcontrolResp>().max(std::mem::size_of::<PvmemcontrolReq>()),
|
||||||
|
) {
|
||||||
|
warn!("guest sent invalid phys addr {:#x}", buf_phys_addr.0);
|
||||||
|
return PvmemcontrolDevice::new(
|
||||||
|
PvmemcontrolTransport::error(),
|
||||||
|
PvmemcontrolState::Broken,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let conn = {
|
||||||
|
// find an available port+byte combination, and fail if full
|
||||||
|
let mut next_conn = state.next_conn;
|
||||||
|
while state.port_buf_map.contains_key(&next_conn) {
|
||||||
|
next_conn = next_conn.next();
|
||||||
|
if next_conn == state.next_conn {
|
||||||
|
warn!("connections exhausted");
|
||||||
|
return PvmemcontrolDevice::new(
|
||||||
|
PvmemcontrolTransport::error(),
|
||||||
|
PvmemcontrolState::Broken,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_conn
|
||||||
|
};
|
||||||
|
state.next_conn = conn.next();
|
||||||
|
state.port_buf_map.insert(conn, buf_phys_addr);
|
||||||
|
|
||||||
|
// inform guest of the connection
|
||||||
|
let response = PvmemcontrolTransport::register_response(conn.command);
|
||||||
|
|
||||||
|
PvmemcontrolDevice::new(response, PvmemcontrolState::PercpuInit(state))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset() -> Self {
|
||||||
|
PvmemcontrolDevice::new(
|
||||||
|
PvmemcontrolTransport::ack(),
|
||||||
|
PvmemcontrolState::PercpuInit(PercpuInitState::new()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error() -> Self {
|
||||||
|
PvmemcontrolDevice::new(PvmemcontrolTransport::error(), PvmemcontrolState::Broken)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(PercpuInitState { port_buf_map, .. }: PercpuInitState) -> Self {
|
||||||
|
PvmemcontrolDevice::new(
|
||||||
|
PvmemcontrolTransport::ack(),
|
||||||
|
PvmemcontrolState::Ready(port_buf_map),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_command(
|
||||||
|
&mut self,
|
||||||
|
guest_memory: &GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
|
||||||
|
command: PvmemcontrolTransportCommand,
|
||||||
|
) {
|
||||||
|
let state = std::mem::replace(&mut self.state, PvmemcontrolState::Broken);
|
||||||
|
|
||||||
|
*self = match command {
|
||||||
|
PvmemcontrolTransportCommand::Reset => Self::reset(),
|
||||||
|
PvmemcontrolTransportCommand::Register => {
|
||||||
|
if let PvmemcontrolState::PercpuInit(state) = state {
|
||||||
|
// SAFETY: By device protocol. If driver is wrong the device
|
||||||
|
// can enter a Broken state, but the behavior is still sound.
|
||||||
|
Self::register_percpu_buf(guest_memory, state, unsafe {
|
||||||
|
self.transport.as_register()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
debug!("received register without reset");
|
||||||
|
Self::error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PvmemcontrolTransportCommand::Ready => {
|
||||||
|
if let PvmemcontrolState::PercpuInit(state) = state {
|
||||||
|
Self::ready(state)
|
||||||
|
} else {
|
||||||
|
debug!("received ready without reset");
|
||||||
|
Self::error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PvmemcontrolTransportCommand::Disconnect => Self::error(),
|
||||||
|
PvmemcontrolTransportCommand::Ack => {
|
||||||
|
debug!("received ack as command");
|
||||||
|
Self::error()
|
||||||
|
}
|
||||||
|
PvmemcontrolTransportCommand::Error => {
|
||||||
|
debug!("received error as command");
|
||||||
|
Self::error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read from the transport
|
||||||
|
fn read_transport(&self, offset: u64, data: &mut [u8]) {
|
||||||
|
self.transport
|
||||||
|
.as_slice()
|
||||||
|
.iter()
|
||||||
|
.skip(offset as usize)
|
||||||
|
.zip(data.iter_mut())
|
||||||
|
.for_each(|(src, dest)| *dest = *src)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// can only write to transport payload
|
||||||
|
/// command is a special register that needs separate dispatching
|
||||||
|
fn write_transport(&mut self, offset: u64, data: &[u8]) {
|
||||||
|
self.transport
|
||||||
|
.payload
|
||||||
|
.as_mut_slice()
|
||||||
|
.iter_mut()
|
||||||
|
.skip(offset as usize)
|
||||||
|
.zip(data.iter())
|
||||||
|
.for_each(|(dest, src)| *dest = *src)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_connection(&self, conn: GuestConnection) -> Option<GuestAddress> {
|
||||||
|
match &self.state {
|
||||||
|
PvmemcontrolState::Ready(map) => map.get(&conn).copied(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PvmemcontrolBusDevice {
|
||||||
|
mem: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
|
||||||
|
dev: RwLock<PvmemcontrolDevice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PvmemcontrolPciDevice {
|
||||||
|
id: String,
|
||||||
|
configuration: PciConfiguration,
|
||||||
|
bar_regions: Vec<PciBarConfiguration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PvmemcontrolBusDevice {
|
||||||
|
/// f is called with the host address of `range_base` and only when
|
||||||
|
/// [`range_base`, `range_base` + `range_len`) is present in the guest
|
||||||
|
fn operate_on_memory_range<F>(&self, addr: u64, length: u64, f: F) -> result::Result<(), Error>
|
||||||
|
where
|
||||||
|
F: FnOnce(*mut libc::c_void, libc::size_t) -> libc::c_int,
|
||||||
|
{
|
||||||
|
let memory = self.mem.memory();
|
||||||
|
let range_base = GuestAddress(addr);
|
||||||
|
let range_len = usize::try_from(length).map_err(|_| Error::InvalidRequest)?;
|
||||||
|
|
||||||
|
// assume guest memory is not interleaved with vmm memory on the host.
|
||||||
|
if !memory.check_range(range_base, range_len) {
|
||||||
|
return Err(Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
|
||||||
|
range_base,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let hva = memory
|
||||||
|
.get_host_address(range_base)
|
||||||
|
.map_err(Error::GuestMemory)?;
|
||||||
|
let res = f(hva as *mut libc::c_void, range_len as libc::size_t);
|
||||||
|
if res != 0 {
|
||||||
|
return Err(Error::LibcFail(io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn madvise(&self, addr: u64, length: u64, advice: libc::c_int) -> result::Result<(), Error> {
|
||||||
|
// SAFETY: [`base`, `base` + `len`) is guest memory
|
||||||
|
self.operate_on_memory_range(addr, length, |base, len| unsafe {
|
||||||
|
libc::madvise(base, len, advice)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mlock(&self, addr: u64, length: u64, on_default: bool) -> result::Result<(), Error> {
|
||||||
|
// SAFETY: [`base`, `base` + `len`) is guest memory
|
||||||
|
self.operate_on_memory_range(addr, length, |base, len| unsafe {
|
||||||
|
libc::mlock2(base, len, if on_default { libc::MLOCK_ONFAULT } else { 0 })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn munlock(&self, addr: u64, length: u64) -> result::Result<(), Error> {
|
||||||
|
// SAFETY: [`base`, `base` + `len`) is guest memory
|
||||||
|
self.operate_on_memory_range(addr, length, |base, len| unsafe {
|
||||||
|
libc::munlock(base, len)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mprotect(
|
||||||
|
&self,
|
||||||
|
addr: u64,
|
||||||
|
length: u64,
|
||||||
|
protection: libc::c_int,
|
||||||
|
) -> result::Result<(), Error> {
|
||||||
|
// SAFETY: [`base`, `base` + `len`) is guest memory
|
||||||
|
self.operate_on_memory_range(addr, length, |base, len| unsafe {
|
||||||
|
libc::mprotect(base, len, protection)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_vma_anon_name(&self, addr: u64, length: u64, name: u64) -> result::Result<(), Error> {
|
||||||
|
let name = (name != 0).then(|| CString::new(format!("pvmemcontrol-{}", name)).unwrap());
|
||||||
|
let name_ptr = if let Some(name) = &name {
|
||||||
|
name.as_ptr()
|
||||||
|
} else {
|
||||||
|
std::ptr::null()
|
||||||
|
};
|
||||||
|
debug!("addr {:X} length {} name {:?}", addr, length, name);
|
||||||
|
|
||||||
|
// SAFETY: [`base`, `base` + `len`) is guest memory
|
||||||
|
self.operate_on_memory_range(addr, length, |base, len| unsafe {
|
||||||
|
libc::prctl(
|
||||||
|
libc::PR_SET_VMA,
|
||||||
|
libc::PR_SET_VMA_ANON_NAME,
|
||||||
|
base,
|
||||||
|
len,
|
||||||
|
name_ptr,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_request(
|
||||||
|
&self,
|
||||||
|
func_code: FunctionCode,
|
||||||
|
addr: u64,
|
||||||
|
length: u64,
|
||||||
|
arg: u64,
|
||||||
|
) -> Result<PvmemcontrolResp, Error> {
|
||||||
|
let result = match func_code {
|
||||||
|
FunctionCode::Info => {
|
||||||
|
return Ok(PvmemcontrolResp {
|
||||||
|
ret_errno: 0.into(),
|
||||||
|
ret_code: 0.into(),
|
||||||
|
ret_value: get_page_size().into(),
|
||||||
|
arg0: MAJOR_VERSION.into(),
|
||||||
|
arg1: MINOR_VERSION.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
FunctionCode::Dontneed => self.madvise(addr, length, libc::MADV_DONTNEED),
|
||||||
|
FunctionCode::Remove => self.madvise(addr, length, libc::MADV_REMOVE),
|
||||||
|
FunctionCode::Free => self.madvise(addr, length, libc::MADV_FREE),
|
||||||
|
FunctionCode::Pageout => self.madvise(addr, length, libc::MADV_PAGEOUT),
|
||||||
|
FunctionCode::Dontdump => self.madvise(addr, length, libc::MADV_DONTDUMP),
|
||||||
|
FunctionCode::SetVMAAnonName => self.set_vma_anon_name(addr, length, arg),
|
||||||
|
FunctionCode::Mlock => self.mlock(addr, length, false),
|
||||||
|
FunctionCode::Munlock => self.munlock(addr, length),
|
||||||
|
FunctionCode::MprotectNone => self.mprotect(addr, length, libc::PROT_NONE),
|
||||||
|
FunctionCode::MprotectR => self.mprotect(addr, length, libc::PROT_READ),
|
||||||
|
FunctionCode::MprotectW => self.mprotect(addr, length, libc::PROT_WRITE),
|
||||||
|
FunctionCode::MprotectRW => {
|
||||||
|
self.mprotect(addr, length, libc::PROT_READ | libc::PROT_WRITE)
|
||||||
|
}
|
||||||
|
FunctionCode::Mergeable => self.madvise(addr, length, libc::MADV_MERGEABLE),
|
||||||
|
FunctionCode::Unmergeable => self.madvise(addr, length, libc::MADV_UNMERGEABLE),
|
||||||
|
};
|
||||||
|
result.map(|_| PvmemcontrolResp::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_request(
|
||||||
|
&self,
|
||||||
|
PvmemcontrolReq {
|
||||||
|
func_code,
|
||||||
|
addr,
|
||||||
|
length,
|
||||||
|
arg,
|
||||||
|
}: PvmemcontrolReq,
|
||||||
|
) -> Result<PvmemcontrolResp, Error> {
|
||||||
|
let (func_code, addr, length, arg) = (
|
||||||
|
func_code.to_native(),
|
||||||
|
addr.to_native(),
|
||||||
|
length.to_native(),
|
||||||
|
arg.to_native(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let resp_or_err = FunctionCode::try_from(func_code)
|
||||||
|
.map_err(|_| Error::UnknownFunctionCode(func_code))
|
||||||
|
.and_then(|func_code| self.process_request(func_code, addr, length, arg));
|
||||||
|
|
||||||
|
let resp = match resp_or_err {
|
||||||
|
Ok(resp) => resp,
|
||||||
|
Err(e) => match e {
|
||||||
|
Error::InvalidArgument(arg) => PvmemcontrolResp {
|
||||||
|
ret_errno: (libc::EINVAL as u32).into(),
|
||||||
|
ret_code: (arg as u32).into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Error::LibcFail(err) => PvmemcontrolResp {
|
||||||
|
ret_errno: (err.raw_os_error().unwrap_or(libc::EFAULT) as u32).into(),
|
||||||
|
ret_code: 0u32.into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Error::UnknownFunctionCode(func_code) => PvmemcontrolResp {
|
||||||
|
ret_errno: (libc::EOPNOTSUPP as u32).into(),
|
||||||
|
ret_code: (func_code as u32).into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Error::GuestMemory(err) => {
|
||||||
|
warn!("{}", err);
|
||||||
|
PvmemcontrolResp {
|
||||||
|
ret_errno: (libc::EINVAL as u32).into(),
|
||||||
|
ret_code: (func_code as u32).into(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// device error, stop responding
|
||||||
|
other => return Err(other),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_pvmemcontrol_request(&self, guest_addr: GuestAddress) {
|
||||||
|
let request: PvmemcontrolReq = if let Ok(x) = self.mem.memory().read_obj(guest_addr) {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
warn!("cannot read from guest address {:#x}", guest_addr.0);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let response: PvmemcontrolResp = match self.handle_request(request) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("cannot process request {:?} with error {}", request, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.mem.memory().write_obj(response, guest_addr).is_err() {
|
||||||
|
warn!("cannot write to guest address {:#x}", guest_addr.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_guest_write(&self, offset: u64, data: &[u8]) {
|
||||||
|
if offset as usize != std::mem::offset_of!(PvmemcontrolTransport, command) {
|
||||||
|
if data.len() != 4 && data.len() != 8 {
|
||||||
|
warn!("guest write is not 4 or 8 bytes long");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.dev.write().unwrap().write_transport(offset, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data = if data.len() == 4 {
|
||||||
|
let mut d = [0u8; 4];
|
||||||
|
d.iter_mut()
|
||||||
|
.zip(data.iter())
|
||||||
|
.for_each(|(d, data)| *d = *data);
|
||||||
|
d
|
||||||
|
} else {
|
||||||
|
warn!("guest write with non u32 at command register");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let data_cmd = u32::from_le_bytes(data);
|
||||||
|
let command = PvmemcontrolTransportCommand::try_from(data_cmd);
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Ok(command) => self.dev.write().unwrap().run_command(&self.mem, command),
|
||||||
|
Err(_) => {
|
||||||
|
GuestConnection::try_from(data_cmd)
|
||||||
|
.and_then(|conn| {
|
||||||
|
self.dev
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.find_connection(conn)
|
||||||
|
.ok_or(Error::InvalidConnection(conn.command))
|
||||||
|
})
|
||||||
|
.map(|gpa| self.handle_pvmemcontrol_request(gpa))
|
||||||
|
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_guest_read(&self, offset: u64, data: &mut [u8]) {
|
||||||
|
self.dev.read().unwrap().read_transport(offset, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PvmemcontrolDevice {
|
||||||
|
pub fn make_device(
|
||||||
|
id: String,
|
||||||
|
mem: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>,
|
||||||
|
) -> (PvmemcontrolPciDevice, PvmemcontrolBusDevice) {
|
||||||
|
let dev = RwLock::new(PvmemcontrolDevice::error());
|
||||||
|
let mut configuration = PciConfiguration::new(
|
||||||
|
PVMEMCONTROL_VENDOR_ID,
|
||||||
|
PVMEMCONTROL_DEVICE_ID,
|
||||||
|
0x1,
|
||||||
|
PciClassCode::BaseSystemPeripheral,
|
||||||
|
&PvmemcontrolSubclass::Other,
|
||||||
|
None,
|
||||||
|
PciHeaderType::Device,
|
||||||
|
PVMEMCONTROL_SUBSYSTEM_VENDOR_ID,
|
||||||
|
PVMEMCONTROL_SUBSYSTEM_ID,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let command: [u8; 2] = [0x03, 0x01]; // memory, io, SERR#
|
||||||
|
|
||||||
|
configuration.write_config_register(1, 0, &command);
|
||||||
|
(
|
||||||
|
PvmemcontrolPciDevice {
|
||||||
|
id,
|
||||||
|
configuration,
|
||||||
|
bar_regions: Vec::new(),
|
||||||
|
},
|
||||||
|
PvmemcontrolBusDevice { mem, dev },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciDevice for PvmemcontrolPciDevice {
|
||||||
|
fn write_config_register(
|
||||||
|
&mut self,
|
||||||
|
reg_idx: usize,
|
||||||
|
offset: u64,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<Arc<Barrier>> {
|
||||||
|
self.configuration
|
||||||
|
.write_config_register(reg_idx, offset, data);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_config_register(&mut self, reg_idx: usize) -> u32 {
|
||||||
|
self.configuration.read_config_register(reg_idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<String> {
|
||||||
|
Some(self.id.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_bar_reprogramming(
|
||||||
|
&mut self,
|
||||||
|
reg_idx: usize,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<BarReprogrammingParams> {
|
||||||
|
self.configuration.detect_bar_reprogramming(reg_idx, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate_bars(
|
||||||
|
&mut self,
|
||||||
|
_allocator: &Arc<Mutex<SystemAllocator>>,
|
||||||
|
mmio32_allocator: &mut AddressAllocator,
|
||||||
|
_mmio64_allocator: &mut AddressAllocator,
|
||||||
|
resources: Option<Vec<Resource>>,
|
||||||
|
) -> Result<Vec<PciBarConfiguration>, PciDeviceError> {
|
||||||
|
let mut bars = Vec::new();
|
||||||
|
let region_type = PciBarRegionType::Memory32BitRegion;
|
||||||
|
let bar_id = 0;
|
||||||
|
let region_size = PVMEMCONTROL_DEVICE_MMIO_SIZE;
|
||||||
|
let restoring = resources.is_some();
|
||||||
|
let bar_addr = mmio32_allocator
|
||||||
|
.allocate(None, region_size, Some(PVMEMCONTROL_DEVICE_MMIO_ALIGN))
|
||||||
|
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
|
||||||
|
|
||||||
|
let bar = PciBarConfiguration::default()
|
||||||
|
.set_index(bar_id as usize)
|
||||||
|
.set_address(bar_addr.raw_value())
|
||||||
|
.set_size(region_size)
|
||||||
|
.set_region_type(region_type)
|
||||||
|
.set_prefetchable(PciBarPrefetchable::NotPrefetchable);
|
||||||
|
|
||||||
|
if !restoring {
|
||||||
|
self.configuration
|
||||||
|
.add_pci_bar(&bar)
|
||||||
|
.map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
bars.push(bar);
|
||||||
|
self.bar_regions.clone_from(&bars);
|
||||||
|
Ok(bars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_bars(
|
||||||
|
&mut self,
|
||||||
|
_allocator: &mut SystemAllocator,
|
||||||
|
mmio32_allocator: &mut AddressAllocator,
|
||||||
|
_mmio64_allocator: &mut AddressAllocator,
|
||||||
|
) -> Result<(), PciDeviceError> {
|
||||||
|
for bar in self.bar_regions.drain(..) {
|
||||||
|
mmio32_allocator.free(GuestAddress(bar.addr()), bar.size())
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_bar(&mut self, old_base: u64, new_base: u64) -> result::Result<(), io::Error> {
|
||||||
|
for bar in self.bar_regions.iter_mut() {
|
||||||
|
if bar.addr() == old_base {
|
||||||
|
*bar = bar.set_address(new_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pausable for PvmemcontrolPciDevice {
|
||||||
|
fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self) -> std::result::Result<(), MigratableError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshottable for PvmemcontrolPciDevice {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||||
|
let mut snapshot = Snapshot::new_from_state(&())?;
|
||||||
|
|
||||||
|
// Snapshot PciConfiguration
|
||||||
|
snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?);
|
||||||
|
|
||||||
|
Ok(snapshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transportable for PvmemcontrolPciDevice {}
|
||||||
|
impl Migratable for PvmemcontrolPciDevice {}
|
||||||
|
|
||||||
|
impl BusDeviceSync for PvmemcontrolBusDevice {
|
||||||
|
fn read(&self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
self.handle_guest_read(offset, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
self.handle_guest_write(offset, data);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -450,7 +450,7 @@ impl BusDevice for Tpm {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Invalid value passed to CRTL_REQ register");
|
error!("Invalid value passed to CTRL_REQ register");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ definition in XML format:
|
|||||||
### Command Line Interface
|
### Command Line Interface
|
||||||
|
|
||||||
The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching
|
The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching
|
||||||
the Cloud Hypervisor binary, i.e. it can not be used for controlling the VMM or
|
the Cloud Hypervisor binary, i.e. it cannot be used for controlling the VMM or
|
||||||
the launched VM once they're up and running.
|
the launched VM once they're up and running.
|
||||||
|
|
||||||
If you want to inspect the VMM, or control the VM after launching Cloud
|
If you want to inspect the VMM, or control the VM after launching Cloud
|
||||||
|
|||||||
@@ -159,10 +159,10 @@ as we might need to update the direct kernel boot command line, replacing
|
|||||||
|
|
||||||
Update all references to the previous image name to the new one.
|
Update all references to the previous image name to the new one.
|
||||||
|
|
||||||
## NVIDIA image for VFIO baremetal CI
|
## NVIDIA image for VFIO bare-metal CI
|
||||||
|
|
||||||
Here we are going to describe how to create a cloud image that contains the
|
Here we are going to describe how to create a cloud image that contains the
|
||||||
necessary NVIDIA drivers for our VFIO baremetal CI.
|
necessary NVIDIA drivers for our VFIO bare-metal CI.
|
||||||
|
|
||||||
### Download base image
|
### Download base image
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Cloud Hypervisor uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) for fuzzing individual components.
|
Cloud Hypervisor uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) for fuzzing individual components.
|
||||||
|
|
||||||
The fuzzers are are in the `fuzz/fuzz_targets` directory
|
The fuzzers are in the `fuzz/fuzz_targets` directory
|
||||||
|
|
||||||
## Preparation
|
## Preparation
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ On-line CPU(s) list: 0-7
|
|||||||
|
|
||||||
After a reboot the added CPUs will remain.
|
After a reboot the added CPUs will remain.
|
||||||
|
|
||||||
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the reisze API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
|
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the resize API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 2
|
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 2
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Another reason for having a virtual IOMMU is to allow passing physical devices
|
|||||||
from the host through multiple layers of virtualization. Let's take as example
|
from the host through multiple layers of virtualization. Let's take as example
|
||||||
a system with a physical IOMMU running a VM with a virtual IOMMU. The
|
a system with a physical IOMMU running a VM with a virtual IOMMU. The
|
||||||
implementation of the virtual IOMMU is responsible for updating the physical
|
implementation of the virtual IOMMU is responsible for updating the physical
|
||||||
DMA Remapping table (DMAR) everytime the DMA mapping changes. This must happen
|
DMA Remapping table (DMAR) every time the DMA mapping changes. This must happen
|
||||||
through the VFIO framework on the host as this is the only userspace interface
|
through the VFIO framework on the host as this is the only userspace interface
|
||||||
to interact with a physical IOMMU.
|
to interact with a physical IOMMU.
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ On AArch64 architecture, the virtual IOMMU can still be used even if ACPI is not
|
|||||||
enabled. But the effect is different with what the aforementioned test showed.
|
enabled. But the effect is different with what the aforementioned test showed.
|
||||||
|
|
||||||
When ACPI is disabled, virtual IOMMU is supported through Flattened Device Tree
|
When ACPI is disabled, virtual IOMMU is supported through Flattened Device Tree
|
||||||
(FDT). In this case, the guest kernel can not tell which device should be
|
(FDT). In this case, the guest kernel cannot tell which device should be
|
||||||
IOMMU-attached and which should not. No matter how many devices you attached to
|
IOMMU-attached and which should not. No matter how many devices you attached to
|
||||||
the virtual IOMMU by setting `iommu=on` option, all the devices on the PCI bus
|
the virtual IOMMU by setting `iommu=on` option, all the devices on the PCI bus
|
||||||
will be attached to the virtual IOMMU (except the IOMMU itself). Each of the
|
will be attached to the virtual IOMMU (except the IOMMU itself). Each of the
|
||||||
|
|||||||
105
docs/landlock.md
Normal file
105
docs/landlock.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Sandboxing using Landlock
|
||||||
|
|
||||||
|
Landlock is a lightweight mechanism to allow unprivileged applications to
|
||||||
|
sandbox themselves.
|
||||||
|
|
||||||
|
During initial stages of running, applications can define the set of resources
|
||||||
|
(mostly files) they need to access during their lifetime. All such rules are
|
||||||
|
used to create a ruleset. Once the ruleset is applied, the process cannot access
|
||||||
|
any resources outside of the ruleset during its lifetime, even if it were
|
||||||
|
compromised.
|
||||||
|
|
||||||
|
Under the scope of `read` and `write` access, Landlock currently allows some
|
||||||
|
additional accesses (eg: for now, access to extended file attributes is always
|
||||||
|
allowed). Eventually, Landlock will only allow accesses similar to Unix
|
||||||
|
permissions.
|
||||||
|
|
||||||
|
## Host Setup
|
||||||
|
|
||||||
|
Landlock should be enabled in Host kernel to use it with cloud-hypervisor.
|
||||||
|
Please following [Kernel-Support](https://docs.kernel.org/userspace-api/landlock.html#kernel-support) link to enable Landlock on Host kernel.
|
||||||
|
|
||||||
|
|
||||||
|
Landlock support can be checked with following command:
|
||||||
|
```
|
||||||
|
$ sudo dmesg | grep -w landlock
|
||||||
|
[ 0.000000] landlock: Up and running.
|
||||||
|
```
|
||||||
|
Linux kernel confirms Landlock support with above message in dmesg.
|
||||||
|
|
||||||
|
## Enable Landlock
|
||||||
|
|
||||||
|
At the time of enabling Landlock, Cloud-Hypervisor process needs the complete
|
||||||
|
list of files it accesses over its lifetime. So, Landlock is enabled `vm_create`
|
||||||
|
stage of guest boot.
|
||||||
|
|
||||||
|
### Command Line
|
||||||
|
Append `--landlock` to Cloud-Hypervisor's command line to enable Landlock
|
||||||
|
support.
|
||||||
|
|
||||||
|
If you expect guest to access additional paths after it boots
|
||||||
|
(ex: during hotplug), those paths can be passed using `--landlock-rules` command
|
||||||
|
line parameter.
|
||||||
|
|
||||||
|
### API
|
||||||
|
Landlock can also be enabled during `vm.create` request by passing a config like below:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"landlock_enable": true,
|
||||||
|
"landlock_rules": [
|
||||||
|
{
|
||||||
|
"path": "/tmp/disk1",
|
||||||
|
"access": "rw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/tmp/disk2",
|
||||||
|
"access": "rw"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
To enable Landlock:
|
||||||
|
|
||||||
|
```
|
||||||
|
./cloud-hypervisor \
|
||||||
|
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||||
|
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
|
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
|
--landlock
|
||||||
|
```
|
||||||
|
Hotplugging any new file-backed resources to above guest will result in
|
||||||
|
**Permission Denied** error.
|
||||||
|
|
||||||
|
To enable Landlock with hotplug support:
|
||||||
|
|
||||||
|
```
|
||||||
|
./cloud-hypervisor \
|
||||||
|
--api-socket /tmpXXXX/ch.socket \
|
||||||
|
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
|
||||||
|
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
|
||||||
|
--cmdline "console=hvc0 root=/dev/vda1 rw" \
|
||||||
|
--cpus boot=4 \
|
||||||
|
--memory size=1024M \
|
||||||
|
--net "tap=,mac=,ip=,mask=" \
|
||||||
|
--landlock \
|
||||||
|
--landlock-rules path="/path/to/hotplug1",access="rw" path="/path/to/hotplug2",access="rw"
|
||||||
|
|
||||||
|
./ch-remote --api-socket /tmpXXXX/ch.socket \
|
||||||
|
add-disk "path=/path/to/hotplug/blk.raw"
|
||||||
|
```
|
||||||
|
|
||||||
|
`--landlock-rules` accepts file or directory paths among its options.
|
||||||
|
|
||||||
|
# References
|
||||||
|
|
||||||
|
* https://landlock.io/
|
||||||
@@ -466,7 +466,7 @@ List of virtual CPUs attached to the guest NUMA node identified by the
|
|||||||
`guest_numa_id` option. This allows for describing a list of CPUs which
|
`guest_numa_id` option. This allows for describing a list of CPUs which
|
||||||
must be seen by the guest as belonging to the NUMA node `guest_numa_id`.
|
must be seen by the guest as belonging to the NUMA node `guest_numa_id`.
|
||||||
|
|
||||||
One can use this option for a fine grained description of the NUMA topology
|
One can use this option for a fine-grained description of the NUMA topology
|
||||||
regarding the CPUs associated with it, which might help the guest run more
|
regarding the CPUs associated with it, which might help the guest run more
|
||||||
efficiently.
|
efficiently.
|
||||||
|
|
||||||
@@ -573,7 +573,7 @@ _Example_
|
|||||||
### PCI bus
|
### PCI bus
|
||||||
|
|
||||||
Cloud Hypervisor supports guests with one or more PCI segments. The default PCI segment always
|
Cloud Hypervisor supports guests with one or more PCI segments. The default PCI segment always
|
||||||
has affinity to NUMA node 0. Be default, all other PCI segments have afffinity to NUMA node 0.
|
has affinity to NUMA node 0. Be default, all other PCI segments have affinity to NUMA node 0.
|
||||||
The user may configure the NUMA affinity for any additional PCI segments.
|
The user may configure the NUMA affinity for any additional PCI segments.
|
||||||
|
|
||||||
_Example_
|
_Example_
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ e.g. `dev_cli.sh`. The only prerequisite is [Docker installation](https://docs.d
|
|||||||
Please note that upon its first invocation, this script will pull a
|
Please note that upon its first invocation, this script will pull a
|
||||||
fairly large container image.
|
fairly large container image.
|
||||||
|
|
||||||
|
## Run the Performance Tests
|
||||||
|
|
||||||
To generate metrics data for all available performance tests (including
|
To generate metrics data for all available performance tests (including
|
||||||
boot time, block I/O throughput, and network throughput & latency) and
|
boot time, block I/O throughput, and network throughput & latency) and
|
||||||
output the result into a json file:
|
output the result into a json file:
|
||||||
@@ -27,3 +29,92 @@ To generate metrics data for selected performance tests, e.g. boot time only:
|
|||||||
```
|
```
|
||||||
$ ./scripts/dev_cli.sh tests --metrics -- -- --report-file /tmp/metrics.json --test-filter boot_time
|
$ ./scripts/dev_cli.sh tests --metrics -- -- --report-file /tmp/metrics.json --test-filter boot_time
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To set custom timeout or test iterations for all performance tests:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./scripts/dev_cli.sh tests --metrics -- -- --timeout 5 --iterations 10
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tests Details
|
||||||
|
|
||||||
|
The following table lists the supported performance tests with default
|
||||||
|
timeout and number of iterations. The `timeout` defines the maximum
|
||||||
|
execution time of each test for each iteration. The `iteration` defines
|
||||||
|
how many times a test needs to be executed to generate the final metrics
|
||||||
|
data.
|
||||||
|
|
||||||
|
| **Type** | **Metric** | **Timeout(s)** | **Iterations** |
|
||||||
|
|------------|--------------------------------------------|----------------|----------------|
|
||||||
|
| Boot Time | boot_time_ms | 2 | 10 |
|
||||||
|
| | boot_time_pmem_ms | 2 | 10 |
|
||||||
|
| | boot_time_16_vcpus_ms | 2 | 10 |
|
||||||
|
| | boot_time_16_vcpus_pmem_ms | 2 | 10 |
|
||||||
|
| Virtio Net | virtio_net_latency_us | 10 | 5 |
|
||||||
|
| | virtio_net_throughput_single_queue_rx_gbps | 10 | 5 |
|
||||||
|
| | virtio_net_throughput_single_queue_tx_gbps | 10 | 5 |
|
||||||
|
| | virtio_net_throughput_multi_queue_rx_gbps | 10 | 5 |
|
||||||
|
| | virtio_net_throughput_multi_queue_tx_gbps | 10 | 5 |
|
||||||
|
| Block | block_read_MiBps | 10 | 5 |
|
||||||
|
| | block_write_MiBps | 10 | 5 |
|
||||||
|
| | block_random_read_MiBps | 10 | 5 |
|
||||||
|
| | block_random_write_MiBps | 10 | 5 |
|
||||||
|
| | block_multi_queue_read_MiBps | 10 | 5 |
|
||||||
|
| | block_multi_queue_write_MiBps | 10 | 5 |
|
||||||
|
| | block_multi_queue_random_read_MiBps | 10 | 5 |
|
||||||
|
| | block_multi_queue_random_write_MiBps | 10 | 5 |
|
||||||
|
| | block_read_IOPS | 10 | 5 |
|
||||||
|
| | block_write_IOPS | 10 | 5 |
|
||||||
|
| | block_random_read_IOPS | 10 | 5 |
|
||||||
|
| | block_random_write_IOPS | 10 | 5 |
|
||||||
|
| | block_multi_queue_read_IOPS | 10 | 5 |
|
||||||
|
| | block_multi_queue_write_IOPS | 10 | 5 |
|
||||||
|
| | block_multi_queue_random_read_IOPS | 10 | 5 |
|
||||||
|
| | block_multi_queue_random_write_IOPS | 10 | 5 |
|
||||||
|
| Other | restore_latency_time_ms | 2 | 10 |
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
Performance-metrics output the result into a json file if `report-file`
|
||||||
|
param is set. The fields included in JSON include:
|
||||||
|
|
||||||
|
| Field Name | Content |
|
||||||
|
|--------------------|------------------------------------------|
|
||||||
|
| git_human_readable | Recent tag information of git repository |
|
||||||
|
| git_revision | Commit id of HEAD |
|
||||||
|
| git_commit_date | Commit date of HEAD |
|
||||||
|
| date | Date for executing the program |
|
||||||
|
| results | A list of metrics |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Here is an example of generating metrics data for the boot time using
|
||||||
|
`pmem`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ./scripts/dev_cli.sh tests --metrics -- -- --test-filter boot_time_pmem_ms
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is a sample output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"git_human_readable": "v40.0",
|
||||||
|
"git_revision": "e9b263975786abbf895469b93dfc00f21ce39a88",
|
||||||
|
"git_commit_date": "Fri Jun 21 08:40:44 2024 +0000",
|
||||||
|
"date": "Tue Jul 16 16:35:29 UTC 2024",
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"name": "boot_time_pmem_ms",
|
||||||
|
"mean": 105.9461,
|
||||||
|
"std_dev": 7.140993312558129,
|
||||||
|
"max": 120.01499999999999,
|
||||||
|
"min": 92.37600000000002
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the metrics data above is for illustration purpose only and
|
||||||
|
does not represent the actual performance of Cloud Hypervisor on your
|
||||||
|
system.
|
||||||
|
|||||||
2
docs/releases.md
Executable file → Normal file
2
docs/releases.md
Executable file → Normal file
@@ -95,7 +95,7 @@ E - EOL
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### LTS Stablity Considerations
|
### LTS Stability Considerations
|
||||||
|
|
||||||
An LTS release is just a `MAJOR` release for which point releases are made for
|
An LTS release is just a `MAJOR` release for which point releases are made for
|
||||||
longer following the same rules for what can be backported to a `POINT` release.
|
longer following the same rules for what can be backported to a `POINT` release.
|
||||||
|
|||||||
@@ -93,6 +93,21 @@ start using it.
|
|||||||
At this point, the VM is fully restored and is identical to the VM which was
|
At this point, the VM is fully restored and is identical to the VM which was
|
||||||
snapshot earlier.
|
snapshot earlier.
|
||||||
|
|
||||||
|
## Restore a VM with new Net FDs
|
||||||
|
For a VM created with FDs explicitly passed to NetConfig, a set of valid FDs
|
||||||
|
need to be provided along with the VM restore command in the following syntax:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# First terminal
|
||||||
|
./cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
|
||||||
|
|
||||||
|
# Second terminal
|
||||||
|
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock restore source_url=file:///home/foo/snapshot net_fds=[net1@[23,24],net2@[25,26]]
|
||||||
|
```
|
||||||
|
In the example above, the net device with id `net1` will be backed by FDs '23'
|
||||||
|
and '24', and the net device with id `net2` will be backed by FDs '25' and '26'
|
||||||
|
from the restored VM.
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
VFIO devices and Intel SGX are out of scope.
|
VFIO devices and Intel SGX are out of scope.
|
||||||
|
|||||||
@@ -37,6 +37,6 @@ generating traces of the boot. These can be relocated for focus tracing on a
|
|||||||
narrow part of the code base.
|
narrow part of the code base.
|
||||||
|
|
||||||
A `tracer::trace_point!()` macro is also provided for an instantaneous trace
|
A `tracer::trace_point!()` macro is also provided for an instantaneous trace
|
||||||
point however this is not in use in the code base currently nor is handled by
|
point however this is neither in use in the code base currently nor is handled by
|
||||||
the visualisation script due to the difficulty in representation in the SVG.
|
the visualisation script due to the difficulty in representation in the SVG.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "event_monitor"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "event_monitor"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
flume = "0.11.0"
|
flume = "0.11.0"
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
serde = { version = "1.0.197", features = ["rc", "derive"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.120"
|
||||||
|
|||||||
432
fuzz/Cargo.lock
generated
432
fuzz/Cargo.lock
generated
@@ -12,47 +12,48 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.13"
|
version = "0.6.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
"anstyle-query",
|
"anstyle-query",
|
||||||
"anstyle-wincon",
|
"anstyle-wincon",
|
||||||
"colorchoice",
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.6"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.0.2"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.2"
|
version = "3.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@@ -60,16 +61,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.82"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "api_client"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"vmm-sys-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
@@ -105,9 +99,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@@ -117,9 +111,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
@@ -127,7 +121,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"crc-any",
|
"crc-any",
|
||||||
"io-uring",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"remain",
|
"remain",
|
||||||
@@ -156,12 +149,13 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.92"
|
version = "1.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
|
checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -172,61 +166,36 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.4"
|
version = "4.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.2"
|
version = "4.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"strsim 0.11.1",
|
"strsim",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cloud-hypervisor"
|
|
||||||
version = "38.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"api_client",
|
|
||||||
"clap",
|
|
||||||
"epoll",
|
|
||||||
"event_monitor",
|
|
||||||
"hypervisor",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"option_parser",
|
|
||||||
"seccompiler",
|
|
||||||
"serde_json",
|
|
||||||
"signal-hook",
|
|
||||||
"thiserror",
|
|
||||||
"tpm",
|
|
||||||
"tracer",
|
|
||||||
"vm-memory",
|
|
||||||
"vmm",
|
|
||||||
"vmm-sys-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloud-hypervisor-fuzz"
|
name = "cloud-hypervisor-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block",
|
"block",
|
||||||
"cloud-hypervisor",
|
|
||||||
"devices",
|
"devices",
|
||||||
"epoll",
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -248,24 +217,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.0"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc-any"
|
name = "crc-any"
|
||||||
version = "2.4.4"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c01a5e1f881f6fb6099a7bdf949e946719fd4f1fefa56264890574febf0eb6d0"
|
checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"debug-helper",
|
"debug-helper",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.8"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
|
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"darling_macro",
|
"darling_macro",
|
||||||
@@ -273,23 +242,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_core"
|
name = "darling_core"
|
||||||
version = "0.20.8"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
|
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim 0.10.0",
|
"strsim",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.8"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -309,12 +278,13 @@ dependencies = [
|
|||||||
"acpi_tables",
|
"acpi_tables",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arch",
|
"arch",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"num_enum",
|
||||||
"pci",
|
"pci",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -326,16 +296,42 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2"
|
||||||
|
version = "0.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2_derive"
|
||||||
|
version = "0.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epoll"
|
name = "epoll"
|
||||||
version = "4.3.3"
|
version = "4.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event_monitor"
|
name = "event_monitor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -384,10 +380,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "gdbstub"
|
||||||
version = "0.2.14"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
checksum = "6341b3480afbb34eaefc7f92713bc92f2d83e338aaa1c44192f9c2956f4a4903"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"cfg-if",
|
||||||
|
"log",
|
||||||
|
"managed",
|
||||||
|
"num-traits",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdbstub_arch"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e3b1357bd3203fc09a6601327ae0ab38865d14231d0b65d3143f5762cc7977d"
|
||||||
|
dependencies = [
|
||||||
|
"gdbstub",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -396,6 +416,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hypervisor"
|
name = "hypervisor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -421,15 +447,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "indexmap"
|
||||||
version = "0.6.3"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9febecd4aebbe9c7c23c8e536e966805fdf09944c8a915e7991ee51acb67087"
|
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"equivalent",
|
||||||
"libc",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@@ -438,9 +470,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.30"
|
version = "0.1.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2"
|
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -471,17 +503,28 @@ 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 = "bedae2ca4a531bebe311abaf9691f5cc14eaa21475243caa2e39c43bb872947d"
|
checksum = "bedae2ca4a531bebe311abaf9691f5cc14eaa21475243caa2e39c43bb872947d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "landlock"
|
||||||
version = "0.2.153"
|
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 = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
checksum = "dafb8a4afee64f167eb2b52d32f0eea002e41a7a6450e68c799c8ec3a81a634c"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2",
|
||||||
|
"libc",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
@@ -505,9 +548,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.11"
|
version = "0.4.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
@@ -515,9 +558,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.21"
|
version = "0.4.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "managed"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "micro_http"
|
name = "micro_http"
|
||||||
@@ -563,6 +618,36 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
@@ -573,6 +658,12 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|||||||
name = "option_parser"
|
name = "option_parser"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pci"
|
name = "pci"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -595,10 +686,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro-crate"
|
||||||
version = "1.0.81"
|
version = "3.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -625,9 +725,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remain"
|
name = "remain"
|
||||||
version = "0.2.13"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad9f2390298a947ee0aa6073d440e221c0726188cfbcdf9604addb6ee393eb4a"
|
checksum = "46aef80f842736de545ada6ec65b81ee91504efd6853f4b96de7414c42ae7443"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -636,9 +736,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.17"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
@@ -657,18 +757,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.198"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.198"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -677,9 +777,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.115"
|
version = "1.0.120"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
|
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -688,9 +788,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "3.8.0"
|
version = "3.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c85f8e96d1d6857f13768fcbd895fcb06225510022a2774ed8b5150581847b0"
|
checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -699,9 +799,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "3.8.0"
|
version = "3.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8b3a576c4eb2924262d5951a3b737ccaf16c931e39a2810c36f9a7e25575557"
|
checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -713,6 +813,12 @@ dependencies = [
|
|||||||
name = "serial_buffer"
|
name = "serial_buffer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
@@ -747,12 +853,6 @@ dependencies = [
|
|||||||
"lock_api",
|
"lock_api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -761,9 +861,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.58"
|
version = "2.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -772,24 +872,41 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.58"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.58"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tpm"
|
name = "tpm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -822,15 +939,15 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.8.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
@@ -878,11 +995,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vhost"
|
name = "vhost"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b64e816d0d49769fbfaa1494eb77cc2a3ddc526ead05c7f922cb7d64106286f"
|
checksum = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"libc",
|
"libc",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
@@ -929,9 +1046,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-queue"
|
name = "virtio-queue"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3f69a13d6610db9312acbb438b0390362af905d37634a2106be70c0f734986d"
|
checksum = "07d8406e7250c934462de585d8f2d2781c31819bca1fbb7c5e964ca6bbaabfe8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"virtio-bindings",
|
"virtio-bindings",
|
||||||
@@ -1006,7 +1123,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"arch",
|
"arch",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"block",
|
"block",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -1014,7 +1131,10 @@ dependencies = [
|
|||||||
"epoll",
|
"epoll",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"flume",
|
"flume",
|
||||||
|
"gdbstub",
|
||||||
|
"gdbstub_arch",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
|
"landlock",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"log",
|
"log",
|
||||||
@@ -1150,13 +1270,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm",
|
||||||
@@ -1165,51 +1286,66 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.5.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.32"
|
version = "0.7.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
@@ -1217,9 +1353,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.7.32"
|
version = "0.7.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -1,135 +1,133 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cloud-hypervisor-fuzz"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["Automatically generated"]
|
authors = ["Automatically generated"]
|
||||||
publish = false
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "cloud-hypervisor-fuzz"
|
||||||
|
publish = false
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
igvm = []
|
igvm = []
|
||||||
|
pvmemcontrol = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
block = { path = "../block" }
|
block = { path = "../block" }
|
||||||
devices = { path = "../devices" }
|
devices = { path = "../devices" }
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
libfuzzer-sys = "0.4.7"
|
libfuzzer-sys = "0.4.7"
|
||||||
linux-loader = { version = "0.11.0", features = ["elf", "bzimage", "pe"] }
|
linux-loader = { version = "0.11.0", features = ["bzimage", "elf", "pe"] }
|
||||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
seccompiler = "0.4.0"
|
seccompiler = "0.4.0"
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
virtio-queue = "0.11.0"
|
virtio-queue = "0.12.0"
|
||||||
vmm = { path = "../vmm" }
|
vm-device = { path = "../vm-device" }
|
||||||
vmm-sys-util = "0.12.1"
|
|
||||||
vm-memory = "0.14.1"
|
vm-memory = "0.14.1"
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
vm-device = { path = "../vm-device" }
|
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
|
vmm = { path = "../vmm", features = ["guest_debug"] }
|
||||||
[dependencies.cloud-hypervisor]
|
vmm-sys-util = "0.12.1"
|
||||||
path = ".."
|
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["."]
|
members = ["."]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "balloon"
|
name = "balloon"
|
||||||
path = "fuzz_targets/balloon.rs"
|
path = "fuzz_targets/balloon.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "block"
|
name = "block"
|
||||||
path = "fuzz_targets/block.rs"
|
path = "fuzz_targets/block.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "cmos"
|
name = "cmos"
|
||||||
path = "fuzz_targets/cmos.rs"
|
path = "fuzz_targets/cmos.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "console"
|
name = "console"
|
||||||
path = "fuzz_targets/console.rs"
|
path = "fuzz_targets/console.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "http_api"
|
name = "http_api"
|
||||||
path = "fuzz_targets/http_api.rs"
|
path = "fuzz_targets/http_api.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "iommu"
|
name = "iommu"
|
||||||
path = "fuzz_targets/iommu.rs"
|
path = "fuzz_targets/iommu.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "linux_loader"
|
name = "linux_loader"
|
||||||
path = "fuzz_targets/linux_loader.rs"
|
path = "fuzz_targets/linux_loader.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "linux_loader_cmdline"
|
name = "linux_loader_cmdline"
|
||||||
path = "fuzz_targets/linux_loader_cmdline.rs"
|
path = "fuzz_targets/linux_loader_cmdline.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "mem"
|
name = "mem"
|
||||||
path = "fuzz_targets/mem.rs"
|
path = "fuzz_targets/mem.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "net"
|
name = "net"
|
||||||
path = "fuzz_targets/net.rs"
|
path = "fuzz_targets/net.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "pmem"
|
name = "pmem"
|
||||||
path = "fuzz_targets/pmem.rs"
|
path = "fuzz_targets/pmem.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "qcow"
|
name = "qcow"
|
||||||
path = "fuzz_targets/qcow.rs"
|
path = "fuzz_targets/qcow.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "rng"
|
name = "rng"
|
||||||
path = "fuzz_targets/rng.rs"
|
path = "fuzz_targets/rng.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "serial"
|
name = "serial"
|
||||||
path = "fuzz_targets/serial.rs"
|
path = "fuzz_targets/serial.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "vhdx"
|
name = "vhdx"
|
||||||
path = "fuzz_targets/vhdx.rs"
|
path = "fuzz_targets/vhdx.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
doc = false
|
||||||
name = "watchdog"
|
name = "watchdog"
|
||||||
path = "fuzz_targets/watchdog.rs"
|
path = "fuzz_targets/watchdog.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl RequestHandler for StubApiRequestHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn vm_coredump(&mut self, _: &str) -> Result<(), VmError> {
|
fn vm_coredump(&mut self, _: &str) -> Result<(), VmError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -180,17 +180,20 @@ impl RequestHandler for StubApiRequestHandler {
|
|||||||
vdpa: None,
|
vdpa: None,
|
||||||
vsock: None,
|
vsock: None,
|
||||||
pvpanic: false,
|
pvpanic: false,
|
||||||
|
#[cfg(feature = "pvmemcontrol")]
|
||||||
|
pvmemcontrol: None,
|
||||||
iommu: false,
|
iommu: false,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
sgx_epc: None,
|
sgx_epc: None,
|
||||||
numa: None,
|
numa: None,
|
||||||
watchdog: false,
|
watchdog: false,
|
||||||
#[cfg(feature = "guest_debug")]
|
|
||||||
gdb: false,
|
gdb: false,
|
||||||
pci_segments: None,
|
pci_segments: None,
|
||||||
platform: None,
|
platform: None,
|
||||||
tpm: None,
|
tpm: None,
|
||||||
preserved_fds: None,
|
preserved_fds: None,
|
||||||
|
landlock_enable: false,
|
||||||
|
landlock_rules: None,
|
||||||
})),
|
})),
|
||||||
state: VmState::Running,
|
state: VmState::Running,
|
||||||
memory_actual_size: 0,
|
memory_actual_size: 0,
|
||||||
|
|||||||
@@ -1,39 +1,47 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hypervisor"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Microsoft Authors"]
|
authors = ["Microsoft Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "Apache-2.0 OR BSD-3-Clause"
|
license = "Apache-2.0 OR BSD-3-Clause"
|
||||||
|
name = "hypervisor"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
kvm = ["kvm-ioctls", "kvm-bindings", "vfio-ioctls/kvm"]
|
kvm = ["kvm-bindings", "kvm-ioctls", "vfio-ioctls/kvm"]
|
||||||
mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv", "iced-x86"]
|
mshv = ["iced-x86", "mshv-bindings", "mshv-ioctls", "vfio-ioctls/mshv"]
|
||||||
sev_snp = ["igvm_parser", "igvm_defs"]
|
sev_snp = ["igvm", "igvm_defs"]
|
||||||
tdx = []
|
tdx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.86"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm_defs", optional = true }
|
igvm = { version = "0.3.3", optional = true }
|
||||||
igvm_parser = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm", optional = true }
|
igvm_defs = { version = "0.3.1", optional = true }
|
||||||
libc = "0.2.153"
|
|
||||||
log = "0.4.21"
|
|
||||||
kvm-bindings = { version = "0.8.1", optional = true, features = ["serde"] }
|
kvm-bindings = { version = "0.8.1", optional = true, features = ["serde"] }
|
||||||
kvm-ioctls = { version = "0.17.0", optional = true }
|
kvm-ioctls = { version = "0.17.0", optional = true }
|
||||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
|
libc = "0.2.155"
|
||||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
|
log = "0.4.22"
|
||||||
serde = { version = "1.0.197", features = ["rc", "derive"] }
|
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", tag = "v0.2.0", features = [
|
||||||
serde_with = { version = "3.7.0", default-features = false, features = ["macros"] }
|
"fam-wrappers",
|
||||||
|
"with-serde",
|
||||||
|
], optional = true }
|
||||||
|
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", tag = "v0.2.0", optional = true }
|
||||||
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
|
serde_with = { version = "3.9.0", default-features = false, features = [
|
||||||
|
"macros",
|
||||||
|
] }
|
||||||
|
thiserror = "1.0.62"
|
||||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||||
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic"] }
|
vm-memory = { version = "0.14.1", features = [
|
||||||
|
"backend-atomic",
|
||||||
|
"backend-mmap",
|
||||||
|
] }
|
||||||
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
|
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
|
||||||
thiserror = "1.0.58"
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||||
|
default-features = false
|
||||||
|
features = ["decoder", "fast_fmt", "instr_info", "op_code_info", "std"]
|
||||||
optional = true
|
optional = true
|
||||||
version = "1.21.0"
|
version = "1.21.0"
|
||||||
default-features = false
|
|
||||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
|
|||||||
@@ -5,15 +5,19 @@
|
|||||||
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
|
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Errors thrown while setting up the VGIC.
|
/// Errors thrown while setting up the VGIC.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||||
|
#[error("Failed creating GIC device: {0}")]
|
||||||
CreateGic(HypervisorVmError),
|
CreateGic(HypervisorVmError),
|
||||||
/// Error while setting device attributes for the GIC.
|
/// Error while setting device attributes for the GIC.
|
||||||
|
#[error("Failed setting device attributes for the GIC: {0}")]
|
||||||
SetDeviceAttribute(HypervisorDeviceError),
|
SetDeviceAttribute(HypervisorDeviceError),
|
||||||
/// Error while getting device attributes for the GIC.
|
/// Error while getting device attributes for the GIC.
|
||||||
|
#[error("Failed getting device attributes for the GIC: {0}")]
|
||||||
GetDeviceAttribute(HypervisorDeviceError),
|
GetDeviceAttribute(HypervisorDeviceError),
|
||||||
}
|
}
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|||||||
@@ -137,14 +137,6 @@ pub trait PlatformEmulator {
|
|||||||
///
|
///
|
||||||
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError>;
|
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.
|
/// Fetch instruction bytes from memory.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub mod cmp;
|
|||||||
pub mod mov;
|
pub mod mov;
|
||||||
pub mod movs;
|
pub mod movs;
|
||||||
pub mod or;
|
pub mod or;
|
||||||
|
pub mod stos;
|
||||||
|
|
||||||
fn get_op<T: CpuStateManager>(
|
fn get_op<T: CpuStateManager>(
|
||||||
insn: &Instruction,
|
insn: &Instruction,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ macro_rules! movs {
|
|||||||
let len = std::mem::size_of::<$bound>();
|
let len = std::mem::size_of::<$bound>();
|
||||||
|
|
||||||
while count > 0 {
|
while count > 0 {
|
||||||
let mut memory: [u8; 4] = [0; 4];
|
let mut memory: [u8; 8] = [0; 8];
|
||||||
|
|
||||||
let src = state
|
let src = state
|
||||||
.linearize(Register::DS, rsi, false)
|
.linearize(Register::DS, rsi, false)
|
||||||
@@ -83,6 +83,11 @@ macro_rules! movs {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Movsq_m64_m64;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Movsq_m64_m64 {
|
||||||
|
movs!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Movsd_m32_m32;
|
pub struct Movsd_m32_m32;
|
||||||
impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
|
impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
|
||||||
movs!(u32);
|
movs!(u32);
|
||||||
@@ -103,6 +108,40 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::arch::x86::emulator::mock_vmm::*;
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rep_movsq_m64_m64() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 32] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
|
||||||
|
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
|
||||||
|
0xcd, 0xcd, 0xcd, 0xcd, // 0xcdcdcdcd
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
];
|
||||||
|
let insn = [0xf3, 0x48, 0xa5]; // rep movsq
|
||||||
|
let regs = vec![
|
||||||
|
(Register::ECX, 2),
|
||||||
|
(Register::ESI, 0),
|
||||||
|
(Register::EDI, 0x10),
|
||||||
|
];
|
||||||
|
let mut data = [0u8; 8];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0x10, &mut data).unwrap();
|
||||||
|
assert_eq!(0xaabbccdd12345678, <u64>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x18, &mut data).unwrap();
|
||||||
|
assert_eq!(0xcdcdcdcd5aa55aa5, <u64>::from_le_bytes(data));
|
||||||
|
// The rest should be default value 0 from MockVmm
|
||||||
|
vmm.read_memory(0x20, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u64>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rep_movsd_m32_m32() {
|
fn test_rep_movsd_m32_m32() {
|
||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
|
|||||||
236
hypervisor/src/arch/x86/emulator/instructions/stos.rs
Normal file
236
hypervisor/src/arch/x86/emulator/instructions/stos.rs
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
//
|
||||||
|
// Copyright © 2024 Microsoft
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
//
|
||||||
|
// STOS - Store String
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
|
use crate::arch::x86::regs::DF;
|
||||||
|
|
||||||
|
macro_rules! stos {
|
||||||
|
($bound:ty) => {
|
||||||
|
fn emulate(
|
||||||
|
&self,
|
||||||
|
insn: &Instruction,
|
||||||
|
state: &mut T,
|
||||||
|
platform: &mut dyn PlatformEmulator<CpuState = T>,
|
||||||
|
) -> Result<(), EmulationError<Exception>> {
|
||||||
|
let mut count: u64 = if insn.has_rep_prefix() {
|
||||||
|
state
|
||||||
|
.read_reg(Register::ECX)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
let rax = state
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
|
||||||
|
let mut rdi = state
|
||||||
|
.read_reg(Register::RDI)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
|
||||||
|
let df = (state.flags() & DF) != 0;
|
||||||
|
let len = std::mem::size_of::<$bound>();
|
||||||
|
let rax_bytes = rax.to_le_bytes();
|
||||||
|
|
||||||
|
while count > 0 {
|
||||||
|
let dst = state
|
||||||
|
.linearize(Register::ES, rdi, true)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
|
||||||
|
platform
|
||||||
|
.write_memory(dst, &rax_bytes[0..len])
|
||||||
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
|
if df {
|
||||||
|
rdi = rdi.wrapping_sub(len as u64);
|
||||||
|
} else {
|
||||||
|
rdi = rdi.wrapping_add(len as u64);
|
||||||
|
}
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if insn.has_rep_prefix() {
|
||||||
|
state
|
||||||
|
.write_reg(Register::ECX, 0)
|
||||||
|
.map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stosq_m64_RAX;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Stosq_m64_RAX {
|
||||||
|
stos!(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stosd_m32_EAX;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Stosd_m32_EAX {
|
||||||
|
stos!(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stosw_m16_AX;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Stosw_m16_AX {
|
||||||
|
stos!(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stosb_m8_AL;
|
||||||
|
impl<T: CpuStateManager> InstructionHandler<T> for Stosb_m8_AL {
|
||||||
|
stos!(u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rep_stosb() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 12] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
|
||||||
|
0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
|
||||||
|
];
|
||||||
|
let insn = [0xf3, 0xaa]; // rep stosb
|
||||||
|
let regs = vec![
|
||||||
|
(Register::ECX, 3),
|
||||||
|
(Register::EDI, 0x0),
|
||||||
|
(Register::RAX, 0x123456ff),
|
||||||
|
];
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0, &mut data).unwrap();
|
||||||
|
assert_eq!(0x12ffffff, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(4, &mut data).unwrap();
|
||||||
|
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stosw() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 4] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
];
|
||||||
|
let insn = [0x66, 0xab]; // stosw
|
||||||
|
let regs = vec![(Register::EDI, 0x1), (Register::AX, 0xaabb)];
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0x0, &mut data).unwrap();
|
||||||
|
assert_eq!(0x12aabb78, <u32>::from_le_bytes(data));
|
||||||
|
// The rest should be default value 0 from MockVmm
|
||||||
|
vmm.read_memory(0x4, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x8 + 8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rep_stosw() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 8] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
];
|
||||||
|
let insn = [0x66, 0xf3, 0xab]; // rep stosw
|
||||||
|
let regs = vec![
|
||||||
|
(Register::ECX, 2),
|
||||||
|
(Register::EDI, 0x2),
|
||||||
|
(Register::AX, 0xaabb),
|
||||||
|
];
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0x0, &mut data).unwrap();
|
||||||
|
assert_eq!(0xaabb5678, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x4, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0000aabb, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rep_stosd() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 12] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
];
|
||||||
|
let insn = [0xf3, 0xab]; // rep stosd
|
||||||
|
let regs = vec![
|
||||||
|
(Register::ECX, 2),
|
||||||
|
(Register::EDI, 0x8),
|
||||||
|
(Register::EAX, 0xaabbccdd),
|
||||||
|
];
|
||||||
|
let mut data = [0u8; 4];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
// Go backwards this time
|
||||||
|
let mut state = vmm.cpu_state(0).unwrap();
|
||||||
|
state.set_flags(state.flags() | DF);
|
||||||
|
vmm.set_cpu_state(0, state).unwrap();
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0x0, &mut data).unwrap();
|
||||||
|
assert_eq!(0x12345678, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x4, &mut data).unwrap();
|
||||||
|
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x8, &mut data).unwrap();
|
||||||
|
assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0xc, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u32>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rep_stosq() {
|
||||||
|
let ip: u64 = 0x1000;
|
||||||
|
let memory: [u8; 8] = [
|
||||||
|
0x78, 0x56, 0x34, 0x12, // 0x12345678
|
||||||
|
0x00, 0x00, 0x00, 0x00, // 0x00000000
|
||||||
|
];
|
||||||
|
let insn = [0xf3, 0x48, 0xab]; // rep stosq
|
||||||
|
let regs = vec![
|
||||||
|
(Register::ECX, 2),
|
||||||
|
(Register::RDI, 0x0),
|
||||||
|
(Register::RAX, 0x11223344aabbccdd),
|
||||||
|
];
|
||||||
|
let mut data = [0u8; 8];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
|
||||||
|
|
||||||
|
assert!(vmm.emulate_first_insn(0, &insn).is_ok());
|
||||||
|
|
||||||
|
vmm.read_memory(0x0, &mut data).unwrap();
|
||||||
|
assert_eq!(0x11223344aabbccdd, <u64>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x8, &mut data).unwrap();
|
||||||
|
assert_eq!(0x11223344aabbccdd, <u64>::from_le_bytes(data));
|
||||||
|
vmm.read_memory(0x10, &mut data).unwrap();
|
||||||
|
assert_eq!(0x0, <u64>::from_le_bytes(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -135,7 +135,7 @@ pub trait CpuStateManager: Clone {
|
|||||||
.checked_add(segment_register.base)
|
.checked_add(segment_register.base)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
PlatformError::InvalidAddress(anyhow!(
|
PlatformError::InvalidAddress(anyhow!(
|
||||||
"Logical address {:#x} can not be linearized with segment {:#x?}",
|
"Logical address {:#x} cannot be linearized with segment {:#x?}",
|
||||||
logical_addr,
|
logical_addr,
|
||||||
segment_register
|
segment_register
|
||||||
))
|
))
|
||||||
@@ -148,7 +148,7 @@ pub trait CpuStateManager: Clone {
|
|||||||
// Must not write to a read-only segment.
|
// Must not write to a read-only segment.
|
||||||
if segment_type_ro(segment_type) && write {
|
if segment_type_ro(segment_type) && write {
|
||||||
return Err(PlatformError::InvalidAddress(anyhow!(
|
return Err(PlatformError::InvalidAddress(anyhow!(
|
||||||
"Can not write to a read-only segment"
|
"Cannot write to a read-only segment"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,11 +532,17 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
|||||||
(mov, Mov_moffs64_RAX),
|
(mov, Mov_moffs64_RAX),
|
||||||
(mov, Mov_RAX_moffs64),
|
(mov, Mov_RAX_moffs64),
|
||||||
// MOVS
|
// MOVS
|
||||||
|
(movs, Movsq_m64_m64),
|
||||||
(movs, Movsd_m32_m32),
|
(movs, Movsd_m32_m32),
|
||||||
(movs, Movsw_m16_m16),
|
(movs, Movsw_m16_m16),
|
||||||
(movs, Movsb_m8_m8),
|
(movs, Movsb_m8_m8),
|
||||||
// OR
|
// OR
|
||||||
(or, Or_rm8_r8)
|
(or, Or_rm8_r8),
|
||||||
|
// STOS
|
||||||
|
(stos, Stosb_m8_AL),
|
||||||
|
(stos, Stosw_m16_AX),
|
||||||
|
(stos, Stosd_m32_EAX),
|
||||||
|
(stos, Stosq_m64_RAX)
|
||||||
);
|
);
|
||||||
|
|
||||||
handler
|
handler
|
||||||
@@ -561,7 +567,7 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
|||||||
|
|
||||||
decoder.set_ip(state.ip());
|
decoder.set_ip(state.ip());
|
||||||
|
|
||||||
while decoder.can_decode() && !stop_emulation {
|
while !stop_emulation {
|
||||||
decoder.decode_out(&mut insn);
|
decoder.decode_out(&mut insn);
|
||||||
|
|
||||||
if decoder.last_error() == DecoderError::NoMoreBytes {
|
if decoder.last_error() == DecoderError::NoMoreBytes {
|
||||||
@@ -588,6 +594,7 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
|||||||
// Once we have the new stream, we must create a new decoder
|
// Once we have the new stream, we must create a new decoder
|
||||||
// and emulate one last instruction from the last decoded IP.
|
// and emulate one last instruction from the last decoded IP.
|
||||||
decoder = Decoder::new(64, &fetched_insn_stream, DecoderOptions::NONE);
|
decoder = Decoder::new(64, &fetched_insn_stream, DecoderOptions::NONE);
|
||||||
|
decoder.set_ip(last_decoded_ip);
|
||||||
decoder.decode_out(&mut insn);
|
decoder.decode_out(&mut insn);
|
||||||
if decoder.last_error() != DecoderError::None {
|
if decoder.last_error() != DecoderError::None {
|
||||||
return Err(EmulationError::InstructionFetchingError(anyhow!(
|
return Err(EmulationError::InstructionFetchingError(anyhow!(
|
||||||
@@ -595,8 +602,6 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
|
|||||||
insn_format!(insn)
|
insn_format!(insn)
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_emulation = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emulate the decoded instruction
|
// Emulate the decoded instruction
|
||||||
@@ -751,10 +756,6 @@ mod mock_vmm {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
|
|
||||||
Ok(gva)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
let rip = self
|
let rip = self
|
||||||
.state
|
.state
|
||||||
@@ -771,12 +772,77 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::arch::x86::emulator::mock_vmm::*;
|
use crate::arch::x86::emulator::mock_vmm::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Emulate executing an empty stream. Instructions should be fetched from
|
||||||
|
// memory.
|
||||||
|
//
|
||||||
|
// mov rax, 0x1000
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
fn test_empty_instruction_stream() {
|
||||||
|
let target_rax: u64 = 0x1000;
|
||||||
|
let target_rbx: 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 mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &[], Some(2)).is_ok());
|
||||||
|
|
||||||
|
let rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, target_rax);
|
||||||
|
|
||||||
|
let rbx: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RBX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rbx, target_rbx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Emulate executing an empty stream. Instructions should be fetched from
|
||||||
|
// memory. The emulation should abort.
|
||||||
|
//
|
||||||
|
// mov rax, 0x1000
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
// ... garbage ...
|
||||||
|
fn test_empty_instruction_stream_bad() {
|
||||||
|
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
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, // Garbage
|
||||||
|
// Data at IP + 0x10 (0x1234567812345678 in LE)
|
||||||
|
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &[], None).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Emulate truncated instruction stream, which should cause a fetch.
|
// Emulate truncated instruction stream, which should cause a fetch.
|
||||||
//
|
//
|
||||||
// mov rax, 0x1000
|
// mov rax, 0x1000
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
// Test with a first instruction truncated.
|
// Test with a first instruction truncated.
|
||||||
fn test_fetch_first_instruction() {
|
fn test_fetch_first_instruction() {
|
||||||
|
let target_rax: u64 = 0x1000;
|
||||||
let ip: u64 = 0x1000;
|
let ip: u64 = 0x1000;
|
||||||
let cpu_id = 0;
|
let cpu_id = 0;
|
||||||
let memory = [
|
let memory = [
|
||||||
@@ -801,7 +867,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.read_reg(Register::RAX)
|
.read_reg(Register::RAX)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(rax, ip);
|
assert_eq!(rax, target_rax);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -839,6 +905,52 @@ mod tests {
|
|||||||
assert_eq!(rbx, target_rax);
|
assert_eq!(rbx, target_rax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Emulate only one instruction.
|
||||||
|
//
|
||||||
|
// mov rax, 0x1000
|
||||||
|
// mov rbx, qword ptr [rax+10h]
|
||||||
|
// The emulation should stop after the first instruction.
|
||||||
|
fn test_emulate_one_instruction() {
|
||||||
|
let target_rax: u64 = 0x1000;
|
||||||
|
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, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut vmm = MockVmm::new(ip, vec![], Some((ip, &memory)));
|
||||||
|
assert!(vmm.emulate_insn(cpu_id, &insn, Some(1)).is_ok());
|
||||||
|
|
||||||
|
let new_ip: u64 = vmm.cpu_state(cpu_id).unwrap().ip();
|
||||||
|
assert_eq!(new_ip, ip + 0x7 /* length of mov rax,0x1000 */);
|
||||||
|
|
||||||
|
let rax: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RAX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rax, target_rax);
|
||||||
|
|
||||||
|
// The second instruction is not executed so RBX should be zero.
|
||||||
|
let rbx: u64 = vmm
|
||||||
|
.cpu_state(cpu_id)
|
||||||
|
.unwrap()
|
||||||
|
.read_reg(Register::RBX)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(rbx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Emulate truncated instruction stream, which should cause a fetch.
|
// Emulate truncated instruction stream, which should cause a fetch.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ pub enum Exception {
|
|||||||
pub mod regs;
|
pub mod regs;
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
|
||||||
pub struct SegmentRegister {
|
pub struct SegmentRegister {
|
||||||
pub base: u64,
|
pub base: u64,
|
||||||
pub limit: u32,
|
pub limit: u32,
|
||||||
@@ -174,7 +173,6 @@ macro_rules! msr_data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
|
||||||
pub struct StandardRegisters {
|
pub struct StandardRegisters {
|
||||||
pub rax: u64,
|
pub rax: u64,
|
||||||
pub rbx: u64,
|
pub rbx: u64,
|
||||||
@@ -197,14 +195,12 @@ pub struct StandardRegisters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
|
||||||
pub struct DescriptorTable {
|
pub struct DescriptorTable {
|
||||||
pub base: u64,
|
pub base: u64,
|
||||||
pub limit: u16,
|
pub limit: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "with-serde", derive(Deserialize, Serialize))]
|
|
||||||
pub struct SpecialRegisters {
|
pub struct SpecialRegisters {
|
||||||
pub cs: SegmentRegister,
|
pub cs: SegmentRegister,
|
||||||
pub ds: SegmentRegister,
|
pub ds: SegmentRegister,
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ pub trait Hypervisor: Send + Sync {
|
|||||||
CpuVendor::AMD
|
CpuVendor::AMD
|
||||||
} else {
|
} else {
|
||||||
// Not known yet, the corresponding manufacturer manual should contain the
|
// Not known yet, the corresponding manufacturer manual should contain the
|
||||||
// necesssary info. See also https://wiki.osdev.org/CPUID#CPU_Vendor_ID_String
|
// necessary info. See also https://wiki.osdev.org/CPUID#CPU_Vendor_ID_String
|
||||||
CpuVendor::default()
|
CpuVendor::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,46 +77,61 @@ static VGIC_DIST_REGS: &[DistReg] = &[
|
|||||||
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
|
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result<()> {
|
fn dist_attr_set(gic: &DeviceFd, offset: u32, val: u32) -> Result<()> {
|
||||||
|
let gic_dist_attr = kvm_device_attr {
|
||||||
|
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||||
|
attr: offset as u64,
|
||||||
|
addr: &val as *const u32 as u64,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
|
||||||
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dist_attr_get(gic: &DeviceFd, offset: u32) -> Result<u32> {
|
||||||
|
let mut val = 0;
|
||||||
|
|
||||||
let mut gic_dist_attr = kvm_device_attr {
|
let mut gic_dist_attr = kvm_device_attr {
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||||
attr: offset as u64,
|
attr: offset as u64,
|
||||||
addr: val as *const u32 as u64,
|
addr: &mut val as *mut u32 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
|
||||||
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
|
// get_device_attr should be marked as unsafe, and will be in future.
|
||||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
// SAFETY: gic_dist_attr.addr is safe to write to.
|
||||||
})?;
|
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
|
||||||
} else {
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
|
})?;
|
||||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
|
||||||
})?;
|
Ok(val)
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the distributor control register.
|
/// Get the distributor control register.
|
||||||
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
|
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
|
||||||
let val: u32 = 0;
|
dist_attr_get(gic, GICD_CTLR)
|
||||||
dist_attr_access(gic, GICD_CTLR, &val, false)?;
|
|
||||||
Ok(val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the distributor control register.
|
/// Set the distributor control register.
|
||||||
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
|
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
|
||||||
dist_attr_access(gic, GICD_CTLR, &val, true)
|
dist_attr_set(gic, GICD_CTLR, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
|
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
|
||||||
let num_irq = 0;
|
let mut num_irq = 0;
|
||||||
|
|
||||||
let mut nr_irqs_attr = kvm_device_attr {
|
let mut nr_irqs_attr = kvm_device_attr {
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
|
||||||
attr: 0,
|
attr: 0,
|
||||||
addr: &num_irq as *const u32 as u64,
|
addr: &mut num_irq as *mut u32 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
|
// get_device_attr should be marked as unsafe, and will be in future.
|
||||||
|
// SAFETY: nr_irqs_attr.addr is safe to write to.
|
||||||
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
|
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
|
||||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
})?;
|
})?;
|
||||||
@@ -158,8 +173,7 @@ pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
|
|||||||
let end = compute_reg_len(gic, dreg, base)?;
|
let end = compute_reg_len(gic, dreg, base)?;
|
||||||
|
|
||||||
while base < end {
|
while base < end {
|
||||||
let val = state[idx];
|
dist_attr_set(gic, base, state[idx])?;
|
||||||
dist_attr_access(gic, base, &val, true)?;
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
base += REG_SIZE as u32;
|
base += REG_SIZE as u32;
|
||||||
}
|
}
|
||||||
@@ -175,9 +189,7 @@ pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
|
|||||||
let end = compute_reg_len(gic, dreg, base)?;
|
let end = compute_reg_len(gic, dreg, base)?;
|
||||||
|
|
||||||
while base < end {
|
while base < end {
|
||||||
let val: u32 = 0;
|
state.push(dist_attr_get(gic, base)?);
|
||||||
dist_attr_access(gic, base, &val, false)?;
|
|
||||||
state.push(val);
|
|
||||||
base += REG_SIZE as u32;
|
base += REG_SIZE as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,23 +79,38 @@ static VGIC_ICC_REGS: &[u64] = &[
|
|||||||
SYS_ICC_AP1R3_EL1,
|
SYS_ICC_AP1R3_EL1,
|
||||||
];
|
];
|
||||||
|
|
||||||
fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool) -> Result<()> {
|
fn icc_attr_set(gic: &DeviceFd, offset: u64, typer: u64, val: u32) -> Result<()> {
|
||||||
|
let gic_icc_attr = kvm_device_attr {
|
||||||
|
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||||
|
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
|
||||||
|
addr: &val as *const u32 as u64,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
|
||||||
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn icc_attr_get(gic: &DeviceFd, offset: u64, typer: u64) -> Result<u32> {
|
||||||
|
let mut val = 0;
|
||||||
|
|
||||||
let mut gic_icc_attr = kvm_device_attr {
|
let mut gic_icc_attr = kvm_device_attr {
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||||
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
|
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
|
||||||
addr: val as *const u32 as u64,
|
addr: &mut val as *mut u32 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
|
||||||
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
|
// get_device_attr should be marked as unsafe, and will be in future.
|
||||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
// SAFETY: gic_icc_attr.addr is safe to write to.
|
||||||
})?;
|
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
|
||||||
} else {
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
|
})?;
|
||||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
|
||||||
})?;
|
Ok(val)
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get ICC registers.
|
/// Get ICC registers.
|
||||||
@@ -107,10 +122,9 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
|||||||
for ix in gicr_typer {
|
for ix in gicr_typer {
|
||||||
let i = *ix;
|
let i = *ix;
|
||||||
for icc_offset in VGIC_ICC_REGS {
|
for icc_offset in VGIC_ICC_REGS {
|
||||||
let val = 0;
|
|
||||||
if *icc_offset == SYS_ICC_CTLR_EL1 {
|
if *icc_offset == SYS_ICC_CTLR_EL1 {
|
||||||
// calculate priority bits by reading the ctrl_el1 register.
|
// calculate priority bits by reading the ctrl_el1 register.
|
||||||
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
let val = icc_attr_get(gic, *icc_offset, i)?;
|
||||||
// The priority bits are found in the ICC_CTLR_EL1 register (bits from 10:8).
|
// The priority bits are found in the ICC_CTLR_EL1 register (bits from 10:8).
|
||||||
// See page 194 from https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_
|
// See page 194 from https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_
|
||||||
// architecture_specification.pdf.
|
// architecture_specification.pdf.
|
||||||
@@ -130,8 +144,7 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
|||||||
// 7 bits of priority.
|
// 7 bits of priority.
|
||||||
else if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
|
else if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
|
||||||
if num_priority_bits >= 6 {
|
if num_priority_bits >= 6 {
|
||||||
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
state.push(icc_attr_get(gic, *icc_offset, i)?);
|
||||||
state.push(val);
|
|
||||||
}
|
}
|
||||||
} else if *icc_offset == SYS_ICC_AP0R2_EL1
|
} else if *icc_offset == SYS_ICC_AP0R2_EL1
|
||||||
|| *icc_offset == SYS_ICC_AP0R3_EL1
|
|| *icc_offset == SYS_ICC_AP0R3_EL1
|
||||||
@@ -139,12 +152,10 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
|||||||
|| *icc_offset == SYS_ICC_AP1R3_EL1
|
|| *icc_offset == SYS_ICC_AP1R3_EL1
|
||||||
{
|
{
|
||||||
if num_priority_bits == 7 {
|
if num_priority_bits == 7 {
|
||||||
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
state.push(icc_attr_get(gic, *icc_offset, i)?);
|
||||||
state.push(val);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
state.push(icc_attr_get(gic, *icc_offset, i)?);
|
||||||
state.push(val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +176,7 @@ pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result
|
|||||||
}
|
}
|
||||||
if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
|
if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
|
||||||
if num_priority_bits >= 6 {
|
if num_priority_bits >= 6 {
|
||||||
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
|
icc_attr_set(gic, *icc_offset, i, state[idx])?;
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -176,12 +187,12 @@ pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result
|
|||||||
|| *icc_offset == SYS_ICC_AP1R3_EL1
|
|| *icc_offset == SYS_ICC_AP1R3_EL1
|
||||||
{
|
{
|
||||||
if num_priority_bits == 7 {
|
if num_priority_bits == 7 {
|
||||||
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
|
icc_attr_set(gic, *icc_offset, i, state[idx])?;
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
|
icc_attr_set(gic, *icc_offset, i, state[idx])?;
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,34 +24,38 @@ const GITS_CWRITER: u32 = 0x0088;
|
|||||||
const GITS_CREADR: u32 = 0x0090;
|
const GITS_CREADR: u32 = 0x0090;
|
||||||
const GITS_BASER: u32 = 0x0100;
|
const GITS_BASER: u32 = 0x0100;
|
||||||
|
|
||||||
/// Access an ITS device attribute.
|
fn gicv3_its_attr_set(its_device: &DeviceFd, group: u32, attr: u32, val: u64) -> Result<()> {
|
||||||
///
|
let gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
||||||
/// This is a helper function to get/set the ITS device attribute depending
|
group,
|
||||||
/// the bool parameter `set` provided.
|
attr: attr as u64,
|
||||||
pub fn gicv3_its_attr_access(
|
addr: &val as *const u64 as u64,
|
||||||
its_device: &DeviceFd,
|
flags: 0,
|
||||||
group: u32,
|
};
|
||||||
attr: u32,
|
|
||||||
val: &u64,
|
its_device
|
||||||
set: bool,
|
.set_device_attr(&gicv3_its_attr)
|
||||||
) -> Result<()> {
|
.map_err(|e| Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gicv3_its_attr_get(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u64> {
|
||||||
|
let mut val = 0;
|
||||||
|
|
||||||
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
|
||||||
group,
|
group,
|
||||||
attr: attr as u64,
|
attr: attr as u64,
|
||||||
addr: val as *const u64 as u64,
|
addr: &mut val as *mut u64 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
|
||||||
its_device.set_device_attr(&gicv3_its_attr).map_err(|e| {
|
// get_device_attr should be marked as unsafe, and will be in future.
|
||||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
// SAFETY: gicv3_its_attr.addr is safe to write to.
|
||||||
})
|
its_device
|
||||||
} else {
|
.get_device_attr(&mut gicv3_its_attr)
|
||||||
its_device
|
.map_err(|e| {
|
||||||
.get_device_attr(&mut gicv3_its_attr)
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
.map_err(|e| {
|
})?;
|
||||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
|
||||||
})
|
Ok(val)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function that saves/restores ITS tables into guest RAM.
|
/// Function that saves/restores ITS tables into guest RAM.
|
||||||
@@ -324,60 +328,43 @@ impl Vgic for KvmGicV3Its {
|
|||||||
|
|
||||||
let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
|
let icc_state = get_icc_regs(&self.device, &gicr_typers)?;
|
||||||
|
|
||||||
let its_baser_state: [u64; 8] = [0; 8];
|
let mut its_baser_state: [u64; 8] = [0; 8];
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
gicv3_its_attr_access(
|
its_baser_state[i as usize] = gicv3_its_attr_get(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_BASER + i * 8,
|
GITS_BASER + i * 8,
|
||||||
&its_baser_state[i as usize],
|
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let its_ctlr_state: u64 = 0;
|
let its_ctlr_state = gicv3_its_attr_get(
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CTLR,
|
GITS_CTLR,
|
||||||
&its_ctlr_state,
|
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let its_cbaser_state: u64 = 0;
|
let its_cbaser_state = gicv3_its_attr_get(
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CBASER,
|
GITS_CBASER,
|
||||||
&its_cbaser_state,
|
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let its_creadr_state: u64 = 0;
|
let its_creadr_state = gicv3_its_attr_get(
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CREADR,
|
GITS_CREADR,
|
||||||
&its_creadr_state,
|
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let its_cwriter_state: u64 = 0;
|
let its_cwriter_state = gicv3_its_attr_get(
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CWRITER,
|
GITS_CWRITER,
|
||||||
&its_cwriter_state,
|
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let its_iidr_state: u64 = 0;
|
let its_iidr_state = gicv3_its_attr_get(
|
||||||
gicv3_its_attr_access(
|
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_IIDR,
|
GITS_IIDR,
|
||||||
&its_iidr_state,
|
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Gicv3ItsState {
|
Ok(Gicv3ItsState {
|
||||||
@@ -407,57 +394,51 @@ impl Vgic for KvmGicV3Its {
|
|||||||
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
|
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
|
||||||
|
|
||||||
//Restore GICv3ITS registers
|
//Restore GICv3ITS registers
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_set(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_IIDR,
|
GITS_IIDR,
|
||||||
&state.its_iidr,
|
state.its_iidr,
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_set(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CBASER,
|
GITS_CBASER,
|
||||||
&state.its_cbaser,
|
state.its_cbaser,
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_set(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CREADR,
|
GITS_CREADR,
|
||||||
&state.its_creadr,
|
state.its_creadr,
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_set(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CWRITER,
|
GITS_CWRITER,
|
||||||
&state.its_cwriter,
|
state.its_cwriter,
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_set(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_BASER + i * 8,
|
GITS_BASER + i * 8,
|
||||||
&state.its_baser[i as usize],
|
state.its_baser[i as usize],
|
||||||
true,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore ITS tables
|
// Restore ITS tables
|
||||||
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
|
gicv3_its_tables_access(self.its_device.as_ref().unwrap(), false)?;
|
||||||
|
|
||||||
gicv3_its_attr_access(
|
gicv3_its_attr_set(
|
||||||
self.its_device.as_ref().unwrap(),
|
self.its_device.as_ref().unwrap(),
|
||||||
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
|
||||||
GITS_CTLR,
|
GITS_CTLR,
|
||||||
&state.its_ctlr,
|
state.its_ctlr,
|
||||||
true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const KVM_ARM64_SYSREG_MPIDR_EL1: u64 = KVM_REG_ARM64
|
|||||||
| (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
| (((5_u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||||
|
|
||||||
/// This is how we represent the registers of a distributor.
|
/// This is how we represent the registers of a distributor.
|
||||||
/// It is relrvant their offset from the base address of the
|
/// It is relevant their offset from the base address of the
|
||||||
/// distributor.
|
/// distributor.
|
||||||
/// Each register has a different number
|
/// Each register has a different number
|
||||||
/// of bits_per_irq and is therefore variable length.
|
/// of bits_per_irq and is therefore variable length.
|
||||||
@@ -96,23 +96,38 @@ static VGIC_SGI_REGS: &[RdistReg] = &[
|
|||||||
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
|
VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn redist_attr_access(gic: &DeviceFd, offset: u32, typer: u64, val: &u32, set: bool) -> Result<()> {
|
fn redist_attr_set(gic: &DeviceFd, offset: u32, typer: u64, val: u32) -> Result<()> {
|
||||||
|
let gic_redist_attr = kvm_device_attr {
|
||||||
|
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
||||||
|
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
|
||||||
|
addr: &val as *const u32 as u64,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
|
||||||
|
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redist_attr_get(gic: &DeviceFd, offset: u32, typer: u64) -> Result<u32> {
|
||||||
|
let mut val = 0;
|
||||||
|
|
||||||
let mut gic_redist_attr = kvm_device_attr {
|
let mut gic_redist_attr = kvm_device_attr {
|
||||||
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS,
|
||||||
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
|
attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr
|
||||||
addr: val as *const u32 as u64,
|
addr: &mut val as *mut u32 as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
if set {
|
|
||||||
gic.set_device_attr(&gic_redist_attr).map_err(|e| {
|
// get_device_attr should be marked as unsafe, and will be in future.
|
||||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
// SAFETY: gic_redist_attr.addr is safe to write to.
|
||||||
})?;
|
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
|
||||||
} else {
|
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||||
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
|
})?;
|
||||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
|
||||||
})?;
|
Ok(val)
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn access_redists_aux(
|
fn access_redists_aux(
|
||||||
@@ -129,14 +144,11 @@ fn access_redists_aux(
|
|||||||
let end = base + rdreg.length as u32;
|
let end = base + rdreg.length as u32;
|
||||||
|
|
||||||
while base < end {
|
while base < end {
|
||||||
let mut val = 0;
|
|
||||||
if set {
|
if set {
|
||||||
val = state[*idx];
|
redist_attr_set(gic, base, *i, state[*idx])?;
|
||||||
redist_attr_access(gic, base, *i, &val, true)?;
|
|
||||||
*idx += 1;
|
*idx += 1;
|
||||||
} else {
|
} else {
|
||||||
redist_attr_access(gic, base, *i, &val, false)?;
|
state.push(redist_attr_get(gic, base, *i)?);
|
||||||
state.push(val);
|
|
||||||
}
|
}
|
||||||
base += REG_SIZE as u32;
|
base += REG_SIZE as u32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,12 +106,23 @@ pub fn is_system_register(regid: u64) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||||
if !kvm.check_extension(Cap::SignalMsi) {
|
macro_rules! check_extension {
|
||||||
return Err(KvmError::CapabilityMissing(Cap::SignalMsi));
|
($cap:expr) => {
|
||||||
}
|
if !kvm.check_extension($cap) {
|
||||||
if !kvm.check_extension(Cap::OneReg) {
|
return Err(KvmError::CapabilityMissing($cap));
|
||||||
return Err(KvmError::CapabilityMissing(Cap::OneReg));
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetGuestDebug is required but some kernels have it implemented without the capability flag.
|
||||||
|
check_extension!(Cap::ImmediateExit);
|
||||||
|
check_extension!(Cap::Ioeventfd);
|
||||||
|
check_extension!(Cap::Irqchip);
|
||||||
|
check_extension!(Cap::Irqfd);
|
||||||
|
check_extension!(Cap::IrqRouting);
|
||||||
|
check_extension!(Cap::MpState);
|
||||||
|
check_extension!(Cap::OneReg);
|
||||||
|
check_extension!(Cap::UserMemory);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -535,9 +535,9 @@ impl vm::Vm for KvmVm {
|
|||||||
|
|
||||||
if self.check_extension(crate::kvm::Cap::MsiDevid) {
|
if self.check_extension(crate::kvm::Cap::MsiDevid) {
|
||||||
// On AArch64, there is limitation on the range of the 'devid',
|
// On AArch64, there is limitation on the range of the 'devid',
|
||||||
// it can not be greater than 65536 (the max of u16).
|
// it cannot be greater than 65536 (the max of u16).
|
||||||
//
|
//
|
||||||
// BDF can not be used directly, because 'segment' is in high
|
// BDF cannot be used directly, because 'segment' is in high
|
||||||
// 16 bits. The layout of the u32 BDF is:
|
// 16 bits. The layout of the u32 BDF is:
|
||||||
// |---- 16 bits ----|-- 8 bits --|-- 5 bits --|-- 3 bits --|
|
// |---- 16 bits ----|-- 8 bits --|-- 5 bits --|-- 3 bits --|
|
||||||
// | segment | bus | device | function |
|
// | segment | bus | device | function |
|
||||||
@@ -1505,7 +1505,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// Set the floating point state (FPU) of a vCPU using the `KVM_SET_FPU` ioct.
|
/// Set the floating point state (FPU) of a vCPU using the `KVM_SET_FPU` ioctl.
|
||||||
///
|
///
|
||||||
fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> {
|
fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> {
|
||||||
let fpu: kvm_bindings::kvm_fpu = (*fpu).clone().into();
|
let fpu: kvm_bindings::kvm_fpu = (*fpu).clone().into();
|
||||||
@@ -1986,7 +1986,7 @@ impl cpu::Vcpu for KvmVcpu {
|
|||||||
/// SREGS saves/restores a pending interrupt, similar to what
|
/// SREGS saves/restores a pending interrupt, similar to what
|
||||||
/// VCPU_EVENTS also does.
|
/// VCPU_EVENTS also does.
|
||||||
///
|
///
|
||||||
/// GET_MSRS requires a pre-populated data structure to do something
|
/// GET_MSRS requires a prepopulated data structure to do something
|
||||||
/// meaningful. For SET_MSRS it will then contain good data.
|
/// meaningful. For SET_MSRS it will then contain good data.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
|||||||
@@ -32,29 +32,37 @@ pub use {
|
|||||||
/// Check KVM extension for Linux
|
/// Check KVM extension for Linux
|
||||||
///
|
///
|
||||||
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||||
if !kvm.check_extension(Cap::SignalMsi) {
|
macro_rules! check_extension {
|
||||||
return Err(KvmError::CapabilityMissing(Cap::SignalMsi));
|
($cap:expr) => {
|
||||||
}
|
if !kvm.check_extension($cap) {
|
||||||
if !kvm.check_extension(Cap::TscDeadlineTimer) {
|
return Err(KvmError::CapabilityMissing($cap));
|
||||||
return Err(KvmError::CapabilityMissing(Cap::TscDeadlineTimer));
|
}
|
||||||
}
|
};
|
||||||
if !kvm.check_extension(Cap::SplitIrqchip) {
|
|
||||||
return Err(KvmError::CapabilityMissing(Cap::SplitIrqchip));
|
|
||||||
}
|
|
||||||
if !kvm.check_extension(Cap::SetIdentityMapAddr) {
|
|
||||||
return Err(KvmError::CapabilityMissing(Cap::SetIdentityMapAddr));
|
|
||||||
}
|
|
||||||
if !kvm.check_extension(Cap::SetTssAddr) {
|
|
||||||
return Err(KvmError::CapabilityMissing(Cap::SetTssAddr));
|
|
||||||
}
|
|
||||||
if !kvm.check_extension(Cap::ImmediateExit) {
|
|
||||||
return Err(KvmError::CapabilityMissing(Cap::ImmediateExit));
|
|
||||||
}
|
|
||||||
if !kvm.check_extension(Cap::GetTscKhz) {
|
|
||||||
return Err(KvmError::CapabilityMissing(Cap::GetTscKhz));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeviceCtrl, EnableCap, and SetGuestDebug are also required, but some kernels have
|
||||||
|
// the features implemented without the capability flags.
|
||||||
|
check_extension!(Cap::AdjustClock);
|
||||||
|
check_extension!(Cap::ExtCpuid);
|
||||||
|
check_extension!(Cap::GetTscKhz);
|
||||||
|
check_extension!(Cap::ImmediateExit);
|
||||||
|
check_extension!(Cap::Ioeventfd);
|
||||||
|
check_extension!(Cap::Irqchip);
|
||||||
|
check_extension!(Cap::Irqfd);
|
||||||
|
check_extension!(Cap::IrqRouting);
|
||||||
|
check_extension!(Cap::MpState);
|
||||||
|
check_extension!(Cap::SetIdentityMapAddr);
|
||||||
|
check_extension!(Cap::SetTssAddr);
|
||||||
|
check_extension!(Cap::SplitIrqchip);
|
||||||
|
check_extension!(Cap::TscDeadlineTimer);
|
||||||
|
check_extension!(Cap::UserMemory);
|
||||||
|
check_extension!(Cap::UserNmi);
|
||||||
|
check_extension!(Cap::VcpuEvents);
|
||||||
|
check_extension!(Cap::Xcrs);
|
||||||
|
check_extension!(Cap::Xsave);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct VcpuKvmState {
|
pub struct VcpuKvmState {
|
||||||
pub cpuid: Vec<CpuIdEntry>,
|
pub cpuid: Vec<CpuIdEntry>,
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ pub enum ClockData {
|
|||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
Kvm(kvm_bindings::kvm_clock_data),
|
Kvm(kvm_bindings::kvm_clock_data),
|
||||||
#[cfg(feature = "mshv")]
|
#[cfg(feature = "mshv")]
|
||||||
Mshv, /* MSHV does not support ClockData yet */
|
Mshv(mshv::MshvClockData),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
// Copyright © 2020, Microsoft Corporation
|
// Copyright © 2020, Microsoft Corporation
|
||||||
//
|
//
|
||||||
|
|
||||||
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
use crate::arch::emulator::PlatformEmulator;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
use crate::arch::x86::emulator::Emulator;
|
||||||
use crate::cpu;
|
use crate::cpu;
|
||||||
use crate::cpu::Vcpu;
|
|
||||||
use crate::hypervisor;
|
use crate::hypervisor;
|
||||||
|
use crate::mshv::emulator::MshvEmulatorContext;
|
||||||
use crate::vec_with_array_field;
|
use crate::vec_with_array_field;
|
||||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||||
use crate::HypervisorType;
|
use crate::HypervisorType;
|
||||||
@@ -20,18 +19,15 @@ use std::collections::HashMap;
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use vfio_ioctls::VfioDeviceFd;
|
use vfio_ioctls::VfioDeviceFd;
|
||||||
use vm::DataMatch;
|
use vm::DataMatch;
|
||||||
|
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(feature = "sev_snp")]
|
||||||
mod snp_constants;
|
mod snp_constants;
|
||||||
// x86_64 dependencies
|
// x86_64 dependencies
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use snp_constants::*;
|
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::ClockData;
|
use crate::ClockData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
||||||
USER_MEMORY_REGION_ADJUSTABLE, USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ,
|
USER_MEMORY_REGION_ADJUSTABLE, USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ,
|
||||||
@@ -39,18 +35,16 @@ use crate::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(feature = "sev_snp")]
|
||||||
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
#[cfg(feature = "sev_snp")]
|
||||||
#[cfg(target_arch = "x86_64")]
|
use snp_constants::*;
|
||||||
pub use x86_64::VcpuMshvState;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub use x86_64::*;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
|
pub use x86_64::*;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub use x86_64::{emulator, VcpuMshvState};
|
||||||
|
|
||||||
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
||||||
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||||
@@ -60,7 +54,7 @@ const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
|||||||
///
|
///
|
||||||
pub use {
|
pub use {
|
||||||
mshv_bindings::mshv_create_device as CreateDevice,
|
mshv_bindings::mshv_create_device as CreateDevice,
|
||||||
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls::DeviceFd,
|
mshv_bindings::mshv_device_attr as DeviceAttr, mshv_ioctls, mshv_ioctls::DeviceFd,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PAGE_SHIFT: usize = 12;
|
pub const PAGE_SHIFT: usize = 12;
|
||||||
@@ -92,6 +86,25 @@ impl From<mshv_user_mem_region> for UserMemoryRegion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
impl From<MshvClockData> for ClockData {
|
||||||
|
fn from(d: MshvClockData) -> Self {
|
||||||
|
ClockData::Mshv(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
impl From<ClockData> for MshvClockData {
|
||||||
|
fn from(ms: ClockData) -> Self {
|
||||||
|
match ms {
|
||||||
|
ClockData::Mshv(s) => s,
|
||||||
|
/* Needed in case other hypervisors are enabled */
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => unreachable!("MSHV clock data is not valid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<UserMemoryRegion> for mshv_user_mem_region {
|
impl From<UserMemoryRegion> for mshv_user_mem_region {
|
||||||
fn from(region: UserMemoryRegion) -> Self {
|
fn from(region: UserMemoryRegion) -> Self {
|
||||||
let mut flags: u32 = 0;
|
let mut flags: u32 = 0;
|
||||||
@@ -254,6 +267,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set additional partition property for SEV-SNP partition.
|
// Set additional partition property for SEV-SNP partition.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
if mshv_vm_type == VmType::Snp {
|
if mshv_vm_type == VmType::Snp {
|
||||||
let snp_policy = snp::get_default_snp_guest_policy();
|
let snp_policy = snp::get_default_snp_guest_policy();
|
||||||
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
|
let vmgexit_offloads = snp::get_default_vmgexit_offload_features();
|
||||||
@@ -285,12 +299,20 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
// to override this behavior with a more suitable option i.e., ignore
|
// to override this behavior with a more suitable option i.e., ignore
|
||||||
// writes from the guest and return zero in attempt to read unimplemented
|
// writes from the guest and return zero in attempt to read unimplemented
|
||||||
// MSR.
|
// MSR.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
fd.set_partition_property(
|
fd.set_partition_property(
|
||||||
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
|
hv_partition_property_code_HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION,
|
||||||
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64,
|
hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO as u64,
|
||||||
)
|
)
|
||||||
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||||
|
|
||||||
|
// Always create a frozen partition
|
||||||
|
fd.set_partition_property(
|
||||||
|
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
|
||||||
|
1u64,
|
||||||
|
)
|
||||||
|
.map_err(|e| hypervisor::HypervisorError::SetPartitionProperty(e.into()))?;
|
||||||
|
|
||||||
let vm_fd = Arc::new(fd);
|
let vm_fd = Arc::new(fd);
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -346,7 +368,16 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
/// Get the supported CpuID
|
/// Get the supported CpuID
|
||||||
///
|
///
|
||||||
fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||||
Ok(Vec::new())
|
let mut cpuid = Vec::new();
|
||||||
|
let functions: [u32; 2] = [0x1, 0xb];
|
||||||
|
|
||||||
|
for function in functions {
|
||||||
|
cpuid.push(CpuIdEntry {
|
||||||
|
function,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(cpuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get maximum number of vCPUs
|
/// Get maximum number of vCPUs
|
||||||
@@ -600,14 +631,18 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
hv_message_type_HVMSG_UNMAPPED_GPA => {
|
msg_type @ (hv_message_type_HVMSG_UNMAPPED_GPA
|
||||||
|
| hv_message_type_HVMSG_GPA_INTERCEPT) => {
|
||||||
let info = x.to_memory_info().unwrap();
|
let info = x.to_memory_info().unwrap();
|
||||||
let insn_len = info.instruction_byte_count as usize;
|
let insn_len = info.instruction_byte_count as usize;
|
||||||
assert!(insn_len > 0 && insn_len <= 16);
|
let gva = info.guest_virtual_address;
|
||||||
|
let gpa = info.guest_physical_address;
|
||||||
|
|
||||||
|
debug!("Exit ({:?}) GVA {:x} GPA {:x}", msg_type, gva, gpa);
|
||||||
|
|
||||||
let mut context = MshvEmulatorContext {
|
let mut context = MshvEmulatorContext {
|
||||||
vcpu: self,
|
vcpu: self,
|
||||||
map: (info.guest_virtual_address, info.guest_physical_address),
|
map: (gva, gpa),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new emulator.
|
// Create a new emulator.
|
||||||
@@ -615,7 +650,10 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
|
|
||||||
// Emulate the trapped instruction, and only the first one.
|
// Emulate the trapped instruction, and only the first one.
|
||||||
let new_state = emul
|
let new_state = emul
|
||||||
.emulate_first_insn(self.vp_index as usize, &info.instruction_bytes)
|
.emulate_first_insn(
|
||||||
|
self.vp_index as usize,
|
||||||
|
&info.instruction_bytes[..insn_len],
|
||||||
|
)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||||
|
|
||||||
// Set CPU state back.
|
// Set CPU state back.
|
||||||
@@ -678,6 +716,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
)))?;
|
)))?;
|
||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
hv_message_type_HVMSG_UNACCEPTED_GPA => {
|
hv_message_type_HVMSG_UNACCEPTED_GPA => {
|
||||||
let info = x.to_memory_info().unwrap();
|
let info = x.to_memory_info().unwrap();
|
||||||
let gva = info.guest_virtual_address;
|
let gva = info.guest_virtual_address;
|
||||||
@@ -689,11 +728,13 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
gva,
|
gva,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
hv_message_type_HVMSG_X64_CPUID_INTERCEPT => {
|
hv_message_type_HVMSG_X64_CPUID_INTERCEPT => {
|
||||||
let info = x.to_cpuid_info().unwrap();
|
let info = x.to_cpuid_info().unwrap();
|
||||||
debug!("cpuid eax: {:x}", { info.rax });
|
debug!("cpuid eax: {:x}", { info.rax });
|
||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
hv_message_type_HVMSG_X64_MSR_INTERCEPT => {
|
hv_message_type_HVMSG_X64_MSR_INTERCEPT => {
|
||||||
let info = x.to_msr_info().unwrap();
|
let info = x.to_msr_info().unwrap();
|
||||||
if info.header.intercept_access_type == 0 {
|
if info.header.intercept_access_type == 0 {
|
||||||
@@ -703,6 +744,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
Ok(cpu::VmExit::Ignore)
|
Ok(cpu::VmExit::Ignore)
|
||||||
}
|
}
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT => {
|
hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT => {
|
||||||
//TODO: Handler for VMCALL here.
|
//TODO: Handler for VMCALL here.
|
||||||
let info = x.to_exception_info().unwrap();
|
let info = x.to_exception_info().unwrap();
|
||||||
@@ -757,6 +799,10 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
GHCB_INFO_REGISTER_REQUEST => {
|
GHCB_INFO_REGISTER_REQUEST => {
|
||||||
let mut ghcb_gpa = hv_x64_register_sev_ghcb::default();
|
let mut ghcb_gpa = hv_x64_register_sev_ghcb::default();
|
||||||
|
|
||||||
|
// Disable the previously used GHCB page.
|
||||||
|
self.disable_prev_ghcb_page()?;
|
||||||
|
|
||||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||||
unsafe {
|
unsafe {
|
||||||
ghcb_gpa.__bindgen_anon_1.set_enabled(1);
|
ghcb_gpa.__bindgen_anon_1.set_enabled(1);
|
||||||
@@ -784,6 +830,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
resp_ghcb_msr.__bindgen_anon_2.set_gpa_page_number(
|
resp_ghcb_msr.__bindgen_anon_2.set_gpa_page_number(
|
||||||
ghcb_msr.__bindgen_anon_2.gpa_page_number(),
|
ghcb_msr.__bindgen_anon_2.gpa_page_number(),
|
||||||
);
|
);
|
||||||
|
debug!("GHCB GPA is {:x}", ghcb_gpa.as_uint64);
|
||||||
}
|
}
|
||||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||||
let reg_name_value = unsafe {
|
let reg_name_value = unsafe {
|
||||||
@@ -884,15 +931,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
let mut swei1_rw_gpa_arg =
|
self.clear_swexit_info1(ghcb_gpa)?;
|
||||||
mshv_bindings::mshv_read_write_gpa {
|
|
||||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
|
|
||||||
byte_count: std::mem::size_of::<u64>() as u32,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
self.fd.gpa_write(&mut swei1_rw_gpa_arg).map_err(
|
|
||||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
SVM_NAE_HV_DOORBELL_PAGE_QUERY => {
|
SVM_NAE_HV_DOORBELL_PAGE_QUERY => {
|
||||||
let mut reg_assocs = [ hv_register_assoc {
|
let mut reg_assocs = [ hv_register_assoc {
|
||||||
@@ -914,6 +953,9 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|
self.fd.gpa_write(&mut swei2_rw_gpa_arg).map_err(
|
||||||
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
|e| cpu::HypervisorCpuError::GpaWrite(e.into()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
|
self.clear_swexit_info1(ghcb_gpa)?;
|
||||||
}
|
}
|
||||||
SVM_NAE_HV_DOORBELL_PAGE_CLEAR => {
|
SVM_NAE_HV_DOORBELL_PAGE_CLEAR => {
|
||||||
let mut swei2_rw_gpa_arg =
|
let mut swei2_rw_gpa_arg =
|
||||||
@@ -1014,14 +1056,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
self.clear_swexit_info1(ghcb_gpa)?;
|
||||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
|
|
||||||
byte_count: std::mem::size_of::<u64>() as u32,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
self.fd
|
|
||||||
.gpa_write(&mut swei1_rw_gpa_arg)
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
|
||||||
}
|
}
|
||||||
SVM_EXITCODE_MMIO_READ => {
|
SVM_EXITCODE_MMIO_READ => {
|
||||||
let src_gpa =
|
let src_gpa =
|
||||||
@@ -1050,6 +1085,9 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
self.fd
|
self.fd
|
||||||
.gpa_write(&mut arg)
|
.gpa_write(&mut arg)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||||
|
|
||||||
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
|
self.clear_swexit_info1(ghcb_gpa)?;
|
||||||
}
|
}
|
||||||
SVM_EXITCODE_MMIO_WRITE => {
|
SVM_EXITCODE_MMIO_WRITE => {
|
||||||
let dst_gpa =
|
let dst_gpa =
|
||||||
@@ -1078,6 +1116,9 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
cpu::HypervisorCpuError::RunVcpu(e.into())
|
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
|
self.clear_swexit_info1(ghcb_gpa)?;
|
||||||
}
|
}
|
||||||
SVM_EXITCODE_SNP_GUEST_REQUEST => {
|
SVM_EXITCODE_SNP_GUEST_REQUEST => {
|
||||||
let req_gpa =
|
let req_gpa =
|
||||||
@@ -1123,15 +1164,8 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
.sev_snp_ap_create(&mshv_ap_create_req)
|
.sev_snp_ap_create(&mshv_ap_create_req)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||||
|
|
||||||
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
|
self.clear_swexit_info1(ghcb_gpa)?;
|
||||||
byte_count: std::mem::size_of::<u64>() as u32,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.fd
|
|
||||||
.gpa_write(&mut swei1_rw_gpa_arg)
|
|
||||||
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
|
||||||
}
|
}
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
|
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
|
||||||
@@ -1288,7 +1322,7 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
self.set_fpu(&state.fpu)?;
|
self.set_fpu(&state.fpu)?;
|
||||||
self.set_xcrs(&state.xcrs)?;
|
self.set_xcrs(&state.xcrs)?;
|
||||||
// These registers are global and needed to be set only for first VCPU
|
// These registers are global and needed to be set only for first VCPU
|
||||||
// as Microsoft Hypervisor allows setting this regsier for only one VCPU
|
// as Microsoft Hypervisor allows setting this register for only one VCPU
|
||||||
if self.vp_index == 0 {
|
if self.vp_index == 0 {
|
||||||
self.fd
|
self.fd
|
||||||
.set_misc_regs(&state.misc)
|
.set_misc_regs(&state.misc)
|
||||||
@@ -1427,6 +1461,42 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MshvVcpu {
|
impl MshvVcpu {
|
||||||
|
///
|
||||||
|
/// Deactivate previously used GHCB page.
|
||||||
|
///
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
|
fn disable_prev_ghcb_page(&self) -> cpu::Result<()> {
|
||||||
|
let mut reg_assocs = [hv_register_assoc {
|
||||||
|
name: hv_register_name_HV_X64_REGISTER_SEV_GHCB_GPA,
|
||||||
|
..Default::default()
|
||||||
|
}];
|
||||||
|
self.fd.get_reg(&mut reg_assocs).unwrap();
|
||||||
|
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||||
|
let prev_ghcb_gpa = unsafe { reg_assocs[0].value.reg64 };
|
||||||
|
|
||||||
|
debug!("Prev GHCB GPA is {:x}", prev_ghcb_gpa);
|
||||||
|
|
||||||
|
let mut ghcb_gpa = hv_x64_register_sev_ghcb::default();
|
||||||
|
|
||||||
|
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||||
|
unsafe {
|
||||||
|
ghcb_gpa.__bindgen_anon_1.set_enabled(0);
|
||||||
|
ghcb_gpa.__bindgen_anon_1.set_page_number(prev_ghcb_gpa);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||||
|
let reg_name_value = unsafe {
|
||||||
|
[(
|
||||||
|
hv_register_name_HV_X64_REGISTER_SEV_GHCB_GPA,
|
||||||
|
ghcb_gpa.as_uint64,
|
||||||
|
)]
|
||||||
|
};
|
||||||
|
|
||||||
|
set_registers_64!(self.fd, reg_name_value)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
///
|
///
|
||||||
/// X86 specific call that returns the vcpu's current "xcrs".
|
/// X86 specific call that returns the vcpu's current "xcrs".
|
||||||
@@ -1468,133 +1538,26 @@ impl MshvVcpu {
|
|||||||
.set_vcpu_events(events)
|
.set_vcpu_events(events)
|
||||||
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
|
.map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
///
|
||||||
struct MshvEmulatorContext<'a> {
|
/// Clear SW_EXIT_INFO1 register for SEV-SNP guests.
|
||||||
vcpu: &'a MshvVcpu,
|
///
|
||||||
map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
|
#[cfg(feature = "sev_snp")]
|
||||||
}
|
fn clear_swexit_info1(
|
||||||
|
&self,
|
||||||
|
ghcb_gpa: u64,
|
||||||
|
) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
||||||
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
|
let mut swei1_rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
||||||
|
base_gpa: ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET,
|
||||||
|
byte_count: std::mem::size_of::<u64>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
self.fd
|
||||||
|
.gpa_write(&mut swei1_rw_gpa_arg)
|
||||||
|
.map_err(|e| cpu::HypervisorCpuError::GpaWrite(e.into()))?;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
Ok(cpu::VmExit::Ignore)
|
||||||
impl<'a> MshvEmulatorContext<'a> {
|
|
||||||
// Do the actual gva -> gpa translation
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
fn translate(&self, gva: u64) -> Result<u64, PlatformError> {
|
|
||||||
if self.map.0 == gva {
|
|
||||||
return Ok(self.map.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: More fine-grained control for the flags
|
|
||||||
let flags = HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_WRITE;
|
|
||||||
|
|
||||||
let (gpa, result_code) = self
|
|
||||||
.vcpu
|
|
||||||
.translate_gva(gva, flags.into())
|
|
||||||
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
|
||||||
|
|
||||||
match result_code {
|
|
||||||
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
|
|
||||||
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
/// Platform emulation for Hyper-V
|
|
||||||
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|
||||||
type CpuState = EmulatorCpuState;
|
|
||||||
|
|
||||||
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
|
||||||
let gpa = self.translate(gva)?;
|
|
||||||
debug!(
|
|
||||||
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
|
||||||
data.len(),
|
|
||||||
gva,
|
|
||||||
gpa
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
|
||||||
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
|
||||||
vm_ops
|
|
||||||
.mmio_read(gpa, data)
|
|
||||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
|
||||||
let gpa = self.translate(gva)?;
|
|
||||||
debug!(
|
|
||||||
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
|
||||||
data.len(),
|
|
||||||
gva,
|
|
||||||
gpa
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
|
||||||
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
|
||||||
vm_ops
|
|
||||||
.mmio_write(gpa, data)
|
|
||||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
|
|
||||||
if cpu_id != self.vcpu.vp_index as usize {
|
|
||||||
return Err(PlatformError::GetCpuStateFailure(anyhow!(
|
|
||||||
"CPU id mismatch {:?} {:?}",
|
|
||||||
cpu_id,
|
|
||||||
self.vcpu.vp_index
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let regs = self
|
|
||||||
.vcpu
|
|
||||||
.get_regs()
|
|
||||||
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
|
||||||
let sregs = self
|
|
||||||
.vcpu
|
|
||||||
.get_sregs()
|
|
||||||
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
|
||||||
|
|
||||||
debug!("mshv emulator: Getting new CPU state");
|
|
||||||
debug!("mshv emulator: {:#x?}", regs);
|
|
||||||
|
|
||||||
Ok(EmulatorCpuState { regs, sregs })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
|
|
||||||
if cpu_id != self.vcpu.vp_index as usize {
|
|
||||||
return Err(PlatformError::SetCpuStateFailure(anyhow!(
|
|
||||||
"CPU id mismatch {:?} {:?}",
|
|
||||||
cpu_id,
|
|
||||||
self.vcpu.vp_index
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("mshv emulator: Setting new CPU state");
|
|
||||||
debug!("mshv emulator: {:#x?}", state.regs);
|
|
||||||
|
|
||||||
self.vcpu
|
|
||||||
.set_regs(&state.regs)
|
|
||||||
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
|
|
||||||
self.vcpu
|
|
||||||
.set_sregs(&state.sregs)
|
|
||||||
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gva_to_gpa(&self, gva: u64) -> Result<u64, PlatformError> {
|
|
||||||
self.translate(gva)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch(&self, _ip: u64, _instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
|
||||||
Err(PlatformError::MemoryReadFailure(anyhow!("unimplemented")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1930,13 +1893,23 @@ impl vm::Vm for MshvVm {
|
|||||||
/// Retrieve guest clock.
|
/// Retrieve guest clock.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn get_clock(&self) -> vm::Result<ClockData> {
|
fn get_clock(&self) -> vm::Result<ClockData> {
|
||||||
Ok(ClockData::Mshv)
|
let val = self
|
||||||
|
.fd
|
||||||
|
.get_partition_property(hv_partition_property_code_HV_PARTITION_PROPERTY_REFERENCE_TIME)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::GetClock(e.into()))?;
|
||||||
|
Ok(MshvClockData { ref_time: val }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set guest clock.
|
/// Set guest clock.
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
fn set_clock(&self, _data: &ClockData) -> vm::Result<()> {
|
fn set_clock(&self, data: &ClockData) -> vm::Result<()> {
|
||||||
Ok(())
|
let data: MshvClockData = (*data).into();
|
||||||
|
self.fd
|
||||||
|
.set_partition_property(
|
||||||
|
hv_partition_property_code_HV_PARTITION_PROPERTY_REFERENCE_TIME,
|
||||||
|
data.ref_time,
|
||||||
|
)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::SetClock(e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Downcast to the underlying MshvVm type
|
/// Downcast to the underlying MshvVm type
|
||||||
@@ -2047,4 +2020,70 @@ impl vm::Vm for MshvVm {
|
|||||||
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
|
fn get_preferred_target(&self, kvi: &mut VcpuInit) -> vm::Result<()> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pause the VM
|
||||||
|
fn pause(&self) -> vm::Result<()> {
|
||||||
|
// Freeze the partition
|
||||||
|
self.fd
|
||||||
|
.set_partition_property(
|
||||||
|
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
|
||||||
|
1u64,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
vm::HypervisorVmError::SetVmProperty(anyhow!(
|
||||||
|
"Failed to set partition property: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resume the VM
|
||||||
|
fn resume(&self) -> vm::Result<()> {
|
||||||
|
// Resuming the partition using TIME_FREEZE property
|
||||||
|
self.fd
|
||||||
|
.set_partition_property(
|
||||||
|
hv_partition_property_code_HV_PARTITION_PROPERTY_TIME_FREEZE,
|
||||||
|
0u64,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
vm::HypervisorVmError::SetVmProperty(anyhow!(
|
||||||
|
"Failed to set partition property: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
|
fn gain_page_access(&self, gpa: u64, size: u32) -> vm::Result<()> {
|
||||||
|
if !self.sev_snp_enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_gpfn: u64 = gpa >> PAGE_SHIFT;
|
||||||
|
let end_gpfn: u64 = (gpa + size as u64 - 1) >> PAGE_SHIFT;
|
||||||
|
|
||||||
|
let gpas: Vec<u64> = (start_gpfn..=end_gpfn).map(|x| x << PAGE_SHIFT).collect();
|
||||||
|
|
||||||
|
if !gpas.is_empty() {
|
||||||
|
let mut gpa_list = vec_with_array_field::<mshv_modify_gpa_host_access, u64>(gpas.len());
|
||||||
|
gpa_list[0].gpa_list_size = gpas.len() as u64;
|
||||||
|
gpa_list[0].host_access = HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE;
|
||||||
|
gpa_list[0].acquire = 1;
|
||||||
|
gpa_list[0].flags = 0;
|
||||||
|
|
||||||
|
// SAFETY: gpa_list initialized with gpas.len() and now it is being turned into
|
||||||
|
// gpas_slice with gpas.len() again. It is guaranteed to be large enough to hold
|
||||||
|
// everything from gpas.
|
||||||
|
unsafe {
|
||||||
|
let gpas_slice: &mut [u64] = gpa_list[0].gpa_list.as_mut_slice(gpas.len());
|
||||||
|
gpas_slice.copy_from_slice(gpas.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fd
|
||||||
|
.modify_gpa_host_access(&gpa_list[0])
|
||||||
|
.map_err(|e| vm::HypervisorVmError::ModifyGpaHostAccess(e.into()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
145
hypervisor/src/mshv/x86_64/emulator.rs
Normal file
145
hypervisor/src/mshv/x86_64/emulator.rs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||||
|
//
|
||||||
|
// Copyright © 2024, Microsoft Corporation
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
||||||
|
use crate::arch::x86::emulator::{CpuStateManager, EmulatorCpuState};
|
||||||
|
use crate::cpu::Vcpu;
|
||||||
|
use crate::mshv::MshvVcpu;
|
||||||
|
use iced_x86::Register;
|
||||||
|
use mshv_bindings::*;
|
||||||
|
|
||||||
|
pub struct MshvEmulatorContext<'a> {
|
||||||
|
pub vcpu: &'a MshvVcpu,
|
||||||
|
pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MshvEmulatorContext<'a> {
|
||||||
|
// Do the actual gva -> gpa translation
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
fn translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError> {
|
||||||
|
if self.map.0 == gva {
|
||||||
|
return Ok(self.map.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (gpa, result_code) = self
|
||||||
|
.vcpu
|
||||||
|
.translate_gva(gva, flags.into())
|
||||||
|
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
||||||
|
|
||||||
|
match result_code {
|
||||||
|
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
|
||||||
|
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_memory_flags(
|
||||||
|
&self,
|
||||||
|
gva: u64,
|
||||||
|
data: &mut [u8],
|
||||||
|
flags: u32,
|
||||||
|
) -> Result<(), PlatformError> {
|
||||||
|
let gpa = self.translate(gva, flags)?;
|
||||||
|
debug!(
|
||||||
|
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
||||||
|
data.len(),
|
||||||
|
gva,
|
||||||
|
gpa
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||||
|
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
||||||
|
vm_ops
|
||||||
|
.mmio_read(gpa, data)
|
||||||
|
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Platform emulation for Hyper-V
|
||||||
|
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||||
|
type CpuState = EmulatorCpuState;
|
||||||
|
|
||||||
|
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||||
|
let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
|
||||||
|
debug!(
|
||||||
|
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
||||||
|
data.len(),
|
||||||
|
gva,
|
||||||
|
gpa
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||||
|
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
||||||
|
vm_ops
|
||||||
|
.mmio_write(gpa, data)
|
||||||
|
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
|
||||||
|
if cpu_id != self.vcpu.vp_index as usize {
|
||||||
|
return Err(PlatformError::GetCpuStateFailure(anyhow!(
|
||||||
|
"CPU id mismatch {:?} {:?}",
|
||||||
|
cpu_id,
|
||||||
|
self.vcpu.vp_index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let regs = self
|
||||||
|
.vcpu
|
||||||
|
.get_regs()
|
||||||
|
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
||||||
|
let sregs = self
|
||||||
|
.vcpu
|
||||||
|
.get_sregs()
|
||||||
|
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
||||||
|
|
||||||
|
debug!("mshv emulator: Getting new CPU state");
|
||||||
|
debug!("mshv emulator: {:#x?}", regs);
|
||||||
|
|
||||||
|
Ok(EmulatorCpuState { regs, sregs })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
|
||||||
|
if cpu_id != self.vcpu.vp_index as usize {
|
||||||
|
return Err(PlatformError::SetCpuStateFailure(anyhow!(
|
||||||
|
"CPU id mismatch {:?} {:?}",
|
||||||
|
cpu_id,
|
||||||
|
self.vcpu.vp_index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("mshv emulator: Setting new CPU state");
|
||||||
|
debug!("mshv emulator: {:#x?}", state.regs);
|
||||||
|
|
||||||
|
self.vcpu
|
||||||
|
.set_regs(&state.regs)
|
||||||
|
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
|
||||||
|
self.vcpu
|
||||||
|
.set_sregs(&state.sregs)
|
||||||
|
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
let rip =
|
||||||
|
self.cpu_state(self.vcpu.vp_index as usize)?
|
||||||
|
.linearize(Register::CS, ip, false)?;
|
||||||
|
self.read_memory_flags(
|
||||||
|
rip,
|
||||||
|
instruction_bytes,
|
||||||
|
HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_EXECUTE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ use crate::arch::x86::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub mod emulator;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||||
///
|
///
|
||||||
@@ -43,6 +45,11 @@ pub struct VcpuMshvState {
|
|||||||
pub vp_states: AllVpStateComponents,
|
pub vp_states: AllVpStateComponents,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct MshvClockData {
|
||||||
|
pub ref_time: u64,
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for VcpuMshvState {
|
impl fmt::Display for VcpuMshvState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let expected_num_msrs = self.msrs.len();
|
let expected_num_msrs = self.msrs.len();
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ pub enum HypervisorVmError {
|
|||||||
/// Assert virtual interrupt error
|
/// Assert virtual interrupt error
|
||||||
///
|
///
|
||||||
#[error("Failed to assert virtual Interrupt: {0}")]
|
#[error("Failed to assert virtual Interrupt: {0}")]
|
||||||
AsserttVirtualInterrupt(#[source] anyhow::Error),
|
AssertVirtualInterrupt(#[source] anyhow::Error),
|
||||||
|
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(feature = "sev_snp")]
|
||||||
///
|
///
|
||||||
@@ -231,6 +231,16 @@ pub enum HypervisorVmError {
|
|||||||
///
|
///
|
||||||
#[error("Failed to complete isolated import: {0}")]
|
#[error("Failed to complete isolated import: {0}")]
|
||||||
CompleteIsolatedImport(#[source] anyhow::Error),
|
CompleteIsolatedImport(#[source] anyhow::Error),
|
||||||
|
/// Failed to set VM property
|
||||||
|
///
|
||||||
|
#[error("Failed to set VM property: {0}")]
|
||||||
|
SetVmProperty(#[source] anyhow::Error),
|
||||||
|
///
|
||||||
|
/// Modify GPA host access error
|
||||||
|
///
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
|
#[error("Failed to modify GPA host access: {0}")]
|
||||||
|
ModifyGpaHostAccess(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Result type for returning from a function
|
/// Result type for returning from a function
|
||||||
@@ -390,6 +400,21 @@ pub trait Vm: Send + Sync + Any {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pause the VM
|
||||||
|
fn pause(&self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resume the VM
|
||||||
|
fn resume(&self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
|
fn gain_page_access(&self, _gpa: u64, _size: u32) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VmOps: Send + Sync {
|
pub trait VmOps: Send + Sync {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "net_gen"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Chromium OS Authors"]
|
authors = ["The Chromium OS Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "net_gen"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "net_util"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Chromium OS Authors"]
|
authors = ["The Chromium OS Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "net_util"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
getrandom = "0.2.13"
|
getrandom = "0.2.14"
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
log = "0.4.21"
|
log = "0.4.22"
|
||||||
net_gen = { path = "../net_gen" }
|
net_gen = { path = "../net_gen" }
|
||||||
rate_limiter = { path = "../rate_limiter" }
|
rate_limiter = { path = "../rate_limiter" }
|
||||||
serde = "1.0.197"
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
virtio-bindings = "0.2.2"
|
virtio-bindings = "0.2.2"
|
||||||
virtio-queue = "0.11.0"
|
virtio-queue = "0.12.0"
|
||||||
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
vm-memory = { version = "0.14.1", features = [
|
||||||
|
"backend-atomic",
|
||||||
|
"backend-bitmap",
|
||||||
|
"backend-mmap",
|
||||||
|
] }
|
||||||
vm-virtio = { path = "../vm-virtio" }
|
vm-virtio = { path = "../vm-virtio" }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
pnet = "0.34.0"
|
pnet = "0.35.0"
|
||||||
pnet_datalink = "0.34.0"
|
pnet_datalink = "0.35.0"
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.120"
|
||||||
|
|||||||
8
net_util/build.rs
Normal file
8
net_util/build.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright © 2024 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo::rustc-check-cfg=cfg(fuzzing)");
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr {
|
|||||||
sin_family: net_gen::AF_INET as u16,
|
sin_family: net_gen::AF_INET as u16,
|
||||||
sin_port: 0,
|
sin_port: 0,
|
||||||
// SAFETY: ip_addr can be safely transmute to in_addr
|
// SAFETY: ip_addr can be safely transmute to in_addr
|
||||||
sin_addr: unsafe { mem::transmute(ip_addr.octets()) },
|
sin_addr: unsafe { mem::transmute::<[u8; 4], net_gen::inn::in_addr>(ip_addr.octets()) },
|
||||||
__pad: [0; 8usize],
|
__pad: [0; 8usize],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use super::{register_listener, unregister_listener, vnet_hdr_len, Tap};
|
use super::{register_listener, unregister_listener, vnet_hdr_len, Tap};
|
||||||
use crate::GuestMemoryMmap;
|
|
||||||
use rate_limiter::{RateLimiter, TokenType};
|
use rate_limiter::{RateLimiter, TokenType};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
@@ -12,6 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||||
|
use vm_memory::bitmap::Bitmap;
|
||||||
use vm_memory::{Bytes, GuestMemory};
|
use vm_memory::{Bytes, GuestMemory};
|
||||||
use vm_virtio::{AccessPlatform, Translatable};
|
use vm_virtio::{AccessPlatform, Translatable};
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ use vm_virtio::{AccessPlatform, Translatable};
|
|||||||
pub struct TxVirtio {
|
pub struct TxVirtio {
|
||||||
pub counter_bytes: Wrapping<u64>,
|
pub counter_bytes: Wrapping<u64>,
|
||||||
pub counter_frames: Wrapping<u64>,
|
pub counter_frames: Wrapping<u64>,
|
||||||
|
iovecs: IovecBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TxVirtio {
|
impl Default for TxVirtio {
|
||||||
@@ -32,12 +33,13 @@ impl TxVirtio {
|
|||||||
TxVirtio {
|
TxVirtio {
|
||||||
counter_bytes: Wrapping(0),
|
counter_bytes: Wrapping(0),
|
||||||
counter_frames: Wrapping(0),
|
counter_frames: Wrapping(0),
|
||||||
|
iovecs: IovecBuffer::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_desc_chain(
|
pub fn process_desc_chain<B: Bitmap + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
tap: &Tap,
|
tap: &Tap,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue,
|
||||||
rate_limiter: &mut Option<RateLimiter>,
|
rate_limiter: &mut Option<RateLimiter>,
|
||||||
@@ -54,7 +56,7 @@ impl TxVirtio {
|
|||||||
|
|
||||||
let mut next_desc = desc_chain.next();
|
let mut next_desc = desc_chain.next();
|
||||||
|
|
||||||
let mut iovecs = Vec::new();
|
let mut iovecs = self.iovecs.borrow();
|
||||||
while let Some(desc) = next_desc {
|
while let Some(desc) = next_desc {
|
||||||
let desc_addr = desc
|
let desc_addr = desc
|
||||||
.addr()
|
.addr()
|
||||||
@@ -145,6 +147,7 @@ impl TxVirtio {
|
|||||||
pub struct RxVirtio {
|
pub struct RxVirtio {
|
||||||
pub counter_bytes: Wrapping<u64>,
|
pub counter_bytes: Wrapping<u64>,
|
||||||
pub counter_frames: Wrapping<u64>,
|
pub counter_frames: Wrapping<u64>,
|
||||||
|
iovecs: IovecBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RxVirtio {
|
impl Default for RxVirtio {
|
||||||
@@ -158,12 +161,13 @@ impl RxVirtio {
|
|||||||
RxVirtio {
|
RxVirtio {
|
||||||
counter_bytes: Wrapping(0),
|
counter_bytes: Wrapping(0),
|
||||||
counter_frames: Wrapping(0),
|
counter_frames: Wrapping(0),
|
||||||
|
iovecs: IovecBuffer::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_desc_chain(
|
pub fn process_desc_chain<B: Bitmap + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
tap: &Tap,
|
tap: &Tap,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue,
|
||||||
rate_limiter: &mut Option<RateLimiter>,
|
rate_limiter: &mut Option<RateLimiter>,
|
||||||
@@ -193,7 +197,7 @@ impl RxVirtio {
|
|||||||
.ok_or(NetQueuePairError::DescriptorInvalidHeader)?;
|
.ok_or(NetQueuePairError::DescriptorInvalidHeader)?;
|
||||||
let mut next_desc = Some(desc);
|
let mut next_desc = Some(desc);
|
||||||
|
|
||||||
let mut iovecs = Vec::new();
|
let mut iovecs = self.iovecs.borrow();
|
||||||
while let Some(desc) = next_desc {
|
while let Some(desc) = next_desc {
|
||||||
let desc_addr = desc
|
let desc_addr = desc
|
||||||
.addr()
|
.addr()
|
||||||
@@ -288,6 +292,53 @@ impl RxVirtio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
struct IovecBuffer(Vec<libc::iovec>);
|
||||||
|
|
||||||
|
// SAFETY: Implementing Send for IovecBuffer is safe as the pointer inside is iovec.
|
||||||
|
// The iovecs are usually constructed from virtio descriptors, which are safe to send across
|
||||||
|
// threads.
|
||||||
|
unsafe impl Send for IovecBuffer {}
|
||||||
|
// SAFETY: Implementing Sync for IovecBuffer is safe as the pointer inside is iovec.
|
||||||
|
// The iovecs are usually constructed from virtio descriptors, which are safe to access from
|
||||||
|
// multiple threads.
|
||||||
|
unsafe impl Sync for IovecBuffer {}
|
||||||
|
|
||||||
|
impl IovecBuffer {
|
||||||
|
fn new() -> Self {
|
||||||
|
// Here we use 4 as the default capacity because it is enough for most cases.
|
||||||
|
const DEFAULT_CAPACITY: usize = 4;
|
||||||
|
IovecBuffer(Vec::with_capacity(DEFAULT_CAPACITY))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn borrow(&mut self) -> IovecBufferBorrowed<'_> {
|
||||||
|
IovecBufferBorrowed(&mut self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IovecBufferBorrowed<'a>(&'a mut Vec<libc::iovec>);
|
||||||
|
|
||||||
|
impl<'a> std::ops::Deref for IovecBufferBorrowed<'a> {
|
||||||
|
type Target = Vec<libc::iovec>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::ops::DerefMut for IovecBufferBorrowed<'a> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IovecBufferBorrowed<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Clear the buffer to make sure old values are not used after
|
||||||
|
self.0.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct NetCounters {
|
pub struct NetCounters {
|
||||||
pub tx_bytes: Arc<AtomicU64>,
|
pub tx_bytes: Arc<AtomicU64>,
|
||||||
@@ -350,9 +401,9 @@ pub struct NetQueuePair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NetQueuePair {
|
impl NetQueuePair {
|
||||||
pub fn process_tx(
|
pub fn process_tx<B: Bitmap + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue,
|
||||||
) -> Result<bool, NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
let tx_tap_retry = self.tx.process_desc_chain(
|
let tx_tap_retry = self.tx.process_desc_chain(
|
||||||
@@ -400,9 +451,9 @@ impl NetQueuePair {
|
|||||||
.map_err(NetQueuePairError::QueueNeedsNotification)
|
.map_err(NetQueuePairError::QueueNeedsNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_rx(
|
pub fn process_rx<B: Bitmap + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mem: &GuestMemoryMmap,
|
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||||
queue: &mut Queue,
|
queue: &mut Queue,
|
||||||
) -> Result<bool, NetQueuePairError> {
|
) -> Result<bool, NetQueuePairError> {
|
||||||
self.rx_desc_avail = !self.rx.process_desc_chain(
|
self.rx_desc_avail = !self.rx.process_desc_chain(
|
||||||
@@ -418,7 +469,7 @@ impl NetQueuePair {
|
|||||||
.map_or(false, |r| r.is_blocked());
|
.map_or(false, |r| r.is_blocked());
|
||||||
|
|
||||||
// Stop listening on the `RX_TAP_EVENT` when:
|
// Stop listening on the `RX_TAP_EVENT` when:
|
||||||
// 1) there is no available describles, or
|
// 1) there is no available describes, or
|
||||||
// 2) the RX rate limit is reached.
|
// 2) the RX rate limit is reached.
|
||||||
if self.rx_tap_listening && (!self.rx_desc_avail || rate_limit_reached) {
|
if self.rx_tap_listening && (!self.rx_desc_avail || rate_limit_reached) {
|
||||||
unregister_listener(
|
unregister_listener(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "option_parser"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "option_parser"
|
||||||
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub enum OptionParserError {
|
|||||||
UnknownOption(String),
|
UnknownOption(String),
|
||||||
InvalidSyntax(String),
|
InvalidSyntax(String),
|
||||||
Conversion(String, String),
|
Conversion(String, String),
|
||||||
|
InvalidValue(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for OptionParserError {
|
impl fmt::Display for OptionParserError {
|
||||||
@@ -33,6 +34,7 @@ impl fmt::Display for OptionParserError {
|
|||||||
OptionParserError::Conversion(field, value) => {
|
OptionParserError::Conversion(field, value) => {
|
||||||
write!(f, "unable to convert {value} for {field}")
|
write!(f, "unable to convert {value} for {field}")
|
||||||
}
|
}
|
||||||
|
OptionParserError::InvalidValue(s) => write!(f, "invalid value: {s}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +205,7 @@ impl FromStr for ByteSized {
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let s = s.trim_end_matches(|c| c == 'K' || c == 'M' || c == 'G');
|
let s = s.trim_end_matches(['K', 'M', 'G']);
|
||||||
s.parse::<u64>()
|
s.parse::<u64>()
|
||||||
.map_err(|_| ByteSizedParseError::InvalidValue(s.to_owned()))?
|
.map_err(|_| ByteSizedParseError::InvalidValue(s.to_owned()))?
|
||||||
<< shift
|
<< shift
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pci"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Samuel Ortiz <sameo@linux.intel.com>"]
|
authors = ["Samuel Ortiz <sameo@linux.intel.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "pci"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
@@ -10,19 +10,24 @@ kvm = ["vfio-ioctls/kvm"]
|
|||||||
mshv = ["vfio-ioctls/mshv"]
|
mshv = ["vfio-ioctls/mshv"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.86"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
|
libc = "0.2.155"
|
||||||
|
log = "0.4.22"
|
||||||
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
thiserror = "1.0.62"
|
||||||
|
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = [
|
||||||
|
"fam-wrappers",
|
||||||
|
] }
|
||||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||||
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
|
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
|
||||||
|
|
||||||
vmm-sys-util = "0.12.1"
|
|
||||||
libc = "0.2.153"
|
|
||||||
log = "0.4.21"
|
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
|
||||||
thiserror = "1.0.58"
|
|
||||||
vm-allocator = { path = "../vm-allocator" }
|
vm-allocator = { path = "../vm-allocator" }
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
vm-memory = { version = "0.14.1", features = [
|
||||||
|
"backend-atomic",
|
||||||
|
"backend-bitmap",
|
||||||
|
"backend-mmap",
|
||||||
|
] }
|
||||||
vm-migration = { path = "../vm-migration" }
|
vm-migration = { path = "../vm-migration" }
|
||||||
|
vmm-sys-util = "0.12.1"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use std::any::Any;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::{Arc, Barrier, Mutex};
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
use vm_device::{Bus, BusDevice};
|
use vm_device::{Bus, BusDevice, BusDeviceSync};
|
||||||
|
|
||||||
const VENDOR_ID_INTEL: u16 = 0x8086;
|
const VENDOR_ID_INTEL: u16 = 0x8086;
|
||||||
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
|
||||||
@@ -122,7 +122,7 @@ impl PciBus {
|
|||||||
|
|
||||||
pub fn register_mapping(
|
pub fn register_mapping(
|
||||||
&self,
|
&self,
|
||||||
dev: Arc<Mutex<dyn BusDevice>>,
|
dev: Arc<dyn BusDeviceSync>,
|
||||||
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
||||||
mmio_bus: &Bus,
|
mmio_bus: &Bus,
|
||||||
bars: Vec<PciBarConfiguration>,
|
bars: Vec<PciBarConfiguration>,
|
||||||
@@ -409,7 +409,7 @@ impl BusDevice for PciConfigMmio {
|
|||||||
// Only allow reads to the register boundary.
|
// Only allow reads to the register boundary.
|
||||||
let start = offset as usize % 4;
|
let start = offset as usize % 4;
|
||||||
let end = start + data.len();
|
let end = start + data.len();
|
||||||
if end > 4 || offset > u64::from(u32::max_value()) {
|
if end > 4 || offset > u64::from(u32::MAX) {
|
||||||
for d in data {
|
for d in data {
|
||||||
*d = 0xff;
|
*d = 0xff;
|
||||||
}
|
}
|
||||||
@@ -423,7 +423,7 @@ impl BusDevice for PciConfigMmio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
if offset > u64::from(u32::max_value()) {
|
if offset > u64::from(u32::MAX) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.config_space_write(offset as u32, offset % 4, data);
|
self.config_space_write(offset as u32, offset % 4, data);
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ pub enum PciExpressCapabilityId {
|
|||||||
VfResizeableBar = 0x0024,
|
VfResizeableBar = 0x0024,
|
||||||
DataLinkFeature = 0x0025,
|
DataLinkFeature = 0x0025,
|
||||||
PhysicalLayerSixteenGts = 0x0026,
|
PhysicalLayerSixteenGts = 0x0026,
|
||||||
LaneMargeningAtTheReceiver = 0x0027,
|
LaneMarginingAtTheReceiver = 0x0027,
|
||||||
HierarchyId = 0x0028,
|
HierarchyId = 0x0028,
|
||||||
NativePcieEnclosureManagement = 0x0029,
|
NativePcieEnclosureManagement = 0x0029,
|
||||||
PhysicalLayerThirtyTwoGts = 0x002a,
|
PhysicalLayerThirtyTwoGts = 0x002a,
|
||||||
@@ -345,7 +345,7 @@ impl From<u16> for PciExpressCapabilityId {
|
|||||||
0x0024 => PciExpressCapabilityId::VfResizeableBar,
|
0x0024 => PciExpressCapabilityId::VfResizeableBar,
|
||||||
0x0025 => PciExpressCapabilityId::DataLinkFeature,
|
0x0025 => PciExpressCapabilityId::DataLinkFeature,
|
||||||
0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
|
0x0026 => PciExpressCapabilityId::PhysicalLayerSixteenGts,
|
||||||
0x0027 => PciExpressCapabilityId::LaneMargeningAtTheReceiver,
|
0x0027 => PciExpressCapabilityId::LaneMarginingAtTheReceiver,
|
||||||
0x0028 => PciExpressCapabilityId::HierarchyId,
|
0x0028 => PciExpressCapabilityId::HierarchyId,
|
||||||
0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
|
0x0029 => PciExpressCapabilityId::NativePcieEnclosureManagement,
|
||||||
0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
|
0x002a => PciExpressCapabilityId::PhysicalLayerThirtyTwoGts,
|
||||||
@@ -743,7 +743,7 @@ impl PciConfiguration {
|
|||||||
.ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
|
.ok_or(Error::BarAddressInvalid(config.addr, config.size))?;
|
||||||
match config.region_type {
|
match config.region_type {
|
||||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
|
PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => {
|
||||||
if end_addr > u64::from(u32::max_value()) {
|
if end_addr > u64::from(u32::MAX) {
|
||||||
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,10 +757,6 @@ impl PciConfiguration {
|
|||||||
return Err(Error::BarInvalid64(bar_idx));
|
return Err(Error::BarInvalid64(bar_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if end_addr > u64::max_value() {
|
|
||||||
return Err(Error::BarAddressInvalid(config.addr, config.size));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.bars[bar_idx + 1].used {
|
if self.bars[bar_idx + 1].used {
|
||||||
return Err(Error::BarInUse64(bar_idx));
|
return Err(Error::BarInUse64(bar_idx));
|
||||||
}
|
}
|
||||||
@@ -818,7 +814,7 @@ impl PciConfiguration {
|
|||||||
.checked_add(config.size - 1)
|
.checked_add(config.size - 1)
|
||||||
.ok_or(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) {
|
||||||
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
return Err(Error::RomBarAddressInvalid(config.addr, config.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::fmt::{self, Display};
|
|||||||
use std::sync::{Arc, Barrier, Mutex};
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
use std::{io, result};
|
use std::{io, result};
|
||||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||||
use vm_device::{BusDevice, Resource};
|
use vm_device::Resource;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -54,7 +54,7 @@ pub struct BarReprogrammingParams {
|
|||||||
pub region_type: PciBarRegionType,
|
pub region_type: PciBarRegionType,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PciDevice: BusDevice {
|
pub trait PciDevice: Send {
|
||||||
/// Allocates the needed PCI BARs space using the `allocate` function which takes a size and
|
/// Allocates the needed PCI BARs space using the `allocate` function which takes a size and
|
||||||
/// returns an address. Returns a Vec of (GuestAddress, GuestUsize) tuples.
|
/// returns an address. Returns a Vec of (GuestAddress, GuestUsize) tuples.
|
||||||
fn allocate_bars(
|
fn allocate_bars(
|
||||||
@@ -79,7 +79,7 @@ pub trait PciDevice: BusDevice {
|
|||||||
|
|
||||||
/// Sets a register in the configuration space.
|
/// Sets a register in the configuration space.
|
||||||
/// * `reg_idx` - The index of the config register to modify.
|
/// * `reg_idx` - The index of the config register to modify.
|
||||||
/// * `offset` - Offset in to the register.
|
/// * `offset` - Offset into the register.
|
||||||
fn write_config_register(
|
fn write_config_register(
|
||||||
&mut self,
|
&mut self,
|
||||||
reg_idx: usize,
|
reg_idx: usize,
|
||||||
@@ -97,11 +97,11 @@ pub trait PciDevice: BusDevice {
|
|||||||
) -> Option<BarReprogrammingParams> {
|
) -> Option<BarReprogrammingParams> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
/// Reads from a BAR region mapped in to the device.
|
/// Reads from a BAR region mapped into the device.
|
||||||
/// * `addr` - The guest address inside the BAR.
|
/// * `addr` - The guest address inside the BAR.
|
||||||
/// * `data` - Filled with the data from `addr`.
|
/// * `data` - Filled with the data from `addr`.
|
||||||
fn read_bar(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {}
|
fn read_bar(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {}
|
||||||
/// Writes to a BAR region mapped in to the device.
|
/// Writes to a BAR region mapped into the device.
|
||||||
/// * `addr` - The guest address inside the BAR.
|
/// * `addr` - The guest address inside the BAR.
|
||||||
/// * `data` - The data to write.
|
/// * `data` - The data to write.
|
||||||
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) -> Option<Arc<Barrier>> {
|
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) -> Option<Arc<Barrier>> {
|
||||||
|
|||||||
@@ -213,6 +213,12 @@ impl MsixConfig {
|
|||||||
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
|
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
|
||||||
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
|
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
|
||||||
|
|
||||||
|
if index >= self.table_entries.len() {
|
||||||
|
debug!("Invalid MSI-X table entry index {index}");
|
||||||
|
data.copy_from_slice(&[0xff; 8][..data.len()]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match data.len() {
|
match data.len() {
|
||||||
4 => {
|
4 => {
|
||||||
let value = match modulo_offset {
|
let value = match modulo_offset {
|
||||||
@@ -260,6 +266,11 @@ impl MsixConfig {
|
|||||||
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
|
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
|
||||||
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
|
let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO;
|
||||||
|
|
||||||
|
if index >= self.table_entries.len() {
|
||||||
|
debug!("Invalid MSI-X table entry index {index}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Store the value of the entry before modification
|
// Store the value of the entry before modification
|
||||||
let old_entry = self.table_entries[index].clone();
|
let old_entry = self.table_entries[index].clone();
|
||||||
|
|
||||||
@@ -351,6 +362,12 @@ impl MsixConfig {
|
|||||||
let index: usize = (offset / MSIX_PBA_ENTRIES_MODULO) as usize;
|
let index: usize = (offset / MSIX_PBA_ENTRIES_MODULO) as usize;
|
||||||
let modulo_offset = offset % MSIX_PBA_ENTRIES_MODULO;
|
let modulo_offset = offset % MSIX_PBA_ENTRIES_MODULO;
|
||||||
|
|
||||||
|
if index >= self.pba_entries.len() {
|
||||||
|
debug!("Invalid MSI-X PBA entry index {index}");
|
||||||
|
data.copy_from_slice(&[0xff; 8][..data.len()]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match data.len() {
|
match data.len() {
|
||||||
4 => {
|
4 => {
|
||||||
let value: u32 = match modulo_offset {
|
let value: u32 = match modulo_offset {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use std::any::Any;
|
|||||||
use std::os::unix::prelude::AsRawFd;
|
use std::os::unix::prelude::AsRawFd;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::sync::{Arc, Barrier, Mutex};
|
use std::sync::{Arc, Barrier, Mutex};
|
||||||
use std::u32;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use vfio_bindings::bindings::vfio::*;
|
use vfio_bindings::bindings::vfio::*;
|
||||||
use vfio_ioctls::VfioIrq;
|
use vfio_ioctls::VfioIrq;
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
|
build = "../build.rs"
|
||||||
|
edition = "2021"
|
||||||
name = "performance-metrics"
|
name = "performance-metrics"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
|
||||||
edition = "2021"
|
|
||||||
build = "../build.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.4", features = ["wrap_help"] }
|
clap = { version = "4.5.4", features = ["wrap_help"] }
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
serde = { version = "1.0.197", features = ["rc", "derive"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.120"
|
||||||
test_infra = { path = "../test_infra" }
|
test_infra = { path = "../test_infra" }
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ mod adjuster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEST_LIST: [PerformanceTest; 29] = [
|
const TEST_LIST: [PerformanceTest; 30] = [
|
||||||
PerformanceTest {
|
PerformanceTest {
|
||||||
name: "boot_time_ms",
|
name: "boot_time_ms",
|
||||||
func_ptr: performance_boot_time,
|
func_ptr: performance_boot_time,
|
||||||
@@ -306,6 +306,16 @@ const TEST_LIST: [PerformanceTest; 29] = [
|
|||||||
},
|
},
|
||||||
unit_adjuster: adjuster::s_to_ms,
|
unit_adjuster: adjuster::s_to_ms,
|
||||||
},
|
},
|
||||||
|
PerformanceTest {
|
||||||
|
name: "restore_latency_time_ms",
|
||||||
|
func_ptr: performance_restore_latency,
|
||||||
|
control: PerformanceTestControl {
|
||||||
|
test_timeout: 2,
|
||||||
|
test_iterations: 10,
|
||||||
|
..PerformanceTestControl::default()
|
||||||
|
},
|
||||||
|
unit_adjuster: adjuster::identity,
|
||||||
|
},
|
||||||
PerformanceTest {
|
PerformanceTest {
|
||||||
name: "boot_time_16_vcpus_pmem_ms",
|
name: "boot_time_16_vcpus_pmem_ms",
|
||||||
func_ptr: performance_boot_time_pmem,
|
func_ptr: performance_boot_time_pmem,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-arm64-custom-20210929-
|
|||||||
enum Error {
|
enum Error {
|
||||||
BootTimeParse,
|
BootTimeParse,
|
||||||
Infra(InfraError),
|
Infra(InfraError),
|
||||||
|
RestoreTimeParse,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InfraError> for Error {
|
impl From<InfraError> for Error {
|
||||||
@@ -34,7 +35,7 @@ impl From<InfraError> for Error {
|
|||||||
const BLK_IO_TEST_IMG: &str = "/var/tmp/ch-blk-io-test.img";
|
const BLK_IO_TEST_IMG: &str = "/var/tmp/ch-blk-io-test.img";
|
||||||
|
|
||||||
pub fn init_tests() {
|
pub fn init_tests() {
|
||||||
// The test image can not be created on tmpfs (e.g. /tmp) filesystem,
|
// The test image cannot be created on tmpfs (e.g. /tmp) filesystem,
|
||||||
// as tmpfs does not support O_DIRECT
|
// as tmpfs does not support O_DIRECT
|
||||||
assert!(exec_host_command_output(&format!(
|
assert!(exec_host_command_output(&format!(
|
||||||
"dd if=/dev/zero of={BLK_IO_TEST_IMG} bs=1M count=4096"
|
"dd if=/dev/zero of={BLK_IO_TEST_IMG} bs=1M count=4096"
|
||||||
@@ -76,6 +77,26 @@ fn direct_kernel_boot_path() -> PathBuf {
|
|||||||
kernel_path
|
kernel_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remote_command(api_socket: &str, command: &str, arg: Option<&str>) -> bool {
|
||||||
|
let mut cmd = std::process::Command::new(clh_command("ch-remote"));
|
||||||
|
cmd.args([&format!("--api-socket={}", api_socket), command]);
|
||||||
|
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
cmd.arg(arg);
|
||||||
|
}
|
||||||
|
let output = cmd.output().unwrap();
|
||||||
|
if output.status.success() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"Error running ch-remote command: {:?}\nstderr: {}",
|
||||||
|
&cmd,
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
pub fn performance_net_throughput(control: &PerformanceTestControl) -> f64 {
|
||||||
let test_timeout = control.test_timeout;
|
let test_timeout = control.test_timeout;
|
||||||
let (rx, bandwidth) = control.net_control.unwrap();
|
let (rx, bandwidth) = control.net_control.unwrap();
|
||||||
@@ -199,7 +220,7 @@ fn parse_boot_time_output(output: &[u8]) -> Result<f64, Error> {
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
t[7].eq("seconds"),
|
t[7].eq("seconds"),
|
||||||
"Expecting 'seconds' as the the last word of the 'Debug I/O port' output"
|
"Expecting 'seconds' as the last word of the 'Debug I/O port' output"
|
||||||
);
|
);
|
||||||
|
|
||||||
t[6].parse::<f64>().unwrap()
|
t[6].parse::<f64>().unwrap()
|
||||||
@@ -226,7 +247,7 @@ fn parse_boot_time_output(output: &[u8]) -> Result<f64, Error> {
|
|||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
t[7].eq("seconds"),
|
t[7].eq("seconds"),
|
||||||
"Expecting 'seconds' as the the last word of the 'Debug I/O port' output"
|
"Expecting 'seconds' as the last word of the 'Debug I/O port' output"
|
||||||
);
|
);
|
||||||
|
|
||||||
t[6].parse::<f64>().unwrap()
|
t[6].parse::<f64>().unwrap()
|
||||||
@@ -255,7 +276,7 @@ fn measure_boot_time(cmd: &mut GuestCommand, test_timeout: u32) -> Result<f64, E
|
|||||||
let _ = child.kill();
|
let _ = child.kill();
|
||||||
let output = child.wait_with_output().unwrap();
|
let output = child.wait_with_output().unwrap();
|
||||||
|
|
||||||
parse_boot_time_output(&output.stderr).map_err(|e| {
|
parse_boot_time_output(&output.stderr).inspect_err(|_| {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====",
|
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====",
|
||||||
String::from_utf8_lossy(&output.stdout)
|
String::from_utf8_lossy(&output.stdout)
|
||||||
@@ -264,8 +285,6 @@ fn measure_boot_time(cmd: &mut GuestCommand, test_timeout: u32) -> Result<f64, E
|
|||||||
"\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
|
"\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
);
|
);
|
||||||
|
|
||||||
e
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,6 +425,120 @@ pub fn performance_block_io(control: &PerformanceTestControl) -> f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the event_monitor file based on the format that each event
|
||||||
|
// is followed by a double newline
|
||||||
|
fn parse_event_file(event_file: &str) -> Vec<serde_json::Value> {
|
||||||
|
let content = fs::read(event_file).unwrap();
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for entry in String::from_utf8_lossy(&content)
|
||||||
|
.trim()
|
||||||
|
.split("\n\n")
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
{
|
||||||
|
ret.push(serde_json::from_str(entry).unwrap());
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_restore_time_output(events: &[serde_json::Value]) -> Result<f64, Error> {
|
||||||
|
for entry in events.iter() {
|
||||||
|
if entry["event"].as_str().unwrap() == "restored" {
|
||||||
|
let duration = entry["timestamp"]["secs"].as_u64().unwrap() as f64 * 1_000f64
|
||||||
|
+ entry["timestamp"]["nanos"].as_u64().unwrap() as f64 / 1_000_000f64;
|
||||||
|
return Ok(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::RestoreTimeParse)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure_restore_time(
|
||||||
|
cmd: &mut GuestCommand,
|
||||||
|
event_file: &str,
|
||||||
|
test_timeout: u32,
|
||||||
|
) -> Result<f64, Error> {
|
||||||
|
let mut child = cmd
|
||||||
|
.capture_output()
|
||||||
|
.verbosity(VerbosityLevel::Warn)
|
||||||
|
.set_print_cmd(false)
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
thread::sleep(Duration::new((test_timeout / 2) as u64, 0));
|
||||||
|
let _ = child.kill();
|
||||||
|
let output = child.wait_with_output().unwrap();
|
||||||
|
|
||||||
|
let json_events = parse_event_file(event_file);
|
||||||
|
|
||||||
|
parse_restore_time_output(&json_events).inspect_err(|_| {
|
||||||
|
eprintln!(
|
||||||
|
"\n\n==== Start child stdout ====\n\n{}\n\n==== End child stdout ====\
|
||||||
|
\n\n==== Start child stderr ====\n\n{}\n\n==== End child stderr ====",
|
||||||
|
String::from_utf8_lossy(&output.stdout),
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn performance_restore_latency(control: &PerformanceTestControl) -> f64 {
|
||||||
|
let r = std::panic::catch_unwind(|| {
|
||||||
|
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||||
|
let guest = performance_test_new_guest(Box::new(focal));
|
||||||
|
let api_socket_source = String::from(
|
||||||
|
guest
|
||||||
|
.tmp_dir
|
||||||
|
.as_path()
|
||||||
|
.join("cloud-hypervisor.sock")
|
||||||
|
.to_str()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut child = GuestCommand::new(&guest)
|
||||||
|
.args(["--api-socket", &api_socket_source])
|
||||||
|
.args([
|
||||||
|
"--cpus",
|
||||||
|
&format!("boot={}", control.num_boot_vcpus.unwrap_or(1)),
|
||||||
|
])
|
||||||
|
.args(["--memory", "size=256M"])
|
||||||
|
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||||
|
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||||
|
.args(["--console", "off"])
|
||||||
|
.default_disks()
|
||||||
|
.set_print_cmd(false)
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
thread::sleep(Duration::new((control.test_timeout / 2) as u64, 0));
|
||||||
|
let snapshot_dir = String::from(guest.tmp_dir.as_path().join("snapshot").to_str().unwrap());
|
||||||
|
std::fs::create_dir(&snapshot_dir).unwrap();
|
||||||
|
assert!(remote_command(&api_socket_source, "pause", None));
|
||||||
|
assert!(remote_command(
|
||||||
|
&api_socket_source,
|
||||||
|
"snapshot",
|
||||||
|
Some(format!("file://{}", snapshot_dir).as_str()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let _ = child.kill();
|
||||||
|
|
||||||
|
let event_path = String::from(guest.tmp_dir.as_path().join("event.json").to_str().unwrap());
|
||||||
|
let mut cmd = GuestCommand::new(&guest);
|
||||||
|
let c = cmd
|
||||||
|
.args([
|
||||||
|
"--restore",
|
||||||
|
format!("source_url=file://{}", snapshot_dir).as_str(),
|
||||||
|
])
|
||||||
|
.args(["--event-monitor", format!("path={}", event_path).as_str()]);
|
||||||
|
|
||||||
|
measure_restore_time(c, event_path.as_str(), control.test_timeout).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
match r {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => {
|
||||||
|
panic!("test failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -496,7 +629,42 @@ cloud-hypervisor: 613.57361ms: <vcpu0> INFO:vmm/src/vm.rs:392 -- [Debug I/O port
|
|||||||
|
|
||||||
assert_eq!(parse_boot_time_output(output.as_bytes()).unwrap(), 0.4525);
|
assert_eq!(parse_boot_time_output(output.as_bytes()).unwrap(), 0.4525);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_restore_time_output() {
|
||||||
|
let output = r#"
|
||||||
|
{
|
||||||
|
"timestamp": {
|
||||||
|
"secs": 0,
|
||||||
|
"nanos": 4664404
|
||||||
|
},
|
||||||
|
"source": "virtio-device",
|
||||||
|
"event": "activated",
|
||||||
|
"properties": {
|
||||||
|
"id": "__rng"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"timestamp": {
|
||||||
|
"secs": 0,
|
||||||
|
"nanos": 5505133
|
||||||
|
},
|
||||||
|
"source": "vm",
|
||||||
|
"event": "restored",
|
||||||
|
"properties": null
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for entry in String::from(output)
|
||||||
|
.trim()
|
||||||
|
.split("\n\n")
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
{
|
||||||
|
ret.push(serde_json::from_str(entry).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(parse_restore_time_output(&ret).unwrap(), 5.505133_f64);
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_fio_output() {
|
fn test_parse_fio_output() {
|
||||||
let output = r#"
|
let output = r#"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
|
edition = "2021"
|
||||||
name = "rate_limiter"
|
name = "rate_limiter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
log = "0.4.21"
|
log = "0.4.22"
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.62"
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ pub(crate) mod tests {
|
|||||||
assert!(h.as_raw_fd() > 0);
|
assert!(h.as_raw_fd() > 0);
|
||||||
|
|
||||||
// ops/s limiter should be disabled so consume(whatever) should work
|
// ops/s limiter should be disabled so consume(whatever) should work
|
||||||
assert!(h.consume(u64::max_value(), TokenType::Ops));
|
assert!(h.consume(u64::MAX, TokenType::Ops));
|
||||||
|
|
||||||
// do full 1000 bytes
|
// do full 1000 bytes
|
||||||
assert!(h.consume(1000, TokenType::Bytes));
|
assert!(h.consume(1000, TokenType::Bytes));
|
||||||
@@ -403,7 +403,7 @@ pub(crate) mod tests {
|
|||||||
assert!(h.as_raw_fd() > 0);
|
assert!(h.as_raw_fd() > 0);
|
||||||
|
|
||||||
// bytes/s limiter should be disabled so consume(whatever) should work
|
// bytes/s limiter should be disabled so consume(whatever) should work
|
||||||
assert!(h.consume(u64::max_value(), TokenType::Bytes));
|
assert!(h.consume(u64::MAX, TokenType::Bytes));
|
||||||
|
|
||||||
// do full 1000 ops
|
// do full 1000 ops
|
||||||
assert!(h.consume(1000, TokenType::Ops));
|
assert!(h.consume(1000, TokenType::Ops));
|
||||||
|
|||||||
@@ -307,14 +307,14 @@ impl RateLimiter {
|
|||||||
///
|
///
|
||||||
/// * `bytes_total_capacity` - the total capacity of the `TokenType::Bytes` token bucket.
|
/// * `bytes_total_capacity` - the total capacity of the `TokenType::Bytes` token bucket.
|
||||||
/// * `bytes_one_time_burst` - initial extra credit on top of `bytes_total_capacity`,
|
/// * `bytes_one_time_burst` - initial extra credit on top of `bytes_total_capacity`,
|
||||||
/// that does not replenish and which can be used for an initial burst of data.
|
/// that does not replenish and which can be used for an initial burst of data.
|
||||||
/// * `bytes_complete_refill_time_ms` - number of milliseconds for the `TokenType::Bytes`
|
/// * `bytes_complete_refill_time_ms` - number of milliseconds for the `TokenType::Bytes`
|
||||||
/// token bucket to go from zero Bytes to `bytes_total_capacity` Bytes.
|
/// token bucket to go from zero Bytes to `bytes_total_capacity` Bytes.
|
||||||
/// * `ops_total_capacity` - the total capacity of the `TokenType::Ops` token bucket.
|
/// * `ops_total_capacity` - the total capacity of the `TokenType::Ops` token bucket.
|
||||||
/// * `ops_one_time_burst` - initial extra credit on top of `ops_total_capacity`,
|
/// * `ops_one_time_burst` - initial extra credit on top of `ops_total_capacity`,
|
||||||
/// that does not replenish and which can be used for an initial burst of data.
|
/// that does not replenish and which can be used for an initial burst of data.
|
||||||
/// * `ops_complete_refill_time_ms` - number of milliseconds for the `TokenType::Ops` token
|
/// * `ops_complete_refill_time_ms` - number of milliseconds for the `TokenType::Ops` token
|
||||||
/// bucket to go from zero Ops to `ops_total_capacity` Ops.
|
/// bucket to go from zero Ops to `ops_total_capacity` Ops.
|
||||||
///
|
///
|
||||||
/// If either bytes/ops *size* or *refill_time* are **zero**, the limiter
|
/// If either bytes/ops *size* or *refill_time* are **zero**, the limiter
|
||||||
/// is **disabled** for that respective token type.
|
/// is **disabled** for that respective token type.
|
||||||
@@ -662,8 +662,8 @@ pub(crate) mod tests {
|
|||||||
// limiter should not be blocked
|
// limiter should not be blocked
|
||||||
assert!(!l.is_blocked());
|
assert!(!l.is_blocked());
|
||||||
// limiter should be disabled so consume(whatever) should work
|
// limiter should be disabled so consume(whatever) should work
|
||||||
assert!(l.consume(u64::max_value(), TokenType::Ops));
|
assert!(l.consume(u64::MAX, TokenType::Ops));
|
||||||
assert!(l.consume(u64::max_value(), TokenType::Bytes));
|
assert!(l.consume(u64::MAX, TokenType::Bytes));
|
||||||
// calling the handler without there having been an event should error
|
// calling the handler without there having been an event should error
|
||||||
assert!(l.event_handler().is_err());
|
assert!(l.event_handler().is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -721,7 +721,7 @@ pub(crate) mod tests {
|
|||||||
assert!(l.as_raw_fd() > 0);
|
assert!(l.as_raw_fd() > 0);
|
||||||
|
|
||||||
// ops/s limiter should be disabled so consume(whatever) should work
|
// ops/s limiter should be disabled so consume(whatever) should work
|
||||||
assert!(l.consume(u64::max_value(), TokenType::Ops));
|
assert!(l.consume(u64::MAX, TokenType::Ops));
|
||||||
|
|
||||||
// do full 1000 bytes
|
// do full 1000 bytes
|
||||||
assert!(l.consume(1000, TokenType::Bytes));
|
assert!(l.consume(1000, TokenType::Bytes));
|
||||||
@@ -754,7 +754,7 @@ pub(crate) mod tests {
|
|||||||
assert!(l.as_raw_fd() > 0);
|
assert!(l.as_raw_fd() > 0);
|
||||||
|
|
||||||
// bytes/s limiter should be disabled so consume(whatever) should work
|
// bytes/s limiter should be disabled so consume(whatever) should work
|
||||||
assert!(l.consume(u64::max_value(), TokenType::Bytes));
|
assert!(l.consume(u64::MAX, TokenType::Bytes));
|
||||||
|
|
||||||
// do full 1000 ops
|
// do full 1000 ops
|
||||||
assert!(l.consume(1000, TokenType::Ops));
|
assert!(l.consume(1000, TokenType::Ops));
|
||||||
|
|||||||
282
release-notes.md
282
release-notes.md
@@ -1,10 +1,20 @@
|
|||||||
|
- [v41.0](#v410)
|
||||||
|
- [Experimental "Pvmemcontrol" Support](#experimental-pvmemcontrol-support)
|
||||||
|
- [Sandboxing With Landlock Support](#sandboxing-with-landlock-support)
|
||||||
|
- [Notable Performance Improvements](#notable-performance-improvements)
|
||||||
|
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||||
|
- [Contributors](#contributors)
|
||||||
|
- [v40.0](#v400)
|
||||||
|
- [Support for Restoring File Descriptor Backed Network Devices](#support-for-restoring-file-descriptor-backed-network-devices)
|
||||||
|
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||||
|
- [Contributors](#contributors-1)
|
||||||
- [v39.0](#v390)
|
- [v39.0](#v390)
|
||||||
- [Variable Sizing of PCI Apertures for Segments](#variable-sizing-of-pci-apertures-for-segments)
|
- [Variable Sizing of PCI Apertures for Segments](#variable-sizing-of-pci-apertures-for-segments)
|
||||||
- [Direct Booting with bzImages](#direct-booting-with-bzimages)
|
- [Direct Booting with bzImages](#direct-booting-with-bzimages)
|
||||||
- [Support for NVIDIA GPUDirect P2P Support](#support-for-nvidia-gpudirect-p2p-support)
|
- [Support for NVIDIA GPUDirect P2P Support](#support-for-nvidia-gpudirect-p2p-support)
|
||||||
- [Guest NMI Injection Support](#guest-nmi-injection-support)
|
- [Guest NMI Injection Support](#guest-nmi-injection-support)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||||
- [Contributors](#contributors)
|
- [Contributors](#contributors-2)
|
||||||
- [v38.0](#v380)
|
- [v38.0](#v380)
|
||||||
- [Group Rate Limiter on Block Devices](#group-rate-limiter-on-block-devices)
|
- [Group Rate Limiter on Block Devices](#group-rate-limiter-on-block-devices)
|
||||||
- [CPU Pinning Support for Block Device Worker Thread](#cpu-pinning-support-for-block-device-worker-thread)
|
- [CPU Pinning Support for Block Device Worker Thread](#cpu-pinning-support-for-block-device-worker-thread)
|
||||||
@@ -12,16 +22,16 @@
|
|||||||
- [New 'debug-console' Device](#new-debug-console-device)
|
- [New 'debug-console' Device](#new-debug-console-device)
|
||||||
- [Improved VFIO Device Support](#improved-vfio-device-support)
|
- [Improved VFIO Device Support](#improved-vfio-device-support)
|
||||||
- [Extended CPU Affinity Support](#extended-cpu-affinity-support)
|
- [Extended CPU Affinity Support](#extended-cpu-affinity-support)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||||
- [Contributors](#contributors-1)
|
- [Contributors](#contributors-3)
|
||||||
- [v37.0](#v370)
|
- [v37.0](#v370)
|
||||||
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
|
- [Long Term Support (LTS) Release](#long-term-support-lts-release)
|
||||||
- [Multiple PCI segments Support for 32-bit VFIO devices](#multiple-pci-segments-support-for-32-bit-vfio-devices)
|
- [Multiple PCI segments Support for 32-bit VFIO devices](#multiple-pci-segments-support-for-32-bit-vfio-devices)
|
||||||
- [Configurable Named TAP Devices](#configurable-named-tap-devices)
|
- [Configurable Named TAP Devices](#configurable-named-tap-devices)
|
||||||
- [TTY Output from Both Serial Device and Virtio Console](#tty-output-from-both-serial-device-and-virtio-console)
|
- [TTY Output from Both Serial Device and Virtio Console](#tty-output-from-both-serial-device-and-virtio-console)
|
||||||
- [Faster VM Restoration from Snapshots](#faster-vm-restoration-from-snapshots)
|
- [Faster VM Restoration from Snapshots](#faster-vm-restoration-from-snapshots)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||||
- [Contributors](#contributors-2)
|
- [Contributors](#contributors-4)
|
||||||
- [v36.0](#v360)
|
- [v36.0](#v360)
|
||||||
- [Command Line Changes](#command-line-changes)
|
- [Command Line Changes](#command-line-changes)
|
||||||
- [Enabled Features Reported via API Endpoint and CLI](#enabled-features-reported-via-api-endpoint-and-cli)
|
- [Enabled Features Reported via API Endpoint and CLI](#enabled-features-reported-via-api-endpoint-and-cli)
|
||||||
@@ -30,31 +40,31 @@
|
|||||||
- [Unix Socket Backend for Serial Port](#unix-socket-backend-for-serial-port)
|
- [Unix Socket Backend for Serial Port](#unix-socket-backend-for-serial-port)
|
||||||
- [AIO Backend for Block Devices](#aio-backend-for-block-devices)
|
- [AIO Backend for Block Devices](#aio-backend-for-block-devices)
|
||||||
- [Documentation Improvements](#documentation-improvements)
|
- [Documentation Improvements](#documentation-improvements)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||||
- [Contributors](#contributors-3)
|
- [Contributors](#contributors-5)
|
||||||
- [v35.0](#v350)
|
- [v35.0](#v350)
|
||||||
- [`virtio-vsock` Support for Linux Guest Kernel v6.3+](#virtio-vsock-support-for-linux-guest-kernel-v63)
|
- [`virtio-vsock` Support for Linux Guest Kernel v6.3+](#virtio-vsock-support-for-linux-guest-kernel-v63)
|
||||||
- [User Specified Serial Number for `virtio-block`](#user-specified-serial-number-for-virtio-block)
|
- [User Specified Serial Number for `virtio-block`](#user-specified-serial-number-for-virtio-block)
|
||||||
- [vCPU TSC Frequency Included in Migration State](#vcpu-tsc-frequency-included-in-migration-state)
|
- [vCPU TSC Frequency Included in Migration State](#vcpu-tsc-frequency-included-in-migration-state)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||||
- [Contributors](#contributors-4)
|
- [Contributors](#contributors-6)
|
||||||
- [v34.0](#v340)
|
- [v34.0](#v340)
|
||||||
- [Paravirtualised Panic Device Support](#paravirtualised-panic-device-support)
|
- [Paravirtualised Panic Device Support](#paravirtualised-panic-device-support)
|
||||||
- [Improvements to VM Core Dump](#improvements-to-vm-core-dump)
|
- [Improvements to VM Core Dump](#improvements-to-vm-core-dump)
|
||||||
- [QCOW2 Support for Backing Files](#qcow2-support-for-backing-files)
|
- [QCOW2 Support for Backing Files](#qcow2-support-for-backing-files)
|
||||||
- [Minimum Host Kernel Bump](#minimum-host-kernel-bump)
|
- [Minimum Host Kernel Bump](#minimum-host-kernel-bump)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||||
- [Contributors](#contributors-5)
|
- [Contributors](#contributors-7)
|
||||||
- [v33.0](#v330)
|
- [v33.0](#v330)
|
||||||
- [D-Bus based API](#d-bus-based-api)
|
- [D-Bus based API](#d-bus-based-api)
|
||||||
- [Expose Host CPU Cache Details for AArch64](#expose-host-cpu-cache-details-for-aarch64)
|
- [Expose Host CPU Cache Details for AArch64](#expose-host-cpu-cache-details-for-aarch64)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||||
- [Contributors](#contributors-6)
|
- [Contributors](#contributors-8)
|
||||||
- [v32.0](#v320)
|
- [v32.0](#v320)
|
||||||
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
|
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
|
||||||
- [API Changes](#api-changes)
|
- [API Changes](#api-changes)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||||
- [Contributors](#contributors-7)
|
- [Contributors](#contributors-9)
|
||||||
- [v31.1](#v311)
|
- [v31.1](#v311)
|
||||||
- [v31.0](#v310)
|
- [v31.0](#v310)
|
||||||
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
|
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
|
||||||
@@ -62,15 +72,15 @@
|
|||||||
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
|
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
|
||||||
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
|
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
|
||||||
- [Documentation Improvements](#documentation-improvements-1)
|
- [Documentation Improvements](#documentation-improvements-1)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||||
- [Contributors](#contributors-8)
|
- [Contributors](#contributors-10)
|
||||||
- [v30.0](#v300)
|
- [v30.0](#v300)
|
||||||
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
|
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
|
||||||
- [Basic vfio-user Server Support](#basic-vfio-user-server-support)
|
- [Basic vfio-user Server Support](#basic-vfio-user-server-support)
|
||||||
- [Heap Profiling Support](#heap-profiling-support)
|
- [Heap Profiling Support](#heap-profiling-support)
|
||||||
- [Documentation Improvements](#documentation-improvements-2)
|
- [Documentation Improvements](#documentation-improvements-2)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||||
- [Contributors](#contributors-9)
|
- [Contributors](#contributors-11)
|
||||||
- [v28.2](#v282)
|
- [v28.2](#v282)
|
||||||
- [v29.0](#v290)
|
- [v29.0](#v290)
|
||||||
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
||||||
@@ -80,10 +90,10 @@
|
|||||||
- [`AArch64` Documentation Integration](#aarch64-documentation-integration)
|
- [`AArch64` Documentation Integration](#aarch64-documentation-integration)
|
||||||
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
|
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
|
||||||
- [TCP Offload Control](#tcp-offload-control)
|
- [TCP Offload Control](#tcp-offload-control)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||||
- [Removals](#removals)
|
- [Removals](#removals)
|
||||||
- [Deprecations](#deprecations)
|
- [Deprecations](#deprecations)
|
||||||
- [Contributors](#contributors-10)
|
- [Contributors](#contributors-12)
|
||||||
- [v28.1](#v281)
|
- [v28.1](#v281)
|
||||||
- [v28.0](#v280)
|
- [v28.0](#v280)
|
||||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||||
@@ -91,9 +101,9 @@
|
|||||||
- [Virtualised TPM Support](#virtualised-tpm-support)
|
- [Virtualised TPM Support](#virtualised-tpm-support)
|
||||||
- [Transparent Huge Page Support](#transparent-huge-page-support)
|
- [Transparent Huge Page Support](#transparent-huge-page-support)
|
||||||
- [README Quick Start Improved](#readme-quick-start-improved)
|
- [README Quick Start Improved](#readme-quick-start-improved)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||||
- [Removals](#removals-1)
|
- [Removals](#removals-1)
|
||||||
- [Contributors](#contributors-11)
|
- [Contributors](#contributors-13)
|
||||||
- [v27.0](#v270)
|
- [v27.0](#v270)
|
||||||
- [Community Engagement](#community-engagement)
|
- [Community Engagement](#community-engagement)
|
||||||
- [Prebuilt Packages](#prebuilt-packages)
|
- [Prebuilt Packages](#prebuilt-packages)
|
||||||
@@ -102,41 +112,41 @@
|
|||||||
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
|
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
|
||||||
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
|
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
|
||||||
- [GDB Support for AArch64](#gdb-support-for-aarch64)
|
- [GDB Support for AArch64](#gdb-support-for-aarch64)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||||
- [Deprecations](#deprecations-1)
|
- [Deprecations](#deprecations-1)
|
||||||
- [Contributors](#contributors-12)
|
- [Contributors](#contributors-14)
|
||||||
- [v26.0](#v260)
|
- [v26.0](#v260)
|
||||||
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
||||||
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||||
- [Deprecations](#deprecations-2)
|
- [Deprecations](#deprecations-2)
|
||||||
- [Removals](#removals-2)
|
- [Removals](#removals-2)
|
||||||
- [Contributors](#contributors-13)
|
- [Contributors](#contributors-15)
|
||||||
- [v25.0](#v250)
|
- [v25.0](#v250)
|
||||||
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
||||||
- [VM "Coredump" Support](#vm-coredump-support)
|
- [VM "Coredump" Support](#vm-coredump-support)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||||
- [Removals](#removals-3)
|
- [Removals](#removals-3)
|
||||||
- [Contributors](#contributors-14)
|
- [Contributors](#contributors-16)
|
||||||
- [v24.0](#v240)
|
- [v24.0](#v240)
|
||||||
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
||||||
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
||||||
- [Sparse Mmap support](#sparse-mmap-support)
|
- [Sparse Mmap support](#sparse-mmap-support)
|
||||||
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
- [Notable Bug Fixes](#notable-bug-fixes-17)
|
||||||
- [Notable Improvements](#notable-improvements)
|
- [Notable Improvements](#notable-improvements)
|
||||||
- [Deprecations](#deprecations-3)
|
- [Deprecations](#deprecations-3)
|
||||||
- [New on the Website](#new-on-the-website)
|
- [New on the Website](#new-on-the-website)
|
||||||
- [Contributors](#contributors-15)
|
- [Contributors](#contributors-17)
|
||||||
- [v23.1](#v231)
|
- [v23.1](#v231)
|
||||||
- [v23.0](#v230)
|
- [v23.0](#v230)
|
||||||
- [vDPA Support](#vdpa-support)
|
- [vDPA Support](#vdpa-support)
|
||||||
- [Updated OS Support list](#updated-os-support-list)
|
- [Updated OS Support list](#updated-os-support-list)
|
||||||
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
||||||
- [`AMX` Support](#amx-support)
|
- [`AMX` Support](#amx-support)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
- [Notable Bug Fixes](#notable-bug-fixes-18)
|
||||||
- [Deprecations](#deprecations-4)
|
- [Deprecations](#deprecations-4)
|
||||||
- [Contributors](#contributors-16)
|
- [Contributors](#contributors-18)
|
||||||
- [v22.1](#v221)
|
- [v22.1](#v221)
|
||||||
- [v22.0](#v220)
|
- [v22.0](#v220)
|
||||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||||
@@ -147,13 +157,13 @@
|
|||||||
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
||||||
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
||||||
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-17)
|
- [Notable Bug Fixes](#notable-bug-fixes-19)
|
||||||
- [Contributors](#contributors-17)
|
- [Contributors](#contributors-19)
|
||||||
- [v21.0](#v210)
|
- [v21.0](#v210)
|
||||||
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
|
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
|
||||||
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
|
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
|
||||||
- [Notable Bug fixes](#notable-bug-fixes-18)
|
- [Notable Bug fixes](#notable-bug-fixes-20)
|
||||||
- [Contributors](#contributors-18)
|
- [Contributors](#contributors-20)
|
||||||
- [v20.2](#v202)
|
- [v20.2](#v202)
|
||||||
- [v20.1](#v201)
|
- [v20.1](#v201)
|
||||||
- [v20.0](#v200)
|
- [v20.0](#v200)
|
||||||
@@ -162,8 +172,8 @@
|
|||||||
- [Improved VFIO support](#improved-vfio-support)
|
- [Improved VFIO support](#improved-vfio-support)
|
||||||
- [Safer code](#safer-code)
|
- [Safer code](#safer-code)
|
||||||
- [Extended documentation](#extended-documentation)
|
- [Extended documentation](#extended-documentation)
|
||||||
- [Notable bug fixes](#notable-bug-fixes-19)
|
- [Notable bug fixes](#notable-bug-fixes-21)
|
||||||
- [Contributors](#contributors-19)
|
- [Contributors](#contributors-21)
|
||||||
- [v19.0](#v190)
|
- [v19.0](#v190)
|
||||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||||
@@ -171,8 +181,8 @@
|
|||||||
- [Live migration enhancements](#live-migration-enhancements)
|
- [Live migration enhancements](#live-migration-enhancements)
|
||||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||||
- [Notable bug fixes](#notable-bug-fixes-20)
|
- [Notable bug fixes](#notable-bug-fixes-22)
|
||||||
- [Contributors](#contributors-20)
|
- [Contributors](#contributors-22)
|
||||||
- [v18.0](#v180)
|
- [v18.0](#v180)
|
||||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||||
@@ -182,23 +192,23 @@
|
|||||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||||
- [Notable bug fixes](#notable-bug-fixes-21)
|
- [Notable bug fixes](#notable-bug-fixes-23)
|
||||||
- [Contributors](#contributors-21)
|
- [Contributors](#contributors-23)
|
||||||
- [v17.0](#v170)
|
- [v17.0](#v170)
|
||||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||||
- [Improved SGX support](#improved-sgx-support)
|
- [Improved SGX support](#improved-sgx-support)
|
||||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||||
- [Notable bug fixes](#notable-bug-fixes-22)
|
- [Notable bug fixes](#notable-bug-fixes-24)
|
||||||
- [Contributors](#contributors-22)
|
- [Contributors](#contributors-24)
|
||||||
- [v16.0](#v160)
|
- [v16.0](#v160)
|
||||||
- [Improved live migration support](#improved-live-migration-support)
|
- [Improved live migration support](#improved-live-migration-support)
|
||||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||||
- [Notable bug fixes](#notable-bug-fixes-23)
|
- [Notable bug fixes](#notable-bug-fixes-25)
|
||||||
- [Removed functionality](#removed-functionality)
|
- [Removed functionality](#removed-functionality)
|
||||||
- [Contributors](#contributors-23)
|
- [Contributors](#contributors-25)
|
||||||
- [v15.0](#v150)
|
- [v15.0](#v150)
|
||||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||||
- [Network device rate limiting](#network-device-rate-limiting)
|
- [Network device rate limiting](#network-device-rate-limiting)
|
||||||
@@ -206,7 +216,7 @@
|
|||||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||||
- [Bug fixes](#bug-fixes)
|
- [Bug fixes](#bug-fixes)
|
||||||
- [Deprecations](#deprecations-5)
|
- [Deprecations](#deprecations-5)
|
||||||
- [Contributors](#contributors-24)
|
- [Contributors](#contributors-26)
|
||||||
- [v0.14.1](#v0141)
|
- [v0.14.1](#v0141)
|
||||||
- [v0.14.0](#v0140)
|
- [v0.14.0](#v0140)
|
||||||
- [Structured event monitoring](#structured-event-monitoring)
|
- [Structured event monitoring](#structured-event-monitoring)
|
||||||
@@ -216,7 +226,7 @@
|
|||||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||||
- [Block device rate limiting](#block-device-rate-limiting)
|
- [Block device rate limiting](#block-device-rate-limiting)
|
||||||
- [Deprecations](#deprecations-6)
|
- [Deprecations](#deprecations-6)
|
||||||
- [Contributors](#contributors-25)
|
- [Contributors](#contributors-27)
|
||||||
- [v0.13.0](#v0130)
|
- [v0.13.0](#v0130)
|
||||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||||
- [Improved huge page support](#improved-huge-page-support)
|
- [Improved huge page support](#improved-huge-page-support)
|
||||||
@@ -224,13 +234,13 @@
|
|||||||
- [VHD disk image support](#vhd-disk-image-support)
|
- [VHD disk image support](#vhd-disk-image-support)
|
||||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||||
- [Contributors](#contributors-26)
|
- [Contributors](#contributors-28)
|
||||||
- [v0.12.0](#v0120)
|
- [v0.12.0](#v0120)
|
||||||
- [ARM64 enhancements](#arm64-enhancements)
|
- [ARM64 enhancements](#arm64-enhancements)
|
||||||
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||||
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||||
- [Enhanced "info" API](#enhanced-info-api)
|
- [Enhanced "info" API](#enhanced-info-api)
|
||||||
- [Contributors](#contributors-27)
|
- [Contributors](#contributors-29)
|
||||||
- [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)
|
||||||
@@ -242,15 +252,15 @@
|
|||||||
- [Default Log Level Changed](#default-log-level-changed)
|
- [Default Log Level Changed](#default-log-level-changed)
|
||||||
- [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-24)
|
- [Notable Bug Fixes](#notable-bug-fixes-26)
|
||||||
- [Contributors](#contributors-28)
|
- [Contributors](#contributors-30)
|
||||||
- [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-25)
|
- [Notable Bug Fixes](#notable-bug-fixes-27)
|
||||||
- [Contributors](#contributors-29)
|
- [Contributors](#contributors-31)
|
||||||
- [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)
|
||||||
@@ -263,17 +273,17 @@
|
|||||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||||
- [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-26)
|
- [Notable Bug Fixes](#notable-bug-fixes-28)
|
||||||
- [Contributors](#contributors-30)
|
- [Contributors](#contributors-32)
|
||||||
- [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)
|
||||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||||
- [Notable Bug Fixes](#notable-bug-fixes-27)
|
- [Notable Bug Fixes](#notable-bug-fixes-29)
|
||||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||||
- [Contributors](#contributors-31)
|
- [Contributors](#contributors-33)
|
||||||
- [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)
|
||||||
@@ -283,14 +293,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-32)
|
- [Contributors](#contributors-34)
|
||||||
- [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-33)
|
- [Contributors](#contributors-35)
|
||||||
- [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)
|
||||||
@@ -298,7 +308,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-34)
|
- [Contributors](#contributors-36)
|
||||||
- [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)
|
||||||
@@ -307,7 +317,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-35)
|
- [Contributors](#contributors-37)
|
||||||
- [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)
|
||||||
@@ -334,6 +344,116 @@
|
|||||||
- [Unit testing](#unit-testing)
|
- [Unit testing](#unit-testing)
|
||||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||||
|
|
||||||
|
# v41.0
|
||||||
|
|
||||||
|
This release has been tracked in our [roadmap
|
||||||
|
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||||
|
v41.0. The following user visible changes have been made:
|
||||||
|
|
||||||
|
### Experimental "Pvmemcontrol" Support
|
||||||
|
|
||||||
|
VMM support has been added for this experimental functionality (requires
|
||||||
|
currently out of tree Linux kernel patches) to allow guests to control its
|
||||||
|
physical memory properties to allow optimisations and security features.
|
||||||
|
(#6318, #6467)
|
||||||
|
|
||||||
|
### Sandboxing With Landlock Support
|
||||||
|
|
||||||
|
Support for restricting the VMM process using the Linux kernel "Landlock" API
|
||||||
|
has been added - this can be used to restrict the files (and the read/write
|
||||||
|
permissions) that the VMM process can access. This adds another layer of
|
||||||
|
security alongside the existing sycall filters (`seccomp`) - this can be
|
||||||
|
enabled with `--landlock` and [fully documentated](docs/landlock.md). (#5170)
|
||||||
|
|
||||||
|
### Notable Performance Improvements
|
||||||
|
|
||||||
|
* Reduced heap allocations in `virtio-net` via the use of a cache of `Iovec`
|
||||||
|
structures (#6636)
|
||||||
|
* Notification suppression ("`EVENT_IDX`") support has been added to
|
||||||
|
`virtio-block` giving a 60% improvement in single queue block throughput and
|
||||||
|
IOPs performance (#6580)
|
||||||
|
* Correct size used for `status` field in `virtio-block` state (#6586)
|
||||||
|
|
||||||
|
### Notable Bug Fixes
|
||||||
|
|
||||||
|
* Avoid panic on out-of-bounds PCI MSI-X access (#6657)
|
||||||
|
* Fix undefined behaviour on AArch64 leading to wrong optimisation on KVM API
|
||||||
|
access (#6647)
|
||||||
|
* Rust v1.80.0 added use of `fcntl` syscall on debug assertions so this is now
|
||||||
|
included in the virtio-device seccomp filters for tests that use this (#6648)
|
||||||
|
* Short reads are now handled correctly in the `virtio-vsock` device (#6621)
|
||||||
|
* Fix undefined behaviour on TTY ioctl leading to wrong optimisation (#6568)
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
Many thanks to everyone who has contributed to our release:
|
||||||
|
|
||||||
|
* Alyssa Ross <hi@alyssa.is>
|
||||||
|
* Bo Chen <chen.bo@intel.com>
|
||||||
|
* Changyuan Lyu <changyuanl@google.com>
|
||||||
|
* Jinank Jain <jinankjain@microsoft.com>
|
||||||
|
* Julian Stecklina <julian.stecklina@cyberus-technology.de>
|
||||||
|
* Muminul Islam <muislam@microsoft.com>
|
||||||
|
* Nuno Das Neves <nudasnev@microsoft.com>
|
||||||
|
* Praveen K Paladugu <prapal@linux.microsoft.com>
|
||||||
|
* Rob Bradford <rbradford@rivosinc.com>
|
||||||
|
* Songqian Li <sionli@tencent.com>
|
||||||
|
* Wei Liu <liuwe@microsoft.com>
|
||||||
|
* Yuanchu Xie <yuanchu@google.com>
|
||||||
|
* ihciah <ihciah@gmail.com>
|
||||||
|
* wuxinyue <wuxinyue.wxy@antgroup.com>
|
||||||
|
|
||||||
|
# v40.0
|
||||||
|
|
||||||
|
This release has been tracked in our [roadmap
|
||||||
|
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||||
|
v40.0. The following user visible changes have been made:
|
||||||
|
|
||||||
|
### Support for Restoring File Descriptor Backed Network Devices
|
||||||
|
|
||||||
|
It is now possible to pass file descriptors over the HTTP API (and using
|
||||||
|
`ch-remote`) when restoring to update the file descriptors for network devices.
|
||||||
|
This enables snapshot & restore functionality for guests using `macvtap` or
|
||||||
|
other file descriptor backed network devices. (#6286)
|
||||||
|
|
||||||
|
### Notable Bug Fixes
|
||||||
|
|
||||||
|
* Default values have been removed from required fields in the OpenAPI metadata
|
||||||
|
(#6495)
|
||||||
|
* The help syntax of `ch-remote remove-device` has been improved (#6456)
|
||||||
|
* A double close of file descriptors has been fixed when using `--serial`
|
||||||
|
(#6486)
|
||||||
|
* To prevent loops a limit on the nesting level for QCOW2 backing files has
|
||||||
|
been introduced (#6482)
|
||||||
|
* Boot time performance has been improved with multiple cores by avoiding
|
||||||
|
`cpuid` instructions and by seeding the in kernel file descriptor table
|
||||||
|
(#6498, #6478)
|
||||||
|
* L1 cache details are more likely to be propagated into the guest (#6523)
|
||||||
|
* The default topology for guests now uses multiple cores rather than sockets
|
||||||
|
(#6504)
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
Many thanks to everyone who has contributed to our release:
|
||||||
|
|
||||||
|
* Alexandru Matei <alexandru.matei@uipath.com>
|
||||||
|
* Bo Chen <chen.bo@intel.com>
|
||||||
|
* Jinank Jain <jinankjain@microsoft.com>
|
||||||
|
* Josh Soref <2119212+jsoref@users.noreply.github.com>
|
||||||
|
* Muminul Islam <muislam@microsoft.com>
|
||||||
|
* Nuno Das Neves <nudasnev@microsoft.com>
|
||||||
|
* Omer Faruk Bayram <omer.faruk@sartura.hr>
|
||||||
|
* Praveen K Paladugu <prapal@linux.microsoft.com>
|
||||||
|
* Purna Pavan Chandra <paekkaladevi@linux.microsoft.com>
|
||||||
|
* Rob Bradford <rbradford@rivosinc.com>
|
||||||
|
* SamrutGadde <samrut.gadde@gmail.com>
|
||||||
|
* Sean Banko <sbanko@crusoe.ai>
|
||||||
|
* Songqian Li <sionli@tencent.com>
|
||||||
|
* Wei Liu <liuwe@microsoft.com>
|
||||||
|
* Yi Wang <foxywang@tencent.com>
|
||||||
|
* Yu Li <liyu.yukiteru@bytedance.com>
|
||||||
|
*
|
||||||
|
|
||||||
# v39.0
|
# v39.0
|
||||||
|
|
||||||
This release has been tracked in our [roadmap
|
This release has been tracked in our [roadmap
|
||||||
@@ -1081,7 +1201,7 @@ releases of the LTS.
|
|||||||
|
|
||||||
### Virtualised TPM Support
|
### Virtualised TPM Support
|
||||||
|
|
||||||
Support for adding an emulated CRB TPM has been added. This has it's own [TPM
|
Support for adding an emulated CRB TPM has been added. This has its own [TPM
|
||||||
documentation](docs/tpm.md).
|
documentation](docs/tpm.md).
|
||||||
|
|
||||||
### Transparent Huge Page Support
|
### Transparent Huge Page Support
|
||||||
@@ -1272,7 +1392,7 @@ plan to use alternatives.
|
|||||||
The following functionality has been removed:
|
The following functionality has been removed:
|
||||||
|
|
||||||
* The unused `poll_queue` parameter has been removed from `--disk` and
|
* The unused `poll_queue` parameter has been removed from `--disk` and
|
||||||
equivalent. This was residual from the the removal of the `vhost-user-block`
|
equivalent. This was residual from the removal of the `vhost-user-block`
|
||||||
spawning feature (#4402.)
|
spawning feature (#4402.)
|
||||||
|
|
||||||
### Contributors
|
### Contributors
|
||||||
@@ -2094,19 +2214,19 @@ Deprecated features will be removed in a subsequent release and users should pla
|
|||||||
Many thanks to everyone who has contributed to our 0.14.0 release including
|
Many thanks to everyone who has contributed to our 0.14.0 release including
|
||||||
some new faces.
|
some new faces.
|
||||||
|
|
||||||
Bo Chen <chen.bo@intel.com>
|
* Bo Chen <chen.bo@intel.com>
|
||||||
Henry Wang <Henry.Wang@arm.com>
|
* Henry Wang <Henry.Wang@arm.com>
|
||||||
Iggy Jackson <iggy@theiggy.com>
|
* Iggy Jackson <iggy@theiggy.com>
|
||||||
Jiachen Zhang <zhangjiachen.jaycee@bytedance.com>
|
* Jiachen Zhang <zhangjiachen.jaycee@bytedance.com>
|
||||||
Michael Zhao <michael.zhao@arm.com>
|
* Michael Zhao <michael.zhao@arm.com>
|
||||||
Muminul Islam <muislam@microsoft.com>
|
* Muminul Islam <muislam@microsoft.com>
|
||||||
Penny Zheng <Penny.Zheng@arm.com>
|
* Penny Zheng <Penny.Zheng@arm.com>
|
||||||
Rob Bradford <robert.bradford@intel.com>
|
* Rob Bradford <robert.bradford@intel.com>
|
||||||
Sebastien Boeuf <sebastien.boeuf@intel.com>
|
* Sebastien Boeuf <sebastien.boeuf@intel.com>
|
||||||
Vineeth Pillai <viremana@linux.microsoft.com>
|
* Vineeth Pillai <viremana@linux.microsoft.com>
|
||||||
Wei Liu <liuwe@microsoft.com>
|
* Wei Liu <liuwe@microsoft.com>
|
||||||
William Douglas <william.r.douglas@gmail.com>
|
* William Douglas <william.r.douglas@gmail.com>
|
||||||
Zide Chen <zide.chen@intel.com>
|
* Zide Chen <zide.chen@intel.com>
|
||||||
|
|
||||||
# v0.13.0
|
# v0.13.0
|
||||||
|
|
||||||
@@ -2191,7 +2311,7 @@ removed.
|
|||||||
### Migration of `vhost-user-fs` backend
|
### Migration of `vhost-user-fs` backend
|
||||||
|
|
||||||
The `vhost-user-fs` backend is no longer included in Cloud Hypervisor and it is
|
The `vhost-user-fs` backend is no longer included in Cloud Hypervisor and it is
|
||||||
instead hosted in [it's own
|
instead hosted in [its own
|
||||||
repository](https://gitlab.com/virtio-fs/virtiofsd-rs)
|
repository](https://gitlab.com/virtio-fs/virtiofsd-rs)
|
||||||
|
|
||||||
### Enhanced "info" API
|
### Enhanced "info" API
|
||||||
@@ -2325,7 +2445,7 @@ Some `virtio-block` device drivers may generate requests with multiple descripto
|
|||||||
|
|
||||||
### Memory Zones
|
### Memory Zones
|
||||||
|
|
||||||
Support has been added for fine grained control of memory allocation for the guest. This includes controlling the backing of sections of guest memory, assigning to specific host NUMA nodes and assigning memory and vCPUs to specific memory nodes inside the guest. Full details of this can be found in the [memory documentation](docs/memory.md).
|
Support has been added for fine-grained control of memory allocation for the guest. This includes controlling the backing of sections of guest memory, assigning to specific host NUMA nodes and assigning memory and vCPUs to specific memory nodes inside the guest. Full details of this can be found in the [memory documentation](docs/memory.md).
|
||||||
|
|
||||||
### `Seccomp` Sandbox Improvements
|
### `Seccomp` Sandbox Improvements
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
FROM ubuntu:22.04 as dev
|
FROM ubuntu:22.04 as dev
|
||||||
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG RUST_TOOLCHAIN="1.74.1"
|
ARG RUST_TOOLCHAIN="1.77.0"
|
||||||
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
ARG CLH_SRC_DIR="/cloud-hypervisor"
|
||||||
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
ARG CLH_BUILD_DIR="$CLH_SRC_DIR/build"
|
||||||
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
ARG CARGO_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_registry"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ CLI_NAME="Cloud Hypervisor"
|
|||||||
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
|
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
|
||||||
|
|
||||||
# Needs to match explicit version in docker-image.yaml workflow
|
# Needs to match explicit version in docker-image.yaml workflow
|
||||||
CTR_IMAGE_VERSION="20240407-0"
|
CTR_IMAGE_VERSION="20240507-0"
|
||||||
: "${CTR_IMAGE:=${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}}"
|
: "${CTR_IMAGE:=${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}}"
|
||||||
|
|
||||||
DOCKER_RUNTIME="docker"
|
DOCKER_RUNTIME="docker"
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ update_workloads() {
|
|||||||
popd || exit
|
popd || exit
|
||||||
|
|
||||||
# Download Cloud Hypervisor binary from its last stable release
|
# Download Cloud Hypervisor binary from its last stable release
|
||||||
LAST_RELEASE_VERSION="v36.0"
|
LAST_RELEASE_VERSION="v39.0"
|
||||||
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static-aarch64"
|
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static-aarch64"
|
||||||
CH_RELEASE_NAME="cloud-hypervisor-static-aarch64"
|
CH_RELEASE_NAME="cloud-hypervisor-static-aarch64"
|
||||||
pushd "$WORKLOADS_DIR" || exit
|
pushd "$WORKLOADS_DIR" || exit
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ fi
|
|||||||
popd || exit
|
popd || exit
|
||||||
|
|
||||||
# Download Cloud Hypervisor binary from its last stable release
|
# Download Cloud Hypervisor binary from its last stable release
|
||||||
LAST_RELEASE_VERSION="v36.0"
|
LAST_RELEASE_VERSION="v39.0"
|
||||||
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static"
|
CH_RELEASE_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$LAST_RELEASE_VERSION/cloud-hypervisor-static"
|
||||||
CH_RELEASE_NAME="cloud-hypervisor-static"
|
CH_RELEASE_NAME="cloud-hypervisor-static"
|
||||||
pushd "$WORKLOADS_DIR" || exit
|
pushd "$WORKLOADS_DIR" || exit
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
# This set of vfio tests require to be ran on a specific machine with
|
# This set of vfio tests require to be ran on a specific machine with
|
||||||
# specific hardware (e.g. the "vfio" bera-metal worker equipped with a
|
# specific hardware (e.g. the "vfio" bare-metal worker equipped with a
|
||||||
# Nvidia Tesla T4 card). So the provisioning of the running machine is
|
# Nvidia Tesla T4 card). So the provisioning of the running machine is
|
||||||
# out of the scope of this script, including the custom guest image with
|
# out of the scope of this script, including the custom guest image with
|
||||||
# Nvidia drivers installed, and properly configured Nvidia Tesla T4 card.
|
# Nvidia drivers installed, and properly configured Nvidia Tesla T4 card.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serial_buffer"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "serial_buffer"
|
||||||
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::marker::PhantomData;
|
|||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::process;
|
use std::process;
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
use zbus::{dbus_proxy, zvariant::Optional};
|
use zbus::{proxy, zvariant::Optional};
|
||||||
|
|
||||||
type ApiResult = Result<(), Error>;
|
type ApiResult = Result<(), Error>;
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ enum TargetApi<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
#[dbus_proxy(name = "org.cloudhypervisor.DBusApi1", assume_defaults = false)]
|
#[proxy(name = "org.cloudhypervisor.DBusApi1", assume_defaults = false)]
|
||||||
trait DBusApi1 {
|
trait DBusApi1 {
|
||||||
fn vmm_ping(&self) -> zbus::Result<String>;
|
fn vmm_ping(&self) -> zbus::Result<String>;
|
||||||
fn vmm_shutdown(&self) -> zbus::Result<()>;
|
fn vmm_shutdown(&self) -> zbus::Result<()>;
|
||||||
@@ -445,14 +445,14 @@ fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResu
|
|||||||
.map_err(Error::HttpApiClient)
|
.map_err(Error::HttpApiClient)
|
||||||
}
|
}
|
||||||
Some("restore") => {
|
Some("restore") => {
|
||||||
let restore_config = restore_config(
|
let (restore_config, fds) = restore_config(
|
||||||
matches
|
matches
|
||||||
.subcommand_matches("restore")
|
.subcommand_matches("restore")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_one::<String>("restore_config")
|
.get_one::<String>("restore_config")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)?;
|
)?;
|
||||||
simple_api_command(socket, "PUT", "restore", Some(&restore_config))
|
simple_api_command_with_fds(socket, "PUT", "restore", Some(&restore_config), fds)
|
||||||
.map_err(Error::HttpApiClient)
|
.map_err(Error::HttpApiClient)
|
||||||
}
|
}
|
||||||
Some("coredump") => {
|
Some("coredump") => {
|
||||||
@@ -661,7 +661,7 @@ fn dbus_api_do_command(matches: &ArgMatches, proxy: &DBusApi1ProxyBlocking<'_>)
|
|||||||
proxy.api_vm_snapshot(&snapshot_config)
|
proxy.api_vm_snapshot(&snapshot_config)
|
||||||
}
|
}
|
||||||
Some("restore") => {
|
Some("restore") => {
|
||||||
let restore_config = restore_config(
|
let (restore_config, _fds) = restore_config(
|
||||||
matches
|
matches
|
||||||
.subcommand_matches("restore")
|
.subcommand_matches("restore")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -849,11 +849,20 @@ fn snapshot_config(url: &str) -> String {
|
|||||||
serde_json::to_string(&snapshot_config).unwrap()
|
serde_json::to_string(&snapshot_config).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_config(config: &str) -> Result<String, Error> {
|
fn restore_config(config: &str) -> Result<(String, Vec<i32>), Error> {
|
||||||
let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?;
|
let mut restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?;
|
||||||
|
// RestoreConfig is modified on purpose to take out the file descriptors.
|
||||||
|
// These fds are passed to the server side process via SCM_RIGHTS
|
||||||
|
let fds = match &mut restore_config.net_fds {
|
||||||
|
Some(net_fds) => net_fds
|
||||||
|
.iter_mut()
|
||||||
|
.flat_map(|net| net.fds.take().unwrap_or_default())
|
||||||
|
.collect(),
|
||||||
|
None => Vec::new(),
|
||||||
|
};
|
||||||
let restore_config = serde_json::to_string(&restore_config).unwrap();
|
let restore_config = serde_json::to_string(&restore_config).unwrap();
|
||||||
|
|
||||||
Ok(restore_config)
|
Ok((restore_config, fds))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coredump_config(destination_url: &str) -> String {
|
fn coredump_config(destination_url: &str) -> String {
|
||||||
@@ -987,7 +996,7 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("remove-device")
|
Command::new("remove-device")
|
||||||
.about("Remove VFIO device")
|
.about("Remove VFIO and PCI device")
|
||||||
.arg(Arg::new("id").index(1).help("<device_id>")),
|
.arg(Arg::new("id").index(1).help("<device_id>")),
|
||||||
)
|
)
|
||||||
.subcommand(Command::new("info").about("Info on the VM"))
|
.subcommand(Command::new("info").about("Info on the VM"))
|
||||||
|
|||||||
138
src/main.rs
138
src/main.rs
@@ -12,17 +12,18 @@ use log::{warn, LevelFilter};
|
|||||||
use option_parser::OptionParser;
|
use option_parser::OptionParser;
|
||||||
use seccompiler::SeccompAction;
|
use seccompiler::SeccompAction;
|
||||||
use signal_hook::consts::SIGSYS;
|
use signal_hook::consts::SIGSYS;
|
||||||
use std::env;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::unix::io::{FromRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::{env, io};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
|
use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
|
||||||
use vmm::api::http::http_api_graceful_shutdown;
|
use vmm::api::http::http_api_graceful_shutdown;
|
||||||
use vmm::api::ApiAction;
|
use vmm::api::ApiAction;
|
||||||
use vmm::config;
|
use vmm::config;
|
||||||
|
use vmm::landlock::{Landlock, LandlockError};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
use vmm_sys_util::signal::block_signal;
|
use vmm_sys_util::signal::block_signal;
|
||||||
|
|
||||||
@@ -85,6 +86,22 @@ enum Error {
|
|||||||
LoggerSetup(log::SetLoggerError),
|
LoggerSetup(log::SetLoggerError),
|
||||||
#[error("Failed to gracefully shutdown http api: {0}")]
|
#[error("Failed to gracefully shutdown http api: {0}")]
|
||||||
HttpApiShutdown(#[source] vmm::Error),
|
HttpApiShutdown(#[source] vmm::Error),
|
||||||
|
#[error("Failed to create Landlock object: {0}")]
|
||||||
|
CreateLandlock(#[source] LandlockError),
|
||||||
|
#[error("Failed to apply Landlock: {0}")]
|
||||||
|
ApplyLandlock(#[source] LandlockError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
enum FdTableError {
|
||||||
|
#[error("Failed to create event fd: {0}")]
|
||||||
|
CreateEventFd(std::io::Error),
|
||||||
|
#[error("Failed to obtain file limit: {0}")]
|
||||||
|
GetRLimit(std::io::Error),
|
||||||
|
#[error("Error calling fcntl with F_GETFD: {0}")]
|
||||||
|
GetFd(std::io::Error),
|
||||||
|
#[error("Failed to duplicate file handle: {0}")]
|
||||||
|
Dup2(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Logger {
|
struct Logger {
|
||||||
@@ -259,6 +276,24 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
|||||||
.num_args(1..)
|
.num_args(1..)
|
||||||
.group("vm-config"),
|
.group("vm-config"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("landlock")
|
||||||
|
.long("landlock")
|
||||||
|
.num_args(0)
|
||||||
|
.help(
|
||||||
|
"enable/disable Landlock.",
|
||||||
|
)
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.default_value("false")
|
||||||
|
.group("vm-config"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("landlock-rules")
|
||||||
|
.long("landlock-rules")
|
||||||
|
.help(config::LandlockConfig::SYNTAX)
|
||||||
|
.num_args(1..)
|
||||||
|
.group("vm-config"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("net")
|
Arg::new("net")
|
||||||
.long("net")
|
.long("net")
|
||||||
@@ -487,6 +522,16 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
|||||||
.num_args(1)
|
.num_args(1)
|
||||||
.group("vm-config"),
|
.group("vm-config"),
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "pvmemcontrol")]
|
||||||
|
let app = app.arg(
|
||||||
|
Arg::new("pvmemcontrol")
|
||||||
|
.long("pvmemcontrol")
|
||||||
|
.help("Pvmemcontrol device")
|
||||||
|
.num_args(0)
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.group("vm-config"),
|
||||||
|
);
|
||||||
|
|
||||||
app.arg(
|
app.arg(
|
||||||
Arg::new("version")
|
Arg::new("version")
|
||||||
.short('V')
|
.short('V')
|
||||||
@@ -622,6 +667,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||||||
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
||||||
|
|
||||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
|
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
|
||||||
|
let landlock_enable = cmd_arguments.get_flag("landlock");
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut event_monitor = cmd_arguments
|
let mut event_monitor = cmd_arguments
|
||||||
@@ -691,6 +737,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||||||
vmm::start_event_monitor_thread(
|
vmm::start_event_monitor_thread(
|
||||||
monitor,
|
monitor,
|
||||||
&seccomp_action,
|
&seccomp_action,
|
||||||
|
landlock_enable,
|
||||||
hypervisor.hypervisor_type(),
|
hypervisor.hypervisor_type(),
|
||||||
exit_evt.try_clone().unwrap(),
|
exit_evt.try_clone().unwrap(),
|
||||||
)
|
)
|
||||||
@@ -717,6 +764,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||||||
exit_evt.try_clone().unwrap(),
|
exit_evt.try_clone().unwrap(),
|
||||||
&seccomp_action,
|
&seccomp_action,
|
||||||
hypervisor,
|
hypervisor,
|
||||||
|
landlock_enable,
|
||||||
)
|
)
|
||||||
.map_err(Error::StartVmmThread)?;
|
.map_err(Error::StartVmmThread)?;
|
||||||
|
|
||||||
@@ -764,6 +812,13 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if landlock_enable {
|
||||||
|
Landlock::new()
|
||||||
|
.map_err(Error::CreateLandlock)?
|
||||||
|
.restrict_self()
|
||||||
|
.map_err(Error::ApplyLandlock)?;
|
||||||
|
}
|
||||||
|
|
||||||
vmm_thread_handle
|
vmm_thread_handle
|
||||||
.thread_handle
|
.thread_handle
|
||||||
.join()
|
.join()
|
||||||
@@ -782,6 +837,77 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||||||
r.map(|_| api_socket_path)
|
r.map(|_| api_socket_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a best-effort solution to the latency induced by the RCU
|
||||||
|
// synchronization that happens in the kernel whenever the file descriptor table
|
||||||
|
// fills up.
|
||||||
|
// The table has initially 64 entries on amd64 and every time it fills up, a new
|
||||||
|
// table is created, double the size of the current one, and the entries are
|
||||||
|
// copied to the new table. The filesystem code that does this uses
|
||||||
|
// synchronize_rcu() to ensure all preexisting RCU read-side critical sections
|
||||||
|
// have completed:
|
||||||
|
//
|
||||||
|
// https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/fs/file.c?h=v6.9.1#n162
|
||||||
|
//
|
||||||
|
// Rust programs that create lots of file handles or use
|
||||||
|
// {File,EventFd}::try_clone() to share them are impacted by this issue. This
|
||||||
|
// behavior is quite noticeable in the snapshot restore scenario, the latency is
|
||||||
|
// a big chunk of the total time required to start cloud-hypervisor and restore
|
||||||
|
// the snapshot.
|
||||||
|
//
|
||||||
|
// The kernel has an optimization in code, where it doesn't call
|
||||||
|
// synchronize_rcu() if there is only one thread in the process. We can take
|
||||||
|
// advantage of this optimization by expanding the descriptor table at
|
||||||
|
// application start, when it has only one thread.
|
||||||
|
//
|
||||||
|
// The code tries to resize the table to an adequate size for most use cases,
|
||||||
|
// 4096, this way we avoid any expansion that might take place later.
|
||||||
|
fn expand_fdtable() -> Result<(), FdTableError> {
|
||||||
|
let mut limits = libc::rlimit {
|
||||||
|
rlim_cur: 0,
|
||||||
|
rlim_max: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: FFI call with valid arguments
|
||||||
|
if unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) } < 0 {
|
||||||
|
return Err(FdTableError::GetRLimit(io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let table_size = if limits.rlim_cur == libc::RLIM_INFINITY {
|
||||||
|
4096
|
||||||
|
} else {
|
||||||
|
std::cmp::min(limits.rlim_cur, 4096) as libc::c_int
|
||||||
|
};
|
||||||
|
|
||||||
|
// The first 3 handles are stdin, stdout, stderr. We don't want to touch
|
||||||
|
// any of them.
|
||||||
|
if table_size <= 3 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let dummy_evt = EventFd::new(0).map_err(FdTableError::CreateEventFd)?;
|
||||||
|
|
||||||
|
// Test if the file descriptor is empty
|
||||||
|
// SAFETY: FFI call with valid arguments
|
||||||
|
let flags: i32 = unsafe { libc::fcntl(table_size - 1, libc::F_GETFD) };
|
||||||
|
if flags >= 0 {
|
||||||
|
// Nothing to do, the table is already big enough
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = io::Error::last_os_error();
|
||||||
|
if err.raw_os_error() != Some(libc::EBADF) {
|
||||||
|
return Err(FdTableError::GetFd(err));
|
||||||
|
}
|
||||||
|
// SAFETY: FFI call with valid arguments
|
||||||
|
if unsafe { libc::dup2(dummy_evt.as_raw_fd(), table_size - 1) } < 0 {
|
||||||
|
return Err(FdTableError::Dup2(io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
// SAFETY: FFI call, trivially
|
||||||
|
unsafe { libc::close(table_size - 1) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(all(feature = "tdx", feature = "sev_snp"))]
|
#[cfg(all(feature = "tdx", feature = "sev_snp"))]
|
||||||
compile_error!("Feature 'tdx' and 'sev_snp' are mutually exclusive.");
|
compile_error!("Feature 'tdx' and 'sev_snp' are mutually exclusive.");
|
||||||
@@ -808,6 +934,10 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(e) = expand_fdtable() {
|
||||||
|
warn!("Error expanding FD table: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
let exit_code = match start_vmm(cmd_arguments) {
|
let exit_code = match start_vmm(cmd_arguments) {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
path.map(|s| std::fs::remove_file(s).ok());
|
path.map(|s| std::fs::remove_file(s).ok());
|
||||||
@@ -934,6 +1064,8 @@ mod unit_tests {
|
|||||||
vdpa: None,
|
vdpa: None,
|
||||||
vsock: None,
|
vsock: None,
|
||||||
pvpanic: false,
|
pvpanic: false,
|
||||||
|
#[cfg(feature = "pvmemcontrol")]
|
||||||
|
pvmemcontrol: None,
|
||||||
iommu: false,
|
iommu: false,
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
sgx_epc: None,
|
sgx_epc: None,
|
||||||
@@ -945,6 +1077,8 @@ mod unit_tests {
|
|||||||
platform: None,
|
platform: None,
|
||||||
tpm: None,
|
tpm: None,
|
||||||
preserved_fds: None,
|
preserved_fds: None,
|
||||||
|
landlock_enable: false,
|
||||||
|
landlock_rules: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected_vm_config, result_vm_config);
|
assert_eq!(expected_vm_config, result_vm_config);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "test_infra"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Cloud Hypervisor Authors"]
|
authors = ["The Cloud Hypervisor Authors"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
name = "test_infra"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
libc = "0.2.153"
|
libc = "0.2.155"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
serde = { version = "1.0.197", features = ["rc", "derive"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.120"
|
||||||
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
|
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
|
||||||
vmm-sys-util = "0.12.1"
|
vmm-sys-util = "0.12.1"
|
||||||
wait-timeout = "0.2.0"
|
wait-timeout = "0.2.0"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user