mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49a389ff12 | ||
|
|
2a9b60dcce | ||
|
|
bbd8d3b71d | ||
|
|
b9163bf431 | ||
|
|
909e1bc3f0 | ||
|
|
4f18476cca | ||
|
|
3d7661664d | ||
|
|
50bac1694f | ||
|
|
dd8a5a7de8 | ||
|
|
dba92c28f7 | ||
|
|
4e298d1abf | ||
|
|
149c342867 | ||
|
|
4054a49e2d | ||
|
|
1001d807ff | ||
|
|
e4a5219f53 | ||
|
|
a458351d8b | ||
|
|
5a27bf878c | ||
|
|
310dafb4d7 | ||
|
|
78895dcc37 | ||
|
|
1757d83db3 | ||
|
|
9cef779cc7 | ||
|
|
a10d1ed78e | ||
|
|
dc7f0a42bc | ||
|
|
53098ae40f | ||
|
|
51cf346828 | ||
|
|
0042447fb9 | ||
|
|
e8c330e220 | ||
|
|
ba78e331c6 | ||
|
|
b6d2ac2c2e | ||
|
|
f6fb442fa3 | ||
|
|
0c2f2d3ec1 | ||
|
|
584c088094 | ||
|
|
5b715f483d | ||
|
|
1e602bd9a6 | ||
|
|
6b57f301db | ||
|
|
cd0db3146f | ||
|
|
ac3ef0849e | ||
|
|
bd8db86b0c | ||
|
|
7b5f06788a | ||
|
|
9a7f278716 | ||
|
|
fb457954bb | ||
|
|
c4063d26be | ||
|
|
9006013c60 | ||
|
|
30cf1eed5e | ||
|
|
c6b023e58d | ||
|
|
46f0e0d14a | ||
|
|
261bfac4d4 | ||
|
|
ec9b9ab542 | ||
|
|
8c9b34c9f1 | ||
|
|
5b822191c0 | ||
|
|
ab7b294688 | ||
|
|
7e419784cd | ||
|
|
48bed965c0 | ||
|
|
0d6cef4521 | ||
|
|
81f8a27ef6 | ||
|
|
e3bd5e9b35 | ||
|
|
80b2c98a68 | ||
|
|
3c05626ad1 | ||
|
|
906580ee92 | ||
|
|
c6889388a3 | ||
|
|
f38fc94115 | ||
|
|
6ecca29aa6 | ||
|
|
2ef04671be | ||
|
|
453bc31994 | ||
|
|
6e6966df8f | ||
|
|
f32c2638f6 | ||
|
|
fbe1cd6404 | ||
|
|
778a72f52f | ||
|
|
76256230d6 | ||
|
|
8cd80ea36b | ||
|
|
5e937c8b88 | ||
|
|
710535343b | ||
|
|
59c5b0a1cd | ||
|
|
c13019d5b5 | ||
|
|
c5774685f4 | ||
|
|
2df8d2fa1e | ||
|
|
dd00bd6ef2 | ||
|
|
2752149bb0 | ||
|
|
1bc74e43f8 | ||
|
|
8acaeb5c82 | ||
|
|
5eaf510f90 | ||
|
|
df1d6eaaee | ||
|
|
a6a3d247da | ||
|
|
838a857311 | ||
|
|
95229b24ab | ||
|
|
71b0eb3c21 | ||
|
|
337cbf3d33 | ||
|
|
675140f627 | ||
|
|
894a4dee6e | ||
|
|
f57d00cd66 | ||
|
|
59a1719b6e | ||
|
|
39ab10244b | ||
|
|
b5d856b31a |
4
.github/workflows/integration-arm64.yaml
vendored
4
.github/workflows/integration-arm64.yaml
vendored
@@ -6,7 +6,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 120
|
||||
name: Tests (ARM64)
|
||||
runs-on: bookworm-arm64
|
||||
steps:
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Load openvswitch module
|
||||
run: sudo modprobe openvswitch
|
||||
- name: Run integration tests (musl)
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 60
|
||||
run: scripts/dev_cli.sh tests --integration --libc musl
|
||||
- name: Install Azure CLI
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
sudo apt install -y docker-ce docker-ce-cli
|
||||
- name: Run rate-limiter integration tests
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 20
|
||||
run: scripts/dev_cli.sh tests --integration-rate-limiter
|
||||
- name: Skipping build for PR
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
32
.github/workflows/package-consistency.yaml
vendored
Normal file
32
.github/workflows/package-consistency.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Cloud Hypervisor Consistency
|
||||
on: [pull_request, merge_group]
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Rust VMM Consistency Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt install -y python3
|
||||
|
||||
- name: Install Rust toolchain stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Check Rust VMM Package Consistency of root Workspace
|
||||
run: python3 scripts/package-consistency-check.py github.com/rust-vmm
|
||||
|
||||
- name: Check Rust VMM Package Consistency of fuzz Workspace
|
||||
run: |
|
||||
pushd fuzz
|
||||
python3 ../scripts/package-consistency-check.py github.com/rust-vmm
|
||||
popd
|
||||
39
.github/workflows/preview-riscv64.yaml
vendored
Normal file
39
.github/workflows/preview-riscv64.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Cloud Hypervisor RISC-V 64-bit Preview
|
||||
on: [pull_request, merge_group]
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Cargo
|
||||
runs-on: riscv64-qemu-host
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
module:
|
||||
- hypervisor
|
||||
- arch
|
||||
- vm-allocator
|
||||
- devices
|
||||
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.77.0
|
||||
|
||||
- name: Build ${{ matrix.module }} Module (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo rustc --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
|
||||
- name: Clippy ${{ matrix.module }} Module (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo clippy --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
|
||||
- name: Test ${{ matrix.module }} Module (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo test --locked -p ${{ matrix.module }} --no-default-features --features "kvm"
|
||||
|
||||
- name: Check no files were modified
|
||||
run: test -z "$(git status --porcelain)"
|
||||
346
Cargo.lock
generated
346
Cargo.lock
generated
@@ -7,23 +7,23 @@ name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#e268627630839bd22f1c13e7e81ec70c7e9b73d6"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
@@ -57,9 +57,9 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
@@ -85,15 +85,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.87"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
@@ -115,7 +115,7 @@ dependencies = [
|
||||
"linux-loader",
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"uuid",
|
||||
"vm-fdt",
|
||||
"vm-memory",
|
||||
@@ -125,9 +125,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb"
|
||||
checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
@@ -204,9 +204,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.2.2"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a53fc6301894e04a92cb2584fedde80cb25ba8e02d9dc39d4a87d036e22f397d"
|
||||
checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-io",
|
||||
@@ -219,7 +219,6 @@ dependencies = [
|
||||
"futures-lite",
|
||||
"rustix",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -235,9 +234,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-signal"
|
||||
version = "0.2.6"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda"
|
||||
checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"async-lock",
|
||||
@@ -248,7 +247,7 @@ dependencies = [
|
||||
"rustix",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -259,9 +258,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.80"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -282,17 +281,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -330,7 +329,7 @@ dependencies = [
|
||||
"remain",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"uuid",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
@@ -421,7 +420,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "42.0.0"
|
||||
version = "43.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -440,7 +439,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"test_infra",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"tpm",
|
||||
"tracer",
|
||||
"vm-memory",
|
||||
@@ -477,9 +476,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -575,7 +574,7 @@ dependencies = [
|
||||
"num_enum",
|
||||
"pci",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"tpm",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
@@ -720,9 +719,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3"
|
||||
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"pin-project-lite",
|
||||
@@ -741,9 +740,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
@@ -753,9 +752,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -796,9 +795,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
@@ -907,9 +906,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -920,9 +919,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
@@ -959,6 +958,7 @@ name = "hypervisor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"concat-idents",
|
||||
@@ -974,7 +974,7 @@ dependencies = [
|
||||
"mshv-ioctls",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -1007,9 +1007,9 @@ dependencies = [
|
||||
"igvm_defs",
|
||||
"open-enum",
|
||||
"range_map_vec",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"tracing",
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1021,7 +1021,7 @@ dependencies = [
|
||||
"bitfield-struct",
|
||||
"open-enum",
|
||||
"static_assertions",
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1085,20 +1085,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.9.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2efe3f1a4437bffe000e6297a593b98184213cd27486776c335f95ab53d48e3a"
|
||||
checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"vmm-sys-util",
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92c2176b91f68903b54ac8c6185bada7d607ca6110998976ff15c032f88a7d39"
|
||||
checksum = "337d1afa126368bbd6a5c328048f71a69a737e9afe7e436b392a8f8d770c9171"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"kvm-bindings",
|
||||
@@ -1114,7 +1114,7 @@ checksum = "dafb8a4afee64f167eb2b52d32f0eea002e41a7a6450e68c799c8ec3a81a634c"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"libc",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1125,9 +1125,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -1167,9 +1167,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d379d0089d0fbf4161c35a4fdfd76125923f1a93632c49195f5372b4c0b1472"
|
||||
checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
@@ -1182,9 +1182,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
@@ -1228,11 +1228,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1243,25 +1243,27 @@ checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
|
||||
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?tag=v0.3.0#fda05380ea4c68b807996299d5ffb2854ca6d01d"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0cb5031f3243a7459b7c13d960d25420980874eebda816db24ce6077e21d43"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num_enum",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"vmm-sys-util",
|
||||
"zerocopy",
|
||||
"zerocopy 0.8.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rust-vmm/mshv?tag=v0.3.0#fda05380ea4c68b807996299d5ffb2854ca6d01d"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89abe853221fa6f14ad4066affb9abda241a03d65622887d5794e1422d0bd75a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
"thiserror",
|
||||
"thiserror 2.0.6",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
@@ -1296,7 +1298,7 @@ dependencies = [
|
||||
"rate_limiter",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
@@ -1355,9 +1357,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.1"
|
||||
version = "0.36.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
|
||||
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -1399,9 +1401,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.102"
|
||||
version = "0.9.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1500,7 +1502,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vfio-bindings",
|
||||
"vfio-ioctls",
|
||||
"vfio_user",
|
||||
@@ -1520,15 +1522,15 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"test_infra",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@@ -1549,9 +1551,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "pnet"
|
||||
@@ -1665,7 +1667,7 @@ version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1738,7 +1740,7 @@ dependencies = [
|
||||
"epoll",
|
||||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
@@ -1768,14 +1770,14 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.5"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1785,9 +1787,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.7"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1796,9 +1798,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "remain"
|
||||
@@ -2006,9 +2008,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2059,7 +2061,16 @@ version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2073,6 +2084,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thousands"
|
||||
version = "0.2.0"
|
||||
@@ -2105,7 +2127,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
@@ -2210,14 +2232,14 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#a51a4746b0d317bfc21fa49d40f9287f3b8137fd"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#e71efe41d59adf23377f7066a49903c6c19f5ac3"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -2225,7 +2247,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#a51a4746b0d317bfc21fa49d40f9287f3b8137fd"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#e71efe41d59adf23377f7066a49903c6c19f5ac3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -2234,7 +2256,7 @@ dependencies = [
|
||||
"log",
|
||||
"mshv-bindings",
|
||||
"mshv-ioctls",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vfio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -2243,7 +2265,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#bf7d7e851b604d8414a7960fb9137b59fc42421d"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#3febcdd3fa2531623865663ca1721e1962ed9979"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
@@ -2251,7 +2273,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vfio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -2259,9 +2281,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c1c4c6c9f79fbe3150d9a403008ca416d34c489897effdda28b646f09900aad"
|
||||
version = "0.12.1"
|
||||
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
@@ -2272,9 +2293,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost-user-backend"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73768c8584e0be5ed8feb063785910cabe3f1af6661a5953fd3247fa611ddfaf"
|
||||
version = "0.16.1"
|
||||
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
@@ -2324,9 +2344,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-bindings"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68d0df4f5ad79b1dc81b5913ac737e24a84dcd5100f36ed953a1faec18aba241"
|
||||
checksum = "1711e61c00f8cb450bd15368152a1e37a12ef195008ddc7d0f4812f9e2b30a68"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-devices"
|
||||
@@ -2350,7 +2370,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"serial_buffer",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vhost",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
@@ -2364,9 +2384,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb1761348d3b5e82131379b9373435b48dc8333100bff3f1cdf9cc541a0ad83"
|
||||
checksum = "872e2f3fbd70a7e6f01689720cce3d5c2c5efe52b484dd07b674246ada0e9a8d"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
@@ -2390,7 +2410,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"hypervisor",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vfio-ioctls",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
@@ -2403,13 +2423,13 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ef5bd734f5f66fb0772
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.15.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a320fc11792e063174402ff444aa3c80363cbf1e31c47b5ef74124406c334ce6"
|
||||
checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -2420,7 +2440,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
@@ -2475,7 +2495,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serial_buffer",
|
||||
"signal-hook",
|
||||
"thiserror",
|
||||
"thiserror 1.0.62",
|
||||
"tracer",
|
||||
"uuid",
|
||||
"vfio-ioctls",
|
||||
@@ -2489,7 +2509,7 @@ dependencies = [
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
"zbus",
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2610,7 +2630,16 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2630,17 +2659,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.4",
|
||||
"windows_aarch64_msvc 0.52.4",
|
||||
"windows_i686_gnu 0.52.4",
|
||||
"windows_i686_msvc 0.52.4",
|
||||
"windows_x86_64_gnu 0.52.4",
|
||||
"windows_x86_64_gnullvm 0.52.4",
|
||||
"windows_x86_64_msvc 0.52.4",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2651,9 +2681,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@@ -2663,9 +2693,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -2675,9 +2705,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -2687,9 +2723,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -2699,9 +2735,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@@ -2711,9 +2747,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -2723,9 +2759,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
@@ -2738,12 +2774,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xdg-home"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8"
|
||||
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2815,7 +2851,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
"zerocopy-derive 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a4e33e6dce36f2adba29746927f8e848ba70989fdb61c772773bbdda8b5d6a7"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2829,6 +2874,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cd137b4cc21bde6ecce3bbbb3350130872cda0be2c6888874279ea76e17d4c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "4.2.0"
|
||||
|
||||
25
Cargo.toml
25
Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
license = "Apache-2.0 AND BSD-3-Clause"
|
||||
name = "cloud-hypervisor"
|
||||
version = "42.0.0"
|
||||
version = "43.0.0"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
# Policy on MSRV (see #4318):
|
||||
@@ -29,14 +29,14 @@ inherits = "release"
|
||||
strip = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
api_client = { path = "api_client" }
|
||||
clap = { version = "4.5.13", features = ["string"] }
|
||||
dhat = { version = "0.3.3", optional = true }
|
||||
epoll = "4.3.3"
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = { version = "0.4.22", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = { workspace = true }
|
||||
@@ -103,18 +103,19 @@ members = [
|
||||
|
||||
[workspace.dependencies]
|
||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
kvm-bindings = "0.9.1"
|
||||
kvm-ioctls = "0.18.0"
|
||||
linux-loader = "0.12.0"
|
||||
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", tag = "v0.3.0" }
|
||||
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", tag = "v0.3.0" }
|
||||
kvm-bindings = "0.10.0"
|
||||
kvm-ioctls = "0.19.0"
|
||||
linux-loader = "0.13.0"
|
||||
mshv-bindings = "0.3.2"
|
||||
mshv-ioctls = "0.3.2"
|
||||
seccompiler = "0.4.0"
|
||||
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main" }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
|
||||
vhost = "0.12.0"
|
||||
virtio-bindings = "0.2.2"
|
||||
virtio-queue = "0.13.0"
|
||||
vhost = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
|
||||
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost", rev = "d983ae0" }
|
||||
virtio-bindings = "0.2.4"
|
||||
virtio-queue = "0.14.0"
|
||||
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }
|
||||
vm-memory = "0.15.0"
|
||||
vm-memory = "0.16.1"
|
||||
vmm-sys-util = "0.12.1"
|
||||
|
||||
@@ -11,10 +11,10 @@ sev_snp = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
byteorder = "1.5.0"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
linux-loader = { workspace = true, features = ["bzimage", "elf", "pe"] }
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
@@ -24,6 +24,6 @@ vm-memory = { workspace = true, features = ["backend-bitmap", "backend-mmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { workspace = true, features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||
fdt_parser = { version = "0.1.5", package = "fdt" }
|
||||
vm-fdt = { workspace = true }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright © 2020, Oracle and/or its affiliates.
|
||||
//
|
||||
@@ -5,7 +6,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Implements platform specific functionality.
|
||||
//! Supported platforms: x86_64, aarch64.
|
||||
//! Supported platforms: x86_64, aarch64, riscv64.
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@@ -32,6 +33,9 @@ pub enum Error {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[error("Platform specific error (aarch64): {0:?}")]
|
||||
PlatformSpecific(aarch64::Error),
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
#[error("Platform specific error (riscv64): {0:?}")]
|
||||
PlatformSpecific(riscv64::Error),
|
||||
#[error("The memory map table extends past the end of guest memory")]
|
||||
MemmapTablePastRamEnd,
|
||||
#[error("Error writing memory map table to guest memory")]
|
||||
@@ -85,6 +89,17 @@ pub use aarch64::{
|
||||
layout::IRQ_BASE, uefi, EntryPoint, _NSIG,
|
||||
};
|
||||
|
||||
/// Module for riscv64 related functionality.
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod riscv64;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub use riscv64::{
|
||||
arch_memory_regions, configure_system, configure_vcpu, fdt::DeviceInfoForFdt,
|
||||
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
|
||||
layout::IRQ_BASE, EntryPoint, _NSIG,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86_64;
|
||||
|
||||
@@ -132,7 +147,7 @@ pub enum DeviceType {
|
||||
/// Device Type: Virtio.
|
||||
Virtio(u32),
|
||||
/// Device Type: Serial.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
Serial,
|
||||
/// Device Type: RTC.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@@ -153,7 +168,7 @@ impl fmt::Display for DeviceType {
|
||||
|
||||
/// Structure to describe MMIO device information
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
pub struct MmioDeviceInfo {
|
||||
pub addr: u64,
|
||||
pub len: u64,
|
||||
@@ -162,7 +177,7 @@ pub struct MmioDeviceInfo {
|
||||
|
||||
/// Structure to describe PCI space information
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
pub struct PciSpaceInfo {
|
||||
pub pci_segment_id: u16,
|
||||
pub mmio_config_address: u64,
|
||||
@@ -170,7 +185,7 @@ pub struct PciSpaceInfo {
|
||||
pub pci_device_space_size: u64,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
impl DeviceInfoForFdt for MmioDeviceInfo {
|
||||
fn addr(&self) -> u64 {
|
||||
self.addr
|
||||
|
||||
484
arch/src/riscv64/fdt.rs
Normal file
484
arch/src/riscv64/fdt.rs
Normal file
@@ -0,0 +1,484 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the THIRD-PARTY file.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{cmp, result, str};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use hypervisor::arch::riscv64::aia::Vaia;
|
||||
use thiserror::Error;
|
||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||
|
||||
use super::super::{DeviceType, GuestMemoryMmap, InitramfsConfig};
|
||||
use super::layout::{
|
||||
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
|
||||
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||
};
|
||||
use crate::PciSpaceInfo;
|
||||
|
||||
const AIA_APLIC_PHANDLE: u32 = 1;
|
||||
const AIA_IMSIC_PHANDLE: u32 = 2;
|
||||
const CPU_INTC_BASE_PHANDLE: u32 = 3;
|
||||
const CPU_BASE_PHANDLE: u32 = 256 + CPU_INTC_BASE_PHANDLE;
|
||||
// Read the documentation specified when appending the root node to the FDT.
|
||||
const ADDRESS_CELLS: u32 = 0x2;
|
||||
const SIZE_CELLS: u32 = 0x2;
|
||||
|
||||
// From https://elixir.bootlin.com/linux/v6.10/source/include/dt-bindings/interrupt-controller/irq.h#L14
|
||||
const _IRQ_TYPE_EDGE_RISING: u32 = 1;
|
||||
const IRQ_TYPE_LEVEL_HI: u32 = 4;
|
||||
|
||||
const S_MODE_EXT_IRQ: u32 = 9;
|
||||
|
||||
/// Trait for devices to be added to the Flattened Device Tree.
|
||||
pub trait DeviceInfoForFdt {
|
||||
/// Returns the address where this device will be loaded.
|
||||
fn addr(&self) -> u64;
|
||||
/// Returns the associated interrupt for this device.
|
||||
fn irq(&self) -> u32;
|
||||
/// Returns the amount of memory that needs to be reserved for this device.
|
||||
fn length(&self) -> u64;
|
||||
}
|
||||
|
||||
/// Errors thrown while configuring the Flattened Device Tree for riscv64.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Failure in writing FDT in memory.
|
||||
#[error("Failure in writing FDT in memory: {0}")]
|
||||
WriteFdtToMemory(GuestMemoryError),
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Creates the flattened device tree for this riscv64 VM.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline: &str,
|
||||
num_vcpu: u32,
|
||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||
aia_device: &Arc<Mutex<dyn Vaia>>,
|
||||
initrd: &Option<InitramfsConfig>,
|
||||
pci_space_info: &[PciSpaceInfo],
|
||||
) -> FdtWriterResult<Vec<u8>> {
|
||||
// Allocate stuff necessary for the holding the blob.
|
||||
let mut fdt = FdtWriter::new()?;
|
||||
|
||||
// For an explanation why these nodes were introduced in the blob take a look at
|
||||
// https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4
|
||||
// In chapter 3.
|
||||
|
||||
// Header or the root node as per above mentioned documentation.
|
||||
let root_node = fdt.begin_node("")?;
|
||||
fdt.property_string("compatible", "linux,dummy-virt")?;
|
||||
// For info on #address-cells and size-cells resort to Table 3.1 Root Node
|
||||
// Properties
|
||||
fdt.property_u32("#address-cells", ADDRESS_CELLS)?;
|
||||
fdt.property_u32("#size-cells", SIZE_CELLS)?;
|
||||
create_cpu_nodes(&mut fdt, num_vcpu)?;
|
||||
create_memory_node(&mut fdt, guest_mem)?;
|
||||
create_chosen_node(&mut fdt, cmdline, initrd)?;
|
||||
create_aia_node(&mut fdt, aia_device)?;
|
||||
create_devices_node(&mut fdt, device_info)?;
|
||||
create_pci_nodes(&mut fdt, pci_space_info)?;
|
||||
|
||||
// End Header node.
|
||||
fdt.end_node(root_node)?;
|
||||
|
||||
let fdt_final = fdt.finish()?;
|
||||
|
||||
Ok(fdt_final)
|
||||
}
|
||||
|
||||
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
// Write FDT to memory.
|
||||
guest_mem
|
||||
.write_slice(fdt_final.as_slice(), super::layout::FDT_START)
|
||||
.map_err(Error::WriteFdtToMemory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Following are the auxiliary function for creating the different nodes that we append to our FDT.
|
||||
fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> FdtWriterResult<()> {
|
||||
// See https://elixir.bootlin.com/linux/v6.10/source/Documentation/devicetree/bindings/riscv/cpus.yaml
|
||||
let cpus = fdt.begin_node("cpus")?;
|
||||
// As per documentation, on RISC-V 64-bit systems value should be set to 1.
|
||||
fdt.property_u32("#address-cells", 0x01)?;
|
||||
fdt.property_u32("#size-cells", 0x0)?;
|
||||
// TODO: Retrieve CPU frequency from cpu timer regs
|
||||
let timebase_frequency: u32 = 0x989680;
|
||||
fdt.property_u32("timebase-frequency", timebase_frequency)?;
|
||||
|
||||
for cpu_index in 0..num_cpus {
|
||||
let cpu = fdt.begin_node(&format!("cpu@{:x}", cpu_index))?;
|
||||
fdt.property_string("device_type", "cpu")?;
|
||||
fdt.property_string("compatible", "riscv")?;
|
||||
fdt.property_string("mmu-type", "sv48")?;
|
||||
fdt.property_string("riscv,isa", "rv64imafdc_smaia_ssaia")?;
|
||||
fdt.property_string("status", "okay")?;
|
||||
fdt.property_u32("reg", cpu_index)?;
|
||||
fdt.property_u32("phandle", CPU_BASE_PHANDLE + cpu_index)?;
|
||||
|
||||
// interrupt controller node
|
||||
let intc_node = fdt.begin_node("interrupt-controller")?;
|
||||
fdt.property_string("compatible", "riscv,cpu-intc")?;
|
||||
fdt.property_u32("#interrupt-cells", 1u32)?;
|
||||
fdt.property_null("#interrupt-controller")?;
|
||||
fdt.property_u32("phandle", CPU_INTC_BASE_PHANDLE + cpu_index)?;
|
||||
fdt.end_node(intc_node)?;
|
||||
|
||||
fdt.end_node(cpu)?;
|
||||
}
|
||||
|
||||
fdt.end_node(cpus)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_memory_node(fdt: &mut FdtWriter, guest_mem: &GuestMemoryMmap) -> FdtWriterResult<()> {
|
||||
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
|
||||
let ram_regions = {
|
||||
let mut ram_regions = Vec::new();
|
||||
let mut current_start = guest_mem
|
||||
.iter()
|
||||
.next()
|
||||
.map(GuestMemoryRegion::start_addr)
|
||||
.expect("GuestMemory must have one memory region at least")
|
||||
.raw_value();
|
||||
let mut current_end = current_start;
|
||||
|
||||
for (start, size) in guest_mem
|
||||
.iter()
|
||||
.map(|m| (m.start_addr().raw_value(), m.len()))
|
||||
{
|
||||
if current_end == start {
|
||||
// This zone is continuous with the previous one.
|
||||
current_end += size;
|
||||
} else {
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
current_start = start;
|
||||
current_end = start + size;
|
||||
}
|
||||
}
|
||||
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
ram_regions
|
||||
};
|
||||
|
||||
let mut mem_reg_property = Vec::new();
|
||||
for region in ram_regions {
|
||||
let mem_size = region.1 - region.0;
|
||||
mem_reg_property.push(region.0);
|
||||
mem_reg_property.push(mem_size);
|
||||
}
|
||||
|
||||
let ram_start = super::layout::RAM_START.raw_value();
|
||||
let memory_node_name = format!("memory@{:x}", ram_start);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_property)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_chosen_node(
|
||||
fdt: &mut FdtWriter,
|
||||
cmdline: &str,
|
||||
initrd: &Option<InitramfsConfig>,
|
||||
) -> FdtWriterResult<()> {
|
||||
let chosen_node = fdt.begin_node("chosen")?;
|
||||
fdt.property_string("bootargs", cmdline)?;
|
||||
|
||||
if let Some(initrd_config) = initrd {
|
||||
let initrd_start = initrd_config.address.raw_value();
|
||||
let initrd_end = initrd_config.address.raw_value() + initrd_config.size as u64;
|
||||
fdt.property_u64("linux,initrd-start", initrd_start)?;
|
||||
fdt.property_u64("linux,initrd-end", initrd_end)?;
|
||||
}
|
||||
|
||||
fdt.end_node(chosen_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_aia_node(fdt: &mut FdtWriter, aia_device: &Arc<Mutex<dyn Vaia>>) -> FdtWriterResult<()> {
|
||||
// IMSIC
|
||||
if aia_device.lock().unwrap().msi_compatible() {
|
||||
use super::layout::IMSIC_START;
|
||||
let imsic_name = format!("imsics@{:x}", IMSIC_START.0);
|
||||
let imsic_node = fdt.begin_node(&imsic_name)?;
|
||||
|
||||
fdt.property_string(
|
||||
"compatible",
|
||||
aia_device.lock().unwrap().imsic_compatibility(),
|
||||
)?;
|
||||
let imsic_reg_prop = aia_device.lock().unwrap().imsic_properties();
|
||||
fdt.property_array_u32("reg", &imsic_reg_prop)?;
|
||||
fdt.property_u32("#interrupt-cells", 0u32)?;
|
||||
fdt.property_null("interrupt-controller")?;
|
||||
fdt.property_null("msi-controller")?;
|
||||
// TODO complete num-ids
|
||||
fdt.property_u32("riscv,num-ids", 2047u32)?;
|
||||
fdt.property_u32("phandle", AIA_IMSIC_PHANDLE)?;
|
||||
|
||||
let mut irq_cells = Vec::new();
|
||||
let num_cpus = aia_device.lock().unwrap().vcpu_count();
|
||||
for i in 0..num_cpus {
|
||||
irq_cells.push(CPU_INTC_BASE_PHANDLE + i);
|
||||
irq_cells.push(S_MODE_EXT_IRQ);
|
||||
}
|
||||
fdt.property_array_u32("interrupts-extended", &irq_cells)?;
|
||||
|
||||
fdt.end_node(imsic_node)?;
|
||||
}
|
||||
|
||||
// APLIC
|
||||
use super::layout::APLIC_START;
|
||||
let aplic_name = format!("aplic@{:x}", APLIC_START.0);
|
||||
let aplic_node = fdt.begin_node(&aplic_name)?;
|
||||
|
||||
fdt.property_string(
|
||||
"compatible",
|
||||
aia_device.lock().unwrap().aplic_compatibility(),
|
||||
)?;
|
||||
let reg_cells = aia_device.lock().unwrap().aplic_properties();
|
||||
fdt.property_array_u32("reg", ®_cells)?;
|
||||
fdt.property_u32("#interrupt-cells", 2u32)?;
|
||||
fdt.property_null("interrupt-controller")?;
|
||||
// TODO complete num-srcs
|
||||
fdt.property_u32("riscv,num-sources", 96u32)?;
|
||||
fdt.property_u32("phandle", AIA_APLIC_PHANDLE)?;
|
||||
fdt.property_u32("msi-parent", AIA_IMSIC_PHANDLE)?;
|
||||
|
||||
fdt.end_node(aplic_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_serial_node<T: DeviceInfoForFdt + Clone + Debug>(
|
||||
fdt: &mut FdtWriter,
|
||||
dev_info: &T,
|
||||
) -> FdtWriterResult<()> {
|
||||
let serial_reg_prop = [dev_info.addr(), dev_info.length()];
|
||||
let irq = [dev_info.irq() - IRQ_BASE, IRQ_TYPE_LEVEL_HI];
|
||||
|
||||
let serial_node = fdt.begin_node(&format!("serial@{:x}", dev_info.addr()))?;
|
||||
fdt.property_string("compatible", "ns16550a")?;
|
||||
fdt.property_array_u64("reg", &serial_reg_prop)?;
|
||||
fdt.property_u32("clock-frequency", 3686400)?;
|
||||
fdt.property_u32("interrupt-parent", AIA_APLIC_PHANDLE)?;
|
||||
fdt.property_array_u32("interrupts", &irq)?;
|
||||
fdt.end_node(serial_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||
fdt: &mut FdtWriter,
|
||||
dev_info: &HashMap<(DeviceType, String), T, S>,
|
||||
) -> FdtWriterResult<()> {
|
||||
for ((device_type, _device_id), info) in dev_info {
|
||||
match device_type {
|
||||
DeviceType::Serial => create_serial_node(fdt, info)?,
|
||||
DeviceType::Virtio(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pci_nodes(fdt: &mut FdtWriter, pci_device_info: &[PciSpaceInfo]) -> FdtWriterResult<()> {
|
||||
// Add node for PCIe controller.
|
||||
// See Documentation/devicetree/bindings/pci/host-generic-pci.txt in the kernel
|
||||
// and https://elinux.org/Device_Tree_Usage.
|
||||
// In multiple PCI segments setup, each PCI segment needs a PCI node.
|
||||
for pci_device_info_elem in pci_device_info.iter() {
|
||||
// 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
|
||||
// could fall below 4G.
|
||||
// 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.
|
||||
let (pci_device_base_64bit, pci_device_size_64bit) =
|
||||
if pci_device_info_elem.pci_device_space_start < PCI_HIGH_BASE.raw_value() {
|
||||
(
|
||||
PCI_HIGH_BASE.raw_value(),
|
||||
pci_device_info_elem.pci_device_space_size
|
||||
- (PCI_HIGH_BASE.raw_value() - pci_device_info_elem.pci_device_space_start),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
pci_device_info_elem.pci_device_space_start,
|
||||
pci_device_info_elem.pci_device_space_size,
|
||||
)
|
||||
};
|
||||
// There is no specific requirement of the 32bit MMIO range, and
|
||||
// therefore at least we can make these ranges 4K aligned.
|
||||
let pci_device_size_32bit: u64 =
|
||||
MEM_32BIT_DEVICES_SIZE / ((1 << 12) * pci_device_info.len() as u64) * (1 << 12);
|
||||
let pci_device_base_32bit: u64 = MEM_32BIT_DEVICES_START.0
|
||||
+ pci_device_size_32bit * pci_device_info_elem.pci_segment_id as u64;
|
||||
|
||||
let ranges = [
|
||||
// io addresses. Since AArch64 will not use IO address,
|
||||
// we can set the same IO address range for every segment.
|
||||
0x1000000,
|
||||
0_u32,
|
||||
0_u32,
|
||||
(MEM_PCI_IO_START.0 >> 32) as u32,
|
||||
MEM_PCI_IO_START.0 as u32,
|
||||
(MEM_PCI_IO_SIZE >> 32) as u32,
|
||||
MEM_PCI_IO_SIZE as u32,
|
||||
// mmio addresses
|
||||
0x2000000, // (ss = 10: 32-bit memory space)
|
||||
(pci_device_base_32bit >> 32) as u32, // PCI address
|
||||
pci_device_base_32bit as u32,
|
||||
(pci_device_base_32bit >> 32) as u32, // CPU address
|
||||
pci_device_base_32bit as u32,
|
||||
(pci_device_size_32bit >> 32) as u32, // size
|
||||
pci_device_size_32bit as u32,
|
||||
// device addresses
|
||||
0x3000000, // (ss = 11: 64-bit memory space)
|
||||
(pci_device_base_64bit >> 32) as u32, // PCI address
|
||||
pci_device_base_64bit as u32,
|
||||
(pci_device_base_64bit >> 32) as u32, // CPU address
|
||||
pci_device_base_64bit as u32,
|
||||
(pci_device_size_64bit >> 32) as u32, // size
|
||||
pci_device_size_64bit as u32,
|
||||
];
|
||||
let bus_range = [0, 0]; // Only bus 0
|
||||
let reg = [
|
||||
pci_device_info_elem.mmio_config_address,
|
||||
PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
|
||||
];
|
||||
// See kernel document Documentation/devicetree/bindings/pci/pci-msi.txt
|
||||
let msi_map = [
|
||||
// rid-base: A single cell describing the first RID matched by the entry.
|
||||
0x0,
|
||||
// msi-controller: A single phandle to an MSI controller.
|
||||
AIA_IMSIC_PHANDLE,
|
||||
// msi-base: An msi-specifier describing the msi-specifier produced for the
|
||||
// first RID matched by the entry.
|
||||
(pci_device_info_elem.pci_segment_id as u32) << 8,
|
||||
// length: A single cell describing how many consecutive RIDs are matched
|
||||
// following the rid-base.
|
||||
0x100,
|
||||
];
|
||||
|
||||
let pci_node_name = format!("pci@{:x}", pci_device_info_elem.mmio_config_address);
|
||||
let pci_node = fdt.begin_node(&pci_node_name)?;
|
||||
|
||||
fdt.property_string("compatible", "pci-host-ecam-generic")?;
|
||||
fdt.property_string("device_type", "pci")?;
|
||||
fdt.property_array_u32("ranges", &ranges)?;
|
||||
fdt.property_array_u32("bus-range", &bus_range)?;
|
||||
fdt.property_u32(
|
||||
"linux,pci-domain",
|
||||
pci_device_info_elem.pci_segment_id as u32,
|
||||
)?;
|
||||
fdt.property_u32("#address-cells", 3)?;
|
||||
fdt.property_u32("#size-cells", 2)?;
|
||||
fdt.property_array_u64("reg", ®)?;
|
||||
fdt.property_u32("#interrupt-cells", 1)?;
|
||||
fdt.property_null("interrupt-map")?;
|
||||
fdt.property_null("interrupt-map-mask")?;
|
||||
fdt.property_null("dma-coherent")?;
|
||||
fdt.property_array_u32("msi-map", &msi_map)?;
|
||||
fdt.property_u32("msi-parent", AIA_IMSIC_PHANDLE)?;
|
||||
|
||||
fdt.end_node(pci_node)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Parse the DTB binary and print for debugging
|
||||
pub fn print_fdt(dtb: &[u8]) {
|
||||
match fdt_parser::Fdt::new(dtb) {
|
||||
Ok(fdt) => {
|
||||
if let Some(root) = fdt.find_node("/") {
|
||||
debug!("Printing the FDT:");
|
||||
print_node(root, 0);
|
||||
} else {
|
||||
debug!("Failed to find root node in FDT for debugging.");
|
||||
}
|
||||
}
|
||||
Err(_) => debug!("Failed to parse FDT for debugging."),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
|
||||
debug!("{:indent$}{}/", "", node.name, indent = n_spaces);
|
||||
for property in node.properties() {
|
||||
let name = property.name;
|
||||
|
||||
// If the property is 'compatible', its value requires special handling.
|
||||
// The u8 array could contain multiple null-terminated strings.
|
||||
// We copy the original array and simply replace all 'null' characters with spaces.
|
||||
let value = if name == "compatible" {
|
||||
let mut compatible = vec![0u8; 256];
|
||||
let handled_value = property
|
||||
.value
|
||||
.iter()
|
||||
.map(|&c| if c == 0 { b' ' } else { c })
|
||||
.collect::<Vec<_>>();
|
||||
let len = cmp::min(255, handled_value.len());
|
||||
compatible[..len].copy_from_slice(&handled_value[..len]);
|
||||
compatible[..(len + 1)].to_vec()
|
||||
} else {
|
||||
property.value.to_vec()
|
||||
};
|
||||
let value = &value;
|
||||
|
||||
// Now the value can be either:
|
||||
// - A null-terminated C string, or
|
||||
// - Binary data
|
||||
// We follow a very simple logic to present the value:
|
||||
// - At first, try to convert it to CStr and print,
|
||||
// - If failed, print it as u32 array.
|
||||
let value_result = match CStr::from_bytes_with_nul(value) {
|
||||
Ok(value_cstr) => match value_cstr.to_str() {
|
||||
Ok(value_str) => Some(value_str),
|
||||
Err(_e) => None,
|
||||
},
|
||||
Err(_e) => None,
|
||||
};
|
||||
|
||||
if let Some(value_str) = value_result {
|
||||
debug!(
|
||||
"{:indent$}{} : {:#?}",
|
||||
"",
|
||||
name,
|
||||
value_str,
|
||||
indent = (n_spaces + 2)
|
||||
);
|
||||
} else {
|
||||
let mut array = Vec::with_capacity(256);
|
||||
array.resize(value.len() / 4, 0u32);
|
||||
BigEndian::read_u32_into(value, &mut array);
|
||||
debug!(
|
||||
"{:indent$}{} : {:X?}",
|
||||
"",
|
||||
name,
|
||||
array,
|
||||
indent = (n_spaces + 2)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// Print children nodes if there is any
|
||||
for child in node.children() {
|
||||
print_node(child, n_spaces + 2);
|
||||
}
|
||||
}
|
||||
105
arch/src/riscv64/layout.rs
Normal file
105
arch/src/riscv64/layout.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//
|
||||
// Memory layout of RISC-V 64-bit guest:
|
||||
//
|
||||
// Physical +---------------------------------------------------------------+
|
||||
// address | |
|
||||
// end | |
|
||||
// ~ ~ ~ ~
|
||||
// | |
|
||||
// | Highmem PCI MMIO space |
|
||||
// | |
|
||||
// RAM end +---------------------------------------------------------------+
|
||||
// (dynamic, | |
|
||||
// including | |
|
||||
// hotplug ~ ~ ~ ~
|
||||
// memory) | |
|
||||
// | DRAM |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// 1 GB +---------------------------------------------------------------+
|
||||
// | |
|
||||
// | PCI MMCONFIG space |
|
||||
// | |
|
||||
// 768 MB +---------------------------------------------------------------+
|
||||
// | |
|
||||
// | |
|
||||
// | PCI MMIO space |
|
||||
// | |
|
||||
// 256 MB +---------------------------------------------------------------|
|
||||
// | |
|
||||
// | Legacy devices space |
|
||||
// | |
|
||||
// 128 MB +---------------------------------------------------------------|
|
||||
// | |
|
||||
// | IMSICs |
|
||||
// | |
|
||||
// 64 MB +---------------------------------------------------------------+
|
||||
// | |
|
||||
// | APLICs |
|
||||
// | |
|
||||
// 0 GB +---------------------------------------------------------------+
|
||||
//
|
||||
//
|
||||
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
/// AIA related devices
|
||||
/// See https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h
|
||||
/// 0x0 ~ 0x0400_0000 (64 MiB) resides APLICs
|
||||
pub const APLIC_START: GuestAddress = GuestAddress(0);
|
||||
pub const APLIC_SIZE: u64 = 0x4000;
|
||||
|
||||
/// 0x0400_0000 ~ 0x0800_0000 (64 MiB) resides IMSICs
|
||||
pub const IMSIC_START: GuestAddress = GuestAddress(0x0400_0000);
|
||||
pub const IMSIC_SIZE: u64 = 0x1000;
|
||||
|
||||
/// Below this address will reside the AIA, above this address will reside the MMIO devices.
|
||||
const MAPPED_IO_START: GuestAddress = GuestAddress(0x0800_0000);
|
||||
|
||||
/// Space 0x0800_0000 ~ 0x1000_0000 is reserved for legacy devices.
|
||||
pub const LEGACY_SERIAL_MAPPED_IO_START: GuestAddress = MAPPED_IO_START;
|
||||
|
||||
/// Space 0x0905_0000 ~ 0x0906_0000 is reserved for pcie io address
|
||||
pub const MEM_PCI_IO_START: GuestAddress = GuestAddress(0x0905_0000);
|
||||
pub const MEM_PCI_IO_SIZE: u64 = 0x1_0000;
|
||||
|
||||
/// Starting from 0x1000_0000 (256MiB) to 0x3000_0000 (768MiB) is used for PCIE MMIO
|
||||
pub const MEM_32BIT_DEVICES_START: GuestAddress = GuestAddress(0x1000_0000);
|
||||
pub const MEM_32BIT_DEVICES_SIZE: u64 = 0x2000_0000;
|
||||
|
||||
/// PCI MMCONFIG space (start: after the device space at 768MiB, length: 256MiB)
|
||||
pub const PCI_MMCONFIG_START: GuestAddress = GuestAddress(0x3000_0000);
|
||||
pub const PCI_MMCONFIG_SIZE: u64 = 256 << 20;
|
||||
// One bus with potentially 256 devices (32 slots x 8 functions).
|
||||
pub const PCI_MMIO_CONFIG_SIZE_PER_SEGMENT: u64 = 4096 * 256;
|
||||
|
||||
/// Start of RAM.
|
||||
pub const RAM_START: GuestAddress = GuestAddress(0x4000_0000);
|
||||
|
||||
/// Kernel command line maximum size on RISC-V.
|
||||
/// See https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/setup.h
|
||||
pub const CMDLINE_MAX_SIZE: usize = 1024;
|
||||
|
||||
/// FDT is at the beginning of RAM.
|
||||
pub const FDT_START: GuestAddress = RAM_START;
|
||||
pub const FDT_MAX_SIZE: u64 = 0x1_0000;
|
||||
|
||||
/// Kernel start after FDT
|
||||
pub const KERNEL_START: GuestAddress = GuestAddress(RAM_START.0 + FDT_MAX_SIZE);
|
||||
|
||||
/// Pci high memory base
|
||||
pub const PCI_HIGH_BASE: GuestAddress = GuestAddress(0x2_0000_0000);
|
||||
|
||||
/// First usable interrupt on riscv64
|
||||
pub const IRQ_BASE: u32 = 0;
|
||||
|
||||
// As per https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/asm/kvm_host.h#L31
|
||||
/// Number of supported interrupts
|
||||
pub const IRQ_NUM: u32 = 1023;
|
||||
173
arch/src/riscv64/mod.rs
Normal file
173
arch/src/riscv64/mod.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
|
||||
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// Module for the flattened device tree.
|
||||
pub mod fdt;
|
||||
/// Layout for this riscv64 system.
|
||||
pub mod layout;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use hypervisor::arch::riscv64::aia::Vaia;
|
||||
use log::{log_enabled, Level};
|
||||
use thiserror::Error;
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||
|
||||
pub use self::fdt::DeviceInfoForFdt;
|
||||
use crate::{DeviceType, GuestMemoryMmap, PciSpaceInfo, RegionType};
|
||||
|
||||
pub const _NSIG: i32 = 65;
|
||||
|
||||
/// Errors thrown while configuring riscv64 system.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Failed to create a FDT.
|
||||
#[error("Failed to create a FDT")]
|
||||
SetupFdt,
|
||||
|
||||
/// Failed to write FDT to memory.
|
||||
#[error("Failed to write FDT to memory: {0}")]
|
||||
WriteFdtToMemory(fdt::Error),
|
||||
|
||||
/// Failed to create a AIA.
|
||||
#[error("Failed to create a AIA")]
|
||||
SetupAia,
|
||||
|
||||
/// Failed to compute the initramfs address.
|
||||
#[error("Failed to compute the initramfs address")]
|
||||
InitramfsAddress,
|
||||
|
||||
/// Error configuring the general purpose registers
|
||||
#[error("Error configuring the general purpose registers: {0}")]
|
||||
RegsConfiguration(hypervisor::HypervisorCpuError),
|
||||
}
|
||||
|
||||
impl From<Error> for super::Error {
|
||||
fn from(e: Error) -> super::Error {
|
||||
super::Error::PlatformSpecific(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
/// Specifies the entry point address where the guest must start
|
||||
/// executing code.
|
||||
pub struct EntryPoint {
|
||||
/// Address in guest memory where the guest must start execution
|
||||
pub entry_addr: GuestAddress,
|
||||
}
|
||||
|
||||
/// Configure the specified VCPU, and return its MPIDR.
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &Arc<dyn hypervisor::Vcpu>,
|
||||
id: u8,
|
||||
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||
) -> super::Result<()> {
|
||||
if let Some((kernel_entry_point, _guest_memory)) = boot_setup {
|
||||
vcpu.setup_regs(
|
||||
id,
|
||||
kernel_entry_point.entry_addr.raw_value(),
|
||||
layout::FDT_START.raw_value(),
|
||||
)
|
||||
.map_err(Error::RegsConfiguration)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
vec![
|
||||
// 0 MiB ~ 256 MiB: AIA and legacy devices
|
||||
(
|
||||
GuestAddress(0),
|
||||
layout::MEM_32BIT_DEVICES_START.0 as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
// 256 MiB ~ 768 MiB: MMIO space
|
||||
(
|
||||
layout::MEM_32BIT_DEVICES_START,
|
||||
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||
RegionType::SubRegion,
|
||||
),
|
||||
// 768 MiB ~ 1 GiB: reserved. The leading 256M for PCIe MMCONFIG space
|
||||
(
|
||||
layout::PCI_MMCONFIG_START,
|
||||
layout::PCI_MMCONFIG_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
// 1GiB ~ inf: RAM
|
||||
(layout::RAM_START, usize::MAX, RegionType::Ram),
|
||||
]
|
||||
}
|
||||
|
||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline: &str,
|
||||
num_vcpu: u32,
|
||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||
initrd: &Option<super::InitramfsConfig>,
|
||||
pci_space_info: &[PciSpaceInfo],
|
||||
aia_device: &Arc<Mutex<dyn Vaia>>,
|
||||
) -> super::Result<()> {
|
||||
let fdt_final = fdt::create_fdt(
|
||||
guest_mem,
|
||||
cmdline,
|
||||
num_vcpu,
|
||||
device_info,
|
||||
aia_device,
|
||||
initrd,
|
||||
pci_space_info,
|
||||
)
|
||||
.map_err(|_| Error::SetupFdt)?;
|
||||
|
||||
if log_enabled!(Level::Debug) {
|
||||
fdt::print_fdt(&fdt_final);
|
||||
}
|
||||
|
||||
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the memory address where the initramfs could be loaded.
|
||||
pub fn initramfs_load_addr(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
initramfs_size: usize,
|
||||
) -> super::Result<u64> {
|
||||
let round_to_pagesize = |size| (size + (super::PAGE_SIZE - 1)) & !(super::PAGE_SIZE - 1);
|
||||
match guest_mem
|
||||
.last_addr()
|
||||
.checked_sub(round_to_pagesize(initramfs_size) as u64 - 1)
|
||||
{
|
||||
Some(offset) => {
|
||||
if guest_mem.address_in_range(offset) {
|
||||
Ok(offset.raw_value())
|
||||
} else {
|
||||
Err(super::Error::PlatformSpecific(Error::InitramfsAddress))
|
||||
}
|
||||
}
|
||||
None => Err(super::Error::PlatformSpecific(Error::InitramfsAddress)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_host_cpu_phys_bits(_hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
|
||||
40
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram() {
|
||||
let regions = arch_memory_regions();
|
||||
assert_eq!(4, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ io_uring = ["dep:io-uring"]
|
||||
byteorder = "1.5.0"
|
||||
crc-any = "2.4.4"
|
||||
io-uring = { version = "0.6.4", optional = true }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
remain = "0.2.14"
|
||||
serde = { version = "1.0.208", features = ["derive"] }
|
||||
|
||||
@@ -6,13 +6,13 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { workspace = true }
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "2.6.0"
|
||||
byteorder = "1.5.0"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
num_enum = "0.7.2"
|
||||
pci = { path = "../pci" }
|
||||
@@ -29,9 +29,10 @@ vm-memory = { workspace = true, features = [
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { workspace = true }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||
arch = { path = "../arch" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
kvm = ["arch/kvm"]
|
||||
pvmemcontrol = []
|
||||
|
||||
144
devices/src/aia.rs
Normal file
144
devices/src/aia.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
// Copyright 2020, ARM Limited.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
use super::interrupt_controller::{Error, InterruptController};
|
||||
extern crate arch;
|
||||
use std::result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use arch::layout;
|
||||
use hypervisor::arch::riscv64::aia::{Vaia, VaiaConfig};
|
||||
use hypervisor::{AiaState, CpuState};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
|
||||
LegacyIrqSourceConfig, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_memory::address::Address;
|
||||
use vm_migration::{Migratable, Pausable, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
// Reserve 32 IRQs for legacy devices.
|
||||
pub const IRQ_LEGACY_BASE: usize = layout::IRQ_BASE as usize;
|
||||
pub const IRQ_LEGACY_COUNT: usize = 32;
|
||||
// TODO: AIA snapshotting is not yet completed.
|
||||
pub const _AIA_SNAPSHOT_ID: &str = "";
|
||||
|
||||
// Aia (Advance Interrupt Architecture) struct provides all the functionality of a
|
||||
// AIA device. It wraps a hypervisor-emulated AIA device (Vaia) provided by the
|
||||
// `hypervisor` crate.
|
||||
// Aia struct also implements InterruptController to provide interrupt delivery
|
||||
// service.
|
||||
pub struct Aia {
|
||||
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
|
||||
// The hypervisor agnostic virtual AIA
|
||||
vaia: Arc<Mutex<dyn Vaia>>,
|
||||
}
|
||||
|
||||
impl Aia {
|
||||
pub fn new(
|
||||
vcpu_count: u8,
|
||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
) -> Result<Aia> {
|
||||
let interrupt_source_group = interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
base: IRQ_LEGACY_BASE as InterruptIndex,
|
||||
count: IRQ_LEGACY_COUNT as InterruptIndex,
|
||||
})
|
||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||
|
||||
let vaia = vm
|
||||
.create_vaia(Aia::create_default_config(vcpu_count as u64))
|
||||
.map_err(Error::CreateAia)?;
|
||||
|
||||
let aia = Aia {
|
||||
interrupt_source_group,
|
||||
vaia,
|
||||
};
|
||||
aia.enable()?;
|
||||
|
||||
Ok(aia)
|
||||
}
|
||||
|
||||
pub fn restore_vaia(
|
||||
&mut self,
|
||||
state: Option<AiaState>,
|
||||
_saved_vcpu_states: &[CpuState],
|
||||
) -> Result<()> {
|
||||
self.vaia
|
||||
.clone()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_state(&state.unwrap())
|
||||
.map_err(Error::RestoreAia)
|
||||
}
|
||||
|
||||
fn enable(&self) -> Result<()> {
|
||||
// Set irqfd for legacy interrupts
|
||||
self.interrupt_source_group
|
||||
.enable()
|
||||
.map_err(Error::EnableInterrupt)?;
|
||||
|
||||
// Set irq_routing for legacy interrupts.
|
||||
// irqchip: Hardcode to 0 as we support only 1 APLIC
|
||||
// pin: Use irq number as pin
|
||||
for i in IRQ_LEGACY_BASE..(IRQ_LEGACY_BASE + IRQ_LEGACY_COUNT) {
|
||||
let config = LegacyIrqSourceConfig {
|
||||
irqchip: 0,
|
||||
pin: (i - IRQ_LEGACY_BASE) as u32,
|
||||
};
|
||||
self.interrupt_source_group
|
||||
.update(
|
||||
i as InterruptIndex,
|
||||
InterruptSourceConfig::LegacyIrq(config),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::EnableInterrupt)?;
|
||||
}
|
||||
|
||||
self.interrupt_source_group
|
||||
.set_gsi()
|
||||
.map_err(Error::EnableInterrupt)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Default config implied by arch::layout
|
||||
pub fn create_default_config(vcpu_count: u64) -> VaiaConfig {
|
||||
VaiaConfig {
|
||||
vcpu_count: vcpu_count as u32,
|
||||
aplic_addr: layout::APLIC_START.raw_value(),
|
||||
imsic_addr: layout::IMSIC_START.raw_value(),
|
||||
nr_irqs: layout::IRQ_NUM,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vaia(&mut self) -> Result<Arc<Mutex<dyn Vaia>>> {
|
||||
Ok(self.vaia.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptController for Aia {
|
||||
// This should be called anytime an interrupt needs to be injected into the
|
||||
// running guest.
|
||||
fn service_irq(&mut self, irq: usize) -> Result<()> {
|
||||
self.interrupt_source_group
|
||||
.trigger(irq as InterruptIndex)
|
||||
.map_err(Error::TriggerInterrupt)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn notifier(&self, irq: usize) -> Option<EventFd> {
|
||||
self.interrupt_source_group.notifier(irq as InterruptIndex)
|
||||
}
|
||||
}
|
||||
|
||||
impl Snapshottable for Aia {}
|
||||
impl Pausable for Aia {}
|
||||
impl Transportable for Aia {}
|
||||
impl Migratable for Aia {}
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
// Copyright 2020, ARM Limited.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
@@ -41,6 +42,14 @@ pub enum Error {
|
||||
/// Failed restoring GIC device.
|
||||
#[error("Failed restoring GIC device: {0}")]
|
||||
RestoreGic(hypervisor::arch::aarch64::gic::Error),
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
/// Failed creating AIA device.
|
||||
#[error("Failed creating AIA device: {0}")]
|
||||
CreateAia(hypervisor::HypervisorVmError),
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
/// Failed restoring AIA device.
|
||||
#[error("Failed restoring AIA device: {0}")]
|
||||
RestoreAia(hypervisor::arch::riscv64::aia::Error),
|
||||
}
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
@@ -67,7 +76,7 @@ pub struct MsiMessage {
|
||||
// Introduce trait InterruptController to uniform the interrupt
|
||||
// service provided for devices.
|
||||
// Device manager uses this trait without caring whether it is a
|
||||
// IOAPIC (X86) or GIC (Arm).
|
||||
// IOAPIC (X86), GIC (Arm) or AIA (RISC-V).
|
||||
pub trait InterruptController: Send {
|
||||
fn service_irq(&mut self, irq: usize) -> Result<()>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
||||
@@ -15,6 +15,8 @@ extern crate event_monitor;
|
||||
extern crate log;
|
||||
|
||||
pub mod acpi;
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod aia;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod debug_console;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@@ -26,6 +28,8 @@ pub mod legacy;
|
||||
#[cfg(feature = "pvmemcontrol")]
|
||||
pub mod pvmemcontrol;
|
||||
pub mod pvpanic;
|
||||
// TODO: TPM is not yet supported
|
||||
#[cfg(not(target_arch = "riscv64"))]
|
||||
pub mod tpm;
|
||||
|
||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||
|
||||
13
docs/api.md
13
docs/api.md
@@ -50,17 +50,12 @@ for more details about the API payloads and responses.
|
||||
|
||||
#### REST API Location and availability
|
||||
|
||||
The REST API is available as soon as the Cloud Hypervisor binary is started,
|
||||
through a local UNIX socket.
|
||||
By default, it is located at `/run/user/{user ID}/cloud-hypervisor.{Cloud Hypervisor PID}`.
|
||||
For example, if you launched Cloud Hypervisor as user ID 1000 and its PID is
|
||||
123456, the Cloud Hypervisor REST API will be available at `/run/user/1000/cloud-hypervisor.123456`.
|
||||
|
||||
The REST API default URL can be overridden through the Cloud Hypervisor
|
||||
option `--api-socket`:
|
||||
The REST API, if enabled, is available as soon as the Cloud Hypervisor binary is started,
|
||||
through either a local UNIX socket as given in the Cloud Hypervisor option `--api-socket path=...`
|
||||
or a fd with `--api-socket fd=...`.
|
||||
|
||||
```
|
||||
$ ./target/debug/cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
|
||||
$ ./target/debug/cloud-hypervisor --api-socket path=/tmp/cloud-hypervisor.sock
|
||||
Cloud Hypervisor Guest
|
||||
API server: /tmp/cloud-hypervisor.sock
|
||||
vCPUs: 1
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
# Live Migration
|
||||
|
||||
This document gives two examples of how to use the live migration
|
||||
support in Cloud Hypervisor:
|
||||
This document gives examples of how to use the live migration support
|
||||
in Cloud Hypervisor:
|
||||
|
||||
1. local migration - migrating between two VMs running on the same
|
||||
machine;
|
||||
1. nested-vm migration - migrating between two nested VMs whose host VMs
|
||||
are running on the same machine.
|
||||
1. local migration - migrating a VM from one Cloud Hypervisor instance to another on the same machine;
|
||||
1. remote migration - migrating a VM between two machines;
|
||||
|
||||
> :warning: These examples place sockets /tmp. This is done for
|
||||
> simplicity and should not be done in production.
|
||||
|
||||
## Local Migration (Suitable for Live Upgrade of VMM)
|
||||
|
||||
Launch the source VM (on the host machine):
|
||||
```bash
|
||||
|
||||
```console
|
||||
$ target/release/cloud-hypervisor
|
||||
--kernel ~/workloads/vmlinux \
|
||||
--disk path=~/workloads/focal.raw \
|
||||
@@ -20,17 +23,20 @@ $ target/release/cloud-hypervisor
|
||||
```
|
||||
|
||||
Launch the destination VM from the same directory (on the host machine):
|
||||
```bash
|
||||
|
||||
```console
|
||||
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
|
||||
```
|
||||
|
||||
Get ready for receiving migration for the destination VM (on the host machine):
|
||||
```bash
|
||||
|
||||
```console
|
||||
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock
|
||||
```
|
||||
|
||||
Start to send migration for the source VM (on the host machine):
|
||||
```bash
|
||||
|
||||
```console
|
||||
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration --local unix:/tmp/sock
|
||||
```
|
||||
|
||||
@@ -38,102 +44,139 @@ When the above commands completed, the source VM should be successfully
|
||||
migrated to the destination VM. Now the destination VM is running while
|
||||
the source VM is terminated gracefully.
|
||||
|
||||
## Nested-VM Migration
|
||||
## Remote Migration
|
||||
|
||||
Launch VM 1 (on the host machine) with an extra virtio-blk device for
|
||||
exposing a guest image for the nested source VM:
|
||||
> Note: the example below also attached an additional virtio-blk device
|
||||
> with a dummy image for testing purpose (which is optional).
|
||||
```bash
|
||||
$ head -c 1M < /dev/urandom > tmp.img # create a dummy image for testing
|
||||
$ sudo /target/release/cloud-hypervisor \
|
||||
In this example, we will migrate a VM from one machine (`src`) to
|
||||
another (`dst`) across the network. To keep it simple, we will use a
|
||||
minimal VM setup without storage.
|
||||
|
||||
### Preparation
|
||||
|
||||
Make sure that `src` and `dst` can reach each other via the
|
||||
network. You should be able to ping each machine. Also each machine
|
||||
should have an open TCP port.
|
||||
|
||||
You will need a kernel and initramfs for a minimal Linux system. For
|
||||
this example, we will use the Debian netboot image.
|
||||
|
||||
Place the kernel and initramfs into the _same directory_ on both
|
||||
machines. This is important for the migration to succeed. We will use
|
||||
`/var/images`:
|
||||
|
||||
```console
|
||||
src $ export DEBIAN=https://ftp.debian.org/debian/dists/stable/main/installer-amd64/current/images/netboot/debian-installer/amd64
|
||||
src $ mkdir -p /var/images
|
||||
src $ curl $DEBIAN/linux > /var/images/linux
|
||||
src $ curl $DEBIAN/initrd.gz > /var/images/initrd
|
||||
```
|
||||
|
||||
Repeat the above steps on the destination host.
|
||||
|
||||
### Unix Socket Migration
|
||||
|
||||
If Unix socket is selected for migration, we can tunnel traffic through "socat".
|
||||
|
||||
#### Starting the Receiver VM
|
||||
|
||||
On the receiver side, we prepare an empty VM:
|
||||
|
||||
```console
|
||||
dst $ cloud-hypervisor --api-socket /tmp/api
|
||||
```
|
||||
|
||||
In a different terminal, configure the VM as a migration target:
|
||||
|
||||
```console
|
||||
dst $ ch-remote --api-socket=/tmp/api receive-migration unix:/tmp/sock
|
||||
```
|
||||
|
||||
In yet another terminal, forward TCP connections to the Unix domain socket:
|
||||
|
||||
```console
|
||||
dst $ socat TCP-LISTEN:{port},reuseaddr UNIX-CLIENT:/tmp/sock
|
||||
```
|
||||
|
||||
#### Starting the Sender VM
|
||||
|
||||
Let's start the VM on the source machine:
|
||||
|
||||
```console
|
||||
src $ cloud-hypervisor \
|
||||
--serial tty --console off \
|
||||
--cpus boot=1 --memory size=512M \
|
||||
--kernel vmlinux \
|
||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
||||
--disk path=focal-1.raw path=focal-nested.raw path=tmp.img\
|
||||
--net ip=192.168.101.1
|
||||
--cpus boot=2 --memory size=4G \
|
||||
--kernel /var/images/linux \
|
||||
--initramfs /var/images/initrd \
|
||||
--cmdline "console=ttyS0" \
|
||||
--api-socket /tmp/api
|
||||
```
|
||||
|
||||
Launch VM 2 (on the host machine) with an extra virtio-blk device for
|
||||
exposing the same guest image for the nested destination VM:
|
||||
```bash
|
||||
$ sudo /target/release/cloud-hypervisor \
|
||||
After a few seconds the VM should be up and you can interact with it.
|
||||
|
||||
#### Performing the Migration
|
||||
|
||||
First, we start `socat`:
|
||||
|
||||
```console
|
||||
src $ socat UNIX-LISTEN:/tmp/sock,reuseaddr TCP:{dst}:{port}
|
||||
```
|
||||
|
||||
> Replace {dst}:{port} with the actual IP address and port of your destination host.
|
||||
|
||||
Then we kick-off the migration itself:
|
||||
|
||||
```console
|
||||
src $ ch-remote --api-socket=/tmp/api send-migration unix:/tmp/sock
|
||||
```
|
||||
|
||||
When the above commands completed, the VM should be successfully
|
||||
migrated to the destination machine without interrupting the workload.
|
||||
|
||||
### TCP Socket Migration
|
||||
|
||||
If TCP socket is selected for migration, we need to consider migrating
|
||||
in a trusted network.
|
||||
|
||||
#### Starting the Receiver VM
|
||||
|
||||
On the receiver side, we prepare an empty VM:
|
||||
|
||||
```console
|
||||
dst $ cloud-hypervisor --api-socket /tmp/api
|
||||
```
|
||||
|
||||
In a different terminal, prepare to receive the migration:
|
||||
|
||||
```console
|
||||
dst $ ch-remote --api-socket=/tmp/api receive-migration tcp:0.0.0.0:{port}
|
||||
```
|
||||
|
||||
#### Starting the Sender VM
|
||||
|
||||
Let's start the VM on the source machine:
|
||||
|
||||
```console
|
||||
src $ cloud-hypervisor \
|
||||
--serial tty --console off \
|
||||
--cpus boot=1 --memory size=512M \
|
||||
--kernel vmlinux \
|
||||
--cmdline "root=/dev/vda1 console=ttyS0" \
|
||||
--disk path=focal-2.raw path=focal-nested.raw path=tmp.img\
|
||||
--net ip=192.168.102.1
|
||||
--cpus boot=2 --memory size=4G \
|
||||
--kernel /var/images/linux \
|
||||
--initramfs /var/images/initrd \
|
||||
--cmdline "console=ttyS0" \
|
||||
--api-socket /tmp/api
|
||||
```
|
||||
|
||||
Launch the nested source VM (inside the guest OS of the VM 1) :
|
||||
```bash
|
||||
vm-1:~$ sudo ./cloud-hypervisor \
|
||||
--serial tty --console off \
|
||||
--memory size=128M \
|
||||
--kernel vmlinux \
|
||||
--cmdline "console=ttyS0 root=/dev/vda1" \
|
||||
--disk path=/dev/vdb path=/dev/vdc \
|
||||
--api-socket=/tmp/api1 \
|
||||
--net ip=192.168.100.1
|
||||
vm-1:~$ # setup the guest network if needed
|
||||
vm-1:~$ sudo ip addr add 192.168.101.2/24 dev ens4
|
||||
vm-1:~$ sudo ip link set up dev ens4
|
||||
vm-1:~$ sudo ip r add default via 192.168.101.1
|
||||
```
|
||||
Optional: Run the guest workload below (on the guest OS of the nested source VM),
|
||||
which performs intensive virtio-blk operations. Now the console of the nested
|
||||
source VM should repeatedly print `"equal"`, and our goal is migrating
|
||||
this VM and the running workload without interruption.
|
||||
```bash
|
||||
#/bin/bash
|
||||
After a few seconds the VM should be up and you can interact with it.
|
||||
|
||||
# On the guest OS of the nested source VM
|
||||
#### Performing the Migration
|
||||
|
||||
input="/dev/vdb"
|
||||
result=$(md5sum $input)
|
||||
tmp=$(md5sum $input)
|
||||
Initiate the Migration over TCP:
|
||||
|
||||
while [[ "$result" == "$tmp" ]]
|
||||
do
|
||||
echo "equal"
|
||||
tmp=$(md5sum $input)
|
||||
done
|
||||
|
||||
echo "not equal"
|
||||
echo "result = $result"
|
||||
echo "tmp = $tmp"
|
||||
```console
|
||||
src $ ch-remote --api-socket=/tmp/api send-migration tcp:{dst}:{port}
|
||||
```
|
||||
|
||||
Launch the nested destination VM (inside the guest OS of the VM 2):
|
||||
```bash
|
||||
vm-2:~$ sudo ./cloud-hypervisor --api-socket=/tmp/api2
|
||||
vm-2:~$ # setup the guest network with the following commands if needed
|
||||
vm-2:~$ sudo ip addr add 192.168.102.2/24 dev ens4
|
||||
vm-2:~$ sudo ip link set up dev ens4
|
||||
vm-2:~$ sudo ip r add default via 192.168.102.1
|
||||
vm-2:~$ ping 192.168.101.2 # This should succeed
|
||||
```
|
||||
> Note: If the above ping failed, please check the iptables rule on the
|
||||
> host machine, e.g. whether the policy for the `FORWARD` chain is set
|
||||
> to `DROP` (which is the default setting configured by Docker).
|
||||
> Replace {dst}:{port} with the actual IP address and port of your destination host.
|
||||
|
||||
Get ready for receiving migration for the nested destination VM (inside
|
||||
the guest OS of the VM 2):
|
||||
```bash
|
||||
vm-2:~$ sudo ./ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock2
|
||||
vm-2:~$ sudo socat TCP-LISTEN:6000,reuseaddr UNIX-CLIENT:/tmp/sock2
|
||||
```
|
||||
|
||||
Start to send migration for the nested source VM (inside the guest OS of
|
||||
the VM 1):
|
||||
```bash
|
||||
vm-1:~$ sudo socat UNIX-LISTEN:/tmp/sock1,reuseaddr TCP:192.168.102.2:6000
|
||||
vm-1:~$ sudo ./ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/sock1
|
||||
```
|
||||
|
||||
When the above commands completed, the source VM should be successfully
|
||||
migrated to the destination VM without interrupting our testing guest
|
||||
workload. Now the destination VM is running the testing guest workload
|
||||
while the source VM is terminated gracefully.
|
||||
After completing the above commands, the source VM will be migrated to
|
||||
the destination host and continue running there. The source VM instance
|
||||
will terminate normally. All ongoing processes and connections within
|
||||
the VM should remain intact after the migration.
|
||||
|
||||
@@ -5,8 +5,8 @@ name = "event_monitor"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
flume = "0.11.0"
|
||||
libc = "0.2.158"
|
||||
flume = "0.11.1"
|
||||
libc = "0.2.167"
|
||||
once_cell = "1.20.2"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.120"
|
||||
|
||||
47
fuzz/Cargo.lock
generated
47
fuzz/Cargo.lock
generated
@@ -5,7 +5,7 @@ version = 3
|
||||
[[package]]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#ca1a473fe73cdd8eb49c1449faad7aaac06f32c2"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#849d5950196f66dd10f2b2606d8fe8c7cb39ec24"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
@@ -361,9 +361,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -500,9 +500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-bindings"
|
||||
version = "0.9.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2efe3f1a4437bffe000e6297a593b98184213cd27486776c335f95ab53d48e3a"
|
||||
checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"vmm-sys-util",
|
||||
@@ -511,9 +511,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kvm-ioctls"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92c2176b91f68903b54ac8c6185bada7d607ca6110998976ff15c032f88a7d39"
|
||||
checksum = "337d1afa126368bbd6a5c328048f71a69a737e9afe7e436b392a8f8d770c9171"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"kvm-bindings",
|
||||
@@ -551,9 +551,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d379d0089d0fbf4161c35a4fdfd76125923f1a93632c49195f5372b4c0b1472"
|
||||
checksum = "870c3814345f050991f99869417779f6062542bcf4ed81db7a1b926ad1306638"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
@@ -662,9 +662,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -1020,7 +1020,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#a51a4746b0d317bfc21fa49d40f9287f3b8137fd"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -1028,7 +1028,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#a51a4746b0d317bfc21fa49d40f9287f3b8137fd"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#b135b8305c2cc8ec333e0cf77a780445cc98dcee"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -1044,7 +1044,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#bf7d7e851b604d8414a7960fb9137b59fc42421d"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#3febcdd3fa2531623865663ca1721e1962ed9979"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
@@ -1060,9 +1060,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c1c4c6c9f79fbe3150d9a403008ca416d34c489897effdda28b646f09900aad"
|
||||
version = "0.12.1"
|
||||
source = "git+https://github.com/rust-vmm/vhost?rev=d983ae0#d983ae07f78663b7d24059667376992460b571a2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
@@ -1073,9 +1072,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-bindings"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68d0df4f5ad79b1dc81b5913ac737e24a84dcd5100f36ed953a1faec18aba241"
|
||||
checksum = "1711e61c00f8cb450bd15368152a1e37a12ef195008ddc7d0f4812f9e2b30a68"
|
||||
|
||||
[[package]]
|
||||
name = "virtio-devices"
|
||||
@@ -1112,9 +1111,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb1761348d3b5e82131379b9373435b48dc8333100bff3f1cdf9cc541a0ad83"
|
||||
checksum = "872e2f3fbd70a7e6f01689720cce3d5c2c5efe52b484dd07b674246ada0e9a8d"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings",
|
||||
@@ -1147,13 +1146,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vm-fdt"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#982fb8d9c8cd7f53520d7e304b39ff307fa3a641"
|
||||
source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#ef5bd734f5f66fb07722d766981adbc915f0d941"
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.15.0"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a320fc11792e063174402ff444aa3c80363cbf1e31c47b5ef74124406c334ce6"
|
||||
checksum = "e2919f87420b6998a131eb7c78843890295e91a3f8f786ccc925c8d387b75121"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
|
||||
@@ -18,15 +18,15 @@ devices = { path = "../devices" }
|
||||
epoll = "4.3.3"
|
||||
libc = "0.2.155"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
linux-loader = { version = "0.12.0", features = ["bzimage", "elf", "pe"] }
|
||||
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.19.0"
|
||||
seccompiler = "0.4.0"
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.13.0"
|
||||
virtio-queue = "0.14.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.15.0"
|
||||
vm-memory = "0.16.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm = { path = "../vmm", features = ["guest_debug"] }
|
||||
|
||||
@@ -12,7 +12,8 @@ sev_snp = ["igvm", "igvm_defs"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
arc-swap = "1.7.1"
|
||||
byteorder = "1.5.0"
|
||||
cfg-if = "1.0.0"
|
||||
concat-idents = "1.1.5"
|
||||
@@ -20,7 +21,7 @@ igvm = { version = "0.3.3", optional = true }
|
||||
igvm_defs = { version = "0.3.1", optional = true }
|
||||
kvm-bindings = { workspace = true, optional = true, features = ["serde"] }
|
||||
kvm-ioctls = { workspace = true, optional = true }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
mshv-bindings = { workspace = true, features = [
|
||||
"fam-wrappers",
|
||||
@@ -33,7 +34,11 @@ serde_with = { version = "3.9.0", default-features = false, features = [
|
||||
] }
|
||||
thiserror = "1.0.62"
|
||||
vfio-ioctls = { workspace = true, default-features = false }
|
||||
vm-memory = { workspace = true, features = ["backend-atomic", "backend-mmap"] }
|
||||
vm-memory = { workspace = true, features = [
|
||||
"backend-atomic",
|
||||
"backend-bitmap",
|
||||
"backend-mmap",
|
||||
] }
|
||||
vmm-sys-util = { workspace = true, features = ["with-serde"] }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
|
||||
|
||||
@@ -21,3 +21,6 @@ pub mod x86;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod aarch64;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod riscv64;
|
||||
|
||||
63
hypervisor/src/arch/riscv64/aia.rs
Normal file
63
hypervisor/src/arch/riscv64/aia.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::any::Any;
|
||||
use std::result;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{AiaState, HypervisorDeviceError, HypervisorVmError};
|
||||
|
||||
/// Errors thrown while setting up the VAIA.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Error while calling KVM ioctl for setting up the global interrupt controller.
|
||||
#[error("Failed creating AIA device: {0}")]
|
||||
CreateAia(HypervisorVmError),
|
||||
/// Error while setting device attributes for the AIA.
|
||||
#[error("Failed setting device attributes for the AIA: {0}")]
|
||||
SetDeviceAttribute(HypervisorDeviceError),
|
||||
/// Error while getting device attributes for the AIA.
|
||||
#[error("Failed getting device attributes for the AIA: {0}")]
|
||||
GetDeviceAttribute(HypervisorDeviceError),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VaiaConfig {
|
||||
pub vcpu_count: u32,
|
||||
pub aplic_addr: u64,
|
||||
pub imsic_addr: u64,
|
||||
pub nr_irqs: u32,
|
||||
}
|
||||
|
||||
/// Hypervisor agnostic interface for a virtualized AIA
|
||||
pub trait Vaia: Send + Sync {
|
||||
/// Returns the compatibility property of APLIC
|
||||
fn aplic_compatibility(&self) -> &str;
|
||||
|
||||
/// Returns an array with APLIC device properties
|
||||
fn aplic_properties(&self) -> [u32; 4];
|
||||
|
||||
/// Returns the compatibility property of IMSIC
|
||||
fn imsic_compatibility(&self) -> &str;
|
||||
|
||||
/// Returns an array with IMSIC device properties
|
||||
fn imsic_properties(&self) -> [u32; 4];
|
||||
|
||||
/// Returns the number of vCPUs this AIA handles
|
||||
fn vcpu_count(&self) -> u32;
|
||||
|
||||
/// Returns whether the AIA device is MSI compatible or not
|
||||
fn msi_compatible(&self) -> bool;
|
||||
|
||||
/// Downcast the trait object to its concrete type.
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Save the state of AiaImsics.
|
||||
fn state(&self) -> Result<AiaState>;
|
||||
|
||||
/// Restore the state of AiaImsics.
|
||||
fn set_state(&mut self, state: &AiaState) -> Result<()>;
|
||||
}
|
||||
5
hypervisor/src/arch/riscv64/mod.rs
Normal file
5
hypervisor/src/arch/riscv64/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod aia;
|
||||
@@ -1,3 +1,5 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// Copyright © 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
@@ -9,6 +11,7 @@
|
||||
//
|
||||
|
||||
use thiserror::Error;
|
||||
#[cfg(not(target_arch = "riscv64"))]
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
@@ -17,6 +20,8 @@ use crate::aarch64::{RegList, VcpuInit};
|
||||
use crate::arch::x86::{CpuIdEntry, FpuState, LapicState, MsrEntry, SpecialRegisters};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
use crate::riscv64::RegList;
|
||||
use crate::{CpuState, MpState, StandardRegisters};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -216,6 +221,16 @@ pub enum HypervisorCpuError {
|
||||
#[error("Failed to set aarch64 core register: {0}")]
|
||||
SetAarchCoreRegister(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting RISC-V 64-bit core register error
|
||||
///
|
||||
#[error("Failed to get riscv64 core register: {0}")]
|
||||
GetRiscvCoreRegister(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting RISC-V 64-bit core register error
|
||||
///
|
||||
#[error("Failed to set riscv64 core register: {0}")]
|
||||
SetRiscvCoreRegister(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting registers list error
|
||||
///
|
||||
#[error("Failed to retrieve list of registers: {0}")]
|
||||
@@ -231,6 +246,16 @@ pub enum HypervisorCpuError {
|
||||
#[error("Failed to set system register: {0}")]
|
||||
SetSysRegister(#[source] anyhow::Error),
|
||||
///
|
||||
/// Getting RISC-V 64-bit non-core register error
|
||||
///
|
||||
#[error("Failed to get non-core register: {0}")]
|
||||
GetNonCoreRegister(#[source] anyhow::Error),
|
||||
///
|
||||
/// Setting RISC-V 64-bit non-core register error
|
||||
///
|
||||
#[error("Failed to set non-core register: {0}")]
|
||||
SetNonCoreRegister(#[source] anyhow::Error),
|
||||
///
|
||||
/// GVA translation error
|
||||
///
|
||||
#[error("Failed to translate GVA: {0}")]
|
||||
@@ -412,6 +437,7 @@ pub trait Vcpu: Send + Sync {
|
||||
///
|
||||
/// Sets debug registers to set hardware breakpoints and/or enable single step.
|
||||
///
|
||||
#[cfg(not(target_arch = "riscv64"))]
|
||||
fn set_guest_debug(&self, _addrs: &[GuestAddress], _singlestep: bool) -> Result<()> {
|
||||
Err(HypervisorCpuError::SetDebugRegs(anyhow!("unimplemented")))
|
||||
}
|
||||
@@ -428,7 +454,7 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Gets a list of the guest registers that are supported for the
|
||||
/// KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
fn get_reg_list(&self, reg_list: &mut RegList) -> Result<()>;
|
||||
///
|
||||
/// Gets the value of a system register
|
||||
@@ -436,9 +462,14 @@ pub trait Vcpu: Send + Sync {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn get_sys_reg(&self, sys_reg: u32) -> Result<u64>;
|
||||
///
|
||||
/// Gets the value of a non-core register on RISC-V 64-bit
|
||||
///
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
fn get_non_core_reg(&self, non_core_reg: u32) -> Result<u64>;
|
||||
///
|
||||
/// Configure core registers for a given CPU.
|
||||
///
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
fn setup_regs(&self, cpu_id: u8, boot_ip: u64, fdt_start: u64) -> Result<()>;
|
||||
///
|
||||
/// Check if the CPU supports PMU
|
||||
|
||||
@@ -118,6 +118,17 @@ pub trait Hypervisor: Send + Sync {
|
||||
fn create_vm_with_type(&self, _vm_type: u64) -> Result<Arc<dyn Vm>> {
|
||||
unreachable!()
|
||||
}
|
||||
///
|
||||
/// Create a Vm of a specific type using the underlying hypervisor, passing memory size
|
||||
/// Return a hypervisor-agnostic Vm trait object
|
||||
///
|
||||
fn create_vm_with_type_and_memory(
|
||||
&self,
|
||||
_vm_type: u64,
|
||||
#[cfg(feature = "sev_snp")] _mem_size: u64,
|
||||
) -> Result<Arc<dyn Vm>> {
|
||||
unreachable!()
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the supported CpuID
|
||||
|
||||
@@ -103,9 +103,8 @@ fn dist_attr_get(gic: &DeviceFd, offset: u32) -> Result<u32> {
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
// get_device_attr should be marked as unsafe, and will be in future.
|
||||
// SAFETY: gic_dist_attr.addr is safe to write to.
|
||||
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
|
||||
unsafe { gic.get_device_attr(&mut gic_dist_attr) }.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
|
||||
@@ -131,9 +130,8 @@ fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
|
||||
addr: &mut num_irq as *mut u32 as u64,
|
||||
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| {
|
||||
unsafe { gic.get_device_attr(&mut nr_irqs_attr) }.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
Ok(num_irq)
|
||||
|
||||
@@ -105,9 +105,8 @@ fn icc_attr_get(gic: &DeviceFd, offset: u64, typer: u64) -> Result<u32> {
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
// get_device_attr should be marked as unsafe, and will be in future.
|
||||
// SAFETY: gic_icc_attr.addr is safe to write to.
|
||||
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
|
||||
unsafe { gic.get_device_attr(&mut gic_icc_attr) }.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
|
||||
|
||||
@@ -49,13 +49,10 @@ fn gicv3_its_attr_get(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u6
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
// get_device_attr should be marked as unsafe, and will be in future.
|
||||
// SAFETY: gicv3_its_attr.addr is safe to write to.
|
||||
its_device
|
||||
.get_device_attr(&mut gicv3_its_attr)
|
||||
.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
unsafe { its_device.get_device_attr(&mut gicv3_its_attr) }.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
@@ -121,9 +121,8 @@ fn redist_attr_get(gic: &DeviceFd, offset: u32, typer: u64) -> Result<u32> {
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
// get_device_attr should be marked as unsafe, and will be in future.
|
||||
// SAFETY: gic_redist_attr.addr is safe to write to.
|
||||
gic.get_device_attr(&mut gic_redist_attr).map_err(|e| {
|
||||
unsafe { gic.get_device_attr(&mut gic_redist_attr) }.map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})?;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
274
hypervisor/src/kvm/riscv64/aia.rs
Normal file
274
hypervisor/src/kvm/riscv64/aia.rs
Normal file
@@ -0,0 +1,274 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
use kvm_ioctls::DeviceFd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::arch::riscv64::aia::{Error, Result, Vaia, VaiaConfig};
|
||||
use crate::device::HypervisorDeviceError;
|
||||
use crate::kvm::KvmVm;
|
||||
use crate::Vm;
|
||||
|
||||
pub struct KvmAiaImsics {
|
||||
/// The KVM device for the Aia
|
||||
device: DeviceFd,
|
||||
|
||||
/// AIA APLIC address
|
||||
aplic_addr: u64,
|
||||
|
||||
/// AIA IMSIC address
|
||||
imsic_addr: u64,
|
||||
|
||||
/// Number of CPUs handled by the device
|
||||
vcpu_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub struct AiaImsicsState {}
|
||||
|
||||
impl KvmAiaImsics {
|
||||
/// Device trees specific constants
|
||||
|
||||
fn version() -> u32 {
|
||||
kvm_bindings::kvm_device_type_KVM_DEV_TYPE_RISCV_AIA
|
||||
}
|
||||
|
||||
/// Setup the device-specific attributes
|
||||
fn init_device_attributes(&mut self, nr_irqs: u32) -> Result<()> {
|
||||
// AIA part attributes
|
||||
// Getting the working mode of RISC-V AIA, defaults to EMUL, passible
|
||||
// variants are EMUL, HW_ACCL, AUTO
|
||||
let mut aia_mode = kvm_bindings::KVM_DEV_RISCV_AIA_MODE_EMUL;
|
||||
Self::get_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_MODE),
|
||||
&mut aia_mode as *mut u32 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
// Report AIA MODE
|
||||
|
||||
// Setting up the number of wired interrupt sources
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_SRCS),
|
||||
&nr_irqs as *const u32 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
// Getting the number of ids
|
||||
let mut aia_nr_ids: u32 = 0;
|
||||
Self::get_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_IDS),
|
||||
&mut aia_nr_ids as *mut u32 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
// Report NR_IDS
|
||||
|
||||
// Setting up hart_bits
|
||||
let max_hart_index = self.vcpu_count as u64 - 1;
|
||||
let hart_bits = std::cmp::max(64 - max_hart_index.leading_zeros(), 1);
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CONFIG_HART_BITS),
|
||||
&hart_bits as *const u32 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
// Designate addresses of APLIC and IMSICS
|
||||
|
||||
// Setting up RISC-V APLIC
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_ADDR,
|
||||
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_ADDR_APLIC),
|
||||
&self.aplic_addr as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
|
||||
// Helpers to calculate address and attribute of IMSIC of each vCPU
|
||||
let riscv_imsic_addr_of = |cpu_index: u32| -> u64 {
|
||||
self.imsic_addr + (cpu_index * kvm_bindings::KVM_DEV_RISCV_IMSIC_SIZE) as u64
|
||||
};
|
||||
let riscv_imsic_attr_of = |cpu_index: u32| -> u64 { cpu_index as u64 };
|
||||
|
||||
// Setting up RISC-V IMSICs
|
||||
for cpu_index in 0..self.vcpu_count {
|
||||
let cpu_imsic_addr = riscv_imsic_addr_of(cpu_index);
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_ADDR,
|
||||
riscv_imsic_attr_of(cpu_index),
|
||||
&cpu_imsic_addr as *const u64 as u64,
|
||||
0,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Finalizing the AIA device
|
||||
Self::set_device_attribute(
|
||||
&self.device,
|
||||
kvm_bindings::KVM_DEV_RISCV_AIA_GRP_CTRL,
|
||||
u64::from(kvm_bindings::KVM_DEV_RISCV_AIA_CTRL_INIT),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a KVM Vaia device
|
||||
fn create_device(vm: &KvmVm) -> Result<DeviceFd> {
|
||||
let mut aia_device = kvm_bindings::kvm_create_device {
|
||||
type_: Self::version(),
|
||||
fd: 0,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
let device_fd = vm
|
||||
.create_device(&mut aia_device)
|
||||
.map_err(Error::CreateAia)?;
|
||||
|
||||
// We know for sure this is a KVM fd
|
||||
Ok(device_fd.to_kvm().unwrap())
|
||||
}
|
||||
|
||||
/// Get an AIA device attribute
|
||||
fn get_device_attribute(
|
||||
device: &DeviceFd,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
let mut attr = kvm_bindings::kvm_device_attr {
|
||||
flags,
|
||||
group,
|
||||
attr,
|
||||
addr,
|
||||
};
|
||||
// SAFETY: attr.addr is safe to write to.
|
||||
unsafe {
|
||||
device.get_device_attr(&mut attr).map_err(|e| {
|
||||
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an AIA device attribute
|
||||
fn set_device_attribute(
|
||||
device: &DeviceFd,
|
||||
group: u32,
|
||||
attr: u64,
|
||||
addr: u64,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
let attr = kvm_bindings::kvm_device_attr {
|
||||
flags,
|
||||
group,
|
||||
attr,
|
||||
addr,
|
||||
};
|
||||
device.set_device_attr(&attr).map_err(|e| {
|
||||
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Method to initialize the AIA device
|
||||
pub fn new(vm: &dyn Vm, config: VaiaConfig) -> Result<KvmAiaImsics> {
|
||||
// This is inside KVM module
|
||||
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
|
||||
|
||||
let vaia = Self::create_device(vm)?;
|
||||
|
||||
let mut aia_device = KvmAiaImsics {
|
||||
device: vaia,
|
||||
vcpu_count: config.vcpu_count,
|
||||
aplic_addr: config.aplic_addr,
|
||||
imsic_addr: config.imsic_addr,
|
||||
};
|
||||
|
||||
aia_device.init_device_attributes(config.nr_irqs)?;
|
||||
|
||||
Ok(aia_device)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vaia for KvmAiaImsics {
|
||||
fn aplic_compatibility(&self) -> &str {
|
||||
"riscv,aplic"
|
||||
}
|
||||
|
||||
fn aplic_properties(&self) -> [u32; 4] {
|
||||
[
|
||||
0,
|
||||
self.aplic_addr as u32,
|
||||
0,
|
||||
kvm_bindings::KVM_DEV_RISCV_APLIC_SIZE,
|
||||
]
|
||||
}
|
||||
|
||||
fn imsic_compatibility(&self) -> &str {
|
||||
"riscv,imsics"
|
||||
}
|
||||
|
||||
fn imsic_properties(&self) -> [u32; 4] {
|
||||
[
|
||||
0,
|
||||
self.imsic_addr as u32,
|
||||
0,
|
||||
kvm_bindings::KVM_DEV_RISCV_IMSIC_SIZE * self.vcpu_count,
|
||||
]
|
||||
}
|
||||
|
||||
fn vcpu_count(&self) -> u32 {
|
||||
self.vcpu_count
|
||||
}
|
||||
|
||||
fn msi_compatible(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn as_any_concrete_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
/// Save the state of AIA.
|
||||
fn state(&self) -> Result<AiaImsicsState> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Restore the state of AIA_IMSICs.
|
||||
fn set_state(&mut self, _state: &AiaImsicsState) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::arch::riscv64::aia::VaiaConfig;
|
||||
use crate::kvm::KvmAiaImsics;
|
||||
|
||||
fn create_test_vaia_config() -> VaiaConfig {
|
||||
VaiaConfig {
|
||||
vcpu_count: 1,
|
||||
aplic_addr: 0xd000000,
|
||||
imsic_addr: 0x2800000,
|
||||
nr_irqs: 256,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_aia() {
|
||||
let hv = crate::new().unwrap();
|
||||
let vm = hv.create_vm().unwrap();
|
||||
|
||||
assert!(KvmAiaImsics::new(&*vm, create_test_vaia_config()).is_ok());
|
||||
}
|
||||
}
|
||||
163
hypervisor/src/kvm/riscv64/mod.rs
Normal file
163
hypervisor/src/kvm/riscv64/mod.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod aia;
|
||||
|
||||
use kvm_bindings::{
|
||||
kvm_mp_state, kvm_one_reg, kvm_riscv_core, KVM_REG_RISCV_CORE, KVM_REG_RISCV_TYPE_MASK,
|
||||
KVM_REG_SIZE_MASK, KVM_REG_SIZE_U64,
|
||||
};
|
||||
pub use kvm_bindings::{kvm_one_reg as Register, RegList};
|
||||
pub use kvm_ioctls::{Cap, Kvm};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::kvm::{KvmError, KvmResult};
|
||||
|
||||
// This macro gets the offset of a structure (i.e `str`) member (i.e `field`) without having
|
||||
// an instance of that structure.
|
||||
#[macro_export]
|
||||
macro_rules! _offset_of {
|
||||
($str:ty, $field:ident) => {{
|
||||
let tmp: std::mem::MaybeUninit<$str> = std::mem::MaybeUninit::uninit();
|
||||
let base = tmp.as_ptr();
|
||||
|
||||
// Avoid warnings when nesting `unsafe` blocks.
|
||||
#[allow(unused_unsafe)]
|
||||
// SAFETY: The pointer is valid and aligned, just not initialised. Using `addr_of` ensures
|
||||
// that we don't actually read from `base` (which would be UB) nor create an intermediate
|
||||
// reference.
|
||||
let member = unsafe { core::ptr::addr_of!((*base).$field) } as *const u8;
|
||||
|
||||
// Avoid warnings when nesting `unsafe` blocks.
|
||||
#[allow(unused_unsafe)]
|
||||
// SAFETY: The two pointers are within the same allocated object `tmp`. All requirements
|
||||
// from offset_from are upheld.
|
||||
unsafe {
|
||||
member.offset_from(base as *const u8) as usize
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! offset_of {
|
||||
($reg_struct:ty, $field:ident) => {
|
||||
$crate::_offset_of!($reg_struct, $field)
|
||||
};
|
||||
($outer_reg_struct:ty, $outer_field:ident, $($inner_reg_struct:ty, $inner_field:ident), +) => {
|
||||
$crate::_offset_of!($outer_reg_struct, $outer_field) + offset_of!($($inner_reg_struct, $inner_field), +)
|
||||
};
|
||||
}
|
||||
|
||||
// Following are macros that help with getting the ID of a riscv64 register, including config registers, core registers and timer registers.
|
||||
// The register of core registers are wrapped in the `user_regs_struct` structure. See:
|
||||
// https://elixir.bootlin.com/linux/v6.10/source/arch/riscv/include/uapi/asm/kvm.h#L62
|
||||
|
||||
// Get the ID of a register
|
||||
#[macro_export]
|
||||
macro_rules! riscv64_reg_id {
|
||||
($reg_type: tt, $offset: tt) => {
|
||||
// The core registers of an riscv64 machine are represented
|
||||
// in kernel by the `kvm_riscv_core` structure:
|
||||
//
|
||||
// struct kvm_riscv_core {
|
||||
// struct user_regs_struct regs;
|
||||
// unsigned long mode;
|
||||
// };
|
||||
//
|
||||
// struct user_regs_struct {
|
||||
// unsigned long pc;
|
||||
// unsigned long ra;
|
||||
// unsigned long sp;
|
||||
// unsigned long gp;
|
||||
// unsigned long tp;
|
||||
// unsigned long t0;
|
||||
// unsigned long t1;
|
||||
// unsigned long t2;
|
||||
// unsigned long s0;
|
||||
// unsigned long s1;
|
||||
// unsigned long a0;
|
||||
// unsigned long a1;
|
||||
// unsigned long a2;
|
||||
// unsigned long a3;
|
||||
// unsigned long a4;
|
||||
// unsigned long a5;
|
||||
// unsigned long a6;
|
||||
// unsigned long a7;
|
||||
// unsigned long s2;
|
||||
// unsigned long s3;
|
||||
// unsigned long s4;
|
||||
// unsigned long s5;
|
||||
// unsigned long s6;
|
||||
// unsigned long s7;
|
||||
// unsigned long s8;
|
||||
// unsigned long s9;
|
||||
// unsigned long s10;
|
||||
// unsigned long s11;
|
||||
// unsigned long t3;
|
||||
// unsigned long t4;
|
||||
// unsigned long t5;
|
||||
// unsigned long t6;
|
||||
// };
|
||||
// The id of a core register can be obtained like this: offset = id &
|
||||
// ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_RISCV_CORE). Thus,
|
||||
// id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | offset
|
||||
//
|
||||
// To generalize, the id of a register can be obtained by:
|
||||
// id = KVM_REG_RISCV | KVM_REG_SIZE_U64 |
|
||||
// KVM_REG_RISCV_CORE/KVM_REG_RISCV_CONFIG/KVM_REG_RISCV_TIMER |
|
||||
// offset
|
||||
kvm_bindings::KVM_REG_RISCV as u64
|
||||
| u64::from($reg_type)
|
||||
| u64::from(kvm_bindings::KVM_REG_SIZE_U64)
|
||||
| (($offset / std::mem::size_of::<u64>()) as u64)
|
||||
};
|
||||
}
|
||||
|
||||
/// Specifies whether a particular register is a core register or not.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `regid` - The index of the register we are checking.
|
||||
pub fn is_non_core_register(regid: u64) -> bool {
|
||||
if (regid & KVM_REG_RISCV_TYPE_MASK as u64) == KVM_REG_RISCV_CORE as u64 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let size = regid & KVM_REG_SIZE_MASK;
|
||||
|
||||
assert!(
|
||||
size == KVM_REG_SIZE_U64,
|
||||
"Unexpected register size for system register {size}"
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||
macro_rules! check_extension {
|
||||
($cap:expr) => {
|
||||
if !kvm.check_extension($cap) {
|
||||
return Err(KvmError::CapabilityMissing($cap));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
pub struct VcpuKvmState {
|
||||
pub mp_state: kvm_mp_state,
|
||||
pub core_regs: kvm_riscv_core,
|
||||
pub non_core_regs: Vec<kvm_one_reg>,
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// Copyright © 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
@@ -16,6 +18,7 @@
|
||||
//!
|
||||
//! - x86_64
|
||||
//! - arm64
|
||||
//! - riscv64 (experimental)
|
||||
//!
|
||||
|
||||
#[macro_use]
|
||||
@@ -57,6 +60,8 @@ pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
|
||||
pub use device::HypervisorDeviceError;
|
||||
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
|
||||
pub use kvm::{aarch64, GicState};
|
||||
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
|
||||
pub use kvm::{riscv64, AiaState};
|
||||
pub use vm::{
|
||||
DataMatch, HypervisorVmError, InterruptSourceConfig, LegacyIrqSourceConfig, MsiIrqSourceConfig,
|
||||
Vm, VmOps,
|
||||
@@ -193,8 +198,10 @@ pub enum IrqRoutingEntry {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum StandardRegisters {
|
||||
#[cfg(feature = "kvm")]
|
||||
#[cfg(all(feature = "kvm", not(target_arch = "riscv64")))]
|
||||
Kvm(kvm_bindings::kvm_regs),
|
||||
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
|
||||
Kvm(kvm_bindings::kvm_riscv_core),
|
||||
#[cfg(all(feature = "mshv", target_arch = "x86_64"))]
|
||||
Mshv(mshv_bindings::StandardRegisters),
|
||||
}
|
||||
@@ -314,3 +321,125 @@ get_aarch64_reg!(regs, [u64; 31usize]);
|
||||
get_aarch64_reg!(sp, u64);
|
||||
get_aarch64_reg!(pc, u64);
|
||||
get_aarch64_reg!(pstate, u64);
|
||||
|
||||
macro_rules! set_riscv64_reg {
|
||||
(mode) => {
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
impl StandardRegisters {
|
||||
pub fn set_mode(&mut self, val: u64) {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
StandardRegisters::Kvm(s) => s.mode = val,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($reg_name:ident) => {
|
||||
concat_idents!(method_name = "set_", $reg_name {
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
impl StandardRegisters {
|
||||
pub fn method_name(&mut self, val: u64) {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
StandardRegisters::Kvm(s) => s.regs.$reg_name = val,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! get_riscv64_reg {
|
||||
(mode) => {
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
impl StandardRegisters {
|
||||
pub fn get_mode(&self) -> u64 {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
StandardRegisters::Kvm(s) => s.mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($reg_name:ident) => {
|
||||
concat_idents!(method_name = "get_", $reg_name {
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
impl StandardRegisters {
|
||||
pub fn method_name(&self) -> u64 {
|
||||
match self {
|
||||
#[cfg(feature = "kvm")]
|
||||
StandardRegisters::Kvm(s) => s.regs.$reg_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
set_riscv64_reg!(pc);
|
||||
set_riscv64_reg!(ra);
|
||||
set_riscv64_reg!(sp);
|
||||
set_riscv64_reg!(gp);
|
||||
set_riscv64_reg!(tp);
|
||||
set_riscv64_reg!(t0);
|
||||
set_riscv64_reg!(t1);
|
||||
set_riscv64_reg!(t2);
|
||||
set_riscv64_reg!(s0);
|
||||
set_riscv64_reg!(s1);
|
||||
set_riscv64_reg!(a0);
|
||||
set_riscv64_reg!(a1);
|
||||
set_riscv64_reg!(a2);
|
||||
set_riscv64_reg!(a3);
|
||||
set_riscv64_reg!(a4);
|
||||
set_riscv64_reg!(a5);
|
||||
set_riscv64_reg!(a6);
|
||||
set_riscv64_reg!(a7);
|
||||
set_riscv64_reg!(s2);
|
||||
set_riscv64_reg!(s3);
|
||||
set_riscv64_reg!(s4);
|
||||
set_riscv64_reg!(s5);
|
||||
set_riscv64_reg!(s6);
|
||||
set_riscv64_reg!(s7);
|
||||
set_riscv64_reg!(s8);
|
||||
set_riscv64_reg!(s9);
|
||||
set_riscv64_reg!(s10);
|
||||
set_riscv64_reg!(s11);
|
||||
set_riscv64_reg!(t3);
|
||||
set_riscv64_reg!(t4);
|
||||
set_riscv64_reg!(t5);
|
||||
set_riscv64_reg!(t6);
|
||||
set_riscv64_reg!(mode);
|
||||
|
||||
get_riscv64_reg!(pc);
|
||||
get_riscv64_reg!(ra);
|
||||
get_riscv64_reg!(sp);
|
||||
get_riscv64_reg!(gp);
|
||||
get_riscv64_reg!(tp);
|
||||
get_riscv64_reg!(t0);
|
||||
get_riscv64_reg!(t1);
|
||||
get_riscv64_reg!(t2);
|
||||
get_riscv64_reg!(s0);
|
||||
get_riscv64_reg!(s1);
|
||||
get_riscv64_reg!(a0);
|
||||
get_riscv64_reg!(a1);
|
||||
get_riscv64_reg!(a2);
|
||||
get_riscv64_reg!(a3);
|
||||
get_riscv64_reg!(a4);
|
||||
get_riscv64_reg!(a5);
|
||||
get_riscv64_reg!(a6);
|
||||
get_riscv64_reg!(a7);
|
||||
get_riscv64_reg!(s2);
|
||||
get_riscv64_reg!(s3);
|
||||
get_riscv64_reg!(s4);
|
||||
get_riscv64_reg!(s5);
|
||||
get_riscv64_reg!(s6);
|
||||
get_riscv64_reg!(s7);
|
||||
get_riscv64_reg!(s8);
|
||||
get_riscv64_reg!(s9);
|
||||
get_riscv64_reg!(s10);
|
||||
get_riscv64_reg!(s11);
|
||||
get_riscv64_reg!(t3);
|
||||
get_riscv64_reg!(t4);
|
||||
get_riscv64_reg!(t5);
|
||||
get_riscv64_reg!(t6);
|
||||
get_riscv64_reg!(mode);
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use arc_swap::ArcSwap;
|
||||
use mshv_bindings::*;
|
||||
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
|
||||
use vfio_ioctls::VfioDeviceFd;
|
||||
use vm::DataMatch;
|
||||
#[cfg(feature = "sev_snp")]
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
|
||||
use crate::arch::emulator::PlatformEmulator;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -205,48 +211,12 @@ impl MshvHypervisor {
|
||||
.get_msr_index_list()
|
||||
.map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl MshvHypervisor {
|
||||
/// Create a hypervisor based on Mshv
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new() -> hypervisor::Result<Arc<dyn hypervisor::Hypervisor>> {
|
||||
let mshv_obj =
|
||||
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
|
||||
Ok(Arc::new(MshvHypervisor { mshv: mshv_obj }))
|
||||
}
|
||||
/// Check if the hypervisor is available
|
||||
pub fn is_available() -> hypervisor::Result<bool> {
|
||||
match std::fs::metadata("/dev/mshv") {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(err) => Err(hypervisor::HypervisorError::HypervisorAvailableCheck(
|
||||
err.into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of Hypervisor trait for Mshv
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// ```
|
||||
impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
///
|
||||
/// Returns the type of the hypervisor
|
||||
///
|
||||
fn hypervisor_type(&self) -> HypervisorType {
|
||||
HypervisorType::Mshv
|
||||
}
|
||||
|
||||
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn crate::Vm>> {
|
||||
fn create_vm_with_type_and_memory_int(
|
||||
&self,
|
||||
vm_type: u64,
|
||||
#[cfg(feature = "sev_snp")] _mem_size: Option<u64>,
|
||||
) -> hypervisor::Result<Arc<dyn crate::Vm>> {
|
||||
let mshv_vm_type: VmType = match VmType::try_from(vm_type) {
|
||||
Ok(vm_type) => vm_type,
|
||||
Err(_) => return Err(hypervisor::HypervisorError::UnsupportedVmType()),
|
||||
@@ -339,6 +309,14 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
dirty_log_slots: Arc::new(RwLock::new(HashMap::new())),
|
||||
#[cfg(feature = "sev_snp")]
|
||||
sev_snp_enabled: mshv_vm_type == VmType::Snp,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_access_pages: ArcSwap::new(
|
||||
AtomicBitmap::new(
|
||||
_mem_size.unwrap_or_default() as usize,
|
||||
NonZeroUsize::new(HV_PAGE_SIZE).unwrap(),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -350,6 +328,78 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MshvHypervisor {
|
||||
/// Create a hypervisor based on Mshv
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new() -> hypervisor::Result<Arc<dyn hypervisor::Hypervisor>> {
|
||||
let mshv_obj =
|
||||
Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?;
|
||||
Ok(Arc::new(MshvHypervisor { mshv: mshv_obj }))
|
||||
}
|
||||
/// Check if the hypervisor is available
|
||||
pub fn is_available() -> hypervisor::Result<bool> {
|
||||
match std::fs::metadata("/dev/mshv") {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(err) => Err(hypervisor::HypervisorError::HypervisorAvailableCheck(
|
||||
err.into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of Hypervisor trait for Mshv
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use hypervisor::mshv::MshvHypervisor;
|
||||
/// use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
/// ```
|
||||
impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
///
|
||||
/// Returns the type of the hypervisor
|
||||
///
|
||||
fn hypervisor_type(&self) -> HypervisorType {
|
||||
HypervisorType::Mshv
|
||||
}
|
||||
|
||||
///
|
||||
/// Create a Vm of a specific type using the underlying hypervisor, passing memory size
|
||||
/// Return a hypervisor-agnostic Vm trait object
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use hypervisor::kvm::KvmHypervisor;
|
||||
/// use hypervisor::kvm::KvmVm;
|
||||
/// let hypervisor = KvmHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm_with_type(0, 512*1024*1024).unwrap();
|
||||
/// ```
|
||||
fn create_vm_with_type_and_memory(
|
||||
&self,
|
||||
vm_type: u64,
|
||||
#[cfg(feature = "sev_snp")] _mem_size: u64,
|
||||
) -> hypervisor::Result<Arc<dyn vm::Vm>> {
|
||||
self.create_vm_with_type_and_memory_int(
|
||||
vm_type,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
Some(_mem_size),
|
||||
)
|
||||
}
|
||||
|
||||
fn create_vm_with_type(&self, vm_type: u64) -> hypervisor::Result<Arc<dyn crate::Vm>> {
|
||||
self.create_vm_with_type_and_memory_int(
|
||||
vm_type,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a mshv vm object and return the object as Vm trait object
|
||||
///
|
||||
@@ -357,7 +407,7 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// use hypervisor::mshv::MshvHypervisor;
|
||||
/// use hypervisor::mshv::MshvVm;
|
||||
/// let hypervisor = MshvHypervisor::new().unwrap();
|
||||
/// let vm = hypervisor.create_vm().unwrap();
|
||||
@@ -395,6 +445,20 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
struct Ghcb(*mut svm_ghcb_base);
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
// SAFETY: struct is based on GHCB page in the hypervisor,
|
||||
// safe to Send across threads
|
||||
unsafe impl Send for Ghcb {}
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
// SAFETY: struct is based on GHCB page in the hypervisor,
|
||||
// safe to Sync across threads as this is only required for Vcpu trait
|
||||
// functionally not used anyway
|
||||
unsafe impl Sync for Ghcb {}
|
||||
|
||||
/// Vcpu struct for Microsoft Hypervisor
|
||||
pub struct MshvVcpu {
|
||||
fd: VcpuFd,
|
||||
@@ -405,6 +469,10 @@ pub struct MshvVcpu {
|
||||
msrs: Vec<MsrEntry>,
|
||||
vm_ops: Option<Arc<dyn vm::VmOps>>,
|
||||
vm_fd: Arc<VmFd>,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
ghcb: Option<Ghcb>,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_access_pages: ArcSwap<AtomicBitmap>,
|
||||
}
|
||||
|
||||
/// Implementation of Vcpu trait for Microsoft Hypervisor
|
||||
@@ -412,8 +480,8 @@ pub struct MshvVcpu {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// use hypervisor::mshv::MshvHypervisor;
|
||||
/// use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
@@ -732,6 +800,12 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||
"Unhandled VCPU exit: attribute intercept - couldn't modify host access {}", e
|
||||
)))?;
|
||||
// Guest is revoking the shared access, so we need to update the bitmap
|
||||
self.host_access_pages.rcu(|_bitmap| {
|
||||
let bm = self.host_access_pages.load().as_ref().clone();
|
||||
bm.reset_addr_range(gpa_start as usize, gfn_count as usize);
|
||||
bm
|
||||
});
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -789,6 +863,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
let ghcb_msr = svm_ghcb_msr {
|
||||
as_uint64: info.ghcb_msr,
|
||||
};
|
||||
// Safe to use unwrap, for sev_snp guest we already have the
|
||||
// GHCB pointer wrapped in the option, otherwise this place is not reached.
|
||||
let ghcb = self.ghcb.as_ref().unwrap().0;
|
||||
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let ghcb_op = unsafe { ghcb_msr.__bindgen_anon_2.ghcb_info() as u32 };
|
||||
// Sanity check on the header fields before handling other operations.
|
||||
@@ -887,9 +965,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
GHCB_INFO_NORMAL => {
|
||||
let exit_code =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_code as u32;
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let pfn = unsafe { ghcb_msr.__bindgen_anon_2.gpa_page_number() };
|
||||
let ghcb_gpa = pfn << GHCB_INFO_BIT_WIDTH;
|
||||
|
||||
match exit_code {
|
||||
SVM_EXITCODE_HV_DOORBELL_PAGE => {
|
||||
let exit_info1 =
|
||||
@@ -898,10 +974,11 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
SVM_NAE_HV_DOORBELL_PAGE_GET_PREFERRED => {
|
||||
// Hypervisor does not have any preference for doorbell GPA.
|
||||
let preferred_doorbell_gpa: u64 = 0xFFFFFFFFFFFFFFFF;
|
||||
self.gpa_write(
|
||||
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
&preferred_doorbell_gpa.to_le_bytes(),
|
||||
)?;
|
||||
set_svm_field_u64_ptr!(
|
||||
ghcb,
|
||||
exit_info2,
|
||||
preferred_doorbell_gpa
|
||||
);
|
||||
}
|
||||
SVM_NAE_HV_DOORBELL_PAGE_SET => {
|
||||
let exit_info2 = info
|
||||
@@ -928,13 +1005,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
cpu::HypervisorCpuError::SetRegister(e.into())
|
||||
})?;
|
||||
|
||||
self.gpa_write(
|
||||
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
&exit_info2.to_le_bytes(),
|
||||
)?;
|
||||
set_svm_field_u64_ptr!(ghcb, exit_info2, exit_info2);
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1(ghcb_gpa)?;
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
SVM_NAE_HV_DOORBELL_PAGE_QUERY => {
|
||||
let mut reg_assocs = [ hv_register_assoc {
|
||||
@@ -945,19 +1019,13 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
let doorbell_gpa = unsafe { reg_assocs[0].value.reg64 };
|
||||
|
||||
self.gpa_write(
|
||||
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
&doorbell_gpa.to_le_bytes(),
|
||||
)?;
|
||||
set_svm_field_u64_ptr!(ghcb, exit_info2, doorbell_gpa);
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1(ghcb_gpa)?;
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
SVM_NAE_HV_DOORBELL_PAGE_CLEAR => {
|
||||
self.gpa_write(
|
||||
ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET,
|
||||
&[0; 8],
|
||||
)?;
|
||||
set_svm_field_u64_ptr!(ghcb, exit_info2, 0);
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
@@ -991,9 +1059,8 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
let is_write =
|
||||
// SAFETY: Accessing a union element from bindgen generated bindings.
|
||||
unsafe { port_info.__bindgen_anon_1.access_type() == 0 };
|
||||
|
||||
let mut data = [0; 8];
|
||||
self.gpa_read(ghcb_gpa + GHCB_RAX_OFFSET, &mut data)?;
|
||||
// SAFETY: Accessing the field from a mapped address
|
||||
let mut data = unsafe { (*ghcb).rax.to_le_bytes() };
|
||||
|
||||
if is_write {
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
@@ -1009,17 +1076,15 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||
})?;
|
||||
}
|
||||
|
||||
self.gpa_write(ghcb_gpa + GHCB_RAX_OFFSET, &data)?;
|
||||
set_svm_field_u64_ptr!(ghcb, rax, u64::from_le_bytes(data));
|
||||
}
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1(ghcb_gpa)?;
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
SVM_EXITCODE_MMIO_READ => {
|
||||
let src_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
|
||||
let dst_gpa = info.__bindgen_anon_2.__bindgen_anon_1.sw_scratch;
|
||||
let data_len =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2
|
||||
as usize;
|
||||
@@ -1032,16 +1097,18 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||
})?;
|
||||
}
|
||||
|
||||
self.gpa_write(dst_gpa, &data)?;
|
||||
// Copy the data to the shared buffer of the GHCB page
|
||||
let mut buffer_data = [0; 8];
|
||||
buffer_data[..data_len].copy_from_slice(&data[..data_len]);
|
||||
// SAFETY: Updating the value of mapped area
|
||||
unsafe { (*ghcb).shared[0] = u64::from_le_bytes(buffer_data) };
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1(ghcb_gpa)?;
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
SVM_EXITCODE_MMIO_WRITE => {
|
||||
let dst_gpa =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
|
||||
let src_gpa = info.__bindgen_anon_2.__bindgen_anon_1.sw_scratch;
|
||||
let data_len =
|
||||
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info2
|
||||
as usize;
|
||||
@@ -1049,7 +1116,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
assert!(data_len <= 0x8);
|
||||
|
||||
let mut data = vec![0; data_len];
|
||||
self.gpa_read(src_gpa, &mut data)?;
|
||||
// SAFETY: Accessing data from a mapped address
|
||||
let bytes_shared_ghcb =
|
||||
unsafe { (*ghcb).shared[0].to_le_bytes() };
|
||||
data.copy_from_slice(&bytes_shared_ghcb[..data_len]);
|
||||
|
||||
if let Some(vm_ops) = &self.vm_ops {
|
||||
vm_ops.mmio_write(dst_gpa, &data).map_err(|e| {
|
||||
@@ -1058,7 +1128,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
}
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1(ghcb_gpa)?;
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
SVM_EXITCODE_SNP_GUEST_REQUEST
|
||||
| SVM_EXITCODE_SNP_EXTENDED_GUEST_REQUEST => {
|
||||
@@ -1067,15 +1137,16 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
// We don't support extended guest request, so we just write empty data.
|
||||
// This matches the behavior of KVM in Linux 6.11.
|
||||
|
||||
// Read RAX & RBX from the GHCB.
|
||||
let mut data = [0; 8];
|
||||
self.gpa_read(ghcb_gpa + GHCB_RAX_OFFSET, &mut data)?;
|
||||
let data_gpa = u64::from_le_bytes(data);
|
||||
self.gpa_read(ghcb_gpa + GHCB_RBX_OFFSET, &mut data)?;
|
||||
let data_npages = u64::from_le_bytes(data);
|
||||
// Read RBX from the GHCB.
|
||||
// SAFETY: Accessing data from a mapped address
|
||||
let data_gpa = unsafe { (*ghcb).rax };
|
||||
// SAFETY: Accessing data from a mapped address
|
||||
let data_npages = unsafe { (*ghcb).rbx };
|
||||
|
||||
if data_npages > 0 {
|
||||
// The certificates are terminated by 24 zero bytes.
|
||||
// TODO: Need to check if data_gpa is the address of the shared buffer in the GHCB page
|
||||
// in that case we should clear the shared buffer(24 bytes)
|
||||
self.gpa_write(data_gpa, &[0; 24])?;
|
||||
}
|
||||
}
|
||||
@@ -1096,7 +1167,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
req_gpa, rsp_gpa
|
||||
);
|
||||
|
||||
self.gpa_write(ghcb_gpa + GHCB_SW_EXITINFO2_OFFSET, &[0; 8])?;
|
||||
set_svm_field_u64_ptr!(ghcb, exit_info2, 0);
|
||||
}
|
||||
SVM_EXITCODE_SNP_AP_CREATION => {
|
||||
let vmsa_gpa =
|
||||
@@ -1117,7 +1188,7 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))?;
|
||||
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.clear_swexit_info1(ghcb_gpa)?;
|
||||
self.clear_swexit_info1()?;
|
||||
}
|
||||
_ => panic!(
|
||||
"GHCB_INFO_NORMAL: Unhandled exit code: {:0x}",
|
||||
@@ -1495,37 +1566,16 @@ impl MshvVcpu {
|
||||
/// Clear SW_EXIT_INFO1 register for SEV-SNP guests.
|
||||
///
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn clear_swexit_info1(
|
||||
&self,
|
||||
ghcb_gpa: u64,
|
||||
) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
||||
fn clear_swexit_info1(&self) -> std::result::Result<cpu::VmExit, cpu::HypervisorCpuError> {
|
||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||
self.gpa_write(ghcb_gpa + GHCB_SW_EXITINFO1_OFFSET, &[0; 4])?;
|
||||
// Safe to use unwrap, for sev_snp guest we already have the
|
||||
// GHCB pointer wrapped in the option, otherwise this place is not reached.
|
||||
let ghcb = self.ghcb.as_ref().unwrap().0;
|
||||
set_svm_field_u64_ptr!(ghcb, exit_info1, 0);
|
||||
|
||||
Ok(cpu::VmExit::Ignore)
|
||||
}
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn gpa_read(&self, gpa: u64, data: &mut [u8]) -> cpu::Result<()> {
|
||||
for (gpa, chunk) in (gpa..)
|
||||
.step_by(HV_READ_WRITE_GPA_MAX_SIZE as usize)
|
||||
.zip(data.chunks_mut(HV_READ_WRITE_GPA_MAX_SIZE as usize))
|
||||
{
|
||||
let mut rw_gpa_arg = mshv_bindings::mshv_read_write_gpa {
|
||||
base_gpa: gpa,
|
||||
byte_count: chunk.len() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
self.fd
|
||||
.gpa_read(&mut rw_gpa_arg)
|
||||
.map_err(|e| cpu::HypervisorCpuError::GpaRead(e.into()))?;
|
||||
|
||||
chunk.copy_from_slice(&rw_gpa_arg.data[..chunk.len()]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn gpa_write(&self, gpa: u64, data: &[u8]) -> cpu::Result<()> {
|
||||
for (gpa, chunk) in (gpa..)
|
||||
@@ -1558,6 +1608,8 @@ pub struct MshvVm {
|
||||
dirty_log_slots: Arc<RwLock<HashMap<u64, MshvDirtyLogSlot>>>,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
sev_snp_enabled: bool,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_access_pages: ArcSwap<AtomicBitmap>,
|
||||
}
|
||||
|
||||
impl MshvVm {
|
||||
@@ -1580,9 +1632,9 @@ impl MshvVm {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate hypervisor;
|
||||
/// # use hypervisor::mshv::MshvHypervisor;
|
||||
/// # use std::sync::Arc;
|
||||
/// extern crate hypervisor;
|
||||
/// use hypervisor::mshv::MshvHypervisor;
|
||||
/// use std::sync::Arc;
|
||||
/// let mshv = MshvHypervisor::new().unwrap();
|
||||
/// let hypervisor = Arc::new(mshv);
|
||||
/// let vm = hypervisor.create_vm().expect("new VM fd creation failed");
|
||||
@@ -1649,6 +1701,34 @@ impl vm::Vm for MshvVm {
|
||||
.fd
|
||||
.create_vcpu(id)
|
||||
.map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?;
|
||||
|
||||
/* Map the GHCB page to the VMM(root) address space
|
||||
* The map is available after the vcpu creation. This address is mapped
|
||||
* to the overlay ghcb page of the Microsoft Hypervisor, don't have
|
||||
* to worry about the scenario when a guest changes the GHCB mapping.
|
||||
*/
|
||||
#[cfg(feature = "sev_snp")]
|
||||
let ghcb = if self.sev_snp_enabled {
|
||||
// SAFETY: Safe to call as VCPU has this map already available upon creation
|
||||
let addr = unsafe {
|
||||
libc::mmap(
|
||||
std::ptr::null_mut(),
|
||||
HV_PAGE_SIZE,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
libc::MAP_SHARED,
|
||||
vcpu_fd.as_raw_fd(),
|
||||
MSHV_VP_MMAP_OFFSET_GHCB as i64 * libc::sysconf(libc::_SC_PAGE_SIZE),
|
||||
)
|
||||
};
|
||||
if addr == libc::MAP_FAILED {
|
||||
// No point of continuing, without this mmap VMGEXIT will fail anyway
|
||||
// Return error
|
||||
return Err(vm::HypervisorVmError::MmapToRoot);
|
||||
}
|
||||
Some(Ghcb(addr as *mut svm_ghcb_base))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let vcpu = MshvVcpu {
|
||||
fd: vcpu_fd,
|
||||
vp_index: id,
|
||||
@@ -1658,6 +1738,10 @@ impl vm::Vm for MshvVm {
|
||||
msrs: self.msrs.clone(),
|
||||
vm_ops,
|
||||
vm_fd: self.fd.clone(),
|
||||
#[cfg(feature = "sev_snp")]
|
||||
ghcb,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_access_pages: ArcSwap::new(self.host_access_pages.load().clone()),
|
||||
};
|
||||
Ok(Arc::new(vcpu))
|
||||
}
|
||||
@@ -2050,6 +2134,7 @@ impl vm::Vm for MshvVm {
|
||||
#[cfg(feature = "sev_snp")]
|
||||
fn gain_page_access(&self, gpa: u64, size: u32) -> vm::Result<()> {
|
||||
use mshv_ioctls::set_bits;
|
||||
const ONE_GB: usize = 1024 * 1024 * 1024;
|
||||
|
||||
if !self.sev_snp_enabled {
|
||||
return Ok(());
|
||||
@@ -2058,7 +2143,25 @@ impl vm::Vm for MshvVm {
|
||||
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();
|
||||
// Enlarge the bitmap if the PFN is greater than the bitmap length
|
||||
if end_gpfn >= self.host_access_pages.load().as_ref().len() as u64 {
|
||||
self.host_access_pages.rcu(|bitmap| {
|
||||
let mut bm = bitmap.as_ref().clone();
|
||||
bm.enlarge(ONE_GB);
|
||||
bm
|
||||
});
|
||||
}
|
||||
|
||||
let gpas: Vec<u64> = (start_gpfn..=end_gpfn)
|
||||
.filter(|x| {
|
||||
!self
|
||||
.host_access_pages
|
||||
.load()
|
||||
.as_ref()
|
||||
.is_bit_set(*x as usize)
|
||||
})
|
||||
.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());
|
||||
@@ -2081,6 +2184,14 @@ impl vm::Vm for MshvVm {
|
||||
self.fd
|
||||
.modify_gpa_host_access(&gpa_list[0])
|
||||
.map_err(|e| vm::HypervisorVmError::ModifyGpaHostAccess(e.into()))?;
|
||||
|
||||
for acquired_gpa in gpas {
|
||||
self.host_access_pages.rcu(|bitmap| {
|
||||
let bm = bitmap.clone();
|
||||
bm.set_bit((acquired_gpa >> PAGE_SHIFT) as usize);
|
||||
bm
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -16,10 +16,3 @@ pub const ECDSA_SIG_X_COMPONENT_END: usize =
|
||||
pub const ECDSA_SIG_Y_COMPONENT_START: usize = ECDSA_SIG_X_COMPONENT_END;
|
||||
pub const ECDSA_SIG_Y_COMPONENT_END: usize =
|
||||
ECDSA_SIG_X_COMPONENT_END + ECDSA_SIG_Y_COMPONENT_SIZE_IN_BYTES;
|
||||
|
||||
// These constants are derived from GHCB spec Sect. 2.6 Table 3 GHCB Layout
|
||||
// Link: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf
|
||||
pub const GHCB_RAX_OFFSET: u64 = 0x01F8;
|
||||
pub const GHCB_RBX_OFFSET: u64 = 0x0318;
|
||||
pub const GHCB_SW_EXITINFO1_OFFSET: u64 = 0x398;
|
||||
pub const GHCB_SW_EXITINFO2_OFFSET: u64 = 0x3A0;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
//
|
||||
// Copyright © 2019 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||
@@ -12,7 +14,7 @@ use std::any::Any;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs::File;
|
||||
use std::sync::Arc;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[cfg(feature = "sev_snp")]
|
||||
@@ -24,6 +26,8 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
use crate::aarch64::VcpuInit;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
use crate::arch::riscv64::aia::{Vaia, VaiaConfig};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::arch::x86::CpuIdEntry;
|
||||
use crate::cpu::Vcpu;
|
||||
@@ -224,6 +228,11 @@ pub enum HypervisorVmError {
|
||||
#[error("Failed to create Vgic: {0}")]
|
||||
CreateVgic(#[source] anyhow::Error),
|
||||
///
|
||||
/// Create Vaia error
|
||||
///
|
||||
#[error("Failed to create Vaia: {0}")]
|
||||
CreateVaia(#[source] anyhow::Error),
|
||||
///
|
||||
/// Import isolated pages error
|
||||
///
|
||||
#[error("Failed to import isolated pages: {0}")]
|
||||
@@ -242,6 +251,12 @@ pub enum HypervisorVmError {
|
||||
#[cfg(feature = "sev_snp")]
|
||||
#[error("Failed to modify GPA host access: {0}")]
|
||||
ModifyGpaHostAccess(#[source] anyhow::Error),
|
||||
///
|
||||
/// Failed to mmap
|
||||
///
|
||||
#[cfg(feature = "sev_snp")]
|
||||
#[error("Failed to mmap:")]
|
||||
MmapToRoot,
|
||||
}
|
||||
///
|
||||
/// Result type for returning from a function
|
||||
@@ -293,6 +308,7 @@ pub trait Vm: Send + Sync + Any {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Sets the address of the three-page region in the VM's address space.
|
||||
fn set_tss_address(&self, offset: usize) -> Result<()>;
|
||||
#[cfg(not(target_arch = "riscv64"))]
|
||||
/// Creates an in-kernel interrupt controller.
|
||||
fn create_irq_chip(&self) -> Result<()>;
|
||||
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
|
||||
@@ -303,6 +319,8 @@ pub trait Vm: Send + Sync + Any {
|
||||
fn create_vcpu(&self, id: u8, vm_ops: Option<Arc<dyn VmOps>>) -> Result<Arc<dyn Vcpu>>;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn create_vgic(&self, config: VgicConfig) -> Result<Arc<Mutex<dyn Vgic>>>;
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
fn create_vaia(&self, config: VaiaConfig) -> Result<Arc<Mutex<dyn Vaia>>>;
|
||||
|
||||
/// Registers an event to be signaled whenever a certain address is written to.
|
||||
fn register_ioevent(
|
||||
|
||||
@@ -6,8 +6,8 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.3"
|
||||
getrandom = "0.2.14"
|
||||
libc = "0.2.158"
|
||||
getrandom = "0.2.15"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
net_gen = { path = "../net_gen" }
|
||||
rate_limiter = { path = "../rate_limiter" }
|
||||
|
||||
@@ -75,9 +75,7 @@ pub fn open_tap(
|
||||
let mut ifname: String = String::new();
|
||||
let vnet_hdr_size = vnet_hdr_len() as i32;
|
||||
// Check if the given interface exists before we create it.
|
||||
let tap_existed = if_name.map_or(false, |n| {
|
||||
Path::new(&format!("/sys/class/net/{n}")).exists()
|
||||
});
|
||||
let tap_existed = if_name.is_some_and(|n| Path::new(&format!("/sys/class/net/{n}")).exists());
|
||||
|
||||
// In case the tap interface already exists, check if the number of
|
||||
// queues is appropriate. The tap might not support multiqueue while
|
||||
|
||||
@@ -468,7 +468,7 @@ impl NetQueuePair {
|
||||
let rate_limit_reached = self
|
||||
.rx_rate_limiter
|
||||
.as_ref()
|
||||
.map_or(false, |r| r.is_blocked());
|
||||
.is_some_and(|r| r.is_blocked());
|
||||
|
||||
// Stop listening on the `RX_TAP_EVENT` when:
|
||||
// 1) there is no available describes, or
|
||||
|
||||
@@ -10,10 +10,10 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
byteorder = "1.5.0"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0.208", features = ["derive"] }
|
||||
thiserror = "1.0.62"
|
||||
|
||||
@@ -125,19 +125,16 @@ impl PciBus {
|
||||
pub fn register_mapping(
|
||||
&self,
|
||||
dev: Arc<dyn BusDeviceSync>,
|
||||
#[cfg(target_arch = "x86_64")] io_bus: &Bus,
|
||||
io_bus: &Bus,
|
||||
mmio_bus: &Bus,
|
||||
bars: Vec<PciBarConfiguration>,
|
||||
) -> Result<()> {
|
||||
for bar in bars {
|
||||
match bar.region_type() {
|
||||
PciBarRegionType::IoRegion => {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_bus
|
||||
.insert(dev.clone(), bar.addr(), bar.size())
|
||||
.map_err(PciRootError::PioInsert)?;
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
error!("I/O region is not supported");
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
||||
mmio_bus
|
||||
|
||||
@@ -23,7 +23,7 @@ use vfio_ioctls::{
|
||||
use vm_allocator::page_size::{
|
||||
align_page_size_down, align_page_size_up, is_4k_aligned, is_4k_multiple, is_page_size_aligned,
|
||||
};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_allocator::{AddressAllocator, MemorySlotAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
@@ -713,11 +713,7 @@ impl VfioCommon {
|
||||
|
||||
let bar_addr = match region_type {
|
||||
PciBarRegionType::IoRegion => {
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
unimplemented!();
|
||||
|
||||
// The address needs to be 4 bytes aligned.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -795,10 +791,7 @@ impl VfioCommon {
|
||||
for region in self.mmio_regions.iter() {
|
||||
match region.type_ {
|
||||
PciBarRegionType::IoRegion => {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
allocator.free_io_addresses(region.start, region.length);
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
error!("I/O region is not supported");
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion => {
|
||||
mmio32_allocator.free(region.start, region.length);
|
||||
@@ -1416,7 +1409,7 @@ pub struct VfioPciDevice {
|
||||
container: Arc<VfioContainer>,
|
||||
common: VfioCommon,
|
||||
iommu_attached: bool,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
memory_slot_allocator: MemorySlotAllocator,
|
||||
}
|
||||
|
||||
impl VfioPciDevice {
|
||||
@@ -1431,7 +1424,7 @@ impl VfioPciDevice {
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
iommu_attached: bool,
|
||||
bdf: PciBdf,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
memory_slot_allocator: MemorySlotAllocator,
|
||||
snapshot: Option<Snapshot>,
|
||||
x_nv_gpudirect_clique: Option<u8>,
|
||||
) -> Result<Self, VfioPciError> {
|
||||
@@ -1457,7 +1450,7 @@ impl VfioPciDevice {
|
||||
container,
|
||||
common,
|
||||
iommu_attached,
|
||||
memory_slot,
|
||||
memory_slot_allocator,
|
||||
};
|
||||
|
||||
Ok(vfio_pci_device)
|
||||
@@ -1635,7 +1628,7 @@ impl VfioPciDevice {
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: (self.memory_slot)(),
|
||||
slot: self.memory_slot_allocator.next_memory_slot(),
|
||||
start: region.start.0 + area.offset,
|
||||
size: area.size,
|
||||
host_addr: host_addr as u64,
|
||||
@@ -1699,6 +1692,9 @@ impl VfioPciDevice {
|
||||
error!("Could not remove the userspace memory region: {}", e);
|
||||
}
|
||||
|
||||
self.memory_slot_allocator
|
||||
.free_memory_slot(user_memory_region.slot);
|
||||
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let ret = unsafe {
|
||||
libc::munmap(
|
||||
|
||||
@@ -13,7 +13,7 @@ use thiserror::Error;
|
||||
use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::VfioIrq;
|
||||
use vfio_user::{Client, Error as VfioUserError};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_allocator::{AddressAllocator, MemorySlotAllocator, SystemAllocator};
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vm_device::interrupt::{InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig};
|
||||
use vm_device::{BusDevice, Resource};
|
||||
@@ -35,7 +35,7 @@ pub struct VfioUserPciDevice {
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
client: Arc<Mutex<Client>>,
|
||||
common: VfioCommon,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
memory_slot_allocator: MemorySlotAllocator,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -74,7 +74,7 @@ impl VfioUserPciDevice {
|
||||
msi_interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
bdf: PciBdf,
|
||||
memory_slot: Arc<dyn Fn() -> u32 + Send + Sync>,
|
||||
memory_slot_allocator: MemorySlotAllocator,
|
||||
snapshot: Option<Snapshot>,
|
||||
) -> Result<Self, VfioUserPciDeviceError> {
|
||||
let resettable = client.lock().unwrap().resettable();
|
||||
@@ -106,7 +106,7 @@ impl VfioUserPciDevice {
|
||||
vm: vm.clone(),
|
||||
client,
|
||||
common,
|
||||
memory_slot,
|
||||
memory_slot_allocator,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ impl VfioUserPciDevice {
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: (self.memory_slot)(),
|
||||
slot: self.memory_slot_allocator.next_memory_slot(),
|
||||
start: mmio_region.start.0 + s.offset,
|
||||
size: s.size,
|
||||
host_addr: host_addr as u64,
|
||||
@@ -222,6 +222,9 @@ impl VfioUserPciDevice {
|
||||
error!("Could not remove the userspace memory region: {}", e);
|
||||
}
|
||||
|
||||
self.memory_slot_allocator
|
||||
.free_memory_slot(user_memory_region.slot);
|
||||
|
||||
// Remove mmaps
|
||||
// SAFETY: FFI call with correct arguments
|
||||
let ret = unsafe {
|
||||
|
||||
@@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
epoll = "4.3.3"
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
thiserror = "1.0.62"
|
||||
vmm-sys-util = { workspace = true }
|
||||
|
||||
189
release-notes.md
189
release-notes.md
@@ -1,25 +1,30 @@
|
||||
- [v43.0](#v430)
|
||||
- [Live Migration over TCP Connections](#live-migration-over-tcp-connections)
|
||||
- [Notable Performance Improvements](#notable-performance-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Contributors](#contributors)
|
||||
- [v42.0](#v420)
|
||||
- [SVE/SVE2 Support on AArch64](#svesve2-support-on-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Sponsorships](#sponsorships)
|
||||
- [Contributors](#contributors)
|
||||
- [Contributors](#contributors-1)
|
||||
- [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-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v40.0](#v400)
|
||||
- [Support for Restoring File Descriptor Backed Network Devices](#support-for-restoring-file-descriptor-backed-network-devices)
|
||||
- [Notable Performance Improvements](#notable-performance-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [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-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v39.0](#v390)
|
||||
- [Variable Sizing of PCI Apertures for Segments](#variable-sizing-of-pci-apertures-for-segments)
|
||||
- [Direct Booting with bzImages](#direct-booting-with-bzimages)
|
||||
- [Support for NVIDIA GPUDirect P2P Support](#support-for-nvidia-gpudirect-p2p-support)
|
||||
- [Guest NMI Injection Support](#guest-nmi-injection-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v38.0](#v380)
|
||||
- [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)
|
||||
@@ -27,16 +32,16 @@
|
||||
- [New 'debug-console' Device](#new-debug-console-device)
|
||||
- [Improved VFIO Device Support](#improved-vfio-device-support)
|
||||
- [Extended CPU Affinity Support](#extended-cpu-affinity-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v37.0](#v370)
|
||||
- [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)
|
||||
- [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)
|
||||
- [Faster VM Restoration from Snapshots](#faster-vm-restoration-from-snapshots)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [v36.0](#v360)
|
||||
- [Command Line Changes](#command-line-changes)
|
||||
- [Enabled Features Reported via API Endpoint and CLI](#enabled-features-reported-via-api-endpoint-and-cli)
|
||||
@@ -45,31 +50,31 @@
|
||||
- [Unix Socket Backend for Serial Port](#unix-socket-backend-for-serial-port)
|
||||
- [AIO Backend for Block Devices](#aio-backend-for-block-devices)
|
||||
- [Documentation Improvements](#documentation-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v35.0](#v350)
|
||||
- [`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)
|
||||
- [vCPU TSC Frequency Included in Migration State](#vcpu-tsc-frequency-included-in-migration-state)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [v34.0](#v340)
|
||||
- [Paravirtualised Panic Device Support](#paravirtualised-panic-device-support)
|
||||
- [Improvements to VM Core Dump](#improvements-to-vm-core-dump)
|
||||
- [QCOW2 Support for Backing Files](#qcow2-support-for-backing-files)
|
||||
- [Minimum Host Kernel Bump](#minimum-host-kernel-bump)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [v33.0](#v330)
|
||||
- [D-Bus based API](#d-bus-based-api)
|
||||
- [Expose Host CPU Cache Details for AArch64](#expose-host-cpu-cache-details-for-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v32.0](#v320)
|
||||
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
|
||||
- [API Changes](#api-changes)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [v31.1](#v311)
|
||||
- [v31.0](#v310)
|
||||
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
|
||||
@@ -77,15 +82,15 @@
|
||||
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
|
||||
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
|
||||
- [Documentation Improvements](#documentation-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v30.0](#v300)
|
||||
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
|
||||
- [Basic vfio-user Server Support](#basic-vfio-user-server-support)
|
||||
- [Heap Profiling Support](#heap-profiling-support)
|
||||
- [Documentation Improvements](#documentation-improvements-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-13)
|
||||
- [v28.2](#v282)
|
||||
- [v29.0](#v290)
|
||||
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
||||
@@ -95,10 +100,10 @@
|
||||
- [`AArch64` Documentation Integration](#aarch64-documentation-integration)
|
||||
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
|
||||
- [TCP Offload Control](#tcp-offload-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-13)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Removals](#removals)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Contributors](#contributors-14)
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
@@ -106,9 +111,9 @@
|
||||
- [Virtualised TPM Support](#virtualised-tpm-support)
|
||||
- [Transparent Huge Page Support](#transparent-huge-page-support)
|
||||
- [README Quick Start Improved](#readme-quick-start-improved)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-14)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Contributors](#contributors-15)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
@@ -117,41 +122,41 @@
|
||||
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
|
||||
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
|
||||
- [GDB Support for AArch64](#gdb-support-for-aarch64)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-15)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Contributors](#contributors-16)
|
||||
- [v26.0](#v260)
|
||||
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
|
||||
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-16)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-17)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-17)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-18)
|
||||
- [Removals](#removals-3)
|
||||
- [Contributors](#contributors-17)
|
||||
- [Contributors](#contributors-18)
|
||||
- [v24.0](#v240)
|
||||
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
|
||||
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
|
||||
- [Sparse Mmap support](#sparse-mmap-support)
|
||||
- [Expose Platform Serial Number](#expose-platform-serial-number)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-18)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-19)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-18)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v23.1](#v231)
|
||||
- [v23.0](#v230)
|
||||
- [vDPA Support](#vdpa-support)
|
||||
- [Updated OS Support list](#updated-os-support-list)
|
||||
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
|
||||
- [`AMX` Support](#amx-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-19)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-20)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-19)
|
||||
- [Contributors](#contributors-20)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -162,13 +167,13 @@
|
||||
- [PMU Support for AArch64](#pmu-support-for-aarch64)
|
||||
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
|
||||
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-20)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-21)
|
||||
- [Contributors](#contributors-21)
|
||||
- [v21.0](#v210)
|
||||
- [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)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-21)
|
||||
- [Contributors](#contributors-21)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-22)
|
||||
- [Contributors](#contributors-22)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -177,8 +182,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-22)
|
||||
- [Contributors](#contributors-22)
|
||||
- [Notable bug fixes](#notable-bug-fixes-23)
|
||||
- [Contributors](#contributors-23)
|
||||
- [v19.0](#v190)
|
||||
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
|
||||
- [PCI boot time optimisations](#pci-boot-time-optimisations)
|
||||
@@ -186,8 +191,8 @@
|
||||
- [Live migration enhancements](#live-migration-enhancements)
|
||||
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
|
||||
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
|
||||
- [Notable bug fixes](#notable-bug-fixes-23)
|
||||
- [Contributors](#contributors-23)
|
||||
- [Notable bug fixes](#notable-bug-fixes-24)
|
||||
- [Contributors](#contributors-24)
|
||||
- [v18.0](#v180)
|
||||
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
|
||||
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
|
||||
@@ -197,23 +202,23 @@
|
||||
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
|
||||
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
|
||||
- [Power button support on AArch64](#power-button-support-on-aarch64)
|
||||
- [Notable bug fixes](#notable-bug-fixes-24)
|
||||
- [Contributors](#contributors-24)
|
||||
- [Notable bug fixes](#notable-bug-fixes-25)
|
||||
- [Contributors](#contributors-25)
|
||||
- [v17.0](#v170)
|
||||
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
|
||||
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
|
||||
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
|
||||
- [Improved SGX support](#improved-sgx-support)
|
||||
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
|
||||
- [Notable bug fixes](#notable-bug-fixes-25)
|
||||
- [Contributors](#contributors-25)
|
||||
- [Notable bug fixes](#notable-bug-fixes-26)
|
||||
- [Contributors](#contributors-26)
|
||||
- [v16.0](#v160)
|
||||
- [Improved live migration support](#improved-live-migration-support)
|
||||
- [Improved `vhost-user` support](#improved-vhost-user-support)
|
||||
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
|
||||
- [Notable bug fixes](#notable-bug-fixes-26)
|
||||
- [Notable bug fixes](#notable-bug-fixes-27)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-26)
|
||||
- [Contributors](#contributors-27)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -221,7 +226,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-27)
|
||||
- [Contributors](#contributors-28)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -231,7 +236,7 @@
|
||||
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
|
||||
- [Block device rate limiting](#block-device-rate-limiting)
|
||||
- [Deprecations](#deprecations-6)
|
||||
- [Contributors](#contributors-28)
|
||||
- [Contributors](#contributors-29)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -239,13 +244,13 @@
|
||||
- [VHD disk image support](#vhd-disk-image-support)
|
||||
- [Improved Virtio device threading](#improved-virtio-device-threading)
|
||||
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
|
||||
- [Contributors](#contributors-29)
|
||||
- [Contributors](#contributors-30)
|
||||
- [v0.12.0](#v0120)
|
||||
- [ARM64 enhancements](#arm64-enhancements)
|
||||
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
|
||||
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
|
||||
- [Enhanced "info" API](#enhanced-info-api)
|
||||
- [Contributors](#contributors-30)
|
||||
- [Contributors](#contributors-31)
|
||||
- [v0.11.0](#v0110)
|
||||
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
|
||||
- [Windows Guest Support](#windows-guest-support)
|
||||
@@ -257,15 +262,15 @@
|
||||
- [Default Log Level Changed](#default-log-level-changed)
|
||||
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
|
||||
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-27)
|
||||
- [Contributors](#contributors-31)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-28)
|
||||
- [Contributors](#contributors-32)
|
||||
- [v0.10.0](#v0100)
|
||||
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
|
||||
- [Memory Zones](#memory-zones)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
|
||||
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-28)
|
||||
- [Contributors](#contributors-32)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-29)
|
||||
- [Contributors](#contributors-33)
|
||||
- [v0.9.0](#v090)
|
||||
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
|
||||
- [Block and Network Device Statistics](#block-and-network-device-statistics)
|
||||
@@ -278,17 +283,17 @@
|
||||
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
|
||||
- [Intel SGX Support](#intel-sgx-support)
|
||||
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-29)
|
||||
- [Contributors](#contributors-33)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-30)
|
||||
- [Contributors](#contributors-34)
|
||||
- [v0.8.0](#v080)
|
||||
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
|
||||
- [Experimental ARM64 Support](#experimental-arm64-support)
|
||||
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
|
||||
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
|
||||
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-30)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-31)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-34)
|
||||
- [Contributors](#contributors-35)
|
||||
- [v0.7.0](#v070)
|
||||
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
|
||||
- [Alternative `libc` Support](#alternative-libc-support)
|
||||
@@ -298,14 +303,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-35)
|
||||
- [Contributors](#contributors-36)
|
||||
- [v0.6.0](#v060)
|
||||
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
|
||||
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
|
||||
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [PVH Boot](#pvh-boot)
|
||||
- [Contributors](#contributors-36)
|
||||
- [Contributors](#contributors-37)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -313,7 +318,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-37)
|
||||
- [Contributors](#contributors-38)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -322,7 +327,7 @@
|
||||
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
|
||||
- [PCI BAR reprogramming](#pci-bar-reprogramming)
|
||||
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
|
||||
- [Contributors](#contributors-38)
|
||||
- [Contributors](#contributors-39)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -349,6 +354,48 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v43.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v43.0. The following user visible changes have been made:
|
||||
|
||||
### Live Migration over TCP Connections
|
||||
|
||||
Support has been added to enable direct live migration from two hosts via TCP
|
||||
connections. This supplements the existing support for migrating over a UNIX
|
||||
socket which can then be tunnelled as desired. The documentation has been
|
||||
updated. (#6850)
|
||||
|
||||
### Notable Performance Improvements
|
||||
|
||||
The `VIRTIO_RING_F_INDIRECT_DESC` feature has been enabled for `virtio-block`
|
||||
devices. This significantly increases the throughput of the devices with a
|
||||
small negative impact on latency. (#6826)
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Cloud Hypervisor now accepts VFIO devices that use I/O PCI BARs on non x86-64
|
||||
architectures. Whether they function depends on the host PCI host bridge
|
||||
support - previously they would be rejected even if the driver did not use
|
||||
these BARs. (#6871)
|
||||
* Command line groups were adjusted to ensure that at least one payload
|
||||
parameter was provided if any other VM parameters provided. (#6832)
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to everyone who has contributed to our release:
|
||||
|
||||
* Alyssa Ross <hi@alyssa.is>
|
||||
* Andrew Consroe <aconz2@gmail.com>
|
||||
* Bo Chen <bo.arvin.chen@gmail.com>
|
||||
* Jinrong Liang <cloudliang@tencent.com>
|
||||
* Julian Stecklina <julian.stecklina@cyberus-technology.de>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Rob Bradford <rbradford@rivosinc.com>
|
||||
* Ruoqing He <heruoqing@iscas.ac.cn>
|
||||
* Wojtek Czekalski <wczekalski@me.com>
|
||||
|
||||
# v42.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
|
||||
83
scripts/package-consistency-check.py
Executable file
83
scripts/package-consistency-check.py
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/env python3
|
||||
#
|
||||
# Copyright © 2024 Institute of Software, CAS. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
from argparse import ArgumentParser
|
||||
from collections import defaultdict
|
||||
|
||||
def get_cargo_metadata():
|
||||
result = subprocess.run(
|
||||
['cargo', 'metadata', '--format-version=1'],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
exit(1)
|
||||
|
||||
metadata = json.loads(result.stdout)
|
||||
return metadata
|
||||
|
||||
def find_dependents_of_package(metadata, package_source):
|
||||
"""Find dependencies based on the provided source identifier and return related package info."""
|
||||
packages = defaultdict(list)
|
||||
direct_dependents = defaultdict(list)
|
||||
|
||||
# Identify packages from the given package source and record version
|
||||
for pkg in metadata['packages']:
|
||||
repository = pkg['repository'] or ''
|
||||
if package_source in repository:
|
||||
packages[pkg['name']].append(pkg['version'])
|
||||
|
||||
# Find packages that immediately depend on the identified source packages
|
||||
for node in metadata['resolve']['nodes']:
|
||||
current_pkg = next(pkg for pkg in metadata['packages'] if pkg['id'] == node['id'])
|
||||
current_pkg_name = current_pkg['name']
|
||||
current_pkg_version = current_pkg['version']
|
||||
|
||||
for dep_id in node['dependencies']:
|
||||
dep_pkg = next(pkg for pkg in metadata['packages'] if pkg['id'] == dep_id)
|
||||
dep_name = dep_pkg['name']
|
||||
dep_version = dep_pkg['version']
|
||||
|
||||
if dep_name in packages:
|
||||
direct_dependents[(dep_name, dep_version)].append((current_pkg_name, current_pkg_version))
|
||||
|
||||
return packages, direct_dependents
|
||||
|
||||
def check_for_version_conflicts(packages, direct_dependents):
|
||||
"""Check if there are multiple versions of dependencies, and return True if conflicts are found."""
|
||||
has_conflicts = False
|
||||
|
||||
for pkg_name, versions in packages.items():
|
||||
if len(set(versions)) > 1:
|
||||
has_conflicts = True
|
||||
print(f"Error: Multiple versions detected for {pkg_name}: {set(versions)}")
|
||||
for version in set(versions):
|
||||
print(f" Version {version} used by:")
|
||||
for dependent, dep_version in direct_dependents[(pkg_name, version)]:
|
||||
print(f" - {dependent} v{dep_version}")
|
||||
|
||||
return has_conflicts
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(description='Cargo dependency conflict checker.')
|
||||
parser.add_argument('package_source', type=str, help='A keyword used to match the repository URL field')
|
||||
args = parser.parse_args()
|
||||
|
||||
metadata = get_cargo_metadata()
|
||||
if metadata is None:
|
||||
print("Error: Metadata is empty")
|
||||
exit(1)
|
||||
|
||||
packages, direct_dependents = find_dependents_of_package(metadata, args.package_source)
|
||||
|
||||
has_conflicts = check_for_version_conflicts(packages, direct_dependents)
|
||||
|
||||
if has_conflicts:
|
||||
exit(1)
|
||||
|
||||
@@ -154,8 +154,8 @@ update_workloads() {
|
||||
fi
|
||||
popd || exit
|
||||
|
||||
# Download prebuild linux binaries
|
||||
download_linux
|
||||
# Prepare linux image (build from source or download pre-built)
|
||||
prepare_linux
|
||||
|
||||
# Update the kernel in the cloud image for some tests that requires recent kernel version
|
||||
FOCAL_OS_RAW_IMAGE_UPDATE_KERNEL_NAME="focal-server-cloudimg-arm64-custom-20210929-0-update-kernel.raw"
|
||||
|
||||
@@ -56,8 +56,8 @@ popd || exit
|
||||
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
# Download prebuild linux binaries
|
||||
download_linux
|
||||
# Prepare linux image (build from source or download pre-built)
|
||||
prepare_linux
|
||||
fi
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
@@ -44,8 +44,8 @@ if ! grep focal sha1sums-x86_64 | sha1sum --check; then
|
||||
fi
|
||||
popd || exit
|
||||
|
||||
# Download prebuild linux binaries
|
||||
download_linux
|
||||
# Prepare linux image (build from source or download pre-built)
|
||||
prepare_linux
|
||||
|
||||
CFLAGS=""
|
||||
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||
|
||||
@@ -102,8 +102,8 @@ popd || exit
|
||||
# Build custom kernel based on virtio-pmem and virtio-fs upstream patches
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
if [ ! -f "$VMLINUX_IMAGE" ]; then
|
||||
# Download prebuild linux binaries
|
||||
download_linux
|
||||
# Prepare linux image (build from source or download pre-built)
|
||||
prepare_linux
|
||||
fi
|
||||
|
||||
VIRTIOFSD="$WORKLOADS_DIR/virtiofsd"
|
||||
|
||||
@@ -80,8 +80,8 @@ if [ "${TEST_ARCH}" == "aarch64" ]; then
|
||||
guestunmount "$FOCAL_OS_RAW_IMAGE_UPDATE_TOOL_ROOT_DIR"
|
||||
fi
|
||||
|
||||
# Download prebuild linux binaries
|
||||
download_linux
|
||||
# Prepare linux image (build from source or download pre-built)
|
||||
prepare_linux
|
||||
|
||||
CFLAGS=""
|
||||
if [[ "${BUILD_TARGET}" == "${TEST_ARCH}-unknown-linux-musl" ]]; then
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
hypervisor="kvm"
|
||||
test_filter=""
|
||||
build_kernel=false
|
||||
|
||||
# Checkout source code of a GIT repo with specified branch and commit
|
||||
# Args:
|
||||
@@ -75,11 +76,13 @@ cmd_help() {
|
||||
echo ""
|
||||
echo " --hypervisor Underlying hypervisor. Options kvm, mshv"
|
||||
echo " --test-filter Tests to run"
|
||||
echo " --build-guest-kernel Build guest kernel from source instead of downloading pre-built"
|
||||
echo ""
|
||||
echo " --help Display this help message."
|
||||
echo ""
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
process_common_args() {
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -93,9 +96,11 @@ process_common_args() {
|
||||
;;
|
||||
"--test-filter")
|
||||
shift
|
||||
# shellcheck disable=SC2034
|
||||
test_filter="$1"
|
||||
;;
|
||||
"--build-guest-kernel")
|
||||
build_kernel=true
|
||||
;;
|
||||
"--") {
|
||||
shift
|
||||
break
|
||||
@@ -115,14 +120,17 @@ process_common_args() {
|
||||
}
|
||||
|
||||
download_hypervisor_fw() {
|
||||
FW_TAG="0.5.0"
|
||||
if [ -n "$AUTH_DOWNLOAD_TOKEN" ]; then
|
||||
echo "Using authenticated download from GitHub"
|
||||
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest \
|
||||
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/tags/${FW_TAG} \
|
||||
--header "Authorization: Token $AUTH_DOWNLOAD_TOKEN" \
|
||||
--header "X-GitHub-Api-Version: 2022-11-28" | grep "browser_download_url" | grep -o 'https://.*[^ "]')
|
||||
--header "X-GitHub-Api-Version: 2022-11-28" | grep "browser_download_url" |
|
||||
grep -oP '"https://[^"]*hypervisor-fw"' | sed -e 's/^"//' -e 's/"$//')
|
||||
else
|
||||
echo "Using anonymous download from GitHub"
|
||||
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
|
||||
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/tags/${FW_TAG} |
|
||||
grep "browser_download_url" | grep -oP '"https://[^"]*hypervisor-fw"' | sed -e 's/^"//' -e 's/"$//')
|
||||
fi
|
||||
FW="$WORKLOADS_DIR/hypervisor-fw"
|
||||
pushd "$WORKLOADS_DIR" || exit
|
||||
@@ -149,6 +157,18 @@ download_linux() {
|
||||
popd || exit
|
||||
}
|
||||
|
||||
prepare_linux() {
|
||||
if [ "$build_kernel" = true ]; then
|
||||
echo "Building kernel from source"
|
||||
build_custom_linux
|
||||
echo "Using kernel built from source"
|
||||
else
|
||||
echo "Downloading pre-built kernel from GitHub"
|
||||
download_linux
|
||||
echo "Using kernel downloaded from GitHub"
|
||||
fi
|
||||
}
|
||||
|
||||
download_ovmf() {
|
||||
OVMF_FW_TAG="ch-6624aa331f"
|
||||
OVMF_FW_URL="https://github.com/cloud-hypervisor/edk2/releases/download/$OVMF_FW_TAG/CLOUDHV.fd"
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@@ -184,9 +184,10 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.about("Launch a cloud-hypervisor VMM.")
|
||||
.arg_required_else_help(true)
|
||||
.group(ArgGroup::new("vm-config").multiple(true))
|
||||
.group(ArgGroup::new("vm-config").multiple(true).requires("vm-payload"))
|
||||
.group(ArgGroup::new("vmm-config").multiple(true))
|
||||
.group(ArgGroup::new("logging").multiple(true))
|
||||
.group(ArgGroup::new("vm-payload").multiple(true))
|
||||
.arg(
|
||||
Arg::new("cpus")
|
||||
.long("cpus")
|
||||
@@ -243,7 +244,7 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
.long("firmware")
|
||||
.help("Path to firmware that is loaded in an architectural specific way")
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
.group("vm-payload"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("kernel")
|
||||
@@ -253,7 +254,7 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
entry point (e.g. vmlinux) or architecture equivalent",
|
||||
)
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
.group("vm-payload"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("initramfs")
|
||||
@@ -459,7 +460,7 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
.long("tpm")
|
||||
.num_args(1)
|
||||
.help(TpmConfig::SYNTAX)
|
||||
.group("vmm-config"),
|
||||
.group("vm-config"),
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -519,7 +520,7 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
.long("igvm")
|
||||
.help("Path to IGVM file to load.")
|
||||
.num_args(1)
|
||||
.group("vm-config"),
|
||||
.group("vm-payload"),
|
||||
);
|
||||
#[cfg(feature = "sev_snp")]
|
||||
let app = app.arg(
|
||||
|
||||
@@ -7,7 +7,7 @@ version = "0.1.0"
|
||||
[dependencies]
|
||||
dirs = "5.0.1"
|
||||
epoll = "4.3.3"
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
once_cell = "1.20.2"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.120"
|
||||
|
||||
@@ -12,12 +12,14 @@ extern crate test_infra;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{BufRead, Read, Seek, Write};
|
||||
use std::net::TcpListener;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::string::String;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::{mpsc, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::{fs, io, thread};
|
||||
|
||||
use net_util::MacAddr;
|
||||
@@ -9401,7 +9403,7 @@ mod live_migration {
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
@@ -9575,7 +9577,7 @@ mod live_migration {
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
@@ -9793,7 +9795,7 @@ mod live_migration {
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
@@ -10009,7 +10011,7 @@ mod live_migration {
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
@@ -10119,7 +10121,7 @@ mod live_migration {
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
@@ -10266,7 +10268,7 @@ mod live_migration {
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().map_or(false, |s| s.success()) {
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
@@ -10295,6 +10297,220 @@ mod live_migration {
|
||||
handle_child_output(r, &dest_output);
|
||||
}
|
||||
|
||||
// Function to get an available port
|
||||
fn get_available_port() -> u16 {
|
||||
TcpListener::bind("127.0.0.1:0")
|
||||
.expect("Failed to bind to address")
|
||||
.local_addr()
|
||||
.unwrap()
|
||||
.port()
|
||||
}
|
||||
|
||||
fn start_live_migration_tcp(src_api_socket: &str, dest_api_socket: &str) -> bool {
|
||||
// Get an available TCP port
|
||||
let migration_port = get_available_port();
|
||||
let host_ip = "127.0.0.1";
|
||||
|
||||
// Start the 'receive-migration' command on the destination
|
||||
let mut receive_migration = Command::new(clh_command("ch-remote"))
|
||||
.args([
|
||||
&format!("--api-socket={}", dest_api_socket),
|
||||
"receive-migration",
|
||||
&format!("tcp:0.0.0.0:{}", migration_port),
|
||||
])
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
// Give the destination some time to start listening
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
|
||||
// Start the 'send-migration' command on the source
|
||||
let mut send_migration = Command::new(clh_command("ch-remote"))
|
||||
.args([
|
||||
&format!("--api-socket={}", src_api_socket),
|
||||
"send-migration",
|
||||
&format!("tcp:{}:{}", host_ip, migration_port),
|
||||
])
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
// Check if the 'send-migration' command executed successfully
|
||||
let send_success = if let Some(status) = send_migration
|
||||
.wait_timeout(Duration::from_secs(60))
|
||||
.unwrap()
|
||||
{
|
||||
status.success()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !send_success {
|
||||
let _ = send_migration.kill();
|
||||
let output = send_migration.wait_with_output().unwrap();
|
||||
eprintln!(
|
||||
"\n\n==== Start 'send_migration' output ====\n\n---stdout---\n{}\n\n---stderr---\n{}\n\n==== End 'send_migration' output ====\n\n",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the 'receive-migration' command executed successfully
|
||||
let receive_success = if let Some(status) = receive_migration
|
||||
.wait_timeout(Duration::from_secs(60))
|
||||
.unwrap()
|
||||
{
|
||||
status.success()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !receive_success {
|
||||
let _ = receive_migration.kill();
|
||||
let output = receive_migration.wait_with_output().unwrap();
|
||||
eprintln!(
|
||||
"\n\n==== Start 'receive_migration' output ====\n\n---stdout---\n{}\n\n---stderr---\n{}\n\n==== End 'receive_migration' output ====\n\n",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
send_success && receive_success
|
||||
}
|
||||
|
||||
fn _test_live_migration_tcp() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
let kernel_path = direct_kernel_boot_path();
|
||||
let console_text = String::from("On a branch floating down river a cricket, singing.");
|
||||
let net_id = "net123";
|
||||
let net_params = format!(
|
||||
"id={},tap=,mac={},ip={},mask=255.255.255.0",
|
||||
net_id, guest.network.guest_mac, guest.network.host_ip
|
||||
);
|
||||
let memory_param: &[&str] = &["--memory", "size=4G,shared=on"];
|
||||
let boot_vcpus = 2;
|
||||
let max_vcpus = 4;
|
||||
let pmem_temp_file = TempFile::new().unwrap();
|
||||
pmem_temp_file.as_file().set_len(128 << 20).unwrap();
|
||||
std::process::Command::new("mkfs.ext4")
|
||||
.arg(pmem_temp_file.as_path())
|
||||
.output()
|
||||
.expect("Expect creating disk image to succeed");
|
||||
let pmem_path = String::from("/dev/pmem0");
|
||||
|
||||
// Start the source VM
|
||||
let src_vm_path = clh_command("cloud-hypervisor");
|
||||
let src_api_socket = temp_api_path(&guest.tmp_dir);
|
||||
let mut src_vm_cmd = GuestCommand::new_with_binary_path(&guest, &src_vm_path);
|
||||
src_vm_cmd
|
||||
.args([
|
||||
"--cpus",
|
||||
format!("boot={},max={}", boot_vcpus, max_vcpus).as_str(),
|
||||
])
|
||||
.args(memory_param)
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.default_disks()
|
||||
.args(["--net", net_params.as_str()])
|
||||
.args(["--api-socket", &src_api_socket])
|
||||
.args([
|
||||
"--pmem",
|
||||
format!(
|
||||
"file={},discard_writes=on",
|
||||
pmem_temp_file.as_path().to_str().unwrap(),
|
||||
)
|
||||
.as_str(),
|
||||
])
|
||||
.capture_output();
|
||||
let mut src_child = src_vm_cmd.spawn().unwrap();
|
||||
|
||||
// Start the destination VM
|
||||
let mut dest_api_socket = temp_api_path(&guest.tmp_dir);
|
||||
dest_api_socket.push_str(".dest");
|
||||
let mut dest_child = GuestCommand::new(&guest)
|
||||
.args(["--api-socket", &dest_api_socket])
|
||||
.capture_output()
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
guest.wait_vm_boot(None).unwrap();
|
||||
// Ensure the source VM is running normally
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), boot_vcpus);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 3_840_000);
|
||||
guest.check_devices_common(None, Some(&console_text), Some(&pmem_path));
|
||||
|
||||
// On x86_64 architecture, remove and re-add the virtio-net device
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
assert!(remote_command(
|
||||
&src_api_socket,
|
||||
"remove-device",
|
||||
Some(net_id),
|
||||
));
|
||||
thread::sleep(Duration::new(10, 0));
|
||||
// Re-add the virtio-net device
|
||||
assert!(remote_command(
|
||||
&src_api_socket,
|
||||
"add-net",
|
||||
Some(net_params.as_str()),
|
||||
));
|
||||
thread::sleep(Duration::new(10, 0));
|
||||
}
|
||||
// Start TCP live migration
|
||||
assert!(
|
||||
start_live_migration_tcp(&src_api_socket, &dest_api_socket),
|
||||
"Unsuccessful command: 'send-migration' or 'receive-migration'."
|
||||
);
|
||||
});
|
||||
|
||||
// Check and report any errors that occurred during live migration
|
||||
if r.is_err() {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
None,
|
||||
"Error occurred during live-migration",
|
||||
);
|
||||
}
|
||||
|
||||
// Check the source vm has been terminated successful (give it '3s' to settle)
|
||||
thread::sleep(std::time::Duration::new(3, 0));
|
||||
if !src_child.try_wait().unwrap().is_some_and(|s| s.success()) {
|
||||
print_and_panic(
|
||||
src_child,
|
||||
dest_child,
|
||||
None,
|
||||
"Source VM was not terminated successfully.",
|
||||
);
|
||||
};
|
||||
|
||||
// After live migration, ensure the destination VM is running normally
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
// Perform the same checks to ensure the VM has migrated correctly
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), boot_vcpus);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 3_840_000);
|
||||
guest.check_devices_common(None, Some(&console_text), Some(&pmem_path));
|
||||
});
|
||||
|
||||
// Clean up the destination VM and ensure it terminates properly
|
||||
let _ = dest_child.kill();
|
||||
let dest_output = dest_child.wait_with_output().unwrap();
|
||||
handle_child_output(r, &dest_output);
|
||||
|
||||
// Check if the expected `console_text` is present in the destination VM's output
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
assert!(String::from_utf8_lossy(&dest_output.stdout).contains(&console_text));
|
||||
});
|
||||
handle_child_output(r, &dest_output);
|
||||
}
|
||||
|
||||
mod live_migration_parallel {
|
||||
use super::*;
|
||||
#[test]
|
||||
@@ -10307,6 +10523,11 @@ mod live_migration {
|
||||
_test_live_migration(false, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_live_migration_tcp() {
|
||||
_test_live_migration_tcp();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_live_migration_watchdog() {
|
||||
_test_live_migration_watchdog(false, false)
|
||||
|
||||
@@ -5,7 +5,7 @@ name = "tracer"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
once_cell = "1.20.2"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
|
||||
@@ -10,11 +10,11 @@ block = { path = "../block" }
|
||||
clap = { version = "4.5.13", features = ["cargo", "wrap_help"] }
|
||||
env_logger = "0.11.3"
|
||||
epoll = "4.3.3"
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost = { workspace = true, features = ["vhost-user-backend"] }
|
||||
vhost-user-backend = "0.16.0"
|
||||
vhost-user-backend = { workspace = true }
|
||||
virtio-bindings = { workspace = true }
|
||||
virtio-queue = { workspace = true }
|
||||
vm-memory = { workspace = true }
|
||||
|
||||
@@ -9,12 +9,12 @@ version = "0.1.0"
|
||||
clap = { version = "4.5.13", features = ["cargo", "wrap_help"] }
|
||||
env_logger = "0.11.3"
|
||||
epoll = "4.3.3"
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
net_util = { path = "../net_util" }
|
||||
option_parser = { path = "../option_parser" }
|
||||
vhost = { workspace = true, features = ["vhost-user-backend"] }
|
||||
vhost-user-backend = "0.16.0"
|
||||
vhost-user-backend = { workspace = true }
|
||||
virtio-bindings = { workspace = true }
|
||||
vm-memory = { workspace = true }
|
||||
vmm-sys-util = { workspace = true }
|
||||
|
||||
@@ -9,13 +9,13 @@ default = []
|
||||
sev_snp = ["mshv-ioctls"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
arc-swap = "1.7.1"
|
||||
block = { path = "../block" }
|
||||
byteorder = "1.5.0"
|
||||
epoll = "4.3.3"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
log = "0.4.22"
|
||||
mshv-ioctls = { workspace = true, optional = true }
|
||||
net_gen = { path = "../net_gen" }
|
||||
|
||||
@@ -27,7 +27,7 @@ use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use virtio_bindings::virtio_blk::*;
|
||||
use virtio_bindings::virtio_config::*;
|
||||
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
use virtio_bindings::virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC};
|
||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError};
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
@@ -489,8 +489,7 @@ impl EpollHelperHandler for BlockEpollHandler {
|
||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||
})?;
|
||||
|
||||
let rate_limit_reached =
|
||||
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
|
||||
let rate_limit_reached = self.rate_limiter.as_ref().is_some_and(|r| r.is_blocked());
|
||||
|
||||
// Process the queue only when the rate limit is not reached
|
||||
if !rate_limit_reached {
|
||||
@@ -509,8 +508,7 @@ impl EpollHelperHandler for BlockEpollHandler {
|
||||
))
|
||||
})?;
|
||||
|
||||
let rate_limit_reached =
|
||||
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
|
||||
let rate_limit_reached = self.rate_limiter.as_ref().is_some_and(|r| r.is_blocked());
|
||||
|
||||
// Process the queue only when the rate limit is not reached
|
||||
if !rate_limit_reached {
|
||||
@@ -627,8 +625,8 @@ impl Block {
|
||||
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE)
|
||||
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
|
||||
| (1u64 << VIRTIO_BLK_F_TOPOLOGY)
|
||||
| (1u64 << VIRTIO_RING_F_EVENT_IDX);
|
||||
|
||||
| (1u64 << VIRTIO_RING_F_EVENT_IDX)
|
||||
| (1u64 << VIRTIO_RING_F_INDIRECT_DESC);
|
||||
if iommu {
|
||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ impl NetEpollHandler {
|
||||
.net
|
||||
.rx_rate_limiter
|
||||
.as_ref()
|
||||
.map_or(false, |r| r.is_blocked());
|
||||
.is_some_and(|r| r.is_blocked());
|
||||
|
||||
// Start to listen on RX_TAP_EVENT only when the rate limit is not reached
|
||||
if !self.net.rx_tap_listening && !rate_limit_reached {
|
||||
@@ -238,7 +238,7 @@ impl NetEpollHandler {
|
||||
.net
|
||||
.tx_rate_limiter
|
||||
.as_ref()
|
||||
.map_or(false, |r| r.is_blocked());
|
||||
.is_some_and(|r| r.is_blocked());
|
||||
|
||||
if !rate_limit_reached {
|
||||
self.process_tx()?;
|
||||
|
||||
@@ -4,9 +4,13 @@ edition = "2021"
|
||||
name = "vm-allocator"
|
||||
version = "0.1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
kvm = ["arch/kvm"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
vm-memory = { workspace = true }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||
arch = { path = "../arch" }
|
||||
|
||||
@@ -62,7 +62,7 @@ impl GsiAllocator {
|
||||
allocator
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
/// New GSI allocator
|
||||
pub fn new() -> Self {
|
||||
GsiAllocator {
|
||||
@@ -97,7 +97,7 @@ impl GsiAllocator {
|
||||
Ok(irq)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
/// Allocate an IRQ
|
||||
pub fn allocate_irq(&mut self) -> Result<u32> {
|
||||
let irq = self.next_irq;
|
||||
@@ -106,7 +106,7 @@ impl GsiAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
impl Default for GsiAllocator {
|
||||
fn default() -> Self {
|
||||
GsiAllocator::new()
|
||||
|
||||
@@ -21,3 +21,5 @@ pub use crate::gsi::GsiAllocator;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use crate::gsi::GsiApic;
|
||||
pub use crate::system::SystemAllocator;
|
||||
mod memory_slot;
|
||||
pub use memory_slot::MemorySlotAllocator;
|
||||
|
||||
39
vm-allocator/src/memory_slot.rs
Normal file
39
vm-allocator/src/memory_slot.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright © 2024 Rivos Inc
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Allocator for KVM memory slots
|
||||
pub struct MemorySlotAllocator {
|
||||
next_memory_slot: Arc<AtomicU32>,
|
||||
memory_slot_free_list: Arc<Mutex<Vec<u32>>>,
|
||||
}
|
||||
|
||||
impl MemorySlotAllocator {
|
||||
/// Next free memory slot
|
||||
pub fn next_memory_slot(&self) -> u32 {
|
||||
if let Some(slot_id) = self.memory_slot_free_list.lock().unwrap().pop() {
|
||||
return slot_id;
|
||||
}
|
||||
self.next_memory_slot.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Release memory slot for reuse
|
||||
pub fn free_memory_slot(&mut self, slot: u32) {
|
||||
self.memory_slot_free_list.lock().unwrap().push(slot)
|
||||
}
|
||||
|
||||
/// Instantiate struct
|
||||
pub fn new(
|
||||
next_memory_slot: Arc<AtomicU32>,
|
||||
memory_slot_free_list: Arc<Mutex<Vec<u32>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
next_memory_slot,
|
||||
memory_slot_free_list,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,27 +22,30 @@ use crate::page_size::get_page_size;
|
||||
/// ```
|
||||
/// # #[cfg(target_arch = "x86_64")]
|
||||
/// # use vm_allocator::{GsiApic, SystemAllocator};
|
||||
/// # #[cfg(target_arch = "aarch64")]
|
||||
/// # #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
/// # use vm_allocator::SystemAllocator;
|
||||
/// # use vm_memory::{Address, GuestAddress, GuestUsize};
|
||||
/// let mut allocator = SystemAllocator::new(
|
||||
/// #[cfg(target_arch = "x86_64")] GuestAddress(0x1000),
|
||||
/// #[cfg(target_arch = "x86_64")] 0x10000,
|
||||
/// GuestAddress(0x1000),
|
||||
/// 0x10000,
|
||||
/// GuestAddress(0x10000000), 0x10000000,
|
||||
/// #[cfg(target_arch = "x86_64")] vec![GsiApic::new(5, 19)]).unwrap();
|
||||
/// #[cfg(target_arch = "x86_64")]
|
||||
/// assert_eq!(allocator.allocate_irq(), Some(5));
|
||||
/// #[cfg(target_arch = "aarch64")]
|
||||
/// assert_eq!(allocator.allocate_irq(), Some(32));
|
||||
/// #[cfg(target_arch = "riscv64")]
|
||||
/// assert_eq!(allocator.allocate_irq(), Some(0));
|
||||
/// #[cfg(target_arch = "x86_64")]
|
||||
/// assert_eq!(allocator.allocate_irq(), Some(6));
|
||||
/// #[cfg(target_arch = "aarch64")]
|
||||
/// assert_eq!(allocator.allocate_irq(), Some(33));
|
||||
/// #[cfg(target_arch = "riscv64")]
|
||||
/// assert_eq!(allocator.allocate_irq(), Some(1));
|
||||
/// assert_eq!(allocator.allocate_platform_mmio_addresses(None, 0x1000, Some(0x1000)), Some(GuestAddress(0x1fff_f000)));
|
||||
///
|
||||
/// ```
|
||||
pub struct SystemAllocator {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_address_space: AddressAllocator,
|
||||
platform_mmio_address_space: AddressAllocator,
|
||||
gsi_allocator: GsiAllocator,
|
||||
@@ -59,14 +62,13 @@ impl SystemAllocator {
|
||||
/// * `apics` - (X86) Vector of APIC's.
|
||||
///
|
||||
pub fn new(
|
||||
#[cfg(target_arch = "x86_64")] io_base: GuestAddress,
|
||||
#[cfg(target_arch = "x86_64")] io_size: GuestUsize,
|
||||
io_base: GuestAddress,
|
||||
io_size: GuestUsize,
|
||||
platform_mmio_base: GuestAddress,
|
||||
platform_mmio_size: GuestUsize,
|
||||
#[cfg(target_arch = "x86_64")] apics: Vec<GsiApic>,
|
||||
) -> Option<Self> {
|
||||
Some(SystemAllocator {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_address_space: AddressAllocator::new(io_base, io_size)?,
|
||||
platform_mmio_address_space: AddressAllocator::new(
|
||||
platform_mmio_base,
|
||||
@@ -74,7 +76,7 @@ impl SystemAllocator {
|
||||
)?,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
gsi_allocator: GsiAllocator::new(apics),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
gsi_allocator: GsiAllocator::new(),
|
||||
})
|
||||
}
|
||||
@@ -89,7 +91,6 @@ impl SystemAllocator {
|
||||
self.gsi_allocator.allocate_gsi().ok()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Reserves a section of `size` bytes of IO address space.
|
||||
pub fn allocate_io_addresses(
|
||||
&mut self,
|
||||
@@ -115,7 +116,6 @@ impl SystemAllocator {
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
/// Free an IO address range.
|
||||
/// We can only free a range if it matches exactly an already allocated range.
|
||||
pub fn free_io_addresses(&mut self, address: GuestAddress, size: GuestUsize) {
|
||||
|
||||
@@ -10,7 +10,7 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
thiserror = "1.0.62"
|
||||
|
||||
@@ -5,7 +5,7 @@ name = "vm-migration"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
serde = { version = "1.0.208", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.120"
|
||||
thiserror = "1.0.62"
|
||||
|
||||
@@ -26,7 +26,7 @@ tracing = ["tracer/tracing"]
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { workspace = true }
|
||||
anyhow = "1.0.87"
|
||||
anyhow = "1.0.94"
|
||||
arc-swap = "1.7.1"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "2.6.0"
|
||||
@@ -38,7 +38,7 @@ devices = { path = "../devices" }
|
||||
dhat = { version = "0.3.3", optional = true }
|
||||
epoll = "4.3.3"
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
flume = "0.11.0"
|
||||
flume = "0.11.1"
|
||||
futures = { version = "0.3.30", optional = true }
|
||||
gdbstub = { version = "0.7.1", optional = true }
|
||||
gdbstub_arch = { version = "0.3.0", optional = true }
|
||||
@@ -47,7 +47,7 @@ hypervisor = { path = "../hypervisor" }
|
||||
igvm = { version = "0.3.3", optional = true }
|
||||
igvm_defs = { version = "0.3.1", optional = true }
|
||||
landlock = "0.4.0"
|
||||
libc = "0.2.158"
|
||||
libc = "0.2.167"
|
||||
linux-loader = { workspace = true, features = ["bzimage", "elf", "pe"] }
|
||||
log = "0.4.22"
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
|
||||
@@ -1144,7 +1144,8 @@ impl DiskConfig {
|
||||
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
|
||||
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,\
|
||||
id=<device_id>,pci_segment=<segment_id>,rate_limit_group=<group_id>,\
|
||||
queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>";
|
||||
queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>,\
|
||||
serial=<serial_number>";
|
||||
|
||||
pub fn parse(disk: &str) -> Result<Self> {
|
||||
let mut parser = OptionParser::new();
|
||||
|
||||
@@ -536,7 +536,6 @@ impl Console {
|
||||
|
||||
pub(crate) struct AddressManager {
|
||||
pub(crate) allocator: Arc<Mutex<SystemAllocator>>,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub(crate) io_bus: Arc<Bus>,
|
||||
pub(crate) mmio_bus: Arc<Bus>,
|
||||
pub(crate) vm: Arc<dyn hypervisor::Vm>,
|
||||
@@ -556,33 +555,24 @@ impl DeviceRelocation for AddressManager {
|
||||
) -> std::result::Result<(), std::io::Error> {
|
||||
match region_type {
|
||||
PciBarRegionType::IoRegion => {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
// Update system allocator
|
||||
self.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.free_io_addresses(GuestAddress(old_base), len as GuestUsize);
|
||||
// Update system allocator
|
||||
self.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.free_io_addresses(GuestAddress(old_base), len as GuestUsize);
|
||||
|
||||
self.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_io_addresses(
|
||||
Some(GuestAddress(new_base)),
|
||||
len as GuestUsize,
|
||||
None,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "failed allocating new IO range")
|
||||
})?;
|
||||
self.allocator
|
||||
.lock()
|
||||
.unwrap()
|
||||
.allocate_io_addresses(Some(GuestAddress(new_base)), len as GuestUsize, None)
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "failed allocating new IO range")
|
||||
})?;
|
||||
|
||||
// Update PIO bus
|
||||
self.io_bus
|
||||
.update_range(old_base, len, new_base, len)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
error!("I/O region is not supported");
|
||||
// Update PIO bus
|
||||
self.io_bus
|
||||
.update_range(old_base, len, new_base, len)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
}
|
||||
PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => {
|
||||
let allocators = if region_type == PciBarRegionType::Memory32BitRegion {
|
||||
@@ -992,7 +982,7 @@ fn create_mmio_allocators(
|
||||
impl DeviceManager {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
#[cfg(target_arch = "x86_64")] io_bus: Arc<Bus>,
|
||||
io_bus: Arc<Bus>,
|
||||
mmio_bus: Arc<Bus>,
|
||||
vm: Arc<dyn hypervisor::Vm>,
|
||||
config: Arc<Mutex<VmConfig>>,
|
||||
@@ -1072,7 +1062,6 @@ impl DeviceManager {
|
||||
|
||||
let address_manager = Arc::new(AddressManager {
|
||||
allocator: memory_manager.lock().unwrap().allocator(),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_bus,
|
||||
mmio_bus,
|
||||
vm: vm.clone(),
|
||||
@@ -3432,7 +3421,7 @@ impl DeviceManager {
|
||||
legacy_interrupt_group,
|
||||
device_cfg.iommu,
|
||||
pci_device_bdf,
|
||||
Arc::new(move || memory_manager.lock().unwrap().allocate_memory_slot()),
|
||||
memory_manager.lock().unwrap().memory_slot_allocator(),
|
||||
vm_migration::snapshot_from_id(self.snapshot.as_ref(), vfio_name.as_str()),
|
||||
device_cfg.x_nv_gpudirect_clique,
|
||||
)
|
||||
@@ -3512,7 +3501,6 @@ impl DeviceManager {
|
||||
pci_bus
|
||||
.register_mapping(
|
||||
bus_device,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
self.address_manager.io_bus.as_ref(),
|
||||
self.address_manager.mmio_bus.as_ref(),
|
||||
bars.clone(),
|
||||
@@ -3596,7 +3584,7 @@ impl DeviceManager {
|
||||
self.msi_interrupt_manager.clone(),
|
||||
legacy_interrupt_group,
|
||||
pci_device_bdf,
|
||||
Arc::new(move || memory_manager.lock().unwrap().allocate_memory_slot()),
|
||||
memory_manager.lock().unwrap().memory_slot_allocator(),
|
||||
vm_migration::snapshot_from_id(self.snapshot.as_ref(), vfio_user_name.as_str()),
|
||||
)
|
||||
.map_err(DeviceManagerError::VfioUserCreate)?;
|
||||
|
||||
233
vmm/src/lib.rs
233
vmm/src/lib.rs
@@ -11,6 +11,7 @@ extern crate log;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{stdout, Read, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::panic::AssertUnwindSafe;
|
||||
@@ -36,8 +37,8 @@ use serde::{Deserialize, Serialize};
|
||||
use signal_hook::iterator::{Handle, Signals};
|
||||
use thiserror::Error;
|
||||
use tracer::trace_scoped;
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::{ReadVolatile, WriteVolatile};
|
||||
use vm_memory::bitmap::{AtomicBitmap, BitmapSlice};
|
||||
use vm_memory::{ReadVolatile, VolatileMemoryError, VolatileSlice, WriteVolatile};
|
||||
use vm_migration::protocol::*;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
@@ -235,6 +236,89 @@ impl From<u64> for EpollDispatch {
|
||||
}
|
||||
}
|
||||
|
||||
enum SocketStream {
|
||||
Unix(UnixStream),
|
||||
Tcp(TcpStream),
|
||||
}
|
||||
|
||||
impl Read for SocketStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
match self {
|
||||
SocketStream::Unix(stream) => stream.read(buf),
|
||||
SocketStream::Tcp(stream) => stream.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for SocketStream {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
match self {
|
||||
SocketStream::Unix(stream) => stream.write(buf),
|
||||
SocketStream::Tcp(stream) => stream.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
match self {
|
||||
SocketStream::Unix(stream) => stream.flush(),
|
||||
SocketStream::Tcp(stream) => stream.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for SocketStream {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
match self {
|
||||
SocketStream::Unix(s) => s.as_raw_fd(),
|
||||
SocketStream::Tcp(s) => s.as_raw_fd(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadVolatile for SocketStream {
|
||||
fn read_volatile<B: BitmapSlice>(
|
||||
&mut self,
|
||||
buf: &mut VolatileSlice<B>,
|
||||
) -> std::result::Result<usize, VolatileMemoryError> {
|
||||
match self {
|
||||
SocketStream::Unix(s) => s.read_volatile(buf),
|
||||
SocketStream::Tcp(s) => s.read_volatile(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_exact_volatile<B: BitmapSlice>(
|
||||
&mut self,
|
||||
buf: &mut VolatileSlice<B>,
|
||||
) -> std::result::Result<(), VolatileMemoryError> {
|
||||
match self {
|
||||
SocketStream::Unix(s) => s.read_exact_volatile(buf),
|
||||
SocketStream::Tcp(s) => s.read_exact_volatile(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteVolatile for SocketStream {
|
||||
fn write_volatile<B: BitmapSlice>(
|
||||
&mut self,
|
||||
buf: &VolatileSlice<B>,
|
||||
) -> std::result::Result<usize, VolatileMemoryError> {
|
||||
match self {
|
||||
SocketStream::Unix(s) => s.write_volatile(buf),
|
||||
SocketStream::Tcp(s) => s.write_volatile(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_all_volatile<B: BitmapSlice>(
|
||||
&mut self,
|
||||
buf: &VolatileSlice<B>,
|
||||
) -> std::result::Result<(), VolatileMemoryError> {
|
||||
match self {
|
||||
SocketStream::Unix(s) => s.write_all_volatile(buf),
|
||||
SocketStream::Tcp(s) => s.write_all_volatile(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EpollContext {
|
||||
epoll_file: File,
|
||||
}
|
||||
@@ -786,6 +870,8 @@ impl Vmm {
|
||||
false,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
false,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
config.lock().unwrap().memory.total_size(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!(
|
||||
@@ -919,14 +1005,72 @@ impl Vmm {
|
||||
.map(|s| s.into())
|
||||
}
|
||||
|
||||
fn send_migration_socket(
|
||||
destination_url: &str,
|
||||
) -> std::result::Result<SocketStream, MigratableError> {
|
||||
if let Some(address) = destination_url.strip_prefix("tcp:") {
|
||||
info!("Connecting to TCP socket at {}", address);
|
||||
|
||||
let socket = TcpStream::connect(address).map_err(|e| {
|
||||
MigratableError::MigrateSend(anyhow!("Error connecting to TCP socket: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(SocketStream::Tcp(socket))
|
||||
} else {
|
||||
let path = Vmm::socket_url_to_path(destination_url)?;
|
||||
info!("Connecting to UNIX socket at {:?}", path);
|
||||
|
||||
let socket = UnixStream::connect(&path).map_err(|e| {
|
||||
MigratableError::MigrateSend(anyhow!("Error connecting to UNIX socket: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(SocketStream::Unix(socket))
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_migration_socket(
|
||||
receiver_url: &str,
|
||||
) -> std::result::Result<SocketStream, MigratableError> {
|
||||
if let Some(address) = receiver_url.strip_prefix("tcp:") {
|
||||
let listener = TcpListener::bind(address).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error binding to TCP socket: {}", e))
|
||||
})?;
|
||||
|
||||
let (socket, _addr) = listener.accept().map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!(
|
||||
"Error accepting connection on TCP socket: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(SocketStream::Tcp(socket))
|
||||
} else {
|
||||
let path = Vmm::socket_url_to_path(receiver_url)?;
|
||||
let listener = UnixListener::bind(&path).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error binding to UNIX socket: {}", e))
|
||||
})?;
|
||||
|
||||
let (socket, _addr) = listener.accept().map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!(
|
||||
"Error accepting connection on UNIX socket: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
// Remove the UNIX socket file after accepting the connection
|
||||
std::fs::remove_file(&path).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error removing UNIX socket file: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(SocketStream::Unix(socket))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if there were dirty pages to send
|
||||
fn vm_maybe_send_dirty_pages<T>(
|
||||
fn vm_maybe_send_dirty_pages(
|
||||
vm: &mut Vm,
|
||||
socket: &mut T,
|
||||
) -> result::Result<bool, MigratableError>
|
||||
where
|
||||
T: Read + Write + WriteVolatile,
|
||||
{
|
||||
socket: &mut SocketStream,
|
||||
) -> result::Result<bool, MigratableError> {
|
||||
// Send (dirty) memory table
|
||||
let table = vm.dirty_log()?;
|
||||
|
||||
@@ -954,10 +1098,8 @@ impl Vmm {
|
||||
>,
|
||||
send_data_migration: VmSendMigrationData,
|
||||
) -> result::Result<(), MigratableError> {
|
||||
let path = Self::socket_url_to_path(&send_data_migration.destination_url)?;
|
||||
let mut socket = UnixStream::connect(path).map_err(|e| {
|
||||
MigratableError::MigrateSend(anyhow!("Error connecting to UNIX socket: {}", e))
|
||||
})?;
|
||||
// Set up the socket connection
|
||||
let mut socket = Self::send_migration_socket(&send_data_migration.destination_url)?;
|
||||
|
||||
// Start the migration
|
||||
Request::start().write_to(&mut socket)?;
|
||||
@@ -997,7 +1139,17 @@ impl Vmm {
|
||||
};
|
||||
|
||||
if send_data_migration.local {
|
||||
vm.send_memory_fds(&mut socket)?;
|
||||
match &mut socket {
|
||||
SocketStream::Unix(unix_socket) => {
|
||||
// Proceed with sending memory file descriptors over UNIX socket
|
||||
vm.send_memory_fds(unix_socket)?;
|
||||
}
|
||||
SocketStream::Tcp(_tcp_socket) => {
|
||||
return Err(MigratableError::MigrateSend(anyhow!(
|
||||
"--local option is not supported with TCP sockets",
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let vm_migration_config = VmMigrationConfig {
|
||||
@@ -1958,16 +2110,8 @@ impl RequestHandler for Vmm {
|
||||
receive_data_migration.receiver_url
|
||||
);
|
||||
|
||||
let path = Self::socket_url_to_path(&receive_data_migration.receiver_url)?;
|
||||
let listener = UnixListener::bind(&path).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error binding to UNIX socket: {}", e))
|
||||
})?;
|
||||
let (mut socket, _addr) = listener.accept().map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error accepting on UNIX socket: {}", e))
|
||||
})?;
|
||||
std::fs::remove_file(&path).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error unlinking UNIX socket: {}", e))
|
||||
})?;
|
||||
// Accept the connection and get the socket
|
||||
let mut socket = Vmm::receive_migration_socket(&receive_data_migration.receiver_url)?;
|
||||
|
||||
let mut started = false;
|
||||
let mut memory_manager: Option<Arc<Mutex<MemoryManager>>> = None;
|
||||
@@ -2035,24 +2179,35 @@ impl RequestHandler for Vmm {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
let (_, file) = socket.recv_with_fd(&mut buf).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!(
|
||||
"Error receiving slot from socket: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
match &mut socket {
|
||||
SocketStream::Unix(unix_socket) => {
|
||||
let mut buf = [0u8; 4];
|
||||
let (_, file) = unix_socket.recv_with_fd(&mut buf).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!(
|
||||
"Error receiving slot from socket: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
if existing_memory_files.is_none() {
|
||||
existing_memory_files = Some(HashMap::default())
|
||||
if existing_memory_files.is_none() {
|
||||
existing_memory_files = Some(HashMap::default())
|
||||
}
|
||||
|
||||
if let Some(ref mut existing_memory_files) = existing_memory_files {
|
||||
let slot = u32::from_le_bytes(buf);
|
||||
existing_memory_files.insert(slot, file.unwrap());
|
||||
}
|
||||
|
||||
Response::ok().write_to(&mut socket)?;
|
||||
}
|
||||
SocketStream::Tcp(_tcp_socket) => {
|
||||
// For TCP sockets, we cannot transfer file descriptors
|
||||
warn!(
|
||||
"MemoryFd command received over TCP socket, which is not supported"
|
||||
);
|
||||
Response::error().write_to(&mut socket)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut existing_memory_files) = existing_memory_files {
|
||||
let slot = u32::from_le_bytes(buf);
|
||||
existing_memory_files.insert(slot, file.unwrap());
|
||||
}
|
||||
|
||||
Response::ok().write_to(&mut socket)?;
|
||||
}
|
||||
Command::Complete => {
|
||||
info!("Complete Command Received");
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::ops::{BitAnd, Deref, Not, Sub};
|
||||
use std::os::fd::AsFd;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::{ffi, result, thread};
|
||||
|
||||
@@ -33,7 +34,7 @@ use tracer::trace_scoped;
|
||||
use virtio_devices::BlocksState;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vm_allocator::GsiApic;
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_allocator::{AddressAllocator, MemorySlotAllocator, SystemAllocator};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::guest_memory::FileOffset;
|
||||
@@ -162,7 +163,8 @@ struct ArchMemRegion {
|
||||
pub struct MemoryManager {
|
||||
boot_guest_memory: GuestMemoryMmap,
|
||||
guest_memory: GuestMemoryAtomic<GuestMemoryMmap>,
|
||||
next_memory_slot: u32,
|
||||
next_memory_slot: Arc<AtomicU32>,
|
||||
memory_slot_free_list: Arc<Mutex<Vec<u32>>>,
|
||||
start_of_device_area: GuestAddress,
|
||||
end_of_device_area: GuestAddress,
|
||||
end_of_ram_area: GuestAddress,
|
||||
@@ -1155,17 +1157,10 @@ impl MemoryManager {
|
||||
|
||||
let guest_memory = GuestMemoryAtomic::new(guest_memory);
|
||||
|
||||
// Both MMIO and PIO address spaces start at address 0.
|
||||
let allocator = Arc::new(Mutex::new(
|
||||
SystemAllocator::new(
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
GuestAddress(0)
|
||||
},
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
1 << 16
|
||||
},
|
||||
GuestAddress(0),
|
||||
1 << 16,
|
||||
start_of_platform_device_area,
|
||||
PLATFORM_DEVICE_AREA_SIZE,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -1205,7 +1200,8 @@ impl MemoryManager {
|
||||
let mut memory_manager = MemoryManager {
|
||||
boot_guest_memory,
|
||||
guest_memory,
|
||||
next_memory_slot,
|
||||
next_memory_slot: Arc::new(AtomicU32::new(next_memory_slot)),
|
||||
memory_slot_free_list: Arc::new(Mutex::new(Vec::new())),
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
end_of_ram_area,
|
||||
@@ -1729,10 +1725,14 @@ impl MemoryManager {
|
||||
self.end_of_device_area
|
||||
}
|
||||
|
||||
pub fn memory_slot_allocator(&mut self) -> MemorySlotAllocator {
|
||||
let memory_slot_free_list = Arc::clone(&self.memory_slot_free_list);
|
||||
let next_memory_slot = Arc::clone(&self.next_memory_slot);
|
||||
MemorySlotAllocator::new(next_memory_slot, memory_slot_free_list)
|
||||
}
|
||||
|
||||
pub fn allocate_memory_slot(&mut self) -> u32 {
|
||||
let slot_id = self.next_memory_slot;
|
||||
self.next_memory_slot += 1;
|
||||
slot_id
|
||||
self.memory_slot_allocator().next_memory_slot()
|
||||
}
|
||||
|
||||
pub fn create_userspace_mapping(
|
||||
@@ -2120,7 +2120,7 @@ impl MemoryManager {
|
||||
current_ram: self.current_ram,
|
||||
arch_mem_regions: self.arch_mem_regions.clone(),
|
||||
hotplug_slots: self.hotplug_slots.clone(),
|
||||
next_memory_slot: self.next_memory_slot,
|
||||
next_memory_slot: self.next_memory_slot.load(Ordering::SeqCst),
|
||||
selected_slot: self.selected_slot,
|
||||
next_hotplug_slot: self.next_hotplug_slot,
|
||||
}
|
||||
|
||||
@@ -531,7 +531,6 @@ impl Vm {
|
||||
let stop_on_boot = false;
|
||||
|
||||
let memory = memory_manager.lock().unwrap().guest_memory();
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let io_bus = Arc::new(Bus::new());
|
||||
let mmio_bus = Arc::new(Bus::new());
|
||||
|
||||
@@ -622,7 +621,6 @@ impl Vm {
|
||||
let dynamic = true;
|
||||
|
||||
let device_manager = DeviceManager::new(
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
io_bus,
|
||||
mmio_bus,
|
||||
vm.clone(),
|
||||
@@ -827,6 +825,8 @@ impl Vm {
|
||||
tdx_enabled,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
sev_snp_enabled,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
vm_config.lock().unwrap().memory.total_size(),
|
||||
)?;
|
||||
|
||||
let phys_bits = physical_bits(&hypervisor, vm_config.lock().unwrap().cpus.max_phys_bits);
|
||||
@@ -885,6 +885,7 @@ impl Vm {
|
||||
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
|
||||
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||
#[cfg(feature = "sev_snp")] sev_snp_enabled: bool,
|
||||
#[cfg(feature = "sev_snp")] mem_size: u64,
|
||||
) -> Result<Arc<dyn hypervisor::Vm>> {
|
||||
hypervisor.check_required_extensions().unwrap();
|
||||
|
||||
@@ -901,7 +902,7 @@ impl Vm {
|
||||
// Otherwise SEV_SNP_DISABLED: 0
|
||||
// value of sev_snp_enabled is mapped to SEV_SNP_ENABLED for true or SEV_SNP_DISABLED for false
|
||||
let vm = hypervisor
|
||||
.create_vm_with_type(u64::from(sev_snp_enabled))
|
||||
.create_vm_with_type_and_memory(u64::from(sev_snp_enabled), mem_size)
|
||||
.unwrap();
|
||||
} else {
|
||||
let vm = hypervisor.create_vm().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user