mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
162 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a24680abd | ||
|
|
736813f90c | ||
|
|
b15459106c | ||
|
|
5fb078305a | ||
|
|
aac240d076 | ||
|
|
37d71fa038 | ||
|
|
8e52bf251b | ||
|
|
d75bd1675c | ||
|
|
d696cea024 | ||
|
|
7288031c0e | ||
|
|
82e8002fa0 | ||
|
|
a8d1411307 | ||
|
|
9569809d4c | ||
|
|
603b5e862c | ||
|
|
53092359b4 | ||
|
|
0ddb032cab | ||
|
|
bcc6ac68e9 | ||
|
|
4b4954ff86 | ||
|
|
5c5f33050c | ||
|
|
be5b14ef3a | ||
|
|
9dc923f379 | ||
|
|
f57b9b2359 | ||
|
|
6bda6541be | ||
|
|
4592f37bcf | ||
|
|
41a8dcd9ba | ||
|
|
8de24b5f08 | ||
|
|
0be7d1bd82 | ||
|
|
3191ff26e1 | ||
|
|
9979e562b2 | ||
|
|
70146ee7af | ||
|
|
3d049765bd | ||
|
|
f104bcdb6b | ||
|
|
265d21b0c4 | ||
|
|
562af123d5 | ||
|
|
f0dfa2a5ef | ||
|
|
7c8372452b | ||
|
|
f795081bf9 | ||
|
|
c940f6642f | ||
|
|
f56adb8a5a | ||
|
|
e6dd429a64 | ||
|
|
248e786363 | ||
|
|
f1ffd795e0 | ||
|
|
efad6578d1 | ||
|
|
9e2e85a48f | ||
|
|
f7edfefe42 | ||
|
|
801059c2d5 | ||
|
|
5ec1e66e22 | ||
|
|
01383538f3 | ||
|
|
5fb50edb55 | ||
|
|
658a7f9175 | ||
|
|
d8360ddc8e | ||
|
|
f10d0bb33d | ||
|
|
0d5c914bdd | ||
|
|
deaf660a52 | ||
|
|
6897c2a462 | ||
|
|
85556951a6 | ||
|
|
4abebc9e56 | ||
|
|
a91235dab1 | ||
|
|
dbb148b216 | ||
|
|
1861bc49e7 | ||
|
|
3d43245608 | ||
|
|
b163fea66a | ||
|
|
afcb2b285f | ||
|
|
c53781bf5f | ||
|
|
ed4af3a005 | ||
|
|
ac5fc69b2e | ||
|
|
0d884d3f50 | ||
|
|
8e5dd296de | ||
|
|
2f8edc182e | ||
|
|
41382879d3 | ||
|
|
e7cda177cc | ||
|
|
e3fb425615 | ||
|
|
6a86c157af | ||
|
|
0a07c96d17 | ||
|
|
a0b72dce22 | ||
|
|
d2b19bb969 | ||
|
|
dda89d7027 | ||
|
|
a1a018bd83 | ||
|
|
8c50450002 | ||
|
|
7443a9f69e | ||
|
|
28b54df01d | ||
|
|
4e93f85ab1 | ||
|
|
bcf23c1bf6 | ||
|
|
67fc9d990e | ||
|
|
2157f1a1f2 | ||
|
|
adb7cfd72c | ||
|
|
1f70e20c17 | ||
|
|
29e8a242b6 | ||
|
|
d32b2dc137 | ||
|
|
7de45b3a75 | ||
|
|
3709105043 | ||
|
|
2c1eccc6bc | ||
|
|
6f4b5253b4 | ||
|
|
492f24c632 | ||
|
|
47dff4f6bc | ||
|
|
b3f929432e | ||
|
|
e39174ac51 | ||
|
|
5f66a26b2e | ||
|
|
75cdfb0117 | ||
|
|
e6d31a3d81 | ||
|
|
94ed7c1745 | ||
|
|
aa67250049 | ||
|
|
4d79709b5e | ||
|
|
e993310c96 | ||
|
|
16fbab30b1 | ||
|
|
f5d2973546 | ||
|
|
2be304b392 | ||
|
|
969a3b57a3 | ||
|
|
021f450cdb | ||
|
|
0e21b56aea | ||
|
|
8be28f8438 | ||
|
|
12c7cc5e4f | ||
|
|
8f6a6a85e0 | ||
|
|
06b76972e2 | ||
|
|
199d2d05d8 | ||
|
|
42522a88c0 | ||
|
|
fdc19ad85e | ||
|
|
00f0b9e42c | ||
|
|
0ff8d1cb28 | ||
|
|
b6032bc492 | ||
|
|
95b8c6afdd | ||
|
|
f02745a7ed | ||
|
|
fed010fcd1 | ||
|
|
b4c62bf159 | ||
|
|
ea4f07d3bf | ||
|
|
7cb73e9e56 | ||
|
|
06390342a6 | ||
|
|
11d17fbf79 | ||
|
|
e160a17131 | ||
|
|
7364fbdc8e | ||
|
|
35b91f76af | ||
|
|
d1680b9ff9 | ||
|
|
c990f1bdaa | ||
|
|
e3e9e1c84c | ||
|
|
be495ec64a | ||
|
|
8ee26286ac | ||
|
|
935332bc42 | ||
|
|
e4fd066d82 | ||
|
|
a7fa3a0c86 | ||
|
|
b6c266c880 | ||
|
|
fc99e299c3 | ||
|
|
3d5f9a3a98 | ||
|
|
ad9034ed1d | ||
|
|
96f4e33897 | ||
|
|
6147c4c8b7 | ||
|
|
f9076cccfa | ||
|
|
9f046f02a2 | ||
|
|
3734a13cbf | ||
|
|
2707b0f72a | ||
|
|
176023156e | ||
|
|
5051feb0bd | ||
|
|
ec57aade15 | ||
|
|
02da2f2d36 | ||
|
|
62345cd6fd | ||
|
|
f826d92601 | ||
|
|
063aa4b7d5 | ||
|
|
d586c844de | ||
|
|
b3b51bd3a2 | ||
|
|
8bf284e713 | ||
|
|
e023efce3d | ||
|
|
bb7730e00f | ||
|
|
dd66eb834c |
3
.config/nextest.toml
Normal file
3
.config/nextest.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[profile.default]
|
||||
# Don't let one individual test run for more than 10 minutes
|
||||
slow-timeout = { period = "60s", terminate-after = 10 }
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -11,6 +11,7 @@ updates:
|
||||
- dependency-name: "kvm-bindings"
|
||||
- dependency-name: "kvm-ioctls"
|
||||
- dependency-name: "linux-loader"
|
||||
- dependency-name: "micro_http"
|
||||
- dependency-name: "mshv-bindings"
|
||||
- dependency-name: "mshv-ioctls"
|
||||
- dependency-name: "seccompiler"
|
||||
@@ -41,6 +42,7 @@ updates:
|
||||
- dependency-name: "kvm-bindings"
|
||||
- dependency-name: "kvm-ioctls"
|
||||
- dependency-name: "linux-loader"
|
||||
- dependency-name: "micro_http"
|
||||
- dependency-name: "mshv-bindings"
|
||||
- dependency-name: "mshv-ioctls"
|
||||
- dependency-name: "seccompiler"
|
||||
|
||||
2
.github/workflows/audit.yaml
vendored
2
.github/workflows/audit.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
name: Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions-rust-lang/audit@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
28
.github/workflows/build.yaml
vendored
28
.github/workflows/build.yaml
vendored
@@ -15,13 +15,13 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.88.0"
|
||||
- "1.89.0"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -35,40 +35,40 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Build (default features)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor
|
||||
|
||||
- name: Build (kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "kvm"
|
||||
|
||||
- name: Build (default features + tdx)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --features "tdx"
|
||||
|
||||
- name: Build (default features + dbus_api)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "dbus_api" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --features "dbus_api"
|
||||
|
||||
- name: Build (default features + guest_debug)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --features "guest_debug"
|
||||
|
||||
- name: Build (default features + pvmemcontrol)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --features "pvmemcontrol"
|
||||
|
||||
- name: Build (default features + fw_cfg)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "fw_cfg" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --features "fw_cfg"
|
||||
|
||||
- name: Build (default features + ivshmem)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --features "ivshmem" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --features "ivshmem"
|
||||
|
||||
- name: Build (mshv)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv"
|
||||
|
||||
- name: Build (sev_snp)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "sev_snp"
|
||||
|
||||
- name: Build (igvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "igvm"
|
||||
|
||||
- name: Build (mshv + kvm)
|
||||
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm"
|
||||
|
||||
- name: Release Build (default features)
|
||||
run: cargo build --locked --all --release --target=${{ matrix.target }}
|
||||
|
||||
4
.github/workflows/dco.yaml
vendored
4
.github/workflows/dco.yaml
vendored
@@ -3,10 +3,10 @@ on: [pull_request, merge_group]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: DCO Check ("Signed-Off-By")
|
||||
name: DCO Check ("Signed-off-by")
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
|
||||
4
.github/workflows/docker-image.yaml
vendored
4
.github/workflows/docker-image.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value=20250815-0
|
||||
type=raw,value=20251114-0
|
||||
type=sha
|
||||
|
||||
- name: Build and push
|
||||
|
||||
2
.github/workflows/formatting.yaml
vendored
2
.github/workflows/formatting.yaml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
RUSTFLAGS: -D warnings
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
||||
2
.github/workflows/fuzz-build.yaml
vendored
2
.github/workflows/fuzz-build.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
RUSTFLAGS: -D warnings
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
||||
2
.github/workflows/gitlint.yaml
vendored
2
.github/workflows/gitlint.yaml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
2
.github/workflows/hadolint.yaml
vendored
2
.github/workflows/hadolint.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Lint Dockerfile
|
||||
uses: hadolint/hadolint-action@master
|
||||
|
||||
2
.github/workflows/integration-arm64.yaml
vendored
2
.github/workflows/integration-arm64.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- name: Fix workspace permissions
|
||||
run: sudo chown -R runner:runner ${GITHUB_WORKSPACE}
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run unit tests (musl)
|
||||
|
||||
2
.github/workflows/integration-metrics.yaml
vendored
2
.github/workflows/integration-metrics.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
METRICS_PUBLISH_KEY: ${{ secrets.METRICS_PUBLISH_KEY }}
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run metrics tests
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- name: Code checkout
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run rate-limiter integration tests
|
||||
|
||||
2
.github/workflows/integration-vfio.yaml
vendored
2
.github/workflows/integration-vfio.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
run: sudo chown -R runner:runner ${GITHUB_WORKSPACE}
|
||||
- name: Code checkout
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run VFIO integration tests
|
||||
|
||||
2
.github/workflows/integration-windows.yaml
vendored
2
.github/workflows/integration-windows.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
- name: Code checkout
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Docker
|
||||
|
||||
2
.github/workflows/integration-x86-64.yaml
vendored
2
.github/workflows/integration-x86-64.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
steps:
|
||||
- name: Code checkout
|
||||
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Docker
|
||||
|
||||
2
.github/workflows/lychee.yaml
vendored
2
.github/workflows/lychee.yaml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
# Fetch the entire history so git diff can compare against the base branch
|
||||
fetch-depth: 0
|
||||
|
||||
14
.github/workflows/mshv-infra.yaml
vendored
14
.github/workflows/mshv-infra.yaml
vendored
@@ -34,6 +34,12 @@ on:
|
||||
USERNAME:
|
||||
required: true
|
||||
outputs:
|
||||
RG_NAME:
|
||||
description: 'Resource group of the VM'
|
||||
value: ${{ jobs.infra-setup.outputs.RG_NAME }}
|
||||
VM_NAME:
|
||||
description: 'Name of the VM'
|
||||
value: ${{ jobs.infra-setup.outputs.VM_NAME }}
|
||||
PRIVATE_IP:
|
||||
description: 'Private IP of the VM'
|
||||
value: ${{ jobs.infra-setup.outputs.PRIVATE_IP }}
|
||||
@@ -46,6 +52,8 @@ jobs:
|
||||
runs-on: mshv
|
||||
continue-on-error: true
|
||||
outputs:
|
||||
RG_NAME: ${{ steps.rg-setup.outputs.RG_NAME }}
|
||||
VM_NAME: ${{ steps.vm-setup.outputs.VM_NAME }}
|
||||
PRIVATE_IP: ${{ steps.get-vm-ip.outputs.PRIVATE_IP }}
|
||||
steps:
|
||||
- name: Install & login to AZ CLI
|
||||
@@ -112,6 +120,7 @@ jobs:
|
||||
# Create the resource group
|
||||
echo "Creating resource group in location: ${LOCATION}"
|
||||
az group create --name ${RG} --location ${LOCATION}
|
||||
echo "RG_NAME=${RG}" >> $GITHUB_OUTPUT
|
||||
echo "Resource group created successfully."
|
||||
|
||||
- name: Generate SSH Key
|
||||
@@ -173,6 +182,9 @@ jobs:
|
||||
--security-type Standard \
|
||||
--output json
|
||||
|
||||
az vm boot-diagnostics enable --name ${VM_NAME} --resource-group ${RG}
|
||||
|
||||
echo "VM_NAME=${VM_NAME}" >> $GITHUB_OUTPUT
|
||||
echo "VM creation process completed successfully."
|
||||
|
||||
- name: Get VM Private IP
|
||||
@@ -234,4 +246,4 @@ jobs:
|
||||
sudo groupadd -f docker
|
||||
sudo usermod -a -G docker ${USERNAME}
|
||||
sudo systemctl restart docker
|
||||
EOF
|
||||
EOF
|
||||
|
||||
24
.github/workflows/mshv-integration.yaml
vendored
24
.github/workflows/mshv-integration.yaml
vendored
@@ -79,6 +79,28 @@ jobs:
|
||||
sudo ./scripts/dev_cli.sh tests --hypervisor mshv --integration
|
||||
EOF
|
||||
|
||||
- name: Dump dmesg
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
env:
|
||||
KEY: azure_key_${{ github.run_id }}
|
||||
PRIVATE_IP: ${{ needs.infra-setup.outputs.PRIVATE_IP }}
|
||||
USERNAME: ${{ secrets.MSHV_USERNAME }}
|
||||
run: |
|
||||
ssh -i ~/.ssh/${KEY} -o StrictHostKeyChecking=no ${USERNAME}@${PRIVATE_IP} << EOF
|
||||
sudo dmesg
|
||||
EOF
|
||||
|
||||
- name: Dump serial console logs
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
env:
|
||||
RG_NAME: ${{ needs.infra-setup.outputs.RG_NAME }}
|
||||
VM_NAME: ${{ needs.infra-setup.outputs.VM_NAME }}
|
||||
run: |
|
||||
set -e
|
||||
az vm boot-diagnostics get-boot-log --name "${VM_NAME}" --resource-group "${RG_NAME}" | jq -r
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
needs: run-tests
|
||||
@@ -106,4 +128,4 @@ jobs:
|
||||
else
|
||||
echo "SSH key does not exist. Skipping deletion."
|
||||
fi
|
||||
echo "Cleanup process completed."
|
||||
echo "Cleanup process completed."
|
||||
|
||||
2
.github/workflows/openapi.yaml
vendored
2
.github/workflows/openapi.yaml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: openapitools/openapi-generator-cli
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: Validate OpenAPI
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/package-consistency.yaml
vendored
2
.github/workflows/package-consistency.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
8
.github/workflows/preview-riscv64-build.yaml
vendored
8
.github/workflows/preview-riscv64-build.yaml
vendored
@@ -13,18 +13,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.88.0
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.89.0
|
||||
|
||||
- name: Build test (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo rustc --locked --no-default-features --features "kvm"
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo build --locked --no-default-features --features "kvm" -p cloud-hypervisor
|
||||
|
||||
- name: Clippy test (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo clippy --locked --no-default-features --features "kvm"
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo clippy --locked --no-default-features --features "kvm" -p cloud-hypervisor
|
||||
|
||||
- name: Check no files were modified
|
||||
run: test -z "$(git status --porcelain)"
|
||||
|
||||
@@ -19,18 +19,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.88.0
|
||||
run: /opt/scripts/exec-in-qemu.sh rustup default 1.89.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
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo build --locked -p ${{ matrix.module }} --no-default-features --features "kvm"
|
||||
|
||||
- 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
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo clippy --locked -p ${{ matrix.module }} --no-default-features --features "kvm" -- -D warnings
|
||||
|
||||
- name: Test ${{ matrix.module }} Module (kvm)
|
||||
run: /opt/scripts/exec-in-qemu.sh cargo test --locked -p ${{ matrix.module }} --no-default-features --features "kvm"
|
||||
|
||||
30
.github/workflows/quality.yaml
vendored
30
.github/workflows/quality.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (mshv)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings
|
||||
|
||||
- name: Clippy (mshv + kvm)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings
|
||||
|
||||
- name: Clippy (default features)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --tests --examples -- -D warnings
|
||||
|
||||
- name: Clippy (default features + guest_debug)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + pvmemcontrol)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + tracing)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -110,13 +110,13 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings
|
||||
- name: Clippy (default features + fw_cfg)
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }}
|
||||
command: clippy
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "fw_cfg" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "fw_cfg" -- -D warnings
|
||||
|
||||
- name: Clippy (default features + ivshmem)
|
||||
uses: houseabsolute/actions-rust-cross@v1
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --tests --examples --features "ivshmem" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --tests --examples --features "ivshmem" -- -D warnings
|
||||
|
||||
- name: Clippy (sev_snp)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings
|
||||
|
||||
- name: Clippy (igvm)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings
|
||||
|
||||
- name: Clippy (kvm + tdx)
|
||||
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
cross-version: 3e0957637b49b1bbced23ad909170650c5b70635
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks -W clippy::assertions_on_result_states
|
||||
args: --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings
|
||||
|
||||
- name: Check build did not modify any files
|
||||
run: test -z "$(git status --porcelain)"
|
||||
@@ -165,6 +165,6 @@ jobs:
|
||||
name: Typos / Spellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
# Executes "typos ."
|
||||
- uses: crate-ci/typos@v1.39.0
|
||||
- uses: crate-ci/typos@v1.40.0
|
||||
|
||||
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Install musl-gcc
|
||||
if: contains(matrix.platform.target, 'musl')
|
||||
run: sudo apt install -y musl-tools
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: ${{ matrix.platform.args }}
|
||||
strip: true
|
||||
toolchain: "1.88.0"
|
||||
toolchain: "1.89.0"
|
||||
- name: Copy Release Binaries
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
shell: bash
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
cp target/${{ matrix.platform.target }}/release/ch-remote ./${{ matrix.platform.name_ch_remote }}
|
||||
- name: Upload Release Artifacts
|
||||
if: github.event_name == 'create' && github.event.ref_type == 'tag'
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: Artifacts for ${{ matrix.platform.target }}
|
||||
path: |
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
github.event_name == 'create' && github.event.ref_type == 'tag' &&
|
||||
matrix.platform.target == 'x86_64-unknown-linux-gnu'
|
||||
id: upload-release-cloud-hypervisor-vendored-sources
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
path: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||
name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
|
||||
|
||||
2
.github/workflows/reuse.yaml
vendored
2
.github/workflows/reuse.yaml
vendored
@@ -7,6 +7,6 @@ jobs:
|
||||
name: REUSE Compliance Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- name: REUSE Compliance Check
|
||||
uses: fsfe/reuse-action@v6
|
||||
|
||||
2
.github/workflows/shlint.yaml
vendored
2
.github/workflows/shlint.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Run the shell script checkers
|
||||
uses: luizm/action-sh-checker@master
|
||||
env:
|
||||
|
||||
2
.github/workflows/taplo.yaml
vendored
2
.github/workflows/taplo.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install build dependencies
|
||||
|
||||
2
.gitlint
2
.gitlint
@@ -1,7 +1,7 @@
|
||||
[general]
|
||||
extra-path=scripts/gitlint/rules
|
||||
regex-style-search=true
|
||||
ignore=body-max-line-length
|
||||
ignore=body-max-line-length,body-hard-tab
|
||||
|
||||
[ignore-by-author-name]
|
||||
regex=dependabot
|
||||
|
||||
@@ -14,11 +14,33 @@ License](https://opensource.org/licenses/Apache-2.0).
|
||||
## Coding Style
|
||||
|
||||
We follow the [Rust Style](https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/src)
|
||||
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`
|
||||
for each submitted Pull Request (PR).
|
||||
convention and enforce it through the Continuous Integration (CI) process calling into `rustfmt`,
|
||||
`clippy`, and other well-known code quality tool of the ecosystem for each submitted Pull Request (PR).
|
||||
|
||||
## Basic Checks
|
||||
|
||||
```sh
|
||||
# We currently rely on nightly-only formatting features
|
||||
cargo +nightly fmt --all
|
||||
cargo check --all-targets --tests
|
||||
cargo clippy --all-targets --tests
|
||||
# Please note that this will not execute integration tests.
|
||||
cargo test --all-targets --tests
|
||||
|
||||
# To lint your last three commits
|
||||
gitlint --commits "HEAD~3..HEAD"
|
||||
```
|
||||
|
||||
### \[Optional\] Run Integration Tests
|
||||
|
||||
_Caution: These tests are taking a long time to complete (40+ mins) and need special setup._
|
||||
|
||||
```sh
|
||||
bash ./scripts/dev_cli.sh tests --integration -- --test-filter '<optionally filter test by name pattern>'
|
||||
```
|
||||
|
||||
### Setup Commit Hook
|
||||
|
||||
Please consider creating the following hook as `.git/hooks/pre-commit` in order
|
||||
to ensure basic correctness of your code. You can extend this further if you
|
||||
have specific features that you regularly develop against.
|
||||
@@ -26,9 +48,9 @@ have specific features that you regularly develop against.
|
||||
```sh
|
||||
#!/bin/sh
|
||||
|
||||
cargo fmt -- --check || exit 1
|
||||
cargo check --locked --all --all-targets --tests || exit 1
|
||||
cargo clippy --locked --all --all-targets --tests -- -D warnings || exit 1
|
||||
cargo +nightly fmt --all -- --check || exit 1
|
||||
cargo check --locked --all-targets --tests || exit 1
|
||||
cargo clippy --locked --all-targets --tests -- -D warnings || exit 1
|
||||
```
|
||||
|
||||
You will need to `chmod +x .git/hooks/pre-commit` to have it run on every
|
||||
|
||||
353
Cargo.lock
generated
353
Cargo.lock
generated
@@ -27,9 +27,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -66,22 +66,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -311,9 +311,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.4"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
@@ -321,6 +321,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc-any",
|
||||
"flate2",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -334,6 +335,7 @@ dependencies = [
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -363,19 +365,21 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.41"
|
||||
version = "1.2.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
@@ -385,18 +389,18 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.49"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.49"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -413,7 +417,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "49.0.0"
|
||||
version = "50.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -540,7 +544,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitfield-struct 0.12.1",
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"byteorder",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -598,10 +602,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
@@ -653,7 +663,7 @@ version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e74d68fe2927dbf47aa976d14d93db9b23dced457c7bb2bdc6925a16d31b736e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -700,6 +710,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"flume",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -709,6 +720,9 @@ name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
@@ -718,19 +732,29 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"spin",
|
||||
]
|
||||
|
||||
@@ -848,7 +872,7 @@ version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72742d2b395902caf8a5d520d0dd3334ba6d1138938429200e58d5174e275f3f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
@@ -875,20 +899,20 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -905,9 +929,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -997,9 +1021,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.11.4"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -1007,11 +1031,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
|
||||
checksum = "fdd7bddefd0a8833b88a4b68f90dae22c7450d11b354198baee3874fd811b344"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
@@ -1027,9 +1051,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
@@ -1039,28 +1072,38 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
@@ -1088,7 +1131,7 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c8f7370330b4f57981e300fa39b02088f2f2a5c2d0f1f994e8090589619c56d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -1096,9 +1139,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "landlock"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "affe8b77dce5b172f8e290bd801b12832a77cd1942d1ea98259916e89d5829d6"
|
||||
checksum = "49fefd6652c57d68aaa32544a4c0e642929725bdc1fd929367cdeb673ab81088"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"libc",
|
||||
@@ -1113,9 +1156,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -1123,7 +1166,7 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -1143,9 +1186,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.22"
|
||||
version = "1.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
|
||||
checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1179,9 +1222,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
@@ -1220,6 +1263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1230,9 +1274,9 @@ checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
|
||||
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.6.0"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805cf329582f770f62cc612716a04c14815276ae266b6298375a672d3c5a5184"
|
||||
checksum = "66f415da68542aca92b33f55ac3e93031dc30a2941952b99679258f7e0527353"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num_enum",
|
||||
@@ -1244,9 +1288,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mshv-ioctls"
|
||||
version = "0.6.0"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aefaab4c067cf5226a917227640d835327b25b71a8d465f815f74f490344e10a"
|
||||
checksum = "e52a2a02c4107e08f46ba9dfc4e0f4461dffd44fbeca3e5631b4a047d15376c9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mshv-bindings",
|
||||
@@ -1254,15 +1298,6 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net_gen"
|
||||
version = "0.1.0"
|
||||
@@ -1275,7 +1310,7 @@ name = "net_util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
@@ -1298,7 +1333,7 @@ version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -1322,9 +1357,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
|
||||
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
"rustversion",
|
||||
@@ -1332,9 +1367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
||||
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -1359,9 +1394,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "open-enum"
|
||||
@@ -1385,18 +1420,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.5.3+3.5.4"
|
||||
version = "300.5.4+3.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2"
|
||||
checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.109"
|
||||
version = "0.9.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1665,18 +1700,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1713,7 +1748,7 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1739,7 +1774,7 @@ version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1811,7 +1846,7 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -1901,9 +1936,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.15.0"
|
||||
version = "3.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5"
|
||||
checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_with_macros",
|
||||
@@ -1911,9 +1946,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.15.0"
|
||||
version = "3.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27"
|
||||
checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -1943,13 +1978,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
@@ -1977,7 +2018,7 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f84d13b3b8a0d4e91a2629911e951db1bb8671512f5c09d7d4ba34500ba68c8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"parking_lot",
|
||||
@@ -1997,9 +2038,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2013,7 +2054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.0",
|
||||
@@ -2100,9 +2141,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.23.7"
|
||||
version = "0.23.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
|
||||
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -2143,9 +2184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@@ -2154,9 +2195,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.30"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2165,9 +2206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
@@ -2185,9 +2226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
@@ -2197,13 +2238,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"rand",
|
||||
"serde_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -2247,7 +2289,7 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8db5bc783aad75202ad4cbcdc5e893cff1dd8fa24a1bcdb4de8998d3c4d169a"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
@@ -2265,7 +2307,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a4dcad85a129d97d5d4b2f3c47a4affdeedd76bdcd02094bcb5d9b76cac2d05"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"uuid",
|
||||
"vm-memory",
|
||||
@@ -2418,6 +2460,7 @@ name = "vm-migration"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
@@ -2439,7 +2482,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"block",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
@@ -2518,12 +2561,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2697,27 +2740,24 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.11.0"
|
||||
version = "5.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7"
|
||||
checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
@@ -2739,7 +2779,8 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.60.2",
|
||||
"uuid",
|
||||
"windows-sys 0.61.0",
|
||||
"winnow",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
@@ -2748,9 +2789,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.11.0"
|
||||
version = "5.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca"
|
||||
checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -2775,18 +2816,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.27"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2794,10 +2835,38 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.7.0"
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.16+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
@@ -2809,9 +2878,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.7.0"
|
||||
version = "5.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e"
|
||||
checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
|
||||
122
Cargo.toml
122
Cargo.toml
@@ -1,21 +1,6 @@
|
||||
[package]
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
build = "build.rs"
|
||||
default-run = "cloud-hypervisor"
|
||||
description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM & MSHV"
|
||||
edition = "2024"
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
license = "Apache-2.0 AND BSD-3-Clause"
|
||||
name = "cloud-hypervisor"
|
||||
version = "49.0.0"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
# Policy on MSRV (see #4318):
|
||||
# Can only be bumped if satisfying any of the following:
|
||||
# a.) A dependency requires it,
|
||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||
# c.) There is a security issue that is addressed by the toolchain update.
|
||||
rust-version = "1.88.0"
|
||||
# Cloud Hypervisor Workspace
|
||||
#
|
||||
# The main crate producing the binaries is in `./cloud-hypervisor`.
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
@@ -28,59 +13,12 @@ debug = true
|
||||
inherits = "release"
|
||||
strip = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
api_client = { path = "api_client" }
|
||||
clap = { workspace = true, features = ["string"] }
|
||||
dhat = { workspace = true, optional = true }
|
||||
env_logger = { workspace = true }
|
||||
epoll = { workspace = true }
|
||||
event_monitor = { path = "event_monitor" }
|
||||
hypervisor = { path = "hypervisor" }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true, features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
signal-hook = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tpm = { path = "tpm" }
|
||||
tracer = { path = "tracer" }
|
||||
vm-memory = { workspace = true }
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = { workspace = true }
|
||||
zbus = { version = "5.7.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = { workspace = true }
|
||||
net_util = { path = "net_util" }
|
||||
serde_json = { workspace = true }
|
||||
test_infra = { path = "test_infra" }
|
||||
wait-timeout = { workspace = true }
|
||||
|
||||
# Please adjust `vmm::feature_list()` accordingly when changing the
|
||||
# feature list below
|
||||
[features]
|
||||
dbus_api = ["vmm/dbus_api", "zbus"]
|
||||
default = ["io_uring", "kvm"]
|
||||
dhat-heap = ["dhat", "vmm/dhat-heap"] # For heap profiling
|
||||
fw_cfg = ["vmm/fw_cfg"]
|
||||
guest_debug = ["vmm/guest_debug"]
|
||||
igvm = ["mshv", "vmm/igvm"]
|
||||
io_uring = ["vmm/io_uring"]
|
||||
ivshmem = ["vmm/ivshmem"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
pvmemcontrol = ["vmm/pvmemcontrol"]
|
||||
sev_snp = ["igvm", "mshv", "vmm/sev_snp"]
|
||||
tdx = ["vmm/tdx"]
|
||||
tracing = ["tracer/tracing", "vmm/tracing"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"api_client",
|
||||
"arch",
|
||||
"block",
|
||||
"cloud-hypervisor",
|
||||
"devices",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -103,6 +41,7 @@ members = [
|
||||
"vmm",
|
||||
]
|
||||
package.edition = "2024"
|
||||
resolver = "3"
|
||||
|
||||
[workspace.dependencies]
|
||||
# rust-vmm crates
|
||||
@@ -110,8 +49,8 @@ acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main"
|
||||
kvm-bindings = "0.12.1"
|
||||
kvm-ioctls = "0.22.1"
|
||||
linux-loader = "0.13.1"
|
||||
mshv-bindings = "0.6.0"
|
||||
mshv-ioctls = "0.6.0"
|
||||
mshv-bindings = "0.6.5"
|
||||
mshv-ioctls = "0.6.5"
|
||||
seccompiler = "0.5.0"
|
||||
vfio-bindings = { version = "0.6.0", default-features = false }
|
||||
vfio-ioctls = { version = "0.5.1", default-features = false }
|
||||
@@ -132,23 +71,52 @@ igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main" }
|
||||
# serde crates
|
||||
serde = "1.0.228"
|
||||
serde_json = "1.0.145"
|
||||
serde_with = { version = "3.15.0", default-features = false }
|
||||
serde_with = { version = "3.16.1", default-features = false }
|
||||
|
||||
# other crates
|
||||
anyhow = "1.0.99"
|
||||
bitflags = "2.9.4"
|
||||
bitflags = "2.10.0"
|
||||
byteorder = "1.5.0"
|
||||
cfg-if = "1.0.3"
|
||||
clap = "4.5.49"
|
||||
cfg-if = "1.0.4"
|
||||
clap = "4.5.53"
|
||||
dhat = "0.3.3"
|
||||
dirs = "6.0.0"
|
||||
env_logger = "0.11.8"
|
||||
epoll = "4.4.0"
|
||||
flume = "0.11.1"
|
||||
libc = "0.2.177"
|
||||
log = "0.4.28"
|
||||
flume = "0.12.0"
|
||||
itertools = "0.14.0"
|
||||
libc = "0.2.178"
|
||||
log = "0.4.29"
|
||||
signal-hook = "0.3.18"
|
||||
thiserror = "2.0.17"
|
||||
uuid = { version = "1.18.1" }
|
||||
uuid = { version = "1.19.0" }
|
||||
wait-timeout = "0.2.1"
|
||||
zerocopy = { version = "0.8.27", default-features = false }
|
||||
zerocopy = { version = "0.8.31", default-features = false }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
# Any clippy lint (group) in alphabetical order:
|
||||
# https://rust-lang.github.io/rust-clippy/master/index.html
|
||||
|
||||
# Groups
|
||||
all = "deny" # shorthand for the other groups but here for compleness
|
||||
complexity = "deny"
|
||||
correctness = "deny"
|
||||
perf = "deny"
|
||||
style = "deny"
|
||||
suspicious = "deny"
|
||||
|
||||
# Individual Lints
|
||||
assertions_on_result_states = "deny"
|
||||
if_not_else = "deny"
|
||||
manual_string_new = "deny"
|
||||
map_unwrap_or = "deny"
|
||||
needless_pass_by_value = "deny"
|
||||
redundant_else = "deny"
|
||||
semicolon_if_nothing_returned = "deny"
|
||||
undocumented_unsafe_blocks = "deny"
|
||||
uninlined_format_args = "deny"
|
||||
unnecessary_semicolon = "deny"
|
||||
|
||||
[workspace.lints.rust]
|
||||
# `level = warn` is irrelevant here but mandatory for rustc/cargo
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(devcli_testenv)'] }
|
||||
|
||||
@@ -7,3 +7,6 @@ version = "0.1.0"
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
vmm-sys-util = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -142,7 +142,7 @@ pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket
|
||||
method: &str,
|
||||
full_command: &str,
|
||||
request_body: Option<&str>,
|
||||
request_fds: Vec<RawFd>,
|
||||
request_fds: &[RawFd],
|
||||
) -> Result<Option<String>, Error> {
|
||||
socket
|
||||
.send_with_fds(
|
||||
@@ -150,7 +150,7 @@ pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket
|
||||
"{method} /api/v1/{full_command} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n"
|
||||
)
|
||||
.as_bytes()],
|
||||
&request_fds,
|
||||
request_fds,
|
||||
)
|
||||
.map_err(Error::SocketSendFds)?;
|
||||
|
||||
@@ -178,7 +178,7 @@ pub fn simple_api_full_command_with_fds<T: Read + Write + ScmSocket>(
|
||||
method: &str,
|
||||
full_command: &str,
|
||||
request_body: Option<&str>,
|
||||
request_fds: Vec<RawFd>,
|
||||
request_fds: &[RawFd],
|
||||
) -> Result<(), Error> {
|
||||
let response = simple_api_full_command_with_fds_and_response(
|
||||
socket,
|
||||
@@ -201,7 +201,7 @@ pub fn simple_api_full_command<T: Read + Write + ScmSocket>(
|
||||
full_command: &str,
|
||||
request_body: Option<&str>,
|
||||
) -> Result<(), Error> {
|
||||
simple_api_full_command_with_fds(socket, method, full_command, request_body, Vec::new())
|
||||
simple_api_full_command_with_fds(socket, method, full_command, request_body, &[])
|
||||
}
|
||||
|
||||
pub fn simple_api_full_command_and_response<T: Read + Write + ScmSocket>(
|
||||
@@ -210,13 +210,7 @@ pub fn simple_api_full_command_and_response<T: Read + Write + ScmSocket>(
|
||||
full_command: &str,
|
||||
request_body: Option<&str>,
|
||||
) -> Result<Option<String>, Error> {
|
||||
simple_api_full_command_with_fds_and_response(
|
||||
socket,
|
||||
method,
|
||||
full_command,
|
||||
request_body,
|
||||
Vec::new(),
|
||||
)
|
||||
simple_api_full_command_with_fds_and_response(socket, method, full_command, request_body, &[])
|
||||
}
|
||||
|
||||
pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
||||
@@ -224,7 +218,7 @@ pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>(
|
||||
method: &str,
|
||||
c: &str,
|
||||
request_body: Option<&str>,
|
||||
request_fds: Vec<RawFd>,
|
||||
request_fds: &[RawFd],
|
||||
) -> Result<(), Error> {
|
||||
// Create the full VM command. For VMM commands, use
|
||||
// simple_api_full_command().
|
||||
@@ -239,5 +233,5 @@ pub fn simple_api_command<T: Read + Write + ScmSocket>(
|
||||
c: &str,
|
||||
request_body: Option<&str>,
|
||||
) -> Result<(), Error> {
|
||||
simple_api_command_with_fds(socket, method, c, request_body, Vec::new())
|
||||
simple_api_command_with_fds(socket, method, c, request_body, &[])
|
||||
}
|
||||
|
||||
@@ -27,3 +27,6 @@ vmm-sys-util = { workspace = true, features = ["with-serde"] }
|
||||
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||
fdt_parser = { version = "0.1.5", package = "fdt" }
|
||||
vm-fdt = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -19,6 +19,7 @@ use hypervisor::arch::aarch64::regs::{
|
||||
AARCH64_ARCH_TIMER_HYP_IRQ, AARCH64_ARCH_TIMER_PHYS_NONSECURE_IRQ,
|
||||
AARCH64_ARCH_TIMER_PHYS_SECURE_IRQ, AARCH64_ARCH_TIMER_VIRT_IRQ, AARCH64_PMU_IRQ,
|
||||
};
|
||||
use log::{debug, warn};
|
||||
use thiserror::Error;
|
||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||
@@ -87,6 +88,7 @@ pub enum Error {
|
||||
}
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum CacheLevel {
|
||||
/// L1 data cache
|
||||
L1D = 0,
|
||||
@@ -110,9 +112,7 @@ pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
|
||||
}
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
0
|
||||
} else {
|
||||
if file_path.exists() {
|
||||
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
|
||||
// The content of the file is as simple as a size, like: "32K"
|
||||
let src = src.trim();
|
||||
@@ -126,6 +126,8 @@ pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
|
||||
"G" => 1024u32.pow(3),
|
||||
_ => 1,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,11 +143,11 @@ pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 {
|
||||
}
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
0
|
||||
} else {
|
||||
if file_path.exists() {
|
||||
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
|
||||
src.trim().parse::<u32>().unwrap()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +163,11 @@ pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 {
|
||||
}
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
0
|
||||
} else {
|
||||
if file_path.exists() {
|
||||
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
|
||||
src.trim().parse::<u32>().unwrap()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,9 +188,7 @@ pub fn get_cache_shared(cache_level: CacheLevel) -> bool {
|
||||
}
|
||||
|
||||
let file_path = Path::new(&file_directory);
|
||||
if !file_path.exists() {
|
||||
result = false;
|
||||
} else {
|
||||
if file_path.exists() {
|
||||
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
|
||||
let src = src.trim();
|
||||
if src.is_empty() {
|
||||
@@ -196,6 +196,8 @@ pub fn get_cache_shared(cache_level: CacheLevel) -> bool {
|
||||
} else {
|
||||
result = src.contains('-') || src.contains(',');
|
||||
}
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
|
||||
result
|
||||
@@ -206,7 +208,7 @@ pub fn get_cache_shared(cache_level: CacheLevel) -> bool {
|
||||
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline: &str,
|
||||
vcpu_mpidr: Vec<u64>,
|
||||
vcpu_mpidr: &[u64],
|
||||
vcpu_topology: Option<(u16, u16, u16, u16)>,
|
||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||
gic_device: &Arc<Mutex<dyn Vgic>>,
|
||||
@@ -233,7 +235,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
// This is not mandatory but we use it to point the root node to the node
|
||||
// containing description of the interrupt controller for this VM.
|
||||
fdt.property_u32("interrupt-parent", GIC_PHANDLE)?;
|
||||
create_cpu_nodes(&mut fdt, &vcpu_mpidr, vcpu_topology, numa_nodes)?;
|
||||
create_cpu_nodes(&mut fdt, vcpu_mpidr, vcpu_topology, numa_nodes)?;
|
||||
create_memory_node(&mut fdt, guest_mem, numa_nodes)?;
|
||||
create_chosen_node(&mut fdt, cmdline, initrd)?;
|
||||
create_gic_node(&mut fdt, gic_device)?;
|
||||
@@ -257,10 +259,10 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
Ok(fdt_final)
|
||||
}
|
||||
|
||||
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
pub fn write_fdt_to_memory(fdt_final: &[u8], guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
// Write FDT to memory.
|
||||
guest_mem
|
||||
.write_slice(fdt_final.as_slice(), super::layout::FDT_START)
|
||||
.write_slice(fdt_final, super::layout::FDT_START)
|
||||
.map_err(Error::WriteFdtToMemory)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -311,9 +313,7 @@ fn create_cpu_nodes(
|
||||
|
||||
let cache_path = Path::new("/sys/devices/system/cpu/cpu0/cache");
|
||||
let cache_exist: bool = cache_path.exists();
|
||||
if !cache_exist {
|
||||
warn!("cache sysfs system does not exist.");
|
||||
} else {
|
||||
if cache_exist {
|
||||
// L1 Data Cache Info.
|
||||
l1_d_cache_size = get_cache_size(CacheLevel::L1D);
|
||||
l1_d_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1D);
|
||||
@@ -341,6 +341,8 @@ fn create_cpu_nodes(
|
||||
if l3_cache_size != 0 {
|
||||
l3_cache_shared = get_cache_shared(CacheLevel::L3);
|
||||
}
|
||||
} else {
|
||||
warn!("cache sysfs system does not exist.");
|
||||
}
|
||||
|
||||
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
|
||||
@@ -1150,7 +1152,7 @@ fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
|
||||
array,
|
||||
indent = (n_spaces + 2)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Print children nodes if there is any
|
||||
|
||||
@@ -125,7 +125,7 @@ pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline: &str,
|
||||
vcpu_mpidr: Vec<u64>,
|
||||
vcpu_mpidr: &[u64],
|
||||
vcpu_topology: Option<(u16, u16, u16, u16)>,
|
||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||
initrd: &Option<super::InitramfsConfig>,
|
||||
@@ -154,7 +154,7 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
||||
fdt::print_fdt(&fdt_final);
|
||||
}
|
||||
|
||||
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||
fdt::write_fdt_to_memory(&fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -192,7 +192,7 @@ pub fn get_host_cpu_phys_bits(hypervisor: &dyn hypervisor::Hypervisor) -> u8 {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
//! Implements platform specific functionality.
|
||||
//! Supported platforms: x86_64, aarch64, riscv64.
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, result};
|
||||
|
||||
@@ -15,6 +15,7 @@ use std::{cmp, result, str};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use hypervisor::arch::riscv64::aia::Vaia;
|
||||
use log::debug;
|
||||
use thiserror::Error;
|
||||
use vm_fdt::{FdtWriter, FdtWriterResult};
|
||||
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
|
||||
@@ -65,6 +66,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
cmdline: &str,
|
||||
num_vcpu: u32,
|
||||
isa_string: &str,
|
||||
device_info: &HashMap<(DeviceType, String), T, S>,
|
||||
aia_device: &Arc<Mutex<dyn Vaia>>,
|
||||
initrd: &Option<InitramfsConfig>,
|
||||
@@ -84,7 +86,7 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
// Properties
|
||||
fdt.property_u32("#address-cells", ADDRESS_CELLS)?;
|
||||
fdt.property_u32("#size-cells", SIZE_CELLS)?;
|
||||
create_cpu_nodes(&mut fdt, num_vcpu)?;
|
||||
create_cpu_nodes(&mut fdt, num_vcpu, isa_string)?;
|
||||
create_memory_node(&mut fdt, guest_mem)?;
|
||||
create_chosen_node(&mut fdt, cmdline, initrd)?;
|
||||
create_aia_node(&mut fdt, aia_device)?;
|
||||
@@ -99,16 +101,16 @@ pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHash
|
||||
Ok(fdt_final)
|
||||
}
|
||||
|
||||
pub fn write_fdt_to_memory(fdt_final: Vec<u8>, guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
pub fn write_fdt_to_memory(fdt_final: &[u8], guest_mem: &GuestMemoryMmap) -> Result<()> {
|
||||
// Write FDT to memory.
|
||||
guest_mem
|
||||
.write_slice(fdt_final.as_slice(), super::layout::FDT_START)
|
||||
.write_slice(fdt_final, 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<()> {
|
||||
fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32, isa_string: &str) -> 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.
|
||||
@@ -123,7 +125,7 @@ fn create_cpu_nodes(fdt: &mut FdtWriter, num_cpus: u32) -> FdtWriterResult<()> {
|
||||
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("riscv,isa", isa_string)?;
|
||||
fdt.property_string("status", "okay")?;
|
||||
fdt.property_u32("reg", cpu_index)?;
|
||||
fdt.property_u32("phandle", CPU_BASE_PHANDLE + cpu_index)?;
|
||||
@@ -471,7 +473,7 @@ fn print_node(node: fdt_parser::node::FdtNode<'_, '_>, n_spaces: usize) {
|
||||
array,
|
||||
indent = (n_spaces + 2)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Print children nodes if there is any
|
||||
|
||||
@@ -12,6 +12,8 @@ pub mod uefi;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use hypervisor::arch::riscv64::aia::Vaia;
|
||||
@@ -51,6 +53,22 @@ pub enum Error {
|
||||
/// Error configuring the general purpose registers
|
||||
#[error("Error configuring the general purpose registers")]
|
||||
RegsConfiguration(#[source] hypervisor::HypervisorCpuError),
|
||||
|
||||
/// Error opening /proc/cpuinfo
|
||||
#[error("Error opening /proc/cpuinfo")]
|
||||
OpenCpuInfo(#[source] std::io::Error),
|
||||
|
||||
/// Error reading /proc/cpuinfo
|
||||
#[error("Error reading /proc/cpuinfo")]
|
||||
ReadCpuInfo(#[source] std::io::Error),
|
||||
|
||||
/// Invalid ISA string
|
||||
#[error("Invalid ISA string: {0}")]
|
||||
InvalidIsaString(String),
|
||||
|
||||
/// Error parsing /proc/cpuinfo
|
||||
#[error("Error parsing /proc/cpuinfo")]
|
||||
CpuInfoParsing,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@@ -104,6 +122,43 @@ pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
]
|
||||
}
|
||||
|
||||
// Read the first "isa" string from /proc/cpuinfo and filter out the H extension,
|
||||
// while correctly preserving multi-letter extensions.
|
||||
fn isa_string_from_host() -> Result<String, Error> {
|
||||
let file = File::open("/proc/cpuinfo").map_err(Error::OpenCpuInfo)?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line.map_err(Error::ReadCpuInfo)?;
|
||||
let trimmed_line = line.trim();
|
||||
|
||||
if trimmed_line.starts_with("isa") {
|
||||
let parts: Vec<&str> = trimmed_line.split(':').collect();
|
||||
if parts.len() == 2 {
|
||||
let isa_string = parts[1].trim();
|
||||
|
||||
// Split the string by underscores to separate single letter vs long-form
|
||||
// extensions
|
||||
let mut components: Vec<String> =
|
||||
isa_string.split('_').map(|s| s.to_string()).collect();
|
||||
|
||||
if components.is_empty() {
|
||||
return Err(Error::InvalidIsaString(isa_string.to_string()));
|
||||
}
|
||||
|
||||
// Remove H extension if present in single letter extensions
|
||||
let first_component = components[0].chars().filter(|&c| c != 'h').collect();
|
||||
|
||||
components[0] = first_component;
|
||||
|
||||
return Ok(components.join("_"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::CpuInfoParsing)
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
@@ -115,10 +170,12 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
||||
pci_space_info: &[PciSpaceInfo],
|
||||
aia_device: &Arc<Mutex<dyn Vaia>>,
|
||||
) -> super::Result<()> {
|
||||
let isa_string = isa_string_from_host()?;
|
||||
let fdt_final = fdt::create_fdt(
|
||||
guest_mem,
|
||||
cmdline,
|
||||
num_vcpu,
|
||||
&isa_string,
|
||||
device_info,
|
||||
aia_device,
|
||||
initrd,
|
||||
@@ -130,7 +187,7 @@ pub fn configure_system<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Bui
|
||||
fdt::print_fdt(&fdt_final);
|
||||
}
|
||||
|
||||
fdt::write_fdt_to_memory(fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||
fdt::write_fdt_to_memory(&fdt_final, guest_mem).map_err(Error::WriteFdtToMemory)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -161,7 +218,7 @@ pub fn get_host_cpu_phys_bits(_hypervisor: &dyn hypervisor::Hypervisor) -> u8 {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -6,11 +6,19 @@
|
||||
// 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 LICENSE-BSD-3-Clause file.
|
||||
|
||||
pub mod interrupts;
|
||||
pub mod layout;
|
||||
pub mod regs;
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
pub mod tdx;
|
||||
|
||||
mod mpspec;
|
||||
mod mptable;
|
||||
pub mod regs;
|
||||
mod smbios;
|
||||
|
||||
use std::arch::x86_64;
|
||||
use std::mem;
|
||||
|
||||
use hypervisor::arch::x86::{CPUID_FLAG_VALID_INDEX, CpuIdEntry};
|
||||
@@ -19,6 +27,7 @@ use linux_loader::loader::bootparam::{boot_params, setup_header};
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
};
|
||||
use log::{debug, error, info};
|
||||
use thiserror::Error;
|
||||
use vm_memory::{
|
||||
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
|
||||
@@ -26,10 +35,6 @@ use vm_memory::{
|
||||
};
|
||||
|
||||
use crate::{GuestMemoryMmap, InitramfsConfig, RegionType};
|
||||
mod smbios;
|
||||
use std::arch::x86_64;
|
||||
#[cfg(feature = "tdx")]
|
||||
pub mod tdx;
|
||||
|
||||
// While modern architectures support more than 255 CPUs via x2APIC,
|
||||
// legacy devices such as mptable support at most 254 CPUs.
|
||||
@@ -39,12 +44,17 @@ pub const MAX_SUPPORTED_CPUS_LEGACY: u32 = 254;
|
||||
#[cfg(feature = "kvm")]
|
||||
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
||||
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
||||
const VMX_ECX_BIT: u8 = 5; // VMX for Intel
|
||||
const SVM_ECX_BIT: u8 = 2; // SVM for AMD
|
||||
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
||||
const INVARIANT_TSC_EDX_BIT: u8 = 8; // Invariant TSC bit on 0x8000_0007 EDX
|
||||
const AMX_BF16: u8 = 22; // AMX tile computation on bfloat16 numbers
|
||||
const AMX_TILE: u8 = 24; // AMX tile load/store instructions
|
||||
const AMX_INT8: u8 = 25; // AMX tile computation on 8-bit integers
|
||||
|
||||
const AMX_FP16: u8 = 21; // AMX tile computation on fp16 numbers
|
||||
const AMX_COMPLEX: u8 = 8; // AMX tile computation on complex numbers
|
||||
|
||||
// KVM feature bits
|
||||
#[cfg(feature = "tdx")]
|
||||
const KVM_FEATURE_CLOCKSOURCE_BIT: u8 = 0;
|
||||
@@ -278,7 +288,7 @@ impl CpuidPatch {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patch_cpuid(cpuid: &mut [CpuIdEntry], patches: Vec<CpuidPatch>) {
|
||||
pub fn patch_cpuid(cpuid: &mut [CpuIdEntry], patches: &[CpuidPatch]) {
|
||||
for entry in cpuid {
|
||||
for patch in patches.iter() {
|
||||
if entry.function == patch.function && entry.index == patch.index {
|
||||
@@ -615,14 +625,14 @@ pub fn generate_common_cpuid(
|
||||
.get_supported_cpuid()
|
||||
.map_err(Error::CpuidGetSupported)?;
|
||||
|
||||
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
|
||||
CpuidPatch::patch_cpuid(&mut cpuid, &cpuid_patches);
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
let tdx_capabilities = if config.tdx {
|
||||
let caps = hypervisor
|
||||
.tdx_capabilities()
|
||||
.map_err(Error::TdxCapabilities)?;
|
||||
info!("TDX capabilities {:#?}", caps);
|
||||
info!("TDX capabilities {caps:#?}");
|
||||
Some(caps)
|
||||
} else {
|
||||
None
|
||||
@@ -633,8 +643,14 @@ pub fn generate_common_cpuid(
|
||||
match entry.function {
|
||||
// Clear AMX related bits if the AMX feature is not enabled
|
||||
0x7 => {
|
||||
if !config.amx && entry.index == 0 {
|
||||
entry.edx &= !((1 << AMX_BF16) | (1 << AMX_TILE) | (1 << AMX_INT8))
|
||||
if !config.amx {
|
||||
if entry.index == 0 {
|
||||
entry.edx &= !((1 << AMX_BF16) | (1 << AMX_TILE) | (1 << AMX_INT8));
|
||||
}
|
||||
if entry.index == 1 {
|
||||
entry.eax &= !(1 << AMX_FP16);
|
||||
entry.edx &= !(1 << AMX_COMPLEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
0xd =>
|
||||
@@ -656,6 +672,25 @@ pub fn generate_common_cpuid(
|
||||
}
|
||||
}
|
||||
}
|
||||
0x1d => {
|
||||
// Tile Information (purely AMX related).
|
||||
if !config.amx {
|
||||
entry.eax = 0;
|
||||
entry.ebx = 0;
|
||||
entry.ecx = 0;
|
||||
entry.edx = 0;
|
||||
}
|
||||
}
|
||||
0x1e => {
|
||||
// TMUL information (purely AMX related)
|
||||
if !config.amx {
|
||||
entry.eax = 0;
|
||||
entry.ebx = 0;
|
||||
entry.ecx = 0;
|
||||
entry.edx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy host L1 cache details if not populated by KVM
|
||||
0x8000_0005 => {
|
||||
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
|
||||
@@ -701,7 +736,7 @@ pub fn generate_common_cpuid(
|
||||
| (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)
|
||||
| (1 << KVM_FEATURE_ASYNC_PF_BIT)
|
||||
| (1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT)
|
||||
| (1 << KVM_FEATURE_STEAL_TIME_BIT))
|
||||
| (1 << KVM_FEATURE_STEAL_TIME_BIT));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -773,6 +808,7 @@ pub fn generate_common_cpuid(
|
||||
Ok(cpuid)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn configure_vcpu(
|
||||
vcpu: &dyn hypervisor::Vcpu,
|
||||
id: u32,
|
||||
@@ -781,6 +817,7 @@ pub fn configure_vcpu(
|
||||
kvm_hyperv: bool,
|
||||
cpu_vendor: CpuVendor,
|
||||
topology: (u16, u16, u16, u16),
|
||||
nested: bool,
|
||||
) -> super::Result<()> {
|
||||
let x2apic_id = get_x2apic_id(id, Some(topology));
|
||||
|
||||
@@ -799,6 +836,17 @@ pub fn configure_vcpu(
|
||||
entry.ebx &= 0xffffff;
|
||||
entry.ebx |= x2apic_id << 24;
|
||||
apic_id_patched = true;
|
||||
if !nested {
|
||||
// Disable nested virtualization for Intel
|
||||
entry.ecx &= !(1 << VMX_ECX_BIT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if entry.function == 0x8000_0001 {
|
||||
if !nested {
|
||||
// Disable the nested virtualization for AMD
|
||||
entry.ecx &= !(1 << SVM_ECX_BIT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -824,7 +872,7 @@ pub fn configure_vcpu(
|
||||
* APIC_BUS_CYCLE_NS */
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for c in &cpuid {
|
||||
@@ -1303,9 +1351,10 @@ fn update_cpuid_topology(
|
||||
let die_width = u16::BITS - (dies_per_package - 1).leading_zeros() + core_width;
|
||||
|
||||
// The very old way: a flat number of logical CPUs per package: CPUID.1H:EBX[23:16] bits.
|
||||
let core_count = dies_per_package as u32 * cores_per_die as u32 * threads_per_core as u32;
|
||||
let mut cpu_ebx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX).unwrap_or(0);
|
||||
cpu_ebx |= ((dies_per_package as u32) * (cores_per_die as u32) * (threads_per_core as u32))
|
||||
& (0xff << 16);
|
||||
cpu_ebx &= !(0xff << 16);
|
||||
cpu_ebx |= (core_count & 0xff) << 16;
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
||||
|
||||
let mut cpu_edx = CpuidPatch::get_cpuid_reg(cpuid, 0x1, None, CpuidReg::EDX).unwrap_or(0);
|
||||
@@ -1346,6 +1395,7 @@ fn update_cpuid_topology(
|
||||
u32::from(threads_per_core),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::ECX, 1 << 8);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(0), CpuidReg::EDX, x2apic_id);
|
||||
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(1), CpuidReg::EAX, core_width);
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
@@ -1356,6 +1406,7 @@ fn update_cpuid_topology(
|
||||
u32::from(cores_per_die * threads_per_core),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(1), CpuidReg::ECX, 2 << 8);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(1), CpuidReg::EDX, x2apic_id);
|
||||
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::EAX, die_width);
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
@@ -1366,6 +1417,7 @@ fn update_cpuid_topology(
|
||||
u32::from(dies_per_package * cores_per_die * threads_per_core),
|
||||
);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::ECX, 5 << 8);
|
||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::EDX, x2apic_id);
|
||||
|
||||
if matches!(cpu_vendor, CpuVendor::AMD) {
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
@@ -1412,7 +1464,7 @@ fn update_cpuid_topology(
|
||||
edx_bit: Some(28),
|
||||
},
|
||||
];
|
||||
CpuidPatch::patch_cpuid(cpuid, cpuid_patches);
|
||||
CpuidPatch::patch_cpuid(cpuid, &cpuid_patches);
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
cpuid,
|
||||
0x8000_0008,
|
||||
@@ -1427,7 +1479,7 @@ fn update_cpuid_topology(
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use linux_loader::loader::bootparam::boot_e820_entry;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
use std::{mem, result, slice};
|
||||
|
||||
use libc::c_uchar;
|
||||
use log::{info, warn};
|
||||
use thiserror::Error;
|
||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError};
|
||||
|
||||
@@ -297,7 +298,7 @@ pub fn setup_mptable(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use vm_memory::bitmap::BitmapSlice;
|
||||
use vm_memory::{GuestUsize, VolatileMemoryError, VolatileSlice, WriteVolatile};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ pub fn setup_fpu(vcpu: &dyn hypervisor::Vcpu) -> Result<()> {
|
||||
///
|
||||
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
|
||||
pub fn setup_msrs(vcpu: &dyn hypervisor::Vcpu) -> Result<()> {
|
||||
vcpu.set_msrs(&vcpu.boot_msr_entries())
|
||||
vcpu.set_msrs(vcpu.boot_msr_entries())
|
||||
.map_err(Error::SetModelSpecificRegisters)?;
|
||||
|
||||
Ok(())
|
||||
@@ -108,7 +108,7 @@ pub fn setup_regs(vcpu: &dyn hypervisor::Vcpu, entry_point: EntryPoint) -> Resul
|
||||
regs.set_rsp(BOOT_STACK_POINTER.raw_value());
|
||||
regs.set_rsi(ZERO_PAGE_START.raw_value());
|
||||
}
|
||||
};
|
||||
}
|
||||
vcpu.set_regs(®s).map_err(Error::SetBaseRegisters)
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ pub fn configure_segments_and_sregs(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -272,7 +272,7 @@ pub fn setup_smbios(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::str::FromStr;
|
||||
|
||||
use log::{debug, info};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemoryError};
|
||||
@@ -304,7 +305,7 @@ fn align_hob(v: u64) -> u64 {
|
||||
|
||||
impl TdHob {
|
||||
fn update_offset<T>(&mut self) {
|
||||
self.current_offset = align_hob(self.current_offset + std::mem::size_of::<T>() as u64)
|
||||
self.current_offset = align_hob(self.current_offset + std::mem::size_of::<T>() as u64);
|
||||
}
|
||||
|
||||
pub fn start(offset: u64) -> TdHob {
|
||||
@@ -519,7 +520,7 @@ impl TdHob {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@@ -528,7 +529,7 @@ mod tests {
|
||||
let mut f = std::fs::File::open("tdvf.fd").unwrap();
|
||||
let (sections, _) = parse_tdvf_sections(&mut f).unwrap();
|
||||
for section in sections {
|
||||
eprintln!("{section:x?}")
|
||||
eprintln!("{section:x?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ io_uring = ["dep:io-uring"]
|
||||
[dependencies]
|
||||
byteorder = { workspace = true }
|
||||
crc-any = "2.5.0"
|
||||
io-uring = { version = "0.7.10", optional = true }
|
||||
flate2 = "1.0"
|
||||
io-uring = { version = "0.7.11", optional = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
remain = "0.2.15"
|
||||
@@ -28,3 +29,7 @@ vm-memory = { workspace = true, features = [
|
||||
] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = { workspace = true }
|
||||
zstd = "0.13"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -18,6 +18,12 @@ pub enum DiskFileError {
|
||||
/// Failed creating a new AsyncIo.
|
||||
#[error("Failed creating a new AsyncIo")]
|
||||
NewAsyncIo(#[source] std::io::Error),
|
||||
/// Unsupported operation.
|
||||
#[error("Unsupported operation")]
|
||||
Unsupported,
|
||||
/// Resize failed
|
||||
#[error("Resize failed")]
|
||||
ResizeError(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
pub type DiskFileResult<T> = std::result::Result<T, DiskFileError>;
|
||||
@@ -56,11 +62,22 @@ impl AsRawFd for BorrowedDiskFd<'_> {
|
||||
/// This allows abstracting over raw image formats as well as structured
|
||||
/// image formats.
|
||||
pub trait DiskFile: Send {
|
||||
fn size(&mut self) -> DiskFileResult<u64>;
|
||||
/// Returns the logical disk size a guest will see.
|
||||
///
|
||||
/// For raw formats, this is equal to [`Self::physical_size`]. For file formats
|
||||
/// that wrap disk images in a container (e.g. QCOW2), this refers to the
|
||||
/// effective size that the guest will see.
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64>;
|
||||
/// Returns the physical size of the underlying file.
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64>;
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>>;
|
||||
fn topology(&mut self) -> DiskTopology {
|
||||
DiskTopology::default()
|
||||
}
|
||||
fn resize(&mut self, _size: u64) -> DiskFileResult<()> {
|
||||
Err(DiskFileError::Unsupported)
|
||||
}
|
||||
|
||||
/// Returns the file descriptor of the underlying disk image file.
|
||||
///
|
||||
/// The file descriptor is supposed to be used for `fcntl()` calls but no
|
||||
|
||||
@@ -101,13 +101,52 @@ impl LockState {
|
||||
}
|
||||
}
|
||||
|
||||
/// The granularity of the advisory lock.
|
||||
///
|
||||
/// The granularity has significant implications in typical cloud deployments
|
||||
/// with network storage. The Linux kernel will sync advisory locks to network
|
||||
/// file systems, but these backends may have different policies and handle
|
||||
/// locks differently. For example, Netapp speaks a NFS API but will treat
|
||||
/// advisory OFD locks for the whole file as mandatory locks, whereas byte-range
|
||||
/// locks for the whole file will remain advisory [0].
|
||||
///
|
||||
/// As it is a valid use case to prevent multiple CHV instances from accessing
|
||||
/// the same disk but disk management software (e.g., Cinder in OpenStack)
|
||||
/// should be able to snapshot disks while VMs are running, we need special
|
||||
/// control over the lock granularity. Therefore, it is a valid use case to lock
|
||||
/// the whole byte range of a disk image without technically locking the whole
|
||||
/// file - to get the best of both worlds.
|
||||
///
|
||||
/// [0] https://kb.netapp.com/on-prem/ontap/da/NAS/NAS-KBs/How_is_Mandatory_Locking_supported_for_NFSv4_on_ONTAP_9
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LockGranularity {
|
||||
WholeFile,
|
||||
ByteRange(u64 /* from, inclusive */, u64 /* len */),
|
||||
}
|
||||
|
||||
impl LockGranularity {
|
||||
const fn l_start(self) -> u64 {
|
||||
match self {
|
||||
LockGranularity::WholeFile => 0,
|
||||
LockGranularity::ByteRange(start, _) => start,
|
||||
}
|
||||
}
|
||||
|
||||
const fn l_len(self) -> u64 {
|
||||
match self {
|
||||
LockGranularity::WholeFile => 0, /* EOF */
|
||||
LockGranularity::ByteRange(_, len) => len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [`struct@libc::flock`] structure for the whole file.
|
||||
const fn get_flock(lock_type: LockType) -> libc::flock {
|
||||
const fn get_flock(lock_type: LockType, granularity: LockGranularity) -> libc::flock {
|
||||
libc::flock {
|
||||
l_type: lock_type.to_libc_val() as libc::c_short,
|
||||
l_whence: libc::SEEK_SET as libc::c_short,
|
||||
l_start: 0,
|
||||
l_len: 0, /* EOF */
|
||||
l_start: granularity.l_start() as libc::c_long,
|
||||
l_len: granularity.l_len() as libc::c_long,
|
||||
l_pid: 0, /* filled by callee */
|
||||
}
|
||||
}
|
||||
@@ -122,8 +161,13 @@ const fn get_flock(lock_type: LockType) -> libc::flock {
|
||||
/// - `file`: The file to acquire a lock for [`LockType`]. The file's state will
|
||||
/// be logically mutated, but not technically.
|
||||
/// - `lock_type`: The [`LockType`]
|
||||
pub fn try_acquire_lock<Fd: AsRawFd>(file: Fd, lock_type: LockType) -> Result<(), LockError> {
|
||||
let flock = get_flock(lock_type);
|
||||
/// - `granularity`: The [`LockGranularity`].
|
||||
pub fn try_acquire_lock<Fd: AsRawFd>(
|
||||
file: &Fd,
|
||||
lock_type: LockType,
|
||||
granularity: LockGranularity,
|
||||
) -> Result<(), LockError> {
|
||||
let flock = get_flock(lock_type, granularity);
|
||||
|
||||
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_SETLK(&flock));
|
||||
match res {
|
||||
@@ -146,8 +190,9 @@ pub fn try_acquire_lock<Fd: AsRawFd>(file: Fd, lock_type: LockType) -> Result<()
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `file`: The file to clear all locks for [`LockType`].
|
||||
pub fn clear_lock<Fd: AsRawFd>(file: Fd) -> Result<(), LockError> {
|
||||
try_acquire_lock(file, LockType::Unlock)
|
||||
/// - `granularity`: The [`LockGranularity`].
|
||||
pub fn clear_lock<Fd: AsRawFd>(file: &Fd, granularity: LockGranularity) -> Result<(), LockError> {
|
||||
try_acquire_lock(file, LockType::Unlock, granularity)
|
||||
}
|
||||
|
||||
/// Returns the current lock state using [`fcntl`] with respect to the given
|
||||
@@ -155,8 +200,12 @@ pub fn clear_lock<Fd: AsRawFd>(file: Fd) -> Result<(), LockError> {
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `file`: The file for which to get the lock state.
|
||||
pub fn get_lock_state<Fd: AsRawFd>(file: Fd) -> Result<LockState, LockError> {
|
||||
let mut flock = get_flock(LockType::Write);
|
||||
/// - `granularity`: The [`LockGranularity`].
|
||||
pub fn get_lock_state<Fd: AsRawFd>(
|
||||
file: &Fd,
|
||||
granularity: LockGranularity,
|
||||
) -> Result<LockState, LockError> {
|
||||
let mut flock = get_flock(LockType::Write, granularity);
|
||||
let res = fcntl(file.as_raw_fd(), FcntlArg::F_OFD_GETLK(&mut flock));
|
||||
match res {
|
||||
0 => {
|
||||
|
||||
@@ -75,9 +75,17 @@ impl Seek for FixedVhd {
|
||||
}
|
||||
|
||||
impl BlockBackend for FixedVhd {
|
||||
fn size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
fn logical_size(&self) -> Result<u64, crate::Error> {
|
||||
Ok(self.size)
|
||||
}
|
||||
|
||||
/// Returns the physical size of the underlying file.
|
||||
fn physical_size(&self) -> Result<u64, crate::Error> {
|
||||
self.file
|
||||
.metadata()
|
||||
.map(|m| m.len())
|
||||
.map_err(crate::Error::GetFileMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FixedVhd {
|
||||
|
||||
@@ -23,14 +23,22 @@ impl FixedVhdDiskAsync {
|
||||
}
|
||||
|
||||
impl DiskFile for FixedVhdDiskAsync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.0.size().unwrap())
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.0.logical_size().unwrap())
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.0.physical_size().unwrap())
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
FixedVhdAsync::new(self.0.as_raw_fd(), ring_depth, self.0.size().unwrap())
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
FixedVhdAsync::new(
|
||||
self.0.as_raw_fd(),
|
||||
ring_depth,
|
||||
self.0.logical_size().unwrap(),
|
||||
)
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,13 +23,23 @@ impl FixedVhdDiskSync {
|
||||
}
|
||||
|
||||
impl DiskFile for FixedVhdDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.0.size().unwrap())
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.0.logical_size().unwrap())
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.0.physical_size().map_err(|e| {
|
||||
let io_inner = match e {
|
||||
crate::Error::GetFileMetadata(e) => e,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
DiskFileError::Size(io_inner)
|
||||
})
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
FixedVhdSync::new(self.0.as_raw_fd(), self.0.size().unwrap())
|
||||
FixedVhdSync::new(self.0.as_raw_fd(), self.0.logical_size().unwrap())
|
||||
.map_err(DiskFileError::NewAsyncIo)?,
|
||||
) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod async_io;
|
||||
pub mod fcntl;
|
||||
pub mod fixed_vhd;
|
||||
@@ -45,6 +42,7 @@ use std::{cmp, result};
|
||||
#[cfg(feature = "io_uring")]
|
||||
use io_uring::{IoUring, Probe, opcode};
|
||||
use libc::{S_IFBLK, S_IFMT, ioctl};
|
||||
use log::{error, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
use thiserror::Error;
|
||||
@@ -82,8 +80,8 @@ pub enum Error {
|
||||
DetectImageType(#[source] std::io::Error),
|
||||
#[error("Failure in fixed vhd")]
|
||||
FixedVhdError(#[source] std::io::Error),
|
||||
#[error("Getting a block's metadata fails for any reason")]
|
||||
GetFileMetadata,
|
||||
#[error("Getting a block's metadata failed")]
|
||||
GetFileMetadata(#[source] std::io::Error),
|
||||
#[error("The requested operation would cause a seek beyond disk end")]
|
||||
InvalidOffset,
|
||||
#[error("Failure in qcow")]
|
||||
@@ -98,7 +96,7 @@ pub enum Error {
|
||||
|
||||
fn build_device_id(disk_path: &Path) -> result::Result<String, Error> {
|
||||
let blk_metadata = match disk_path.metadata() {
|
||||
Err(_) => return Err(Error::GetFileMetadata),
|
||||
Err(e) => return Err(Error::GetFileMetadata(e)),
|
||||
Ok(m) => m,
|
||||
};
|
||||
// This is how kvmtool does it.
|
||||
@@ -122,7 +120,7 @@ pub fn build_serial(disk_path: &Path) -> Vec<u8> {
|
||||
// This will also zero out any leftover bytes.
|
||||
let disk_id = m.as_bytes();
|
||||
let bytes_to_copy = cmp::min(disk_id.len(), VIRTIO_BLK_ID_BYTES as usize);
|
||||
default_serial[..bytes_to_copy].clone_from_slice(&disk_id[..bytes_to_copy])
|
||||
default_serial[..bytes_to_copy].clone_from_slice(&disk_id[..bytes_to_copy]);
|
||||
}
|
||||
}
|
||||
default_serial
|
||||
@@ -138,7 +136,7 @@ pub enum ExecuteError {
|
||||
Read(#[source] GuestMemoryError),
|
||||
#[error("Failed to read_exact")]
|
||||
ReadExact(#[source] io::Error),
|
||||
#[error("Can't execute an operation other than `read` on a read-only device")]
|
||||
#[error("Can't execute an operation other than `read` or `get_id` on a read-only device")]
|
||||
ReadOnly,
|
||||
#[error("Failed to seek")]
|
||||
Seek(#[source] io::Error),
|
||||
@@ -295,14 +293,7 @@ impl Request {
|
||||
error!("Only head descriptor present: request = {req:?}");
|
||||
})?;
|
||||
|
||||
if !desc.has_next() {
|
||||
status_desc = desc;
|
||||
// Only flush requests are allowed to skip the data descriptor.
|
||||
if req.request_type != RequestType::Flush {
|
||||
error!("Need a data descriptor: request = {req:?}");
|
||||
return Err(Error::DescriptorChainTooShort);
|
||||
}
|
||||
} else {
|
||||
if desc.has_next() {
|
||||
req.data_descriptors.reserve_exact(1);
|
||||
while desc.has_next() {
|
||||
if desc.is_write_only() && req.request_type == RequestType::Out {
|
||||
@@ -328,6 +319,13 @@ impl Request {
|
||||
})?;
|
||||
}
|
||||
status_desc = desc;
|
||||
} else {
|
||||
status_desc = desc;
|
||||
// Only flush requests are allowed to skip the data descriptor.
|
||||
if req.request_type != RequestType::Flush {
|
||||
error!("Need a data descriptor: request = {req:?}");
|
||||
return Err(Error::DescriptorChainTooShort);
|
||||
}
|
||||
}
|
||||
|
||||
// The status MUST always be writable.
|
||||
@@ -398,7 +396,7 @@ impl Request {
|
||||
.map_err(ExecuteError::Write)?;
|
||||
}
|
||||
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
@@ -440,14 +438,17 @@ impl Request {
|
||||
|
||||
let origin_ptr = mem
|
||||
.get_slice(data_addr, data_len)
|
||||
.map_err(ExecuteError::GetHostAddress)?
|
||||
.ptr_guard();
|
||||
.map_err(ExecuteError::GetHostAddress)?;
|
||||
assert!(origin_ptr.len() >= data_len);
|
||||
let origin_ptr = origin_ptr.ptr_guard();
|
||||
|
||||
// Verify the buffer alignment.
|
||||
// In case it's not properly aligned, an intermediate buffer is
|
||||
// created with the correct alignment, and a copy from/to the
|
||||
// origin buffer is performed, depending on the type of operation.
|
||||
let iov_base = if !(origin_ptr.as_ptr() as u64).is_multiple_of(SECTOR_SIZE) {
|
||||
let iov_base = if (origin_ptr.as_ptr() as u64).is_multiple_of(SECTOR_SIZE) {
|
||||
origin_ptr.as_ptr() as *mut libc::c_void
|
||||
} else {
|
||||
let layout = Layout::from_size_align(data_len, SECTOR_SIZE as usize).unwrap();
|
||||
// SAFETY: layout has non-zero size
|
||||
let aligned_ptr = unsafe { alloc_zeroed(layout) };
|
||||
@@ -475,8 +476,6 @@ impl Request {
|
||||
});
|
||||
|
||||
aligned_ptr as *mut libc::c_void
|
||||
} else {
|
||||
origin_ptr.as_ptr() as *mut libc::c_void
|
||||
};
|
||||
|
||||
let iovec = libc::iovec {
|
||||
@@ -563,7 +562,7 @@ impl Request {
|
||||
aligned_operation.aligned_ptr as *const u8,
|
||||
aligned_operation.origin_ptr as *mut u8,
|
||||
aligned_operation.size,
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -574,7 +573,7 @@ impl Request {
|
||||
dealloc(
|
||||
aligned_operation.aligned_ptr as *mut u8,
|
||||
aligned_operation.layout,
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -582,7 +581,7 @@ impl Request {
|
||||
}
|
||||
|
||||
pub fn set_writeback(&mut self, writeback: bool) {
|
||||
self.writeback = writeback
|
||||
self.writeback = writeback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,7 +644,7 @@ pub fn block_io_uring_is_supported() -> bool {
|
||||
let io_uring = match IoUring::new(1) {
|
||||
Ok(io_uring) => io_uring,
|
||||
Err(e) => {
|
||||
info!("{} failed to create io_uring instance: {}", error_msg, e);
|
||||
info!("{error_msg} failed to create io_uring instance: {e}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -658,26 +657,26 @@ pub fn block_io_uring_is_supported() -> bool {
|
||||
match submitter.register_probe(&mut probe) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
info!("{} failed to register a probe: {}", error_msg, e);
|
||||
info!("{error_msg} failed to register a probe: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check IORING_OP_FSYNC is supported
|
||||
if !probe.is_supported(opcode::Fsync::CODE) {
|
||||
info!("{} IORING_OP_FSYNC operation not supported", error_msg);
|
||||
info!("{error_msg} IORING_OP_FSYNC operation not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IORING_OP_READV is supported
|
||||
if !probe.is_supported(opcode::Readv::CODE) {
|
||||
info!("{} IORING_OP_READV operation not supported", error_msg);
|
||||
info!("{error_msg} IORING_OP_READV operation not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IORING_OP_WRITEV is supported
|
||||
if !probe.is_supported(opcode::Writev::CODE) {
|
||||
info!("{} IORING_OP_WRITEV operation not supported", error_msg);
|
||||
info!("{error_msg} IORING_OP_WRITEV operation not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -835,7 +834,14 @@ pub fn detect_image_type(f: &mut File) -> std::io::Result<ImageType> {
|
||||
}
|
||||
|
||||
pub trait BlockBackend: Read + Write + Seek + Send + Debug {
|
||||
fn size(&self) -> Result<u64, Error>;
|
||||
/// Returns the logical disk size a guest will see.
|
||||
///
|
||||
/// For raw formats, this is equal to [`Self::physical_size`]. For file formats
|
||||
/// that wrap disk images in a container (e.g. QCOW2), this refers to the
|
||||
/// effective size that the guest will see.
|
||||
fn logical_size(&self) -> Result<u64, Error>;
|
||||
/// Returns the physical size of the underlying file.
|
||||
fn physical_size(&self) -> Result<u64, Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -862,6 +868,7 @@ ioctl_io_nr!(BLKPBSZGET, 0x12, 123);
|
||||
ioctl_io_nr!(BLKIOMIN, 0x12, 120);
|
||||
ioctl_io_nr!(BLKIOOPT, 0x12, 121);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum BlockSize {
|
||||
LogicalBlock,
|
||||
PhysicalBlock,
|
||||
@@ -901,7 +908,7 @@ impl DiskTopology {
|
||||
};
|
||||
if ret != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
};
|
||||
}
|
||||
|
||||
Ok(block_size)
|
||||
}
|
||||
|
||||
87
block/src/qcow/decoder.rs
Normal file
87
block/src/qcow/decoder.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2025 The Cloud Hypervisor Authors. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Zlib decompress error")]
|
||||
ZlibDecompress(#[source] flate2::DecompressError),
|
||||
#[error("Zlib unexpected status: {0:?}")]
|
||||
ZlibUnexpectedStatus(flate2::Status),
|
||||
#[error("Zstd decompress error")]
|
||||
ZstdDecompress(#[source] std::io::Error),
|
||||
#[error("Zstd: failed to fill buffer")]
|
||||
ZstdFillBuffer(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Generic trait for decoding zlib/zstd formats
|
||||
pub trait Decoder {
|
||||
fn decode(&self, input: &[u8], output: &mut [u8]) -> Result<usize>;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ZlibDecoder {}
|
||||
|
||||
impl Decoder for ZlibDecoder {
|
||||
fn decode(&self, input: &[u8], output: &mut [u8]) -> Result<usize> {
|
||||
use flate2::{Decompress, FlushDecompress, Status};
|
||||
|
||||
let mut decompressor = Decompress::new(false);
|
||||
let status = decompressor
|
||||
.decompress(input, output, FlushDecompress::Finish)
|
||||
.map_err(Error::ZlibDecompress)?;
|
||||
if status == Status::StreamEnd {
|
||||
Ok(decompressor.total_out() as usize)
|
||||
} else {
|
||||
Err(Error::ZlibUnexpectedStatus(status))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ZstdDecoder {}
|
||||
|
||||
impl Decoder for ZstdDecoder {
|
||||
fn decode(&self, input: &[u8], output: &mut [u8]) -> Result<usize> {
|
||||
use std::io::Read;
|
||||
|
||||
let mut decoder = zstd::stream::read::Decoder::new(input).map_err(Error::ZstdDecompress)?;
|
||||
let decoded_size = decoder.read(output).map_err(Error::ZstdFillBuffer)?;
|
||||
Ok(decoded_size)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_zlib_decode() {
|
||||
let d = ZlibDecoder::default();
|
||||
let valid_input = vec![99, 96, 100, 98, 6, 0];
|
||||
let mut output1 = vec![0; 4];
|
||||
d.decode(&valid_input, &mut output1).unwrap();
|
||||
assert_eq!(&output1, b"\x00\x01\x02\x03");
|
||||
|
||||
let invalid_input = vec![1, 2, 3, 4];
|
||||
let mut output2 = vec![0; 1024];
|
||||
d.decode(&invalid_input, &mut output2).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zstd_decode() {
|
||||
let d = ZstdDecoder::default();
|
||||
let valid_input = vec![40, 181, 47, 253, 32, 2, 17, 0, 0, 1, 254];
|
||||
let mut output1 = vec![0; 2];
|
||||
d.decode(&valid_input, &mut output1).unwrap();
|
||||
assert_eq!(&output1, b"\x01\xfe");
|
||||
|
||||
let invalid_input = vec![1, 2, 3, 4];
|
||||
let mut output2 = vec![0; 1024];
|
||||
d.decode(&invalid_input, &mut output2).unwrap_err();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
mod decoder;
|
||||
mod qcow_raw_file;
|
||||
mod raw_file;
|
||||
mod refcount;
|
||||
@@ -17,7 +18,8 @@ use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::str;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use libc::{EINVAL, ENOSPC, ENOTSUP};
|
||||
use libc::{EINVAL, EIO, ENOSPC};
|
||||
use log::error;
|
||||
use remain::sorted;
|
||||
use thiserror::Error;
|
||||
use vmm_sys_util::file_traits::{FileSetLen, FileSync};
|
||||
@@ -25,6 +27,7 @@ use vmm_sys_util::seek_hole::SeekHole;
|
||||
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||
|
||||
use crate::BlockBackend;
|
||||
use crate::qcow::decoder::{Decoder, ZlibDecoder, ZstdDecoder};
|
||||
use crate::qcow::qcow_raw_file::QcowRawFile;
|
||||
pub use crate::qcow::raw_file::RawFile;
|
||||
use crate::qcow::refcount::RefCount;
|
||||
@@ -42,8 +45,6 @@ pub enum Error {
|
||||
BackingFileOpen(#[source] Box<Error>),
|
||||
#[error("Backing file name is too long: {0} bytes over")]
|
||||
BackingFileTooLong(usize),
|
||||
#[error("Compressed blocks not supported")]
|
||||
CompressedBlocksNotSupported,
|
||||
#[error("Failed to evict cache")]
|
||||
EvictingCache(#[source] io::Error),
|
||||
#[error("File larger than max of {MAX_QCOW_FILE_SIZE}: {0}")]
|
||||
@@ -110,6 +111,8 @@ pub enum Error {
|
||||
TooManyL1Entries(u64),
|
||||
#[error("Ref count table too large: {0}")]
|
||||
TooManyRefcounts(u64),
|
||||
#[error("Unsupported compression type")]
|
||||
UnsupportedCompressionType,
|
||||
#[error("Unsupported refcount order")]
|
||||
UnsupportedRefcountOrder,
|
||||
#[error("Unsupported version: {0}")]
|
||||
@@ -122,11 +125,18 @@ pub enum Error {
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ImageType {
|
||||
Raw,
|
||||
Qcow2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CompressionType {
|
||||
Zlib,
|
||||
Zstd,
|
||||
}
|
||||
|
||||
// Maximum data size supported.
|
||||
const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; // 16 TB.
|
||||
|
||||
@@ -153,15 +163,55 @@ const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||
const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||
// Flags
|
||||
const COMPRESSED_FLAG: u64 = 1 << 62;
|
||||
const COMPRESSED_SECTOR_SIZE: u64 = 512;
|
||||
const CLUSTER_USED_FLAG: u64 = 1 << 63;
|
||||
const COMPATIBLE_FEATURES_LAZY_REFCOUNTS: u64 = 1;
|
||||
|
||||
// Compression types as defined in https://www.qemu.org/docs/master/interop/qcow2.html
|
||||
const COMPRESSION_TYPE_ZLIB: u64 = 0; // zlib/deflate <https://www.ietf.org/rfc/rfc1951.txt>
|
||||
const COMPRESSION_TYPE_ZSTD: u64 = 1; // zstd <http://github.com/facebook/zstd>
|
||||
|
||||
// The format supports a "header extension area", that crosvm does not use.
|
||||
const QCOW_EMPTY_HEADER_EXTENSION_SIZE: u32 = 8;
|
||||
|
||||
// Defined by the specification
|
||||
const MAX_BACKING_FILE_SIZE: u32 = 1023;
|
||||
|
||||
fn l2_entry_is_empty(l2_entry: u64) -> bool {
|
||||
l2_entry == 0
|
||||
}
|
||||
|
||||
fn l2_entry_is_compressed(l2_entry: u64) -> bool {
|
||||
l2_entry & COMPRESSED_FLAG != 0
|
||||
}
|
||||
|
||||
// Get file offset and size of compressed cluster data
|
||||
fn l2_entry_compressed_cluster_layout(l2_entry: u64, cluster_bits: u32) -> (u64, usize) {
|
||||
let compressed_size_shift = 62 - (cluster_bits - 8);
|
||||
let compressed_size_mask = (1 << (cluster_bits - 8)) - 1;
|
||||
let compressed_cluster_addr = l2_entry & ((1 << compressed_size_shift) - 1);
|
||||
let nsectors = (l2_entry >> compressed_size_shift & compressed_size_mask) + 1;
|
||||
let compressed_cluster_size = ((nsectors * COMPRESSED_SECTOR_SIZE)
|
||||
- (compressed_cluster_addr & (COMPRESSED_SECTOR_SIZE - 1)))
|
||||
as usize;
|
||||
(compressed_cluster_addr, compressed_cluster_size)
|
||||
}
|
||||
|
||||
// Get file offset of standard (non-compressed) cluster
|
||||
fn l2_entry_std_cluster_addr(l2_entry: u64) -> u64 {
|
||||
l2_entry & L2_TABLE_OFFSET_MASK
|
||||
}
|
||||
|
||||
// Make L2 entry for standard (non-compressed) cluster
|
||||
fn l2_entry_make_std(cluster_addr: u64) -> u64 {
|
||||
(cluster_addr & L2_TABLE_OFFSET_MASK) | CLUSTER_USED_FLAG
|
||||
}
|
||||
|
||||
// Make L1 entry with optional flags
|
||||
fn l1_entry_make(cluster_addr: u64, refcount_is_one: bool) -> u64 {
|
||||
(cluster_addr & L1_TABLE_OFFSET_MASK) | (refcount_is_one as u64 * CLUSTER_USED_FLAG)
|
||||
}
|
||||
|
||||
/// Contains the information from the header of a qcow file.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QcowHeader {
|
||||
@@ -190,6 +240,7 @@ pub struct QcowHeader {
|
||||
pub autoclear_features: u64,
|
||||
pub refcount_order: u32,
|
||||
pub header_size: u32,
|
||||
pub compression_type: CompressionType,
|
||||
|
||||
// Post-header entries
|
||||
pub backing_file_path: Option<String>,
|
||||
@@ -255,8 +306,19 @@ impl QcowHeader {
|
||||
} else {
|
||||
read_u32_from_file(f)?
|
||||
},
|
||||
compression_type: CompressionType::Zlib,
|
||||
backing_file_path: None,
|
||||
};
|
||||
if version == 3 && header.header_size > V3_BARE_HEADER_SIZE {
|
||||
let raw_compression_type = read_u64_from_file(f)? >> (64 - 8);
|
||||
header.compression_type = if raw_compression_type == COMPRESSION_TYPE_ZLIB {
|
||||
Ok(CompressionType::Zlib)
|
||||
} else if raw_compression_type == COMPRESSION_TYPE_ZSTD {
|
||||
Ok(CompressionType::Zstd)
|
||||
} else {
|
||||
Err(Error::UnsupportedCompressionType)
|
||||
}?;
|
||||
}
|
||||
if header.backing_file_size > MAX_BACKING_FILE_SIZE {
|
||||
return Err(Error::BackingFileTooLong(header.backing_file_size as usize));
|
||||
}
|
||||
@@ -274,6 +336,13 @@ impl QcowHeader {
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
pub fn get_decoder(&self) -> Box<dyn Decoder> {
|
||||
match self.compression_type {
|
||||
CompressionType::Zlib => Box::new(ZlibDecoder {}),
|
||||
CompressionType::Zstd => Box::new(ZstdDecoder {}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_for_size_and_path(
|
||||
version: u32,
|
||||
size: u64,
|
||||
@@ -337,6 +406,7 @@ impl QcowHeader {
|
||||
autoclear_features: 0,
|
||||
refcount_order: DEFAULT_REFCOUNT_ORDER,
|
||||
header_size,
|
||||
compression_type: CompressionType::Zlib,
|
||||
backing_file_path: backing_file.map(String::from),
|
||||
})
|
||||
}
|
||||
@@ -588,17 +658,6 @@ impl QcowFile {
|
||||
|
||||
let l2_entries = cluster_size / size_of::<u64>() as u64;
|
||||
|
||||
// Check for compressed blocks
|
||||
for l2_addr_disk in l1_table.get_values() {
|
||||
if *l2_addr_disk != 0
|
||||
&& let Err(e) = Self::read_l2_cluster(&mut raw_file, *l2_addr_disk)
|
||||
&& let Some(os_error) = e.raw_os_error()
|
||||
&& os_error == ENOTSUP
|
||||
{
|
||||
return Err(Error::CompressedBlocksNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
let mut qcow = QcowFile {
|
||||
raw_file,
|
||||
header,
|
||||
@@ -630,7 +689,7 @@ impl QcowFile {
|
||||
/// Creates a new QcowFile at the given path.
|
||||
pub fn new(file: RawFile, version: u32, virtual_size: u64) -> Result<QcowFile> {
|
||||
let header = QcowHeader::create_for_size_and_path(version, virtual_size, None)?;
|
||||
QcowFile::new_from_header(file, header)
|
||||
QcowFile::new_from_header(file, &header)
|
||||
}
|
||||
|
||||
/// Creates a new QcowFile at the given path.
|
||||
@@ -652,12 +711,12 @@ impl QcowFile {
|
||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||
let size = backing_file.virtual_size();
|
||||
let header = QcowHeader::create_for_size_and_path(version, size, Some(backing_file_name))?;
|
||||
let mut result = QcowFile::new_from_header(file, header)?;
|
||||
let mut result = QcowFile::new_from_header(file, &header)?;
|
||||
result.backing_file = Some(Box::new(backing_file));
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn new_from_header(mut file: RawFile, header: QcowHeader) -> Result<QcowFile> {
|
||||
fn new_from_header(mut file: RawFile, header: &QcowHeader) -> Result<QcowFile> {
|
||||
file.rewind().map_err(Error::SeekingFile)?;
|
||||
header.write_to(&mut file)?;
|
||||
|
||||
@@ -714,11 +773,7 @@ impl QcowFile {
|
||||
let raw_file = &mut self.raw_file;
|
||||
self.l2_cache
|
||||
.insert(l1_index, table, |index, evicted| {
|
||||
raw_file.write_pointer_table(
|
||||
l1_table[index],
|
||||
evicted.get_values(),
|
||||
CLUSTER_USED_FLAG,
|
||||
)
|
||||
raw_file.write_pointer_table_direct(l1_table[index], evicted.iter())
|
||||
})
|
||||
.map_err(Error::EvictingCache)?;
|
||||
}
|
||||
@@ -805,7 +860,7 @@ impl QcowFile {
|
||||
// Add references to the L1 table clusters.
|
||||
fn set_l1_refcounts(
|
||||
refcounts: &mut [u16],
|
||||
header: QcowHeader,
|
||||
header: &QcowHeader,
|
||||
cluster_size: u64,
|
||||
) -> Result<()> {
|
||||
let entries_per_cluster = cluster_size / size_of::<u64>() as u64;
|
||||
@@ -820,7 +875,7 @@ impl QcowFile {
|
||||
// Traverse the L1 and L2 tables to find all reachable data clusters.
|
||||
fn set_data_refcounts(
|
||||
refcounts: &mut [u16],
|
||||
header: QcowHeader,
|
||||
header: &QcowHeader,
|
||||
cluster_size: u64,
|
||||
raw_file: &mut QcowRawFile,
|
||||
) -> Result<()> {
|
||||
@@ -859,7 +914,7 @@ impl QcowFile {
|
||||
// Add references to the top-level refcount table clusters.
|
||||
fn set_refcount_table_refcounts(
|
||||
refcounts: &mut [u16],
|
||||
header: QcowHeader,
|
||||
header: &QcowHeader,
|
||||
cluster_size: u64,
|
||||
) -> Result<()> {
|
||||
let refcount_table_offset = header.refcount_table_offset;
|
||||
@@ -943,7 +998,7 @@ impl QcowFile {
|
||||
|
||||
// Rewrite the top-level refcount table.
|
||||
raw_file
|
||||
.write_pointer_table(header.refcount_table_offset, ref_table, 0)
|
||||
.write_pointer_table_direct(header.refcount_table_offset, ref_table.iter())
|
||||
.map_err(Error::WritingHeader)?;
|
||||
|
||||
// Rewrite the header again, now with lazy refcounts disabled.
|
||||
@@ -997,9 +1052,9 @@ impl QcowFile {
|
||||
|
||||
// Find all references clusters and rebuild refcounts.
|
||||
set_header_refcount(&mut refcounts, cluster_size)?;
|
||||
set_l1_refcounts(&mut refcounts, header.clone(), cluster_size)?;
|
||||
set_data_refcounts(&mut refcounts, header.clone(), cluster_size, raw_file)?;
|
||||
set_refcount_table_refcounts(&mut refcounts, header.clone(), cluster_size)?;
|
||||
set_l1_refcounts(&mut refcounts, &header, cluster_size)?;
|
||||
set_data_refcounts(&mut refcounts, &header, cluster_size, raw_file)?;
|
||||
set_refcount_table_refcounts(&mut refcounts, &header, cluster_size)?;
|
||||
|
||||
// Allocate clusters to store the new reference count blocks.
|
||||
let ref_table = alloc_refblocks(&mut refcounts, cluster_size, refblock_clusters)?;
|
||||
@@ -1050,11 +1105,40 @@ impl QcowFile {
|
||||
(address / self.raw_file.cluster_size()) % self.l2_entries
|
||||
}
|
||||
|
||||
// Gets the offset of the given guest address in the host file. If L1, L2, or data clusters have
|
||||
// yet to be allocated, return None.
|
||||
fn file_offset_read(&mut self, address: u64) -> std::io::Result<Option<u64>> {
|
||||
// Decompress the cluster, return EIO on failure
|
||||
fn decompress_l2_cluster(&mut self, l2_entry: u64) -> std::io::Result<Vec<u8>> {
|
||||
let (compressed_cluster_addr, compressed_cluster_size) =
|
||||
l2_entry_compressed_cluster_layout(l2_entry, self.header.cluster_bits);
|
||||
// Read compressed cluster from raw file
|
||||
self.raw_file
|
||||
.file_mut()
|
||||
.seek(SeekFrom::Start(compressed_cluster_addr))?;
|
||||
let mut compressed_cluster = vec![0; compressed_cluster_size];
|
||||
self.raw_file
|
||||
.file_mut()
|
||||
.read_exact(&mut compressed_cluster)?;
|
||||
let decoder = self.header.get_decoder();
|
||||
// Decompress
|
||||
let cluster_size = self.raw_file.cluster_size() as usize;
|
||||
let mut decompressed_cluster = vec![0; cluster_size];
|
||||
let decompressed_size = decoder
|
||||
.decode(&compressed_cluster, &mut decompressed_cluster)
|
||||
.map_err(|_| std::io::Error::from_raw_os_error(EIO))?;
|
||||
if decompressed_size as u64 != self.raw_file.cluster_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EIO));
|
||||
}
|
||||
Ok(decompressed_cluster)
|
||||
}
|
||||
|
||||
fn file_read(
|
||||
&mut self,
|
||||
address: u64,
|
||||
count: usize,
|
||||
buf: &mut [u8],
|
||||
) -> std::io::Result<Option<()>> {
|
||||
let err_inval = std::io::Error::from_raw_os_error(EINVAL);
|
||||
if address >= self.virtual_size() {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
return Err(err_inval);
|
||||
}
|
||||
|
||||
let l1_index = self.l1_table_index(address) as usize;
|
||||
@@ -1072,11 +1156,28 @@ impl QcowFile {
|
||||
|
||||
self.cache_l2_cluster(l1_index, l2_addr_disk, false)?;
|
||||
|
||||
let cluster_addr = self.l2_cache.get(l1_index).unwrap()[l2_index];
|
||||
if cluster_addr == 0 {
|
||||
let l2_entry = self.l2_cache.get(l1_index).unwrap()[l2_index];
|
||||
if l2_entry_is_empty(l2_entry) {
|
||||
// Reading from an unallocated cluster will return zeros.
|
||||
return Ok(None);
|
||||
} else if l2_entry_is_compressed(l2_entry) {
|
||||
// Compressed cluster.
|
||||
// Read it, decompress, then return slice from decompressed data.
|
||||
let mut decompressed_cluster = self.decompress_l2_cluster(l2_entry)?;
|
||||
decompressed_cluster.resize(self.raw_file.cluster_size() as usize, 0);
|
||||
let start = self.raw_file.cluster_offset(address) as usize;
|
||||
let end = start.checked_add(count);
|
||||
if end.is_none() || end.unwrap() > decompressed_cluster.len() {
|
||||
return Err(err_inval);
|
||||
}
|
||||
buf[..count].copy_from_slice(&decompressed_cluster[start..end.unwrap()]);
|
||||
} else {
|
||||
let start = l2_entry_std_cluster_addr(l2_entry) + self.raw_file.cluster_offset(address);
|
||||
let raw_file = self.raw_file.file_mut();
|
||||
raw_file.seek(SeekFrom::Start(start))?;
|
||||
raw_file.read_exact(buf)?;
|
||||
}
|
||||
Ok(Some(cluster_addr + self.raw_file.cluster_offset(address)))
|
||||
Ok(Some(()))
|
||||
}
|
||||
|
||||
// Gets the offset of the given guest address in the host file. If L1, L2, or data clusters need
|
||||
@@ -1100,29 +1201,67 @@ impl QcowFile {
|
||||
set_refcounts.push((new_addr, 1));
|
||||
}
|
||||
|
||||
let cluster_addr = match self.l2_cache.get(l1_index).unwrap()[l2_index] {
|
||||
0 => {
|
||||
let initial_data = if let Some(backing) = self.backing_file.as_mut() {
|
||||
let cluster_size = self.raw_file.cluster_size();
|
||||
let cluster_begin = address - (address % cluster_size);
|
||||
let mut cluster_data = vec![0u8; cluster_size as usize];
|
||||
backing.seek(SeekFrom::Start(cluster_begin))?;
|
||||
backing.read_exact(&mut cluster_data)?;
|
||||
Some(cluster_data)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Need to allocate a data cluster
|
||||
let cluster_addr = self.append_data_cluster(initial_data)?;
|
||||
self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
|
||||
cluster_addr
|
||||
let l2_entry = self.l2_cache.get(l1_index).unwrap()[l2_index];
|
||||
let cluster_addr = if l2_entry_is_compressed(l2_entry) {
|
||||
// Writing to compressed cluster.
|
||||
|
||||
let (compressed_cluster_addr, compressed_cluster_size) =
|
||||
l2_entry_compressed_cluster_layout(l2_entry, self.header.cluster_bits);
|
||||
|
||||
// Allocate new cluster, decompress into new cluster, then use
|
||||
// offset of new cluster.
|
||||
let decompressed_cluster = self.decompress_l2_cluster(l2_entry)?;
|
||||
let cluster_addr = self.append_data_cluster(None)?;
|
||||
self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
|
||||
self.raw_file
|
||||
.file_mut()
|
||||
.seek(SeekFrom::Start(cluster_addr))?;
|
||||
let nwritten = self.raw_file.file_mut().write(&decompressed_cluster)?;
|
||||
if nwritten != decompressed_cluster.len() {
|
||||
return Err(std::io::Error::from_raw_os_error(EIO));
|
||||
}
|
||||
a => a,
|
||||
|
||||
// Decrement refcount for each cluster spanned by the old compressed data
|
||||
let compressed_clusters_end = self.raw_file.cluster_address(
|
||||
compressed_cluster_addr // Start of compressed data
|
||||
+ compressed_cluster_size as u64 // Add size to get end address
|
||||
+ self.raw_file.cluster_size()
|
||||
- 1, // Catch possibly partially used last cluster
|
||||
);
|
||||
let mut addr = self.raw_file.cluster_address(compressed_cluster_addr);
|
||||
while addr < compressed_clusters_end {
|
||||
let refcount = self
|
||||
.refcounts
|
||||
.get_cluster_refcount(&mut self.raw_file, addr)
|
||||
.map_err(|e| std::io::Error::other(Error::GettingRefcount(e)))?;
|
||||
if refcount > 0 {
|
||||
self.set_cluster_refcount_track_freed(addr, refcount - 1)?;
|
||||
}
|
||||
addr += self.raw_file.cluster_size();
|
||||
}
|
||||
|
||||
cluster_addr
|
||||
} else if l2_entry_is_empty(l2_entry) {
|
||||
let initial_data = if let Some(backing) = self.backing_file.as_mut() {
|
||||
let cluster_size = self.raw_file.cluster_size();
|
||||
let cluster_begin = address - (address % cluster_size);
|
||||
let mut cluster_data = vec![0u8; cluster_size as usize];
|
||||
backing.seek(SeekFrom::Start(cluster_begin))?;
|
||||
backing.read_exact(&mut cluster_data)?;
|
||||
Some(cluster_data)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Need to allocate a data cluster
|
||||
let cluster_addr = self.append_data_cluster(initial_data)?;
|
||||
self.update_cluster_addr(l1_index, l2_index, cluster_addr, &mut set_refcounts)?;
|
||||
cluster_addr
|
||||
} else {
|
||||
l2_entry_std_cluster_addr(l2_entry)
|
||||
};
|
||||
|
||||
for (addr, count) in set_refcounts {
|
||||
let mut newly_unref = self.set_cluster_refcount(addr, count)?;
|
||||
self.unref_clusters.append(&mut newly_unref);
|
||||
self.set_cluster_refcount_track_freed(addr, count)?;
|
||||
}
|
||||
|
||||
Ok(cluster_addr + self.raw_file.cluster_offset(address))
|
||||
@@ -1157,7 +1296,7 @@ impl QcowFile {
|
||||
self.l1_table[l1_index] = new_addr;
|
||||
}
|
||||
// 'unwrap' is OK because it was just added.
|
||||
self.l2_cache.get_mut(l1_index).unwrap()[l2_index] = cluster_addr;
|
||||
self.l2_cache.get_mut(l1_index).unwrap()[l2_index] = l2_entry_make_std(cluster_addr);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1166,7 +1305,7 @@ impl QcowFile {
|
||||
// First use a pre allocated cluster if one is available.
|
||||
if let Some(free_cluster) = self.avail_clusters.pop() {
|
||||
if let Some(initial_data) = initial_data {
|
||||
self.raw_file.write_cluster(free_cluster, initial_data)?;
|
||||
self.raw_file.write_cluster(free_cluster, &initial_data)?;
|
||||
} else {
|
||||
self.raw_file.zero_cluster(free_cluster)?;
|
||||
}
|
||||
@@ -1176,7 +1315,7 @@ impl QcowFile {
|
||||
let max_valid_cluster_offset = self.refcounts.max_valid_cluster_offset();
|
||||
if let Some(new_cluster) = self.raw_file.add_cluster_end(max_valid_cluster_offset)? {
|
||||
if let Some(initial_data) = initial_data {
|
||||
self.raw_file.write_cluster(new_cluster, initial_data)?;
|
||||
self.raw_file.write_cluster(new_cluster, &initial_data)?;
|
||||
}
|
||||
Ok(new_cluster)
|
||||
} else {
|
||||
@@ -1190,8 +1329,7 @@ impl QcowFile {
|
||||
fn append_data_cluster(&mut self, initial_data: Option<Vec<u8>>) -> std::io::Result<u64> {
|
||||
let new_addr: u64 = self.get_new_cluster(initial_data)?;
|
||||
// The cluster refcount starts at one indicating it is used but doesn't need COW.
|
||||
let mut newly_unref = self.set_cluster_refcount(new_addr, 1)?;
|
||||
self.unref_clusters.append(&mut newly_unref);
|
||||
self.set_cluster_refcount_track_freed(new_addr, 1)?;
|
||||
Ok(new_addr)
|
||||
}
|
||||
|
||||
@@ -1296,8 +1434,7 @@ impl QcowFile {
|
||||
}
|
||||
|
||||
let new_refcount = refcount - 1;
|
||||
let mut newly_unref = self.set_cluster_refcount(cluster_addr, new_refcount)?;
|
||||
self.unref_clusters.append(&mut newly_unref);
|
||||
self.set_cluster_refcount_track_freed(cluster_addr, new_refcount)?;
|
||||
|
||||
// Rewrite the L2 entry to remove the cluster mapping.
|
||||
// unwrap is safe as we just checked/inserted this entry.
|
||||
@@ -1334,10 +1471,9 @@ impl QcowFile {
|
||||
// Partial cluster - zero out the relevant bytes if it was allocated.
|
||||
// Any space in unallocated clusters can be left alone, since
|
||||
// unallocated clusters already read back as zeroes.
|
||||
if let Some(offset) = self.file_offset_read(curr_addr)? {
|
||||
// Partial cluster - zero it out.
|
||||
self.raw_file.file_mut().write_zeroes_at(offset, count)?;
|
||||
}
|
||||
let offset = self.file_offset_write(curr_addr)?;
|
||||
// Partial cluster - zero it out.
|
||||
self.raw_file.file_mut().write_zeroes_at(offset, count)?;
|
||||
}
|
||||
|
||||
nwritten += count;
|
||||
@@ -1348,14 +1484,8 @@ impl QcowFile {
|
||||
// Reads an L2 cluster from the disk, returning an error if the file can't be read or if any
|
||||
// cluster is compressed.
|
||||
fn read_l2_cluster(raw_file: &mut QcowRawFile, cluster_addr: u64) -> std::io::Result<Vec<u64>> {
|
||||
let file_values = raw_file.read_pointer_cluster(cluster_addr, None)?;
|
||||
if file_values.iter().any(|entry| entry & COMPRESSED_FLAG != 0) {
|
||||
return Err(std::io::Error::from_raw_os_error(ENOTSUP));
|
||||
}
|
||||
Ok(file_values
|
||||
.iter()
|
||||
.map(|entry| *entry & L2_TABLE_OFFSET_MASK)
|
||||
.collect())
|
||||
let l2_table = raw_file.read_pointer_cluster(cluster_addr, None)?;
|
||||
Ok(l2_table)
|
||||
}
|
||||
|
||||
// Put an L2 cluster to the cache with evicting less-used cluster
|
||||
@@ -1383,16 +1513,23 @@ impl QcowFile {
|
||||
let l1_table = &self.l1_table;
|
||||
let raw_file = &mut self.raw_file;
|
||||
self.l2_cache.insert(l1_index, l2_table, |index, evicted| {
|
||||
raw_file.write_pointer_table(
|
||||
l1_table[index],
|
||||
evicted.get_values(),
|
||||
CLUSTER_USED_FLAG,
|
||||
)
|
||||
raw_file.write_pointer_table_direct(l1_table[index], evicted.iter())
|
||||
})?;
|
||||
}
|
||||
Ok(new_cluster)
|
||||
}
|
||||
|
||||
// Set the refcount for a cluster and add any unreferenced clusters to the unref list.
|
||||
fn set_cluster_refcount_track_freed(
|
||||
&mut self,
|
||||
address: u64,
|
||||
refcount: u16,
|
||||
) -> std::io::Result<()> {
|
||||
let mut newly_unref = self.set_cluster_refcount(address, refcount)?;
|
||||
self.unref_clusters.append(&mut newly_unref);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the refcount for a cluster with the given address.
|
||||
// Returns a list of any refblocks that can be reused, this happens when a refblock is moved,
|
||||
// the old location can be reused.
|
||||
@@ -1413,7 +1550,9 @@ impl QcowFile {
|
||||
refcount_set = true;
|
||||
}
|
||||
Ok(Some(freed_cluster)) => {
|
||||
unref_clusters.push(freed_cluster);
|
||||
// Recursively set the freed refcount block's refcount to 0
|
||||
let mut freed = self.set_cluster_refcount(freed_cluster, 0)?;
|
||||
unref_clusters.append(&mut freed);
|
||||
refcount_set = true;
|
||||
}
|
||||
Err(refcount::Error::EvictingRefCounts(e)) => {
|
||||
@@ -1456,11 +1595,8 @@ impl QcowFile {
|
||||
// The index must be valid from when we inserted it.
|
||||
let addr = self.l1_table[*l1_index];
|
||||
if addr != 0 {
|
||||
self.raw_file.write_pointer_table(
|
||||
addr,
|
||||
l2_table.get_values(),
|
||||
CLUSTER_USED_FLAG,
|
||||
)?;
|
||||
self.raw_file
|
||||
.write_pointer_table_direct(addr, l2_table.iter())?;
|
||||
} else {
|
||||
return Err(std::io::Error::from_raw_os_error(EINVAL));
|
||||
}
|
||||
@@ -1474,10 +1610,21 @@ impl QcowFile {
|
||||
// Push L1 table and refcount table last as all the clusters they point to are now
|
||||
// guaranteed to be valid.
|
||||
let mut sync_required = if self.l1_table.dirty() {
|
||||
// Write L1 table with OFLAG_COPIED bits
|
||||
let refcounts = &mut self.refcounts;
|
||||
self.raw_file.write_pointer_table(
|
||||
self.header.l1_table_offset,
|
||||
self.l1_table.get_values(),
|
||||
0,
|
||||
self.l1_table.iter(),
|
||||
|raw_file, l2_addr| {
|
||||
if l2_addr == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let refcount = refcounts
|
||||
.get_cluster_refcount(raw_file, l2_addr)
|
||||
.map_err(|e| std::io::Error::other(Error::GettingRefcount(e)))?;
|
||||
Ok(l1_entry_make(l2_addr, refcount == 1))
|
||||
}
|
||||
},
|
||||
)?;
|
||||
self.l1_table.mark_clean();
|
||||
true
|
||||
@@ -1512,14 +1659,10 @@ impl Read for QcowFile {
|
||||
let mut nread: usize = 0;
|
||||
while nread < read_count {
|
||||
let curr_addr = address + nread as u64;
|
||||
let file_offset = self.file_offset_read(curr_addr)?;
|
||||
let count = self.limit_range_cluster(curr_addr, read_count - nread);
|
||||
|
||||
if let Some(offset) = file_offset {
|
||||
self.raw_file.file_mut().seek(SeekFrom::Start(offset))?;
|
||||
self.raw_file
|
||||
.file_mut()
|
||||
.read_exact(&mut buf[nread..(nread + count)])?;
|
||||
if (self.file_read(curr_addr, count, &mut buf[nread..(nread + count)])?).is_some() {
|
||||
// Data is successfully read from the cluster
|
||||
} else if let Some(backing) = self.backing_file.as_mut() {
|
||||
backing.seek(SeekFrom::Start(curr_addr))?;
|
||||
backing.read_exact(&mut buf[nread..(nread + count)])?;
|
||||
@@ -1665,9 +1808,15 @@ impl SeekHole for QcowFile {
|
||||
}
|
||||
|
||||
impl BlockBackend for QcowFile {
|
||||
fn size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
fn logical_size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
Ok(self.virtual_size())
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
self.raw_file
|
||||
.physical_size()
|
||||
.map_err(crate::Error::GetFileMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an Error if the given offset doesn't align to a cluster boundary.
|
||||
@@ -1815,7 +1964,7 @@ pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -2743,8 +2892,6 @@ mod tests {
|
||||
assert_eq!(orig, read);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(qcow_file.first_zero_refcount().unwrap(), None);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -61,25 +61,49 @@ impl QcowRawFile {
|
||||
self.read_pointer_table(offset, count, mask)
|
||||
}
|
||||
|
||||
/// Writes `table` of u64 pointers to `offset` in the file.
|
||||
/// `non_zero_flags` will be ORed with all non-zero values in `table`.
|
||||
/// writing.
|
||||
pub fn write_pointer_table(
|
||||
/// Internal helper for creating a buffered writer for pointer tables.
|
||||
#[inline]
|
||||
fn setup_pointer_table_writer<T>(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
table: &[u64],
|
||||
non_zero_flags: u64,
|
||||
) -> io::Result<()> {
|
||||
entries: &impl Iterator<Item = T>,
|
||||
) -> io::Result<BufWriter<RawFile>> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
for addr in table {
|
||||
let val = if *addr == 0 {
|
||||
0
|
||||
} else {
|
||||
*addr | non_zero_flags
|
||||
};
|
||||
buffer.write_u64::<BigEndian>(val)?;
|
||||
let my_file = self.file.try_clone()?;
|
||||
let capacity = entries.size_hint().0 * size_of::<u64>();
|
||||
Ok(BufWriter::with_capacity(capacity, my_file))
|
||||
}
|
||||
|
||||
/// Writes a pointer table to `offset` in the file.
|
||||
/// Entries are computed on-the-fly by the callback.
|
||||
pub fn write_pointer_table<'a, T: Copy + 'a>(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
entries: impl Iterator<Item = &'a T>,
|
||||
mut f: impl FnMut(&mut QcowRawFile, T) -> io::Result<u64>,
|
||||
) -> io::Result<()> {
|
||||
let mut buffer = self.setup_pointer_table_writer(offset, &entries)?;
|
||||
|
||||
for addr in entries {
|
||||
let entry = f(self, *addr)?;
|
||||
buffer.write_u64::<BigEndian>(entry)?;
|
||||
}
|
||||
buffer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a pointer table directly without transforming values.
|
||||
pub fn write_pointer_table_direct<'a>(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
entries: impl Iterator<Item = &'a u64>,
|
||||
) -> io::Result<()> {
|
||||
let mut buffer = self.setup_pointer_table_writer(offset, &entries)?;
|
||||
|
||||
for &entry in entries {
|
||||
buffer.write_u64::<BigEndian>(entry)?;
|
||||
}
|
||||
buffer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -101,6 +125,7 @@ impl QcowRawFile {
|
||||
for count in table {
|
||||
buffer.write_u16::<BigEndian>(*count)?;
|
||||
}
|
||||
buffer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -135,6 +160,11 @@ impl QcowRawFile {
|
||||
address & self.cluster_mask
|
||||
}
|
||||
|
||||
/// Returns the base address of the cluster containing `address`.
|
||||
pub fn cluster_address(&self, address: u64) -> u64 {
|
||||
address & !self.cluster_mask
|
||||
}
|
||||
|
||||
/// Zeros out a cluster in the file.
|
||||
pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
|
||||
let cluster_size = self.cluster_size as usize;
|
||||
@@ -144,11 +174,15 @@ impl QcowRawFile {
|
||||
}
|
||||
|
||||
/// Writes
|
||||
pub fn write_cluster(&mut self, address: u64, data: Vec<u8>) -> io::Result<()> {
|
||||
pub fn write_cluster(&mut self, address: u64, data: &[u8]) -> io::Result<()> {
|
||||
let cluster_size = self.cluster_size as usize;
|
||||
self.file.seek(SeekFrom::Start(address))?;
|
||||
self.file.write_all(&data[0..cluster_size])
|
||||
}
|
||||
|
||||
pub fn physical_size(&self) -> Result<u64, std::io::Error> {
|
||||
self.file.metadata().map(|m| m.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for QcowRawFile {
|
||||
|
||||
@@ -259,7 +259,7 @@ impl Write for RawFile {
|
||||
// SAFETY: tmp_ptr was allocated by alloc_zeroed with layout
|
||||
unsafe { dealloc(tmp_ptr, layout) };
|
||||
return Err(io::Error::last_os_error());
|
||||
};
|
||||
}
|
||||
|
||||
tmp_buf[file_offset..(file_offset + buf_len)].copy_from_slice(buf);
|
||||
|
||||
@@ -354,7 +354,11 @@ impl SeekHole for RawFile {
|
||||
}
|
||||
|
||||
impl BlockBackend for RawFile {
|
||||
fn size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
fn logical_size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
Ok(self.metadata().map_err(crate::Error::RawFileError)?.len())
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
Ok(self.metadata().map_err(crate::Error::RawFileError)?.len())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,9 @@ impl RefCount {
|
||||
}
|
||||
|
||||
// Unwrap is safe here as the entry was filled directly above.
|
||||
let dropped_cluster = if !self.refblock_cache.get(table_index).unwrap().dirty() {
|
||||
let dropped_cluster = if self.refblock_cache.get(table_index).unwrap().dirty() {
|
||||
None
|
||||
} else {
|
||||
// Free the previously used block and use a new one. Writing modified counts to new
|
||||
// blocks keeps the on-disk state consistent even if it's out of date.
|
||||
if let Some((addr, _)) = new_cluster.take() {
|
||||
@@ -128,8 +130,6 @@ impl RefCount {
|
||||
} else {
|
||||
return Err(Error::NeedNewCluster);
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.refblock_cache.get_mut(table_index).unwrap()[block_index] = refcount;
|
||||
@@ -156,11 +156,8 @@ impl RefCount {
|
||||
/// Returns true if the table changed since the previous `flush_table()` call.
|
||||
pub fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool> {
|
||||
if self.ref_table.dirty() {
|
||||
raw_file.write_pointer_table(
|
||||
self.refcount_table_offset,
|
||||
self.ref_table.get_values(),
|
||||
0,
|
||||
)?;
|
||||
raw_file
|
||||
.write_pointer_table_direct(self.refcount_table_offset, self.ref_table.iter())?;
|
||||
self.ref_table.mark_clean();
|
||||
Ok(true)
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::IterMut;
|
||||
use std::io;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::ops::{Deref, Index, IndexMut};
|
||||
use std::slice::SliceIndex;
|
||||
|
||||
/// Trait that allows for checking if an implementor is dirty. Useful for types that are cached so
|
||||
@@ -85,6 +85,14 @@ impl<T: 'static + Copy + Default> IndexMut<usize> for VecCache<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Copy + Default> Deref for VecCache<T> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &[T] {
|
||||
&self.vec
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CacheMap<T: Cacheable> {
|
||||
capacity: usize,
|
||||
@@ -135,7 +143,7 @@ impl<T: Cacheable> CacheMap<T> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
|
||||
struct NumCache(());
|
||||
|
||||
@@ -9,11 +9,11 @@ use std::os::fd::AsRawFd;
|
||||
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::AsyncAdaptor;
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::qcow::{QcowFile, RawFile, Result as QcowResult};
|
||||
use crate::{AsyncAdaptor, BlockBackend};
|
||||
|
||||
pub struct QcowDiskSync {
|
||||
qcow_file: QcowFile,
|
||||
@@ -28,12 +28,22 @@ impl QcowDiskSync {
|
||||
}
|
||||
|
||||
impl DiskFile for QcowDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.qcow_file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.qcow_file.physical_size().map_err(|e| {
|
||||
let io_inner = match e {
|
||||
crate::Error::GetFileMetadata(e) => e,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
DiskFileError::Size(io_inner)
|
||||
})
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(QcowSync::new(self.qcow_file.clone())) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::io::{Error, Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use io_uring::{IoUring, opcode, types};
|
||||
use log::warn;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::async_io::{
|
||||
@@ -25,12 +26,19 @@ impl RawFileDisk {
|
||||
}
|
||||
|
||||
impl DiskFile for RawFileDisk {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.metadata()
|
||||
.map(|m| m.len())
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
RawFileAsync::new(self.file.as_raw_fd(), ring_depth)
|
||||
@@ -47,6 +55,10 @@ impl DiskFile for RawFileDisk {
|
||||
}
|
||||
}
|
||||
|
||||
fn resize(&mut self, size: u64) -> DiskFileResult<()> {
|
||||
self.file.set_len(size).map_err(DiskFileError::ResizeError)
|
||||
}
|
||||
|
||||
fn fd(&mut self) -> BorrowedDiskFd<'_> {
|
||||
BorrowedDiskFd::new(self.file.as_raw_fd())
|
||||
}
|
||||
@@ -97,7 +109,7 @@ impl AsyncIo for RawFileAsync {
|
||||
.build()
|
||||
.user_data(user_data),
|
||||
)
|
||||
.map_err(|_| AsyncIoError::ReadVectored(Error::other("Submission queue is full")))?
|
||||
.map_err(|_| AsyncIoError::ReadVectored(Error::other("Submission queue is full")))?;
|
||||
};
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
@@ -125,7 +137,7 @@ impl AsyncIo for RawFileAsync {
|
||||
.build()
|
||||
.user_data(user_data),
|
||||
)
|
||||
.map_err(|_| AsyncIoError::WriteVectored(Error::other("Submission queue is full")))?
|
||||
.map_err(|_| AsyncIoError::WriteVectored(Error::other("Submission queue is full")))?;
|
||||
};
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
@@ -147,7 +159,7 @@ impl AsyncIo for RawFileAsync {
|
||||
.build()
|
||||
.user_data(user_data),
|
||||
)
|
||||
.map_err(|_| AsyncIoError::Fsync(Error::other("Submission queue is full")))?
|
||||
.map_err(|_| AsyncIoError::Fsync(Error::other("Submission queue is full")))?;
|
||||
};
|
||||
|
||||
// Update the submission queue and submit new operations to the
|
||||
@@ -199,7 +211,7 @@ impl AsyncIo for RawFileAsync {
|
||||
)
|
||||
.map_err(|_| {
|
||||
AsyncIoError::ReadVectored(Error::other("Submission queue is full"))
|
||||
})?
|
||||
})?;
|
||||
};
|
||||
submitted = true;
|
||||
}
|
||||
@@ -219,7 +231,7 @@ impl AsyncIo for RawFileAsync {
|
||||
)
|
||||
.map_err(|_| {
|
||||
AsyncIoError::WriteVectored(Error::other("Submission queue is full"))
|
||||
})?
|
||||
})?;
|
||||
};
|
||||
submitted = true;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use log::warn;
|
||||
use vmm_sys_util::aio;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -28,12 +29,19 @@ impl RawFileDiskAio {
|
||||
}
|
||||
|
||||
impl DiskFile for RawFileDiskAio {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.metadata()
|
||||
.map(|m| m.len())
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(
|
||||
RawFileAsyncAio::new(self.file.as_raw_fd(), ring_depth)
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
use log::warn;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::DiskTopology;
|
||||
@@ -25,12 +26,19 @@ impl RawFileDiskSync {
|
||||
}
|
||||
|
||||
impl DiskFile for RawFileDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.seek(SeekFrom::End(0))
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.file
|
||||
.metadata()
|
||||
.map(|m| m.len())
|
||||
.map_err(DiskFileError::Size)
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(Box::new(RawFileSync::new(self.file.as_raw_fd())) as Box<dyn AsyncIo>)
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn is_fixed_vhd(f: &mut File) -> std::io::Result<bool> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
|
||||
|
||||
@@ -202,9 +202,16 @@ impl Seek for Vhdx {
|
||||
}
|
||||
|
||||
impl BlockBackend for Vhdx {
|
||||
fn size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
fn logical_size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
Ok(self.virtual_disk_size())
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> std::result::Result<u64, crate::Error> {
|
||||
self.file
|
||||
.metadata()
|
||||
.map(|m| m.len())
|
||||
.map_err(crate::Error::GetFileMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Vhdx {
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
extern crate log;
|
||||
|
||||
use std::collections::btree_map::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
|
||||
@@ -128,7 +128,7 @@ pub fn read(
|
||||
_ => {
|
||||
return Err(VhdxIoError::InvalidBatEntryState);
|
||||
}
|
||||
};
|
||||
}
|
||||
sector_count -= sector.free_sectors;
|
||||
sector_index += sector.free_sectors;
|
||||
read_count += sector.free_bytes as usize;
|
||||
@@ -210,7 +210,7 @@ pub fn write(
|
||||
_ => {
|
||||
return Err(VhdxIoError::InvalidBatEntryState);
|
||||
}
|
||||
};
|
||||
}
|
||||
sector_count -= sector.free_sectors;
|
||||
sector_index += sector.free_sectors;
|
||||
write_count += sector.free_bytes as usize;
|
||||
|
||||
@@ -8,11 +8,11 @@ use std::os::fd::AsRawFd;
|
||||
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::AsyncAdaptor;
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
|
||||
};
|
||||
use crate::vhdx::{Result as VhdxResult, Vhdx};
|
||||
use crate::{AsyncAdaptor, BlockBackend, Error};
|
||||
|
||||
pub struct VhdxDiskSync {
|
||||
vhdx_file: Vhdx,
|
||||
@@ -27,10 +27,20 @@ impl VhdxDiskSync {
|
||||
}
|
||||
|
||||
impl DiskFile for VhdxDiskSync {
|
||||
fn size(&mut self) -> DiskFileResult<u64> {
|
||||
fn logical_size(&mut self) -> DiskFileResult<u64> {
|
||||
Ok(self.vhdx_file.virtual_disk_size())
|
||||
}
|
||||
|
||||
fn physical_size(&mut self) -> DiskFileResult<u64> {
|
||||
self.vhdx_file.physical_size().map_err(|e| {
|
||||
let io_inner = match e {
|
||||
Error::GetFileMetadata(e) => e,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
DiskFileError::Size(io_inner)
|
||||
})
|
||||
}
|
||||
|
||||
fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
|
||||
Ok(
|
||||
Box::new(VhdxSync::new(self.vhdx_file.clone()).map_err(DiskFileError::NewAsyncIo)?)
|
||||
|
||||
69
cloud-hypervisor/Cargo.toml
Normal file
69
cloud-hypervisor/Cargo.toml
Normal file
@@ -0,0 +1,69 @@
|
||||
[package]
|
||||
authors = ["The Cloud Hypervisor Authors"]
|
||||
build = "build.rs"
|
||||
default-run = "cloud-hypervisor"
|
||||
description = "Open source Virtual Machine Monitor (VMM) that runs on top of KVM & MSHV"
|
||||
edition = "2024"
|
||||
homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
|
||||
license = "Apache-2.0 AND BSD-3-Clause"
|
||||
name = "cloud-hypervisor"
|
||||
version = "50.0.0"
|
||||
# Minimum buildable version:
|
||||
# Keep in sync with version in .github/workflows/build.yaml
|
||||
# Policy on MSRV (see #4318):
|
||||
# Can only be bumped if satisfying any of the following:
|
||||
# a.) A dependency requires it,
|
||||
# b.) If we want to use a new feature and that MSRV is at least 6 months old,
|
||||
# c.) There is a security issue that is addressed by the toolchain update.
|
||||
rust-version = "1.89.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
api_client = { path = "../api_client" }
|
||||
clap = { workspace = true, features = ["string"] }
|
||||
dhat = { workspace = true, optional = true }
|
||||
env_logger = { workspace = true }
|
||||
epoll = { workspace = true }
|
||||
event_monitor = { path = "../event_monitor" }
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true, features = ["std"] }
|
||||
option_parser = { path = "../option_parser" }
|
||||
seccompiler = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
signal-hook = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tpm = { path = "../tpm" }
|
||||
tracer = { path = "../tracer" }
|
||||
vm-memory = { workspace = true }
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = { workspace = true }
|
||||
zbus = { version = "5.7.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = { workspace = true }
|
||||
net_util = { path = "../net_util" }
|
||||
serde_json = { workspace = true }
|
||||
test_infra = { path = "../test_infra" }
|
||||
wait-timeout = { workspace = true }
|
||||
|
||||
# Please adjust `vmm::feature_list()` accordingly when changing the
|
||||
# feature list below
|
||||
[features]
|
||||
dbus_api = ["vmm/dbus_api", "zbus"]
|
||||
default = ["io_uring", "kvm"]
|
||||
dhat-heap = ["dhat", "vmm/dhat-heap"] # For heap profiling
|
||||
fw_cfg = ["vmm/fw_cfg"]
|
||||
guest_debug = ["vmm/guest_debug"]
|
||||
igvm = ["mshv", "vmm/igvm"]
|
||||
io_uring = ["vmm/io_uring"]
|
||||
ivshmem = ["vmm/ivshmem"]
|
||||
kvm = ["vmm/kvm"]
|
||||
mshv = ["vmm/mshv"]
|
||||
pvmemcontrol = ["vmm/pvmemcontrol"]
|
||||
sev_snp = ["igvm", "mshv", "vmm/sev_snp"]
|
||||
tdx = ["vmm/tdx"]
|
||||
tracing = ["tracer/tracing", "vmm/tracing"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -65,6 +65,8 @@ enum Error {
|
||||
ReadingStdin(#[source] std::io::Error),
|
||||
#[error("Error reading from file")]
|
||||
ReadingFile(#[source] std::io::Error),
|
||||
#[error("Invalid disk size")]
|
||||
InvalidDiskSize(#[source] ByteSizedParseError),
|
||||
}
|
||||
|
||||
enum TargetApi<'a> {
|
||||
@@ -320,6 +322,22 @@ fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResu
|
||||
)?;
|
||||
simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient)
|
||||
}
|
||||
Some("resize-disk") => {
|
||||
let resize_disk = resize_disk_config(
|
||||
matches
|
||||
.subcommand_matches("resize-disk")
|
||||
.unwrap()
|
||||
.get_one::<String>("disk")
|
||||
.unwrap(),
|
||||
matches
|
||||
.subcommand_matches("resize-disk")
|
||||
.unwrap()
|
||||
.get_one::<String>("size")
|
||||
.unwrap(),
|
||||
)?;
|
||||
simple_api_command(socket, "PUT", "resize-disk", Some(&resize_disk))
|
||||
.map_err(Error::HttpApiClient)
|
||||
}
|
||||
Some("resize-zone") => {
|
||||
let resize_zone = resize_zone_config(
|
||||
matches
|
||||
@@ -399,7 +417,7 @@ fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResu
|
||||
.get_one::<String>("net_config")
|
||||
.unwrap(),
|
||||
)?;
|
||||
simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), fds)
|
||||
simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), &fds)
|
||||
.map_err(Error::HttpApiClient)
|
||||
}
|
||||
Some("add-user-device") => {
|
||||
@@ -454,7 +472,7 @@ fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResu
|
||||
.get_one::<String>("restore_config")
|
||||
.unwrap(),
|
||||
)?;
|
||||
simple_api_command_with_fds(socket, "PUT", "restore", Some(&restore_config), fds)
|
||||
simple_api_command_with_fds(socket, "PUT", "restore", Some(&restore_config), &fds)
|
||||
.map_err(Error::HttpApiClient)
|
||||
}
|
||||
Some("coredump") => {
|
||||
@@ -762,6 +780,15 @@ fn resize_config(
|
||||
Ok(serde_json::to_string(&resize).unwrap())
|
||||
}
|
||||
|
||||
fn resize_disk_config(id: &str, size: &str) -> Result<String, Error> {
|
||||
let resize_disk = vmm::api::VmResizeDiskData {
|
||||
id: id.to_owned(),
|
||||
desired_size: size.parse::<ByteSized>().map_err(Error::InvalidDiskSize)?.0,
|
||||
};
|
||||
|
||||
Ok(serde_json::to_string(&resize_disk).unwrap())
|
||||
}
|
||||
|
||||
fn resize_zone_config(id: &str, size: &str) -> Result<String, Error> {
|
||||
let resize_zone = vmm::api::VmResizeZoneData {
|
||||
id: id.to_owned(),
|
||||
@@ -1022,6 +1049,20 @@ fn get_cli_commands_sorted() -> Box<[Command]> {
|
||||
.help("New memory size in bytes (supports K/M/G suffix)")
|
||||
.num_args(1),
|
||||
),
|
||||
Command::new("resize-disk")
|
||||
.about("Resize an attached disk")
|
||||
.arg(
|
||||
Arg::new("disk")
|
||||
.long("disk")
|
||||
.help("Disk identifier")
|
||||
.num_args(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("size")
|
||||
.long("size")
|
||||
.help("New disk size")
|
||||
.num_args(1),
|
||||
),
|
||||
Command::new("resize-zone")
|
||||
.about("Resize a memory zone")
|
||||
.arg(
|
||||
@@ -1137,6 +1178,7 @@ fn main() {
|
||||
|
||||
if let Err(top_error) = target_api.do_command(&matches) {
|
||||
// Helper to join strings with a newline.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn join_strs(mut acc: String, next: String) -> String {
|
||||
if !acc.is_empty() {
|
||||
acc.push('\n');
|
||||
@@ -1161,7 +1203,7 @@ fn main() {
|
||||
if let Some(api_client::Error::ServerResponse(status_code, body)) =
|
||||
error.downcast_ref::<api_client::Error>()
|
||||
{
|
||||
let body = body.as_ref().map(|body| body.as_str()).unwrap_or("");
|
||||
let body = body.as_ref().map_or("", |body| body.as_str());
|
||||
|
||||
// Retrieve the list of error messages back.
|
||||
let lines: Vec<&str> = match serde_json::from_str(body) {
|
||||
@@ -1201,11 +1243,11 @@ fn main() {
|
||||
server_api_error_display_modifier,
|
||||
);
|
||||
process::exit(1)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::*;
|
||||
@@ -213,7 +213,8 @@ fn get_cli_options_sorted(
|
||||
topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,\
|
||||
kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,\
|
||||
affinity=<list_of_vcpus_with_their_associated_cpuset>,\
|
||||
features=<list_of_features_to_enable>",
|
||||
features=<list_of_features_to_enable>,\
|
||||
nested=on|off",
|
||||
)
|
||||
.default_value(default_vcpus)
|
||||
.group("vm-config"),
|
||||
@@ -486,7 +487,7 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
.args(args)
|
||||
}
|
||||
|
||||
fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
fn start_vmm(cmd_arguments: &ArgMatches) -> Result<Option<String>, Error> {
|
||||
let log_level = match cmd_arguments.get_count("v") {
|
||||
0 => LevelFilter::Warn,
|
||||
1 => LevelFilter::Info,
|
||||
@@ -722,7 +723,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
cmd_arguments.contains_id("kernel") || cmd_arguments.contains_id("firmware");
|
||||
|
||||
if payload_present {
|
||||
let vm_params = VmParams::from_arg_matches(&cmd_arguments);
|
||||
let vm_params = VmParams::from_arg_matches(cmd_arguments);
|
||||
let vm_config = VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||
|
||||
// Create and boot the VM based off the VM config we just built.
|
||||
@@ -770,7 +771,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
.map_err(Error::VmmThread)?;
|
||||
|
||||
if let Some(api_handle) = vmm_thread_handle.http_api_handle {
|
||||
http_api_graceful_shutdown(api_handle).map_err(Error::HttpApiShutdown)?
|
||||
http_api_graceful_shutdown(api_handle).map_err(Error::HttpApiShutdown)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "dbus_api")]
|
||||
@@ -884,7 +885,7 @@ fn main() {
|
||||
warn!("Error expanding FD table: {e}");
|
||||
}
|
||||
|
||||
let exit_code = match start_vmm(cmd_arguments) {
|
||||
let exit_code = match start_vmm(&cmd_arguments) {
|
||||
Ok(path) => {
|
||||
path.map(|s| std::fs::remove_file(s).ok());
|
||||
info!("Cloud Hypervisor exited successfully");
|
||||
@@ -961,6 +962,7 @@ mod unit_tests {
|
||||
max_phys_bits: 46,
|
||||
affinity: None,
|
||||
features: CpuFeatures::default(),
|
||||
nested: true,
|
||||
},
|
||||
memory: MemoryConfig {
|
||||
size: 536_870_912,
|
||||
@@ -2010,6 +2012,6 @@ mod unit_tests {
|
||||
let (default_vcpus, default_memory, default_rng) = prepare_default_values();
|
||||
let args = get_cli_options_sorted(default_vcpus, default_memory, default_rng);
|
||||
|
||||
assert_args_sorted(|| args.iter())
|
||||
assert_args_sorted(|| args.iter());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,7 @@ linux-loader = { workspace = true, features = [
|
||||
"pe",
|
||||
], optional = true }
|
||||
log = { workspace = true }
|
||||
num_enum = "0.7.4"
|
||||
num_enum = "0.7.5"
|
||||
pci = { path = "../pci" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
@@ -34,7 +34,7 @@ vm-memory = { workspace = true, features = [
|
||||
] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { workspace = true }
|
||||
zerocopy = { version = "0.8.27", features = [
|
||||
zerocopy = { version = "0.8.31", features = [
|
||||
"alloc",
|
||||
"derive",
|
||||
], optional = true }
|
||||
@@ -48,3 +48,6 @@ fw_cfg = ["arch/fw_cfg", "bitfield-struct", "linux-loader", "zerocopy"]
|
||||
ivshmem = []
|
||||
kvm = ["arch/kvm"]
|
||||
pvmemcontrol = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::thread;
|
||||
use std::time::Instant;
|
||||
|
||||
use acpi_tables::{Aml, AmlSink, aml};
|
||||
use log::{error, info, warn};
|
||||
use vm_device::BusDevice;
|
||||
use vm_device::interrupt::InterruptSourceGroup;
|
||||
use vm_memory::GuestAddress;
|
||||
@@ -44,7 +45,7 @@ impl AcpiShutdownDevice {
|
||||
impl BusDevice for AcpiShutdownDevice {
|
||||
// Spec has all fields as zero
|
||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||
data.fill(0)
|
||||
data.fill(0);
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
@@ -213,7 +214,7 @@ impl Aml for AcpiGedDevice {
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes(sink)
|
||||
.to_aml_bytes(sink);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
//
|
||||
// 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};
|
||||
|
||||
@@ -19,6 +17,8 @@ use vm_memory::address::Address;
|
||||
use vm_migration::{Migratable, Pausable, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use super::interrupt_controller::{Error, InterruptController};
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
// Reserve 32 IRQs for legacy devices.
|
||||
@@ -39,6 +39,7 @@ pub struct Aia {
|
||||
}
|
||||
|
||||
impl Aia {
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn new(
|
||||
vcpu_count: u32,
|
||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
@@ -51,9 +52,8 @@ impl Aia {
|
||||
})
|
||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||
|
||||
let vaia = vm
|
||||
.create_vaia(Aia::create_default_config(vcpu_count as u64))
|
||||
.map_err(Error::CreateAia)?;
|
||||
let config = Aia::create_default_config(vcpu_count as u64);
|
||||
let vaia = vm.create_vaia(&config).map_err(Error::CreateAia)?;
|
||||
|
||||
let aia = Aia {
|
||||
interrupt_source_group,
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::io;
|
||||
use std::io::Write;
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
use log::error;
|
||||
use vm_device::BusDevice;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
//
|
||||
// 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};
|
||||
|
||||
@@ -19,6 +17,8 @@ use vm_memory::address::Address;
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use super::interrupt_controller::{Error, InterruptController};
|
||||
|
||||
type Result<T> = result::Result<T, Error>;
|
||||
|
||||
// Reserve 32 IRQs for legacy devices.
|
||||
@@ -38,6 +38,7 @@ pub struct Gic {
|
||||
}
|
||||
|
||||
impl Gic {
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn new(
|
||||
vcpu_count: u32,
|
||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
@@ -50,9 +51,8 @@ impl Gic {
|
||||
})
|
||||
.map_err(Error::CreateInterruptSourceGroup)?;
|
||||
|
||||
let vgic = vm
|
||||
.create_vgic(Gic::create_default_config(vcpu_count as u64))
|
||||
.map_err(Error::CreateGic)?;
|
||||
let config = Gic::create_default_config(vcpu_count as u64);
|
||||
let vgic = vm.create_vgic(&config).map_err(Error::CreateGic)?;
|
||||
|
||||
let gic = Gic {
|
||||
interrupt_source_group,
|
||||
@@ -167,10 +167,7 @@ impl Pausable for Gic {
|
||||
// Flush tables to guest RAM
|
||||
let vgic = self.vgic.as_ref().unwrap().clone();
|
||||
vgic.lock().unwrap().save_data_tables().map_err(|e| {
|
||||
MigratableError::Pause(anyhow!(
|
||||
"Could not save GICv3ITS GIC pending tables {:?}",
|
||||
e
|
||||
))
|
||||
MigratableError::Pause(anyhow!("Could not save GICv3ITS GIC pending tables {e:?}",))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::result;
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use log::{debug, error, trace, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vm_device::BusDevice;
|
||||
use vm_device::interrupt::{
|
||||
@@ -190,8 +191,8 @@ impl Ioapic {
|
||||
pub fn new(
|
||||
id: String,
|
||||
apic_address: GuestAddress,
|
||||
interrupt_manager: Arc<dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>>,
|
||||
state: Option<IoapicState>,
|
||||
interrupt_manager: &dyn InterruptManager<GroupConfig = MsiIrqGroupConfig>,
|
||||
state: Option<&IoapicState>,
|
||||
) -> Result<Ioapic> {
|
||||
let interrupt_source_group = interrupt_manager
|
||||
.create_group(MsiIrqGroupConfig {
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::sync::{Arc, Barrier, Mutex};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use log::{debug, error, warn};
|
||||
use pci::{
|
||||
BarReprogrammingParams, PCI_CONFIGURATION_ID, PciBarConfiguration, PciBarPrefetchable,
|
||||
PciBarRegionType, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType,
|
||||
@@ -33,7 +34,7 @@ const IVSHMEM_DEVICE_ID: u16 = 0x1110;
|
||||
|
||||
const IVSHMEM_REG_BAR_SIZE: u64 = 0x100;
|
||||
|
||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||
type MmapRegion = vm_memory::MmapRegion<AtomicBitmap>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum IvshmemError {
|
||||
@@ -68,7 +69,7 @@ pub trait IvshmemOps: Send + Sync {
|
||||
start_addr: u64,
|
||||
size: usize,
|
||||
backing_file: Option<PathBuf>,
|
||||
) -> Result<(Arc<GuestRegionMmap>, UserspaceMapping), IvshmemError>;
|
||||
) -> Result<(Arc<MmapRegion>, UserspaceMapping), IvshmemError>;
|
||||
|
||||
fn unmap_ram_region(&mut self, mapping: UserspaceMapping) -> Result<(), IvshmemError>;
|
||||
}
|
||||
@@ -95,7 +96,7 @@ pub struct IvshmemDevice {
|
||||
region_size: u64,
|
||||
ivshmem_ops: Arc<Mutex<dyn IvshmemOps>>,
|
||||
backend_file: Option<PathBuf>,
|
||||
region: Option<Arc<GuestRegionMmap>>,
|
||||
region: Option<Arc<MmapRegion>>,
|
||||
userspace_mapping: Option<UserspaceMapping>,
|
||||
}
|
||||
|
||||
@@ -113,14 +114,14 @@ impl IvshmemDevice {
|
||||
region_size: u64,
|
||||
backend_file: Option<PathBuf>,
|
||||
ivshmem_ops: Arc<Mutex<dyn IvshmemOps>>,
|
||||
snapshot: Option<Snapshot>,
|
||||
snapshot: Option<&Snapshot>,
|
||||
) -> Result<Self, IvshmemError> {
|
||||
let pci_configuration_state =
|
||||
vm_migration::state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID).map_err(|e| {
|
||||
IvshmemError::RetrievePciConfigurationState(anyhow!(
|
||||
"Failed to get PciConfigurationState from Snapshot: {e}",
|
||||
))
|
||||
})?;
|
||||
let pci_configuration_state = vm_migration::state_from_id(snapshot, PCI_CONFIGURATION_ID)
|
||||
.map_err(|e| {
|
||||
IvshmemError::RetrievePciConfigurationState(anyhow!(
|
||||
"Failed to get PciConfigurationState from Snapshot: {e}",
|
||||
))
|
||||
})?;
|
||||
|
||||
let state: Option<IvshmemDeviceState> = snapshot
|
||||
.as_ref()
|
||||
@@ -180,11 +181,7 @@ impl IvshmemDevice {
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
pub fn set_region(
|
||||
&mut self,
|
||||
region: Arc<GuestRegionMmap>,
|
||||
userspace_mapping: UserspaceMapping,
|
||||
) {
|
||||
pub fn set_region(&mut self, region: Arc<MmapRegion>, userspace_mapping: UserspaceMapping) {
|
||||
self.region = Some(region);
|
||||
self.userspace_mapping = Some(userspace_mapping);
|
||||
}
|
||||
@@ -209,7 +206,7 @@ impl IvshmemDevice {
|
||||
|
||||
impl BusDevice for IvshmemDevice {
|
||||
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.read_bar(base, offset, data)
|
||||
self.read_bar(base, offset, data);
|
||||
}
|
||||
|
||||
fn write(&mut self, base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
@@ -233,20 +230,18 @@ impl PciDevice for IvshmemDevice {
|
||||
if let Some(resources) = resources {
|
||||
for resource in resources {
|
||||
match resource {
|
||||
Resource::PciBar { index, base, .. } => {
|
||||
match index {
|
||||
IVSHMEM_BAR0_IDX => {
|
||||
bar0_addr = Some(GuestAddress(base));
|
||||
}
|
||||
IVSHMEM_BAR1_IDX => {}
|
||||
IVSHMEM_BAR2_IDX => {
|
||||
bar2_addr = Some(GuestAddress(base));
|
||||
}
|
||||
_ => {
|
||||
error!("Unexpected pci bar index {index}");
|
||||
}
|
||||
};
|
||||
}
|
||||
Resource::PciBar { index, base, .. } => match index {
|
||||
IVSHMEM_BAR0_IDX => {
|
||||
bar0_addr = Some(GuestAddress(base));
|
||||
}
|
||||
IVSHMEM_BAR1_IDX => {}
|
||||
IVSHMEM_BAR2_IDX => {
|
||||
bar2_addr = Some(GuestAddress(base));
|
||||
}
|
||||
_ => {
|
||||
error!("Unexpected pci bar index {index}");
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
error!("Unexpected resource {resource:?}");
|
||||
}
|
||||
@@ -348,7 +343,7 @@ impl PciDevice for IvshmemDevice {
|
||||
_ => {
|
||||
warn!("Invalid bar_idx: {bar_idx}");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn write_bar(&mut self, base: u64, offset: u64, _data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::{mem, thread};
|
||||
#[cfg_attr(target_env = "musl", allow(deprecated))]
|
||||
use libc::time_t;
|
||||
use libc::{CLOCK_REALTIME, clock_gettime, gmtime_r, timespec, tm};
|
||||
use log::{info, warn};
|
||||
use vm_device::BusDevice;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -87,11 +88,11 @@ impl BusDevice for Cmos {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.data[(self.index & INDEX_MASK) as usize] = data[0]
|
||||
self.data[(self.index & INDEX_MASK) as usize] = data[0];
|
||||
}
|
||||
}
|
||||
o => warn!("bad write offset on CMOS device: {o}"),
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
use std::fmt;
|
||||
use std::time::Instant;
|
||||
|
||||
use log::{error, warn};
|
||||
use vm_device::BusDevice;
|
||||
|
||||
/// Debug I/O port, see:
|
||||
@@ -62,7 +63,7 @@ impl DebugPort {
|
||||
|
||||
impl BusDevice for DebugPort {
|
||||
fn read(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {
|
||||
error!("Invalid read to debug port")
|
||||
error!("Invalid read to debug port");
|
||||
}
|
||||
|
||||
fn write(
|
||||
|
||||
@@ -35,6 +35,7 @@ use bitfield_struct::bitfield;
|
||||
use linux_loader::bootparam::boot_params;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use linux_loader::loader::pe::arm64_image_header as boot_params;
|
||||
use log::{debug, error};
|
||||
use vm_device::BusDevice;
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::{
|
||||
@@ -442,7 +443,7 @@ impl FwCfg {
|
||||
fw_cfg_item_list: Option<Vec<FwCfgItem>>,
|
||||
) -> Result<()> {
|
||||
if let Some(mem_size) = mem_size {
|
||||
self.add_e820(mem_size)?
|
||||
self.add_e820(mem_size)?;
|
||||
}
|
||||
if let Some(kernel) = kernel {
|
||||
self.add_kernel_data(&kernel)?;
|
||||
@@ -451,7 +452,7 @@ impl FwCfg {
|
||||
self.add_kernel_cmdline(cmdline);
|
||||
}
|
||||
if let Some(initramfs) = initramfs {
|
||||
self.add_initramfs_data(&initramfs)?
|
||||
self.add_initramfs_data(&initramfs)?;
|
||||
}
|
||||
if let Some(fw_cfg_item_list) = fw_cfg_item_list {
|
||||
for item in fw_cfg_item_list {
|
||||
@@ -626,7 +627,7 @@ impl FwCfg {
|
||||
&access_resp.0.to_be_bytes(),
|
||||
GuestAddress(dma_address + core::mem::offset_of!(FwCfgDmaAccess, control_be) as u64),
|
||||
) {
|
||||
error!("fw_cfg: finishing dma: {e:?}")
|
||||
error!("fw_cfg: finishing dma: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,7 +711,7 @@ impl FwCfg {
|
||||
let bytes = n.to_le_bytes();
|
||||
data.copy_from_slice(&bytes[start..end]);
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(size as u8)
|
||||
}
|
||||
|
||||
@@ -756,7 +757,7 @@ impl BusDevice for FwCfg {
|
||||
"fw_cfg: read from unknown port {port:#x}: {size:#x} bytes and offset {offset:#x}."
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
@@ -792,13 +793,13 @@ impl BusDevice for FwCfg {
|
||||
_ => debug!(
|
||||
"fw_cfg: write to unknown port {port:#x}: {size:#x} bytes and offset {offset:#x} ."
|
||||
),
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use std::ffi::CString;
|
||||
use std::io::Write;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
use log::error;
|
||||
use vm_device::BusDevice;
|
||||
|
||||
/// Provides firmware debug output via I/O port controls
|
||||
@@ -26,9 +27,9 @@ impl BusDevice for FwDebugDevice {
|
||||
/// Upon read return the magic value to indicate that there is a debug port
|
||||
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
|
||||
if data.len() == 1 {
|
||||
data[0] = 0xe9
|
||||
data[0] = 0xe9;
|
||||
} else {
|
||||
error!("Invalid read size on debug port: {}", data.len())
|
||||
error!("Invalid read size on debug port: {}", data.len());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ impl BusDevice for FwDebugDevice {
|
||||
if data.len() == 1 {
|
||||
print!("{}", data[0] as char);
|
||||
} else {
|
||||
error!("Invalid write size on debug port: {}", data.len())
|
||||
error!("Invalid write size on debug port: {}", data.len());
|
||||
}
|
||||
|
||||
None
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::{io, result};
|
||||
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use vm_device::BusDevice;
|
||||
@@ -261,7 +262,7 @@ impl BusDevice for Gpio {
|
||||
let index = ((offset - GPIO_ID_LOW) >> 2) as usize;
|
||||
value = u32::from(GPIO_ID[index]);
|
||||
} else if offset < OFS_DATA {
|
||||
value = self.data & ((offset >> 2) as u32)
|
||||
value = self.data & ((offset >> 2) as u32);
|
||||
} else {
|
||||
value = match offset {
|
||||
GPIODIR => self.dir,
|
||||
@@ -294,7 +295,7 @@ impl BusDevice for Gpio {
|
||||
if data.len() <= 4 {
|
||||
let value = read_le_u32(data);
|
||||
if let Err(e) = self.handle_write(offset, value) {
|
||||
warn!("Failed to write to GPIO PL061 device: {}", e);
|
||||
warn!("Failed to write to GPIO PL061 device: {e}");
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
@@ -323,7 +324,7 @@ impl Transportable for Gpio {}
|
||||
impl Migratable for Gpio {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::thread;
|
||||
|
||||
use log::{error, info};
|
||||
use vm_device::BusDevice;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use std::result;
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
|
||||
use log::warn;
|
||||
use thiserror::Error;
|
||||
use vm_device::BusDevice;
|
||||
|
||||
@@ -201,12 +202,11 @@ impl BusDevice for Rtc {
|
||||
if data.len() <= 4 {
|
||||
let v = read_le_u32(data);
|
||||
if let Err(e) = self.handle_write(offset, v) {
|
||||
warn!("Failed to write to RTC PL031 device: {}", e);
|
||||
warn!("Failed to write to RTC PL031 device: {e}");
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Invalid RTC PL031 write: offset {}, data length {}",
|
||||
offset,
|
||||
"Invalid RTC PL031 write: offset {offset}, data length {}",
|
||||
data.len()
|
||||
);
|
||||
}
|
||||
@@ -216,7 +216,7 @@ impl BusDevice for Rtc {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u64, write_be_u16,
|
||||
|
||||
@@ -215,7 +215,7 @@ impl Serial {
|
||||
fn thr_empty(&mut self) -> Result<()> {
|
||||
if self.is_thr_intr_enabled() {
|
||||
self.add_intr_bit(IIR_THR_BIT);
|
||||
self.trigger_interrupt()?
|
||||
self.trigger_interrupt()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -223,7 +223,7 @@ impl Serial {
|
||||
fn recv_data(&mut self) -> Result<()> {
|
||||
if self.is_recv_intr_enabled() {
|
||||
self.add_intr_bit(IIR_RECV_BIT);
|
||||
self.trigger_interrupt()?
|
||||
self.trigger_interrupt()?;
|
||||
}
|
||||
self.line_status |= LSR_DATA_BIT;
|
||||
Ok(())
|
||||
@@ -240,10 +240,10 @@ impl Serial {
|
||||
fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
|
||||
match offset {
|
||||
DLAB_LOW if self.is_dlab_set() => {
|
||||
self.baud_divisor = (self.baud_divisor & 0xff00) | u16::from(v)
|
||||
self.baud_divisor = (self.baud_divisor & 0xff00) | u16::from(v);
|
||||
}
|
||||
DLAB_HIGH if self.is_dlab_set() => {
|
||||
self.baud_divisor = (self.baud_divisor & 0x00ff) | ((u16::from(v)) << 8)
|
||||
self.baud_divisor = (self.baud_divisor & 0x00ff) | ((u16::from(v)) << 8);
|
||||
}
|
||||
DATA => {
|
||||
if self.is_loop() {
|
||||
@@ -340,7 +340,7 @@ impl Transportable for Serial {}
|
||||
impl Migratable for Serial {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use std::sync::Mutex;
|
||||
|
||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::sync::{Arc, Barrier};
|
||||
use std::time::Instant;
|
||||
use std::{io, result};
|
||||
|
||||
use log::{debug, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use vm_device::BusDevice;
|
||||
@@ -322,7 +323,7 @@ impl Pl011 {
|
||||
self.handle_debug();
|
||||
}
|
||||
off => {
|
||||
debug!("PL011: Bad write offset, offset: {}", off);
|
||||
debug!("PL011: Bad write offset, offset: {off}");
|
||||
return Err(Error::BadWriteOffset(off));
|
||||
}
|
||||
}
|
||||
@@ -424,12 +425,11 @@ impl BusDevice for Pl011 {
|
||||
if data.len() <= 4 {
|
||||
let v = read_le_u32(data);
|
||||
if let Err(e) = self.handle_write(offset, v) {
|
||||
warn!("Failed to write to PL011 device: {}", e);
|
||||
warn!("Failed to write to PL011 device: {e}");
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Invalid PL011 write: offset {}, data length {}",
|
||||
offset,
|
||||
"Invalid PL011 write: offset {offset}, data length {}",
|
||||
data.len()
|
||||
);
|
||||
}
|
||||
@@ -453,7 +453,7 @@ impl Transportable for Pl011 {}
|
||||
impl Migratable for Pl011 {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use std::sync::Mutex;
|
||||
|
||||
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
|
||||
|
||||
@@ -7,13 +7,6 @@
|
||||
|
||||
//! Emulates virtual and hardware devices.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate event_monitor;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod acpi;
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod aia;
|
||||
@@ -34,6 +27,8 @@ pub mod pvpanic;
|
||||
#[cfg(not(target_arch = "riscv64"))]
|
||||
pub mod tpm;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
pub use self::acpi::{AcpiGedDevice, AcpiPmTimerDevice, AcpiShutdownDevice};
|
||||
#[cfg(feature = "ivshmem")]
|
||||
pub use self::ivshmem::IvshmemDevice;
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::ffi::CString;
|
||||
use std::sync::{Arc, Barrier, Mutex, RwLock};
|
||||
use std::{io, result};
|
||||
|
||||
use log::{debug, warn};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use pci::{
|
||||
BarReprogrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
||||
@@ -390,7 +391,7 @@ impl PvmemcontrolDevice {
|
||||
.iter()
|
||||
.skip(offset as usize)
|
||||
.zip(data.iter_mut())
|
||||
.for_each(|(src, dest)| *dest = *src)
|
||||
.for_each(|(src, dest)| *dest = *src);
|
||||
}
|
||||
|
||||
/// can only write to transport payload
|
||||
@@ -402,7 +403,7 @@ impl PvmemcontrolDevice {
|
||||
.iter_mut()
|
||||
.skip(offset as usize)
|
||||
.zip(data.iter())
|
||||
.for_each(|(dest, src)| *dest = *src)
|
||||
.for_each(|(dest, src)| *dest = *src);
|
||||
}
|
||||
|
||||
fn find_connection(&self, conn: GuestConnection) -> Option<GuestAddress> {
|
||||
@@ -429,22 +430,20 @@ impl PvmemcontrolBusDevice {
|
||||
/// [`range_base`, `range_base` + `range_len`) is present in the guest
|
||||
fn operate_on_memory_range<F>(&self, addr: u64, length: u64, f: F) -> result::Result<(), Error>
|
||||
where
|
||||
F: FnOnce(*mut libc::c_void, libc::size_t) -> libc::c_int,
|
||||
F: FnOnce(*mut libc::c_void, usize) -> libc::c_int,
|
||||
{
|
||||
let memory = self.mem.memory();
|
||||
let range_base = GuestAddress(addr);
|
||||
let range_len = usize::try_from(length).map_err(|_| Error::InvalidRequest)?;
|
||||
|
||||
// assume guest memory is not interleaved with vmm memory on the host.
|
||||
if !memory.check_range(range_base, range_len) {
|
||||
let Ok(slice) = memory.get_slice(range_base, range_len) else {
|
||||
return Err(Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
|
||||
range_base,
|
||||
)));
|
||||
}
|
||||
let hva = memory
|
||||
.get_host_address(range_base)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let res = f(hva as *mut libc::c_void, range_len as libc::size_t);
|
||||
};
|
||||
assert!(slice.len() >= range_len);
|
||||
let res = f(slice.ptr_guard_mut().as_ptr() as _, slice.len());
|
||||
if res != 0 {
|
||||
return Err(Error::LibcFail(io::Error::last_os_error()));
|
||||
}
|
||||
@@ -491,7 +490,7 @@ impl PvmemcontrolBusDevice {
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
debug!("addr {:X} length {} name {:?}", addr, length, name);
|
||||
debug!("addr {addr:X} length {length} name {name:?}");
|
||||
|
||||
// SAFETY: [`base`, `base` + `len`) is guest memory
|
||||
self.operate_on_memory_range(addr, length, |base, len| unsafe {
|
||||
@@ -581,7 +580,7 @@ impl PvmemcontrolBusDevice {
|
||||
..Default::default()
|
||||
},
|
||||
Error::GuestMemory(err) => {
|
||||
warn!("{}", err);
|
||||
warn!("{err}");
|
||||
PvmemcontrolResp {
|
||||
ret_errno: (libc::EINVAL as u32).into(),
|
||||
ret_code: (func_code as u32).into(),
|
||||
@@ -606,7 +605,7 @@ impl PvmemcontrolBusDevice {
|
||||
let response: PvmemcontrolResp = match self.handle_request(request) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
warn!("cannot process request {:?} with error {}", request, e);
|
||||
warn!("cannot process request {request:?} with error {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -649,14 +648,16 @@ impl PvmemcontrolBusDevice {
|
||||
.find_connection(conn)
|
||||
.ok_or(Error::InvalidConnection(conn.command))
|
||||
})
|
||||
.map(|gpa| self.handle_pvmemcontrol_request(gpa))
|
||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||
.map_or_else(
|
||||
|err| warn!("{err:?}"),
|
||||
|gpa| self.handle_pvmemcontrol_request(gpa),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_guest_read(&self, offset: u64, data: &mut [u8]) {
|
||||
self.dev.read().unwrap().read_transport(offset, data)
|
||||
self.dev.read().unwrap().read_transport(offset, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,7 +761,7 @@ impl PciDevice for PvmemcontrolPciDevice {
|
||||
_mmio64_allocator: &mut AddressAllocator,
|
||||
) -> Result<(), PciDeviceError> {
|
||||
for bar in self.bar_regions.drain(..) {
|
||||
mmio32_allocator.free(GuestAddress(bar.addr()), bar.size())
|
||||
mmio32_allocator.free(GuestAddress(bar.addr()), bar.size());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -805,7 +806,7 @@ impl Migratable for PvmemcontrolPciDevice {}
|
||||
|
||||
impl BusDeviceSync for PvmemcontrolBusDevice {
|
||||
fn read(&self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.handle_guest_read(offset, data)
|
||||
self.handle_guest_read(offset, data);
|
||||
}
|
||||
|
||||
fn write(&self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
|
||||
@@ -8,6 +8,8 @@ use std::result;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use event_monitor::event;
|
||||
use log::{debug, info};
|
||||
use pci::{
|
||||
BarReprogrammingParams, PCI_CONFIGURATION_ID, PciBarConfiguration, PciBarPrefetchable,
|
||||
PciBarRegionType, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType,
|
||||
@@ -64,13 +66,13 @@ pub struct PvPanicDeviceState {
|
||||
}
|
||||
|
||||
impl PvPanicDevice {
|
||||
pub fn new(id: String, snapshot: Option<Snapshot>) -> Result<Self, PvPanicError> {
|
||||
let pci_configuration_state =
|
||||
vm_migration::state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID).map_err(|e| {
|
||||
PvPanicError::RetrievePciConfigurationState(anyhow!(
|
||||
"Failed to get PciConfigurationState from Snapshot: {e}"
|
||||
))
|
||||
})?;
|
||||
pub fn new(id: String, snapshot: Option<&Snapshot>) -> Result<Self, PvPanicError> {
|
||||
let pci_configuration_state = vm_migration::state_from_id(snapshot, PCI_CONFIGURATION_ID)
|
||||
.map_err(|e| {
|
||||
PvPanicError::RetrievePciConfigurationState(anyhow!(
|
||||
"Failed to get PciConfigurationState from Snapshot: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut configuration = PciConfiguration::new(
|
||||
PVPANIC_VENDOR_ID,
|
||||
@@ -141,7 +143,7 @@ impl PvPanicDevice {
|
||||
|
||||
impl BusDevice for PvPanicDevice {
|
||||
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
|
||||
self.read_bar(base, offset, data)
|
||||
self.read_bar(base, offset, data);
|
||||
}
|
||||
|
||||
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
use std::cmp;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
use anyhow::anyhow;
|
||||
@@ -11,6 +12,7 @@ use anyhow::anyhow;
|
||||
use arch::aarch64::layout::{TPM_SIZE, TPM_START};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use arch::x86_64::layout::{TPM_SIZE, TPM_START};
|
||||
use log::{debug, error, warn};
|
||||
use thiserror::Error;
|
||||
use tpm::TPM_CRB_BUFFER_MAX;
|
||||
use tpm::emulator::{BackendCmd, Emulator};
|
||||
@@ -26,6 +28,7 @@ pub enum Error {
|
||||
type Result<T> = anyhow::Result<T, Error>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum LocStateFields {
|
||||
TpmEstablished,
|
||||
LocAssigned,
|
||||
@@ -34,12 +37,14 @@ enum LocStateFields {
|
||||
TpmRegValidSts,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum LocStsFields {
|
||||
Granted,
|
||||
BeenSeized,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum IntfIdFields {
|
||||
InterfaceType,
|
||||
InterfaceVersion,
|
||||
@@ -57,16 +62,19 @@ enum IntfIdFields {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum IntfId2Fields {
|
||||
Vid,
|
||||
Did,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum CtrlStsFields {
|
||||
TpmSts,
|
||||
TpmIdle,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum CrbRegister {
|
||||
LocState(LocStateFields),
|
||||
LocSts(LocStsFields),
|
||||
@@ -99,6 +107,7 @@ const CRB_LOC_CTRL_REQUEST_ACCESS: u32 = 1 << 0;
|
||||
const CRB_LOC_CTRL_RELINQUISH: u32 = 1 << 1;
|
||||
const CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: u32 = 1 << 3;
|
||||
const CRB_LOC_STS: u32 = 0x0C;
|
||||
|
||||
const fn get_crb_loc_sts_field(f: LocStsFields) -> (u32, u32, u32) {
|
||||
let (offset, len) = match f {
|
||||
LocStsFields::Granted => (0, 1),
|
||||
@@ -219,7 +228,7 @@ pub struct Tpm {
|
||||
}
|
||||
|
||||
impl Tpm {
|
||||
pub fn new(path: String) -> Result<Self> {
|
||||
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let emulator = Emulator::new(path)
|
||||
.map_err(|e| Error::Init(anyhow!("Failed while initializing tpm Emulator: {e:?}")))?;
|
||||
let mut tpm = Tpm {
|
||||
@@ -531,7 +540,7 @@ impl BusDevice for Tpm {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -86,6 +86,7 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
|
||||
| Restore the VM from a snapshot | `/vm.restore` | `/schemas/RestoreConfig` | N/A | The VM is created but not booted |
|
||||
| Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted |
|
||||
| Resize a disk attached to the VM | `/vm.resize-disk` | `/schemas/VmResizeDisk` | N/A | The VM is created |
|
||||
| Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted |
|
||||
| Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created |
|
||||
| Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted |
|
||||
|
||||
14
docs/cpu.md
14
docs/cpu.md
@@ -18,11 +18,12 @@ struct CpusConfig {
|
||||
max_phys_bits: u8,
|
||||
affinity: Option<Vec<CpuAffinity>>,
|
||||
features: CpuFeatures,
|
||||
nested: bool,
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
--cpus boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>
|
||||
--cpus boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>,nested=on|off
|
||||
```
|
||||
|
||||
### `boot`
|
||||
@@ -209,3 +210,14 @@ _Example_
|
||||
```
|
||||
|
||||
In this example the amx CPU feature will be enabled for the VMM.
|
||||
|
||||
|
||||
### `nested`
|
||||
|
||||
Enable nested virtualization (default on). Nested virtualization is needed to access hardware virtualization by this guest. This option can only be changed on x86-64.
|
||||
|
||||
_Example_
|
||||
|
||||
```
|
||||
--cpus nested=on
|
||||
```
|
||||
@@ -7,5 +7,9 @@ version = "0.1.0"
|
||||
[dependencies]
|
||||
flume = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive", "rc"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{fmt, io};
|
||||
|
||||
use log::info;
|
||||
use serde::Serialize;
|
||||
|
||||
static MONITOR: OnceLock<MonitorHandle> = OnceLock::new();
|
||||
@@ -88,12 +89,31 @@ pub fn set_monitor(file: Option<File>) -> io::Result<Monitor> {
|
||||
Ok(monitor)
|
||||
}
|
||||
|
||||
struct PropertiesFormatter<'a>(&'a Option<&'a HashMap<Cow<'a, str>, Cow<'a, str>>>);
|
||||
impl<'a> fmt::Display for PropertiesFormatter<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(map) = self.0 {
|
||||
for (i, (key, value)) in map.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{key} = {value}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_log(source: &str, event: &str, properties: Option<&HashMap<Cow<str>, Cow<str>>>) {
|
||||
// `MONITOR` is always in a valid state (None or Some), because it is set
|
||||
// only once before any threads are spawned, and it's not mutated
|
||||
// afterwards. This function only creates immutable references to `MONITOR`.
|
||||
// Because `MONITOR.tx` is `Sync`, it's safe to share `MONITOR` across
|
||||
// threads, making this function thread-safe.
|
||||
info!(
|
||||
"Event: source = {source} event = {event} {}",
|
||||
PropertiesFormatter(&properties)
|
||||
);
|
||||
if let Some(monitor_handle) = MONITOR.get().as_ref() {
|
||||
let event = Event {
|
||||
timestamp: monitor_handle.start.elapsed(),
|
||||
|
||||
360
fuzz/Cargo.lock
generated
360
fuzz/Cargo.lock
generated
@@ -10,6 +10,12 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
@@ -42,18 +48,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
@@ -105,9 +111,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-struct"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8"
|
||||
checksum = "8769c4854c5ada2852ddf6fd09d15cf43d4c2aaeccb4de6432f5402f08a6003b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -122,9 +128,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.4"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
@@ -132,6 +138,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc-any",
|
||||
"flate2",
|
||||
"libc",
|
||||
"log",
|
||||
"remain",
|
||||
@@ -144,6 +151,7 @@ dependencies = [
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
"vmm-sys-util",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -160,9 +168,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.41"
|
||||
version = "1.2.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -172,24 +180,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.49"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.49"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -254,6 +262,15 @@ dependencies = [
|
||||
"debug-helper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.21.3"
|
||||
@@ -302,7 +319,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"byteorder",
|
||||
"event_monitor",
|
||||
"hypervisor",
|
||||
@@ -320,6 +337,12 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.12"
|
||||
@@ -346,7 +369,7 @@ version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e74d68fe2927dbf47aa976d14d93db9b23dced457c7bb2bdc6925a16d31b736e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -362,10 +385,20 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"flume",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
version = "0.1.5"
|
||||
@@ -374,19 +407,29 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"spin",
|
||||
]
|
||||
|
||||
@@ -414,7 +457,7 @@ version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72742d2b395902caf8a5d520d0dd3334ba6d1138938429200e58d5174e275f3f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"managed",
|
||||
@@ -441,27 +484,27 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "hypervisor"
|
||||
@@ -507,9 +550,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.11.4"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -517,9 +560,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
@@ -533,7 +585,7 @@ version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -564,7 +616,7 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c8f7370330b4f57981e300fa39b02088f2f2a5c2d0f1f994e8090589619c56d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"kvm-bindings",
|
||||
"libc",
|
||||
"vmm-sys-util",
|
||||
@@ -572,9 +624,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "landlock"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "affe8b77dce5b172f8e290bd801b12832a77cd1942d1ea98259916e89d5829d6"
|
||||
checksum = "49fefd6652c57d68aaa32544a4c0e642929725bdc1fd929367cdeb673ab81088"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"libc",
|
||||
@@ -589,9 +641,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -623,9 +675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
@@ -649,10 +701,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.6.0"
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805cf329582f770f62cc612716a04c14815276ae266b6298375a672d3c5a5184"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mshv-bindings"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66f415da68542aca92b33f55ac3e93031dc30a2941952b99679258f7e0527353"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num_enum",
|
||||
@@ -662,15 +724,6 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net_gen"
|
||||
version = "0.1.0"
|
||||
@@ -683,7 +736,7 @@ name = "net_util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
"log",
|
||||
"net_gen",
|
||||
@@ -708,9 +761,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
|
||||
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
"rustversion",
|
||||
@@ -718,9 +771,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
||||
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -736,9 +789,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "open-enum"
|
||||
@@ -794,6 +847,12 @@ dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -814,18 +873,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -862,7 +921,7 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -959,9 +1018,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.15.0"
|
||||
version = "3.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5"
|
||||
checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_with_macros",
|
||||
@@ -969,9 +1028,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.15.0"
|
||||
version = "3.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27"
|
||||
checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -1001,13 +1060,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
@@ -1031,9 +1096,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1091,9 +1156,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.23.7"
|
||||
version = "0.23.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
|
||||
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -1134,9 +1199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
@@ -1146,11 +1211,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"rand",
|
||||
"wasm-bindgen",
|
||||
@@ -1188,7 +1253,7 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8db5bc783aad75202ad4cbcdc5e893cff1dd8fa24a1bcdb4de8998d3c4d169a"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
@@ -1206,7 +1271,7 @@ version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a4dcad85a129d97d5d4b2f3c47a4affdeedd76bdcd02094bcb5d9b76cac2d05"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"uuid",
|
||||
"vm-memory",
|
||||
@@ -1305,6 +1370,7 @@ name = "vm-migration"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
@@ -1326,7 +1392,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"block",
|
||||
"cfg-if",
|
||||
"clap",
|
||||
@@ -1387,12 +1453,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1483,112 +1549,72 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.27"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.16+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
@@ -21,11 +21,11 @@ block = { path = "../block" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.3"
|
||||
hypervisor = { path = "../hypervisor", features = ["mshv_emulator"] }
|
||||
libc = "0.2.177"
|
||||
libc = "0.2.178"
|
||||
libfuzzer-sys = "0.4.10"
|
||||
linux-loader = { version = "0.13.1", features = ["bzimage", "elf", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
mshv-bindings = "0.6.0"
|
||||
mshv-bindings = "0.6.5"
|
||||
net_util = { path = "../net_util" }
|
||||
seccompiler = "0.5.0"
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user