Compare commits

..

15 Commits

Author SHA1 Message Date
Bo Chen
9c15de0118 build: Release v35.1 (bug fix release)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2024-01-10 11:16:58 -08:00
Bo Chen
ca9e20c27a tests: Temporarily disable vhost_user_blk tests on aarch64
See: #5934

Signed-off-by: Bo Chen <chen.bo@intel.com>
(cherry picked from commit 62db13ba0e)
2024-01-10 09:09:27 -08:00
Michael Zhao
028c3054ea scripts: Workaround random wget failures on AArch64
Random failures of downloading `cloud-hypervisor-static-aarch64` with
`wget` were seen. The commit applies a workaround to retry the download
for a few times.

Signed-off-by: Michael Zhao <michael.zhao@arm.com>
(cherry picked from commit f0c5592ba1)
2024-01-10 09:09:27 -08:00
Ravi kumar Veeramally
82f1dcea3c tests: Migrate docker container from ubuntu 20.04 to 22.04
The following tests have been temporarily disabled:

1. Live upgrade/migration test with ovs-dpdk (#5532);
2. Disk hotplug tests on windows guests (#6037);

This patch has been tested with PR #6048.

Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
Tested-by: Bo Chen <chen.bo@intel.com>
(cherry picked from commit 24f384d239)
2024-01-10 09:09:27 -08:00
Wei Liu
bd6c898b1d scripts: propagate RUSTFLAGS and TARGET_CC to test scripts
There is no need to set them in the test scripts while the main script
already has them.

The consolidates how things are done.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
(cherry picked from commit 8ba5682e3b)
2024-01-10 09:09:27 -08:00
Wei Liu
98c1e21d92 scripts: pass BUILD_TARGET directly to test scripts
There is no need to reconstruct it from within the scripts since the
main script already constructed it once.

Drop the previously useless setting of BUILD_TARGET from various
scripts. The value was always overwritten at a later point.

No functional change intended.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
(cherry picked from commit c7e51e51e5)
2024-01-10 09:09:27 -08:00
Wei Liu
7992997299 scripts: dev_cli.sh: respect preset RUSTFLAGS
Signed-off-by: Wei Liu <liuwe@microsoft.com>
(cherry picked from commit 9d42f48f49)
2024-01-10 09:09:27 -08:00
dom.song
a1627d295a tests: Update spdk code to support Ampere's new CPU
When performing integration testing on the Ampere One server, an error occurred when compiling spdk and not recognizing the CPU ID.
The latest spdk already contains fixes.

Signed-off-by: dom.song <dom.song@amperecomputing.com>
(cherry picked from commit 7f47a030e7)
2024-01-10 09:09:27 -08:00
Bo Chen
9278d565e2 mis: Fix clippy issues
Signed-off-by: Bo Chen <chen.bo@intel.com>
2024-01-10 09:09:27 -08:00
Yi Wang
f3c6f5c512 build: fix clippy complex closures issue
CI reports clippy errors:

error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
   --> test_infra/src/lib.rs:93:51
    |
93  |           match (|| -> Result<(), WaitForBootError> {
    |  ___________________________________________________^
94  | |             let listener =
95  | |                 TcpListener::bind(listen_addr.as_str()).map_err(WaitForBootError::Listen)?;
96  | |             listener
...   |
145 | |             }
146 | |         })() {
    | |_________^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_conditions
    = note: `-D clippy::blocks-in-conditions` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]`

Signed-off-by: Yi Wang <foxywang@tencent.com>
(cherry picked from commit 9c2d650cb8)
2024-01-10 09:09:27 -08:00
Bo Chen
7c3c2ee0d6 vmm: tdx: Error out early for TD migration
Signed-off-by: Bo Chen <chen.bo@intel.com>
(cherry picked from commit e64b66054e)
2024-01-10 09:09:27 -08:00
Rui Chang
19e45650a6 vmm: add add-user-device support in cloud-hypervisor.yaml
The change is missed when add "add-user-device" support in
53b2e19934, use this commit to fix it.

Signed-off-by: Rui Chang <rui.chang@arm.com>
(cherry picked from commit 2b457584e0)
2024-01-10 09:09:27 -08:00
Thomas Barrett
c3a4289cbb devices: fix pv_panic alignment
Signed-off-by: Thomas Barrett <tbarrett@crusoeenergy.com>
(cherry picked from commit 5f3ff3c44a)
2024-01-10 09:09:27 -08:00
Jianyong Wu
0886e79630 aarch64: fdt: Use more appropriate default value for topology
Now, default values for vcpu topology are 0s, that is not correct and may
lead to bug. Fix it by setting default value to 1s. Also add check in
case one or more of these values are zero.

Fixes: #5892
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
(cherry picked from commit 2434e76ee0)
2024-01-10 09:09:27 -08:00
Bo Chen
a40a0268bb vmm: tdx: Fix a deadlock while accessing vm_config
The lock to `vm_config` is held for accessing `cpus.kvm_hyperv` passing
as a reference to `arch::generate_common_cpuid()`, so acquiring the same
lock again while calling to the same function is a deadlock.

Fixes: 3793ffe888

Reported-by: Yi Wang <foxywang@tencent.com>
Signed-off-by: Bo Chen <chen.bo@intel.com>
(cherry picked from commit aa6e83126c)
2024-01-10 09:09:27 -08:00
231 changed files with 7060 additions and 14395 deletions

View File

@@ -4,13 +4,12 @@ on:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
jobs:
security_audit:
name: Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/audit@v1
- uses: actions/checkout@v1
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,11 +1,9 @@
name: Cloud Hypervisor Build
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: [pull_request, create]
jobs:
build:
if: github.event_name == 'pull_request'
name: Build
runs-on: ubuntu-latest
strategy:
@@ -15,13 +13,13 @@ jobs:
- stable
- beta
- nightly
- "1.74.1"
- "1.64"
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
steps:
- name: Code checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -29,10 +27,11 @@ jobs:
run: sudo apt install -y musl-tools
- name: Install Rust toolchain (${{ matrix.rust }})
uses: dtolnay/rust-toolchain@stable
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Build (default features)
run: cargo rustc --locked --bin cloud-hypervisor -- -D warnings -D clippy::undocumented_unsafe_blocks
@@ -55,9 +54,6 @@ jobs:
- name: Build (sev_snp)
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (igvm)
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (mshv + kvm)
run: cargo rustc --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" -- -D warnings -D clippy::undocumented_unsafe_blocks

View File

@@ -1,18 +1,16 @@
name: DCO
on: [pull_request, merge_group]
on:
pull_request:
jobs:
check:
name: DCO Check ("Signed-Off-By")
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up Python 3.x
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Check DCO
if: ${{ github.event_name == 'pull_request' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |

View File

@@ -1,13 +1,11 @@
name: Cloud Hypervisor's Docker image update
on:
push:
branches: main
paths: resources/Dockerfile
pull_request:
paths: resources/Dockerfile
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
@@ -18,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
@@ -41,7 +39,7 @@ jobs:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes
tags: |
type=raw,value=20240407-0
type=raw,value={{date 'YYYYMMDD'}}-0
type=sha
- name: Build and push

View File

@@ -1,11 +1,9 @@
name: Cloud Hypervisor Cargo Fuzz Build
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: [pull_request, create]
jobs:
build:
if: github.event_name == 'pull_request'
name: Cargo Fuzz Build
runs-on: ubuntu-latest
strategy:
@@ -14,19 +12,18 @@ jobs:
- nightly
target:
- x86_64-unknown-linux-gnu
env:
RUSTFLAGS: -D warnings
steps:
- name: Code checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Install Rust toolchain (${{ matrix.rust }})
uses: dtolnay/rust-toolchain@stable
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Install Cargo fuzz
run: cargo install cargo-fuzz
- name: Fuzz Build
# Temporary fix for cargo-fuzz on latest nightly: https://github.com/rust-fuzz/cargo-fuzz/issues/276
#run: cargo install cargo-fuzz
run: cargo install --git https://github.com/rust-fuzz/cargo-fuzz --rev b4df3e58f767b5cad8d1aa6753961003f56f3609
- name: Cargo Fuzz Build
run: cargo fuzz build
- name: Fuzz Check
run: cargo fuzz check

View File

@@ -1,25 +0,0 @@
name: Commit messages check
on:
pull_request:
jobs:
gitlint:
name: Check commit messages
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade gitlint
- name: Lint git commit messages
run: |
gitlint --commits origin/$GITHUB_BASE_REF..

View File

@@ -1,4 +1,5 @@
name: Lint Dockerfile
on:
push:
paths:
@@ -13,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Lint Dockerfile
uses: hadolint/hadolint-action@master

View File

@@ -1,54 +0,0 @@
name: Cloud Hypervisor Tests (ARM64)
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
timeout-minutes: 60
name: Tests (ARM64)
runs-on: focal-arm64
steps:
- name: Fix workspace permissions
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
- name: Code checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run unit tests (musl)
run: scripts/dev_cli.sh tests --unit --libc musl
- name: Load openvswitch module
run: sudo modprobe openvswitch
- name: Run integration tests (musl)
timeout-minutes: 30
run: scripts/dev_cli.sh tests --integration --libc musl
- name: Install Azure CLI
if: ${{ github.event_name != 'pull_request' }}
run: |
sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg
curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null
echo "deb [arch=arm64] https://packages.microsoft.com/repos/azure-cli/ focal main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt update
sudo apt install -y azure-cli
- name: Download Windows image
if: ${{ github.event_name != 'pull_request' }}
shell: bash
run: |
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
IMG_PATH=$HOME/workloads/$IMG_BASENAME
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
pushd "$HOME/workloads"
if sha1sum "$IMG_BASENAME.sha1" --check; then
exit
fi
popd
mkdir -p "$HOME/workloads"
az storage blob download --container-name private-images --file "$IMG_GZ_PATH" --name "$IMG_GZ_BLOB_NAME" --connection-string "${{ secrets.CH_PRIVATE_IMAGES }}"
gzip -d $IMG_GZ_PATH
- name: Run Windows guest integration tests
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 30
run: scripts/dev_cli.sh tests --integration-windows --libc musl

View File

@@ -1,22 +0,0 @@
name: Cloud Hypervisor Tests (Metrics)
on:
push:
branches:
- main
jobs:
build:
name: Tests (Metrics)
runs-on: jammy-metrics
env:
METRICS_PUBLISH_KEY: ${{ secrets.METRICS_PUBLISH_KEY }}
steps:
- name: Code checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run metrics tests
timeout-minutes: 60
run: scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json
- name: Upload metrics report
run: 'curl -X PUT https://ch-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'

View File

@@ -1,28 +0,0 @@
name: Cloud Hypervisor Tests (Rate-Limiter)
on: [merge_group, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Tests (Rate-Limiter)
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'jammy-rate-limiter' }}
env:
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
steps:
- name: Fix workspace permissions
if: ${{ github.event_name != 'pull_request' }}
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
- name: Code checkout
if: ${{ github.event_name != 'pull_request' }}
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run rate-limiter integration tests
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 10
run: scripts/dev_cli.sh tests --integration-rate-limiter
- name: Skipping build for PR
if: ${{ github.event_name == 'pull_request' }}
run: echo "Skipping build for PR"

View File

@@ -1,32 +0,0 @@
name: Cloud Hypervisor Tests (SGX)
on: [merge_group, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Tests (SGX)
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'jammy-sgx' }}
env:
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
steps:
- name: Fix workspace permissions
if: ${{ github.event_name != 'pull_request' }}
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
- name: Code checkout
if: ${{ github.event_name != 'pull_request' }}
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run SGX integration tests
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 10
run: scripts/dev_cli.sh tests --integration-sgx
- name: Run SGX integration tests for musl
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 10
run: scripts/dev_cli.sh tests --integration-sgx --libc musl
- name: Skipping build for PR
if: ${{ github.event_name == 'pull_request' }}
run: echo "Skipping build for PR"

View File

@@ -1,32 +0,0 @@
name: Cloud Hypervisor Tests (VFIO)
on: [merge_group, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Tests (VFIO)
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'jammy-vfio' }}
env:
AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }}
steps:
- name: Fix workspace permissions
if: ${{ github.event_name != 'pull_request' }}
run: sudo chown -R github-runner:github-runner ${GITHUB_WORKSPACE}
- name: Code checkout
if: ${{ github.event_name != 'pull_request' }}
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run VFIO integration tests
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 15
run: scripts/dev_cli.sh tests --integration-vfio
- name: Run VFIO integration tests for musl
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 15
run: scripts/dev_cli.sh tests --integration-vfio --libc musl
- name: Skipping build for PR
if: ${{ github.event_name == 'pull_request' }}
run: echo "Skipping build for PR"

View File

@@ -1,50 +0,0 @@
name: Cloud Hypervisor Tests (Windows Guest)
on: [merge_group, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Tests (Windows Guest)
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'garm-jammy-16' }}
steps:
- name: Code checkout
if: ${{ github.event_name != 'pull_request' }}
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Docker
if: ${{ github.event_name != 'pull_request' }}
run: |
sudo apt-get update
sudo apt-get -y install ca-certificates curl gnupg
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt install -y docker-ce docker-ce-cli
- name: Install Azure CLI
if: ${{ github.event_name != 'pull_request' }}
run: |
sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg
curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ jammy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt update
sudo apt install -y azure-cli
- name: Download Windows image
if: ${{ github.event_name != 'pull_request' }}
run: |
mkdir $HOME/workloads
az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2022-amd64-2.raw" --name windows-server-2022-amd64-2.raw --connection-string "${{ secrets.CH_PRIVATE_IMAGES }}"
- name: Run Windows guest integration tests
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 15
run: scripts/dev_cli.sh tests --integration-windows
- name: Run Windows guest integration tests for musl
if: ${{ github.event_name != 'pull_request' }}
timeout-minutes: 15
run: scripts/dev_cli.sh tests --integration-windows --libc musl
- name: Skipping build for PR
if: ${{ github.event_name == 'pull_request' }}
run: echo "Skipping build for PR"

View File

@@ -1,52 +0,0 @@
name: Cloud Hypervisor Tests (x86-64)
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
runner: ['garm-jammy', "garm-jammy-amd"]
libc: ["musl", 'gnu']
name: Tests (x86-64)
runs-on: ${{ github.event_name == 'pull_request' && !(matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') && 'ubuntu-latest' || format('{0}-16', matrix.runner) }}
steps:
- name: Code checkout
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Docker
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
run: |
sudo apt-get update
sudo apt-get -y install ca-certificates curl gnupg
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt install -y docker-ce docker-ce-cli
- name: Prepare for VDPA
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
run: scripts/prepare_vdpa.sh
- name: Run unit tests
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
run: scripts/dev_cli.sh tests --unit --libc ${{ matrix.libc }}
- name: Load openvswitch module
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
run: sudo modprobe openvswitch
- name: Run integration tests
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
timeout-minutes: 40
run: scripts/dev_cli.sh tests --integration --libc ${{ matrix.libc }}
- name: Run live-migration integration tests
if: ${{ github.event_name != 'pull_request' || (matrix.runner == 'garm-jammy' && matrix.libc == 'gnu') }}
timeout-minutes: 20
run: scripts/dev_cli.sh tests --integration-live-migration --libc ${{ matrix.libc }}
- name: Skipping build for PR
if: ${{ github.event_name == 'pull_request' && matrix.runner != 'garm-jammy' && matrix.libc != 'gnu' }}
run: echo "Skipping build for PR"

View File

@@ -1,14 +0,0 @@
name: Cloud Hypervisor OpenAPI Validation
on: [pull_request, merge_group]
jobs:
Validate:
runs-on: ubuntu-latest
container: openapitools/openapi-generator-cli
steps:
- uses: actions/checkout@v4
- name: Validate OpenAPI
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
/usr/local/bin/docker-entrypoint.sh validate -i vmm/src/api/openapi/cloud-hypervisor.yaml

View File

@@ -1,11 +1,9 @@
name: Cloud Hypervisor Quality Checks
on: [pull_request, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: [pull_request, create]
jobs:
build:
if: github.event_name == 'pull_request'
name: Quality (clippy, rustfmt)
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
@@ -36,7 +34,7 @@ jobs:
experimental: true
steps:
- name: Code checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
@@ -48,8 +46,8 @@ jobs:
override: true
components: rustfmt, clippy
- name: Bisectability Check (default features)
if: ${{ github.event_name == 'pull_request' && matrix.target == 'x86_64-unknown-linux-gnu' }}
- name: Debug Check (default features)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
run: |
set -e
commits=$(git rev-list origin/${{ github.base_ref }}..${{ github.sha }})
@@ -111,14 +109,6 @@ jobs:
command: clippy
args: --target=${{ matrix.target }} --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Clippy (igvm)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
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 --no-default-features --tests --examples --features "igvm" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Clippy (kvm + tdx)
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
uses: actions-rs/cargo@v1
@@ -135,6 +125,6 @@ jobs:
name: Typos / Spellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
# Executes "typos ."
- uses: crate-ci/typos@v1.16.11

View File

@@ -1,69 +1,134 @@
name: Cloud Hypervisor Release
on: [create, merge_group]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
GITHUB_TOKEN: ${{ github.token }}
on: [pull_request, create]
jobs:
release:
if: (github.event_name == 'create' && github.event.ref_type == 'tag') || github.event_name == 'merge_group'
name: Release ${{ matrix.platform.target }}
strategy:
fail-fast: false
matrix:
platform:
- target: x86_64-unknown-linux-gnu
args: --all --release --features mshv
name_ch: cloud-hypervisor
name_ch_remote: ch-remote
- target: x86_64-unknown-linux-musl
args: --all --release --features mshv
name_ch: cloud-hypervisor-static
name_ch_remote: ch-remote-static
- target: aarch64-unknown-linux-musl
args: --all --release
name_ch: cloud-hypervisor-static-aarch64
name_ch_remote: ch-remote-static-aarch64
if: (github.event_name == 'create' && github.event.ref_type == 'tag') || github.event_name == 'pull_request'
name: Release
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Install musl-gcc
if: contains(matrix.platform.target, 'musl')
run: sudo apt install -y musl-tools
- name: Create release directory
if: |
github.event_name == 'create' && github.event.ref_type == 'tag' &&
matrix.platform.target == 'x86_64-unknown-linux-gnu'
run: rsync -rv --exclude=.git . ../cloud-hypervisor-${{ github.event.ref }}
- name: Build ${{ matrix.platform.target }}
uses: houseabsolute/actions-rust-cross@v0
- name: Install Rust toolchain (x86_64-unknown-linux-gnu)
uses: actions-rs/toolchain@v1
with:
toolchain: "1.67.1"
target: x86_64-unknown-linux-gnu
- name: Install Rust toolchain (x86_64-unknown-linux-musl)
uses: actions-rs/toolchain@v1
with:
toolchain: "1.67.1"
target: x86_64-unknown-linux-musl
- name: Build
uses: actions-rs/cargo@v1
with:
toolchain: "1.67.1"
command: build
target: ${{ matrix.platform.target }}
args: ${{ matrix.platform.args }}
strip: true
toolchain: 1.74.1
- name: Copy Release Binaries
if: github.event_name == 'create' && github.event.ref_type == 'tag'
shell: bash
run: |
cp target/${{ matrix.platform.target }}/release/cloud-hypervisor ./${{ matrix.platform.name_ch }}
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@v3
args: --all --release --features mshv --target=x86_64-unknown-linux-gnu
- name: Static Build
uses: actions-rs/cargo@v1
with:
name: Artifacts for ${{ matrix.platform.target }}
path: |
./${{ matrix.platform.name_ch }}
./${{ matrix.platform.name_ch_remote }}
toolchain: "1.67.1"
command: build
args: --all --release --features mshv --target=x86_64-unknown-linux-musl
- name: Install Rust toolchain (aarch64-unknown-linux-musl)
uses: actions-rs/toolchain@v1
with:
toolchain: "1.67.1"
target: aarch64-unknown-linux-musl
override: true
- name: Create Release
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: true
prerelease: true
- name: Upload cloud-hypervisor
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: upload-release-cloud-hypervisor
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: target/x86_64-unknown-linux-gnu/release/cloud-hypervisor
asset_name: cloud-hypervisor
asset_content_type: application/octet-stream
- name: Upload static cloud-hypervisor
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: upload-release-static-cloud-hypervisor
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: target/x86_64-unknown-linux-musl/release/cloud-hypervisor
asset_name: cloud-hypervisor-static
asset_content_type: application/octet-stream
- name: Upload ch-remote
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: upload-release-ch-remote
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: target/x86_64-unknown-linux-gnu/release/ch-remote
asset_name: ch-remote
asset_content_type: application/octet-stream
- name: Upload static-ch-remote
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: upload-release-static-ch-remote
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: target/x86_64-unknown-linux-musl/release/ch-remote
asset_name: ch-remote-static
asset_content_type: application/octet-stream
- name: Clean build tree ahead of cross build
uses: actions-rs/cargo@v1
with:
command: clean
- name: Static Build (AArch64)
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --all --release --target=aarch64-unknown-linux-musl
- name: Upload static AArch64 cloud-hypervisor
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: upload-release-static-aarch64-cloud-hypervisor
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: target/aarch64-unknown-linux-musl/release/cloud-hypervisor
asset_name: cloud-hypervisor-static-aarch64
asset_content_type: application/octet-stream
- name: Upload static AArch64 ch-remote
if: github.event_name == 'create' && github.event.ref_type == 'tag'
id: upload-release-static-aarch64-ch-remote
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: target/aarch64-unknown-linux-musl/release/ch-remote
asset_name: ch-remote-static-aarch64
asset_content_type: application/octet-stream
- name: Vendor
if: |
github.event_name == 'create' && github.event.ref_type == 'tag' &&
matrix.platform.target == 'x86_64-unknown-linux-gnu'
working-directory: ../cloud-hypervisor-${{ github.event.ref }}
run: |
mkdir ../vendor-cargo-home
@@ -71,24 +136,16 @@ jobs:
mkdir .cargo
cargo vendor > .cargo/config.toml
- name: Create vendored source archive
if: |
github.event_name == 'create' && github.event.ref_type == 'tag' &&
matrix.platform.target == 'x86_64-unknown-linux-gnu'
run: tar cJf cloud-hypervisor-${{ github.event.ref }}.tar.xz ../cloud-hypervisor-${{ github.event.ref }}
working-directory: ../
run: tar cJf cloud-hypervisor-${{ github.event.ref }}.tar.xz cloud-hypervisor-${{ github.event.ref }}
- name: Upload cloud-hypervisor vendored source archive
if: |
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@v3
with:
path: cloud-hypervisor-${{ github.event.ref }}.tar.xz
name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
- name: Create GitHub Release
if: github.event_name == 'create' && github.event.ref_type == 'tag'
uses: softprops/action-gh-release@v1
id: upload-release-cloud-hypervisor-vendored-sources
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: |
./${{ matrix.platform.name_ch }}
./${{ matrix.platform.name_ch_remote }}
./cloud-hypervisor-${{ github.event.ref }}.tar.xz
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ../cloud-hypervisor-${{ github.event.ref }}.tar.xz
asset_name: cloud-hypervisor-${{ github.event.ref }}.tar.xz
asset_content_type: application/x-xz

View File

@@ -1,12 +0,0 @@
name: REUSE Compliance Check
on: [push, pull_request]
jobs:
reuse:
name: REUSE Compliance Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: REUSE Compliance Check
uses: fsfe/reuse-action@v3

View File

@@ -1,20 +0,0 @@
name: Shell scripts check
on:
pull_request:
merge_group:
push:
branches:
- main
jobs:
sh-checker:
name: Check shell scripts
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run the shell script checkers
uses: luizm/action-sh-checker@master
env:
SHFMT_OPTS: -i 4 -d
SHELLCHECK_OPTS: -x --source-path scripts

1
.gitignore vendored
View File

@@ -6,4 +6,3 @@
**/rusty-tags.vi
/rpm/SOURCES
/.vscode
/vendor

View File

@@ -1,15 +0,0 @@
[general]
extra-path=scripts/gitlint/rules.py
regex-style-search=true
[ignore-by-author-name]
regex=dependabot
ignore=all
# default 72
[title-max-length]
line-length=72
# default 80
[body-max-line-length]
line-length=72

View File

@@ -1,12 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cloud-hypervisor
Upstream-Contact: <>
Source: https://www.cloudhypervisor.org
Files: docs/*.md *.md
Copyright: 2024
License: CC-BY-4.0
Files: scripts/* test_data/* *.toml .git* fuzz/Cargo.lock fuzz/.gitignore resources/linux-config-* vmm/src/api/openapi/cloud-hypervisor.yaml CODEOWNERS Cargo.lock
Copyright: 2024
License: Apache-2.0

1388
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "cloud-hypervisor"
version = "39.0.0"
version = "35.1.0"
authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
default-run = "cloud-hypervisor"
@@ -15,7 +15,7 @@ homepage = "https://github.com/cloud-hypervisor/cloud-hypervisor"
# 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.74.1"
rust-version = "1.64"
[profile.release]
lto = true
@@ -29,47 +29,50 @@ strip = false
debug = true
[dependencies]
anyhow = "1.0.81"
anyhow = "1.0.75"
api_client = { path = "api_client" }
clap = { version = "4.5.4", features = ["string"] }
dhat = { version = "0.3.3", optional = true }
argh = "0.1.9"
dhat = { version = "0.3.2", optional = true }
epoll = "4.3.3"
event_monitor = { path = "event_monitor" }
hypervisor = { path = "hypervisor" }
libc = "0.2.153"
log = { version = "0.4.21", features = ["std"] }
libc = "0.2.147"
log = { version = "0.4.17", features = ["std"] }
option_parser = { path = "option_parser" }
seccompiler = "0.4.0"
serde_json = "1.0.115"
seccompiler = "0.3.0"
serde_json = "1.0.107"
signal-hook = "0.3.17"
thiserror = "1.0.58"
thiserror = "1.0.40"
tpm = { path = "tpm"}
tracer = { path = "tracer" }
vmm = { path = "vmm" }
vmm-sys-util = "0.12.1"
vm-memory = "0.14.1"
zbus = { version = "3.15.2", optional = true }
vmm-sys-util = "0.11.0"
vm-memory = "0.12.2"
zbus = { version = "3.11.1", optional = true }
# List of patched crates
[patch.crates-io]
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
[dev-dependencies]
dirs = "5.0.1"
dirs = "5.0.0"
net_util = { path = "net_util" }
once_cell = "1.19.0"
serde_json = "1.0.115"
once_cell = "1.18.0"
serde_json = "1.0.107"
test_infra = { path = "test_infra" }
wait-timeout = "0.2.0"
# Please adjust `vmm::feature_list()` accordingly when changing the
# feature list below
[features]
default = ["kvm", "io_uring"]
dbus_api = ["zbus", "vmm/dbus_api"]
dhat-heap = ["dhat"] # For heap profiling
guest_debug = ["vmm/guest_debug"]
igvm = ["vmm/igvm", "mshv"]
io_uring = ["vmm/io_uring"]
kvm = ["vmm/kvm"]
mshv = ["vmm/mshv"]
sev_snp = ["igvm", "vmm/sev_snp", "mshv"]
sev_snp = ["vmm/sev_snp", "mshv"]
tdx = ["vmm/tdx"]
tracing = ["vmm/tracing", "tracer/tracing"]

509
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,509 @@
def runWorkers = true
pipeline {
agent none
options {
timeout(time: 4, unit: 'HOURS')
}
stages {
stage('Early checks') {
agent { node { label 'built-in' } }
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Check if worker build can be skipped') {
when {
expression {
return skipWorkerBuild()
}
}
steps {
script {
runWorkers = false
echo 'No changes requiring a build'
}
}
}
stage('Check for RFC/WIP builds') {
when {
changeRequest comparator: 'REGEXP', title: '.*(rfc|RFC|wip|WIP).*'
beforeAgent true
}
steps {
error('Failing as this is marked as a WIP or RFC PR.')
}
}
stage('Cancel older builds') {
when { not { branch 'main' } }
steps {
cancelPreviousBuilds()
}
}
}
}
stage('Build') {
parallel {
stage('Worker build') {
agent { node { label 'jammy' } }
when {
beforeAgent true
expression {
return runWorkers
}
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Prepare environment') {
steps {
sh 'scripts/prepare_vdpa.sh'
}
}
stage('Run OpenAPI tests') {
steps {
sh 'scripts/run_openapi_tests.sh'
}
}
stage('Run unit tests') {
steps {
sh 'scripts/dev_cli.sh tests --unit'
}
}
stage('Run integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration'
}
}
stage('Run live-migration integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration-live-migration'
}
}
stage('Run unit tests for musl') {
steps {
sh 'scripts/dev_cli.sh tests --unit --libc musl'
}
}
stage('Run integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration --libc musl'
}
}
stage('Run live-migration integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl'
}
}
}
}
stage('Worker build - AMD') {
agent { node { label 'jammy-amd' } }
when {
beforeAgent true
expression {
return runWorkers
}
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Prepare environment') {
steps {
sh 'scripts/prepare_vdpa.sh'
}
}
stage('Run integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration -- -- --skip common_parallel::test_vfio'
}
}
stage('Run live-migration integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration-live-migration'
}
}
stage('Run integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration --libc musl -- -- --skip common_parallel::test_vfio'
}
}
stage('Run live-migration integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration-live-migration --libc musl'
}
}
}
}
stage('AArch64 worker build') {
agent { node { label 'bionic-arm64' } }
when {
beforeAgent true
expression {
return runWorkers
}
}
environment {
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Run unit tests') {
steps {
sh 'scripts/dev_cli.sh tests --unit --libc musl'
}
}
stage('Run integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'sudo modprobe openvswitch'
sh 'scripts/dev_cli.sh tests --integration --libc musl'
}
}
stage('Install azure-cli') {
steps {
installAzureCli('focal', 'arm64')
}
}
stage('Download Windows image') {
steps {
sh '''#!/bin/bash -x
IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw
IMG_PATH=$HOME/workloads/$IMG_BASENAME
IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz
IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz
cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/"
pushd "$HOME/workloads"
if sha1sum "$IMG_BASENAME.sha1" --check; then
exit
fi
popd
mkdir -p "$HOME/workloads"
az storage blob download \
--container-name private-images \
--file "$IMG_GZ_PATH" \
--name "$IMG_GZ_BLOB_NAME" \
--connection-string "$AZURE_CONNECTION_STRING"
gzip -d $IMG_GZ_PATH
'''
}
}
stage('Run Windows guest integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
}
}
}
post {
always {
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
deleteDir()
}
}
}
stage('Worker build - Windows guest') {
agent { node { label 'jammy' } }
when {
beforeAgent true
expression {
return runWorkers
}
}
environment {
AZURE_CONNECTION_STRING = credentials('46b4e7d6-315f-4cc1-8333-b58780863b9b')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install azure-cli') {
steps {
installAzureCli('jammy', 'amd64')
}
}
stage('Download assets') {
steps {
sh "mkdir ${env.HOME}/workloads"
sh 'az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2022-amd64-2.raw" --name windows-server-2022-amd64-2.raw --connection-string "$AZURE_CONNECTION_STRING"'
}
}
stage('Run Windows guest integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-windows'
}
}
stage('Run Windows guest integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-windows --libc musl'
}
}
}
}
stage('Worker build - Metrics') {
agent { node { label 'jammy-metrics' } }
when {
branch 'main'
beforeAgent true
expression {
return runWorkers
}
}
environment {
METRICS_PUBLISH_KEY = credentials('52e0945f-ce7a-43d1-87af-67d1d87cc40f')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Run metrics tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --metrics -- -- --report-file /root/workloads/metrics.json'
}
}
stage('Upload metrics report') {
steps {
sh 'curl -X PUT https://cloud-hypervisor-metrics.azurewebsites.net/api/publishmetrics -H "x-functions-key: $METRICS_PUBLISH_KEY" -T ~/workloads/metrics.json'
}
}
}
}
stage('Worker build - Rate Limiter') {
agent { node { label 'focal-metrics' } }
when {
branch 'main'
beforeAgent true
expression {
return runWorkers
}
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Run rate-limiter integration tests') {
options {
timeout(time: 10, unit: 'MINUTES')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-rate-limiter'
}
}
}
}
stage('Worker build - SGX') {
agent { node { label 'jammy-sgx' } }
when {
beforeAgent true
allOf {
branch 'main'
expression {
return runWorkers
}
}
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Run SGX integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-sgx'
}
}
stage('Run SGX integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-sgx --libc musl'
}
}
}
post {
always {
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
deleteDir()
}
}
}
stage('Worker build - VFIO') {
agent { node { label 'jammy-vfio' } }
when {
beforeAgent true
allOf {
branch 'main'
expression {
return runWorkers
}
}
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Run VFIO integration tests') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-vfio'
}
}
stage('Run VFIO integration tests for musl') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
sh 'scripts/dev_cli.sh tests --integration-vfio --libc musl'
}
}
}
post {
always {
sh "sudo chown -R jenkins.jenkins ${WORKSPACE}"
deleteDir()
}
}
}
}
}
}
post {
regression {
script {
if (env.BRANCH_NAME == 'main') {
slackSend(color: '#ff0000', message: '"main" branch build is now failing', channel: '#jenkins-ci')
}
}
}
fixed {
script {
if (env.BRANCH_NAME == 'main') {
slackSend(color: '#00ff00', message: '"main" branch build is now fixed', channel: '#jenkins-ci')
}
}
}
}
}
def cancelPreviousBuilds() {
// Check for other instances of this particular build, cancel any that are older than the current one
def jobName = env.JOB_NAME
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
def currentJob = Jenkins.instance.getItemByFullName(jobName)
// Loop through all instances of this particular job/branch
for (def build : currentJob.builds) {
if (build.isBuilding() && (build.number.toInteger() < currentBuildNumber)) {
echo "Older build still queued. Sending kill signal to build number: ${build.number}"
build.doStop()
}
}
}
def installAzureCli(distro, arch) {
sh 'sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg'
sh 'curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null'
sh "echo \"deb [arch=${arch}] https://packages.microsoft.com/repos/azure-cli/ ${distro} main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
sh 'sudo apt update'
sh 'sudo apt install -y azure-cli'
}
def boolean skipWorkerBuild() {
if (env.CHANGE_TARGET == null) {
return false
}
if (sh(
returnStatus: true,
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v '\\.md'"
) != 0) {
return true
}
if (sh(
returnStatus: true,
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E 'fuzz/'"
) != 0) {
return true
}
if (sh(
returnStatus: true,
script: "git diff --name-only origin/${env.CHANGE_TARGET}... | grep -v -E '.github/'"
) != 0) {
return true
}
return false
}

View File

@@ -108,7 +108,7 @@ do not wish to use the pre-built binaries.
## Booting Linux
Cloud Hypervisor supports direct kernel boot (the x86-64 kernel requires the kernel
built with PVH support or a bzImage) or booting via a firmware (either [Rust Hypervisor
built with PVH support) or booting via a firmware (either [Rust Hypervisor
Firmware](https://github.com/cloud-hypervisor/rust-hypervisor-firmware) or an
edk2 UEFI firmware called `CLOUDHV` / `CLOUDHV_EFI`.)
@@ -150,7 +150,7 @@ $ sudo setcap cap_net_admin+ep ./cloud-hypervisor
$ ./create-cloud-init.sh
$ ./cloud-hypervisor \
--kernel ./hypervisor-fw \
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
--disk path=focal-server-cloudimg-amd64.raw --disk path=/tmp/ubuntu-cloudinit.img \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask="
@@ -163,7 +163,7 @@ GRUB) is required then it necessary to switch to the serial console instead of
```shell
$ ./cloud-hypervisor \
--kernel ./hypervisor-fw \
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
--disk path=focal-server-cloudimg-amd64.raw --disk path=/tmp/ubuntu-cloudinit.img \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
@@ -175,7 +175,7 @@ $ ./cloud-hypervisor \
#### Building your Kernel
Cloud Hypervisor also supports direct kernel boot. For x86-64, a `vmlinux` ELF kernel (compiled with PVH support) or a regular bzImage are supported. In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
Cloud Hypervisor also supports direct kernel boot. For x86-64, a `vmlinux` ELF kernel (compiled with PVH support) is needed. In order to support development there is a custom branch; however provided the required options are enabled any recent kernel will suffice.
To build the kernel:
@@ -225,7 +225,7 @@ $ sudo setcap cap_net_admin+ep ./cloud-hypervisor
$ ./create-cloud-init.sh
$ ./cloud-hypervisor \
--kernel ./linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--disk path=focal-server-cloudimg-amd64.raw path=/tmp/ubuntu-cloudinit.img \
--disk path=focal-server-cloudimg-amd64.raw --disk path=/tmp/ubuntu-cloudinit.img \
--cmdline "console=hvc0 root=/dev/vda1 rw" \
--cpus boot=4 \
--memory size=1024M \
@@ -239,7 +239,7 @@ $ sudo setcap cap_net_admin+ep ./cloud-hypervisor
$ ./create-cloud-init.sh
$ ./cloud-hypervisor \
--kernel ./linux-cloud-hypervisor/arch/arm64/boot/Image \
--disk path=focal-server-cloudimg-arm64.raw path=/tmp/ubuntu-cloudinit.img \
--disk path=focal-server-cloudimg-arm64.raw --disk path=/tmp/ubuntu-cloudinit.img \
--cmdline "console=hvc0 root=/dev/vda1 rw" \
--cpus boot=4 \
--memory size=1024M \

View File

@@ -5,4 +5,4 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
[dependencies]
vmm-sys-util = "0.12.1"
vmm-sys-util = "0.11.0"

View File

@@ -10,19 +10,21 @@ sev_snp = []
tdx = []
[dependencies]
anyhow = "1.0.81"
byteorder = "1.5.0"
anyhow = "1.0.75"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
libc = "0.2.153"
linux-loader = { version = "0.11.0", features = ["elf", "bzimage", "pe"] }
log = "0.4.21"
serde = { version = "1.0.197", features = ["rc", "derive"] }
thiserror = "1.0.58"
uuid = "1.8.0"
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-bitmap"] }
libc = "0.2.147"
linux-loader = { version = "0.9.1", features = ["elf", "bzimage", "pe"] }
log = "0.4.17"
serde = { version = "1.0.168", features = ["rc", "derive"] }
thiserror = "1.0.40"
uuid = "1.3.4"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vm-memory = { version = "0.12.2", features = ["backend-mmap", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" }
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
[target.'cfg(target_arch = "aarch64")'.dependencies]
fdt_parser = { version = "0.1.5", package = "fdt" }
fdt_parser = { version = "0.1.4", package = "fdt" }
vm-fdt = { git = "https://github.com/rust-vmm/vm-fdt", branch = "main" }

View File

@@ -111,9 +111,8 @@ pub const RAM_64BIT_START: GuestAddress = GuestAddress(0x1_0000_0000);
pub const CMDLINE_MAX_SIZE: usize = 2048;
/// FDT is at the beginning of RAM.
/// Maximum size of the device tree blob as specified in https://www.kernel.org/doc/Documentation/arm64/booting.txt.
pub const FDT_START: GuestAddress = RAM_START;
/// Maximum size of the device tree blob as specified in [the kernel
/// documentation](https://www.kernel.org/doc/Documentation/arm64/booting.txt).
pub const FDT_MAX_SIZE: u64 = 0x20_0000;
/// Put ACPI table above dtb

View File

@@ -16,6 +16,7 @@ use crate::{DeviceType, GuestMemoryMmap, NumaNodes, PciSpaceInfo, RegionType};
use hypervisor::arch::aarch64::gic::Vgic;
use log::{log_enabled, Level};
use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};

View File

@@ -1,11 +1,8 @@
// Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use std::io::{Read, Seek, SeekFrom};
use std::os::fd::AsFd;
use std::result;
use vm_memory::{GuestAddress, GuestMemory};
use vm_memory::{Bytes, GuestAddress, GuestMemory};
/// Errors thrown while loading UEFI binary
#[derive(Debug)]
@@ -27,7 +24,7 @@ pub fn load_uefi<F, M: GuestMemory>(
uefi_image: &mut F,
) -> Result<()>
where
F: Read + Seek + AsFd,
F: Read + Seek,
{
let uefi_size = uefi_image
.seek(SeekFrom::End(0))
@@ -39,6 +36,6 @@ where
}
uefi_image.rewind().map_err(|_| Error::SeekUefiStart)?;
guest_mem
.read_exact_volatile_from(guest_addr, &mut uefi_image.as_fd(), uefi_size)
.read_exact_from(guest_addr, uefi_image, uefi_size)
.map_err(|_| Error::ReadUefiImage)
}

View File

@@ -18,6 +18,9 @@ use std::fmt;
use std::result;
use std::sync::Arc;
use thiserror::Error;
use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult};
use versionize_derive::Versionize;
use vm_migration::VersionMapped;
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitmap>;
type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitmap>;
@@ -45,17 +48,13 @@ pub enum Error {
ModlistSetup(#[source] vm_memory::GuestMemoryError),
#[error("RSDP extends past the end of guest memory")]
RsdpPastRamEnd,
#[error("Failed to setup Zero Page for bzImage")]
ZeroPageSetup(#[source] vm_memory::GuestMemoryError),
#[error("Zero Page for bzImage past RAM end")]
ZeroPagePastRamEnd,
}
/// Type for returning public functions outcome.
pub type Result<T> = result::Result<T, Error>;
/// Type for memory region types.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Versionize)]
pub enum RegionType {
/// RAM type
Ram,
@@ -73,6 +72,8 @@ pub enum RegionType {
Reserved,
}
impl VersionMapped for RegionType {}
/// Module for aarch64 related functionality.
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
@@ -90,9 +91,8 @@ pub mod x86_64;
#[cfg(target_arch = "x86_64")]
pub use x86_64::{
arch_memory_regions, configure_system, configure_vcpu, generate_common_cpuid,
generate_ram_ranges, get_host_cpu_phys_bits, initramfs_load_addr, layout,
layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs, CpuidConfig, CpuidFeatureEntry,
EntryPoint, _NSIG,
get_host_cpu_phys_bits, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
layout::CMDLINE_START, regs, CpuidFeatureEntry, EntryPoint, _NSIG,
};
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
@@ -108,7 +108,6 @@ pub struct NumaNode {
pub memory_regions: Vec<Arc<GuestRegionMmap>>,
pub hotplug_regions: Vec<Arc<GuestRegionMmap>>,
pub cpus: Vec<u8>,
pub pci_segments: Vec<u16>,
pub distances: BTreeMap<u32, u8>,
pub memory_zones: Vec<String>,
#[cfg(target_arch = "x86_64")]

View File

@@ -17,14 +17,14 @@ use crate::InitramfsConfig;
use crate::RegionType;
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError};
use linux_loader::loader::bootparam::{boot_params, setup_header};
use linux_loader::loader::bootparam::boot_params;
use linux_loader::loader::elf::start_info::{
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
};
use std::collections::BTreeMap;
use std::mem;
use vm_memory::{
Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
GuestMemoryRegion, GuestUsize,
};
mod smbios;
@@ -37,9 +37,6 @@ const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
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
// KVM feature bits
#[cfg(feature = "tdx")]
@@ -64,8 +61,6 @@ pub const _NSIG: i32 = 65;
pub struct EntryPoint {
/// Address in guest memory where the guest must start execution
pub entry_addr: GuestAddress,
/// This field is used for bzImage to fill the zero page
pub setup_header: Option<setup_header>,
}
const E820_RAM: u32 = 1;
@@ -118,14 +113,37 @@ impl SgxEpcRegion {
}
}
pub struct CpuidConfig {
pub sgx_epc_sections: Option<Vec<SgxEpcSection>>,
pub phys_bits: u8,
pub kvm_hyperv: bool,
#[cfg(feature = "tdx")]
pub tdx: bool,
pub amx: bool,
}
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
// trait (in this case `DataInit`) where:
// * the type that is implementing the trait is foreign or
// * all of the parameters being passed to the trait (if there are any) are also foreign
// is prohibited.
#[derive(Copy, Clone, Default)]
struct StartInfoWrapper(hvm_start_info);
#[derive(Copy, Clone, Default)]
struct MemmapTableEntryWrapper(hvm_memmap_table_entry);
#[derive(Copy, Clone, Default)]
struct ModlistEntryWrapper(hvm_modlist_entry);
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for StartInfoWrapper {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for MemmapTableEntryWrapper {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for ModlistEntryWrapper {}
// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
// trait (in this case `DataInit`) where:
// * the type that is implementing the trait is foreign or
// * all of the parameters being passed to the trait (if there are any) are also foreign
// is prohibited.
#[derive(Copy, Clone, Default)]
struct BootParamsWrapper(boot_params);
// SAFETY: BootParamsWrap is a wrapper over `boot_params` (a series of ints).
unsafe impl ByteValued for BootParamsWrapper {}
#[derive(Debug)]
pub enum Error {
@@ -183,9 +201,6 @@ pub enum Error {
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
#[cfg(feature = "tdx")]
TdxCapabilities(HypervisorError),
/// Failed to configure E820 map for bzImage
E820Configuration,
}
impl From<Error> for super::Error {
@@ -194,26 +209,6 @@ impl From<Error> for super::Error {
}
}
pub fn get_x2apic_id(cpu_id: u32, topology: Option<(u8, u8, u8)>) -> u32 {
if let Some(t) = topology {
let thread_mask_width = u8::BITS - (t.0 - 1).leading_zeros();
let core_mask_width = u8::BITS - (t.1 - 1).leading_zeros();
let die_mask_width = u8::BITS - (t.2 - 1).leading_zeros();
let thread_id = cpu_id % (t.0 as u32);
let core_id = cpu_id / (t.0 as u32) % (t.1 as u32);
let die_id = cpu_id / ((t.0 * t.1) as u32) % (t.2 as u32);
let socket_id = cpu_id / ((t.0 * t.1 * t.2) as u32);
return thread_id
| (core_id << thread_mask_width)
| (die_id << (thread_mask_width + core_mask_width))
| (socket_id << (thread_mask_width + core_mask_width + die_mask_width));
}
cpu_id
}
#[derive(Copy, Clone, Debug)]
pub enum CpuidReg {
EAX,
@@ -233,26 +228,6 @@ pub struct CpuidPatch {
}
impl CpuidPatch {
pub fn get_cpuid_reg(
cpuid: &[CpuIdEntry],
function: u32,
index: Option<u32>,
reg: CpuidReg,
) -> Option<u32> {
for entry in cpuid.iter() {
if entry.function == function && (index.is_none() || index.unwrap() == entry.index) {
return match reg {
CpuidReg::EAX => Some(entry.eax),
CpuidReg::EBX => Some(entry.ebx),
CpuidReg::ECX => Some(entry.ecx),
CpuidReg::EDX => Some(entry.edx),
};
}
}
None
}
pub fn set_cpuid_reg(
cpuid: &mut Vec<CpuIdEntry>,
function: u32,
@@ -577,7 +552,11 @@ impl CpuidFeatureEntry {
pub fn generate_common_cpuid(
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
config: &CpuidConfig,
topology: Option<(u8, u8, u8)>,
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
phys_bits: u8,
kvm_hyperv: bool,
#[cfg(feature = "tdx")] tdx_enabled: bool,
) -> super::Result<Vec<CpuIdEntry>> {
// SAFETY: cpuid called with valid leaves
if unsafe { x86_64::__cpuid(1) }.ecx & 1 << HYPERVISOR_ECX_BIT == 1 << HYPERVISOR_ECX_BIT {
@@ -595,10 +574,7 @@ pub fn generate_common_cpuid(
);
}
info!(
"Generating guest CPUID for with physical address size: {}",
config.phys_bits
);
info!("Generating guest CPUID for with physical address size: {phys_bits}");
let cpuid_patches = vec![
// Patch tsc deadline timer bit
CpuidPatch {
@@ -639,12 +615,16 @@ pub fn generate_common_cpuid(
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
if let Some(sgx_epc_sections) = &config.sgx_epc_sections {
if let Some(t) = topology {
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2);
}
if let Some(sgx_epc_sections) = sgx_epc_sections {
update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?;
}
#[cfg(feature = "tdx")]
let tdx_capabilities = if config.tdx {
let tdx_capabilities = if tdx_enabled {
let caps = hypervisor
.tdx_capabilities()
.map_err(Error::TdxCapabilities)?;
@@ -657,12 +637,6 @@ pub fn generate_common_cpuid(
// Update some existing CPUID
for entry in cpuid.as_mut_slice().iter_mut() {
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)
}
}
0xd =>
{
#[cfg(feature = "tdx")]
@@ -698,12 +672,12 @@ pub fn generate_common_cpuid(
}
// Set CPU physical bits
0x8000_0008 => {
entry.eax = (entry.eax & 0xffff_ff00) | (config.phys_bits as u32 & 0xff);
entry.eax = (entry.eax & 0xffff_ff00) | (phys_bits as u32 & 0xff);
}
0x4000_0001 => {
// These features are not supported by TDX
#[cfg(feature = "tdx")]
if config.tdx {
if tdx_enabled {
entry.eax &= !(1 << KVM_FEATURE_CLOCKSOURCE_BIT
| 1 << KVM_FEATURE_CLOCKSOURCE2_BIT
| 1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT
@@ -731,7 +705,7 @@ pub fn generate_common_cpuid(
});
}
if config.kvm_hyperv {
if kvm_hyperv {
// Remove conflicting entries
cpuid.retain(|c| c.function != 0x4000_0000);
cpuid.retain(|c| c.function != 0x4000_0001);
@@ -787,30 +761,19 @@ pub fn configure_vcpu(
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
cpuid: Vec<CpuIdEntry>,
kvm_hyperv: bool,
cpu_vendor: CpuVendor,
topology: Option<(u8, u8, u8)>,
) -> super::Result<()> {
let x2apic_id = get_x2apic_id(id as u32, topology);
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
let mut cpuid = cpuid;
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, x2apic_id);
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, x2apic_id);
if matches!(cpu_vendor, CpuVendor::AMD) {
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x8000_001e, Some(0), CpuidReg::EAX, x2apic_id);
}
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
// Set ApicId in cpuid for each vcpu
// SAFETY: get host cpuid when eax=1
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
cpu_ebx &= 0xffffff;
cpu_ebx |= x2apic_id << 24;
cpu_ebx |= (id as u32) << 24;
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
if let Some(t) = topology {
update_cpuid_topology(&mut cpuid, t.0, t.1, t.2, cpu_vendor, id);
}
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation
if !kvm_hyperv {
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
@@ -848,7 +811,8 @@ pub fn configure_vcpu(
regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?;
if let Some((kernel_entry_point, guest_memory)) = boot_setup {
regs::setup_regs(vcpu, kernel_entry_point).map_err(Error::RegsConfiguration)?;
regs::setup_regs(vcpu, kernel_entry_point.entry_addr.raw_value())
.map_err(Error::RegsConfiguration)?;
regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?;
regs::setup_sregs(&guest_memory.memory(), vcpu).map_err(Error::SregsConfiguration)?;
}
@@ -897,16 +861,13 @@ pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
pub fn configure_system(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
cmdline_size: usize,
initramfs: &Option<InitramfsConfig>,
_num_cpus: u8,
setup_header: Option<setup_header>,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
serial_number: Option<&str>,
uuid: Option<&str>,
oem_strings: Option<&[&str]>,
topology: Option<(u8, u8, u8)>,
) -> super::Result<()> {
// Write EBDA address to location where ACPICA expects to find it
guest_mem
@@ -919,7 +880,7 @@ pub fn configure_system(
// Place the MP table after the SMIOS table aligned to 16 bytes
let offset = GuestAddress(layout::SMBIOS_START).unchecked_add(size);
let offset = GuestAddress((offset.0 + 16) & !0xf);
mptable::setup_mptable(offset, guest_mem, _num_cpus, topology).map_err(Error::MpTableSetup)?;
mptable::setup_mptable(offset, guest_mem, _num_cpus).map_err(Error::MpTableSetup)?;
// Check that the RAM is not smaller than the RSDP start address
if let Some(rsdp_addr) = rsdp_addr {
@@ -928,31 +889,61 @@ pub fn configure_system(
}
}
match setup_header {
Some(hdr) => configure_32bit_entry(
guest_mem,
cmdline_addr,
cmdline_size,
initramfs,
hdr,
rsdp_addr,
sgx_epc_region,
),
None => configure_pvh(
guest_mem,
cmdline_addr,
initramfs,
rsdp_addr,
sgx_epc_region,
),
}
configure_pvh(
guest_mem,
cmdline_addr,
initramfs,
rsdp_addr,
sgx_epc_region,
)
}
type RamRange = (u64, u64);
fn configure_pvh(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
initramfs: &Option<InitramfsConfig>,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
) -> super::Result<()> {
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
let mut start_info: StartInfoWrapper = StartInfoWrapper(hvm_start_info::default());
start_info.0.magic = XEN_HVM_START_MAGIC_VALUE;
start_info.0.version = 1; // pvh has version 1
start_info.0.nr_modules = 0;
start_info.0.cmdline_paddr = cmdline_addr.raw_value();
start_info.0.memmap_paddr = layout::MEMMAP_START.raw_value();
if let Some(rsdp_addr) = rsdp_addr {
start_info.0.rsdp_paddr = rsdp_addr.0;
}
if let Some(initramfs_config) = initramfs {
// The initramfs has been written to guest memory already, here we just need to
// create the module structure that describes it.
let ramdisk_mod: ModlistEntryWrapper = ModlistEntryWrapper(hvm_modlist_entry {
paddr: initramfs_config.address.raw_value(),
size: initramfs_config.size as u64,
..Default::default()
});
start_info.0.nr_modules += 1;
start_info.0.modlist_paddr = layout::MODLIST_START.raw_value();
// Write the modlist struct to guest memory.
guest_mem
.write_obj(ramdisk_mod, layout::MODLIST_START)
.map_err(super::Error::ModlistSetup)?;
}
// Vector to hold the memory maps which needs to be written to guest memory
// at MEMMAP_START after all of the mappings are recorded.
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
// Create the memory map entries.
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
/// Returns usable physical memory ranges for the guest
/// These should be used to create e820_RAM memory maps
pub fn generate_ram_ranges(guest_mem: &GuestMemoryMmap) -> super::Result<Vec<RamRange>> {
// Merge continuous memory regions into one region.
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
let ram_regions = {
@@ -985,12 +976,16 @@ pub fn generate_ram_ranges(guest_mem: &GuestMemoryMmap) -> super::Result<Vec<Ram
ram_regions
};
// Create the memory map entry for memory region before the gap
let mut ram_ranges = vec![];
if ram_regions.len() > 2 {
error!(
"There should be up to two non-continuous regions, devidided by the
gap at the end of 32bit address space (e.g. between 3G and 4G)."
);
return Err(super::Error::MemmapTableSetup);
}
// Generate the first usable physical memory range before the gap. The e820 map
// should only report memory above 1MiB.
let first_ram_range = {
// Create the memory map entry for memory region before the gap
{
let (first_region_start, first_region_end) =
ram_regions.first().ok_or(super::Error::MemmapTableSetup)?;
let high_ram_start = layout::HIGH_RAM_START.raw_value();
@@ -1010,87 +1005,39 @@ pub fn generate_ram_ranges(guest_mem: &GuestMemoryMmap) -> super::Result<Vec<Ram
}
info!(
"first usable physical memory range, start: 0x{:08x}, end: 0x{:08x}",
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x}",
high_ram_start, first_region_end
);
(high_ram_start, *first_region_end)
};
ram_ranges.push(first_ram_range);
// Generate additional usable physical memory range after the gap if any.
for ram_region in ram_regions.iter().skip(1) {
info!(
"found usable physical memory range, start: 0x{:08x}, end: 0x{:08x}",
ram_region.0, ram_region.1
add_memmap_entry(
&mut memmap,
high_ram_start,
first_region_end - high_ram_start,
E820_RAM,
);
ram_ranges.push(*ram_region);
}
Ok(ram_ranges)
}
// Create the memory map entry for memory region after the gap if any
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
let ram_64bit_start = layout::RAM_64BIT_START.raw_value();
fn configure_pvh(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
initramfs: &Option<InitramfsConfig>,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
) -> super::Result<()> {
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
if second_region_start != &ram_64bit_start {
error!(
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
second_region_start, ram_64bit_start
);
let mut start_info = hvm_start_info {
magic: XEN_HVM_START_MAGIC_VALUE,
version: 1, // pvh has version 1
nr_modules: 0,
cmdline_paddr: cmdline_addr.raw_value(),
memmap_paddr: layout::MEMMAP_START.raw_value(),
..Default::default()
};
return Err(super::Error::MemmapTableSetup);
}
if let Some(rsdp_addr) = rsdp_addr {
start_info.rsdp_paddr = rsdp_addr.0;
}
if let Some(initramfs_config) = initramfs {
// The initramfs has been written to guest memory already, here we just need to
// create the module structure that describes it.
let ramdisk_mod = hvm_modlist_entry {
paddr: initramfs_config.address.raw_value(),
size: initramfs_config.size as u64,
..Default::default()
};
start_info.nr_modules += 1;
start_info.modlist_paddr = layout::MODLIST_START.raw_value();
// Write the modlist struct to guest memory.
guest_mem
.write_obj(ramdisk_mod, layout::MODLIST_START)
.map_err(super::Error::ModlistSetup)?;
}
// Vector to hold the memory maps which needs to be written to guest memory
// at MEMMAP_START after all of the mappings are recorded.
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
// Create the memory map entries.
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
// Get usable physical memory ranges
let ram_ranges = generate_ram_ranges(guest_mem)?;
// Create e820 memory map entries
for ram_range in ram_ranges {
info!(
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x}",
ram_range.0, ram_range.1
ram_64bit_start, second_region_end
);
add_memmap_entry(
&mut memmap,
ram_range.0,
ram_range.1 - ram_range.0,
ram_64bit_start,
second_region_end - ram_64bit_start,
E820_RAM,
);
}
@@ -1111,7 +1058,7 @@ fn configure_pvh(
);
}
start_info.memmap_entries = memmap.len() as u32;
start_info.0.memmap_entries = memmap.len() as u32;
// Copy the vector with the memmap table to the MEMMAP_START address
// which is already saved in the memmap_paddr field of hvm_start_info struct.
@@ -1120,14 +1067,17 @@ fn configure_pvh(
guest_mem
.checked_offset(
memmap_start_addr,
mem::size_of::<hvm_memmap_table_entry>() * start_info.memmap_entries as usize,
mem::size_of::<hvm_memmap_table_entry>() * start_info.0.memmap_entries as usize,
)
.ok_or(super::Error::MemmapTablePastRamEnd)?;
// For every entry in the memmap vector, write it to guest memory.
// For every entry in the memmap vector, create a MemmapTableEntryWrapper
// and write it to guest memory.
for memmap_entry in memmap {
let map_entry_wrapper: MemmapTableEntryWrapper = MemmapTableEntryWrapper(memmap_entry);
guest_mem
.write_obj(memmap_entry, memmap_start_addr)
.write_obj(map_entry_wrapper, memmap_start_addr)
.map_err(|_| super::Error::MemmapTableSetup)?;
memmap_start_addr =
memmap_start_addr.unchecked_add(mem::size_of::<hvm_memmap_table_entry>() as u64);
@@ -1150,113 +1100,6 @@ fn configure_pvh(
Ok(())
}
fn configure_32bit_entry(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
cmdline_size: usize,
initramfs: &Option<InitramfsConfig>,
setup_hdr: setup_header,
rsdp_addr: Option<GuestAddress>,
sgx_epc_region: Option<SgxEpcRegion>,
) -> super::Result<()> {
const KERNEL_LOADER_OTHER: u8 = 0xff;
// Use the provided setup header
let mut params = boot_params {
hdr: setup_hdr,
..Default::default()
};
// Common bootparams settings
if params.hdr.type_of_loader == 0 {
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
}
params.hdr.cmd_line_ptr = cmdline_addr.raw_value() as u32;
params.hdr.cmdline_size = cmdline_size as u32;
if let Some(initramfs_config) = initramfs {
params.hdr.ramdisk_image = initramfs_config.address.raw_value() as u32;
params.hdr.ramdisk_size = initramfs_config.size as u32;
}
add_e820_entry(&mut params, 0, layout::EBDA_START.raw_value(), E820_RAM)?;
let mem_end = guest_mem.last_addr();
if mem_end < layout::MEM_32BIT_RESERVED_START {
add_e820_entry(
&mut params,
layout::HIGH_RAM_START.raw_value(),
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
E820_RAM,
)?;
} else {
add_e820_entry(
&mut params,
layout::HIGH_RAM_START.raw_value(),
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
E820_RAM,
)?;
if mem_end > layout::RAM_64BIT_START {
add_e820_entry(
&mut params,
layout::RAM_64BIT_START.raw_value(),
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
E820_RAM,
)?;
}
}
add_e820_entry(
&mut params,
layout::PCI_MMCONFIG_START.0,
layout::PCI_MMCONFIG_SIZE,
E820_RESERVED,
)?;
if let Some(sgx_epc_region) = sgx_epc_region {
add_e820_entry(
&mut params,
sgx_epc_region.start().raw_value(),
sgx_epc_region.size(),
E820_RESERVED,
)?;
}
if let Some(rsdp_addr) = rsdp_addr {
params.acpi_rsdp_addr = rsdp_addr.0;
}
let zero_page_addr = layout::ZERO_PAGE_START;
guest_mem
.checked_offset(zero_page_addr, mem::size_of::<boot_params>())
.ok_or(super::Error::ZeroPagePastRamEnd)?;
guest_mem
.write_obj(params, zero_page_addr)
.map_err(super::Error::ZeroPageSetup)?;
Ok(())
}
/// Add an e820 region to the e820 map.
/// Returns Ok(()) if successful, or an error if there is no space left in the map.
fn add_e820_entry(
params: &mut boot_params,
addr: u64,
size: u64,
mem_type: u32,
) -> Result<(), Error> {
if params.e820_entries >= params.e820_table.len() as u8 {
return Err(Error::E820Configuration);
}
params.e820_table[params.e820_entries as usize].addr = addr;
params.e820_table[params.e820_entries as usize].size = size;
params.e820_table[params.e820_entries as usize].type_ = mem_type;
params.e820_entries += 1;
Ok(())
}
fn add_memmap_entry(memmap: &mut Vec<hvm_memmap_table_entry>, addr: u64, size: u64, mem_type: u32) {
// Add the table entry to the vector
memmap.push(hvm_memmap_table_entry {
@@ -1317,27 +1160,11 @@ fn update_cpuid_topology(
threads_per_core: u8,
cores_per_die: u8,
dies_per_package: u8,
cpu_vendor: CpuVendor,
id: u8,
) {
let x2apic_id = get_x2apic_id(
id as u32,
Some((threads_per_core, cores_per_die, dies_per_package)),
);
let thread_width = 8 - (threads_per_core - 1).leading_zeros();
let core_width = (8 - (cores_per_die - 1).leading_zeros()) + thread_width;
let die_width = (8 - (dies_per_package - 1).leading_zeros()) + core_width;
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;
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);
cpu_edx |= 1 << 28;
CpuidPatch::set_cpuid_reg(cpuid, 0x1, None, CpuidReg::EDX, cpu_edx);
// CPU Topology leaf 0xb
CpuidPatch::set_cpuid_reg(cpuid, 0xb, Some(0), CpuidReg::EAX, thread_width);
CpuidPatch::set_cpuid_reg(
@@ -1389,72 +1216,13 @@ 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);
if matches!(cpu_vendor, CpuVendor::AMD) {
CpuidPatch::set_cpuid_reg(
cpuid,
0x8000_001e,
Some(0),
CpuidReg::EBX,
((threads_per_core as u32 - 1) << 8) | (x2apic_id & 0xff),
);
CpuidPatch::set_cpuid_reg(
cpuid,
0x8000_001e,
Some(0),
CpuidReg::ECX,
((dies_per_package as u32 - 1) << 8) | (thread_width + die_width) & 0xff,
);
CpuidPatch::set_cpuid_reg(cpuid, 0x8000_001e, Some(0), CpuidReg::EDX, 0);
if cores_per_die * threads_per_core > 1 {
let ecx =
CpuidPatch::get_cpuid_reg(cpuid, 0x8000_0001, Some(0), CpuidReg::ECX).unwrap_or(0);
CpuidPatch::set_cpuid_reg(
cpuid,
0x8000_0001,
Some(0),
CpuidReg::ECX,
ecx | (1u32 << 1) | (1u32 << 22),
);
CpuidPatch::set_cpuid_reg(
cpuid,
0x0000_0001,
Some(0),
CpuidReg::EBX,
(x2apic_id << 24) | (8 << 8) | (((cores_per_die * threads_per_core) as u32) << 16),
);
let cpuid_patches = vec![
// Patch tsc deadline timer bit
CpuidPatch {
function: 1,
index: 0,
flags_bit: None,
eax_bit: None,
ebx_bit: None,
ecx_bit: None,
edx_bit: Some(28),
},
];
CpuidPatch::patch_cpuid(cpuid, cpuid_patches);
CpuidPatch::set_cpuid_reg(
cpuid,
0x8000_0008,
Some(0),
CpuidReg::ECX,
((thread_width + core_width + die_width) << 12)
| ((cores_per_die * threads_per_core) - 1) as u32,
);
} else {
CpuidPatch::set_cpuid_reg(cpuid, 0x8000_0008, Some(0), CpuidReg::ECX, 0u32);
}
}
}
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
// sections exposed to the guest.
fn update_cpuid_sgx(
cpuid: &mut Vec<CpuIdEntry>,
epc_sections: &[SgxEpcSection],
epc_sections: Vec<SgxEpcSection>,
) -> Result<(), Error> {
// Something's wrong if there's no EPC section.
if epc_sections.is_empty() {
@@ -1503,7 +1271,6 @@ fn update_cpuid_sgx(
#[cfg(test)]
mod tests {
use super::*;
use linux_loader::loader::bootparam::boot_e820_entry;
#[test]
fn regions_base_addr() {
@@ -1520,16 +1287,13 @@ mod tests {
let config_err = configure_system(
&gm,
GuestAddress(0),
0,
&None,
1,
None,
Some(layout::RSDP_POINTER),
None,
None,
None,
None,
None,
);
assert!(config_err.is_err());
@@ -1545,7 +1309,6 @@ mod tests {
configure_system(
&gm,
GuestAddress(0),
0,
&None,
no_vcpus,
None,
@@ -1553,8 +1316,6 @@ mod tests {
None,
None,
None,
None,
None,
)
.unwrap();
@@ -1575,7 +1336,6 @@ mod tests {
configure_system(
&gm,
GuestAddress(0),
0,
&None,
no_vcpus,
None,
@@ -1583,15 +1343,12 @@ mod tests {
None,
None,
None,
None,
None,
)
.unwrap();
configure_system(
&gm,
GuestAddress(0),
0,
&None,
no_vcpus,
None,
@@ -1599,52 +1356,10 @@ mod tests {
None,
None,
None,
None,
None,
)
.unwrap();
}
#[test]
fn test_add_e820_entry() {
let e820_table = [(boot_e820_entry {
addr: 0x1,
size: 4,
type_: 1,
}); 128];
let expected_params = boot_params {
e820_table,
e820_entries: 1,
..Default::default()
};
let mut params: boot_params = Default::default();
add_e820_entry(
&mut params,
e820_table[0].addr,
e820_table[0].size,
e820_table[0].type_,
)
.unwrap();
assert_eq!(
format!("{:?}", params.e820_table[0]),
format!("{:?}", expected_params.e820_table[0])
);
assert_eq!(params.e820_entries, expected_params.e820_entries);
// Exercise the scenario where the field storing the length of the e820 entry table is
// is bigger than the allocated memory.
params.e820_entries = params.e820_table.len() as u8 + 1;
assert!(add_e820_entry(
&mut params,
e820_table[0].addr,
e820_table[0].size,
e820_table[0].type_
)
.is_err());
}
#[test]
fn test_add_memmap_entry() {
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
@@ -1669,25 +1384,4 @@ mod tests {
assert_eq!(format!("{memmap:?}"), format!("{expected_memmap:?}"));
}
#[test]
fn test_get_x2apic_id() {
let x2apic_id = get_x2apic_id(0, Some((2, 3, 1)));
assert_eq!(x2apic_id, 0);
let x2apic_id = get_x2apic_id(1, Some((2, 3, 1)));
assert_eq!(x2apic_id, 1);
let x2apic_id = get_x2apic_id(2, Some((2, 3, 1)));
assert_eq!(x2apic_id, 2);
let x2apic_id = get_x2apic_id(6, Some((2, 3, 1)));
assert_eq!(x2apic_id, 8);
let x2apic_id = get_x2apic_id(7, Some((2, 3, 1)));
assert_eq!(x2apic_id, 9);
let x2apic_id = get_x2apic_id(8, Some((2, 3, 1)));
assert_eq!(x2apic_id, 10);
}
}

View File

@@ -1,6 +1,4 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.

View File

@@ -6,9 +6,10 @@
// found in the LICENSE-BSD-3-Clause file.
use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START};
use crate::x86_64::{get_x2apic_id, mpspec};
use crate::x86_64::mpspec;
use crate::GuestMemoryMmap;
use libc::c_char;
use std::io;
use std::mem;
use std::result;
use std::slice;
@@ -125,18 +126,9 @@ fn compute_mp_size(num_cpus: u8) -> usize {
}
/// Performs setup of the MP table for the given `num_cpus`.
pub fn setup_mptable(
offset: GuestAddress,
mem: &GuestMemoryMmap,
num_cpus: u8,
topology: Option<(u8, u8, u8)>,
) -> Result<()> {
if num_cpus > 0 {
let cpu_id_max = num_cpus - 1;
let x2apic_id_max = get_x2apic_id(cpu_id_max.into(), topology);
if x2apic_id_max >= MAX_SUPPORTED_CPUS {
return Err(Error::TooManyCpus);
}
pub fn setup_mptable(offset: GuestAddress, mem: &GuestMemoryMmap, num_cpus: u8) -> Result<()> {
if num_cpus as u32 > MAX_SUPPORTED_CPUS {
return Err(Error::TooManyCpus);
}
// Used to keep track of the next base pointer into the MP table.
@@ -150,7 +142,7 @@ pub fn setup_mptable(
}
let mut checksum: u8 = 0;
let ioapicid: u8 = MAX_SUPPORTED_CPUS as u8 + 1;
let ioapicid: u8 = num_cpus + 1;
// The checked_add here ensures the all of the following base_mp.unchecked_add's will be without
// overflow.
@@ -162,7 +154,7 @@ pub fn setup_mptable(
return Err(Error::AddressOverflow);
}
mem.read_exact_volatile_from(base_mp, &mut vec![0; mp_size].as_slice(), mp_size)
mem.read_exact_from(base_mp, &mut io::repeat(0), mp_size)
.map_err(Error::Clear)?;
{
@@ -188,7 +180,7 @@ pub fn setup_mptable(
for cpu_id in 0..num_cpus {
let mut mpc_cpu = MpcCpuWrapper(mpspec::mpc_cpu::default());
mpc_cpu.0.type_ = mpspec::MP_PROCESSOR as u8;
mpc_cpu.0.apicid = get_x2apic_id(cpu_id as u32, topology) as u8;
mpc_cpu.0.apicid = cpu_id;
mpc_cpu.0.apicver = APIC_VERSION;
mpc_cpu.0.cpuflag = mpspec::CPU_ENABLED as u8
| if cpu_id == 0 {
@@ -299,9 +291,7 @@ pub fn setup_mptable(
mod tests {
use super::*;
use crate::layout::MPTABLE_START;
use vm_memory::{
bitmap::BitmapSlice, GuestUsize, VolatileMemoryError, VolatileSlice, WriteVolatile,
};
use vm_memory::{GuestAddress, GuestUsize};
fn table_entry_size(type_: u8) -> usize {
match type_ as u32 {
@@ -320,7 +310,7 @@ mod tests {
let mem =
GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus))]).unwrap();
setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap();
setup_mptable(MPTABLE_START, &mem, num_cpus).unwrap();
}
#[test]
@@ -329,7 +319,7 @@ mod tests {
let mem = GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus) - 1)])
.unwrap();
assert!(setup_mptable(MPTABLE_START, &mem, num_cpus, None).is_err());
assert!(setup_mptable(MPTABLE_START, &mem, num_cpus).is_err());
}
#[test]
@@ -338,7 +328,7 @@ mod tests {
let mem =
GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus))]).unwrap();
setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap();
setup_mptable(MPTABLE_START, &mem, num_cpus).unwrap();
let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap();
@@ -354,31 +344,27 @@ mod tests {
let mem =
GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus))]).unwrap();
setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap();
setup_mptable(MPTABLE_START, &mem, num_cpus).unwrap();
let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap();
let mpc_offset = GuestAddress(mpf_intel.0.physptr as GuestUsize);
let mpc_table: MpcTableWrapper = mem.read_obj(mpc_offset).unwrap();
struct Sum(u8);
impl WriteVolatile for Sum {
fn write_volatile<B: BitmapSlice>(
&mut self,
buf: &VolatileSlice<B>,
) -> result::Result<usize, VolatileMemoryError> {
let mut tmp = vec![0u8; buf.len()];
tmp.write_all_volatile(buf)?;
for v in tmp.iter() {
impl io::Write for Sum {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
for v in buf.iter() {
self.0 = self.0.wrapping_add(*v);
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
let mut sum = Sum(0);
mem.write_volatile_to(mpc_offset, &mut sum, mpc_table.0.length as usize)
mem.write_to(mpc_offset, &mut sum, mpc_table.0.length as usize)
.unwrap();
assert_eq!(sum.0, 0);
}
@@ -392,7 +378,7 @@ mod tests {
.unwrap();
for i in 0..MAX_SUPPORTED_CPUS as u8 {
setup_mptable(MPTABLE_START, &mem, i, None).unwrap();
setup_mptable(MPTABLE_START, &mem, i).unwrap();
let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap();
let mpc_offset = GuestAddress(mpf_intel.0.physptr as GuestUsize);
@@ -425,7 +411,7 @@ mod tests {
let mem =
GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(cpus as u8))]).unwrap();
let result = setup_mptable(MPTABLE_START, &mem, cpus as u8, None);
let result = setup_mptable(MPTABLE_START, &mem, cpus as u8);
assert!(result.is_err());
}
}

View File

@@ -6,10 +6,8 @@
// 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.
use crate::layout::{
BOOT_GDT_START, BOOT_IDT_START, BOOT_STACK_POINTER, PVH_INFO_START, ZERO_PAGE_START,
};
use crate::{EntryPoint, GuestMemoryMmap};
use crate::layout::{BOOT_GDT_START, BOOT_IDT_START, PVH_INFO_START};
use crate::GuestMemoryMmap;
use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
use hypervisor::arch::x86::regs::CR0_PE;
use hypervisor::arch::x86::{FpuState, SpecialRegisters, StandardRegisters};
@@ -79,22 +77,13 @@ pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
/// # Arguments
///
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
/// * `entry_point` - Description of the boot entry to set up.
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, entry_point: EntryPoint) -> Result<()> {
let regs = match entry_point.setup_header {
None => StandardRegisters {
rflags: 0x0000000000000002u64,
rip: entry_point.entry_addr.raw_value(),
rbx: PVH_INFO_START.raw_value(),
..Default::default()
},
Some(_) => StandardRegisters {
rflags: 0x0000000000000002u64,
rip: entry_point.entry_addr.raw_value(),
rsp: BOOT_STACK_POINTER.raw_value(),
rsi: ZERO_PAGE_START.raw_value(),
..Default::default()
},
/// * `boot_ip` - Starting instruction pointer.
pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, boot_ip: u64) -> Result<()> {
let regs = StandardRegisters {
rflags: 0x0000000000000002u64,
rbx: PVH_INFO_START.raw_value(),
rip: boot_ip,
..Default::default()
};
vcpu.set_regs(&regs).map_err(Error::SetBaseRegisters)
}
@@ -175,6 +164,7 @@ pub fn configure_segments_and_sregs(
#[cfg(test)]
mod tests {
use super::*;
use crate::GuestMemoryMmap;
use vm_memory::GuestAddress;
fn create_guest_mem() -> GuestMemoryMmap {

View File

@@ -9,18 +9,19 @@ default = []
io_uring = ["dep:io-uring"]
[dependencies]
byteorder = "1.5.0"
crc-any = "2.4.4"
io-uring = { version = "0.6.3", optional = true }
libc = "0.2.153"
log = "0.4.21"
remain = "0.2.13"
serde = { version = "1.0.197", features = ["derive"] }
smallvec = "1.13.2"
thiserror = "1.0.58"
uuid = { version = "1.8.0", features = ["v4"] }
virtio-bindings = { version = "0.2.2", features = ["virtio-v5_0_0"] }
virtio-queue = "0.11.0"
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
byteorder = "1.4.3"
crc32c = "0.6.4"
io-uring = { version = "0.6.1", optional = true }
libc = "0.2.147"
log = "0.4.17"
remain = "0.2.11"
smallvec = "1.11.0"
thiserror = "1.0.40"
uuid = { version = "1.3.4", features = ["v4"] }
versionize = "0.1.10"
versionize_derive = "0.1.4"
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
virtio-queue = "0.9.0"
vm-memory = { version = "0.12.2", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.12.1"
vmm-sys-util = "0.11.0"

View File

@@ -24,7 +24,6 @@ pub mod qcow_sync;
///
/// Enabled with the `"io_uring"` feature
pub mod raw_async;
pub mod raw_async_aio;
pub mod raw_sync;
pub mod vhd;
pub mod vhdx;
@@ -37,11 +36,11 @@ use crate::vhdx::{Vhdx, VhdxError};
#[cfg(feature = "io_uring")]
use io_uring::{opcode, IoUring, Probe};
use libc::{ioctl, S_IFBLK, S_IFMT};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::cmp;
use std::collections::VecDeque;
use std::convert::TryInto;
use std::fmt::Debug;
use std::fs::File;
use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
@@ -53,6 +52,8 @@ use std::sync::Arc;
use std::sync::MutexGuard;
use std::time::Instant;
use thiserror::Error;
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use virtio_bindings::virtio_blk::*;
use virtio_queue::DescriptorChain;
use vm_memory::{
@@ -60,7 +61,6 @@ use vm_memory::{
GuestMemoryError, GuestMemoryLoadGuard,
};
use vm_virtio::{AccessPlatform, Translatable};
use vmm_sys_util::aio;
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr};
@@ -141,14 +141,10 @@ pub enum ExecuteError {
Flush(io::Error),
#[error("Failed to read: {0}")]
Read(GuestMemoryError),
#[error("Failed to read_exact: {0}")]
ReadExact(io::Error),
#[error("Failed to seek: {0}")]
Seek(io::Error),
#[error("Failed to write: {0}")]
Write(GuestMemoryError),
#[error("Failed to write_all: {0}")]
WriteAll(io::Error),
#[error("Unsupported request: {0}")]
Unsupported(u32),
#[error("Failed to submit io uring: {0}")]
@@ -171,10 +167,8 @@ impl ExecuteError {
ExecuteError::BadRequest(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::ReadExact(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Seek(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Write(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::WriteAll(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Unsupported(_) => VIRTIO_BLK_S_UNSUPP,
ExecuteError::SubmitIoUring(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::GetHostAddress(_) => VIRTIO_BLK_S_IOERR,
@@ -355,21 +349,13 @@ impl Request {
match self.request_type {
RequestType::In => {
let mut buf = vec![0u8; *data_len as usize];
disk.read_exact(&mut buf).map_err(ExecuteError::ReadExact)?;
mem.read_exact_volatile_from(
*data_addr,
&mut buf.as_slice(),
*data_len as usize,
)
.map_err(ExecuteError::Read)?;
mem.read_exact_from(*data_addr, disk, *data_len as usize)
.map_err(ExecuteError::Read)?;
len += data_len;
}
RequestType::Out => {
let mut buf: Vec<u8> = Vec::new();
mem.write_all_volatile_to(*data_addr, &mut buf, *data_len as usize)
mem.write_all_to(*data_addr, disk, *data_len as usize)
.map_err(ExecuteError::Write)?;
disk.write_all(&buf).map_err(ExecuteError::WriteAll)?;
if !self.writeback {
disk.flush().map_err(ExecuteError::Flush)?;
}
@@ -543,7 +529,7 @@ impl Request {
}
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Default, Versionize)]
#[repr(C, packed)]
pub struct VirtioBlockConfig {
pub capacity: u64,
@@ -566,7 +552,7 @@ pub struct VirtioBlockConfig {
pub write_zeroes_may_unmap: u8,
pub unused1: [u8; 3],
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Default, Versionize)]
#[repr(C, packed)]
pub struct VirtioBlockGeometry {
pub cylinders: u16,
@@ -579,11 +565,6 @@ unsafe impl ByteValued for VirtioBlockConfig {}
// SAFETY: data structure only contain a series of integers
unsafe impl ByteValued for VirtioBlockGeometry {}
/// Check if aio can be used on the current system.
pub fn block_aio_is_supported() -> bool {
aio::IoContext::new(1).is_ok()
}
/// Check if io_uring for block device can be used on the current system, as
/// it correctly supports the expected io_uring features.
pub fn block_io_uring_is_supported() -> bool {

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
mod qcow_raw_file;
mod raw_file;
@@ -1824,6 +1822,7 @@ pub fn detect_image_type(file: &mut RawFile) -> Result<ImageType> {
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, Seek, SeekFrom, Write};
use vmm_sys_util::tempfile::TempFile;
use vmm_sys_util::write_zeroes::WriteZeroes;

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use super::RawFile;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

View File

@@ -11,6 +11,7 @@
use crate::BlockBackend;
use libc::c_void;
use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::convert::TryInto;
use std::fs::{File, Metadata};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::os::unix::io::{AsRawFd, RawFd};

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::fmt::{self, Display};
use std::io;

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::collections::hash_map::IterMut;
use std::collections::HashMap;
@@ -138,7 +136,7 @@ impl<T: Cacheable> CacheMap<T> {
mod tests {
use super::*;
struct NumCache(());
struct NumCache(pub u64);
impl Cacheable for NumCache {
fn dirty(&self) -> bool {
true
@@ -150,28 +148,28 @@ mod tests {
let mut cache = CacheMap::<NumCache>::new(3);
let mut evicted = None;
cache
.insert(0, NumCache(()), |index, _| {
.insert(0, NumCache(5), |index, _| {
evicted = Some(index);
Ok(())
})
.unwrap();
assert_eq!(evicted, None);
cache
.insert(1, NumCache(()), |index, _| {
.insert(1, NumCache(6), |index, _| {
evicted = Some(index);
Ok(())
})
.unwrap();
assert_eq!(evicted, None);
cache
.insert(2, NumCache(()), |index, _| {
.insert(2, NumCache(7), |index, _| {
evicted = Some(index);
Ok(())
})
.unwrap();
assert_eq!(evicted, None);
cache
.insert(3, NumCache(()), |index, _| {
.insert(3, NumCache(8), |index, _| {
evicted = Some(index);
Ok(())
})

View File

@@ -1,150 +0,0 @@
// Copyright © 2023 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
//
// Copyright © 2023 Crusoe Energy Systems LLC
//
use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
};
use crate::DiskTopology;
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, RawFd};
use vmm_sys_util::aio;
use vmm_sys_util::eventfd::EventFd;
pub struct RawFileDiskAio {
file: File,
}
impl RawFileDiskAio {
pub fn new(file: File) -> Self {
RawFileDiskAio { file }
}
}
impl DiskFile for RawFileDiskAio {
fn size(&mut self) -> DiskFileResult<u64> {
self.file
.seek(SeekFrom::End(0))
.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)
.map_err(DiskFileError::NewAsyncIo)?,
) as Box<dyn AsyncIo>)
}
fn topology(&mut self) -> DiskTopology {
if let Ok(topology) = DiskTopology::probe(&self.file) {
topology
} else {
warn!("Unable to get device topology. Using default topology");
DiskTopology::default()
}
}
}
pub struct RawFileAsyncAio {
fd: RawFd,
ctx: aio::IoContext,
eventfd: EventFd,
}
impl RawFileAsyncAio {
pub fn new(fd: RawFd, queue_depth: u32) -> std::io::Result<Self> {
let eventfd = EventFd::new(libc::EFD_NONBLOCK)?;
let ctx = aio::IoContext::new(queue_depth)?;
Ok(RawFileAsyncAio { fd, ctx, eventfd })
}
}
impl AsyncIo for RawFileAsyncAio {
fn notifier(&self) -> &EventFd {
&self.eventfd
}
fn read_vectored(
&mut self,
offset: libc::off_t,
iovecs: &[libc::iovec],
user_data: u64,
) -> AsyncIoResult<()> {
let iocbs = [&mut aio::IoControlBlock {
aio_fildes: self.fd.as_raw_fd() as u32,
aio_lio_opcode: aio::IOCB_CMD_PREADV as u16,
aio_buf: iovecs.as_ptr() as u64,
aio_nbytes: iovecs.len() as u64,
aio_offset: offset,
aio_data: user_data,
aio_flags: aio::IOCB_FLAG_RESFD,
aio_resfd: self.eventfd.as_raw_fd() as u32,
..Default::default()
}];
let _ = self
.ctx
.submit(&iocbs[..])
.map_err(AsyncIoError::ReadVectored)?;
Ok(())
}
fn write_vectored(
&mut self,
offset: libc::off_t,
iovecs: &[libc::iovec],
user_data: u64,
) -> AsyncIoResult<()> {
let iocbs = [&mut aio::IoControlBlock {
aio_fildes: self.fd.as_raw_fd() as u32,
aio_lio_opcode: aio::IOCB_CMD_PWRITEV as u16,
aio_buf: iovecs.as_ptr() as u64,
aio_nbytes: iovecs.len() as u64,
aio_offset: offset,
aio_data: user_data,
aio_flags: aio::IOCB_FLAG_RESFD,
aio_resfd: self.eventfd.as_raw_fd() as u32,
..Default::default()
}];
let _ = self
.ctx
.submit(&iocbs[..])
.map_err(AsyncIoError::WriteVectored)?;
Ok(())
}
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
if let Some(user_data) = user_data {
let iocbs = [&mut aio::IoControlBlock {
aio_fildes: self.fd.as_raw_fd() as u32,
aio_lio_opcode: aio::IOCB_CMD_FSYNC as u16,
aio_data: user_data,
aio_flags: aio::IOCB_FLAG_RESFD,
aio_resfd: self.eventfd.as_raw_fd() as u32,
..Default::default()
}];
let _ = self.ctx.submit(&iocbs[..]).map_err(AsyncIoError::Fsync)?;
} else {
// SAFETY: FFI call with a valid fd
unsafe { libc::fsync(self.fd) };
}
Ok(())
}
fn next_completed_request(&mut self) -> Option<(u64, i32)> {
let mut events: [aio::IoEvent; 1] = [aio::IoEvent::default()];
let rc = self.ctx.get_events(0, &mut events, None).unwrap();
if rc == 0 {
None
} else {
Some((events[0].data, events[0].res as i32))
}
}
}

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::{read_aligned_block_size, DiskTopology};
use std::convert::TryInto;
use std::fs::File;
use std::io::{Seek, SeekFrom};

View File

@@ -7,6 +7,7 @@ extern crate log;
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
use remain::sorted;
use std::collections::btree_map::BTreeMap;
use std::convert::TryInto;
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
@@ -90,7 +91,7 @@ pub type Result<T> = std::result::Result<T, VhdxHeaderError>;
#[derive(Clone, Debug)]
pub struct FileTypeIdentifier {
pub _signature: u64,
pub signature: u64,
}
impl FileTypeIdentifier {
@@ -98,14 +99,14 @@ impl FileTypeIdentifier {
pub fn new(f: &mut File) -> Result<FileTypeIdentifier> {
f.seek(SeekFrom::Start(FILE_START))
.map_err(VhdxHeaderError::SeekFileTypeIdentifier)?;
let _signature = f
let signature = f
.read_u64::<LittleEndian>()
.map_err(VhdxHeaderError::ReadFileTypeIdentifier)?;
if _signature != VHDX_SIGN {
if signature != VHDX_SIGN {
return Err(VhdxHeaderError::InvalidVHDXSign);
}
Ok(FileTypeIdentifier { _signature })
Ok(FileTypeIdentifier { signature })
}
}
@@ -191,9 +192,7 @@ impl Header {
};
new_header.get_header_as_buffer(&mut buffer);
let mut crc = crc_any::CRC::crc32c();
crc.digest(&buffer);
new_header.checksum = crc.get_crc() as u32;
new_header.checksum = crc32c::crc32c(&buffer);
new_header.get_header_as_buffer(&mut buffer);
f.seek(SeekFrom::Start(start))
@@ -352,6 +351,12 @@ impl RegionTableEntry {
}
}
#[derive(Clone, Debug)]
struct RegionEntry {
_start: u64,
_end: u64,
}
enum HeaderNo {
First,
Second,
@@ -475,10 +480,7 @@ pub fn calculate_checksum(buffer: &mut [u8], csum_offset: usize) -> Result<u32>
// Zero the checksum in the buffer
LittleEndian::write_u32(csum_buf, 0);
// Calculate the checksum on the resulting buffer
let mut crc = crc_any::CRC::crc32c();
crc.digest(&buffer);
let new_csum = crc.get_crc() as u32;
let new_csum = crc32c::crc32c(buffer);
// Put back the original checksum in the buffer
LittleEndian::write_u32(&mut buffer[csum_offset..csum_offset + 4], orig_csum);

View File

@@ -6,23 +6,24 @@ edition = "2021"
[dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.81"
anyhow = "1.0.75"
arch = { path = "../arch" }
bitflags = "2.5.0"
byteorder = "1.5.0"
bitflags = "2.3.3"
byteorder = "1.4.3"
event_monitor = { path = "../event_monitor" }
hypervisor = { path = "../hypervisor" }
libc = "0.2.153"
log = "0.4.21"
libc = "0.2.147"
log = "0.4.17"
pci = { path = "../pci" }
serde = { version = "1.0.197", features = ["derive"] }
thiserror = "1.0.58"
thiserror = "1.0.40"
tpm = { path = "../tpm" }
versionize = "0.1.10"
versionize_derive = "0.1.4"
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = "0.14.1"
vm-memory = "0.12.2"
vm-migration = { path = "../vm-migration" }
vmm-sys-util = "0.12.1"
vmm-sys-util = "0.11.0"
[target.'cfg(target_arch = "aarch64")'.dependencies]
arch = { path = "../arch" }

View File

@@ -1,64 +0,0 @@
// Copyright © 2023 Cyberus Technology
//
// SPDX-License-Identifier: Apache-2.0
//
//! Module for [`DebugconState`].
use std::io;
use std::io::Write;
use std::sync::{Arc, Barrier};
use vm_device::BusDevice;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
/// I/O-port.
pub const DEFAULT_PORT: u64 = 0xe9;
#[derive(Default)]
pub struct DebugconState {}
/// Emulates a debug console similar to the QEMU debugcon device. This device
/// is stateless and only prints the bytes (usually text) that are written to
/// it.
///
/// This device is only available on x86.
///
/// Reference:
/// - https://github.com/qemu/qemu/blob/master/hw/char/debugcon.c
/// - https://phip1611.de/blog/how-to-use-qemus-debugcon-feature-and-write-to-a-file/
pub struct DebugConsole {
id: String,
out: Box<dyn io::Write + Send>,
}
impl DebugConsole {
pub fn new(id: String, out: Box<dyn io::Write + Send>) -> Self {
Self { id, out }
}
}
impl BusDevice for DebugConsole {
fn read(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {}
fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
if let Err(e) = self.out.write_all(data) {
// unlikely
error!("debug-console: failed writing data: {e:?}");
}
None
}
}
impl Snapshottable for DebugConsole {
fn id(&self) -> String {
self.id.clone()
}
fn snapshot(&mut self) -> Result<Snapshot, MigratableError> {
Snapshot::new_from_state(&())
}
}
impl Pausable for DebugConsole {}
impl Transportable for DebugConsole {}
impl Migratable for DebugConsole {}

View File

@@ -11,16 +11,19 @@
use super::interrupt_controller::{Error, InterruptController};
use byteorder::{ByteOrder, LittleEndian};
use serde::{Deserialize, Serialize};
use std::result;
use std::sync::{Arc, Barrier};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
MsiIrqGroupConfig, MsiIrqSourceConfig,
};
use vm_device::BusDevice;
use vm_memory::GuestAddress;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
};
use vmm_sys_util::eventfd::EventFd;
type Result<T> = result::Result<T, Error>;
@@ -133,7 +136,7 @@ pub struct Ioapic {
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
}
#[derive(Serialize, Deserialize)]
#[derive(Versionize)]
pub struct IoapicState {
id_reg: u32,
reg_sel: u32,
@@ -141,6 +144,7 @@ pub struct IoapicState {
used_entries: [bool; NUM_IOAPIC_PINS],
apic_address: u64,
}
impl VersionMapped for IoapicState {}
impl BusDevice for Ioapic {
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
@@ -440,7 +444,7 @@ impl Snapshottable for Ioapic {
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
Snapshot::new_from_state(&self.state())
Snapshot::new_from_versioned_state(&self.state())
}
}

View File

@@ -1,8 +1,6 @@
// 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 file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use libc::{clock_gettime, gmtime_r, timespec, tm, CLOCK_REALTIME};
use std::cmp::min;

View File

@@ -8,13 +8,16 @@
//!
use crate::{read_le_u32, write_le_u32};
use serde::{Deserialize, Serialize};
use std::result;
use std::sync::{Arc, Barrier};
use std::{fmt, io};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
};
const OFS_DATA: u64 = 0x400; // Data Register
const GPIODIR: u64 = 0x400; // Direction Register
@@ -86,7 +89,7 @@ pub struct Gpio {
interrupt: Arc<dyn InterruptSourceGroup>,
}
#[derive(Serialize, Deserialize)]
#[derive(Versionize)]
pub struct GpioState {
data: u32,
old_in_data: u32,
@@ -99,6 +102,8 @@ pub struct GpioState {
afsel: u32,
}
impl VersionMapped for GpioState {}
impl Gpio {
/// Constructs an PL061 GPIO device.
pub fn new(
@@ -323,7 +328,7 @@ impl Snapshottable for Gpio {
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
Snapshot::new_from_state(&self.state())
Snapshot::new_from_versioned_state(&self.state())
}
}
@@ -334,6 +339,8 @@ impl Migratable for Gpio {}
#[cfg(test)]
mod tests {
use super::*;
use crate::{read_le_u32, write_le_u32};
use std::sync::Arc;
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;

View File

@@ -1,8 +1,6 @@
// 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.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::sync::{
atomic::{AtomicBool, Ordering},

View File

@@ -329,9 +329,10 @@ impl BusDevice for Rtc {
mod tests {
use super::*;
use crate::{
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u64, write_be_u16,
write_be_u32, write_le_i32, write_le_u16, write_le_u64,
read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u32, read_le_u64, write_be_u16,
write_be_u32, write_le_i32, write_le_u16, write_le_u32, write_le_u64,
};
use std::sync::Arc;
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;

View File

@@ -5,13 +5,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::sync::{Arc, Barrier};
use std::{io, result};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
};
use vmm_sys_util::errno::Result;
const LOOP_SIZE: usize = 0x40;
@@ -71,7 +74,7 @@ pub struct Serial {
out: Option<Box<dyn io::Write + Send>>,
}
#[derive(Serialize, Deserialize)]
#[derive(Versionize)]
pub struct SerialState {
interrupt_enable: u8,
interrupt_identification: u8,
@@ -83,6 +86,7 @@ pub struct SerialState {
baud_divisor: u16,
in_buffer: Vec<u8>,
}
impl VersionMapped for SerialState {}
impl Serial {
pub fn new(
@@ -162,8 +166,8 @@ impl Serial {
Self::new(id, interrupt, None, state)
}
pub fn set_out(&mut self, out: Option<Box<dyn io::Write + Send>>) {
self.out = out;
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
self.out = Some(out);
}
/// Queues raw bytes for the guest to read and signals the interrupt if the line status would
@@ -330,7 +334,7 @@ impl Snapshottable for Serial {
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
Snapshot::new_from_state(&self.state())
Snapshot::new_from_versioned_state(&self.state())
}
}
@@ -341,7 +345,8 @@ impl Migratable for Serial {}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
use std::io;
use std::sync::{Arc, Mutex};
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;

View File

@@ -7,15 +7,18 @@
//!
use crate::{read_le_u32, write_le_u32};
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::fmt;
use std::sync::{Arc, Barrier};
use std::time::Instant;
use std::{io, result};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_device::interrupt::InterruptSourceGroup;
use vm_device::BusDevice;
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
};
/* Registers */
const UARTDR: u64 = 0;
@@ -91,7 +94,7 @@ pub struct Pl011 {
timestamp: std::time::Instant,
}
#[derive(Serialize, Deserialize)]
#[derive(Versionize)]
pub struct Pl011State {
flags: u32,
lcr: u32,
@@ -110,6 +113,8 @@ pub struct Pl011State {
read_trigger: u32,
}
impl VersionMapped for Pl011State {}
impl Pl011 {
/// Constructs an AMBA PL011 UART device.
pub fn new(
@@ -196,8 +201,8 @@ impl Pl011 {
}
}
pub fn set_out(&mut self, out: Option<Box<dyn io::Write + Send>>) {
self.out = out;
pub fn set_out(&mut self, out: Box<dyn io::Write + Send>) {
self.out = Some(out);
}
fn state(&self) -> Pl011State {
@@ -449,7 +454,7 @@ impl Snapshottable for Pl011 {
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
Snapshot::new_from_state(&self.state())
Snapshot::new_from_versioned_state(&self.state())
}
}
@@ -460,7 +465,8 @@ impl Migratable for Pl011 {}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
use std::io;
use std::sync::{Arc, Mutex};
use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
use vmm_sys_util::eventfd::EventFd;

View File

@@ -15,8 +15,6 @@ extern crate event_monitor;
extern crate log;
pub mod acpi;
#[cfg(target_arch = "x86_64")]
pub mod debug_console;
#[cfg(target_arch = "aarch64")]
pub mod gic;
pub mod interrupt_controller;

View File

@@ -9,15 +9,18 @@ use pci::{
PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass,
PCI_CONFIGURATION_ID,
};
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::result;
use std::sync::{Arc, Barrier, Mutex};
use thiserror::Error;
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_allocator::{AddressAllocator, SystemAllocator};
use vm_device::{BusDevice, Resource};
use vm_memory::{Address, GuestAddress};
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
};
const PVPANIC_VENDOR_ID: u16 = 0x1b36;
const PVPANIC_DEVICE_ID: u16 = 0x0011;
@@ -57,20 +60,23 @@ pub struct PvPanicDevice {
bar_regions: Vec<PciBarConfiguration>,
}
#[derive(Serialize, Deserialize)]
#[derive(Versionize)]
pub struct PvPanicDeviceState {
events: u8,
}
impl VersionMapped for 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
))
})?;
vm_migration::versioned_state_from_id(snapshot.as_ref(), PCI_CONFIGURATION_ID)
.map_err(|e| {
PvPanicError::RetrievePciConfigurationState(anyhow!(
"Failed to get PciConfigurationState from Snapshot: {}",
e
))
})?;
let mut configuration = PciConfiguration::new(
PVPANIC_VENDOR_ID,
@@ -91,7 +97,7 @@ impl PvPanicDevice {
let state: Option<PvPanicDeviceState> = snapshot
.as_ref()
.map(|s| s.to_state())
.map(|s| s.to_versioned_state())
.transpose()
.map_err(|e| {
PvPanicError::CreatePvPanicDevice(anyhow!(
@@ -175,9 +181,8 @@ impl PciDevice for PvPanicDevice {
fn allocate_bars(
&mut self,
_allocator: &Arc<Mutex<SystemAllocator>>,
mmio32_allocator: &mut AddressAllocator,
_mmio64_allocator: &mut AddressAllocator,
allocator: &Arc<Mutex<SystemAllocator>>,
_mmio_allocator: &mut AddressAllocator,
resources: Option<Vec<Resource>>,
) -> std::result::Result<Vec<PciBarConfiguration>, PciDeviceError> {
let mut bars = Vec::new();
@@ -185,8 +190,10 @@ impl PciDevice for PvPanicDevice {
let bar_id = 0;
let region_size = PVPANIC_DEVICE_MMIO_SIZE;
let restoring = resources.is_some();
let bar_addr = mmio32_allocator
.allocate(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT))
let bar_addr = allocator
.lock()
.unwrap()
.allocate_mmio_hole_addresses(None, region_size, Some(PVPANIC_DEVICE_MMIO_ALIGNMENT))
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?;
let bar = PciBarConfiguration::default()
@@ -204,19 +211,18 @@ impl PciDevice for PvPanicDevice {
}
bars.push(bar);
self.bar_regions.clone_from(&bars);
self.bar_regions = bars.clone();
Ok(bars)
}
fn free_bars(
&mut self,
_allocator: &mut SystemAllocator,
mmio32_allocator: &mut AddressAllocator,
_mmio64_allocator: &mut AddressAllocator,
allocator: &mut SystemAllocator,
_mmio_allocator: &mut AddressAllocator,
) -> std::result::Result<(), PciDeviceError> {
for bar in self.bar_regions.drain(..) {
mmio32_allocator.free(GuestAddress(bar.addr()), bar.size());
allocator.free_mmio_hole_addresses(GuestAddress(bar.addr()), bar.size());
}
Ok(())
@@ -253,7 +259,7 @@ impl Snapshottable for PvPanicDevice {
}
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
let mut snapshot = Snapshot::new_from_state(&self.state())?;
let mut snapshot = Snapshot::new_from_versioned_state(&self.state())?;
// Snapshot PciConfiguration
snapshot.add_snapshot(self.configuration.id(), self.configuration.snapshot()?);

View File

@@ -1,5 +1,4 @@
All documentations (e.g. files with extension `.md`) in this repository is
covered by the following license:
The documentation in this directory is covered by the following license:
Attribution 4.0 International

View File

@@ -36,5 +36,3 @@ You can run a SEV-SNP VM using the following command:
--memory size=1G \
--disk path=ubuntu.img
```
For more information related to Microsoft Hypervisor please see [mshv.md](mshv.md)

View File

@@ -110,7 +110,6 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
| Add vsock device to the VM | `/vm.add-vsock` | `/schemas/VsockConfig` | `/schemas/PciDeviceInfo` | The VM is booted |
| Remove device from the VM | `/vm.remove-device` | `/schemas/VmRemoveDevice` | N/A | The VM is booted |
| Dump the VM counters | `/vm.counters` | N/A | `/schemas/VmCounters` | The VM is booted |
| Inject an NMI | `/vm.nmi` | N/A | N/A | The VM is booted |
| Prepare to receive a migration | `/vm.receive-migration` | `/schemas/ReceiveMigrationData` | N/A | N/A |
| Start to send migration to target | `/vm.send-migration` | `/schemas/SendMigrationData` | N/A | The VM is booted and (shared mem or hugepages enabled) |
@@ -148,7 +147,7 @@ We want to create a virtual machine with the following characteristics:
`/opt/clh/images/focal-server-cloudimg-amd64.raw`
```shell
#!/usr/bin/env bash
#!/bin/bash
curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-X PUT 'http://localhost/api/v1/vm.create' \
@@ -168,7 +167,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
Once the VM is created, we can boot it:
```shell
#!/usr/bin/env bash
#!/bin/bash
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.boot'
```
@@ -178,7 +177,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1
We can fetch information about any VM, as soon as it's created:
```shell
#!/usr/bin/env bash
#!/bin/bash
curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-X GET 'http://localhost/api/v1/vm.info' \
@@ -190,7 +189,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
We can reboot a VM that's already booted:
```shell
#!/usr/bin/env bash
#!/bin/bash
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.reboot'
```
@@ -200,7 +199,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1
Once booted, we can shut a VM down from the REST API:
```shell
#!/usr/bin/env bash
#!/bin/bash
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.shutdown'
```
@@ -296,7 +295,7 @@ From the CLI, one can:
The REST API, D-Bus API and the CLI all rely on a common, [internal API](#internal-api).
The CLI options are parsed by the
[clap crate](https://docs.rs/clap/4.3.11/clap/) and then translated into
[argh crate](https://docs.rs/argh/latest/argh/) and then translated into
[internal API](#internal-api) commands.
The REST API is processed by an HTTP thread using the
@@ -328,7 +327,7 @@ As a summary, the REST API, the D-Bus API and the CLI are essentially frontends
| | +------------------------+
| +----------+ | VMM
| CLI | | |
+----------->+ clap +--------------+
+----------->+ argh +--------------+
| |
+----------+
@@ -386,7 +385,7 @@ APIs work together, let's look at a complete VM creation flow, from the
[REST API](#rest-api) in order to creates a virtual machine:
```
shell
#!/usr/bin/env bash
#!/bin/bash
curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-X PUT 'http://localhost/api/v1/vm.create' \

View File

@@ -54,7 +54,7 @@ Value is a boolean set to `off` by default.
_Example_
```
--balloon size=2G,deflate_on_oom=on
--ballloon size=2G,deflate_on_oom=on
```
### `free_page_reporting`
@@ -72,5 +72,5 @@ Value is a boolean set to `off` by default.
_Example_
```
--balloon size=0,free_page_reporting=on
--ballloon size=0,free_page_reporting=on
```

View File

@@ -1,81 +0,0 @@
# Code coverage
LLVM provides a set of tools to collect code coverage data and present the data
in human-consumable forms.
## Building a suitable binary
The compiler flag to generate code coverage data has been stabilized since Rust
1.60.
An instrumented binary can be built with the following command:
```shell
cargo clean && RUSTFLAGS='-C instrument-coverage' cargo build
```
Using either `debug` or `release` profile is fine. You will need to adjust
the path for some commands.
## Running the binary
Run the binary as you normally would. When the process exits, you will see
files with the prefix `profraw`.
Multiple runs of the same binary will produce multiple `profraw` files.
The more diverse the runs are, the better. Try to exercise different features
as much as possible.
## Combining raw data
Raw data files can be combined with `llvm-profdata`.
```shell
rustup component add llvm-tools-preview
# Assuming profraw files reside in the current directory and its children directories
find . -name '*.profraw' -exec llvm-profdata merge -sparse {} -o coverage.profdata \;
```
A file named `coverage.profdata` will be generated.
## Generating HTML files for human consumption
This can be done either with LLVM or `grcov`.
Here is an example using grcov.
```shell
cargo install grcov
# Assuming the profdata file is in the top level directory of the Cloud Hypervisor repository
grcov . --binary-path ./target/x86_64-unknown-linux-gnu/release -s . -t html --branch --ignore-not-existing -o coverage-html-output/
```
You can then open the `index.html` file under coverage-html-output to see the
results.
## Notes on running the in-tree integration tests and unit tests
Please set RUSTFLAGS the same way while invoking `dev_cli.sh`. The script will
pass RUSTFLAGS to the container.
Since the `profraw` files are generated from within the container, the file
paths embedded in the data files are going to be different. It is easier to do
the data processing from within the container if you don't want to fight the
tool chain.
```shell
# Get a shell
./scripts/dev_cli.sh shell
# Install llvm-tools-preview for llvm-profdata
rustup component add llvm-tools-preview
# Merge data files by using the following command
find . -name '*.profraw' -exec `rustc --print sysroot`/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse {} -o coverage.profdata \;
# As of writing, the container has Rust 1.67.1. It is too old for grcov.
rustup install stable
cargo +stable install grcov
# Run grcov as usual
grcov . --binary-path ./target/x86_64-unknown-linux-gnu/release -s . -t html --branch --ignore-not-existing -o coverage-html-output/
```

View File

@@ -170,7 +170,7 @@ We usually start from one of the custom cloud image we have previously created
but we can use a stock cloud image as well.
```bash
wget https://ch-images.azureedge.net/jammy-server-cloudimg-amd64-custom-20230119-0.raw
wget https://cloud-hypervisor.azureedge.net/jammy-server-cloudimg-amd64-custom-20230119-0.raw
mv jammy-server-cloudimg-amd64-custom-20230119-0.raw jammy-server-cloudimg-amd64-nvidia.raw
```
@@ -326,4 +326,4 @@ VM will be booted with this image.
```
sudo cloud-init clean
```
```

View File

@@ -1,16 +1,7 @@
# `cloud-hypervisor` debug IO ports
# `cloud-hypervisor` debug IO port
When running x86 guests, `cloud-hypervisor` provides different kinds of debug ports:
- [`0x80` debug port](https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
- Debug console (by default at `0xe9`).
- Firmware debug port at `0x402`.
All of them can be used to trace user-defined guest events and all of them can
be used simultaneously.
## Debug Ports Overview
### `0x80` I/O port
`cloud-hypervisor` uses the [`0x80`](https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html)
I/O port to trace user defined guest events.
Whenever the guest write one byte between `0x0` and `0xF` on this particular
I/O port, `cloud-hypervisor` will log and timestamp that event at the `debug`
@@ -39,13 +30,13 @@ guest will have `cloud-hypervisor` generate timestamped logs of all those steps.
That provides a basic but convenient way of measuring not only the overall guest
boot time but all intermediate steps as well.
#### Logging
## Logging
Assuming parts of the guest software stack have been instrumented to use the
`cloud-hypervisor` debug I/O port, we may want to gather the related logs.
To do so we need to start `cloud-hypervisor` with the right debug level
(`-vvv`). It is also recommended to have it log into a dedicated file in order
(`-v -v -v`). It is also recommended to have it log into a dedicated file in order
to easily grep for the tracing logs (e.g.
`--log-file /tmp/cloud-hypervisor.log`):
@@ -57,7 +48,7 @@ to easily grep for the tracing logs (e.g.
--memory size=1024M \
--rng \
--log-file /tmp/ch-fw.log \
-vvv
-v -v -v
```
After booting the guest, we then have to grep for the debug I/O port traces in
@@ -68,29 +59,3 @@ $ grep "Debug I/O port" /tmp/ch-fw.log
cloud-hypervisor: 19.762449ms: DEBUG:vmm/src/vm.rs:510 -- [Debug I/O port: Firmware code 0x0] 0.019004 seconds
cloud-hypervisor: 403.499628ms: DEBUG:vmm/src/vm.rs:510 -- [Debug I/O port: Firmware code 0x1] 0.402744 seconds
```
### Debug console port
The debug console is inspired by QEMU and Bochs, which have a similar feature.
By default, the I/O port `0xe9` is used. This port can be configured like a
console. Thus, it can print to a tty, a file, or a pty, for example.
### Firmware debug port
The firmware debug port is also a simple port that prints all bytes written to
it. The firmware debug port only prints to stdout.
## When do I need these ports?
The ports are on the one hand interesting for firmware or kernel developers, as
they provide an easy way to print debug information from within a guest.
Furthermore, you can patch "normal" software to measure certain events, such as
the boot time of a guest.
## Which port should I choose?
The `0x80` debug port and the port of the firmware debug device are always
available. The debug console must be activated via the command line, but
provides more configuration options.
You can use different ports for different aspect of your logging messages.

View File

@@ -8,7 +8,7 @@ To enable debugging with GDB, build with the `guest_debug` feature enabled:
cargo build --features guest_debug
```
To use the `--gdb` option, specify the Unix Domain Socket with `--path` that Cloud Hypervisor will use to communicate with the host's GDB:
To use the `--gdb` option, specify the Unix Domain Socket with `path` that Cloud Hypervisor will use to communicate with the host's GDB:
```bash
./cloud-hypervisor \

View File

@@ -27,16 +27,16 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
--rng \
--api-socket=/tmp/ch-socket
--api-socket /tmp/ch-socket
$ popd
```
Notice the addition of `--api-socket=/tmp/ch-socket` and a `max` parameter on `--cpus boot=4,max=8`.
Notice the addition of `--api-socket /tmp/ch-socket` and a `max` parameter on `--cpus boot=4,max=8`.
To ask the VMM to add additional vCPUs then use the resize API:
```shell
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 8
./ch-remote --api-socket /tmp/ch-socket resize --cpus 8
```
The extra vCPU threads will be created and advertised to the running kernel. The kernel does not bring up the CPUs immediately and instead the user must "online" them from inside the VM:
@@ -56,7 +56,7 @@ After a reboot the added CPUs will remain.
Removing CPUs works similarly by reducing the number in the "desired_vcpus" field of the reisze API. The CPUs will be automatically offlined inside the guest so there is no need to run any commands inside the guest:
```shell
./ch-remote --api-socket=/tmp/ch-socket resize --cpus 2
./ch-remote --api-socket /tmp/ch-socket resize --cpus 2
```
As per adding CPUs to the guest, after a reboot the VM will be running with the reduced number of vCPUs.
@@ -85,7 +85,7 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--memory size=1024M,hotplug_size=8192M \
--net "tap=,mac=,ip=,mask=" \
--rng \
--api-socket=/tmp/ch-socket
--api-socket /tmp/ch-socket
$ popd
```
@@ -98,7 +98,7 @@ root@ch-guest ~ # echo online | sudo tee /sys/devices/system/memory/auto_online_
To ask the VMM to expand the RAM for the VM:
```shell
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
./ch-remote --api-socket /tmp/ch-socket resize --memory 3G
```
The new memory is now available to use inside the VM:
@@ -134,14 +134,14 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--disk path=focal-server-cloudimg-amd64.raw \
--memory size=1024M,hotplug_size=8192M,hotplug_method=virtio-mem \
--net "tap=,mac=,ip=,mask=" \
--api-socket=/tmp/ch-socket
--api-socket /tmp/ch-socket
$ popd
```
To ask the VMM to expand the RAM for the VM (request is in bytes):
```shell
./ch-remote --api-socket=/tmp/ch-socket resize --memory 3G
./ch-remote --api-socket /tmp/ch-socket resize --memory 3G
```
The new memory is now available to use inside the VM:
@@ -172,17 +172,17 @@ $ ./cloud-hypervisor/target/release/cloud-hypervisor \
--cpus boot=4 \
--memory size=1024M \
--net "tap=,mac=,ip=,mask=" \
--api-socket=/tmp/ch-socket
--api-socket /tmp/ch-socket
```
Notice the addition of `--api-socket=/tmp/ch-socket`.
Notice the addition of `--api-socket /tmp/ch-socket`.
### Add VFIO Device
To ask the VMM to add additional VFIO device then use the `add-device` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-device path=/sys/bus/pci/devices/0000:01:00.0/
./ch-remote --api-socket /tmp/ch-socket add-device path=/sys/bus/pci/devices/0000:01:00.0/
```
### Add Disk Device
@@ -190,7 +190,7 @@ To ask the VMM to add additional VFIO device then use the `add-device` API.
To ask the VMM to add additional disk device then use the `add-disk` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-disk path=/foo/bar/cloud.img
./ch-remote --api-socket /tmp/ch-socket add-disk path=/foo/bar/cloud.img
```
### Add Fs Device
@@ -198,7 +198,7 @@ To ask the VMM to add additional disk device then use the `add-disk` API.
To ask the VMM to add additional fs device then use the `add-fs` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-fs tag=myfs,socket=/foo/bar/virtiofs.sock
./ch-remote --api-socket /tmp/ch-socket add-fs tag=myfs,socket=/foo/bar/virtiofs.sock
```
### Add Net Device
@@ -206,7 +206,7 @@ To ask the VMM to add additional fs device then use the `add-fs` API.
To ask the VMM to add additional network device then use the `add-net` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-net tap=chtap0
./ch-remote --api-socket /tmp/ch-socket add-net tap=chtap0
```
### Add Pmem Device
@@ -214,7 +214,7 @@ To ask the VMM to add additional network device then use the `add-net` API.
To ask the VMM to add additional PMEM device then use the `add-pmem` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-pmem file=/foo/bar.cloud.img
./ch-remote --api-socket /tmp/ch-socket add-pmem file=/foo/bar.cloud.img
```
### Add Vsock Device
@@ -222,7 +222,7 @@ To ask the VMM to add additional PMEM device then use the `add-pmem` API.
To ask the VMM to add additional vsock device then use the `add-vsock` API.
```shell
./ch-remote --api-socket=/tmp/ch-socket add-vsock cid=3,socket=/foo/bar/vsock.sock
./ch-remote --api-socket /tmp/ch-socket add-vsock cid=3,socket=/foo/bar/vsock.sock
```
### Common Across All PCI Devices
@@ -244,7 +244,7 @@ After a reboot the added PCI device will remain.
Removing a PCI device works the same way for all kind of PCI devices. The unique identifier related to the device must be provided. This identifier can be provided by the user when adding the new device, or by default Cloud Hypervisor will assign one.
```shell
./ch-remote --api-socket=/tmp/ch-socket remove-device _disk0
./ch-remote --api-socket /tmp/ch-socket remove-device _disk0
```
As per adding a PCI device to the guest, after a reboot the VM will be running without the removed PCI device.

View File

@@ -103,7 +103,7 @@ firmware:
### TDShim
> **Note**
> The latest version of TDShim being tested is [_v0.8.0_](https://github.com/confidential-containers/td-shim/releases/tag/v0.8.0).
> The latest version of TDShim being tested is [_66bb334_](https://github.com/confidential-containers/td-shim/tree/66bb33451befbf1291abe3cfea7ee9e99d922b0d).
This is a lightweight version of the TDVF, written in Rust and designed for
direct kernel boot, which is useful for containers use cases.
@@ -113,7 +113,7 @@ and `LLVM` first. The TDshim can be build as follows:
```bash
git clone https://github.com/confidential-containers/td-shim
cd td-shim
git checkout v0.8.0
git checkout 66bb33451befbf1291abe3cfea7ee9e99d922b0d
cargo install cargo-xbuild
export CC=clang
export AR=llvm-ar
@@ -121,13 +121,15 @@ export CC_x86_64_unknown_none=clang
export AR_x86_64_unknown_none=llvm-ar
git submodule update --init --recursive
./sh_script/preparation.sh
cargo image --release
cargo xbuild -p td-shim --target x86_64-unknown-none --release --features=main,tdx
cargo run -p td-shim-tools --bin td-shim-ld --features=linker -- target/x86_64-unknown-none/release/ResetVector.bin target/x86_64-unknown-none/release/td-shim -o target/release/final.bin
```
If debug logs from the TDShim is needed, here are the alternative
commands:
```bash
cargo image
cargo xbuild -p td-shim --target x86_64-unknown-none --features=main,tdx
cargo run -p td-shim-tools --bin td-shim-ld --features=linker -- target/x86_64-unknown-none/debug/ResetVector.bin target/x86_64-unknown-none/debug/td-shim -o target/debug/final.bin
```
And run a TDX VM by providing the firmware previously built, along with a guest

View File

@@ -42,14 +42,3 @@ actual rate limit users get can be as low as
generally advisable to keep `bw/ops_refill_time` larger than `100 ms`
(`cool_down_time`) to make sure the actual rate limit is close to users'
expectation ("refill-rate").
## Rate Limit Groups
It is possible to throttle the aggregate bandwidth or operations
of multiple virtio-blk devices using a `rate_limit_group`. virtio-blk devices may be
dynamically added and removed from a `rate_limit_group`. The following example
demonstrates how to throttle the aggregate bandwidth of two disks to 10 MiB/s.
```
--disk path=disk0.raw,rate_limit_group=group0 \
path=disk1.raw,rate_limit_group=group0 \
--rate-limit-group bw_size=1048576,bw_refill_time,bw_refill_time=100
```

View File

@@ -60,7 +60,10 @@ implement a full emulation of a physical IOMMU.
### Kernel
As of Kernel 5.14, virtio-iommu is available for both X86-64 and Aarch64.
Since virtio-iommu has landed partially into the version 5.3 of the Linux
kernel, a special branch is needed to get things working with Cloud Hypervisor.
By partially, we are talking about x86 specifically, as it is already fully
functional for ARM architectures.
## Usage
@@ -242,7 +245,7 @@ e.g.
```bash
./cloud-hypervisor \
--api-socket=/tmp/api \
--api-socket /tmp/api \
--cpus boot=1 \
--memory size=4G,hugepages=on \
--disk path=focal-server-cloudimg-amd64.raw \
@@ -257,7 +260,7 @@ requiring the IOMMU then may be hotplugged:
e.g.
```bash
./ch-remote --api-socket=/tmp/api add-device path=/sys/bus/pci/devices/0000:00:04.0,iommu=on,pci_segment=1
./ch-remote --api-socket /tmp/api add-device path=/sys/bus/pci/devices/0000:00:04.0,iommu=on,pci_segment=1
```
Devices that cannot be placed behind an IOMMU (e.g. lacking an `iommu=` option)

View File

@@ -16,22 +16,22 @@ $ target/release/cloud-hypervisor
--disk path=~/workloads/focal.raw \
--cpus boot=1 --memory size=1G,shared=on \
--cmdline "root=/dev/vda1 console=ttyS0" \
--serial tty --console off --api-socket=/tmp/api1
--serial tty --console off --api-socket /tmp/api1
```
Launch the destination VM from the same directory (on the host machine):
```bash
$ target/release/cloud-hypervisor --api-socket=/tmp/api2
$ target/release/cloud-hypervisor --api-socket /tmp/api2
```
Get ready for receiving migration for the destination VM (on the host machine):
```bash
$ target/release/ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock
$ target/release/ch-remote --api-socket /tmp/api2 receive-migration unix:/tmp/sock
```
Start to send migration for the source VM (on the host machine):
```bash
$ target/release/ch-remote --api-socket=/tmp/api1 send-migration --local unix:/tmp/sock
$ target/release/ch-remote --api-socket /tmp/api1 send-migration --local unix:/tmp/sock
```
When the above commands completed, the source VM should be successfully
@@ -51,7 +51,7 @@ $ sudo /target/release/cloud-hypervisor \
--cpus boot=1 --memory size=512M \
--kernel vmlinux \
--cmdline "root=/dev/vda1 console=ttyS0" \
--disk path=focal-1.raw path=focal-nested.raw path=tmp.img\
--disk path=focal-1.raw path=focal-nested.raw --disk path=tmp.img\
--net ip=192.168.101.1
```
@@ -63,7 +63,7 @@ $ sudo /target/release/cloud-hypervisor \
--cpus boot=1 --memory size=512M \
--kernel vmlinux \
--cmdline "root=/dev/vda1 console=ttyS0" \
--disk path=focal-2.raw path=focal-nested.raw path=tmp.img\
--disk path=focal-2.raw path=focal-nested.raw --disk path=tmp.img\
--net ip=192.168.102.1
```
@@ -74,8 +74,8 @@ vm-1:~$ sudo ./cloud-hypervisor \
--memory size=128M \
--kernel vmlinux \
--cmdline "console=ttyS0 root=/dev/vda1" \
--disk path=/dev/vdb path=/dev/vdc \
--api-socket=/tmp/api1 \
--disk path=/dev/vdb --disk path=/dev/vdc \
--api-socket /tmp/api1 \
--net ip=192.168.100.1
vm-1:~$ # setup the guest network if needed
vm-1:~$ sudo ip addr add 192.168.101.2/24 dev ens4
@@ -108,7 +108,7 @@ echo "tmp = $tmp"
Launch the nested destination VM (inside the guest OS of the VM 2):
```bash
vm-2:~$ sudo ./cloud-hypervisor --api-socket=/tmp/api2
vm-2:~$ sudo ./cloud-hypervisor --api-socket /tmp/api2
vm-2:~$ # setup the guest network with the following commands if needed
vm-2:~$ sudo ip addr add 192.168.102.2/24 dev ens4
vm-2:~$ sudo ip link set up dev ens4
@@ -122,7 +122,7 @@ vm-2:~$ ping 192.168.101.2 # This should succeed
Get ready for receiving migration for the nested destination VM (inside
the guest OS of the VM 2):
```bash
vm-2:~$ sudo ./ch-remote --api-socket=/tmp/api2 receive-migration unix:/tmp/sock2
vm-2:~$ sudo ./ch-remote --api-socket /tmp/api2 receive-migration unix:/tmp/sock2
vm-2:~$ sudo socat TCP-LISTEN:6000,reuseaddr UNIX-CLIENT:/tmp/sock2
```
@@ -130,7 +130,7 @@ Start to send migration for the nested source VM (inside the guest OS of
the VM 1):
```bash
vm-1:~$ sudo socat UNIX-LISTEN:/tmp/sock1,reuseaddr TCP:192.168.102.2:6000
vm-1:~$ sudo ./ch-remote --api-socket=/tmp/api1 send-migration unix:/tmp/sock1
vm-1:~$ sudo ./ch-remote --api-socket /tmp/api1 send-migration unix:/tmp/sock1
```
When the above commands completed, the source VM should be successfully

View File

@@ -38,6 +38,6 @@ This level is for the benefit of developers. It should be used for sporadic and
### `debug!()`
Use `-vv` to enable.
Use `-v -v` to enable.
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.
For the most verbose of logging messages. It is acceptable to "spam" the log with repeated invocations of the same message. This level of logging would be combined with `--log-file`.

View File

@@ -516,7 +516,7 @@ different distances, it can be described with the following example.
_Example_
```
--numa guest_numa_id=0,distances=[1@15,2@25] guest_numa_id=1,distances=[0@15,2@20] guest_numa_id=2,distances=[0@25,1@20]
--numa guest_numa_id=0,distances=[1@15,2@25] --numa guest_numa_id=1,distances=[0@15,2@20] guest_numa_id=2,distances=[0@25,1@20]
```
### `memory_zones`
@@ -540,14 +540,14 @@ demarcate the list.
Note that a memory zone must belong to a single NUMA node. The following
configuration is incorrect, therefore not allowed:
`--numa guest_numa_id=0,memory_zones=mem0 guest_numa_id=1,memory_zones=mem0`
`--numa guest_numa_id=0,memory_zones=mem0 --numa guest_numa_id=1,memory_zones=mem0`
_Example_
```
--memory size=0
--memory-zone id=mem0,size=1G id=mem1,size=1G id=mem2,size=1G
--numa guest_numa_id=0,memory_zones=[mem0,mem2] guest_numa_id=1,memory_zones=mem1
--memory-zone id=mem0,size=1G id=mem1,size=1G --memory-zone id=mem2,size=1G
--numa guest_numa_id=0,memory_zones=[mem0,mem2] --numa guest_numa_id=1,memory_zones=mem1
```
### `sgx_epc_sections`
@@ -567,21 +567,12 @@ _Example_
```
--sgx-epc id=epc0,size=32M id=epc1,size=64M id=epc2,size=32M
--numa guest_numa_id=0,sgx_epc_sections=epc1 guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
--numa guest_numa_id=0,sgx_epc_sections=epc1 --numa guest_numa_id=1,sgx_epc_sections=[epc0,epc2]
```
### PCI bus
Cloud Hypervisor supports guests with one or more PCI segments. The default PCI segment always
has affinity to NUMA node 0. Be default, all other PCI segments have afffinity to NUMA node 0.
The user may configure the NUMA affinity for any additional PCI segments.
_Example_
```
--platform num_pci_segments=2
--memory-zone size=16G,host_numa_node=0,id=mem0
--memory-zone size=16G,host_numa_node=1,id=mem1
--numa guest_numa_id=0,memory_zones=mem0,pci_segments=[0]
--numa guest_numa_id=1,memory_zones=mem1,pci_segments=[1]
```
Cloud Hypervisor supports only one PCI bus, which is why it has been tied to
the NUMA node 0 by default. It is the user responsibility to organize the NUMA
nodes correctly so that vCPUs and guest RAM which should be located on the same
NUMA node as the PCI bus end up on the NUMA node 0.

View File

@@ -1,48 +0,0 @@
# Microsoft Hypervisor
The Microsoft Hypervisor is a Type 1 hypervisor which runs on x64 and ARM64 architectures. As the foundation of the Hyper-V virtualization stack, it runs millions of Linux and Windows guests in Azure and on-premises deployments. It supports nested virtualization, and security features like AMD's SEV-SNP. It also supports various features in Windows such as [Device guard and confidential guard](https://techcommunity.microsoft.com/t5/iis-support-blog/windows-10-device-guard-and-credential-guard-demystified/ba-p/376419), and [WSL2](https://docs.microsoft.com/en-us/windows/wsl/wsl2-faq)
Since 2020, Microsoft has been releasing open-source components to support Linux running as root partition on the Microsoft Hypervisor.
1. Kernel patches to support Linux booting as root partition
2. A Linux kernel driver exposing an IOCTL interface for managing guest partitions, via a device node - /dev/mshv
3. Rust bindings and IOCTL wrappers
4. IGVM related crates
## Components
The following components are related to MSHV support with Cloud-Hypervisor:
* [igvm-crates](https://github.com/microsoft/igvm) : Parsing IGVM file
* [mshv-crates](https://github.com/rust-vmm/mshv) : Rust crates to interact with kernel module (/dev/mshv)
* [igvm-tooling](https://github.com/microsoft/igvm-tooling) : Tool to generate IGVM file
## IGVM
Independent Guest Virtual Machine (IGVM) file format.The format specification can be found in the igvm_defs crate, with a Rust implementation of the binary format in the igvm crate.
The IGVM file format is designed to encapsulate all information required to launch a virtual machine on any given virtualization stack, with support for different isolation technologies such as AMD SEV-SNP and Intel TDX.
At a conceptual level, this file format is a set of commands created by the tool that generated the file, used by the loader to construct the initial guest state. The file format also contains measurement information that the underlying platform will use to confirm that the file was loaded correctly and signed by the appropriate authorities.
Cloud Hypervisor can be built using igvm feature flag along with mshv and/or sev-snp. IGVM only works with MSHV.
## SEV-SNP
AMD's [Secure Encrypted Virtualization (SEV)](https://www.amd.com/en/developer/sev.html) and extensions such as Secure Nested Paging (SEV-SNP) encrypt memory and restrict access to a guest VM's memory and registers, securing it against a compromised hypervisor or VMM. They utilize the Platform Security Processor (PSP) to store keys and encrypt/decrypt the data. Microsoft has been continuously adding/improving support for SEV-SNP on Microsoft Hyper-V. Cloud-Hypervisor can be built with the sev_snp feature including mshv and igvm feature.
## Use Cases
Cloud Hypervisor can be built to run on an MSHV root partition by enabling the mshv feature, e.g.:
```cargo build --locked --all --all-targets --no-default-features --tests --examples --features mshv```
Cloud Hypervisor on MSHV can boot Linux guests using an IGVM file. IGVM feature depends on mshv for running legacy VMs.e.g.:
```cargo build --locked --all --all-targets --no-default-features --tests --examples --features igvm```
For running confidential VMs on mshv, you will only need to enable sev_snp, it requires and enables mshv and igvm automatically, eg.:
```cargo build --locked --all --all-targets --no-default-features --tests --examples --features sev_snp```

View File

@@ -25,7 +25,7 @@ $ perf record -g target/profiling/cloud-hypervisor \
--cpus boot=1 --memory size=1G \
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
--serial tty --console off \
--api-socket=/tmp/api1
--api-socket /tmp/api1
```
For analysing the samples:
@@ -52,5 +52,5 @@ $ perf record --call-graph lbr --all-user --user-callchains -g target/release/cl
--cpus boot=1 --memory size=1G \
--cmdline "root=/dev/pmem0p1 console=ttyS0" \
--serial tty --console off \
--api-socket=/tmp/api1
--api-socket /tmp/api1
```

View File

@@ -25,14 +25,14 @@ First thing, we must run a Cloud Hypervisor VM:
At any point in time when the VM is running, one might choose to pause it:
```bash
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock pause
./ch-remote --api-socket /tmp/cloud-hypervisor.sock pause
```
Once paused, the VM can be safely snapshot into the specified directory and
using the following command:
```bash
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock snapshot file:///home/foo/snapshot
./ch-remote --api-socket /tmp/cloud-hypervisor.sock snapshot file:///home/foo/snapshot
```
Given the directory was present on the system, the snapshot will succeed and
@@ -79,7 +79,7 @@ Or using two different commands from two terminals:
./cloud-hypervisor --api-socket /tmp/cloud-hypervisor.sock
# Second terminal
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock restore source_url=file:///home/foo/snapshot
./ch-remote --api-socket /tmp/cloud-hypervisor.sock restore source_url=file:///home/foo/snapshot
```
Remember the VM is restored in a `paused` state, which was the VM's state when
@@ -87,7 +87,7 @@ it was snapshot. For this reason, one must explicitly `resume` the VM before to
start using it.
```bash
./ch-remote --api-socket=/tmp/cloud-hypervisor.sock resume
./ch-remote --api-socket /tmp/cloud-hypervisor.sock resume
```
At this point, the VM is fully restored and is identical to the VM which was

View File

@@ -122,64 +122,4 @@ $ ls /sys/kernel/iommu_groups/22/devices/
This means these two devices are under the same IOMMU group 22. In such case,
it is important to bind both devices to VFIO and pass them both through the
VM, otherwise this could cause some functional and security issues.
### Advanced Configuration Options
When using NVIDIA GPUs in a VFIO passthrough configuration, advanced
configuration options are supported to enable GPUDirect P2P DMA over
PCIe. When enabled, loads and stores between GPUs use native PCIe
peer-to-peer transactions instead of a shared memory buffer. This drastically
decreases P2P latency between GPUs. This functionality is supported by
cloud-hypervisor on NVIDIA Turing, Ampere, Hopper, and Lovelace GPUs.
The NVIDIA driver does not enable GPUDirect P2P over PCIe within guests
by default because hardware support for routing P2P TLP between PCIe root
ports is optional. PCIe P2P should always be supported between devices
on the same PCIe switch. The `x_nv_gpudirect_clique` config argument may
be used to signal support for PCIe P2P traffic between NVIDIA VFIO endpoints.
The guest driver assumes that P2P traffic is supported between all endpoints
that are part of the same clique.
```
--device path=/sys/bus/pci/devices/0000:01:00.0/,x_nv_gpudirect_clique=0
```
The following command can be run on the guest to verify that GPUDirect P2P is
correctly enabled.
```
nvidia-smi topo -p2p r
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
GPU0 X OK OK OK OK OK OK OK
GPU1 OK X OK OK OK OK OK OK
GPU2 OK OK X OK OK OK OK OK
GPU3 OK OK OK X OK OK OK OK
GPU4 OK OK OK OK X OK OK OK
GPU5 OK OK OK OK OK X OK OK
GPU6 OK OK OK OK OK OK X OK
GPU7 OK OK OK OK OK OK OK X
```
Some VFIO devices have a 32-bit mmio BAR. When using many such devices, it is
possible to exhaust the 32-bit mmio space available on a PCI segment. The
following example demonstrates an example device with a 16 MiB 32-bit mmio BAR.
```
lspci -s 0000:01:00.0 -v
0000:01:00.0 3D controller: NVIDIA Corporation Device 26b9 (rev a1)
[...]
Memory at f9000000 (32-bit, non-prefetchable) [size=16M]
Memory at 46000000000 (64-bit, prefetchable) [size=64G]
Memory at 48040000000 (64-bit, prefetchable) [size=32M]
[...]
```
When using multiple PCI segments, the 32-bit mmio address space available to
be allocated to VFIO devices is equally split between all PCI segments by
default. This can be tuned with the `--pci-segment` flag. The following example
demonstrates a guest with two PCI segments. 2/3 of the 32-bit mmio address
space is available for use by devices on PCI segment 0 and 1/3 of the 32-bit
mmio address space is available for use by devices on PCI segment 1.
```
--platform num_pci_segments=2
--pci-segment pci_segment=0,mmio32_aperture_weight=2
--pci-segment pci_segment=1,mmio32_aperture_weight=1
```
VM, otherwise this could cause some functional and security issues.

View File

@@ -94,7 +94,7 @@ VMs run in client mode. They connect to the socket created by the `dpdkvhostuser
--memory size=1024M,hugepages=on,shared=true \
--kernel linux/arch/x86/boot/compressed/vmlinux.bin \
--cmdline "console=ttyS0 root=/dev/vda1 rw iommu=off" \
--disk path=images/focal-server-cloudimg-amd64.raw vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
--disk path=images/focal-server-cloudimg-amd64.raw --disk vhost_user=true,socket=/var/tmp/vhost.1,num_queues=4,queue_size=128 \
--console off \
--serial tty \
--rng

View File

@@ -2,8 +2,8 @@
The purpose of this document is to illustrate how to test vhost-user-net
in cloud-hypervisor with OVS/DPDK as the backend. This document was
tested with Open vSwitch v2.17.8, DPDK v21.11.4, and Cloud Hypervisor
v37.0 on Ubuntu 22.04.3 (host kernel v5.15.0).
tested with Open vSwitch v2.13.1, DPDK v19.11.3, and Cloud Hypervisor
v15.0 on Ubuntu 20.04.1 (host kernel v5.4.0).
## Framework
@@ -74,8 +74,8 @@ Here is an example how to create a bridge and add two DPDK ports to it
# create a bridge
ovs-vsctl add-br ovsbr0 -- set bridge ovsbr0 datapath_type=netdev
# create two DPDK ports and add them to the bridge
ovs-vsctl add-port ovsbr0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuserclient options:vhost-server-path=/tmp/vhost-user1
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2 type=dpdkvhostuserclient options:vhost-server-path=/tmp/vhost-user2
ovs-vsctl add-port ovsbr0 vhost-user1 -- set Interface vhost-user1 type=dpdkvhostuser
ovs-vsctl add-port ovsbr0 vhost-user2 -- set Interface vhost-user2 type=dpdkvhostuser
# set the number of rx queues
ovs-vsctl set Interface vhost-user1 options:n_rxq=2
ovs-vsctl set Interface vhost-user2 options:n_rxq=2
@@ -92,7 +92,7 @@ VMs run in client mode. They connect to the socket created by the `dpdkvhostuser
--kernel vmlinux \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--net mac=52:54:00:02:d9:01,vhost_user=true,socket=/tmp/vhost-user1,num_queues=4,vhost_mode=server
--net mac=52:54:00:02:d9:01,vhost_user=true,socket=/var/run/openvswitch/vhost-user1,num_queues=4
# From another terminal. We need to give the cloud-hypervisor binary the NET_ADMIN capabilities for it to set TAP interfaces up on the host.
./cloud-hypervisor \
@@ -101,21 +101,21 @@ VMs run in client mode. They connect to the socket created by the `dpdkvhostuser
--kernel vmlinux \
--cmdline "console=ttyS0 console=hvc0 root=/dev/vda1 rw" \
--disk path=focal-server-cloudimg-amd64.raw \
--net mac=52:54:20:11:C5:02,vhost_user=true,socket=/tmp/vhost-user2,num_queues=4,vhost_mode=server
--net mac=52:54:20:11:C5:02,vhost_user=true,socket=/var/run/openvswitch/vhost-user2,num_queues=4
```
_Setup VM1_
```bash
# From inside the guest
sudo ip addr add 172.100.0.1/24 dev ens3
sudo ip link set up dev ens3
sudo ip addr add 172.100.0.1/24 dev ens2
sudo ip link set up dev ens2
```
_Setup VM2_
```bash
# From inside the guest
sudo ip addr add 172.100.0.2/24 dev ens3
sudo ip link set up dev ens3
sudo ip addr add 172.100.0.2/24 dev ens2
sudo ip link set up dev ens2
```
_Ping VM1 from VM2_

View File

@@ -1,6 +1,6 @@
# VSOCK support
VSOCK provides a way for guest and host to communicate through a socket. `cloud-hypervisor` only supports stream VSOCK sockets.
VSOCK provides a way for guest and host to communicate through a socket. VSOCK sockets support both stream and datagram types.
The `virtio-vsock` is based on the [Firecracker](https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md) implementation, where additional details can be found.
@@ -50,14 +50,14 @@ The examples use __socat__ `>=1.7.4` to illustrate the VSOCK functionality. Howe
### Connecting from Host to Guest
The guest starts to listen on the defined port:
The host starts to listen on the defined port:
`$ socat - VSOCK-LISTEN:1234`
Once the guest is listening, the host can send data:
Once the host is listening, the guest can send data:
`echo -e "CONNECT 1234\\nHello from host!" | socat - UNIX-CONNECT:/tmp/ch.vsock
Note the string `CONNECT <port>` prepended to the actual data. It is possible for the guest to start listening on different ports, thus the specific command is needed to instruct VSOCK to which listener the host wants to connect. It needs to be sent once per connection. Once the connection established, data transfers can take place directly.
### Connecting from Guest to Host

View File

@@ -49,7 +49,7 @@ qemu-system-x86_64 \
-m 4G \
-bios ./$OVMF_DIR/OVMF_CODE.fd \
-cdrom ./$WIN_ISO_FILE \
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom \
-drive file=./$VIRTIO_ISO_FILE,index=0,media=cdrom
-drive if=none,id=root,file=./$IMG_FILE \
-device virtio-blk-pci,drive=root,disable-legacy=on \
-device virtio-net-pci,netdev=mynet0,disable-legacy=on \

View File

@@ -5,8 +5,8 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
[dependencies]
flume = "0.11.0"
libc = "0.2.153"
once_cell = "1.19.0"
serde = { version = "1.0.197", features = ["rc", "derive"] }
serde_json = "1.0.115"
flume = "0.10.14"
libc = "0.2.147"
once_cell = "1.18.0"
serde = { version = "1.0.168", features = ["rc", "derive"] }
serde_json = "1.0.107"

588
fuzz/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,32 +8,32 @@ edition = "2021"
[package.metadata]
cargo-fuzz = true
[features]
igvm = []
[dependencies]
block = { path = "../block" }
devices = { path = "../devices" }
epoll = "4.3.3"
libc = "0.2.153"
epoll = "4.3.1"
libc = "0.2.148"
libfuzzer-sys = "0.4.7"
linux-loader = { version = "0.11.0", features = ["elf", "bzimage", "pe"] }
linux-loader = { version = "0.9.1", features = ["elf", "bzimage", "pe"] }
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
net_util = { path = "../net_util" }
once_cell = "1.19.0"
seccompiler = "0.4.0"
once_cell = "1.18.0"
seccompiler = "0.3.0"
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.11.0"
virtio-queue = "0.9.0"
vmm = { path = "../vmm" }
vmm-sys-util = "0.12.1"
vm-memory = "0.14.1"
vm-migration = { path = "../vm-migration" }
vmm-sys-util = "0.11.2"
vm-memory = "0.12.2"
vm-device = { path = "../vm-device" }
vm-virtio = { path = "../vm-virtio" }
[dependencies.cloud-hypervisor]
path = ".."
[patch.crates-io]
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
# Prevent this from interfering with workspaces
[workspace]
members = ["."]

View File

@@ -21,7 +21,6 @@ use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
use std::collections::BTreeMap;
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
@@ -50,7 +49,6 @@ fuzz_target!(|bytes| {
let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap();
let disk_file: File = unsafe { File::from_raw_fd(shm) };
let qcow_disk = Box::new(RawFileDiskSync::new(disk_file)) as Box<dyn DiskFile>;
let queue_affinity = BTreeMap::new();
let mut block = Block::new(
"tmp".to_owned(),
qcow_disk,
@@ -64,7 +62,6 @@ fuzz_target!(|bytes| {
None,
EventFd::new(EFD_NONBLOCK).unwrap(),
None,
queue_affinity,
)
.unwrap();

View File

@@ -6,6 +6,8 @@
use devices::legacy::Cmos;
use libc::EFD_NONBLOCK;
use libfuzzer_sys::fuzz_target;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use vm_device::BusDevice;
use vmm_sys_util::eventfd::EventFd;

View File

@@ -7,18 +7,9 @@ use libfuzzer_sys::fuzz_target;
use micro_http::Request;
use once_cell::sync::Lazy;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver};
use std::sync::{Arc, Mutex};
use std::thread;
use vm_migration::MigratableError;
use vmm::api::{
http::*, ApiRequest, RequestHandler, VmInfoResponse, VmReceiveMigrationData,
VmSendMigrationData, VmmPingResponse,
};
use vmm::config::RestoreConfig;
use vmm::vm::{Error as VmError, VmState};
use vmm::vm_config::*;
use vmm::api::{http::*, ApiRequest, ApiResponsePayload};
use vmm::{EpollContext, EpollDispatch};
use vmm_sys_util::eventfd::EventFd;
@@ -78,208 +69,6 @@ fn generate_request(bytes: &[u8]) -> Option<Request> {
Request::try_from(&request, None).ok()
}
struct StubApiRequestHandler;
impl RequestHandler for StubApiRequestHandler {
fn vm_create(&mut self, _: Arc<Mutex<VmConfig>>) -> Result<(), VmError> {
Ok(())
}
fn vm_boot(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_pause(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_resume(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_snapshot(&mut self, _: &str) -> Result<(), VmError> {
Ok(())
}
fn vm_restore(&mut self, _: RestoreConfig) -> Result<(), VmError> {
Ok(())
}
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
fn vm_coredump(&mut self, _: &str) -> Result<(), VmError> {
Ok(())
}
fn vm_shutdown(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_reboot(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_info(&self) -> Result<VmInfoResponse, VmError> {
Ok(VmInfoResponse {
config: Arc::new(Mutex::new(VmConfig {
cpus: CpusConfig {
boot_vcpus: 1,
max_vcpus: 1,
topology: None,
kvm_hyperv: false,
max_phys_bits: 46,
affinity: None,
features: CpuFeatures::default(),
},
memory: MemoryConfig {
size: 536_870_912,
mergeable: false,
hotplug_method: HotplugMethod::Acpi,
hotplug_size: None,
hotplugged_size: None,
shared: false,
hugepages: false,
hugepage_size: None,
prefault: false,
zones: None,
thp: true,
},
payload: Some(PayloadConfig {
kernel: Some(PathBuf::from("/path/to/kernel")),
firmware: None,
cmdline: None,
initramfs: None,
#[cfg(feature = "igvm")]
igvm: None,
}),
rate_limit_groups: None,
disks: None,
net: None,
rng: RngConfig {
src: PathBuf::from("/dev/urandom"),
iommu: false,
},
balloon: None,
fs: None,
pmem: None,
serial: ConsoleConfig {
file: None,
mode: ConsoleOutputMode::Null,
iommu: false,
socket: None,
},
console: ConsoleConfig {
file: None,
mode: ConsoleOutputMode::Tty,
iommu: false,
socket: None,
},
#[cfg(target_arch = "x86_64")]
debug_console: DebugConsoleConfig::default(),
devices: None,
user_devices: None,
vdpa: None,
vsock: None,
pvpanic: false,
iommu: false,
#[cfg(target_arch = "x86_64")]
sgx_epc: None,
numa: None,
watchdog: false,
#[cfg(feature = "guest_debug")]
gdb: false,
pci_segments: None,
platform: None,
tpm: None,
preserved_fds: None,
})),
state: VmState::Running,
memory_actual_size: 0,
device_tree: None,
})
}
fn vmm_ping(&self) -> VmmPingResponse {
VmmPingResponse {
build_version: String::new(),
version: String::new(),
pid: 0,
features: Vec::new(),
}
}
fn vm_delete(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vmm_shutdown(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_resize(&mut self, _: Option<u8>, _: Option<u64>, _: Option<u64>) -> Result<(), VmError> {
Ok(())
}
fn vm_resize_zone(&mut self, _: String, _: u64) -> Result<(), VmError> {
Ok(())
}
fn vm_add_device(&mut self, _: DeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_add_user_device(&mut self, _: UserDeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_remove_device(&mut self, _: String) -> Result<(), VmError> {
Ok(())
}
fn vm_add_disk(&mut self, _: DiskConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_add_fs(&mut self, _: FsConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_add_pmem(&mut self, _: PmemConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_add_net(&mut self, _: NetConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_add_vdpa(&mut self, _: VdpaConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_add_vsock(&mut self, _: VsockConfig) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_counters(&mut self) -> Result<Option<Vec<u8>>, VmError> {
Ok(None)
}
fn vm_power_button(&mut self) -> Result<(), VmError> {
Ok(())
}
fn vm_receive_migration(&mut self, _: VmReceiveMigrationData) -> Result<(), MigratableError> {
Ok(())
}
fn vm_send_migration(&mut self, _: VmSendMigrationData) -> Result<(), MigratableError> {
Ok(())
}
fn vm_nmi(&mut self) -> Result<(), VmError> {
Ok(())
}
}
fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
let mut epoll = EpollContext::new().unwrap();
epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
@@ -309,7 +98,89 @@ fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receive
EpollDispatch::Api => {
for _ in 0..api_evt.read().unwrap() {
let api_request = api_receiver.recv().unwrap();
api_request(&mut StubApiRequestHandler).unwrap();
match api_request {
ApiRequest::VmCreate(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmDelete(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmBoot(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmShutdown(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmReboot(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmInfo(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmmPing(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmPause(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmResume(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmSnapshot(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmRestore(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmmShutdown(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmResize(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmResizeZone(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddDevice(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddUserDevice(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmRemoveDevice(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddDisk(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddFs(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddPmem(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddNet(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddVdpa(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmAddVsock(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmCounters(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmReceiveMigration(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmSendMigration(_, sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
ApiRequest::VmPowerButton(sender) => {
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
}
}
}
}
_ => {

View File

@@ -23,8 +23,6 @@ fuzz_target!(|bytes| {
kernel: None,
cmdline: Some(String::from_utf8_lossy(&bytes).to_string()),
initramfs: None,
#[cfg(feature = "igvm")]
igvm: None,
};
let kernel_cmdline = match vmm::vm::Vm::generate_cmdline(&payload_config) {
Ok(cmdline) => cmdline,

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
#![no_main]
use libfuzzer_sys::fuzz_target;

View File

@@ -8,32 +8,30 @@ license = "Apache-2.0 OR BSD-3-Clause"
[features]
kvm = ["kvm-ioctls", "kvm-bindings", "vfio-ioctls/kvm"]
mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv", "iced-x86"]
sev_snp = ["igvm_parser", "igvm_defs"]
sev_snp = []
tdx = []
[dependencies]
anyhow = "1.0.81"
byteorder = "1.5.0"
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm_defs", optional = true }
igvm_parser = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm", optional = true }
libc = "0.2.153"
log = "0.4.21"
kvm-bindings = { version = "0.8.1", optional = true, features = ["serde"] }
kvm-ioctls = { version = "0.17.0", optional = true }
anyhow = "1.0.75"
byteorder = "1.4.3"
thiserror = "1.0.40"
libc = "0.2.147"
log = "0.4.17"
kvm-ioctls = { version = "0.13.0", optional = true }
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx", features = ["with-serde", "fam-wrappers"], optional = true }
mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true }
mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true}
serde = { version = "1.0.197", features = ["rc", "derive"] }
serde_with = { version = "3.7.0", default-features = false, features = ["macros"] }
serde = { version = "1.0.168", features = ["rc", "derive"] }
serde_with = { version = "3.0.0", default-features = false, features = ["macros"] }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = "0.12.1", features = ["with-serde"] }
thiserror = "1.0.58"
vm-memory = { version = "0.12.2", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
optional = true
version = "1.21.0"
version = "1.19.0"
default-features = false
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
[dev-dependencies]
env_logger = "0.11.3"
env_logger = "0.10.0"

View File

@@ -1,6 +1,4 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
use std::any::Any;

View File

@@ -1,5 +1,3 @@
// Copyright 2022 Arm Limited (or its affiliates). All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
pub mod gic;

View File

@@ -10,8 +10,10 @@
// CMP-Compare Two Operands
//
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::regs::*;
use crate::arch::x86::Exception;
// CMP affects OF, SF, ZF, AF, PF and CF
const FLAGS_MASK: u64 = CF | PF | AF | ZF | SF | OF;
@@ -209,6 +211,8 @@ impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm8 {
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;

View File

@@ -12,7 +12,9 @@
// Copies the second operand (source operand) to the first operand (destination operand).
//
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::Exception;
macro_rules! mov_rm_r {
($bound:ty) => {
@@ -269,6 +271,7 @@ impl<T: CpuStateManager> InstructionHandler<T> for Mov_RAX_moffs64 {
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;

View File

@@ -10,8 +10,10 @@
// MOVS - Move Data from String to String
//
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::regs::DF;
use crate::arch::x86::Exception;
macro_rules! movs {
($bound:ty) => {
@@ -100,6 +102,7 @@ impl<T: CpuStateManager> InstructionHandler<T> for Movsb_m8_m8 {
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;

View File

@@ -10,7 +10,9 @@
// OR - Logical inclusive OR
//
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::Exception;
macro_rules! or_rm_r {
($bound:ty) => {
@@ -50,6 +52,7 @@ impl<T: CpuStateManager> InstructionHandler<T> for Or_rm8_r8 {
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;

View File

@@ -648,9 +648,13 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> {
#[cfg(test)]
mod mock_vmm {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::EmulatorCpuState as CpuState;
use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState as CpuState};
use crate::arch::x86::gdt::{gdt_entry, segment_from_gdt};
use crate::arch::x86::Exception;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
@@ -768,6 +772,7 @@ mod mock_vmm {
#[cfg(test)]
mod tests {
#![allow(unused_mut)]
use super::*;
use crate::arch::x86::emulator::mock_vmm::*;

View File

@@ -311,17 +311,3 @@ pub struct MsrEntry {
pub index: u32,
pub data: u64,
}
#[serde_with::serde_as]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct XsaveState {
#[serde_as(as = "[_; 1024usize]")]
pub region: [u32; 1024usize],
}
impl Default for XsaveState {
fn default() -> Self {
// SAFETY: this is plain old data structure
unsafe { ::std::mem::zeroed() }
}
}

View File

@@ -1,6 +1,4 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.

Some files were not shown because too many files have changed in this diff Show More