Compare commits

..

18 Commits
v33.0 ... v32.1

Author SHA1 Message Date
Bo Chen
4eb4dee9cf build: Release v32.1 (bug fix release)
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-06 14:55:16 -07:00
Bo Chen
aa850b5c3d misc: Address clippy beta issues
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Alyssa Ross
c84d43d85d vmm: ignore and warn TAP FDs send in vm.create
This does the same thing as df2a7c17 ("vmm: Ignore and warn TAP FDs
sent via the HTTP request body"), but for the vm.create endpoint,
which also previously would accept file descriptors in the body, and
try to use whatever fd occupied that number as a TAP device.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-07-05 09:36:22 -07:00
Alyssa Ross
4eac4dfb0f vmm: allow restart_syscall() in PTY process
This can be triggered by debugging cloud-hypervisor using gdb, or
probably if the process is suspended and restarted.

Fixes: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/5489
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-07-05 09:36:22 -07:00
Jianyong Wu
475d1879f2 vfio: fix vfio device fail to initialize issue for 64k page size
Currently, vfio device fails to initialize as the msix-cap region in BAR
is mapped as RW region.

To resolve the initialization issue, this commit avoids mapping the
msix-cap region in the BAR. However, this solution introduces another
problem where aligning the msix table offset in the BAR to the page
size may cause overlap with the MMIO RW region, leading to reduced
performance. By enlarging the entire region in the BAR and relocating
the msix table to achieve page size alignment, this problem can be
overcomed effectively.

Fixes: #5292
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
Jianyong Wu
42ad38d28c vm-allocator: Add page size related functions
To avoid code duplication extract page related functions to their
own module and add utility functions for manipulating addresses
related to page sizes

Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
Bo Chen
8a8fe39c99 vmm: Clarify memory regions are required to be page-size aligned
Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Bo Chen
7da34333f5 arch: Refactor the way of creating memory mapping
This patch clarifies the assumptions we have regarding the guest address
space layout while creating memory mapping in E820 on x86_64 and fdt on
aarch64. It also explicitly checks on these assumptions and report
errors if these assumptions do not hold.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Yu Li
1da2abf4b5 vmm: memory_manager: align down the rest space of ram_region
This commit renames `ram_region_sub_size` to `ram_region_available_size`
and make its value align down to the default page size or hugepage
size of the current memory zone, which can prevent the memory zone from
being split into misaligned parts.  And if the available size of ram
region is zero, this region will be marked as consumed even it has
unused space.

Note that there is two methods to use hugepages.

1. Specify `hugepages` for `memory` or `memory-zone`, if the
   `hugepage_size` is not specified, the value can be got by `statfs`
   for `/dev/hugepages`.
2. Specify a `file` in hugetlbfs for `memory-zone`, the hugepage size
   can also be got by `statfs` for the file.

The value for alignment will be the hugepage size if this memory zone
is using hugepages, otherwise the value will be default page size of
system.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
e5835fdc75 arch: let arch_memory_regions return all available regions
The previous `arch_memory_regions` function will provide some memory
regions with the specified memory size and fill all the previous
regions before using the next one, but sometimes there may be no need
to fill up the previous one, e.g., the previous one should be aligned
with hugepage size.

This commit make `arch_memory_regions` function not take any
parameters and return the max available regions, the memory manager
can use them on demand.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
c10de48f08 arch: create memory mapping by the actual memory info
The original codes did not consider that the previous memory region
might not be full and always set it to the maximum size.

This commit fixes this problem by creating memory mappings based on
the actual memory details in both E820 on x86_64 and fdt on aarch64.

Fixes: #5463

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
5b0478d12a logger: use write with \r\n instead of writeln
The device manager will set tty or pty to raw mode, all the `\n` will
be LF without CR, which makes the output difficult to read.

This commit solves it by using `write` with `\r\n` instead of
`writeln`, which can print CR and LF explicitly.

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Yu Li
d1504e5654 vmm: use unwrap_or instead of match for prefault
Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
2023-07-05 09:36:22 -07:00
Jianyong Wu
d86905b92a arch: x86_64: Populate the APIC Id
Program the APIC ID (CPUID leaf 0x1 EBX) with the CPU id. This resolves
an issue where the EDKII firmware expects the APIC ID to vary per-CPU.

Fixes: #5475
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
Alyssa Ross
f0de3e5b55 vmm, virtio-devices: allow mremap for consoles
SerialBuffer uses VecDeque::extend, which calls realloc, which a
maximum buffer size of 1 MiB.  Starting at allocation sizes of
128 KiB, musl's mallocng allocator will use mremap for the allocation.
Since this was not permitted by the seccomp rules, heavy write load
could crash cloud-hypervisor with a seccomp failure.  (Encountered
using virtio-console, but I don't see any reason it wouldn't happen
for the legacy serial device too.)

Signed-off-by: Alyssa Ross <hi@alyssa.is>
2023-07-05 09:36:22 -07:00
Rafael Mendonca
81a4274738 main: Fix error propagation if starting the VM fails
Commit 21d40d7 ("main: reset tty if starting the VM fails") changed
start_vmm() to join the vmm thread if an error happens after the vmm
thread is started. The implementation put all the error-prone code that
is run after the vmm is started in a closure, to be able to always join
the vmm thread, regardless of any error happening. However, it missed
propagating the error that might happen inside the closure back to the
main function, after joining the vmm thread.

For some cmd line options, the above issue inhibits proper error
reporting when starting a VM with invalid commands, as many parameters
are parsed after the vmm is started, thus if such parsing fails, no
error will be reported back to the user.

See: #5435
Fixes: 21d40d7 ("main: reset tty if starting the VM fails")
Signed-off-by: Rafael Mendonca <rafaelmendsr@gmail.com>
2023-07-05 09:36:22 -07:00
Bo Chen
ea1a05f846 vmm: Allocate guest memory address space before TDX initialization
The refactoring on deferring address space allocation (#5169) broke TDX,
as TDX initialization needs to access guest memory for encryption and
measurement of guest pages.

Signed-off-by: Bo Chen <chen.bo@intel.com>
2023-07-05 09:36:22 -07:00
Jianyong Wu
378eadbc1a vfio: align memory region size and address to PAGE_SIZE
In current implementation, memory region used in vfio is assumed to
align to 4k which may cause error when the PAGE_SIZE is not 4k, like on
Arm, it can be 16k and 64k.

Remove this assumption and align memory resource used by vfio to
PAGE_SIZE then vfio can run on host with 64k PAGE_SIZE.

Fixes: #5292
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
2023-07-05 09:36:22 -07:00
72 changed files with 1094 additions and 3305 deletions

View File

@@ -13,7 +13,7 @@ jobs:
- stable
- beta
- nightly
- "1.64"
- "1.62"
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
@@ -42,9 +42,6 @@ jobs:
- name: Build (default features + tdx)
run: cargo rustc --locked --bin cloud-hypervisor --features "tdx" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (default features + dbus_api)
run: cargo rustc --locked --bin cloud-hypervisor --features "dbus_api" -- -D warnings -D clippy::undocumented_unsafe_blocks
- name: Build (default features + guest_debug)
run: cargo rustc --locked --bin cloud-hypervisor --features "guest_debug" -- -D warnings -D clippy::undocumented_unsafe_blocks

View File

@@ -1,26 +0,0 @@
name: Lint Dockerfile
on:
push:
paths:
- resources/Dockerfile
pull_request:
paths:
- resources/Dockerfile
jobs:
hadolint:
name: Run Hadolint Dockerfile Linter
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Lint Dockerfile
uses: hadolint/hadolint-action@master
with:
dockerfile: ./resources/Dockerfile
format: tty
no-fail: false
verbose: true
failure-threshold: info

1068
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "cloud-hypervisor"
version = "33.0.0"
version = "32.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.64"
rust-version = "1.62"
[profile.release]
lto = true
@@ -29,7 +29,7 @@ strip = false
debug = true
[dependencies]
anyhow = "1.0.71"
anyhow = "1.0.70"
api_client = { path = "api_client" }
argh = "0.1.9"
dhat = { version = "0.3.2", optional = true }
@@ -42,20 +42,18 @@ option_parser = { path = "option_parser" }
seccompiler = "0.3.0"
serde_json = "1.0.96"
signal-hook = "0.3.15"
thiserror = "1.0.40"
thiserror = "1.0.39"
tpm = { path = "tpm"}
tracer = { path = "tracer" }
vmm = { path = "vmm" }
vmm-sys-util = "0.11.0"
vm-memory = "0.11.0"
zbus = { version = "3.11.1", optional = true }
vm-memory = "0.10.0"
# 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" }
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "main" }
[dev-dependencies]
dirs = "5.0.0"
@@ -67,7 +65,6 @@ wait-timeout = "0.2.0"
[features]
default = ["kvm"]
dbus_api = ["zbus", "vmm/dbus_api"]
dhat-heap = ["dhat"] # For heap profiling
guest_debug = ["vmm/guest_debug"]
kvm = ["vmm/kvm"]

92
Jenkinsfile vendored
View File

@@ -192,52 +192,52 @@ pipeline {
}
}
}
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 - 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 {

View File

@@ -140,10 +140,7 @@ The Ubuntu cloud images do not ship with a default password so it necessary to
use a `cloud-init` disk image to customise the image on the first boot. A basic
`cloud-init` image is generated by this [script](scripts/create-cloud-init.sh).
This seeds the image with a default username/password of `cloud/cloud123`. It
is only necessary to add this disk image on the first boot. Script also assigns
default IP address using `test_data/cloud-init/ubuntu/local/network-config` details
with `--net "mac=12:34:56:78:90:ab,tap="` option. Then the matching mac address
interface will be enabled as per `network-config` details.
is only necessary to add this disk image on the first boot.
```shell
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor

View File

@@ -9,18 +9,18 @@ default = []
tdx = []
[dependencies]
anyhow = "1.0.71"
anyhow = "1.0.70"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
libc = "0.2.139"
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
log = "0.4.17"
serde = { version = "1.0.164", features = ["rc", "derive"] }
thiserror = "1.0.40"
uuid = "1.3.4"
serde = { version = "1.0.156", features = ["rc", "derive"] }
thiserror = "1.0.39"
uuid = "1.3.0"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-bitmap"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }

View File

@@ -24,8 +24,6 @@ use super::layout::{
IRQ_BASE, MEM_32BIT_DEVICES_SIZE, MEM_32BIT_DEVICES_START, MEM_PCI_IO_SIZE, MEM_PCI_IO_START,
PCI_HIGH_BASE, PCI_MMIO_CONFIG_SIZE_PER_SEGMENT,
};
use std::fs;
use std::path::Path;
use vm_fdt::{FdtWriter, FdtWriterResult};
use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError, GuestMemoryRegion};
@@ -42,12 +40,8 @@ const VIRTIO_IOMMU_PHANDLE: u32 = 5;
// NOTE: Keep FIRST_VCPU_PHANDLE the last PHANDLE defined.
// This is a value for uniquely identifying the FDT node containing the first vCPU.
// The last number of vCPU phandle depends on the number of vCPUs.
const FIRST_VCPU_PHANDLE: u32 = 8;
const FIRST_VCPU_PHANDLE: u32 = 6;
// This is a value for uniquely identifying the FDT node containing the L2 cache info
const L2_CACHE_PHANDLE: u32 = 6;
// This is a value for uniquely identifying the FDT node containing the L3 cache info
const L3_CACHE_PHANDLE: u32 = 7;
// Read the documentation specified when appending the root node to the FDT.
const ADDRESS_CELLS: u32 = 0x2;
const SIZE_CELLS: u32 = 0x2;
@@ -87,97 +81,6 @@ pub enum Error {
}
type Result<T> = result::Result<T, Error>;
pub enum CacheLevel {
/// L1 data cache
L1D = 0,
/// L1 instruction cache
L1I = 1,
/// L2 cache
L2 = 2,
/// L3 cache
L3 = 3,
}
/// NOTE: CACHE SIZE file directory example,
/// "/sys/devices/system/cpu/cpu0/cache/index0/size".
pub fn get_cache_size(cache_level: CacheLevel) -> u32 {
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
match cache_level {
CacheLevel::L1D => file_directory += "/index0/size",
CacheLevel::L1I => file_directory += "/index1/size",
CacheLevel::L2 => file_directory += "/index2/size",
CacheLevel::L3 => file_directory += "/index3/size",
}
let file_path = Path::new(&file_directory);
if !file_path.exists() {
error!("File: {} not exist.", file_directory);
0
} else {
info!("File: {} exist.", file_directory);
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
// The content of the file is as simple as a size, like: "32K"
let src = src.trim();
let src_digits: u32 = src[0..src.len() - 1].parse().unwrap();
let src_unit = &src[src.len() - 1..];
src_digits
* match src_unit {
"K" => 1024,
"M" => 1024u32.pow(2),
"G" => 1024u32.pow(3),
_ => 1,
}
}
}
/// NOTE: CACHE COHERENCY LINE SIZE file directory example,
/// "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size".
pub fn get_cache_coherency_line_size(cache_level: CacheLevel) -> u32 {
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
match cache_level {
CacheLevel::L1D => file_directory += "/index0/coherency_line_size",
CacheLevel::L1I => file_directory += "/index1/coherency_line_size",
CacheLevel::L2 => file_directory += "/index2/coherency_line_size",
CacheLevel::L3 => file_directory += "/index3/coherency_line_size",
}
let file_path = Path::new(&file_directory);
if !file_path.exists() {
error!("File: {} not exist.", file_directory);
0
} else {
info!("File: {} exist.", file_directory);
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
src.trim().parse::<u32>().unwrap()
}
}
/// NOTE: CACHE NUMBER OF SETS file directory example,
/// "/sys/devices/system/cpu/cpu0/cache/index0/number_of_sets".
pub fn get_cache_number_of_sets(cache_level: CacheLevel) -> u32 {
let mut file_directory: String = "/sys/devices/system/cpu/cpu0/cache".to_string();
match cache_level {
CacheLevel::L1D => file_directory += "/index0/number_of_sets",
CacheLevel::L1I => file_directory += "/index1/number_of_sets",
CacheLevel::L2 => file_directory += "/index2/number_of_sets",
CacheLevel::L3 => file_directory += "/index3/number_of_sets",
}
let file_path = Path::new(&file_directory);
if !file_path.exists() {
error!("File: {} not exist.", file_directory);
0
} else {
info!("File: {} exist.", file_directory);
let src = fs::read_to_string(file_directory).expect("File not exists or file corrupted.");
src.trim().parse::<u32>().unwrap()
}
}
/// Creates the flattened device tree for this aarch64 VM.
#[allow(clippy::too_many_arguments)]
pub fn create_fdt<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
@@ -256,54 +159,6 @@ fn create_cpu_nodes(
let num_cpus = vcpu_mpidr.len();
// Add cache info.
// L1 Data Cache Info.
let mut l1_d_cache_size: u32 = 0;
let mut l1_d_cache_line_size: u32 = 0;
let mut l1_d_cache_sets: u32 = 0;
// L1 Instruction Cache Info.
let mut l1_i_cache_size: u32 = 0;
let mut l1_i_cache_line_size: u32 = 0;
let mut l1_i_cache_sets: u32 = 0;
// L2 Cache Info.
let mut l2_cache_size: u32 = 0;
let mut l2_cache_line_size: u32 = 0;
let mut l2_cache_sets: u32 = 0;
// L3 Cache Info.
let mut l3_cache_size: u32 = 0;
let mut l3_cache_line_size: u32 = 0;
let mut l3_cache_sets: u32 = 0;
let cache_path = Path::new("/sys/devices/system/cpu/cpu0/cache");
let cache_exist: bool = cache_path.exists();
if !cache_exist {
error!("cache sysfs system does not exist.");
} else {
info!("cache sysfs system exists.");
// L1 Data Cache Info.
l1_d_cache_size = get_cache_size(CacheLevel::L1D);
l1_d_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1D);
l1_d_cache_sets = get_cache_number_of_sets(CacheLevel::L1D);
// L1 Instruction Cache Info.
l1_i_cache_size = get_cache_size(CacheLevel::L1I);
l1_i_cache_line_size = get_cache_coherency_line_size(CacheLevel::L1I);
l1_i_cache_sets = get_cache_number_of_sets(CacheLevel::L1I);
// L2 Cache Info.
l2_cache_size = get_cache_size(CacheLevel::L2);
l2_cache_line_size = get_cache_coherency_line_size(CacheLevel::L2);
l2_cache_sets = get_cache_number_of_sets(CacheLevel::L2);
// L3 Cache Info.
l3_cache_size = get_cache_size(CacheLevel::L3);
l3_cache_line_size = get_cache_coherency_line_size(CacheLevel::L3);
l3_cache_sets = get_cache_number_of_sets(CacheLevel::L3);
}
for (cpu_id, mpidr) in vcpu_mpidr.iter().enumerate().take(num_cpus) {
let cpu_name = format!("cpu@{cpu_id:x}");
let cpu_node = fdt.begin_node(&cpu_name)?;
@@ -318,21 +173,6 @@ fn create_cpu_nodes(
fdt.property_u32("reg", (mpidr & 0x7FFFFF) as u32)?;
fdt.property_u32("phandle", cpu_id as u32 + FIRST_VCPU_PHANDLE)?;
if cache_exist && l1_d_cache_size != 0 && l1_i_cache_size != 0 {
// Add cache info.
fdt.property_u32("d-cache-size", l1_d_cache_size)?;
fdt.property_u32("d-cache-line-size", l1_d_cache_line_size)?;
fdt.property_u32("d-cache-sets", l1_d_cache_sets)?;
fdt.property_u32("i-cache-size", l1_i_cache_size)?;
fdt.property_u32("i-cache-line-size", l1_i_cache_line_size)?;
fdt.property_u32("i-cache-sets", l1_i_cache_sets)?;
if l2_cache_size != 0 {
fdt.property_u32("next-level-cache", L2_CACHE_PHANDLE)?;
}
}
// Add `numa-node-id` property if there is any numa config.
if numa_nodes.len() > 1 {
for numa_node_idx in 0..numa_nodes.len() {
@@ -346,36 +186,6 @@ fn create_cpu_nodes(
fdt.end_node(cpu_node)?;
}
if cache_exist && l2_cache_size != 0 {
let l2_cache_name = "l2-cache0";
let l2_cache_node = fdt.begin_node(l2_cache_name)?;
fdt.property_u32("phandle", L2_CACHE_PHANDLE)?;
fdt.property_string("compatible", "cache")?;
fdt.property_u32("cache-size", l2_cache_size)?;
fdt.property_u32("cache-line-size", l2_cache_line_size)?;
fdt.property_u32("cache-sets", l2_cache_sets)?;
fdt.property_u32("cache-level", 2)?;
if l3_cache_size != 0 {
fdt.property_u32("next-level-cache", L3_CACHE_PHANDLE)?;
}
fdt.end_node(l2_cache_node)?;
}
if cache_exist && l3_cache_size != 0 {
let l3_cache_name = "l3-cache0";
let l3_cache_node = fdt.begin_node(l3_cache_name)?;
fdt.property_u32("phandle", L3_CACHE_PHANDLE)?;
fdt.property_string("compatible", "cache")?;
fdt.property_null("cache-unified")?;
fdt.property_u32("cache-size", l3_cache_size)?;
fdt.property_u32("cache-line-size", l3_cache_line_size)?;
fdt.property_u32("cache-sets", l3_cache_sets)?;
fdt.property_u32("cache-level", 3)?;
fdt.end_node(l3_cache_node)?;
}
if let Some(topology) = vcpu_topology {
let (threads_per_core, cores_per_package, packages) = topology;
let cpu_map_node = fdt.begin_node("cpu-map")?;

View File

@@ -180,8 +180,11 @@ pub fn initramfs_load_addr(
}
}
pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
let host_cpu_phys_bits = hypervisor.get_host_ipa_limit().try_into().unwrap();
pub fn get_host_cpu_phys_bits() -> u8 {
// A dummy hypervisor created only for querying the host IPA size and will
// be freed after the query.
let hv = hypervisor::new().unwrap();
let host_cpu_phys_bits = hv.get_host_ipa_limit().try_into().unwrap();
if host_cpu_phys_bits == 0 {
// Host kernel does not support `get_host_ipa_limit`,
// we return the default value 40 here.

View File

@@ -16,7 +16,7 @@ use crate::GuestMemoryMmap;
use crate::InitramfsConfig;
use crate::RegionType;
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError};
use hypervisor::{HypervisorCpuError, HypervisorError};
use linux_loader::loader::bootparam::boot_params;
use linux_loader::loader::elf::start_info::{
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
@@ -1139,7 +1139,7 @@ pub fn initramfs_load_addr(
Ok(aligned_addr)
}
pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
pub fn get_host_cpu_phys_bits() -> u8 {
// SAFETY: call cpuid with valid leaves
unsafe {
let leaf = x86_64::__cpuid(0x8000_0000);
@@ -1148,7 +1148,9 @@ pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u
// Some physical address bits may become reserved when the feature is enabled.
// See AMD64 Architecture Programmer's Manual Volume 2, Section 7.10.1
let reduced = if leaf.eax >= 0x8000_001f
&& matches!(hypervisor.get_cpu_vendor(), CpuVendor::AMD)
&& leaf.ebx == 0x6874_7541 // Vendor ID: AuthenticAMD
&& leaf.ecx == 0x444d_4163
&& leaf.edx == 0x6974_6e65
&& x86_64::__cpuid(0x8000_001f).eax & 0x1 != 0
{
(x86_64::__cpuid(0x8000_001f).ebx >> 6) & 0x3f

View File

@@ -13,13 +13,13 @@ libc = "0.2.139"
log = "0.4.17"
qcow = { path = "../qcow" }
smallvec = "1.10.0"
thiserror = "1.0.40"
thiserror = "1.0.39"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vhdx = { path = "../vhdx" }
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
virtio-queue = "0.8.0"
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
virtio-queue = "0.7.1"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.11.0"

View File

@@ -6,19 +6,19 @@ edition = "2021"
[dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.71"
anyhow = "1.0.70"
arch = { path = "../arch" }
bitflags = "2.3.3"
bitflags = "1.3.2"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
libc = "0.2.139"
log = "0.4.17"
thiserror = "1.0.40"
thiserror = "1.0.39"
tpm = { path = "../tpm" }
versionize = "0.1.10"
versionize_derive = "0.1.4"
vm-device = { path = "../vm-device" }
vm-memory = "0.11.0"
vm-memory = "0.10.0"
vm-migration = { path = "../vm-migration" }
vmm-sys-util = "0.11.0"

View File

@@ -116,7 +116,6 @@ impl Aml for AcpiGedDevice {
true,
self.address.0,
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
None,
)]),
),
&aml::OpRegion::new(

View File

@@ -1,21 +1,18 @@
- [Cloud Hypervisor API](#cloud-hypervisor-api)
- [External API](#external-api)
- [REST API](#rest-api)
- [REST API Location and availability](#rest-api-location-and-availability)
- [REST API Endpoints](#rest-api-endpoints)
- [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
- [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
- [REST API Examples](#rest-api-examples)
- [Create a Virtual Machine](#create-a-virtual-machine)
- [Boot a Virtual Machine](#boot-a-virtual-machine)
- [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
- [Reboot a Virtual Machine](#reboot-a-virtual-machine)
- [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
- [D-Bus API](#d-bus-api)
- [D-Bus API Location and availability](#d-bus-api-location-and-availability)
- [D-Bus API Interface](#d-bus-api-interface)
- [Location and availability](#location-and-availability)
- [Endpoints](#endpoints)
- [Virtual Machine Manager (VMM) Actions](#virtual-machine-manager-vmm-actions)
- [Virtual Machine (VM) Actions](#virtual-machine-vm-actions)
- [REST API Examples](#rest-api-examples)
- [Create a Virtual Machine](#create-a-virtual-machine)
- [Boot a Virtual Machine](#boot-a-virtual-machine)
- [Dump a Virtual Machine Information](#dump-a-virtual-machine-information)
- [Reboot a Virtual Machine](#reboot-a-virtual-machine)
- [Shut a Virtual Machine Down](#shut-a-virtual-machine-down)
- [Command Line Interface](#command-line-interface)
- [REST API, D-Bus API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
- [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
- [Internal API](#internal-api)
- [Goals and Design](#goals-and-design)
- [End to End Example](#end-to-end-example)
@@ -24,11 +21,9 @@
The Cloud Hypervisor API is made of 2 distinct interfaces:
1. **The External API** This is the user facing API. Users and operators
can control and manage the Cloud Hypervisor through various options
including a REST API, a Command Line Interface (CLI) or a D-Bus based API,
which is not compiled into Cloud Hypervisor by default.
1. **The external API**. This is the user facing API. Users and operators can
control and manage Cloud Hypervisor through either a REST API or a Command
Line Interface (CLI).
1. **The internal API**, based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/)
module. This API is used internally by the Cloud Hypervisor threads to
communicate between each others.
@@ -45,10 +40,10 @@ API triggers VM and VMM specific actions, and as such it is designed as a
collection of RPC-style, static methods.
The API is [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
compliant. Please consult the [Cloud Hypervisor OpenAPI Document](https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/master/vmm/src/api/openapi/cloud-hypervisor.yaml)
for more details about the API payloads and responses.
compliant. Please consult the [Cloud Hypervisor API](https://raw.githubusercontent.com/cloud-hypervisor/cloud-hypervisor/master/vmm/src/api/openapi/cloud-hypervisor.yaml)
document for more details about the API payloads and responses.
#### REST API Location and availability
### Location and availability
The REST API is available as soon as the Cloud Hypervisor binary is started,
through a local UNIX socket.
@@ -70,18 +65,18 @@ Cloud Hypervisor Guest
Disk(s): None
```
#### REST API Endpoints
### Endpoints
The Cloud Hypervisor API exposes the following actions through its endpoints:
##### Virtual Machine Manager (VMM) Actions
#### Virtual Machine Manager (VMM) Actions
| Action | Endpoint | Request Body | Response Body | Prerequisites |
| ----------------------------------- | --------------- | ------------ | -------------------------- | ------------------ |
| Check for the REST API availability | `/vmm.ping` | N/A | `/schemas/VmmPingResponse` | N/A |
| Shut the VMM down | `/vmm.shutdown` | N/A | N/A | The VMM is running |
##### Virtual Machine (VM) Actions
#### Virtual Machine (VM) Actions
| Action | Endpoint | Request Body | Response Body | Prerequisites |
| ---------------------------------- | ----------------------- | ------------------------------- | ------------------------ | ------------------------------------------------------ |
@@ -115,10 +110,10 @@ The Cloud Hypervisor API exposes the following actions through its endpoints:
* The `vmcoredump` action is available exclusively for the `x86_64`
architecture and can be executed only when the `guest_debug` feature is
enabled. Without this feature, the corresponding [REST API](#rest-api) or
[D-Bus API](#d-bus-api) endpoints are not available.
enabled. Without this feature, the corresponding REST API endpoint is not
available.
#### REST API Examples
### REST API Examples
For the following set of examples, we assume Cloud Hypervisor is started with
the REST API available at `/tmp/cloud-hypervisor.sock`:
@@ -134,7 +129,7 @@ Cloud Hypervisor Guest
Disk(s): None
```
##### Create a Virtual Machine
#### Create a Virtual Machine
We want to create a virtual machine with the following characteristics:
@@ -162,7 +157,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
}'
```
##### Boot a Virtual Machine
#### Boot a Virtual Machine
Once the VM is created, we can boot it:
@@ -172,7 +167,7 @@ Once the VM is created, we can boot it:
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.boot'
```
##### Dump a Virtual Machine Information
#### Dump a Virtual Machine Information
We can fetch information about any VM, as soon as it's created:
@@ -184,7 +179,7 @@ curl --unix-socket /tmp/cloud-hypervisor.sock -i \
-H 'Accept: application/json'
```
##### Reboot a Virtual Machine
#### Reboot a Virtual Machine
We can reboot a VM that's already booted:
@@ -194,7 +189,7 @@ We can reboot a VM that's already booted:
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.reboot'
```
##### Shut a Virtual Machine Down
#### Shut a Virtual Machine Down
Once booted, we can shut a VM down from the REST API:
@@ -204,50 +199,6 @@ Once booted, we can shut a VM down from the REST API:
curl --unix-socket /tmp/cloud-hypervisor.sock -i -X PUT 'http://localhost/api/v1/vm.shutdown'
```
### D-Bus API
Cloud Hypervisor offers a D-Bus API as an alternative to its REST API. As of
writing this document, the D-Bus API mirrors the functionality of the REST
API and shares the same set of endpoints, meaning that it supports every call
that is supported by the REST API and can be a drop-in replacement since it
also consumes/produces JSON.
#### D-Bus API Location and availability
This feature is not compiled into Cloud Hypervisor by default. Users who
wish to use the D-Bus API, must explicitly enable it with the `dbus_api`
feature flag when compiling Cloud Hypervisor.
```sh
$ ./scripts/dev_cli.sh build --release --libc musl -- --features dbus_api
```
Once this feature is enabled, it can be configured with the following
CLI options:
```
--dbus-service-name
well known name of the service
--dbus-object-path
object path to serve the dbus interface
--dbus-system-bus use the system bus instead of a session bus
```
Example invocation:
```sh
$ ./cloud-hypervisor --dbus-service-name "org.cloudhypervisor.DBusApi" \
--dbus-object-path "/org/cloudhypervisor/DBusApi"
```
This will start serving a service with the name `org.cloudhypervisor.DBusApi1`
which in turn can be used to control and manage Cloud Hypervisor.
#### D-Bus API Interface
Please refer to the [REST API](#rest-api) documentation. As previously
mentioned, the D-Bus API currently mirrors the behaviour of the REST API.
### Command Line Interface
The Cloud Hypervisor Command Line Interface (CLI) can only be used for launching
@@ -255,26 +206,22 @@ the Cloud Hypervisor binary, i.e. it can not be used for controlling the VMM or
the launched VM once they're up and running.
If you want to inspect the VMM, or control the VM after launching Cloud
Hypervisor from the CLI, you must use either the [REST API](#rest-api)
or the [D-Bus API](#d-bus-api).
Hypervisor from the CLI, you must use the [REST API](#rest-api).
From the CLI, one can:
From the CLI, one can either:
1. Create and boot a complete virtual machine by using the CLI options to build
the VM config. Run `cloud-hypervisor --help` for a complete list of CLI
options. As soon as the `cloud-hypervisor` binary is launched, contrary
to the [D-Bus API](#d-bus-api), the [REST API](#rest-api) is available
for controlling and managing the VM. The [D-Bus API](#d-bus-api) doesn't start
automatically and needs to be explicitly configured in order to be run.
1. Start either the REST API, D-Bus API or both simultaneously without passing
any VM configuration options. The VM can then be asynchronously created and
booted by calling API methods of choice. It should be noted that one external
API does not exclude another; it is possible to have both the REST and D-Bus
APIs running simultaneously.
options. As soon as the `cloud-hypervisor` binary is launched, the
[REST API](#rest-api) is available for controlling and managing the VM.
1. Start the [REST API](#rest-api) server only, by not passing any VM
configuration options. The VM can then be asynchronously created and booted
by sending HTTP commands to the [REST API](#rest-api). Check the
[REST API examples](#rest-api-examples) section for more details.
### REST API, D-Bus API and CLI Architectural Relationship
### REST API and CLI Architectural Relationship
The REST API, D-Bus API and the CLI all rely on a common, [internal API](#internal-api).
The REST API and the CLI both rely on a common, [internal API](#internal-api).
The CLI options are parsed by the
[argh crate](https://docs.rs/argh/latest/argh/) and then translated into
@@ -285,11 +232,7 @@ The REST API is processed by an HTTP thread using the
crate. As with the CLI, the HTTP requests eventually get translated into
[internal API](#internal-api) commands.
The D-Bus API is implemented using the [zbus](https://github.com/dbus2/zbus)
crate and runs in its own thread. Whenever it needs to call the [internal API](#internal-api),
the [blocking](https://github.com/smol-rs/blocking) crate is used perform the call in zbus' async context.
As a summary, the REST API, the D-Bus API and the CLI are essentially frontends for the
As a summary, the REST API and the CLI are essentially frontends for the
[internal API](#internal-api):
```
@@ -300,11 +243,11 @@ As a summary, the REST API, the D-Bus API and the CLI are essentially frontends
| +------------------+ |
| | +------------------------+
| | | |
+------------+ | +----------+ | | |
| | | D-Bus API | | | | +--------------+ |
| User +---------+----------->+ zbus +--------------+------> | Internal API | |
| | | | | | | +--------------+ |
+------------+ | +----------+ | | |
+------------+ | | | |
| | | | | +--------------+ |
| User +---------+ +------> | Internal API | |
| | | | | +--------------+ |
+------------+ | | | |
| | | |
| | +------------------------+
| +----------+ | VMM
@@ -319,23 +262,22 @@ As a summary, the REST API, the D-Bus API and the CLI are essentially frontends
## Internal API
The Cloud Hypervisor internal API, as its name suggests, is used internally
by the different Cloud Hypervisor threads (VMM, HTTP, D-Bus, control loop,
etc) to send commands and responses to each others.
by the different Cloud Hypervisor threads (VMM, HTTP, control loop, etc) to
send commands and responses to each others.
It is based on [rust's Multi-Producer, Single-Consumer (MPSC)](https://doc.rust-lang.org/std/sync/mpsc/),
and the single consumer (a.k.a. the API receiver) is the Cloud Hypervisor
control loop.
API producers are the HTTP thread handling the [REST API](#rest-api), the
D-Bus thread handling the [D-Bus API](#d-bus-api) and the main thread that
initially parses the [CLI](#command-line-interface).
API producers are the HTTP thread handling the [REST API](#rest-api) and the
main thread that initially parses the [CLI](#command-line-interface).
### Goals and Design
The internal API is designed for controlling, managing and inspecting a Cloud
Hypervisor VMM and its guest. It is a backend for handling external, user
visible requests through the [REST API](#rest-api), the [D-Bus API](#d-bus-api)
or the [CLI](#command-line-interface) interfaces.
visible requests through either the [REST API](#rest-api) or the
[CLI](#command-line-interface) interfaces.
The API follows a command-response scheme that closely maps the [REST API](#rest-api).
Any command must be replied to with a response.

View File

@@ -225,7 +225,7 @@ Number Start End Size File system Name Flags
### Create a macvtap interface
Rely on the following [documentation](macvtap-bridge.md) to set up a
Rely on the following [documentation](docs/macvtap-bridge.md) to set up a
macvtap interface to provide your VM with proper connectivity.
### Boot the image

View File

@@ -6,5 +6,5 @@ edition = "2021"
[dependencies]
libc = "0.2.139"
serde = { version = "1.0.164", features = ["rc", "derive"] }
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_json = "1.0.96"

119
fuzz/Cargo.lock generated
View File

@@ -5,7 +5,7 @@ version = 3
[[package]]
name = "acpi_tables"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#05a609136387cc1cc9b499cee4320020325c263f"
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#98dcb0309d362dd83f6ffcac4f66914a2fbd5a73"
dependencies = [
"zerocopy",
]
@@ -100,12 +100,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded"
[[package]]
name = "block_util"
version = "0.1.0"
@@ -119,7 +113,7 @@ dependencies = [
"versionize",
"versionize_derive",
"vhdx",
"virtio-bindings",
"virtio-bindings 0.2.0",
"virtio-queue",
"vm-memory",
"vm-virtio",
@@ -149,7 +143,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloud-hypervisor"
version = "32.0.0"
version = "31.0.0"
dependencies = [
"anyhow",
"api_client",
@@ -254,7 +248,7 @@ dependencies = [
"acpi_tables",
"anyhow",
"arch",
"bitflags 2.3.2",
"bitflags",
"byteorder",
"hypervisor",
"libc",
@@ -275,7 +269,7 @@ version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"libc",
]
@@ -302,9 +296,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"libc",
@@ -332,9 +326,9 @@ dependencies = [
[[package]]
name = "iced-x86"
version = "1.19.0"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cc8d38244d84278262c8ebe6930cc44283d194cbabae2651f6112103802fb5"
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec"
dependencies = [
"lazy_static",
]
@@ -351,7 +345,7 @@ version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd1e1a01cfb924fd8c5c43b6827965db394f5a3a16c599ce03452266e1cf984c"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"libc",
]
@@ -399,9 +393,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.147"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "libfuzzer-sys"
@@ -416,18 +410,21 @@ dependencies = [
[[package]]
name = "linux-loader"
version = "0.9.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3adb7b28e189741eca3b1a4a27de0bf15e0907c9d4b0c74bd2d7d84ef72e08"
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f"
dependencies = [
"vm-memory",
]
[[package]]
name = "log"
version = "0.4.19"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "micro_http"
@@ -459,7 +456,7 @@ dependencies = [
"thiserror",
"versionize",
"versionize_derive",
"virtio-bindings",
"virtio-bindings 0.2.0",
"virtio-queue",
"vm-memory",
"vm-virtio",
@@ -468,9 +465,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.18.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "option_parser"
@@ -501,9 +498,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.63"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
checksum = "c4ec6d5fe0b140acb27c9a0444118cf55bfbb4e0b259739429abb4521dd67c16"
dependencies = [
"unicode-ident",
]
@@ -521,9 +518,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.28"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
dependencies = [
"proc-macro2",
]
@@ -580,18 +577,18 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.164"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
dependencies = [
"proc-macro2",
"quote",
@@ -600,9 +597,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.99"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"itoa",
"ryu",
@@ -611,9 +608,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.0.0"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe"
dependencies = [
"serde",
"serde_with_macros",
@@ -621,9 +618,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.0.0"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f"
dependencies = [
"darling",
"proc-macro2",
@@ -734,15 +731,15 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.9"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "uuid"
version = "1.3.4"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom",
]
@@ -777,7 +774,7 @@ dependencies = [
[[package]]
name = "vfio-bindings"
version = "0.4.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
dependencies = [
"vmm-sys-util",
]
@@ -785,7 +782,7 @@ dependencies = [
[[package]]
name = "vfio-ioctls"
version = "0.2.0"
source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
dependencies = [
"byteorder",
"kvm-bindings",
@@ -801,9 +798,9 @@ dependencies = [
[[package]]
name = "vfio_user"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#eef6bec4d421f08ed1688fe67c5ea33aabbf5069"
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#afbbd5722885e961ce12baea12efe01d52ce14b0"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"libc",
"log",
"serde",
@@ -830,16 +827,22 @@ dependencies = [
[[package]]
name = "vhost"
version = "0.7.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5"
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"libc",
"vm-memory",
"vmm-sys-util",
]
[[package]]
name = "virtio-bindings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b"
[[package]]
name = "virtio-bindings"
version = "0.2.0"
@@ -871,7 +874,7 @@ dependencies = [
"versionize",
"versionize_derive",
"vhost",
"virtio-bindings",
"virtio-bindings 0.2.0",
"virtio-queue",
"vm-allocator",
"vm-device",
@@ -883,12 +886,12 @@ dependencies = [
[[package]]
name = "virtio-queue"
version = "0.8.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8"
checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd"
dependencies = [
"log",
"virtio-bindings",
"virtio-bindings 0.1.0",
"vm-memory",
"vmm-sys-util",
]
@@ -922,9 +925,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927
[[package]]
name = "vm-memory"
version = "0.11.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07"
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
dependencies = [
"arc-swap",
"libc",
@@ -961,7 +964,7 @@ dependencies = [
"anyhow",
"arc-swap",
"arch",
"bitflags 2.3.2",
"bitflags",
"block_util",
"devices",
"epoll",
@@ -1006,7 +1009,7 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"libc",
"serde",
"serde_derive",

View File

@@ -12,20 +12,20 @@ cargo-fuzz = true
block_util = { path = "../block_util" }
devices = { path = "../devices" }
epoll = "4.3.1"
libc = "0.2.147"
libc = "0.2.144"
libfuzzer-sys = "0.4.6"
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
linux-loader = { version = "0.8.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.18.0"
once_cell = "1.17.1"
qcow = { path = "../qcow" }
seccompiler = "0.3.0"
vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.8.0"
virtio-queue = "0.7.1"
vmm = { path = "../vmm" }
vmm-sys-util = "0.11.1"
vm-memory = "0.11.0"
vm-memory = "0.10.0"
vm-device = { path = "../vm-device" }
vm-virtio = { path = "../vm-virtio" }

View File

@@ -11,23 +11,23 @@ mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv"]
tdx = []
[dependencies]
anyhow = "1.0.71"
anyhow = "1.0.70"
byteorder = "1.4.3"
thiserror = "1.0.40"
thiserror = "1.0.39"
libc = "0.2.139"
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.164", features = ["rc", "derive"] }
serde_with = { version = "3.0.0", default-features = false, features = ["macros"] }
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_with = { version = "2.3.2", default-features = false, features = ["macros"] }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
[target.'cfg(target_arch = "x86_64")'.dependencies.iced-x86]
version = "1.19.0"
version = "1.18.0"
default-features = false
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]

View File

@@ -21,15 +21,6 @@ use crate::MpState;
use thiserror::Error;
use vm_memory::GuestAddress;
#[cfg(target_arch = "x86_64")]
#[derive(Copy, Clone, Default)]
pub enum CpuVendor {
#[default]
Unknown,
Intel,
AMD,
}
#[derive(Error, Debug)]
///
/// Enum for CPU error

View File

@@ -9,14 +9,10 @@
//
#[cfg(target_arch = "x86_64")]
use crate::arch::x86::CpuIdEntry;
#[cfg(target_arch = "x86_64")]
use crate::cpu::CpuVendor;
#[cfg(feature = "tdx")]
use crate::kvm::TdxCapabilities;
use crate::vm::Vm;
use crate::HypervisorType;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64;
use std::sync::Arc;
use thiserror::Error;
@@ -79,11 +75,6 @@ pub enum HypervisorError {
///
#[error("Failed to set partition property:{0}")]
SetPartitionProperty(#[source] anyhow::Error),
///
/// Running on an unsupported CPU
///
#[error("Unsupported CPU:{0}")]
UnsupportedCpu(#[source] anyhow::Error),
}
///
@@ -145,27 +136,4 @@ pub trait Hypervisor: Send + Sync {
/// Get maximum number of vCPUs
fn get_max_vcpus(&self) -> u32;
#[cfg(target_arch = "x86_64")]
///
/// Determine CPU vendor
///
fn get_cpu_vendor(&self) -> CpuVendor {
// SAFETY: call cpuid with valid leaves
unsafe {
let leaf = x86_64::__cpuid(0x0);
if leaf.ebx == 0x756e_6547 && leaf.ecx == 0x6c65_746e && leaf.edx == 0x4965_6e69 {
// Vendor string GenuineIntel
CpuVendor::Intel
} else if leaf.ebx == 0x6874_7541 && leaf.ecx == 0x444d_4163 && leaf.edx == 0x6974_6e65
{
// Vendor string AuthenticAMD
CpuVendor::AMD
} else {
// Not known yet, the corresponding manufacturer manual should contain the
// necesssary info. See also https://wiki.osdev.org/CPUID#CPU_Vendor_ID_String
CpuVendor::default()
}
}
}
}

View File

@@ -49,8 +49,6 @@ mod cpu;
mod device;
pub use crate::hypervisor::{Hypervisor, HypervisorError};
#[cfg(target_arch = "x86_64")]
pub use cpu::CpuVendor;
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::HypervisorDeviceError;
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]

View File

@@ -10,7 +10,6 @@
#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
#[allow(clippy::missing_safety_doc)]
impl<T> __IncompleteArrayField<T> {
#[inline]
pub const fn new() -> Self {

View File

@@ -10,7 +10,6 @@
#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
#[allow(clippy::missing_safety_doc)]
impl<T> __IncompleteArrayField<T> {
#[inline]
pub const fn new() -> Self {

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

View File

@@ -11,13 +11,13 @@ libc = "0.2.139"
log = "0.4.17"
net_gen = { path = "../net_gen" }
rate_limiter = { path = "../rate_limiter" }
serde = "1.0.164"
thiserror = "1.0.40"
serde = "1.0.156"
thiserror = "1.0.39"
versionize = "0.1.10"
versionize_derive = "0.1.4"
virtio-bindings = "0.2.0"
virtio-queue = "0.8.0"
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
virtio-queue = "0.7.1"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.11.0"

View File

@@ -10,7 +10,7 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"]
[dependencies]
anyhow = "1.0.71"
anyhow = "1.0.70"
byteorder = "1.4.3"
hypervisor = { path = "../hypervisor" }
vfio-bindings = { git = "https://github.com/rust-vmm/vfio", branch = "main", features = ["fam-wrappers"] }
@@ -20,11 +20,11 @@ vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
vmm-sys-util = "0.11.0"
libc = "0.2.139"
log = "0.4.17"
serde = { version = "1.0.164", features = ["derive"] }
thiserror = "1.0.40"
serde = { version = "1.0.156", features = ["derive"] }
thiserror = "1.0.39"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" }

View File

@@ -452,9 +452,10 @@ impl From<PciBarType> for PciBarRegionType {
}
}
impl From<PciBarRegionType> for PciBarType {
fn from(val: PciBarRegionType) -> Self {
match val {
#[allow(clippy::from_over_into)]
impl Into<PciBarType> for PciBarRegionType {
fn into(self) -> PciBarType {
match self {
PciBarRegionType::IoRegion => PciBarType::Io,
PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
@@ -468,9 +469,10 @@ pub enum PciBarPrefetchable {
Prefetchable = 0x08,
}
impl From<PciBarPrefetchable> for bool {
fn from(val: PciBarPrefetchable) -> Self {
match val {
#[allow(clippy::from_over_into)]
impl Into<bool> for PciBarPrefetchable {
fn into(self) -> bool {
match self {
PciBarPrefetchable::NotPrefetchable => false,
PciBarPrefetchable::Prefetchable => true,
}

View File

@@ -8,8 +8,8 @@ build = "../build.rs"
[dependencies]
argh = "0.1.9"
dirs = "5.0.0"
serde = { version = "1.0.164", features = ["rc", "derive"] }
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_json = "1.0.96"
test_infra = { path = "../test_infra" }
thiserror = "1.0.40"
thiserror = "1.0.39"
wait-timeout = "0.2.0"

View File

@@ -1,13 +1,9 @@
- [v33.0](#v330)
- [D-Bus based API](#d-bus-based-api)
- [Passthrough CPU cache information for AArch64](#passthrough-cpu-cache-information-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes)
- [Contributors](#contributors)
- [v32.1](#v321)
- [v32.0](#v320)
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
- [API Changes](#api-changes)
- [Notable Bug Fixes](#notable-bug-fixes-1)
- [Contributors](#contributors-1)
- [Notable Bug Fixes](#notable-bug-fixes)
- [Contributors](#contributors)
- [v31.1](#v311)
- [v31.0](#v310)
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
@@ -15,15 +11,15 @@
- [Improvements on Console `SIGWINCH` Handler](#improvements-on-console-sigwinch-handler)
- [Remove Directory Support from `MemoryZoneConfig::file`](#remove-directory-support-from-memoryzoneconfigfile)
- [Documentation Improvements](#documentation-improvements)
- [Notable Bug Fixes](#notable-bug-fixes-2)
- [Contributors](#contributors-2)
- [Notable Bug Fixes](#notable-bug-fixes-1)
- [Contributors](#contributors-1)
- [v30.0](#v300)
- [Command Line Changes for Reduced Binary Size](#command-line-changes-for-reduced-binary-size)
- [Basic vfio-user Server Support](#basic-vfio-user-server-support)
- [Heap Profiling Support](#heap-profiling-support)
- [Documentation Improvements](#documentation-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-3)
- [Contributors](#contributors-3)
- [Notable Bug Fixes](#notable-bug-fixes-2)
- [Contributors](#contributors-2)
- [v28.2](#v282)
- [v29.0](#v290)
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
@@ -33,10 +29,10 @@
- [`AArch64` Documentation Integration](#aarch64-documentation-integration)
- [`virtio-block` Counters Enhancement](#virtio-block-counters-enhancement)
- [TCP Offload Control](#tcp-offload-control)
- [Notable Bug Fixes](#notable-bug-fixes-4)
- [Notable Bug Fixes](#notable-bug-fixes-3)
- [Removals](#removals)
- [Deprecations](#deprecations)
- [Contributors](#contributors-4)
- [Contributors](#contributors-3)
- [v28.1](#v281)
- [v28.0](#v280)
- [Community Engagement (Reminder)](#community-engagement-reminder)
@@ -44,9 +40,9 @@
- [Virtualised TPM Support](#virtualised-tpm-support)
- [Transparent Huge Page Support](#transparent-huge-page-support)
- [README Quick Start Improved](#readme-quick-start-improved)
- [Notable Bug Fixes](#notable-bug-fixes-5)
- [Notable Bug Fixes](#notable-bug-fixes-4)
- [Removals](#removals-1)
- [Contributors](#contributors-5)
- [Contributors](#contributors-4)
- [v27.0](#v270)
- [Community Engagement](#community-engagement)
- [Prebuilt Packages](#prebuilt-packages)
@@ -55,41 +51,41 @@
- [Simplified Build Feature Flags](#simplified-build-feature-flags)
- [Asynchronous Kernel Loading](#asynchronous-kernel-loading)
- [GDB Support for AArch64](#gdb-support-for-aarch64)
- [Notable Bug Fixes](#notable-bug-fixes-6)
- [Notable Bug Fixes](#notable-bug-fixes-5)
- [Deprecations](#deprecations-1)
- [Contributors](#contributors-6)
- [Contributors](#contributors-5)
- [v26.0](#v260)
- [SMBIOS Improvements via `--platform`](#smbios-improvements-via---platform)
- [Unified Binary MSHV and KVM Support](#unified-binary-mshv-and-kvm-support)
- [Notable Bug Fixes](#notable-bug-fixes-7)
- [Notable Bug Fixes](#notable-bug-fixes-6)
- [Deprecations](#deprecations-2)
- [Removals](#removals-2)
- [Contributors](#contributors-7)
- [Contributors](#contributors-6)
- [v25.0](#v250)
- [`ch-remote` Improvements](#ch-remote-improvements-1)
- [VM "Coredump" Support](#vm-coredump-support)
- [Notable Bug Fixes](#notable-bug-fixes-8)
- [Notable Bug Fixes](#notable-bug-fixes-7)
- [Removals](#removals-3)
- [Contributors](#contributors-8)
- [Contributors](#contributors-7)
- [v24.0](#v240)
- [Bypass Mode for `virtio-iommu`](#bypass-mode-for-virtio-iommu)
- [Ensure Identifiers Uniqueness](#ensure-identifiers-uniqueness)
- [Sparse Mmap support](#sparse-mmap-support)
- [Expose Platform Serial Number](#expose-platform-serial-number)
- [Notable Bug Fixes](#notable-bug-fixes-9)
- [Notable Bug Fixes](#notable-bug-fixes-8)
- [Notable Improvements](#notable-improvements)
- [Deprecations](#deprecations-3)
- [New on the Website](#new-on-the-website)
- [Contributors](#contributors-9)
- [Contributors](#contributors-8)
- [v23.1](#v231)
- [v23.0](#v230)
- [vDPA Support](#vdpa-support)
- [Updated OS Support list](#updated-os-support-list)
- [`AArch64` Memory Map Improvements](#aarch64-memory-map-improvements)
- [`AMX` Support](#amx-support)
- [Notable Bug Fixes](#notable-bug-fixes-10)
- [Notable Bug Fixes](#notable-bug-fixes-9)
- [Deprecations](#deprecations-4)
- [Contributors](#contributors-10)
- [Contributors](#contributors-9)
- [v22.1](#v221)
- [v22.0](#v220)
- [GDB Debug Stub Support](#gdb-debug-stub-support)
@@ -100,13 +96,13 @@
- [PMU Support for AArch64](#pmu-support-for-aarch64)
- [Documentation Under CC-BY-4.0 License](#documentation-under-cc-by-40-license)
- [Deprecation of "Classic" `virtiofsd`](#deprecation-of-classic-virtiofsd)
- [Notable Bug Fixes](#notable-bug-fixes-11)
- [Contributors](#contributors-11)
- [Notable Bug Fixes](#notable-bug-fixes-10)
- [Contributors](#contributors-10)
- [v21.0](#v210)
- [Efficient Local Live Migration (for Live Upgrade)](#efficient-local-live-migration-for-live-upgrade)
- [Recommended Kernel is Now 5.15](#recommended-kernel-is-now-515)
- [Notable Bug fixes](#notable-bug-fixes-12)
- [Contributors](#contributors-12)
- [Notable Bug fixes](#notable-bug-fixes-11)
- [Contributors](#contributors-11)
- [v20.2](#v202)
- [v20.1](#v201)
- [v20.0](#v200)
@@ -115,8 +111,8 @@
- [Improved VFIO support](#improved-vfio-support)
- [Safer code](#safer-code)
- [Extended documentation](#extended-documentation)
- [Notable bug fixes](#notable-bug-fixes-13)
- [Contributors](#contributors-13)
- [Notable bug fixes](#notable-bug-fixes-12)
- [Contributors](#contributors-12)
- [v19.0](#v190)
- [Improved PTY handling for serial and `virtio-console`](#improved-pty-handling-for-serial-and-virtio-console)
- [PCI boot time optimisations](#pci-boot-time-optimisations)
@@ -124,8 +120,8 @@
- [Live migration enhancements](#live-migration-enhancements)
- [`virtio-mem` support with `vfio-user`](#virtio-mem-support-with-vfio-user)
- [AArch64 for `virtio-iommu`](#aarch64-for-virtio-iommu)
- [Notable bug fixes](#notable-bug-fixes-14)
- [Contributors](#contributors-14)
- [Notable bug fixes](#notable-bug-fixes-13)
- [Contributors](#contributors-13)
- [v18.0](#v180)
- [Experimental User Device (`vfio-user`) support](#experimental-user-device-vfio-user-support)
- [Migration support for `vhost-user` devices](#migration-support-for-vhost-user-devices)
@@ -135,23 +131,23 @@
- [Live migration on MSHV hypervisor](#live-migration-on-mshv-hypervisor)
- [AArch64 CPU topology support](#aarch64-cpu-topology-support)
- [Power button support on AArch64](#power-button-support-on-aarch64)
- [Notable bug fixes](#notable-bug-fixes-15)
- [Contributors](#contributors-15)
- [Notable bug fixes](#notable-bug-fixes-14)
- [Contributors](#contributors-14)
- [v17.0](#v170)
- [ARM64 NUMA support using ACPI](#arm64-numa-support-using-acpi)
- [`Seccomp` support for MSHV backend](#seccomp-support-for-mshv-backend)
- [Hotplug of `macvtap` devices](#hotplug-of-macvtap-devices)
- [Improved SGX support](#improved-sgx-support)
- [Inflight tracking for `vhost-user` devices](#inflight-tracking-for-vhost-user-devices)
- [Notable bug fixes](#notable-bug-fixes-16)
- [Contributors](#contributors-16)
- [Notable bug fixes](#notable-bug-fixes-15)
- [Contributors](#contributors-15)
- [v16.0](#v160)
- [Improved live migration support](#improved-live-migration-support)
- [Improved `vhost-user` support](#improved-vhost-user-support)
- [ARM64 ACPI and UEFI support](#arm64-acpi-and-uefi-support)
- [Notable bug fixes](#notable-bug-fixes-17)
- [Notable bug fixes](#notable-bug-fixes-16)
- [Removed functionality](#removed-functionality)
- [Contributors](#contributors-17)
- [Contributors](#contributors-16)
- [v15.0](#v150)
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
- [Network device rate limiting](#network-device-rate-limiting)
@@ -159,7 +155,7 @@
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
- [Bug fixes](#bug-fixes)
- [Deprecations](#deprecations-5)
- [Contributors](#contributors-18)
- [Contributors](#contributors-17)
- [v0.14.1](#v0141)
- [v0.14.0](#v0140)
- [Structured event monitoring](#structured-event-monitoring)
@@ -169,7 +165,7 @@
- [PTY control for serial and `virtio-console`](#pty-control-for-serial-and-virtio-console)
- [Block device rate limiting](#block-device-rate-limiting)
- [Deprecations](#deprecations-6)
- [Contributors](#contributors-19)
- [Contributors](#contributors-18)
- [v0.13.0](#v0130)
- [Wider VFIO device support](#wider-vfio-device-support)
- [Improved huge page support](#improved-huge-page-support)
@@ -177,13 +173,13 @@
- [VHD disk image support](#vhd-disk-image-support)
- [Improved Virtio device threading](#improved-virtio-device-threading)
- [Clean shutdown support via synthetic power button](#clean-shutdown-support-via-synthetic-power-button)
- [Contributors](#contributors-20)
- [Contributors](#contributors-19)
- [v0.12.0](#v0120)
- [ARM64 enhancements](#arm64-enhancements)
- [Removal of `vhost-user-net` and `vhost-user-block` self spawning](#removal-of-vhost-user-net-and-vhost-user-block-self-spawning)
- [Migration of `vhost-user-fs` backend](#migration-of-vhost-user-fs-backend)
- [Enhanced "info" API](#enhanced-info-api)
- [Contributors](#contributors-21)
- [Contributors](#contributors-20)
- [v0.11.0](#v0110)
- [`io_uring` support by default for `virtio-block`](#io_uring-support-by-default-for-virtio-block)
- [Windows Guest Support](#windows-guest-support)
@@ -195,15 +191,15 @@
- [Default Log Level Changed](#default-log-level-changed)
- [New `--balloon` Parameter Added](#new---balloon-parameter-added)
- [Experimental `virtio-watchdog` Support](#experimental-virtio-watchdog-support)
- [Notable Bug Fixes](#notable-bug-fixes-18)
- [Contributors](#contributors-22)
- [Notable Bug Fixes](#notable-bug-fixes-17)
- [Contributors](#contributors-21)
- [v0.10.0](#v0100)
- [`virtio-block` Support for Multiple Descriptors](#virtio-block-support-for-multiple-descriptors)
- [Memory Zones](#memory-zones)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements)
- [Preliminary KVM HyperV Emulation Control](#preliminary-kvm-hyperv-emulation-control)
- [Notable Bug Fixes](#notable-bug-fixes-19)
- [Contributors](#contributors-23)
- [Notable Bug Fixes](#notable-bug-fixes-18)
- [Contributors](#contributors-22)
- [v0.9.0](#v090)
- [`io_uring` Based Block Device Support](#io_uring-based-block-device-support)
- [Block and Network Device Statistics](#block-and-network-device-statistics)
@@ -216,17 +212,17 @@
- [Enhancements to ARM64 Support](#enhancements-to-arm64-support)
- [Intel SGX Support](#intel-sgx-support)
- [`Seccomp` Sandbox Improvements](#seccomp-sandbox-improvements-1)
- [Notable Bug Fixes](#notable-bug-fixes-20)
- [Contributors](#contributors-24)
- [Notable Bug Fixes](#notable-bug-fixes-19)
- [Contributors](#contributors-23)
- [v0.8.0](#v080)
- [Experimental Snapshot and Restore Support](#experimental-snapshot-and-restore-support)
- [Experimental ARM64 Support](#experimental-arm64-support)
- [Support for Using 5-level Paging in Guests](#support-for-using-5-level-paging-in-guests)
- [Virtio Device Interrupt Suppression for Network Devices](#virtio-device-interrupt-suppression-for-network-devices)
- [`vhost_user_fs` Improvements](#vhost_user_fs-improvements)
- [Notable Bug Fixes](#notable-bug-fixes-21)
- [Notable Bug Fixes](#notable-bug-fixes-20)
- [Command Line and API Changes](#command-line-and-api-changes)
- [Contributors](#contributors-25)
- [Contributors](#contributors-24)
- [v0.7.0](#v070)
- [Block, Network, Persistent Memory (PMEM), VirtioFS and Vsock hotplug](#block-network-persistent-memory-pmem-virtiofs-and-vsock-hotplug)
- [Alternative `libc` Support](#alternative-libc-support)
@@ -236,14 +232,14 @@
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
- [Updated Distribution Support](#updated-distribution-support)
- [Command Line and API Changes](#command-line-and-api-changes-1)
- [Contributors](#contributors-26)
- [Contributors](#contributors-25)
- [v0.6.0](#v060)
- [Directly Assigned Devices Hotplug](#directly-assigned-devices-hotplug)
- [Shared Filesystem Improvements](#shared-filesystem-improvements)
- [Block and Networking IO Self Offloading](#block-and-networking-io-self-offloading)
- [Command Line Interface](#command-line-interface)
- [PVH Boot](#pvh-boot)
- [Contributors](#contributors-27)
- [Contributors](#contributors-26)
- [v0.5.1](#v051)
- [v0.5.0](#v050)
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
@@ -251,7 +247,7 @@
- [New Interrupt Management Framework](#new-interrupt-management-framework)
- [Development Tools](#development-tools)
- [Kata Containers Integration](#kata-containers-integration)
- [Contributors](#contributors-28)
- [Contributors](#contributors-27)
- [v0.4.0](#v040)
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
@@ -260,7 +256,7 @@
- [Userspace IOAPIC by default](#userspace-ioapic-by-default)
- [PCI BAR reprogramming](#pci-bar-reprogramming)
- [New `cloud-hypervisor` organization](#new-cloud-hypervisor-organization)
- [Contributors](#contributors-29)
- [Contributors](#contributors-28)
- [v0.3.0](#v030)
- [Block device offloading](#block-device-offloading)
- [Network device backend](#network-device-backend)
@@ -287,50 +283,19 @@
- [Unit testing](#unit-testing)
- [Integration tests parallelization](#integration-tests-parallelization)
# v33.0
# v32.1
This release has been tracked in our [roadmap
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
v33.0. The following user visible changes have been made:
### D-Bus based API
A D-Bus based API has been added as an alternative to the existing REST
API. This feature is gated by the `dbus_api` feature. Details can be
found in the [API documentation](docs/api.md).
### Expose Host CPU Cache Details for AArch64
Now the CPU cache information on the host is properly exposed to the
guest on AArch64.
### Notable Bug Fixes
This is a bug fix release. The following issues have been addressed:
* Report errors explicitly to users when VM failed to boot (#5453)
* Fix VFIO on platforms with non-4k page size (#5450, #5469)
* Fix TDX initialization (#5454)
* Ensure all guest memory regions are page-size aligned (#5496)
* Fix seccomp filter lists related to virtio-console, serial and pty
(#5506, #5524)
(#5506, #5524)
* Populate APIC ID properly (#5512)
* Ignore and warn TAP FDs in more situations (#5522)
### Contributors
Many thanks to everyone who has contributed to our release:
* Alyssa Ross <hi@alyssa.is>
* Anatol Belski <anbelski@linux.microsoft.com>
* Bo Chen <chen.bo@intel.com>
* Jianyong Wu <jianyong.wu@arm.com>
* Omer Faruk Bayram <omer.faruk@sartura.hr>
* Rafael Mendonca <rafaelmendsr@gmail.com>
* Ravi kumar Veeramally <ravikumar.veeramally@intel.com>
* Rob Bradford <rbradford@rivosinc.com>
* Ruslan Mstoi <ruslan.mstoi@intel.com>
* Yu Li <liyu.yukiteru@bytedance.com>
* zhongbingnan <zhongbingnan@bytedance.com>
# v32.0
This release has been tracked in our [roadmap

View File

@@ -15,67 +15,58 @@ ARG CARGO_GIT_REGISTRY_DIR="$CLH_BUILD_DIR/cargo_git_registry"
ENV CARGO_HOME=/usr/local/rust
ENV RUSTUP_HOME=$CARGO_HOME
ENV PATH="$PATH:$CARGO_HOME/bin"
ENV DEBIAN_FRONTEND=noninteractive
# Install all CI dependencies
# DL3015 ignored cause not installing openvswitch-switch-dpdk recommended packages breaks ovs_dpdk test
# hadolint ignore=DL3008,DL3015
RUN apt-get update \
&& apt-get -yq upgrade \
&& apt-get install --no-install-recommends -yq \
build-essential \
bc \
curl \
wget \
sudo \
mtools \
musl-tools \
libssl-dev \
pkg-config \
flex \
bison \
libelf-dev \
qemu-utils \
libglib2.0-dev \
libpixman-1-dev \
libseccomp-dev \
libcap-ng-dev \
socat \
dosfstools \
cpio \
python \
python3 \
python3-setuptools \
ntfs-3g \
python3-distutils \
uuid-dev \
iperf3 \
zip \
git-core \
dnsmasq \
dmsetup \
ca-certificates \
unzip \
iproute2 \
dbus \
&& apt-get install openvswitch-switch-dpdk -yq \
&& apt-get -yq upgrade \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq \
build-essential \
bc \
curl \
wget \
sudo \
mtools \
musl-tools \
libssl-dev \
pkg-config \
flex \
bison \
libelf-dev \
qemu-utils \
libglib2.0-dev \
libpixman-1-dev \
libseccomp-dev \
libcap-ng-dev \
socat \
dosfstools \
cpio \
python \
python3 \
python3-setuptools \
ntfs-3g \
openvswitch-switch-dpdk \
python3-distutils \
uuid-dev \
iperf3 \
zip \
git-core \
dnsmasq \
dmsetup \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old
&& rm -rf /var/lib/apt/lists/*
RUN update-alternatives --set ovs-vswitchd /usr/lib/openvswitch-switch-dpdk/ovs-vswitchd-dpdk
# hadolint ignore=DL3008
RUN if [ "$TARGETARCH" = "amd64" ]; then \
apt-get update \
&& apt-get -yq upgrade \
&& apt-get install --no-install-recommends -yq gcc-multilib gawk \
libtool expect gnutls-dev gnutls-bin libfuse-dev \
libjson-glib-dev libgmp-dev libtasn1-dev python3-twisted \
net-tools softhsm2 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old; fi
apt-get update \
&& apt-get -yq upgrade \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq gcc-multilib gawk \
libtool expect gnutls-dev gnutls-bin libfuse-dev \
libjson-glib-dev libgmp-dev libtasn1-dev python3-twisted \
net-tools softhsm2 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*; fi
# hadolint ignore=DL3008
RUN if [ "$TARGETARCH" = "arm64" ]; then \
# On AArch64, `setcap` binary should be installed via `libcap2-bin`.
# The `setcap` binary is used in integration tests.
@@ -83,7 +74,7 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \
# kernel (any version) image in `/boot` and modules in `/lib/modules`.
apt-get update \
&& apt-get -yq upgrade \
&& apt-get install --no-install-recommends -yq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq \
libcap2-bin \
libguestfs-tools \
linux-image-generic \
@@ -93,12 +84,11 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \
perl \
texinfo \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old; fi
&& rm -rf /var/lib/apt/lists/*; fi
# Fix the libssl-dev install
# hadolint ignore=SC2155
RUN export ARCH="$(uname -m)" \
&& cp /usr/include/"$ARCH"-linux-gnu/openssl/opensslconf.h /usr/include/openssl/
&& cp /usr/include/$ARCH-linux-gnu/openssl/opensslconf.h /usr/include/openssl/
ENV X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/
ENV X86_64_UNKNOWN_LINUX_MUSL_OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/
ENV AARCH64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu/
@@ -106,10 +96,9 @@ ENV AARCH64_UNKNOWN_LINUX_MUSL_OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu/
ENV OPENSSL_INCLUDE_DIR=/usr/include/
# Install the rust toolchain
# hadolint ignore=DL4006,SC2155
RUN export ARCH="$(uname -m)" \
&& nohup curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "$RUST_TOOLCHAIN" \
&& rustup target add "$ARCH"-unknown-linux-musl --toolchain "$RUST_TOOLCHAIN" \
&& rustup target add $ARCH-unknown-linux-musl --toolchain "$RUST_TOOLCHAIN" \
&& if [ "$TARGETARCH" = "amd64" ]; then rustup toolchain add --profile minimal $RUST_TOOLCHAIN-x86_64-unknown-linux-musl; fi \
&& if [ "$TARGETARCH" = "amd64" ]; then rustup component add rustfmt; fi \
&& if [ "$TARGETARCH" = "amd64" ]; then rustup component add clippy; fi \
@@ -119,14 +108,12 @@ RUN export ARCH="$(uname -m)" \
&& ln -s "$CARGO_GIT_REGISTRY_DIR" "$CARGO_HOME/git"
# Set the rust environment
# hadolint ignore=SC2016
RUN echo 'source $CARGO_HOME/env' >> "$HOME"/.bashrc \
&& mkdir "$HOME"/.cargo \
&& ln -s $CARGO_HOME/env "$HOME"/.cargo/env
RUN echo 'source $CARGO_HOME/env' >> $HOME/.bashrc \
&& mkdir $HOME/.cargo \
&& ln -s $CARGO_HOME/env $HOME/.cargo/env
# install SPDK NVMe
# only for 'x86_64' platform images as 'docker buildx' can't build 'spdk'
# hadolint ignore=DL3003,SC2046
RUN if [ "$TARGETARCH" = "amd64" ]; then \
git clone https://github.com/spdk/spdk \
&& cd spdk \
@@ -136,7 +123,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
&& ./scripts/pkgdep.sh \
&& apt-get clean \
&& ./configure --with-vfio-user \
&& make -j $(nproc) \
&& make -j `nproc` \
&& mkdir /usr/local/bin/spdk-nvme \
&& cp ./build/bin/nvmf_tgt /usr/local/bin/spdk-nvme \
&& cp ./scripts/rpc.py /usr/local/bin/spdk-nvme \
@@ -144,7 +131,6 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
&& cd .. && rm -rf spdk; fi
# install swtpm only for x86_64 arch
# hadolint ignore=DL3003
RUN if [ "$TARGETARCH" = "amd64" ]; then \
git clone https://github.com/stefanberger/libtpms libtpms_build \
&& cd libtpms_build \
@@ -165,7 +151,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
# install ethr tool for performance tests
RUN if [ "$TARGETARCH" = "amd64" ]; then \
wget -nv https://github.com/microsoft/ethr/releases/latest/download/ethr_linux.zip \
wget https://github.com/microsoft/ethr/releases/latest/download/ethr_linux.zip \
&& unzip ethr_linux.zip \
&& cp ethr /usr/local/bin \
&& rm ethr_linux.zip; fi

View File

@@ -3,7 +3,7 @@ set -x
rm -f /tmp/ubuntu-cloudinit.img
mkdosfs -n CIDATA -C /tmp/ubuntu-cloudinit.img 8192
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/local/user-data ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/local/meta-data ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/local/network-config ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/user-data ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/meta-data ::
mcopy -oi /tmp/ubuntu-cloudinit.img -s test_data/cloud-init/ubuntu/network-config ::

View File

@@ -7,7 +7,7 @@
CLI_NAME="Cloud Hypervisor"
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
CTR_IMAGE_VERSION="20230620-0"
CTR_IMAGE_VERSION="20230316-0"
CTR_IMAGE="${CTR_IMAGE_TAG}:${CTR_IMAGE_VERSION}"
DOCKER_RUNTIME="docker"
@@ -426,7 +426,7 @@ cmd_tests() {
--env USER="root" \
--env CH_LIBC="${libc}" \
"$CTR_IMAGE" \
dbus-run-session ./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
fi
if [ "$integration_sgx" = true ]; then

View File

@@ -281,12 +281,4 @@ else
exit $RES
fi
# Run tests on dbus_api
if [ $RES -eq 0 ]; then
cargo build --features "dbus_api" --all --release --target $BUILD_TARGET
export RUST_BACKTRACE=1
time cargo test "dbus_api::$test_filter" --target $BUILD_TARGET -- ${test_binary_args[*]}
RES=$?
fi
exit $RES

View File

@@ -7,42 +7,9 @@ source $(dirname "$0")/test-util.sh
process_common_args "$@"
WORKLOADS_DIR="$HOME/workloads"
mkdir -p "$WORKLOADS_DIR"
cp scripts/sha1sums-x86_64 $WORKLOADS_DIR
FW_URL=$(curl --silent https://api.github.com/repos/cloud-hypervisor/rust-hypervisor-firmware/releases/latest | grep "browser_download_url" | grep -o 'https://.*[^ "]')
FW="$WORKLOADS_DIR/hypervisor-fw"
if [ ! -f "$FW" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $FW_URL || exit 1
popd
fi
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.qcow2"
FOCAL_OS_IMAGE_URL="https://cloud-hypervisor.azureedge.net/$FOCAL_OS_IMAGE_NAME"
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_IMAGE" ]; then
pushd $WORKLOADS_DIR
time wget --quiet $FOCAL_OS_IMAGE_URL || exit 1
popd
fi
FOCAL_OS_RAW_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
FOCAL_OS_RAW_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_RAW_IMAGE_NAME"
if [ ! -f "$FOCAL_OS_RAW_IMAGE" ]; then
pushd $WORKLOADS_DIR
time qemu-img convert -p -f qcow2 -O raw $FOCAL_OS_IMAGE_NAME $FOCAL_OS_RAW_IMAGE_NAME || exit 1
popd
fi
pushd $WORKLOADS_DIR
sha1sum sha1sums-x86_64 --check --ignore-missing
if [ $? -ne 0 ]; then
echo "sha1sum validation of images failed, remove invalid images to fix the issue."
exit 1
fi
popd
FW="$WORKLOADS_DIR/hypervisor-fw"
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
build_custom_linux
@@ -64,7 +31,7 @@ VFIO_DIR="$WORKLOADS_DIR/vfio"
VFIO_DISK_IMAGE="$WORKLOADS_DIR/vfio.img"
rm -rf $VFIO_DIR $VFIO_DISK_IMAGE
mkdir -p $VFIO_DIR
cp $FOCAL_OS_RAW_IMAGE $VFIO_DIR
cp $FOCAL_OS_IMAGE $VFIO_DIR
cp $FW $VFIO_DIR
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1

View File

@@ -189,13 +189,4 @@ if [ $RES -eq 0 ]; then
RES=$?
fi
# Run tests on dbus_api
if [ $RES -eq 0 ]; then
cargo build --no-default-features --features "kvm,mshv,dbus_api" --all --release --target $BUILD_TARGET
export RUST_BACKTRACE=1
# integration tests now do not reply on build feature "dbus_api"
time cargo test $test_features "dbus_api::$test_filter" -- ${test_binary_args[*]}
RES=$?
fi
exit $RES

View File

@@ -3,3 +3,4 @@ f1eccdc5e1b515dbad294426ab081b47ebfb97c0 focal-server-cloudimg-amd64-custom-2021
7f5a8358243a96adf61f5c20139b29f308f2c0e3 focal-server-cloudimg-amd64-custom-20210609-0.raw
864c074e2f1bd753667a35188b510d83a1d62793 jammy-server-cloudimg-amd64-custom-20230119-0.qcow2
24358ee053f94e7f710ea4ce9e7a63eff2a1eb25 jammy-server-cloudimg-amd64-custom-20230119-0.raw

View File

@@ -11,19 +11,13 @@ use argh::FromArgs;
use option_parser::{ByteSized, ByteSizedParseError};
use std::fmt;
use std::io::Read;
use std::marker::PhantomData;
use std::os::unix::net::UnixStream;
use std::process;
#[cfg(feature = "dbus_api")]
use zbus::{dbus_proxy, zvariant::Optional};
type ApiResult = Result<(), Error>;
#[derive(Debug)]
enum Error {
HttpApiClient(ApiClientError),
#[cfg(feature = "dbus_api")]
DBusApiClient(zbus::Error),
Connect(std::io::Error),
ApiClient(ApiClientError),
InvalidMemorySize(ByteSizedParseError),
InvalidBalloonSize(ByteSizedParseError),
AddDeviceConfig(vmm::config::Error),
@@ -43,9 +37,8 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
HttpApiClient(e) => e.fmt(f),
#[cfg(feature = "dbus_api")]
DBusApiClient(e) => write!(f, "Error D-Bus proxy: {e}"),
ApiClient(e) => e.fmt(f),
Connect(e) => write!(f, "Error opening HTTP socket: {e}"),
InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"),
InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"),
AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"),
@@ -63,430 +56,12 @@ impl fmt::Display for Error {
}
}
enum TargetApi<'a> {
HttpApi(UnixStream, PhantomData<&'a ()>),
#[cfg(feature = "dbus_api")]
DBusApi(DBusApi1ProxyBlocking<'a>),
}
#[cfg(feature = "dbus_api")]
#[dbus_proxy(name = "org.cloudhypervisor.DBusApi1", assume_defaults = false)]
trait DBusApi1 {
fn vmm_ping(&self) -> zbus::Result<String>;
fn vmm_shutdown(&self) -> zbus::Result<()>;
fn vm_add_device(&self, device_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_disk(&self, disk_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_fs(&self, fs_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_net(&self, net_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_pmem(&self, pmem_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_user_device(&self, vm_add_user_device: &str) -> zbus::Result<Optional<String>>;
fn vm_add_vdpa(&self, vdpa_config: &str) -> zbus::Result<Optional<String>>;
fn vm_add_vsock(&self, vsock_config: &str) -> zbus::Result<Optional<String>>;
fn vm_boot(&self) -> zbus::Result<()>;
fn vm_coredump(&self, vm_coredump_data: &str) -> zbus::Result<()>;
fn vm_counters(&self) -> zbus::Result<Optional<String>>;
fn vm_create(&self, vm_config: &str) -> zbus::Result<()>;
fn vm_delete(&self) -> zbus::Result<()>;
fn vm_info(&self) -> zbus::Result<String>;
fn vm_pause(&self) -> zbus::Result<()>;
fn vm_power_button(&self) -> zbus::Result<()>;
fn vm_reboot(&self) -> zbus::Result<()>;
fn vm_remove_device(&self, vm_remove_device: &str) -> zbus::Result<()>;
fn vm_resize(&self, vm_resize: &str) -> zbus::Result<()>;
fn vm_resize_zone(&self, vm_resize_zone: &str) -> zbus::Result<()>;
fn vm_restore(&self, restore_config: &str) -> zbus::Result<()>;
fn vm_receive_migration(&self, receive_migration_data: &str) -> zbus::Result<()>;
fn vm_send_migration(&self, receive_migration_data: &str) -> zbus::Result<()>;
fn vm_resume(&self) -> zbus::Result<()>;
fn vm_shutdown(&self) -> zbus::Result<()>;
fn vm_snapshot(&self, vm_snapshot_config: &str) -> zbus::Result<()>;
}
#[cfg(feature = "dbus_api")]
impl<'a> DBusApi1ProxyBlocking<'a> {
fn new_connection(name: &'a str, path: &'a str, system_bus: bool) -> Result<Self, zbus::Error> {
let connection = if system_bus {
zbus::blocking::Connection::system()?
} else {
zbus::blocking::Connection::session()?
};
Self::builder(&connection)
.destination(name)?
.path(path)?
.build()
}
fn print_response(&self, result: zbus::Result<Optional<String>>) -> ApiResult {
result
.map(|ret| {
if let Some(ref output) = *ret {
println!("{output}");
}
})
.map_err(Error::DBusApiClient)
}
fn api_vmm_ping(&self) -> ApiResult {
self.vmm_ping()
.map(|ping| println!("{ping}"))
.map_err(Error::DBusApiClient)
}
fn api_vmm_shutdown(&self) -> ApiResult {
self.vmm_shutdown().map_err(Error::DBusApiClient)
}
fn api_vm_add_device(&self, device_config: &str) -> ApiResult {
self.print_response(self.vm_add_device(device_config))
}
fn api_vm_add_disk(&self, disk_config: &str) -> ApiResult {
self.print_response(self.vm_add_disk(disk_config))
}
fn api_vm_add_fs(&self, fs_config: &str) -> ApiResult {
self.print_response(self.vm_add_fs(fs_config))
}
fn api_vm_add_net(&self, net_config: &str) -> ApiResult {
self.print_response(self.vm_add_net(net_config))
}
fn api_vm_add_pmem(&self, pmem_config: &str) -> ApiResult {
self.print_response(self.vm_add_pmem(pmem_config))
}
fn api_vm_add_user_device(&self, vm_add_user_device: &str) -> ApiResult {
self.print_response(self.vm_add_user_device(vm_add_user_device))
}
fn api_vm_add_vdpa(&self, vdpa_config: &str) -> ApiResult {
self.print_response(self.vm_add_vdpa(vdpa_config))
}
fn api_vm_add_vsock(&self, vsock_config: &str) -> ApiResult {
self.print_response(self.vm_add_vsock(vsock_config))
}
fn api_vm_boot(&self) -> ApiResult {
self.vm_boot().map_err(Error::DBusApiClient)
}
fn api_vm_coredump(&self, vm_coredump_data: &str) -> ApiResult {
self.vm_coredump(vm_coredump_data)
.map_err(Error::DBusApiClient)
}
fn api_vm_counters(&self) -> ApiResult {
self.print_response(self.vm_counters())
}
fn api_vm_create(&self, vm_config: &str) -> ApiResult {
self.vm_create(vm_config).map_err(Error::DBusApiClient)
}
fn api_vm_delete(&self) -> ApiResult {
self.vm_delete().map_err(Error::DBusApiClient)
}
fn api_vm_info(&self) -> ApiResult {
self.vm_info()
.map(|info| println!("{info}"))
.map_err(Error::DBusApiClient)
}
fn api_vm_pause(&self) -> ApiResult {
self.vm_pause().map_err(Error::DBusApiClient)
}
fn api_vm_power_button(&self) -> ApiResult {
self.vm_power_button().map_err(Error::DBusApiClient)
}
fn api_vm_reboot(&self) -> ApiResult {
self.vm_reboot().map_err(Error::DBusApiClient)
}
fn api_vm_remove_device(&self, vm_remove_device: &str) -> ApiResult {
self.vm_remove_device(vm_remove_device)
.map_err(Error::DBusApiClient)
}
fn api_vm_resize(&self, vm_resize: &str) -> ApiResult {
self.vm_resize(vm_resize).map_err(Error::DBusApiClient)
}
fn api_vm_resize_zone(&self, vm_resize_zone: &str) -> ApiResult {
self.vm_resize_zone(vm_resize_zone)
.map_err(Error::DBusApiClient)
}
fn api_vm_restore(&self, restore_config: &str) -> ApiResult {
self.vm_restore(restore_config)
.map_err(Error::DBusApiClient)
}
fn api_vm_receive_migration(&self, receive_migration_data: &str) -> ApiResult {
self.vm_receive_migration(receive_migration_data)
.map_err(Error::DBusApiClient)
}
fn api_vm_send_migration(&self, send_migration_data: &str) -> ApiResult {
self.vm_send_migration(send_migration_data)
.map_err(Error::DBusApiClient)
}
fn api_vm_resume(&self) -> ApiResult {
self.vm_resume().map_err(Error::DBusApiClient)
}
fn api_vm_shutdown(&self) -> ApiResult {
self.vm_shutdown().map_err(Error::DBusApiClient)
}
fn api_vm_snapshot(&self, vm_snapshot_config: &str) -> ApiResult {
self.vm_snapshot(vm_snapshot_config)
.map_err(Error::DBusApiClient)
}
}
impl<'a> TargetApi<'a> {
fn do_command(&mut self, toplevel: &TopLevel) -> ApiResult {
match self {
Self::HttpApi(api_socket, _) => rest_api_do_command(toplevel, api_socket),
#[cfg(feature = "dbus_api")]
Self::DBusApi(proxy) => dbus_api_do_command(toplevel, proxy),
}
}
}
fn rest_api_do_command(toplevel: &TopLevel, socket: &mut UnixStream) -> ApiResult {
match toplevel.command {
SubCommandEnum::Boot(_) => {
simple_api_command(socket, "PUT", "boot", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Delete(_) => {
simple_api_command(socket, "PUT", "delete", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::ShutdownVmm(_) => {
simple_api_full_command(socket, "PUT", "vmm.shutdown", None)
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Resume(_) => {
simple_api_command(socket, "PUT", "resume", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::PowerButton(_) => {
simple_api_command(socket, "PUT", "power-button", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Reboot(_) => {
simple_api_command(socket, "PUT", "reboot", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Pause(_) => {
simple_api_command(socket, "PUT", "pause", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Info(_) => {
simple_api_command(socket, "GET", "info", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Counters(_) => {
simple_api_command(socket, "GET", "counters", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Ping(_) => {
simple_api_full_command(socket, "GET", "vmm.ping", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Shutdown(_) => {
simple_api_command(socket, "PUT", "shutdown", None).map_err(Error::HttpApiClient)
}
SubCommandEnum::Resize(ref config) => {
let resize = resize_config(config.cpus, &config.memory, &config.balloon)?;
simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient)
}
SubCommandEnum::ResizeZone(ref config) => {
let resize_zone = resize_zone_config(&config.id, &config.size)?;
simple_api_command(socket, "PUT", "resize-zone", Some(&resize_zone))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddDevice(ref config) => {
let device_config = add_device_config(&config.device_config)?;
simple_api_command(socket, "PUT", "add-device", Some(&device_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::RemoveDevice(ref config) => {
let remove_device_data = remove_device_config(&config.device_config);
simple_api_command(socket, "PUT", "remove-device", Some(&remove_device_data))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddDisk(ref config) => {
let disk_config = add_disk_config(&config.disk_config)?;
simple_api_command(socket, "PUT", "add-disk", Some(&disk_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddFs(ref config) => {
let fs_config = add_fs_config(&config.fs_config)?;
simple_api_command(socket, "PUT", "add-fs", Some(&fs_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddPmem(ref config) => {
let pmem_config = add_pmem_config(&config.pmem_config)?;
simple_api_command(socket, "PUT", "add-pmem", Some(&pmem_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddNet(ref config) => {
let (net_config, fds) = add_net_config(&config.net_config)?;
simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), fds)
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddUserDevice(ref config) => {
let device_config = add_user_device_config(&config.device_config)?;
simple_api_command(socket, "PUT", "add-user-device", Some(&device_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddVdpa(ref config) => {
let vdpa_config = add_vdpa_config(&config.vdpa_config)?;
simple_api_command(socket, "PUT", "add-vdpa", Some(&vdpa_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::AddVsock(ref config) => {
let vsock_config = add_vsock_config(&config.vsock_config)?;
simple_api_command(socket, "PUT", "add-vsock", Some(&vsock_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Snapshot(ref config) => {
let snapshot_config = snapshot_api_config(&config.snapshot_config);
simple_api_command(socket, "PUT", "snapshot", Some(&snapshot_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Restore(ref config) => {
let restore_config = restore_config(&config.restore_config)?;
simple_api_command(socket, "PUT", "restore", Some(&restore_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Coredump(ref config) => {
let coredump_config = coredump_config(&config.coredump_config);
simple_api_command(socket, "PUT", "coredump", Some(&coredump_config))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::SendMigration(ref config) => {
let send_migration_data =
send_migration_data(&config.send_migration_config, config.send_migration_local);
simple_api_command(socket, "PUT", "send-migration", Some(&send_migration_data))
.map_err(Error::HttpApiClient)
}
SubCommandEnum::ReceiveMigration(ref config) => {
let receive_migration_data = receive_migration_data(&config.receive_migration_config);
simple_api_command(
socket,
"PUT",
"receive-migration",
Some(&receive_migration_data),
)
.map_err(Error::HttpApiClient)
}
SubCommandEnum::Create(ref config) => {
let data = create_data(&config.vm_config)?;
simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::HttpApiClient)
}
SubCommandEnum::Version(_) => {
// Already handled outside of this function
panic!()
}
}
}
#[cfg(feature = "dbus_api")]
fn dbus_api_do_command(toplevel: &TopLevel, proxy: &mut DBusApi1ProxyBlocking<'_>) -> ApiResult {
match toplevel.command {
SubCommandEnum::Boot(_) => proxy.api_vm_boot(),
SubCommandEnum::Delete(_) => proxy.api_vm_delete(),
SubCommandEnum::ShutdownVmm(_) => proxy.api_vmm_shutdown(),
SubCommandEnum::Resume(_) => proxy.api_vm_resume(),
SubCommandEnum::PowerButton(_) => proxy.api_vm_power_button(),
SubCommandEnum::Reboot(_) => proxy.api_vm_reboot(),
SubCommandEnum::Pause(_) => proxy.api_vm_pause(),
SubCommandEnum::Info(_) => proxy.api_vm_info(),
SubCommandEnum::Counters(_) => proxy.api_vm_counters(),
SubCommandEnum::Ping(_) => proxy.api_vmm_ping(),
SubCommandEnum::Shutdown(_) => proxy.api_vm_shutdown(),
SubCommandEnum::Resize(ref config) => {
let resize = resize_config(config.cpus, &config.memory, &config.balloon)?;
proxy.api_vm_resize(&resize)
}
SubCommandEnum::ResizeZone(ref config) => {
let resize_zone = resize_zone_config(&config.id, &config.size)?;
proxy.api_vm_resize_zone(&resize_zone)
}
SubCommandEnum::AddDevice(ref config) => {
let device_config = add_device_config(&config.device_config)?;
proxy.api_vm_add_device(&device_config)
}
SubCommandEnum::RemoveDevice(ref config) => {
let remove_device_data = remove_device_config(&config.device_config);
proxy.api_vm_remove_device(&remove_device_data)
}
SubCommandEnum::AddDisk(ref config) => {
let disk_config = add_disk_config(&config.disk_config)?;
proxy.api_vm_add_disk(&disk_config)
}
SubCommandEnum::AddFs(ref config) => {
let fs_config = add_fs_config(&config.fs_config)?;
proxy.api_vm_add_fs(&fs_config)
}
SubCommandEnum::AddPmem(ref config) => {
let pmem_config = add_pmem_config(&config.pmem_config)?;
proxy.api_vm_add_pmem(&pmem_config)
}
SubCommandEnum::AddNet(ref config) => {
let (net_config, _fds) = add_net_config(&config.net_config)?;
proxy.api_vm_add_net(&net_config)
}
SubCommandEnum::AddUserDevice(ref config) => {
let device_config = add_user_device_config(&config.device_config)?;
proxy.api_vm_add_user_device(&device_config)
}
SubCommandEnum::AddVdpa(ref config) => {
let vdpa_config = add_vdpa_config(&config.vdpa_config)?;
proxy.api_vm_add_vdpa(&vdpa_config)
}
SubCommandEnum::AddVsock(ref config) => {
let vsock_config = add_vsock_config(&config.vsock_config)?;
proxy.api_vm_add_vsock(&vsock_config)
}
SubCommandEnum::Snapshot(ref config) => {
let snapshot_config = snapshot_api_config(&config.snapshot_config);
proxy.api_vm_snapshot(&snapshot_config)
}
SubCommandEnum::Restore(ref config) => {
let restore_config = restore_config(&config.restore_config)?;
proxy.api_vm_restore(&restore_config)
}
SubCommandEnum::Coredump(ref config) => {
let coredump_config = coredump_config(&config.coredump_config);
proxy.api_vm_coredump(&coredump_config)
}
SubCommandEnum::SendMigration(ref config) => {
let send_migration_data =
send_migration_data(&config.send_migration_config, config.send_migration_local);
proxy.api_vm_send_migration(&send_migration_data)
}
SubCommandEnum::ReceiveMigration(ref config) => {
let receive_migration_data = receive_migration_data(&config.receive_migration_config);
proxy.api_vm_receive_migration(&receive_migration_data)
}
SubCommandEnum::Create(ref config) => {
let data = create_data(&config.vm_config)?;
proxy.api_vm_create(&data)
}
SubCommandEnum::Version(_) => {
// Already handled outside of this function
panic!()
}
}
}
fn resize_config(
fn resize_api_command(
socket: &mut UnixStream,
desired_vcpus: Option<u8>,
memory: &Option<String>,
balloon: &Option<String>,
) -> Result<String, Error> {
) -> Result<(), Error> {
let desired_ram: Option<u64> = if let Some(memory) = memory {
Some(
memory
@@ -515,10 +90,16 @@ fn resize_config(
desired_balloon,
};
Ok(serde_json::to_string(&resize).unwrap())
simple_api_command(
socket,
"PUT",
"resize",
Some(&serde_json::to_string(&resize).unwrap()),
)
.map_err(Error::ApiClient)
}
fn resize_zone_config(id: &str, size: &str) -> Result<String, Error> {
fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Result<(), Error> {
let resize_zone = vmm::api::VmResizeZoneData {
id: id.to_owned(),
desired_ram: size
@@ -527,52 +108,89 @@ fn resize_zone_config(id: &str, size: &str) -> Result<String, Error> {
.0,
};
Ok(serde_json::to_string(&resize_zone).unwrap())
simple_api_command(
socket,
"PUT",
"resize-zone",
Some(&serde_json::to_string(&resize_zone).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_device_config(config: &str) -> Result<String, Error> {
fn add_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?;
let device_config = serde_json::to_string(&device_config).unwrap();
Ok(device_config)
simple_api_command(
socket,
"PUT",
"add-device",
Some(&serde_json::to_string(&device_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_user_device_config(config: &str) -> Result<String, Error> {
fn add_user_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let device_config =
vmm::config::UserDeviceConfig::parse(config).map_err(Error::AddUserDeviceConfig)?;
let device_config = serde_json::to_string(&device_config).unwrap();
Ok(device_config)
simple_api_command(
socket,
"PUT",
"add-user-device",
Some(&serde_json::to_string(&device_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn remove_device_config(id: &str) -> String {
fn remove_device_api_command(socket: &mut UnixStream, id: &str) -> Result<(), Error> {
let remove_device_data = vmm::api::VmRemoveDeviceData { id: id.to_owned() };
serde_json::to_string(&remove_device_data).unwrap()
simple_api_command(
socket,
"PUT",
"remove-device",
Some(&serde_json::to_string(&remove_device_data).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_disk_config(config: &str) -> Result<String, Error> {
fn add_disk_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let disk_config = vmm::config::DiskConfig::parse(config).map_err(Error::AddDiskConfig)?;
let disk_config = serde_json::to_string(&disk_config).unwrap();
Ok(disk_config)
simple_api_command(
socket,
"PUT",
"add-disk",
Some(&serde_json::to_string(&disk_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_fs_config(config: &str) -> Result<String, Error> {
fn add_fs_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let fs_config = vmm::config::FsConfig::parse(config).map_err(Error::AddFsConfig)?;
let fs_config = serde_json::to_string(&fs_config).unwrap();
Ok(fs_config)
simple_api_command(
socket,
"PUT",
"add-fs",
Some(&serde_json::to_string(&fs_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_pmem_config(config: &str) -> Result<String, Error> {
fn add_pmem_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let pmem_config = vmm::config::PmemConfig::parse(config).map_err(Error::AddPmemConfig)?;
let pmem_config = serde_json::to_string(&pmem_config).unwrap();
Ok(pmem_config)
simple_api_command(
socket,
"PUT",
"add-pmem",
Some(&serde_json::to_string(&pmem_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_net_config(config: &str) -> Result<(String, Vec<i32>), Error> {
fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let mut net_config = vmm::config::NetConfig::parse(config).map_err(Error::AddNetConfig)?;
// NetConfig is modified on purpose here by taking the list of file
@@ -580,66 +198,113 @@ fn add_net_config(config: &str) -> Result<(String, Vec<i32>), Error> {
// process would not make any sense since the file descriptor may be
// represented with different values.
let fds = net_config.fds.take().unwrap_or_default();
let net_config = serde_json::to_string(&net_config).unwrap();
Ok((net_config, fds))
simple_api_command_with_fds(
socket,
"PUT",
"add-net",
Some(&serde_json::to_string(&net_config).unwrap()),
fds,
)
.map_err(Error::ApiClient)
}
fn add_vdpa_config(config: &str) -> Result<String, Error> {
fn add_vdpa_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let vdpa_config = vmm::config::VdpaConfig::parse(config).map_err(Error::AddVdpaConfig)?;
let vdpa_config = serde_json::to_string(&vdpa_config).unwrap();
Ok(vdpa_config)
simple_api_command(
socket,
"PUT",
"add-vdpa",
Some(&serde_json::to_string(&vdpa_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn add_vsock_config(config: &str) -> Result<String, Error> {
fn add_vsock_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let vsock_config = vmm::config::VsockConfig::parse(config).map_err(Error::AddVsockConfig)?;
let vsock_config = serde_json::to_string(&vsock_config).unwrap();
Ok(vsock_config)
simple_api_command(
socket,
"PUT",
"add-vsock",
Some(&serde_json::to_string(&vsock_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn snapshot_api_config(url: &str) -> String {
fn snapshot_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
let snapshot_config = vmm::api::VmSnapshotConfig {
destination_url: String::from(url),
};
serde_json::to_string(&snapshot_config).unwrap()
simple_api_command(
socket,
"PUT",
"snapshot",
Some(&serde_json::to_string(&snapshot_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn restore_config(config: &str) -> Result<String, Error> {
fn restore_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?;
let restore_config = serde_json::to_string(&restore_config).unwrap();
Ok(restore_config)
simple_api_command(
socket,
"PUT",
"restore",
Some(&serde_json::to_string(&restore_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn coredump_config(destination_url: &str) -> String {
fn coredump_api_command(socket: &mut UnixStream, destination_url: &str) -> Result<(), Error> {
let coredump_config = vmm::api::VmCoredumpData {
destination_url: String::from(destination_url),
};
serde_json::to_string(&coredump_config).unwrap()
simple_api_command(
socket,
"PUT",
"coredump",
Some(&serde_json::to_string(&coredump_config).unwrap()),
)
.map_err(Error::ApiClient)
}
fn receive_migration_data(url: &str) -> String {
fn receive_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
let receive_migration_data = vmm::api::VmReceiveMigrationData {
receiver_url: url.to_owned(),
};
serde_json::to_string(&receive_migration_data).unwrap()
simple_api_command(
socket,
"PUT",
"receive-migration",
Some(&serde_json::to_string(&receive_migration_data).unwrap()),
)
.map_err(Error::ApiClient)
}
fn send_migration_data(url: &str, local: bool) -> String {
fn send_migration_api_command(
socket: &mut UnixStream,
url: &str,
local: bool,
) -> Result<(), Error> {
let send_migration_data = vmm::api::VmSendMigrationData {
destination_url: url.to_owned(),
local,
};
serde_json::to_string(&send_migration_data).unwrap()
simple_api_command(
socket,
"PUT",
"send-migration",
Some(&serde_json::to_string(&send_migration_data).unwrap()),
)
.map_err(Error::ApiClient)
}
fn create_data(path: &str) -> Result<String, Error> {
fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error> {
let mut data = String::default();
if path == "-" {
std::io::stdin()
@@ -649,7 +314,100 @@ fn create_data(path: &str) -> Result<String, Error> {
data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?;
}
Ok(data)
simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::ApiClient)
}
fn do_command(toplevel: &TopLevel) -> Result<(), Error> {
let mut socket =
UnixStream::connect(toplevel.api_socket.as_deref().unwrap()).map_err(Error::Connect)?;
match toplevel.command {
SubCommandEnum::Boot(_) => {
simple_api_command(&mut socket, "PUT", "boot", None).map_err(Error::ApiClient)
}
SubCommandEnum::Delete(_) => {
simple_api_command(&mut socket, "PUT", "delete", None).map_err(Error::ApiClient)
}
SubCommandEnum::ShutdownVmm(_) => {
simple_api_full_command(&mut socket, "PUT", "vmm.shutdown", None)
.map_err(Error::ApiClient)
}
SubCommandEnum::Resume(_) => {
simple_api_command(&mut socket, "PUT", "resume", None).map_err(Error::ApiClient)
}
SubCommandEnum::PowerButton(_) => {
simple_api_command(&mut socket, "PUT", "power-button", None).map_err(Error::ApiClient)
}
SubCommandEnum::Reboot(_) => {
simple_api_command(&mut socket, "PUT", "reboot", None).map_err(Error::ApiClient)
}
SubCommandEnum::Pause(_) => {
simple_api_command(&mut socket, "PUT", "pause", None).map_err(Error::ApiClient)
}
SubCommandEnum::Info(_) => {
simple_api_command(&mut socket, "GET", "info", None).map_err(Error::ApiClient)
}
SubCommandEnum::Counters(_) => {
simple_api_command(&mut socket, "GET", "counters", None).map_err(Error::ApiClient)
}
SubCommandEnum::Ping(_) => {
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).map_err(Error::ApiClient)
}
SubCommandEnum::Shutdown(_) => {
simple_api_command(&mut socket, "PUT", "shutdown", None).map_err(Error::ApiClient)
}
SubCommandEnum::Resize(ref config) => {
resize_api_command(&mut socket, config.cpus, &config.memory, &config.balloon)
}
SubCommandEnum::ResizeZone(ref config) => {
resize_zone_api_command(&mut socket, &config.id, &config.size)
}
SubCommandEnum::AddDevice(ref config) => {
add_device_api_command(&mut socket, &config.device_config)
}
SubCommandEnum::RemoveDevice(ref config) => {
remove_device_api_command(&mut socket, &config.device_config)
}
SubCommandEnum::AddDisk(ref config) => {
add_disk_api_command(&mut socket, &config.disk_config)
}
SubCommandEnum::AddFs(ref config) => add_fs_api_command(&mut socket, &config.fs_config),
SubCommandEnum::AddPmem(ref config) => {
add_pmem_api_command(&mut socket, &config.pmem_config)
}
SubCommandEnum::AddNet(ref config) => add_net_api_command(&mut socket, &config.net_config),
SubCommandEnum::AddUserDevice(ref config) => {
add_user_device_api_command(&mut socket, &config.device_config)
}
SubCommandEnum::AddVdpa(ref config) => {
add_vdpa_api_command(&mut socket, &config.vdpa_config)
}
SubCommandEnum::AddVsock(ref config) => {
add_vsock_api_command(&mut socket, &config.vsock_config)
}
SubCommandEnum::Snapshot(ref config) => {
snapshot_api_command(&mut socket, &config.snapshot_config)
}
SubCommandEnum::Restore(ref config) => {
restore_api_command(&mut socket, &config.restore_config)
}
SubCommandEnum::Coredump(ref config) => {
coredump_api_command(&mut socket, &config.coredump_config)
}
SubCommandEnum::SendMigration(ref config) => send_migration_api_command(
&mut socket,
&config.send_migration_config,
config.send_migration_local,
),
SubCommandEnum::ReceiveMigration(ref config) => {
receive_migration_api_command(&mut socket, &config.receive_migration_config)
}
SubCommandEnum::Create(ref config) => create_api_command(&mut socket, &config.vm_config),
SubCommandEnum::Version(_) => {
// Already handled outside of this function
panic!()
}
}
}
#[derive(FromArgs, PartialEq, Debug)]
@@ -661,21 +419,6 @@ struct TopLevel {
#[argh(option, long = "api-socket")]
/// HTTP API socket path (UNIX domain socket)
api_socket: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-service-name")]
/// well known name of the dbus service
dbus_name: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-object-path")]
/// object path which the interface is being served at
dbus_path: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(switch, long = "dbus-system-bus")]
/// use the system bus instead of a session bus
dbus_system_bus: bool,
}
#[derive(FromArgs, PartialEq, Debug)]
@@ -949,52 +692,12 @@ fn main() {
return;
}
let mut target_api = match (
&toplevel.api_socket,
#[cfg(feature = "dbus_api")]
&toplevel.dbus_name,
#[cfg(feature = "dbus_api")]
&toplevel.dbus_path,
) {
#[cfg(not(feature = "dbus_api"))]
(Some(ref api_socket),) => TargetApi::HttpApi(
UnixStream::connect(api_socket).unwrap_or_else(|e| {
eprintln!("Error opening HTTP socket: {e}");
process::exit(1)
}),
PhantomData,
),
#[cfg(feature = "dbus_api")]
(Some(ref api_socket), None, None) => TargetApi::HttpApi(
UnixStream::connect(api_socket).unwrap_or_else(|e| {
eprintln!("Error opening HTTP socket: {e}");
process::exit(1)
}),
PhantomData,
),
#[cfg(feature = "dbus_api")]
(None, Some(ref dbus_name), Some(ref dbus_path)) => TargetApi::DBusApi(
DBusApi1ProxyBlocking::new_connection(dbus_name, dbus_path, toplevel.dbus_system_bus)
.map_err(Error::DBusApiClient)
.unwrap_or_else(|e| {
eprintln!("Error creating D-Bus proxy: {e}");
process::exit(1)
}),
),
#[cfg(feature = "dbus_api")]
(Some(_), Some(_) | None, Some(_) | None) => {
println!(
"`api-socket` and (dbus-service-name or dbus-object-path) are mutually exclusive"
);
process::exit(1);
}
_ => {
println!("Please either provide the api-socket option or dbus-service-name and dbus-object-path options");
process::exit(1);
}
};
if toplevel.api_socket.is_none() {
println!("Please specify --api-socket");
process::exit(1)
}
if let Err(e) = target_api.do_command(&toplevel) {
if let Err(e) = do_command(&toplevel) {
eprintln!("Error running command: {e}");
process::exit(1)
};

View File

@@ -18,8 +18,6 @@ use std::os::unix::io::{FromRawFd, RawFd};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use thiserror::Error;
#[cfg(feature = "dbus_api")]
use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
use vmm::config;
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::block_signal;
@@ -59,12 +57,6 @@ enum Error {
ParsingApiSocket(std::num::ParseIntError),
#[error("Error parsing --event-monitor: {0}")]
ParsingEventMonitor(option_parser::OptionParserError),
#[cfg(feature = "dbus_api")]
#[error("`--dbus-object-path` option isn't provided")]
MissingDBusObjectPath,
#[cfg(feature = "dbus_api")]
#[error("`--dbus-service-name` option isn't provided")]
MissingDBusServiceName,
#[error("Error parsing --event-monitor: path or fd required")]
BareEventMonitor,
#[error("Error doing event monitor I/O: {0}")]
@@ -146,19 +138,19 @@ fn default_rng() -> String {
/// Launch a cloud-hypervisor VMM.
pub struct TopLevel {
#[argh(option, long = "cpus", default = "default_vcpus()")]
/// boot=<boot_vcpus>, max=<max_vcpus>, topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>, kvm_hyperv=on|off, max_phys_bits=<maximum_number_of_physical_bits>, affinity=<list_of_vcpus_with_their_associated_cpuset>, features=<list_of_features_to_enable>
/// boot=<boot_vcpus>,max=<max_vcpus>,topology=<threads_per_core>:<cores_per_die>:<dies_per_package>:<packages>,kvm_hyperv=on|off,max_phys_bits=<maximum_number_of_physical_bits>,affinity=<list_of_vcpus_with_their_associated_cpuset>,features=<list_of_features_to_enable>
cpus: String,
#[argh(option, long = "platform")]
/// num_pci_segments=<num_pci_segments>, iommu_segments=<list_of_segments>, serial_number=<dmi_device_serial_number>, uuid=<dmi_device_uuid>, oem_strings=<list_of_strings>
/// num_pci_segments=<num_pci_segments>,iommu_segments=<list_of_segments>,serial_number=<dmi_device_serial_number>,uuid=<dmi_device_uuid>,oem_strings=<list_of_strings>
platform: Option<String>,
#[argh(option, long = "memory", default = "default_memory()")]
/// size=<guest_memory_size>, mergeable=on|off, shared=on|off, hugepages=on|off, hugepage_size=<hugepage_size>, hotplug_method=acpi|virtio-mem, hotplug_size=<hotpluggable_memory_size>, hotplugged_size=<hotplugged_memory_size>, prefault=on|off, thp=on|off
/// size=<guest_memory_size>,mergeable=on|off,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,hotplug_method=acpi|virtio-mem,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off,thp=on|off
memory: String,
#[argh(option, long = "memory-zone")]
/// size=<guest_memory_region_size>, file=<backing_file>, shared=on|off, hugepages=on|off, hugepage_size=<hugepage_size>, host_numa_node=<node_id>, id=<zone_identifier>, hotplug_size=<hotpluggable_memory_size>, hotplugged_size=<hotplugged_memory_size>, prefault=on|off
/// size=<guest_memory_region_size>,file=<backing_file>,shared=on|off,hugepages=on|off,hugepage_size=<hugepage_size>,host_numa_node=<node_id>,id=<zone_identifier>,hotplug_size=<hotpluggable_memory_size>,hotplugged_size=<hotplugged_memory_size>,prefault=on|off
memory_zone: Vec<String>,
#[argh(option, long = "firmware")]
@@ -178,27 +170,27 @@ pub struct TopLevel {
cmdline: Option<String>,
#[argh(option, long = "disk")]
/// path=<disk_image_path>, readonly=on|off, direct=on|off, iommu=on|off, num_queues=<number_of_queues>, queue_size=<size_of_each_queue>, vhost_user=on|off, socket=<vhost_user_socket_path>, bw_size=<bytes>, bw_one_time_burst=<bytes>, bw_refill_time=<ms>, ops_size=<io_ops>, ops_one_time_burst=<io_ops>, ops_refill_time=<ms>, id=<device_id>, pci_segment=<segment_id>
/// path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,vhost_user=on|off,socket=<vhost_user_socket_path>,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,id=<device_id>,pci_segment=<segment_id>
disk: Vec<String>,
#[argh(option, long = "net")]
/// tap=<if_name>, ip=<ip_addr>, mask=<net_mask>, mac=<mac_addr>, fd=<fd1,fd2...>, iommu=on|off, num_queues=<number_of_queues>, queue_size=<size_of_each_queue>, id=<device_id>, vhost_user=<vhost_user_enable>, socket=<vhost_user_socket_path>, vhost_mode=client|server, bw_size=<bytes>, bw_one_time_burst=<bytes>, bw_refill_time=<ms>, ops_size=<io_ops>, ops_one_time_burst=<io_ops>, ops_refill_time=<ms>, pci_segment=<segment_id>offload_tso=on|off, offload_ufo=on|off, offload_csum=on|off
/// tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1,fd2...>,iommu=on|off,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,pci_segment=<segment_id>offload_tso=on|off,offload_ufo=on|off,offload_csum=on|off
net: Vec<String>,
#[argh(option, long = "rng", default = "default_rng()")]
/// src=<entropy_source_path>, iommu=on|off
/// src=<entropy_source_path>,iommu=on|off
rng: String,
#[argh(option, long = "balloon")]
/// size=<balloon_size>, deflate_on_oom=on|off, free_page_reporting=on|off
/// size=<balloon_size>,deflate_on_oom=on|off,free_page_reporting=on|off
balloon: Option<String>,
#[argh(option, long = "fs")]
/// tag=<tag_name>, socket=<socket_path>, num_queues=<number_of_queues>, queue_size=<size_of_each_queue>, id=<device_id>, pci_segment=<segment_id>
/// tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,pci_segment=<segment_id>
fs: Vec<String>,
#[argh(option, long = "pmem")]
/// file=<backing_file_path>, size=<persistent_memory_size>, iommu=on|off, discard_writes=on|off, id=<device_id>, pci_segment=<segment_id>
/// file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,discard_writes=on|off,id=<device_id>,pci_segment=<segment_id>
pmem: Vec<String>,
#[argh(option, long = "serial", default = "String::from(\"null\")")]
@@ -206,27 +198,27 @@ pub struct TopLevel {
serial: String,
#[argh(option, long = "console", default = "String::from(\"tty\")")]
/// off|null|pty|tty|file=/path/to/a/file, iommu=on|off
/// off|null|pty|tty|file=/path/to/a/file,iommu=on|off
console: String,
#[argh(option, long = "device")]
/// path=<device_path>, iommu=on|off, id=<device_id>, pci_segment=<segment_id>
/// path=<device_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
device: Vec<String>,
#[argh(option, long = "user-device")]
/// socket=<socket_path>, id=<device_id>, pci_segment=<segment_id>
/// socket=<socket_path>,id=<device_id>,pci_segment=<segment_id>
user_device: Vec<String>,
#[argh(option, long = "vdpa")]
/// path=<device_path>, num_queues=<number_of_queues>, iommu=on|off, id=<device_id>, pci_segment=<segment_id>
/// path=<device_path>,num_queues=<number_of_queues>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
vdpa: Vec<String>,
#[argh(option, long = "vsock")]
/// cid=<context_id>, socket=<socket_path>, iommu=on|off, id=<device_id>, pci_segment=<segment_id>
/// cid=<context_id>,socket=<socket_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>
vsock: Option<String>,
#[argh(option, long = "numa")]
/// guest_numa_id=<node_id>, cpus=<cpus_id>, distances=<list_of_distances_to_destination_nodes>, memory_zones=<list_of_memory_zones>, sgx_epc_sections=<list_of_sgx_epc_sections>
/// guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections>
numa: Vec<String>,
#[argh(switch, long = "watchdog")]
@@ -245,27 +237,12 @@ pub struct TopLevel {
/// path=<path/to/a/file>|fd=<fd>
api_socket: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-service-name")]
/// well known name of the service
dbus_name: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(option, long = "dbus-object-path")]
/// object path to serve the dbus interface
dbus_path: Option<String>,
#[cfg(feature = "dbus_api")]
#[argh(switch, long = "dbus-system-bus")]
/// use the system bus instead of a session bus
dbus_system_bus: bool,
#[argh(option, long = "event-monitor")]
/// path=<path/to/a/file>|fd=<fd>
event_monitor: Option<String>,
#[argh(option, long = "restore")]
/// source_url=<source_url>, prefault=on|off
/// source_url=<source_url>,prefault=on|off
restore: Option<String>,
#[argh(option, long = "seccomp", default = "String::from(\"true\")")]
@@ -278,7 +255,7 @@ pub struct TopLevel {
#[cfg(target_arch = "x86_64")]
#[argh(option, long = "sgx-epc")]
/// id=<epc_section_identifier>, size=<epc_section_size>, prefault=on|off
/// id=<epc_section_identifier>,size=<epc_section_size>,prefault=on|off
sgx_epc: Vec<String>,
#[cfg(feature = "guest_debug")]
@@ -439,18 +416,6 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
(None, None)
};
#[cfg(feature = "dbus_api")]
let dbus_options = match (&toplevel.dbus_name, &toplevel.dbus_path) {
(Some(ref name), Some(ref path)) => Ok(Some(DBusApiOptions {
service_name: name.to_owned(),
object_path: path.to_owned(),
system_bus: toplevel.dbus_system_bus,
})),
(Some(_), None) => Err(Error::MissingDBusObjectPath),
(None, Some(_)) => Err(Error::MissingDBusServiceName),
(None, None) => Ok(None),
}?;
if let Some(ref monitor_config) = toplevel.event_monitor {
let mut parser = OptionParser::new();
parser.add("path").add("fd");
@@ -480,7 +445,7 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
let (api_request_sender, api_request_receiver) = channel();
let api_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateApiEventFd)?;
let api_request_sender_clone = api_request_sender.clone();
let http_sender = api_request_sender.clone();
let seccomp_action = match &toplevel.seccomp as &str {
"true" => SeccompAction::Trap,
"false" => SeccompAction::Allow,
@@ -548,14 +513,12 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
let vmm_thread_handle = vmm::start_vmm_thread(
let vmm_thread = vmm::start_vmm_thread(
vmm::VmmVersionInfo::new(env!("BUILD_VERSION"), env!("CARGO_PKG_VERSION")),
&api_socket_path,
api_socket_fd,
#[cfg(feature = "dbus_api")]
dbus_options,
api_evt.try_clone().unwrap(),
api_request_sender_clone,
http_sender,
api_request_receiver,
#[cfg(feature = "guest_debug")]
gdb_socket_path,
@@ -605,17 +568,11 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
}
}
vmm_thread_handle
.thread_handle
vmm_thread
.join()
.map_err(Error::ThreadJoin)?
.map_err(Error::VmmThread)?;
#[cfg(feature = "dbus_api")]
if let Some(chs) = vmm_thread_handle.dbus_shutdown_chs {
dbus_api_graceful_shutdown(chs);
}
r.map(|_| api_socket_path)
}

View File

@@ -1,2 +0,0 @@
instance-id: cloud
local-hostname: cloud

View File

@@ -1,7 +0,0 @@
version: 2
ethernets:
ens4:
match:
macaddress: 12:34:56:78:90:ab
addresses: [192.168.249.2/24]
gateway4: 192.168.249.1

View File

@@ -1,10 +0,0 @@
#cloud-config
users:
- name: cloud
passwd: $6$7125787751a8d18a$sHwGySomUA1PawiNFWVCKYQN.Ec.Wzz0JtPPL1MvzFrkwmop2dq7.4CYf03A5oemPQ4pOFCCrtCelvFBEle/K.
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: False
inactive: False
shell: /bin/bash
ssh_pwauth: True

View File

@@ -9,7 +9,7 @@ dirs = "5.0.0"
epoll = "4.3.1"
libc = "0.2.139"
once_cell = "1.17.1"
serde = { version = "1.0.164", features = ["rc", "derive"] }
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_json = "1.0.96"
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
vmm-sys-util = "0.11.0"

View File

@@ -249,8 +249,7 @@ impl DiskConfig for UbuntuDiskConfig {
.unwrap()
.join("test_data")
.join("cloud-init")
.join("ubuntu")
.join("ci");
.join("ubuntu");
vec!["meta-data"].iter().for_each(|x| {
rate_limited_copy(source_file_dir.join(x), cloud_init_directory.join(x))

View File

@@ -10,6 +10,8 @@
extern crate test_infra;
use api_client::simple_api_command;
use api_client::simple_api_full_command;
use net_util::MacAddr;
use std::collections::HashMap;
use std::fs;
@@ -73,324 +75,6 @@ const DIRECT_KERNEL_BOOT_CMDLINE: &str =
const CONSOLE_TEST_STRING: &str = "Started OpenBSD Secure Shell server";
// This enum exists to make it more convenient to
// implement test for both D-Bus and REST APIs.
enum TargetApi {
// API socket
HttpApi(String),
// well known service name, object path
DBusApi(String, String),
}
impl TargetApi {
fn new_http_api(tmp_dir: &TempDir) -> Self {
Self::HttpApi(temp_api_path(tmp_dir))
}
fn new_dbus_api(tmp_dir: &TempDir) -> Self {
// `tmp_dir` is in the form of "/tmp/chXXXXXX"
// and we take the `chXXXXXX` part as a unique identifier for the guest
let id = tmp_dir.as_path().file_name().unwrap().to_str().unwrap();
Self::DBusApi(
format!("org.cloudhypervisor.{id}"),
format!("/org/cloudhypervisor/{id}"),
)
}
fn guest_args(&self) -> Vec<&str> {
match self {
TargetApi::HttpApi(api_socket) => {
vec!["--api-socket", api_socket.as_str()]
}
TargetApi::DBusApi(service_name, object_path) => {
vec![
"--dbus-service-name",
service_name.as_str(),
"--dbus-object-path",
object_path.as_str(),
]
}
}
}
fn remote_args(&self) -> Vec<&str> {
// `guest_args` and `remote_args` are consistent with each other
self.guest_args()
}
fn remote_command(&self, command: &str, arg: Option<&str>) -> bool {
let mut cmd = Command::new(clh_command("ch-remote"));
cmd.args(self.remote_args());
cmd.arg(command);
if let Some(arg) = arg {
cmd.arg(arg);
}
let output = cmd.output().unwrap();
if output.status.success() {
true
} else {
eprintln!("Error running ch-remote command: {:?}", &cmd);
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("stderr: {stderr}");
false
}
}
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check that it looks as expected.
fn _test_api_create_boot(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
assert!(target_api.remote_command("create", Some(create_config),));
// Then boot it
assert!(target_api.remote_command("boot", None));
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check it can be shutdown and then
// booted again
fn _test_api_shutdown(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
let r = std::panic::catch_unwind(|| {
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then shut it down
assert!(target_api.remote_command("shutdown", None));
// Then boot it again
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check it can be deleted and then recreated
// booted again.
fn _test_api_delete(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
let r = std::panic::catch_unwind(|| {
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then delete it
assert!(target_api.remote_command("delete", None));
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it again
assert!(target_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check that it looks as expected.
// Then we pause the VM, check that it's no longer available.
// Finally we resume the VM and check that it's available.
fn _test_api_pause_resume(target_api: TargetApi, guest: Guest) {
let mut child = GuestCommand::new(&guest)
.args(target_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API server is running
assert!(target_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
assert!(target_api.remote_command("create", Some(create_config)));
// Then boot it
assert!(target_api.remote_command("boot", None));
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// We now pause the VM
assert!(target_api.remote_command("pause", None));
// Check pausing again fails
assert!(!target_api.remote_command("pause", None));
thread::sleep(std::time::Duration::new(2, 0));
// SSH into the VM should fail
assert!(ssh_command_ip(
"grep -c processor /proc/cpuinfo",
&guest.network.guest_ip,
2,
5
)
.is_err());
// Resume the VM
assert!(target_api.remote_command("resume", None));
// Check resuming again fails
assert!(!target_api.remote_command("resume", None));
thread::sleep(std::time::Duration::new(2, 0));
// Now we should be able to SSH back in and get the right number of CPUs
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
fn prepare_virtiofsd(tmp_dir: &TempDir, shared_dir: &str) -> (std::process::Child, String) {
let mut workload_path = dirs::home_dir().unwrap();
workload_path.push("workloads");
@@ -2226,7 +1910,7 @@ fn enable_guest_watchdog(guest: &Guest, watchdog_sec: u32) {
}
mod common_parallel {
use std::{fs::OpenOptions, io::SeekFrom};
use std::{fs::OpenOptions, io::SeekFrom, os::unix::net::UnixStream};
use crate::*;
@@ -4295,7 +3979,7 @@ mod common_parallel {
.ssh_command_l1(
"sudo /mnt/ch-remote \
--api-socket /tmp/ch_api.sock \
resize --memory 1073741824",
resize --memory=1073741824",
)
.unwrap();
assert!(guest.get_total_memory_l2().unwrap_or_default() > 960_000);
@@ -4354,35 +4038,279 @@ mod common_parallel {
}
#[test]
fn test_api_http_shutdown() {
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check that it looks as expected.
fn test_api_create_boot() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_shutdown(TargetApi::new_http_api(&guest.tmp_dir), guest)
let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, http_body).unwrap();
remote_command(
&api_socket,
"create",
Some(temp_config_path.as_os_str().to_str().unwrap()),
);
// Then boot it
remote_command(&api_socket, "boot", None);
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
#[test]
fn test_api_http_delete() {
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check it can be shutdown and then
// booted again
fn test_api_shutdown() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_delete(TargetApi::new_http_api(&guest.tmp_dir), guest);
let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let r = std::panic::catch_unwind(|| {
// socket has to be created again inside catch_unwind block to avoid errors
let mut socket = UnixStream::connect(&api_socket).unwrap();
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then shut it down
simple_api_command(&mut socket, "PUT", "shutdown", None).unwrap();
// Then boot it again
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
#[test]
fn test_api_http_pause_resume() {
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check it can be deleted and then recreated
// booted again.
fn test_api_delete() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_pause_resume(TargetApi::new_http_api(&guest.tmp_dir), guest)
let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let r = std::panic::catch_unwind(|| {
// socket has to be created again inside catch_unwind block to avoid errors
let mut socket = UnixStream::connect(&api_socket).unwrap();
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then delete it
simple_api_command(&mut socket, "PUT", "delete", None).unwrap();
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it again
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
#[test]
fn test_api_http_create_boot() {
// Start cloud-hypervisor with no VM parameters, only the API server running.
// From the API: Create a VM, boot it and check that it looks as expected.
// Then we pause the VM, check that it's no longer available.
// Finally we resume the VM and check that it's available.
fn test_api_pause_resume() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_create_boot(TargetApi::new_http_api(&guest.tmp_dir), guest)
let api_socket = temp_api_path(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest)
.args(["--api-socket", &api_socket])
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
let mut socket = UnixStream::connect(&api_socket).unwrap();
// Verify API server is running
simple_api_full_command(&mut socket, "GET", "vmm.ping", None).unwrap();
// Create the VM first
let cpu_count: u8 = 4;
let http_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
simple_api_command(&mut socket, "PUT", "create", Some(&http_body)).unwrap();
// Then boot it
simple_api_command(&mut socket, "PUT", "boot", None).unwrap();
thread::sleep(std::time::Duration::new(20, 0));
let r = std::panic::catch_unwind(|| {
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// We now pause the VM
assert!(remote_command(&api_socket, "pause", None));
// Check pausing again fails
assert!(!remote_command(&api_socket, "pause", None));
thread::sleep(std::time::Duration::new(2, 0));
// SSH into the VM should fail
assert!(ssh_command_ip(
"grep -c processor /proc/cpuinfo",
&guest.network.guest_ip,
2,
5
)
.is_err());
// Resume the VM
assert!(remote_command(&api_socket, "resume", None));
// Check resuming again fails
assert!(!remote_command(&api_socket, "resume", None));
thread::sleep(std::time::Duration::new(2, 0));
// Now we should be able to SSH back in and get the right number of CPUs
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
#[test]
@@ -4996,6 +4924,7 @@ mod common_parallel {
handle_child_output(r, &output);
}
#[allow(clippy::useless_conversion)]
fn create_loop_device(backing_file_path: &str, block_size: u32, num_retries: usize) -> String {
const LOOP_CONFIGURE: u64 = 0x4c0a;
const LOOP_CTL_GET_FREE: u64 = 0x4c82;
@@ -5056,9 +4985,12 @@ mod common_parallel {
.unwrap();
// Request a free loop device
let loop_device_number =
unsafe { libc::ioctl(loop_ctl_file.as_raw_fd(), LOOP_CTL_GET_FREE as _) };
let loop_device_number = unsafe {
libc::ioctl(
loop_ctl_file.as_raw_fd(),
LOOP_CTL_GET_FREE.try_into().unwrap(),
)
};
if loop_device_number < 0 {
panic!("Couldn't find a free loop device");
}
@@ -5090,7 +5022,7 @@ mod common_parallel {
let ret = unsafe {
libc::ioctl(
loop_device_file.as_raw_fd(),
LOOP_CONFIGURE as _,
LOOP_CONFIGURE.try_into().unwrap(),
&loop_config,
)
};
@@ -6861,116 +6793,6 @@ mod common_parallel {
}
}
mod dbus_api {
use crate::*;
// Start cloud-hypervisor with no VM parameters, running both the HTTP
// and DBus APIs. Alternate calls to the external APIs (HTTP and DBus)
// to create a VM, boot it, and verify that it can be shut down and then
// booted again.
#[test]
fn test_api_dbus_and_http_interleaved() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
let dbus_api = TargetApi::new_dbus_api(&guest.tmp_dir);
let http_api = TargetApi::new_http_api(&guest.tmp_dir);
let mut child = GuestCommand::new(&guest)
.args(dbus_api.guest_args())
.args(http_api.guest_args())
.capture_output()
.spawn()
.unwrap();
thread::sleep(std::time::Duration::new(1, 0));
// Verify API servers are running
assert!(dbus_api.remote_command("ping", None));
assert!(http_api.remote_command("ping", None));
// Create the VM first
let cpu_count: u8 = 4;
let request_body = guest.api_create_body(
cpu_count,
direct_kernel_boot_path().to_str().unwrap(),
DIRECT_KERNEL_BOOT_CMDLINE,
);
let temp_config_path = guest.tmp_dir.as_path().join("config");
std::fs::write(&temp_config_path, request_body).unwrap();
let create_config = temp_config_path.as_os_str().to_str().unwrap();
let r = std::panic::catch_unwind(|| {
// Create the VM
assert!(dbus_api.remote_command("create", Some(create_config),));
// Then boot it
assert!(http_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
// Sync and shutdown without powering off to prevent filesystem
// corruption.
guest.ssh_command("sync").unwrap();
guest.ssh_command("sudo shutdown -H now").unwrap();
// Wait for the guest to be fully shutdown
thread::sleep(std::time::Duration::new(20, 0));
// Then shutdown the VM
assert!(dbus_api.remote_command("shutdown", None));
// Then boot it again
assert!(http_api.remote_command("boot", None));
guest.wait_vm_boot(None).unwrap();
// Check that the VM booted as expected
assert_eq!(guest.get_cpu_count().unwrap_or_default() as u8, cpu_count);
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
});
let _ = child.kill();
let output = child.wait_with_output().unwrap();
handle_child_output(r, &output);
}
#[test]
fn test_api_dbus_create_boot() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_create_boot(TargetApi::new_dbus_api(&guest.tmp_dir), guest)
}
#[test]
fn test_api_dbus_shutdown() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_shutdown(TargetApi::new_dbus_api(&guest.tmp_dir), guest)
}
#[test]
fn test_api_dbus_delete() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_delete(TargetApi::new_dbus_api(&guest.tmp_dir), guest);
}
#[test]
fn test_api_dbus_pause_resume() {
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
let guest = Guest::new(Box::new(focal));
_test_api_pause_resume(TargetApi::new_dbus_api(&guest.tmp_dir), guest)
}
}
mod common_sequential {
#[cfg(not(feature = "mshv"))]
use crate::*;
@@ -8282,7 +8104,7 @@ mod vfio {
.ssh_command_l1(
"sudo /mnt/ch-remote \
--api-socket /tmp/ch_api.sock \
resize --memory 1073741824",
resize --memory=1073741824",
)
.unwrap();
assert!(guest.get_total_memory_l2().unwrap_or_default() > 960_000);

View File

@@ -8,7 +8,7 @@ edition = "2021"
libc = "0.2.139"
log = "0.4.17"
once_cell = "1.17.1"
serde = { version = "1.0.164", features = ["rc", "derive"] }
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_json = "1.0.96"
[features]

View File

@@ -11,5 +11,5 @@ crc32c = "0.6.3"
libc = "0.2.139"
log = "0.4.17"
remain = "0.2.6"
thiserror = "1.0.40"
uuid = { version = "1.3.4", features = ["v4"] }
thiserror = "1.0.39"
uuid = { version = "1.3.0", features = ["v4"] }

View File

@@ -14,10 +14,10 @@ libc = "0.2.139"
log = "0.4.17"
option_parser = { path = "../option_parser" }
qcow = { path = "../qcow" }
vhost = { version = "0.7.0", features = ["vhost-user-slave"] }
vhost-user-backend = "0.9.0"
vhost = { version = "0.6.0", features = ["vhost-user-slave"] }
vhost-user-backend = "0.8.0"
virtio-bindings = "0.2.0"
virtio-queue = "0.8.0"
vm-memory = "0.11.0"
virtio-queue = "0.7.1"
vm-memory = "0.10.0"
vmm-sys-util = "0.11.0"

View File

@@ -13,9 +13,9 @@ libc = "0.2.139"
log = "0.4.17"
net_util = { path = "../net_util" }
option_parser = { path = "../option_parser" }
vhost = { version = "0.7.0", features = ["vhost-user-slave"] }
vhost-user-backend = "0.9.0"
vhost = { version = "0.6.0", features = ["vhost-user-slave"] }
vhost-user-backend = "0.8.0"
virtio-bindings = "0.2.0"
vm-memory = "0.11.0"
vm-memory = "0.10.0"
vmm-sys-util = "0.11.0"

View File

@@ -8,7 +8,7 @@ edition = "2021"
default = []
[dependencies]
anyhow = "1.0.71"
anyhow = "1.0.70"
arc-swap = "1.5.1"
block_util = { path = "../block_util" }
byteorder = "1.4.3"
@@ -22,18 +22,18 @@ net_util = { path = "../net_util" }
pci = { path = "../pci" }
rate_limiter = { path = "../rate_limiter" }
seccompiler = "0.3.0"
serde = { version = "1.0.164", features = ["derive"] }
serde = { version = "1.0.156", features = ["derive"] }
serde_json = "1.0.96"
serial_buffer = { path = "../serial_buffer" }
thiserror = "1.0.40"
thiserror = "1.0.39"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vhost = { version = "0.7.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
vhost = { version = "0.6.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
virtio-bindings = { version = "0.2.0", features = ["virtio-v5_0_0"] }
virtio-queue = "0.8.0"
virtio-queue = "0.7.1"
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.11.0"

View File

@@ -48,13 +48,14 @@ use super::pci_common_config::VirtioPciCommonConfigState;
/// Vector value used to disable MSI for a queue.
const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff;
#[allow(clippy::enum_variant_names)]
enum PciCapabilityType {
Common = 1,
Notify = 2,
Isr = 3,
Device = 4,
Pci = 5,
SharedMemory = 8,
CommonConfig = 1,
NotifyConfig = 2,
IsrConfig = 3,
DeviceConfig = 4,
PciConfig = 5,
SharedMemoryConfig = 8,
}
// This offset represents the 2 bytes omitted from the VirtioPciCap structure
@@ -209,7 +210,7 @@ impl PciCapability for VirtioPciCfgCap {
impl VirtioPciCfgCap {
fn new() -> Self {
VirtioPciCfgCap {
cap: VirtioPciCap::new(PciCapabilityType::Pci, 0, 0, 0),
cap: VirtioPciCap::new(PciCapabilityType::PciConfig, 0, 0, 0),
..Default::default()
}
}
@@ -666,7 +667,7 @@ impl VirtioPciDevice {
) -> std::result::Result<(), PciDeviceError> {
// Add pointers to the different configuration structures from the PCI capabilities.
let common_cap = VirtioPciCap::new(
PciCapabilityType::Common,
PciCapabilityType::CommonConfig,
settings_bar,
COMMON_CONFIG_BAR_OFFSET as u32,
COMMON_CONFIG_SIZE as u32,
@@ -676,7 +677,7 @@ impl VirtioPciDevice {
.map_err(PciDeviceError::CapabilitiesSetup)?;
let isr_cap = VirtioPciCap::new(
PciCapabilityType::Isr,
PciCapabilityType::IsrConfig,
settings_bar,
ISR_CONFIG_BAR_OFFSET as u32,
ISR_CONFIG_SIZE as u32,
@@ -687,7 +688,7 @@ impl VirtioPciDevice {
// TODO(dgreid) - set based on device's configuration size?
let device_cap = VirtioPciCap::new(
PciCapabilityType::Device,
PciCapabilityType::DeviceConfig,
settings_bar,
DEVICE_CONFIG_BAR_OFFSET as u32,
DEVICE_CONFIG_SIZE as u32,
@@ -697,7 +698,7 @@ impl VirtioPciDevice {
.map_err(PciDeviceError::CapabilitiesSetup)?;
let notify_cap = VirtioPciNotifyCap::new(
PciCapabilityType::Notify,
PciCapabilityType::NotifyConfig,
settings_bar,
NOTIFICATION_BAR_OFFSET as u32,
NOTIFICATION_SIZE as u32,
@@ -1056,7 +1057,7 @@ impl PciDevice for VirtioPciDevice {
for (idx, shm) in shm_list.region_list.iter().enumerate() {
let shm_cap = VirtioPciCap64::new(
PciCapabilityType::SharedMemory,
PciCapabilityType::SharedMemoryConfig,
VIRTIO_SHM_BAR_INDEX as u8,
idx as u8,
shm.offset,

View File

@@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
libc = "0.2.139"
vm-memory = "0.11.0"
vm-memory = "0.10.0"
[target.'cfg(target_arch = "aarch64")'.dependencies]
arch = { path = "../arch" }

View File

@@ -63,6 +63,7 @@ impl GsiAllocator {
}
#[cfg(target_arch = "aarch64")]
#[allow(clippy::new_without_default)]
/// New GSI allocator
pub fn new() -> Self {
GsiAllocator {
@@ -105,10 +106,3 @@ impl GsiAllocator {
Ok(irq)
}
}
#[cfg(target_arch = "aarch64")]
impl Default for GsiAllocator {
fn default() -> Self {
GsiAllocator::new()
}
}

View File

@@ -10,11 +10,11 @@ kvm = ["vfio-ioctls/kvm"]
mshv = ["vfio-ioctls/mshv"]
[dependencies]
anyhow = "1.0.71"
anyhow = "1.0.70"
hypervisor = { path = "../hypervisor" }
thiserror = "1.0.40"
serde = { version = "1.0.164", features = ["rc", "derive"] }
thiserror = "1.0.39"
serde = { version = "1.0.156", features = ["rc", "derive"] }
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vm-memory = { version = "0.11.0", features = ["backend-mmap"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap"] }
vmm-sys-util = "0.11.0"

View File

@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
edition = "2021"
[dependencies]
anyhow = "1.0.71"
thiserror = "1.0.40"
serde = { version = "1.0.164", features = ["rc", "derive"] }
anyhow = "1.0.70"
thiserror = "1.0.39"
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_json = "1.0.96"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }

View File

@@ -12,8 +12,8 @@ use versionize::{VersionMap, Versionize};
pub mod protocol;
/// Global VMM version for versioning
const MAJOR_VERSION: u16 = 33;
const MINOR_VERSION: u16 = 0;
const MAJOR_VERSION: u16 = 32;
const MINOR_VERSION: u16 = 1;
const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;
pub trait VersionMapped {

View File

@@ -9,5 +9,5 @@ default = []
[dependencies]
log = "0.4.17"
virtio-queue = "0.8.0"
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
virtio-queue = "0.7.1"
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }

View File

@@ -6,7 +6,6 @@ edition = "2021"
[features]
default = []
dbus_api = ["blocking", "futures", "zbus"]
guest_debug = ["kvm", "gdbstub", "gdbstub_arch"]
kvm = ["hypervisor/kvm", "vfio-ioctls/kvm", "vm-device/kvm", "pci/kvm"]
mshv = ["hypervisor/mshv", "vfio-ioctls/mshv", "vm-device/mshv", "pci/mshv"]
@@ -15,21 +14,19 @@ tracing = ["tracer/tracing"]
[dependencies]
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
anyhow = "1.0.71"
anyhow = "1.0.70"
arc-swap = "1.5.1"
arch = { path = "../arch" }
bitflags = "2.3.3"
bitflags = "1.3.2"
block_util = { path = "../block_util" }
blocking = { version = "1.3.0", optional = true }
devices = { path = "../devices" }
epoll = "4.3.1"
event_monitor = { path = "../event_monitor" }
futures = { version = "0.3.27", optional = true }
gdbstub = { version = "0.6.4", optional = true }
gdbstub_arch = { version = "0.2.4", optional = true }
hypervisor = { path = "../hypervisor" }
libc = "0.2.139"
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
log = "0.4.17"
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
net_util = { path = "../net_util" }
@@ -38,25 +35,24 @@ option_parser = { path = "../option_parser" }
pci = { path = "../pci" }
qcow = { path = "../qcow" }
seccompiler = "0.3.0"
serde = { version = "1.0.164", features = ["rc", "derive"] }
serde = { version = "1.0.156", features = ["rc", "derive"] }
serde_json = "1.0.96"
serial_buffer = { path = "../serial_buffer" }
signal-hook = "0.3.15"
thiserror = "1.0.40"
thiserror = "1.0.39"
tracer = { path = "../tracer" }
uuid = "1.3.4"
uuid = "1.3.0"
versionize = "0.1.10"
versionize_derive = "0.1.4"
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
vfio_user = { git = "https://github.com/rust-vmm/vfio-user", branch = "main" }
vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" }
virtio-queue = "0.8.0"
virtio-queue = "0.7.1"
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-migration = { path = "../vm-migration" }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
zbus = { version = "3.11.1", optional = true }
zerocopy = "0.6.1"
zerocopy = "0.6.1"

View File

@@ -1,377 +0,0 @@
// Copyright © 2023 Sartura Ltd.
//
// SPDX-License-Identifier: Apache-2.0
//
use super::{ApiRequest, VmAction};
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::{Error as VmmError, Result as VmmResult};
use crate::{NetConfig, VmConfig};
use futures::channel::oneshot;
use futures::{executor, FutureExt};
use hypervisor::HypervisorType;
use seccompiler::{apply_filter, SeccompAction};
use std::panic::AssertUnwindSafe;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use std::thread;
use vmm_sys_util::eventfd::EventFd;
use zbus::fdo::{self, Result};
use zbus::zvariant::Optional;
use zbus::{dbus_interface, ConnectionBuilder};
pub type DBusApiShutdownChannels = (oneshot::Sender<()>, oneshot::Receiver<()>);
pub struct DBusApiOptions {
pub service_name: String,
pub object_path: String,
pub system_bus: bool,
}
pub struct DBusApi {
api_notifier: EventFd,
api_sender: futures::lock::Mutex<Sender<ApiRequest>>,
}
fn api_error(error: impl std::fmt::Debug) -> fdo::Error {
fdo::Error::Failed(format!("{error:?}"))
}
// This method is intended to ensure that the DBusApi thread has enough time to
// send a response to the VmmShutdown method call before it is terminated. If
// this step is omitted, the thread may be terminated before it can send a
// response, resulting in an error message stating that the message recipient
// disconnected from the message bus without providing a reply.
pub fn dbus_api_graceful_shutdown(ch: DBusApiShutdownChannels) {
let (send_shutdown, mut recv_done) = ch;
// send the shutdown signal and return
// if it errors out
if send_shutdown.send(()).is_err() {
return;
}
// loop until `recv_err` errors out
// or as long as the return value indicates
// "immediately stale" (None)
while let Ok(None) = recv_done.try_recv() {}
}
impl DBusApi {
pub fn new(api_notifier: EventFd, api_sender: Sender<ApiRequest>) -> Self {
Self {
api_notifier,
api_sender: futures::lock::Mutex::new(api_sender),
}
}
async fn clone_api_sender(&self) -> Sender<ApiRequest> {
// lock the async mutex, clone the `Sender` and then immediately
// drop the MutexGuard so that other tasks can clone the
// `Sender` as well
self.api_sender.lock().await.clone()
}
fn clone_api_notifier(&self) -> Result<EventFd> {
self.api_notifier
.try_clone()
.map_err(|err| fdo::Error::IOError(format!("{err:?}")))
}
async fn vm_action(&self, action: VmAction) -> Result<Optional<String>> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let result = blocking::unblock(move || super::vm_action(api_notifier, api_sender, action))
.await
.map_err(api_error)?
// We're using `from_utf8_lossy` here to not deal with the
// error case of `from_utf8` as we know that `b.body` is valid JSON.
.map(|b| String::from_utf8_lossy(&b.body).to_string());
Ok(result.into())
}
}
#[dbus_interface(name = "org.cloudhypervisor.DBusApi1")]
impl DBusApi {
async fn vmm_ping(&self) -> Result<String> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let result = blocking::unblock(move || super::vmm_ping(api_notifier, api_sender))
.await
.map_err(api_error)?;
serde_json::to_string(&result).map_err(api_error)
}
async fn vmm_shutdown(&self) -> Result<()> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
blocking::unblock(move || super::vmm_shutdown(api_notifier, api_sender))
.await
.map_err(api_error)
}
async fn vm_add_device(&self, device_config: String) -> Result<Optional<String>> {
let device_config = serde_json::from_str(&device_config).map_err(api_error)?;
self.vm_action(VmAction::AddDevice(Arc::new(device_config)))
.await
}
async fn vm_add_disk(&self, disk_config: String) -> Result<Optional<String>> {
let disk_config = serde_json::from_str(&disk_config).map_err(api_error)?;
self.vm_action(VmAction::AddDisk(Arc::new(disk_config)))
.await
}
async fn vm_add_fs(&self, fs_config: String) -> Result<Optional<String>> {
let fs_config = serde_json::from_str(&fs_config).map_err(api_error)?;
self.vm_action(VmAction::AddFs(Arc::new(fs_config))).await
}
async fn vm_add_net(&self, net_config: String) -> Result<Optional<String>> {
let mut net_config: NetConfig = serde_json::from_str(&net_config).map_err(api_error)?;
if net_config.fds.is_some() {
warn!("Ignoring FDs sent via the D-Bus request body");
net_config.fds = None;
}
self.vm_action(VmAction::AddNet(Arc::new(net_config))).await
}
async fn vm_add_pmem(&self, pmem_config: String) -> Result<Optional<String>> {
let pmem_config = serde_json::from_str(&pmem_config).map_err(api_error)?;
self.vm_action(VmAction::AddPmem(Arc::new(pmem_config)))
.await
}
async fn vm_add_user_device(&self, vm_add_user_device: String) -> Result<Optional<String>> {
let vm_add_user_device = serde_json::from_str(&vm_add_user_device).map_err(api_error)?;
self.vm_action(VmAction::AddUserDevice(Arc::new(vm_add_user_device)))
.await
}
async fn vm_add_vdpa(&self, vdpa_config: String) -> Result<Optional<String>> {
let vdpa_config = serde_json::from_str(&vdpa_config).map_err(api_error)?;
self.vm_action(VmAction::AddVdpa(Arc::new(vdpa_config)))
.await
}
async fn vm_add_vsock(&self, vsock_config: String) -> Result<Optional<String>> {
let vsock_config = serde_json::from_str(&vsock_config).map_err(api_error)?;
self.vm_action(VmAction::AddVsock(Arc::new(vsock_config)))
.await
}
async fn vm_boot(&self) -> Result<()> {
self.vm_action(VmAction::Boot).await.map(|_| ())
}
#[allow(unused_variables)]
// zbus doesn't support cfg attributes on interface methods
// as a workaround, we make the *call to the internal API* conditionally
// compile and return an error on unsupported platforms.
async fn vm_coredump(&self, vm_coredump_data: String) -> Result<()> {
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
{
let vm_coredump_data = serde_json::from_str(&vm_coredump_data).map_err(api_error)?;
self.vm_action(VmAction::Coredump(Arc::new(vm_coredump_data)))
.await
.map(|_| ())
}
#[cfg(not(all(target_arch = "x86_64", feature = "guest_debug")))]
Err(api_error(
"VmCoredump only works on x86_64 with the `guest_debug` feature enabled",
))
}
async fn vm_counters(&self) -> Result<Optional<String>> {
self.vm_action(VmAction::Counters).await
}
async fn vm_create(&self, vm_config: String) -> Result<()> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let mut vm_config: VmConfig = serde_json::from_str(&vm_config).map_err(api_error)?;
if let Some(ref mut nets) = vm_config.net {
if nets.iter().any(|net| net.fds.is_some()) {
warn!("Ignoring FDs sent via the D-Bus request body");
}
for net in nets {
net.fds = None;
}
}
blocking::unblock(move || {
super::vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
})
.await
.map_err(api_error)?;
Ok(())
}
async fn vm_delete(&self) -> Result<()> {
self.vm_action(VmAction::Delete).await.map(|_| ())
}
async fn vm_info(&self) -> Result<String> {
let api_sender = self.clone_api_sender().await;
let api_notifier = self.clone_api_notifier()?;
let result = blocking::unblock(move || super::vm_info(api_notifier, api_sender))
.await
.map_err(api_error)?;
serde_json::to_string(&result).map_err(api_error)
}
async fn vm_pause(&self) -> Result<()> {
self.vm_action(VmAction::Pause).await.map(|_| ())
}
async fn vm_power_button(&self) -> Result<()> {
self.vm_action(VmAction::PowerButton).await.map(|_| ())
}
async fn vm_reboot(&self) -> Result<()> {
self.vm_action(VmAction::Reboot).await.map(|_| ())
}
async fn vm_remove_device(&self, vm_remove_device: String) -> Result<()> {
let vm_remove_device = serde_json::from_str(&vm_remove_device).map_err(api_error)?;
self.vm_action(VmAction::RemoveDevice(Arc::new(vm_remove_device)))
.await
.map(|_| ())
}
async fn vm_resize(&self, vm_resize: String) -> Result<()> {
let vm_resize = serde_json::from_str(&vm_resize).map_err(api_error)?;
self.vm_action(VmAction::Resize(Arc::new(vm_resize)))
.await
.map(|_| ())
}
async fn vm_resize_zone(&self, vm_resize_zone: String) -> Result<()> {
let vm_resize_zone = serde_json::from_str(&vm_resize_zone).map_err(api_error)?;
self.vm_action(VmAction::ResizeZone(Arc::new(vm_resize_zone)))
.await
.map(|_| ())
}
async fn vm_restore(&self, restore_config: String) -> Result<()> {
let restore_config = serde_json::from_str(&restore_config).map_err(api_error)?;
self.vm_action(VmAction::Restore(Arc::new(restore_config)))
.await
.map(|_| ())
}
async fn vm_receive_migration(&self, receive_migration_data: String) -> Result<()> {
let receive_migration_data =
serde_json::from_str(&receive_migration_data).map_err(api_error)?;
self.vm_action(VmAction::ReceiveMigration(Arc::new(receive_migration_data)))
.await
.map(|_| ())
}
async fn vm_send_migration(&self, send_migration_data: String) -> Result<()> {
let send_migration_data = serde_json::from_str(&send_migration_data).map_err(api_error)?;
self.vm_action(VmAction::SendMigration(Arc::new(send_migration_data)))
.await
.map(|_| ())
}
async fn vm_resume(&self) -> Result<()> {
self.vm_action(VmAction::Resume).await.map(|_| ())
}
async fn vm_shutdown(&self) -> Result<()> {
self.vm_action(VmAction::Shutdown).await.map(|_| ())
}
async fn vm_snapshot(&self, vm_snapshot_config: String) -> Result<()> {
let vm_snapshot_config = serde_json::from_str(&vm_snapshot_config).map_err(api_error)?;
self.vm_action(VmAction::Snapshot(Arc::new(vm_snapshot_config)))
.await
.map(|_| ())
}
}
pub fn start_dbus_thread(
dbus_options: DBusApiOptions,
api_notifier: EventFd,
api_sender: Sender<ApiRequest>,
seccomp_action: &SeccompAction,
exit_evt: EventFd,
hypervisor_type: HypervisorType,
) -> VmmResult<(thread::JoinHandle<VmmResult<()>>, DBusApiShutdownChannels)> {
let dbus_iface = DBusApi::new(api_notifier, api_sender);
let connection = executor::block_on(async move {
let conn_builder = if dbus_options.system_bus {
ConnectionBuilder::system()?
} else {
ConnectionBuilder::session()?
};
conn_builder
.internal_executor(false)
.name(dbus_options.service_name)?
.serve_at(dbus_options.object_path, dbus_iface)?
.build()
.await
})
.map_err(VmmError::CreateDBusSession)?;
let (send_shutdown, recv_shutdown) = oneshot::channel::<()>();
let (send_done, recv_done) = oneshot::channel::<()>();
// Retrieve seccomp filter for API thread
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::DBusApi, hypervisor_type)
.map_err(VmmError::CreateSeccompFilter)?;
let thread_join_handle = thread::Builder::new()
.name("dbus-thread".to_string())
.spawn(move || {
// Apply seccomp filter for API thread.
if !api_seccomp_filter.is_empty() {
apply_filter(&api_seccomp_filter)
.map_err(VmmError::ApplySeccompFilter)
.map_err(|e| {
error!("Error applying seccomp filter: {:?}", e);
exit_evt.write(1).ok();
e
})?;
}
std::panic::catch_unwind(AssertUnwindSafe(move || {
executor::block_on(async move {
let recv_shutdown = recv_shutdown.fuse();
let executor_tick = futures::future::Fuse::terminated();
futures::pin_mut!(recv_shutdown, executor_tick);
executor_tick.set(connection.executor().tick().fuse());
loop {
futures::select! {
_ = executor_tick => executor_tick.set(connection.executor().tick().fuse()),
_ = recv_shutdown => {
send_done.send(()).ok();
break;
},
}
}
})
}))
.map_err(|_| {
error!("dbus-api thread panicked");
exit_evt.write(1).ok()
})
.ok();
Ok(())
})
.map_err(VmmError::DBusThreadSpawn)?;
Ok((thread_join_handle, (send_shutdown, recv_done)))
}

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
//
use self::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown};
use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown};
use crate::api::{ApiError, ApiRequest, VmAction};
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::{Error as VmmError, Result};
@@ -23,8 +23,6 @@ use std::sync::Arc;
use std::thread;
use vmm_sys_util::eventfd::EventFd;
pub mod http_endpoint;
/// Errors associated with VMM management
#[derive(Debug)]
pub enum HttpError {
@@ -284,7 +282,7 @@ fn start_http_thread(
hypervisor_type: HypervisorType,
) -> Result<thread::JoinHandle<Result<()>>> {
// Retrieve seccomp filter for API thread
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::Api, hypervisor_type)
.map_err(VmmError::CreateSeccompFilter)?;
thread::Builder::new()

View File

@@ -28,15 +28,12 @@
//! response channel Receiver.
//! 5. The thread handles the response and forwards potential errors.
#[cfg(feature = "dbus_api")]
pub mod dbus;
pub mod http;
#[cfg(feature = "dbus_api")]
pub use self::dbus::start_dbus_thread;
pub use self::http::start_http_fd_thread;
pub use self::http::start_http_path_thread;
pub mod http;
pub mod http_endpoint;
use crate::config::{
DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, UserDeviceConfig,
VdpaConfig, VmConfig, VsockConfig,

View File

@@ -98,13 +98,6 @@ macro_rules! extract_bits_64 {
};
}
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
macro_rules! extract_bits_64_without_offset {
($value: tt, $length: tt) => {
$value & (!0u64 >> (64 - $length))
};
}
pub const CPU_MANAGER_ACPI_SIZE: usize = 0xc;
#[derive(Debug, Error)]
@@ -723,7 +716,7 @@ impl CpuManager {
);
self.cpuid = {
let phys_bits = physical_bits(hypervisor, self.config.max_phys_bits);
let phys_bits = physical_bits(self.config.max_phys_bits);
arch::generate_common_cpuid(
hypervisor,
topology,
@@ -1618,7 +1611,8 @@ impl CpuManager {
// PA or IPA size is determined
let tcr_ips = extract_bits_64!(tcr_el1, 32, 3);
let pa_range = extract_bits_64_without_offset!(id_aa64mmfr0_el1, 4);
#[allow(clippy::identity_op)]
let pa_range = extract_bits_64!(id_aa64mmfr0_el1, 0, 4);
// The IPA size in TCR_BL1 and PA Range in ID_AA64MMFR0_EL1 should match.
// To be safe, we use the minimum value if they are different.
let pa_range = std::cmp::min(tcr_ips, pa_range);
@@ -1651,7 +1645,8 @@ impl CpuManager {
let descaddrmask = descaddrmask & !indexmask_grainsize;
// Translation table base address
let mut descaddr: u64 = extract_bits_64_without_offset!(ttbr1_el1, 48);
#[allow(clippy::identity_op)]
let mut descaddr: u64 = extract_bits_64!(ttbr1_el1, 0, 48);
// In the case of FEAT_LPA and FEAT_LPA2, the initial translation table
// addresss bits [48:51] comes from TTBR1_EL1 bits [2:5].
if pa_size == 52 {
@@ -1992,7 +1987,6 @@ impl Aml for CpuManager {
true,
acpi_address.0,
acpi_address.0 + CPU_MANAGER_ACPI_SIZE as u64 - 1,
None,
)]),
),
// OpRegion and Fields map MMIO range into individual field values

View File

@@ -4302,7 +4302,6 @@ impl Aml for DeviceManager {
true,
self.acpi_address.0,
self.acpi_address.0 + DEVICE_MANAGER_ACPI_SIZE as u64 - 1,
None,
)]),
),
// OpRegion and Fields map MMIO range into individual field values

View File

@@ -25,8 +25,6 @@ use crate::migration::{recv_vm_config, recv_vm_state};
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::vm::{Error as VmError, Vm, VmState};
use anyhow::anyhow;
#[cfg(feature = "dbus_api")]
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
use libc::{tcsetattr, termios, EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW};
use memory_manager::MemoryManagerSnapshotData;
use pci::PciBdf;
@@ -115,16 +113,6 @@ pub enum Error {
#[error("Error spawning HTTP thread: {0}")]
HttpThreadSpawn(#[source] io::Error),
/// Cannot create D-Bus thread
#[cfg(feature = "dbus_api")]
#[error("Error spawning D-Bus thread: {0}")]
DBusThreadSpawn(#[source] io::Error),
/// Cannot start D-Bus session
#[cfg(feature = "dbus_api")]
#[error("Error starting D-Bus session: {0}")]
CreateDBusSession(#[source] zbus::Error),
/// Cannot handle the VM STDIN stream
#[error("Error handling VM stdin: {0:?}")]
Stdin(VmError),
@@ -294,7 +282,6 @@ pub fn start_vmm_thread(
vmm_version: VmmVersionInfo,
http_path: &Option<String>,
http_fd: Option<RawFd>,
#[cfg(feature = "dbus_api")] dbus_options: Option<DBusApiOptions>,
api_event: EventFd,
api_sender: Sender<ApiRequest>,
api_receiver: Receiver<ApiRequest>,
@@ -304,7 +291,7 @@ pub fn start_vmm_thread(
exit_event: EventFd,
seccomp_action: &SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>,
) -> Result<VmmThreadHandle> {
) -> Result<thread::JoinHandle<Result<()>>> {
#[cfg(feature = "guest_debug")]
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
#[cfg(feature = "guest_debug")]
@@ -314,7 +301,7 @@ pub fn start_vmm_thread(
#[cfg(feature = "guest_debug")]
let gdb_vm_debug_event = vm_debug_event.try_clone().map_err(Error::EventFdClone)?;
let api_event_clone = api_event.try_clone().map_err(Error::EventFdClone)?;
let http_api_event = api_event.try_clone().map_err(Error::EventFdClone)?;
let hypervisor_type = hypervisor.hypervisor_type();
// Retrieve seccomp filter
@@ -355,28 +342,11 @@ pub fn start_vmm_thread(
.map_err(Error::VmmThreadSpawn)?
};
// The VMM thread is started, we can start the dbus thread
// and start serving HTTP requests
#[cfg(feature = "dbus_api")]
let dbus_shutdown_chs = match dbus_options {
Some(opts) => {
let (_, chs) = api::start_dbus_thread(
opts,
api_event_clone.try_clone().map_err(Error::EventFdClone)?,
api_sender.clone(),
seccomp_action,
exit_event.try_clone().map_err(Error::EventFdClone)?,
hypervisor_type,
)?;
Some(chs)
}
None => None,
};
// The VMM thread is started, we can start serving HTTP requests
if let Some(http_path) = http_path {
api::start_http_path_thread(
http_path,
api_event_clone,
http_api_event,
api_sender,
seccomp_action,
exit_event,
@@ -385,7 +355,7 @@ pub fn start_vmm_thread(
} else if let Some(http_fd) = http_fd {
api::start_http_fd_thread(
http_fd,
api_event_clone,
http_api_event,
api_sender,
seccomp_action,
exit_event,
@@ -407,11 +377,7 @@ pub fn start_vmm_thread(
.map_err(Error::GdbThreadSpawn)?;
}
Ok(VmmThreadHandle {
thread_handle: thread,
#[cfg(feature = "dbus_api")]
dbus_shutdown_chs,
})
Ok(thread)
}
#[derive(Clone, Deserialize, Serialize)]
@@ -437,12 +403,6 @@ impl VmmVersionInfo {
}
}
pub struct VmmThreadHandle {
pub thread_handle: thread::JoinHandle<Result<()>>,
#[cfg(feature = "dbus_api")]
pub dbus_shutdown_chs: Option<DBusApiShutdownChannels>,
}
pub struct Vmm {
epoll: EpollContext,
exit_evt: EventFd,
@@ -1259,8 +1219,7 @@ impl Vmm {
))
})?;
let phys_bits =
vm::physical_bits(&self.hypervisor, config.lock().unwrap().cpus.max_phys_bits);
let phys_bits = vm::physical_bits(config.lock().unwrap().cpus.max_phys_bits);
let memory_manager = MemoryManager::new(
vm,
@@ -1576,8 +1535,7 @@ impl Vmm {
let vm_config = vm.get_config();
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
let common_cpuid = {
let phys_bits =
vm::physical_bits(&hypervisor, vm_config.lock().unwrap().cpus.max_phys_bits);
let phys_bits = vm::physical_bits(vm_config.lock().unwrap().cpus.max_phys_bits);
arch::generate_common_cpuid(
&hypervisor,
None,
@@ -1767,7 +1725,7 @@ impl Vmm {
let dest_cpuid = &{
let vm_config = &src_vm_config.lock().unwrap();
let phys_bits = vm::physical_bits(&self.hypervisor, vm_config.cpus.max_phys_bits);
let phys_bits = vm::physical_bits(vm_config.cpus.max_phys_bits);
arch::generate_common_cpuid(
&self.hypervisor.clone(),
None,

View File

@@ -2334,7 +2334,6 @@ impl Aml for MemoryMethods {
true,
0x0000_0000_0000_0000u64,
0xFFFF_FFFF_FFFF_FFFEu64,
None,
)]),
),
&aml::CreateQWordField::new(
@@ -2417,7 +2416,6 @@ impl Aml for MemoryManager {
true,
acpi_address.0,
acpi_address.0 + MEMORY_MANAGER_ACPI_SIZE as u64 - 1,
None,
)]),
),
// OpRegion and Fields map MMIO range into individual field values
@@ -2513,7 +2511,6 @@ impl Aml for MemoryManager {
true,
min,
max,
None,
)]),
),
&aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0xfu8)]),

View File

@@ -353,19 +353,17 @@ impl Aml for PciSegment {
layout::MEM_32BIT_DEVICES_START.0 as u32,
(layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1)
as u32,
None,
),
&aml::AddressSpace::new_memory(
aml::AddressSpaceCachable::NotCacheable,
true,
self.start_of_device_area,
self.end_of_device_area,
None,
),
#[cfg(target_arch = "x86_64")]
&aml::AddressSpace::new_io(0u16, 0x0cf7u16, None),
&aml::AddressSpace::new_io(0u16, 0x0cf7u16),
#[cfg(target_arch = "x86_64")]
&aml::AddressSpace::new_io(0x0d00u16, 0xffffu16, None),
&aml::AddressSpace::new_io(0x0d00u16, 0xffffu16),
]),
)
} else {
@@ -383,7 +381,6 @@ impl Aml for PciSegment {
true,
self.start_of_device_area,
self.end_of_device_area,
None,
),
]),
)

View File

@@ -12,9 +12,7 @@ use seccompiler::{
use std::convert::TryInto;
pub enum Thread {
HttpApi,
#[cfg(feature = "dbus_api")]
DBusApi,
Api,
SignalHandler,
Vcpu,
Vmm,
@@ -748,9 +746,9 @@ fn vcpu_thread_rules(
])
}
// The filter containing the white listed syscall rules required by the HTTP API to
// The filter containing the white listed syscall rules required by the API to
// function.
fn http_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
fn api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
Ok(vec![
(libc::SYS_accept4, vec![]),
(libc::SYS_brk, vec![]),
@@ -777,50 +775,12 @@ fn http_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError>
])
}
// The filter containing the white listed syscall rules required by the D-Bus API
// to function.
#[cfg(feature = "dbus_api")]
fn dbus_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
Ok(vec![
(libc::SYS_brk, vec![]),
(libc::SYS_clock_gettime, vec![]),
(libc::SYS_clone, vec![]),
(libc::SYS_clone3, vec![]),
(libc::SYS_close, vec![]),
(libc::SYS_dup, vec![]),
(libc::SYS_epoll_ctl, vec![]),
(libc::SYS_exit, vec![]),
(libc::SYS_futex, vec![]),
(libc::SYS_getrandom, vec![]),
(libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_mprotect, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_prctl, vec![]),
(libc::SYS_recvmsg, vec![]),
// musl is missing this constant
// (libc::SYS_rseq, vec![]),
#[cfg(target_arch = "x86_64")]
(334, vec![]),
#[cfg(target_arch = "aarch64")]
(293, vec![]),
(libc::SYS_rt_sigprocmask, vec![]),
(libc::SYS_sched_getaffinity, vec![]),
(libc::SYS_sendmsg, vec![]),
(libc::SYS_set_robust_list, vec![]),
(libc::SYS_sigaltstack, vec![]),
(libc::SYS_write, vec![]),
])
}
fn get_seccomp_rules(
thread_type: Thread,
hypervisor_type: HypervisorType,
) -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
match thread_type {
Thread::HttpApi => Ok(http_api_thread_rules()?),
#[cfg(feature = "dbus_api")]
Thread::DBusApi => Ok(dbus_api_thread_rules()?),
Thread::Api => Ok(api_thread_rules()?),
Thread::SignalHandler => Ok(signal_handler_thread_rules()?),
Thread::Vcpu => Ok(vcpu_thread_rules(hypervisor_type)?),
Thread::Vmm => Ok(vmm_thread_rules(hypervisor_type)?),

View File

@@ -417,8 +417,8 @@ impl VmOps for VmOpsHandler {
}
}
pub fn physical_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>, max_phys_bits: u8) -> u8 {
let host_phys_bits = get_host_cpu_phys_bits(hypervisor);
pub fn physical_bits(max_phys_bits: u8) -> u8 {
let host_phys_bits = get_host_cpu_phys_bits();
cmp::min(host_phys_bits, max_phys_bits)
}
@@ -762,7 +762,7 @@ impl Vm {
tdx_enabled,
)?;
let phys_bits = physical_bits(&hypervisor, vm_config.lock().unwrap().cpus.max_phys_bits);
let phys_bits = physical_bits(vm_config.lock().unwrap().cpus.max_phys_bits);
let memory_manager = if let Some(snapshot) =
snapshot_from_id(snapshot.as_ref(), MEMORY_MANAGER_SNAPSHOT_ID)
@@ -2413,10 +2413,7 @@ impl Snapshottable for Vm {
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
let common_cpuid = {
let phys_bits = physical_bits(
&self.hypervisor,
self.config.lock().unwrap().cpus.max_phys_bits,
);
let phys_bits = physical_bits(self.config.lock().unwrap().cpus.max_phys_bits);
arch::generate_common_cpuid(
&self.hypervisor,
None,