mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Compare commits
178 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e0e632361 | ||
|
|
1c9df58440 | ||
|
|
1917e86206 | ||
|
|
787f5085b6 | ||
|
|
fec39ccf51 | ||
|
|
115085eb9e | ||
|
|
802e9009b9 | ||
|
|
37fbece885 | ||
|
|
ec6e87479f | ||
|
|
5b51024ef7 | ||
|
|
99a98f270d | ||
|
|
c0af33a4c9 | ||
|
|
0cf868b5c1 | ||
|
|
3da60dfa5e | ||
|
|
1c8a91940e | ||
|
|
8c117a8117 | ||
|
|
7d0aa1fd02 | ||
|
|
a0190b16aa | ||
|
|
1bd8eb68ff | ||
|
|
8363eddd97 | ||
|
|
70aa5bc231 | ||
|
|
b8ce1816e9 | ||
|
|
43eed4861f | ||
|
|
268c4ea214 | ||
|
|
29a0ddc58e | ||
|
|
4079e4f1ad | ||
|
|
fba0b5f93c | ||
|
|
05cc5f596c | ||
|
|
c1b33329db | ||
|
|
402a24a688 | ||
|
|
0809e7f675 | ||
|
|
a718716831 | ||
|
|
5a9dd7489c | ||
|
|
dec8d619d4 | ||
|
|
c39cdb3c1f | ||
|
|
26ae8b6d5c | ||
|
|
de31b3fadc | ||
|
|
b06ad85604 | ||
|
|
8d89736c68 | ||
|
|
55ee8eb482 | ||
|
|
1017157bb6 | ||
|
|
499989fb17 | ||
|
|
ce0f30bb54 | ||
|
|
fce202a90c | ||
|
|
95626ae564 | ||
|
|
1e1012c31b | ||
|
|
56207dc3a0 | ||
|
|
57fdaa3a39 | ||
|
|
19c5c0af9f | ||
|
|
e26e7b4d72 | ||
|
|
e8d6c9ecb0 | ||
|
|
0712027eb1 | ||
|
|
7ea5d7a416 | ||
|
|
7023d4ff84 | ||
|
|
5f31ad319e | ||
|
|
5083549afe | ||
|
|
beed5e5d6d | ||
|
|
7feb25daf0 | ||
|
|
f2574897cd | ||
|
|
2698924c0d | ||
|
|
6bd4927d11 | ||
|
|
f485922b78 | ||
|
|
1faaa2cd0a | ||
|
|
6c630f4908 | ||
|
|
3b39c41a01 | ||
|
|
309e346c4c | ||
|
|
3f6833b315 | ||
|
|
a862c804ba | ||
|
|
83914d80a2 | ||
|
|
a7aecb5eee | ||
|
|
a64d27f841 | ||
|
|
7a458d85d1 | ||
|
|
a92d852848 | ||
|
|
0664647109 | ||
|
|
f2c813e1cf | ||
|
|
f00df25d40 | ||
|
|
c016a0d4d3 | ||
|
|
5c96fbb19b | ||
|
|
b955f09b80 | ||
|
|
79ad6f8536 | ||
|
|
be4f04a906 | ||
|
|
f8415debe9 | ||
|
|
9014a5e59c | ||
|
|
612621bb46 | ||
|
|
18e1d2caf5 | ||
|
|
4be406a308 | ||
|
|
7bf2a2c382 | ||
|
|
7df80220ec | ||
|
|
84d2511de0 | ||
|
|
374263ddac | ||
|
|
7b76b6b61e | ||
|
|
e06f12673b | ||
|
|
89e658d9ff | ||
|
|
5ac68534b1 | ||
|
|
6acceaa87c | ||
|
|
a3898fd8e1 | ||
|
|
7026622c24 | ||
|
|
41e5384f15 | ||
|
|
681a30bd15 | ||
|
|
1976157761 | ||
|
|
0b1e626fe3 | ||
|
|
79bc42f3c2 | ||
|
|
eca75dcfc9 | ||
|
|
600d1664ee | ||
|
|
e6208d1c0e | ||
|
|
cf635c64d7 | ||
|
|
42ca292dce | ||
|
|
b7338c96eb | ||
|
|
bf4af14e73 | ||
|
|
24a3505f0b | ||
|
|
20f3691474 | ||
|
|
c56a3ce59a | ||
|
|
18f7a37b48 | ||
|
|
98496434b9 | ||
|
|
0b2994a6e3 | ||
|
|
553e6857bd | ||
|
|
083ce323c0 | ||
|
|
8fff4c1af3 | ||
|
|
35ecfb6ec5 | ||
|
|
a61a013311 | ||
|
|
b509fd94cd | ||
|
|
631af8a4f7 | ||
|
|
aa14fe214a | ||
|
|
45e3f49bba | ||
|
|
03db406645 | ||
|
|
ba1e89139a | ||
|
|
21d40d7489 | ||
|
|
c90a0ffff6 | ||
|
|
dc7931f592 | ||
|
|
841627f10a | ||
|
|
97fdb65012 | ||
|
|
d17d70fae1 | ||
|
|
71d1296d09 | ||
|
|
0d58e51a3a | ||
|
|
97012c511d | ||
|
|
c10e2a79b1 | ||
|
|
f3870ae638 | ||
|
|
3c0b389c82 | ||
|
|
9881c94f84 | ||
|
|
ceb8151747 | ||
|
|
2554f2a8d4 | ||
|
|
036af673e6 | ||
|
|
990362e314 | ||
|
|
83d57d3cce | ||
|
|
a0babfb9c1 | ||
|
|
3394a59466 | ||
|
|
f8e0376f6d | ||
|
|
6379074264 | ||
|
|
5dbc72948f | ||
|
|
04d111ee15 | ||
|
|
a9623c7a28 | ||
|
|
4baf85857a | ||
|
|
e3d2917d5f | ||
|
|
a84b540b65 | ||
|
|
8eb162e3d7 | ||
|
|
2804608a1c | ||
|
|
e0125653b1 | ||
|
|
e431a48201 | ||
|
|
c143cb3af0 | ||
|
|
b6feae0ace | ||
|
|
520aff2efc | ||
|
|
41880919a0 | ||
|
|
865291d840 | ||
|
|
346ee09e6b | ||
|
|
59012ccc6e | ||
|
|
3f17f4657b | ||
|
|
086ca9b935 | ||
|
|
9b724303ac | ||
|
|
948573db0d | ||
|
|
0352846af2 | ||
|
|
8b3f0043b0 | ||
|
|
9d532acab6 | ||
|
|
df2a7c1764 | ||
|
|
3f5dac988f | ||
|
|
3096f1d42f | ||
|
|
42a4e593ce | ||
|
|
b56da6ee24 | ||
|
|
90dcbc3232 |
5
.github/workflows/build.yaml
vendored
5
.github/workflows/build.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
- "1.62"
|
||||
- "1.64"
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
@@ -42,6 +42,9 @@ 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
|
||||
|
||||
|
||||
26
.github/workflows/hadolint.yaml
vendored
Normal file
26
.github/workflows/hadolint.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
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
|
||||
1192
Cargo.lock
generated
1192
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
20
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cloud-hypervisor"
|
||||
version = "31.0.0"
|
||||
version = "33.0.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.62"
|
||||
rust-version = "1.64"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -29,7 +29,7 @@ strip = false
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
api_client = { path = "api_client" }
|
||||
argh = "0.1.9"
|
||||
dhat = { version = "0.3.2", optional = true }
|
||||
@@ -40,30 +40,34 @@ libc = "0.2.139"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
option_parser = { path = "option_parser" }
|
||||
seccompiler = "0.3.0"
|
||||
serde_json = "1.0.95"
|
||||
serde_json = "1.0.96"
|
||||
signal-hook = "0.3.15"
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
tpm = { path = "tpm"}
|
||||
tracer = { path = "tracer" }
|
||||
vmm = { path = "vmm" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
vm-memory = "0.10.0"
|
||||
vm-memory = "0.11.0"
|
||||
zbus = { version = "3.11.1", optional = true }
|
||||
|
||||
# List of patched crates
|
||||
[patch.crates-io]
|
||||
kvm-bindings = { git = "https://github.com/cloud-hypervisor/kvm-bindings", branch = "ch-v0.6.0-tdx" }
|
||||
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls", branch = "main" }
|
||||
versionize_derive = { git = "https://github.com/cloud-hypervisor/versionize_derive", branch = "ch" }
|
||||
vhost = { git = "https://github.com/rust-vmm/vhost", branch = "main" }
|
||||
|
||||
[dev-dependencies]
|
||||
dirs = "4.0.0"
|
||||
dirs = "5.0.0"
|
||||
net_util = { path = "net_util" }
|
||||
once_cell = "1.17.1"
|
||||
serde_json = "1.0.95"
|
||||
serde_json = "1.0.96"
|
||||
test_infra = { path = "test_infra" }
|
||||
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"]
|
||||
|
||||
@@ -140,7 +140,10 @@ 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.
|
||||
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.
|
||||
|
||||
```shell
|
||||
$ sudo setcap cap_net_admin+ep ./cloud-hypervisor
|
||||
|
||||
@@ -9,18 +9,18 @@ default = []
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.39"
|
||||
uuid = "1.3.0"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.40"
|
||||
uuid = "1.3.4"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = { version = "0.11.0", features = ["with-serde"] }
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ 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};
|
||||
|
||||
@@ -40,8 +42,12 @@ 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 = 6;
|
||||
const FIRST_VCPU_PHANDLE: u32 = 8;
|
||||
|
||||
// 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;
|
||||
@@ -81,6 +87,97 @@ 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>(
|
||||
@@ -159,6 +256,54 @@ 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)?;
|
||||
@@ -173,6 +318,21 @@ 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() {
|
||||
@@ -186,6 +346,36 @@ 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")?;
|
||||
@@ -260,32 +450,86 @@ fn create_memory_node(
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
} else {
|
||||
let last_addr = guest_mem.last_addr().raw_value();
|
||||
if last_addr < super::layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
// Case 1: all RAM is under the hole
|
||||
let mem_size = last_addr - super::layout::RAM_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||
let memory_node = fdt.begin_node("memory")?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
} else {
|
||||
// Case 2: RAM is split by the hole
|
||||
// Region 1: RAM before the hole
|
||||
let mem_size = super::layout::MEM_32BIT_RESERVED_START.raw_value()
|
||||
- super::layout::RAM_START.raw_value();
|
||||
let mem_reg_prop = [super::layout::RAM_START.raw_value(), mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", super::layout::RAM_START.raw_value());
|
||||
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
|
||||
let ram_regions = {
|
||||
let mut ram_regions = Vec::new();
|
||||
let mut current_start = guest_mem
|
||||
.iter()
|
||||
.next()
|
||||
.map(GuestMemoryRegion::start_addr)
|
||||
.expect("GuestMemory must have one memory region at least")
|
||||
.raw_value();
|
||||
let mut current_end = current_start;
|
||||
|
||||
for (start, size) in guest_mem
|
||||
.iter()
|
||||
.map(|m| (m.start_addr().raw_value(), m.len()))
|
||||
{
|
||||
if current_end == start {
|
||||
// This zone is continuous with the previous one.
|
||||
current_end += size;
|
||||
} else {
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
current_start = start;
|
||||
current_end = start + size;
|
||||
}
|
||||
}
|
||||
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
ram_regions
|
||||
};
|
||||
|
||||
if ram_regions.len() > 2 {
|
||||
panic!(
|
||||
"There should be up to two non-continuous regions, devidided by the
|
||||
gap at the end of 32bit address space."
|
||||
);
|
||||
}
|
||||
|
||||
// Create the memory node for memory region before the gap
|
||||
{
|
||||
let (first_region_start, first_region_end) = ram_regions
|
||||
.first()
|
||||
.expect("There should be at last one memory region");
|
||||
let ram_start = super::layout::RAM_START.raw_value();
|
||||
let mem_32bit_reserved_start = super::layout::MEM_32BIT_RESERVED_START.raw_value();
|
||||
|
||||
if !((first_region_start <= &ram_start)
|
||||
&& (first_region_end > &ram_start)
|
||||
&& (first_region_end <= &mem_32bit_reserved_start))
|
||||
{
|
||||
panic!(
|
||||
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
|
||||
ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
|
||||
first_region_start, first_region_end, ram_start, mem_32bit_reserved_start
|
||||
);
|
||||
}
|
||||
|
||||
let mem_size = first_region_end - ram_start;
|
||||
let mem_reg_prop = [ram_start, mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", ram_start);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
fdt.end_node(memory_node)?;
|
||||
}
|
||||
|
||||
// Region 2: RAM after the hole
|
||||
let mem_size = last_addr - super::layout::RAM_64BIT_START.raw_value() + 1;
|
||||
let mem_reg_prop = [super::layout::RAM_64BIT_START.raw_value(), mem_size];
|
||||
let memory_node_name =
|
||||
format!("memory@{:x}", super::layout::RAM_64BIT_START.raw_value());
|
||||
// Create the memory map entry for memory region after the gap if any
|
||||
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
|
||||
let ram_64bit_start = super::layout::RAM_64BIT_START.raw_value();
|
||||
|
||||
if second_region_start != &ram_64bit_start {
|
||||
panic!(
|
||||
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
|
||||
second_region_start, ram_64bit_start
|
||||
);
|
||||
}
|
||||
|
||||
let mem_size = second_region_end - ram_64bit_start;
|
||||
let mem_reg_prop = [ram_64bit_start, mem_size];
|
||||
let memory_node_name = format!("memory@{:x}", ram_64bit_start);
|
||||
let memory_node = fdt.begin_node(&memory_node_name)?;
|
||||
fdt.property_string("device_type", "memory")?;
|
||||
fdt.property_array_u64("reg", &mem_reg_prop)?;
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestUsize};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryAtomic};
|
||||
|
||||
pub const _NSIG: i32 = 65;
|
||||
|
||||
@@ -83,8 +83,8 @@ pub fn configure_vcpu(
|
||||
Ok(mpidr)
|
||||
}
|
||||
|
||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
let mut regions = vec![
|
||||
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
vec![
|
||||
// 0 MiB ~ 256 MiB: UEFI, GIC and legacy devices
|
||||
(
|
||||
GuestAddress(0),
|
||||
@@ -103,39 +103,21 @@ pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, Region
|
||||
layout::PCI_MMCONFIG_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
];
|
||||
|
||||
let ram_32bit_space_size =
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START);
|
||||
|
||||
// RAM space
|
||||
// Case1: guest memory fits before the gap
|
||||
if size <= ram_32bit_space_size {
|
||||
regions.push((layout::RAM_START, size as usize, RegionType::Ram));
|
||||
// Case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
// Push memory before the gap
|
||||
regions.push((
|
||||
// 1GiB ~ 4032 MiB: RAM before the gap
|
||||
(
|
||||
layout::RAM_START,
|
||||
ram_32bit_space_size as usize,
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
// Other memory is placed after 4GiB
|
||||
regions.push((
|
||||
layout::RAM_64BIT_START,
|
||||
(size - ram_32bit_space_size) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
}
|
||||
|
||||
// Add the 32-bit reserved memory hole as a reserved region
|
||||
regions.push((
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_RESERVED_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
));
|
||||
|
||||
regions
|
||||
),
|
||||
// 4GiB ~ inf: RAM after the gap
|
||||
(layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
|
||||
// Add the 32-bit reserved memory hole as a reserved region
|
||||
(
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_RESERVED_SIZE as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||
@@ -198,11 +180,8 @@ pub fn initramfs_load_addr(
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
if host_cpu_phys_bits == 0 {
|
||||
// Host kernel does not support `get_host_ipa_limit`,
|
||||
// we return the default value 40 here.
|
||||
@@ -217,26 +196,12 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram_2gb() {
|
||||
let regions = arch_memory_regions((1usize << 31) as u64); //2GB
|
||||
assert_eq!(5, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!((1usize << 31), regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[4].2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arch_memory_regions_dram_4gb() {
|
||||
let regions = arch_memory_regions((1usize << 32) as u64); //4GB
|
||||
let ram_32bit_space_size =
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::RAM_START) as usize;
|
||||
fn test_arch_memory_regions_dram() {
|
||||
let regions = arch_memory_regions();
|
||||
assert_eq!(6, regions.len());
|
||||
assert_eq!(layout::RAM_START, regions[3].0);
|
||||
assert_eq!(ram_32bit_space_size, regions[3].1);
|
||||
assert_eq!(RegionType::Ram, regions[3].2);
|
||||
assert_eq!(RegionType::Reserved, regions[5].2);
|
||||
assert_eq!(RegionType::Ram, regions[4].2);
|
||||
assert_eq!(((1usize << 32) - ram_32bit_space_size), regions[4].1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::GuestMemoryMmap;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::{HypervisorCpuError, HypervisorError};
|
||||
use hypervisor::{CpuVendor, 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,
|
||||
@@ -210,7 +210,6 @@ impl From<Error> for super::Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CpuidReg {
|
||||
EAX,
|
||||
@@ -776,6 +775,13 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
// Set ApicId in cpuid for each vcpu
|
||||
// SAFETY: get host cpuid when eax=1
|
||||
let mut cpu_ebx = unsafe { core::arch::x86_64::__cpuid(1) }.ebx;
|
||||
cpu_ebx &= 0xffffff;
|
||||
cpu_ebx |= (id as u32) << 24;
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1, None, CpuidReg::EBX, cpu_ebx);
|
||||
|
||||
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation
|
||||
if !kvm_hyperv {
|
||||
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
|
||||
@@ -828,47 +834,29 @@ pub fn configure_vcpu(
|
||||
/// These should be used to configure the GuestMemory structure for the platform.
|
||||
/// For x86_64 all addresses are valid from the start of the kernel except a
|
||||
/// carve out at the end of 32bit address space.
|
||||
pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
let reserved_memory_gap_start = layout::MEM_32BIT_RESERVED_START
|
||||
.checked_add(layout::MEM_32BIT_DEVICES_SIZE)
|
||||
.expect("32-bit reserved region is too large");
|
||||
|
||||
let requested_memory_size = GuestAddress(size);
|
||||
let mut regions = Vec::new();
|
||||
|
||||
// case1: guest memory fits before the gap
|
||||
if size <= layout::MEM_32BIT_RESERVED_START.raw_value() {
|
||||
regions.push((GuestAddress(0), size as usize, RegionType::Ram));
|
||||
// case2: guest memory extends beyond the gap
|
||||
} else {
|
||||
// push memory before the gap
|
||||
regions.push((
|
||||
pub fn arch_memory_regions() -> Vec<(GuestAddress, usize, RegionType)> {
|
||||
vec![
|
||||
// 0 GiB ~ 3GiB: memory before the gap
|
||||
(
|
||||
GuestAddress(0),
|
||||
layout::MEM_32BIT_RESERVED_START.raw_value() as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
regions.push((
|
||||
layout::RAM_64BIT_START,
|
||||
requested_memory_size.unchecked_offset_from(layout::MEM_32BIT_RESERVED_START) as usize,
|
||||
RegionType::Ram,
|
||||
));
|
||||
}
|
||||
|
||||
// Add the 32-bit device memory hole as a sub region.
|
||||
regions.push((
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||
RegionType::SubRegion,
|
||||
));
|
||||
|
||||
// Add the 32-bit reserved memory hole as a sub region.
|
||||
regions.push((
|
||||
reserved_memory_gap_start,
|
||||
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
|
||||
RegionType::Reserved,
|
||||
));
|
||||
|
||||
regions
|
||||
),
|
||||
// 4 GiB ~ inf: memory after the gap
|
||||
(layout::RAM_64BIT_START, usize::MAX, RegionType::Ram),
|
||||
// 3 GiB ~ 3712 MiB: 32-bit device memory hole
|
||||
(
|
||||
layout::MEM_32BIT_RESERVED_START,
|
||||
layout::MEM_32BIT_DEVICES_SIZE as usize,
|
||||
RegionType::SubRegion,
|
||||
),
|
||||
// 3712 MiB ~ 3968 MiB: 32-bit reserved memory hole
|
||||
(
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_add(layout::MEM_32BIT_DEVICES_SIZE),
|
||||
(layout::MEM_32BIT_RESERVED_SIZE - layout::MEM_32BIT_DEVICES_SIZE) as usize,
|
||||
RegionType::Reserved,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Configures the system and should be called once per vm before starting vcpu threads.
|
||||
@@ -966,30 +954,102 @@ fn configure_pvh(
|
||||
// Create the memory map entries.
|
||||
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM);
|
||||
|
||||
let mem_end = guest_mem.last_addr();
|
||||
// Merge continuous memory regions into one region.
|
||||
// Note: memory regions from "GuestMemory" are sorted and non-zero sized.
|
||||
let ram_regions = {
|
||||
let mut ram_regions = Vec::new();
|
||||
let mut current_start = guest_mem
|
||||
.iter()
|
||||
.next()
|
||||
.map(GuestMemoryRegion::start_addr)
|
||||
.expect("GuestMemory must have one memory region at least")
|
||||
.raw_value();
|
||||
let mut current_end = current_start;
|
||||
|
||||
if mem_end < layout::MEM_32BIT_RESERVED_START {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::HIGH_RAM_START.raw_value(),
|
||||
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
|
||||
E820_RAM,
|
||||
);
|
||||
} else {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::HIGH_RAM_START.raw_value(),
|
||||
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
|
||||
E820_RAM,
|
||||
);
|
||||
if mem_end > layout::RAM_64BIT_START {
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
layout::RAM_64BIT_START.raw_value(),
|
||||
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
|
||||
E820_RAM,
|
||||
);
|
||||
for (start, size) in guest_mem
|
||||
.iter()
|
||||
.map(|m| (m.start_addr().raw_value(), m.len()))
|
||||
{
|
||||
if current_end == start {
|
||||
// This zone is continuous with the previous one.
|
||||
current_end += size;
|
||||
} else {
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
current_start = start;
|
||||
current_end = start + size;
|
||||
}
|
||||
}
|
||||
|
||||
ram_regions.push((current_start, current_end));
|
||||
|
||||
ram_regions
|
||||
};
|
||||
|
||||
if ram_regions.len() > 2 {
|
||||
error!(
|
||||
"There should be up to two non-continuous regions, devidided by the
|
||||
gap at the end of 32bit address space (e.g. between 3G and 4G)."
|
||||
);
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
// Create the memory map entry for memory region before the gap
|
||||
{
|
||||
let (first_region_start, first_region_end) =
|
||||
ram_regions.first().ok_or(super::Error::MemmapTableSetup)?;
|
||||
let high_ram_start = layout::HIGH_RAM_START.raw_value();
|
||||
let mem_32bit_reserved_start = layout::MEM_32BIT_RESERVED_START.raw_value();
|
||||
|
||||
if !((first_region_start <= &high_ram_start)
|
||||
&& (first_region_end > &high_ram_start)
|
||||
&& (first_region_end <= &mem_32bit_reserved_start))
|
||||
{
|
||||
error!(
|
||||
"Unexpected first memory region layout: (start: 0x{:08x}, end: 0x{:08x}).
|
||||
high_ram_start: 0x{:08x}, mem_32bit_reserved_start: 0x{:08x}",
|
||||
first_region_start, first_region_end, high_ram_start, mem_32bit_reserved_start
|
||||
);
|
||||
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
info!(
|
||||
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
|
||||
high_ram_start, first_region_end
|
||||
);
|
||||
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
high_ram_start,
|
||||
first_region_end - high_ram_start,
|
||||
E820_RAM,
|
||||
);
|
||||
}
|
||||
|
||||
// Create the memory map entry for memory region after the gap if any
|
||||
if let Some((second_region_start, second_region_end)) = ram_regions.get(1) {
|
||||
let ram_64bit_start = layout::RAM_64BIT_START.raw_value();
|
||||
|
||||
if second_region_start != &ram_64bit_start {
|
||||
error!(
|
||||
"Unexpected second memory region layout: start: 0x{:08x}, ram_64bit_start: 0x{:08x}",
|
||||
second_region_start, ram_64bit_start
|
||||
);
|
||||
|
||||
return Err(super::Error::MemmapTableSetup);
|
||||
}
|
||||
|
||||
info!(
|
||||
"create_memmap_entry, start: 0x{:08x}, end: 0x{:08x})",
|
||||
ram_64bit_start, second_region_end
|
||||
);
|
||||
add_memmap_entry(
|
||||
&mut memmap,
|
||||
ram_64bit_start,
|
||||
second_region_end - ram_64bit_start,
|
||||
E820_RAM,
|
||||
);
|
||||
}
|
||||
|
||||
add_memmap_entry(
|
||||
@@ -1079,7 +1139,7 @@ pub fn initramfs_load_addr(
|
||||
Ok(aligned_addr)
|
||||
}
|
||||
|
||||
pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
pub fn get_host_cpu_phys_bits(hypervisor: &Arc<dyn hypervisor::Hypervisor>) -> u8 {
|
||||
// SAFETY: call cpuid with valid leaves
|
||||
unsafe {
|
||||
let leaf = x86_64::__cpuid(0x8000_0000);
|
||||
@@ -1088,9 +1148,7 @@ pub fn get_host_cpu_phys_bits() -> u8 {
|
||||
// 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
|
||||
&& leaf.ebx == 0x6874_7541 // Vendor ID: AuthenticAMD
|
||||
&& leaf.ecx == 0x444d_4163
|
||||
&& leaf.edx == 0x6974_6e65
|
||||
&& matches!(hypervisor.get_cpu_vendor(), CpuVendor::AMD)
|
||||
&& x86_64::__cpuid(0x8000_001f).eax & 0x1 != 0
|
||||
{
|
||||
(x86_64::__cpuid(0x8000_001f).ebx >> 6) & 0x3f
|
||||
@@ -1225,16 +1283,8 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn regions_lt_4gb() {
|
||||
let regions = arch_memory_regions(1 << 29);
|
||||
assert_eq!(3, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1usize << 29, regions[0].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_gt_4gb() {
|
||||
let regions = arch_memory_regions((1 << 32) + 0x8000);
|
||||
fn regions_base_addr() {
|
||||
let regions = arch_memory_regions();
|
||||
assert_eq!(4, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(1 << 32), regions[1].0);
|
||||
@@ -1258,11 +1308,10 @@ mod tests {
|
||||
assert!(config_err.is_err());
|
||||
|
||||
// Now assigning some memory that falls before the 32bit memory hole.
|
||||
let mem_size = 128 << 20;
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
let arch_mem_regions = arch_memory_regions();
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
.filter(|r| r.2 == RegionType::Ram)
|
||||
.filter(|r| r.2 == RegionType::Ram && r.1 != usize::MAX)
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
@@ -1280,48 +1329,18 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now assigning some memory that is equal to the start of the 32bit memory hole.
|
||||
let mem_size = 3328 << 20;
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
.filter(|r| r.2 == RegionType::Ram)
|
||||
.map(|r| (r.0, r.1))
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
configure_system(
|
||||
&gm,
|
||||
GuestAddress(0),
|
||||
&None,
|
||||
no_vcpus,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Now assigning some memory that falls after the 32bit memory hole.
|
||||
let mem_size = 3330 << 20;
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
let arch_mem_regions = arch_memory_regions();
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
.filter(|r| r.2 == RegionType::Ram)
|
||||
.map(|r| (r.0, r.1))
|
||||
.map(|r| {
|
||||
if r.1 == usize::MAX {
|
||||
(r.0, 128 << 20)
|
||||
} else {
|
||||
(r.0, r.1)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
|
||||
configure_system(
|
||||
|
||||
@@ -13,13 +13,13 @@ libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
qcow = { path = "../qcow" }
|
||||
smallvec = "1.10.0"
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
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.7.1"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.8.0"
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
4
build.rs
4
build.rs
@@ -17,8 +17,8 @@ fn main() {
|
||||
}
|
||||
|
||||
// This println!() has a special behavior, as it will set the environment
|
||||
// variable BUILT_VERSION, so that it can be reused from the binary.
|
||||
// variable BUILD_VERSION, so that it can be reused from the binary.
|
||||
// Particularly, this is used from src/main.rs to display the exact
|
||||
// version.
|
||||
println!("cargo:rustc-env=BUILT_VERSION={version}");
|
||||
println!("cargo:rustc-env=BUILD_VERSION={version}");
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
bitflags = "2.3.3"
|
||||
byteorder = "1.4.3"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
tpm = { path = "../tpm" }
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = "0.10.0"
|
||||
vm-memory = "0.11.0"
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ impl Aml for AcpiGedDevice {
|
||||
true,
|
||||
self.address.0,
|
||||
self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
|
||||
None,
|
||||
)]),
|
||||
),
|
||||
&aml::OpRegion::new(
|
||||
|
||||
158
docs/api.md
158
docs/api.md
@@ -1,18 +1,21 @@
|
||||
- [Cloud Hypervisor API](#cloud-hypervisor-api)
|
||||
- [External API](#external-api)
|
||||
- [REST API](#rest-api)
|
||||
- [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)
|
||||
- [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)
|
||||
- [Command Line Interface](#command-line-interface)
|
||||
- [REST API and CLI Architectural Relationship](#rest-api-and-cli-architectural-relationship)
|
||||
- [REST API, D-Bus 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)
|
||||
@@ -21,9 +24,11 @@
|
||||
|
||||
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 Cloud Hypervisor through either a REST API or a Command
|
||||
Line Interface (CLI).
|
||||
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 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.
|
||||
@@ -40,10 +45,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 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.
|
||||
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.
|
||||
|
||||
### Location and availability
|
||||
#### REST API Location and availability
|
||||
|
||||
The REST API is available as soon as the Cloud Hypervisor binary is started,
|
||||
through a local UNIX socket.
|
||||
@@ -65,18 +70,18 @@ Cloud Hypervisor Guest
|
||||
Disk(s): None
|
||||
```
|
||||
|
||||
### Endpoints
|
||||
#### REST API 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 |
|
||||
| ---------------------------------- | ----------------------- | ------------------------------- | ------------------------ | ------------------------------------------------------ |
|
||||
@@ -110,10 +115,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 endpoint is not
|
||||
available.
|
||||
enabled. Without this feature, the corresponding [REST API](#rest-api) or
|
||||
[D-Bus API](#d-bus-api) endpoints are 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`:
|
||||
@@ -129,7 +134,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:
|
||||
|
||||
@@ -157,7 +162,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:
|
||||
|
||||
@@ -167,7 +172,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:
|
||||
|
||||
@@ -179,7 +184,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:
|
||||
|
||||
@@ -189,7 +194,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:
|
||||
|
||||
@@ -199,6 +204,50 @@ 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
|
||||
@@ -206,22 +255,26 @@ 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 the [REST API](#rest-api).
|
||||
Hypervisor from the CLI, you must use either the [REST API](#rest-api)
|
||||
or the [D-Bus API](#d-bus-api).
|
||||
|
||||
From the CLI, one can either:
|
||||
From the CLI, one can:
|
||||
|
||||
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, 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.
|
||||
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.
|
||||
|
||||
### REST API and CLI Architectural Relationship
|
||||
### REST API, D-Bus API and CLI Architectural Relationship
|
||||
|
||||
The REST API and the CLI both rely on a common, [internal API](#internal-api).
|
||||
The REST API, D-Bus API and the CLI all rely on a common, [internal API](#internal-api).
|
||||
|
||||
The CLI options are parsed by the
|
||||
[argh crate](https://docs.rs/argh/latest/argh/) and then translated into
|
||||
@@ -232,7 +285,11 @@ 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.
|
||||
|
||||
As a summary, the REST API and the CLI are essentially frontends for the
|
||||
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
|
||||
[internal API](#internal-api):
|
||||
|
||||
```
|
||||
@@ -243,11 +300,11 @@ As a summary, the REST API and the CLI are essentially frontends for the
|
||||
| +------------------+ |
|
||||
| | +------------------------+
|
||||
| | | |
|
||||
+------------+ | | | |
|
||||
| | | | | +--------------+ |
|
||||
| User +---------+ +------> | Internal API | |
|
||||
| | | | | +--------------+ |
|
||||
+------------+ | | | |
|
||||
+------------+ | +----------+ | | |
|
||||
| | | D-Bus API | | | | +--------------+ |
|
||||
| User +---------+----------->+ zbus +--------------+------> | Internal API | |
|
||||
| | | | | | | +--------------+ |
|
||||
+------------+ | +----------+ | | |
|
||||
| | | |
|
||||
| | +------------------------+
|
||||
| +----------+ | VMM
|
||||
@@ -262,22 +319,23 @@ As a summary, the REST API and the CLI are essentially frontends for the
|
||||
## Internal API
|
||||
|
||||
The Cloud Hypervisor internal API, as its name suggests, is used internally
|
||||
by the different Cloud Hypervisor threads (VMM, HTTP, control loop, etc) to
|
||||
send commands and responses to each others.
|
||||
by the different Cloud Hypervisor threads (VMM, HTTP, D-Bus, 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) and the
|
||||
main thread that initially parses the [CLI](#command-line-interface).
|
||||
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).
|
||||
|
||||
### 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 either the [REST API](#rest-api) or the
|
||||
[CLI](#command-line-interface) interfaces.
|
||||
visible requests through the [REST API](#rest-api), the [D-Bus API](#d-bus-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.
|
||||
|
||||
@@ -225,7 +225,7 @@ Number Start End Size File system Name Flags
|
||||
|
||||
### Create a macvtap interface
|
||||
|
||||
Rely on the following [documentation](docs/macvtap-bridge.md) to set up a
|
||||
Rely on the following [documentation](macvtap-bridge.md) to set up a
|
||||
macvtap interface to provide your VM with proper connectivity.
|
||||
|
||||
### Boot the image
|
||||
|
||||
@@ -6,5 +6,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.95"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
|
||||
151
fuzz/Cargo.lock
generated
151
fuzz/Cargo.lock
generated
@@ -5,16 +5,16 @@ version = 3
|
||||
[[package]]
|
||||
name = "acpi_tables"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#0b892e2c2053c4ecfac8d9e5a52babae75114702"
|
||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#05a609136387cc1cc9b499cee4320020325c263f"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "api_client"
|
||||
@@ -100,6 +100,12 @@ 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"
|
||||
@@ -113,7 +119,7 @@ dependencies = [
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vhdx",
|
||||
"virtio-bindings 0.2.0",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
@@ -143,7 +149,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cloud-hypervisor"
|
||||
version = "30.0.0"
|
||||
version = "32.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"api_client",
|
||||
@@ -208,9 +214,9 @@ checksum = "55626594feae15d266d52440b26ff77de0e22230cf0c113abe619084c1ddc910"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.4"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
|
||||
checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
@@ -218,27 +224,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.4"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
|
||||
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.4"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
|
||||
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -248,7 +254,7 @@ dependencies = [
|
||||
"acpi_tables",
|
||||
"anyhow",
|
||||
"arch",
|
||||
"bitflags",
|
||||
"bitflags 2.3.2",
|
||||
"byteorder",
|
||||
"hypervisor",
|
||||
"libc",
|
||||
@@ -269,7 +275,7 @@ version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -296,9 +302,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -326,9 +332,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec"
|
||||
checksum = "b7cc8d38244d84278262c8ebe6930cc44283d194cbabae2651f6112103802fb5"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
@@ -345,7 +351,7 @@ version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd1e1a01cfb924fd8c5c43b6827965db394f5a3a16c599ce03452266e1cf984c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -393,9 +399,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
@@ -410,21 +416,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-loader"
|
||||
version = "0.8.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9259ddbfbb52cc918f6bbc60390004ddd0228cf1d85f402009ff2b3d95de83f"
|
||||
checksum = "8d3adb7b28e189741eca3b1a4a27de0bf15e0907c9d4b0c74bd2d7d84ef72e08"
|
||||
dependencies = [
|
||||
"vm-memory",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
@@ -456,7 +459,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"virtio-bindings 0.2.0",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vm-virtio",
|
||||
@@ -465,9 +468,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "option_parser"
|
||||
@@ -498,9 +501,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -518,9 +521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -542,7 +545,7 @@ checksum = "e13cca257d068dd3a390d04b2c3009a3fad2ee5048dfa8f239d048372810470c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.4",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -577,29 +580,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.159"
|
||||
version = "1.0.164"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
|
||||
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.159"
|
||||
version = "1.0.164"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
||||
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.4",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.95"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
||||
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -608,9 +611,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "2.3.2"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0"
|
||||
checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
@@ -618,14 +621,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "2.3.2"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c"
|
||||
checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -676,9 +679,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.4"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -702,7 +705,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.4",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -731,15 +734,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.0"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
||||
checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -774,7 +777,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-bindings"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
|
||||
dependencies = [
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -782,7 +785,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio-ioctls"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#43439e056ddfa84a4f7906ee7f2f58be70505c08"
|
||||
source = "git+https://github.com/rust-vmm/vfio?branch=main#89f8e77dd1a2829197ecde65b686bafcc8a1def4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
@@ -798,9 +801,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "vfio_user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#afbbd5722885e961ce12baea12efe01d52ce14b0"
|
||||
source = "git+https://github.com/rust-vmm/vfio-user?branch=main#eef6bec4d421f08ed1688fe67c5ea33aabbf5069"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"log",
|
||||
"serde",
|
||||
@@ -827,22 +830,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vhost"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072"
|
||||
checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"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"
|
||||
@@ -874,7 +871,7 @@ dependencies = [
|
||||
"versionize",
|
||||
"versionize_derive",
|
||||
"vhost",
|
||||
"virtio-bindings 0.2.0",
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-allocator",
|
||||
"vm-device",
|
||||
@@ -886,12 +883,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "virtio-queue"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd"
|
||||
checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8"
|
||||
dependencies = [
|
||||
"log",
|
||||
"virtio-bindings 0.1.0",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
@@ -925,9 +922,9 @@ source = "git+https://github.com/rust-vmm/vm-fdt?branch=main#c5a99ab71b130435927
|
||||
|
||||
[[package]]
|
||||
name = "vm-memory"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c"
|
||||
checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
@@ -964,7 +961,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"arch",
|
||||
"bitflags",
|
||||
"bitflags 2.3.2",
|
||||
"block_util",
|
||||
"devices",
|
||||
"epoll",
|
||||
@@ -1009,7 +1006,7 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
||||
@@ -12,20 +12,20 @@ cargo-fuzz = true
|
||||
block_util = { path = "../block_util" }
|
||||
devices = { path = "../devices" }
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.141"
|
||||
libc = "0.2.147"
|
||||
libfuzzer-sys = "0.4.6"
|
||||
linux-loader = { version = "0.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
linux-loader = { version = "0.9.0", features = ["elf", "bzimage", "pe"] }
|
||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||
net_util = { path = "../net_util" }
|
||||
once_cell = "1.17.1"
|
||||
once_cell = "1.18.0"
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.3.0"
|
||||
vhdx = { path = "../vhdx" }
|
||||
virtio-devices = { path = "../virtio-devices" }
|
||||
virtio-queue = "0.7.1"
|
||||
virtio-queue = "0.8.0"
|
||||
vmm = { path = "../vmm" }
|
||||
vmm-sys-util = "0.11.1"
|
||||
vm-memory = "0.10.0"
|
||||
vm-memory = "0.11.0"
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
|
||||
|
||||
@@ -11,23 +11,23 @@ mshv = ["mshv-ioctls", "mshv-bindings", "vfio-ioctls/mshv"]
|
||||
tdx = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
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.151", features = ["rc", "derive"] }
|
||||
serde_with = { version = "2.3.1", default-features = false, features = ["macros"] }
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_with = { version = "3.0.0", default-features = false, features = ["macros"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.11.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.18.0"
|
||||
version = "1.19.0"
|
||||
default-features = false
|
||||
features = ["std", "decoder", "op_code_info", "instr_info", "fast_fmt"]
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ pub const MTRR_MEM_TYPE_WB: u64 = 0x6;
|
||||
pub const NUM_IOAPIC_PINS: usize = 24;
|
||||
|
||||
// X86 Exceptions
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Exception {
|
||||
DE = 0, // Divide Error
|
||||
|
||||
@@ -21,6 +21,15 @@ 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
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
//
|
||||
#[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;
|
||||
|
||||
@@ -75,6 +79,11 @@ 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),
|
||||
}
|
||||
|
||||
///
|
||||
@@ -133,4 +142,30 @@ pub trait Hypervisor: Send + Sync {
|
||||
fn get_guest_debug_hw_bps(&self) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,6 @@ impl KvmGicV3Its {
|
||||
}
|
||||
|
||||
/// Method to initialize the GIC device
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(vm: &dyn Vm, config: VgicConfig) -> Result<KvmGicV3Its> {
|
||||
// This is inside KVM module
|
||||
let vm = vm.as_any().downcast_ref::<KvmVm>().expect("Wrong VM type?");
|
||||
|
||||
@@ -330,7 +330,7 @@ impl KvmVm {
|
||||
Ok(VfioDeviceFd::new_from_kvm(device_fd))
|
||||
}
|
||||
/// Checks if a particular `Cap` is available.
|
||||
fn check_extension(&self, c: Cap) -> bool {
|
||||
pub fn check_extension(&self, c: Cap) -> bool {
|
||||
self.fd.check_extension(c)
|
||||
}
|
||||
}
|
||||
@@ -1084,6 +1084,11 @@ impl hypervisor::Hypervisor for KvmHypervisor {
|
||||
self.kvm.get_guest_debug_hw_bps() as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Get maximum number of vCPUs
|
||||
fn get_max_vcpus(&self) -> u32 {
|
||||
self.kvm.get_max_vcpus().min(u32::MAX as usize) as u32
|
||||
}
|
||||
}
|
||||
/// Vcpu struct for KVM
|
||||
pub struct KvmVcpu {
|
||||
|
||||
@@ -49,6 +49,8 @@ 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"))]
|
||||
|
||||
@@ -279,6 +279,13 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
||||
fn get_supported_cpuid(&self) -> hypervisor::Result<Vec<CpuIdEntry>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Get maximum number of vCPUs
|
||||
fn get_max_vcpus(&self) -> u32 {
|
||||
// TODO: Using HV_MAXIMUM_PROCESSORS would be better
|
||||
// but the ioctl API is limited to u8
|
||||
256
|
||||
}
|
||||
}
|
||||
|
||||
/// Vcpu struct for Microsoft Hypervisor
|
||||
@@ -454,10 +461,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/* Advance RIP and update RAX */
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_x64_register_name_HV_X64_REGISTER_RIP,
|
||||
hv_register_name_HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_x64_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
@@ -503,10 +510,10 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
/* Advance RIP and update RAX */
|
||||
let arr_reg_name_value = [
|
||||
(
|
||||
hv_x64_register_name_HV_X64_REGISTER_RIP,
|
||||
hv_register_name_HV_X64_REGISTER_RIP,
|
||||
info.header.rip + insn_len,
|
||||
),
|
||||
(hv_x64_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
(hv_register_name_HV_X64_REGISTER_RAX, ret_rax),
|
||||
];
|
||||
set_registers_64!(self.fd, arr_reg_name_value)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetRegister(e.into()))?;
|
||||
@@ -576,8 +583,14 @@ impl cpu::Vcpu for MshvVcpu {
|
||||
///
|
||||
/// X86 specific call to setup the CPUID registers.
|
||||
///
|
||||
fn set_cpuid2(&self, _cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
|
||||
Ok(())
|
||||
fn set_cpuid2(&self, cpuid: &[CpuIdEntry]) -> cpu::Result<()> {
|
||||
let cpuid: Vec<mshv_bindings::hv_cpuid_entry> = cpuid.iter().map(|e| (*e).into()).collect();
|
||||
let mshv_cpuid = <CpuId>::from_entries(&cpuid)
|
||||
.map_err(|_| cpu::HypervisorCpuError::SetCpuid(anyhow!("failed to create CpuId")))?;
|
||||
|
||||
self.fd
|
||||
.register_intercept_result_cpuid(&mshv_cpuid)
|
||||
.map_err(|e| cpu::HypervisorCpuError::SetCpuid(e.into()))
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#[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 {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#[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 {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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)]
|
||||
@@ -28,10 +27,12 @@ pub mod inn;
|
||||
// generated with bindgen /usr/include/linux/sockios.h --no-unstable-rust
|
||||
// --constified-enum '*' --with-derive-default
|
||||
pub mod sockios;
|
||||
pub use if_tun::*;
|
||||
pub use iff::*;
|
||||
pub use inn::*;
|
||||
pub use sockios::*;
|
||||
pub use if_tun::{
|
||||
sock_fprog, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_TAP, IFF_VNET_HDR, TUN_F_CSUM, TUN_F_TSO4,
|
||||
TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO,
|
||||
};
|
||||
pub use iff::{ifreq, net_device_flags_IFF_UP, setsockopt, sockaddr, AF_INET};
|
||||
pub use inn::sockaddr_in;
|
||||
|
||||
pub const TUNTAP: ::std::os::raw::c_uint = 84;
|
||||
|
||||
|
||||
@@ -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.151"
|
||||
thiserror = "1.0.39"
|
||||
serde = "1.0.164"
|
||||
thiserror = "1.0.40"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
virtio-bindings = "0.2.0"
|
||||
virtio-queue = "0.7.1"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.8.0"
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -25,4 +25,4 @@ vmm-sys-util = "0.11.0"
|
||||
once_cell = "1.17.1"
|
||||
pnet = "0.33.0"
|
||||
pnet_datalink = "0.33.0"
|
||||
serde_json = "1.0.95"
|
||||
serde_json = "1.0.96"
|
||||
|
||||
@@ -10,7 +10,7 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
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.151", features = ["derive"] }
|
||||
thiserror = "1.0.39"
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
thiserror = "1.0.40"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
|
||||
@@ -452,10 +452,9 @@ impl From<PciBarType> for PciBarRegionType {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<PciBarType> for PciBarRegionType {
|
||||
fn into(self) -> PciBarType {
|
||||
match self {
|
||||
impl From<PciBarRegionType> for PciBarType {
|
||||
fn from(val: PciBarRegionType) -> Self {
|
||||
match val {
|
||||
PciBarRegionType::IoRegion => PciBarType::Io,
|
||||
PciBarRegionType::Memory32BitRegion => PciBarType::Mmio32,
|
||||
PciBarRegionType::Memory64BitRegion => PciBarType::Mmio64,
|
||||
@@ -469,10 +468,9 @@ pub enum PciBarPrefetchable {
|
||||
Prefetchable = 0x08,
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<bool> for PciBarPrefetchable {
|
||||
fn into(self) -> bool {
|
||||
match self {
|
||||
impl From<PciBarPrefetchable> for bool {
|
||||
fn from(val: PciBarPrefetchable) -> Self {
|
||||
match val {
|
||||
PciBarPrefetchable::NotPrefetchable => false,
|
||||
PciBarPrefetchable::Prefetchable => true,
|
||||
}
|
||||
|
||||
@@ -511,6 +511,16 @@ impl MsixCap {
|
||||
self.pba & 0xffff_fff8
|
||||
}
|
||||
|
||||
pub fn table_set_offset(&mut self, addr: u32) {
|
||||
self.table &= 0x7;
|
||||
self.table += addr;
|
||||
}
|
||||
|
||||
pub fn pba_set_offset(&mut self, addr: u32) {
|
||||
self.pba &= 0x7;
|
||||
self.pba += addr;
|
||||
}
|
||||
|
||||
pub fn table_bir(&self) -> u32 {
|
||||
self.table & 0x7
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::{
|
||||
use anyhow::anyhow;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use hypervisor::HypervisorVmError;
|
||||
use libc::{sysconf, _SC_PAGESIZE};
|
||||
use std::any::Any;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::io;
|
||||
@@ -27,6 +28,9 @@ use vfio_bindings::bindings::vfio::*;
|
||||
use vfio_ioctls::{
|
||||
VfioContainer, VfioDevice, VfioIrq, VfioRegionInfoCap, VfioRegionSparseMmapArea,
|
||||
};
|
||||
use vm_allocator::page_size::{
|
||||
align_page_size_down, align_page_size_up, is_4k_aligned, is_4k_multiple, is_page_size_aligned,
|
||||
};
|
||||
use vm_allocator::{AddressAllocator, SystemAllocator};
|
||||
use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
|
||||
@@ -498,6 +502,29 @@ impl VfioCommon {
|
||||
Ok(vfio_common)
|
||||
}
|
||||
|
||||
/// In case msix table offset is not page size aligned, we need do some fixup to achive it.
|
||||
/// Becuse we don't want the MMIO RW region and trap region overlap each other.
|
||||
fn fixup_msix_region(&mut self, bar_id: u32, region_size: u64) -> u64 {
|
||||
let msix = self.interrupt.msix.as_mut().unwrap();
|
||||
let msix_cap = &mut msix.cap;
|
||||
|
||||
// Suppose table_bir equals to pba_bir here. Am I right?
|
||||
let (table_offset, table_size) = msix_cap.table_range();
|
||||
if is_page_size_aligned(table_offset) || msix_cap.table_bir() != bar_id {
|
||||
return region_size;
|
||||
}
|
||||
|
||||
let (pba_offset, pba_size) = msix_cap.pba_range();
|
||||
let msix_sz = align_page_size_up(table_size + pba_size);
|
||||
// Expand region to hold RW and trap region which both page size aligned
|
||||
let size = std::cmp::max(region_size * 2, msix_sz * 2);
|
||||
// let table starts from the middle of the region
|
||||
msix_cap.table_set_offset((size / 2) as u32);
|
||||
msix_cap.pba_set_offset((size / 2 + pba_offset - table_offset) as u32);
|
||||
|
||||
size
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_bars(
|
||||
&mut self,
|
||||
allocator: &Arc<Mutex<SystemAllocator>>,
|
||||
@@ -661,9 +688,16 @@ impl VfioCommon {
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
|
||||
}
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
// BAR allocation must be naturally aligned
|
||||
// We need do some fixup to keep MMIO RW region and msix cap region page size
|
||||
// aligned.
|
||||
region_size = self.fixup_msix_region(bar_id, region_size);
|
||||
mmio_allocator
|
||||
.allocate(restored_bar_addr, region_size, Some(region_size))
|
||||
.allocate(
|
||||
restored_bar_addr,
|
||||
region_size,
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
Some(unsafe { sysconf(_SC_PAGESIZE) as GuestUsize }),
|
||||
)
|
||||
.ok_or(PciDeviceError::IoAllocationFailed(region_size))?
|
||||
}
|
||||
};
|
||||
@@ -800,6 +834,23 @@ impl VfioCommon {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn get_msix_cap_idx(&self) -> Option<usize> {
|
||||
let mut cap_next = self
|
||||
.vfio_wrapper
|
||||
.read_config_byte(PCI_CONFIG_CAPABILITY_OFFSET);
|
||||
|
||||
while cap_next != 0 {
|
||||
let cap_id = self.vfio_wrapper.read_config_byte(cap_next.into());
|
||||
if PciCapabilityId::from(cap_id) == PciCapabilityId::MsiX {
|
||||
return Some(cap_next as usize);
|
||||
} else {
|
||||
cap_next = self.vfio_wrapper.read_config_byte((cap_next + 1).into());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn parse_capabilities(&mut self, bdf: PciBdf) {
|
||||
let mut cap_next = self
|
||||
.vfio_wrapper
|
||||
@@ -1154,6 +1205,15 @@ impl VfioCommon {
|
||||
return self.configuration.read_reg(reg_idx);
|
||||
}
|
||||
|
||||
if let Some(id) = self.get_msix_cap_idx() {
|
||||
let msix = self.interrupt.msix.as_mut().unwrap();
|
||||
if reg_idx * 4 == id + 4 {
|
||||
return msix.cap.table;
|
||||
} else if reg_idx * 4 == id + 8 {
|
||||
return msix.cap.pba;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we don't support passing multi-functions devices, we should
|
||||
// mask the multi-function bit, bit 7 of the Header Type byte on the
|
||||
// register 3.
|
||||
@@ -1316,18 +1376,6 @@ impl VfioPciDevice {
|
||||
self.iommu_attached
|
||||
}
|
||||
|
||||
fn align_4k(address: u64) -> u64 {
|
||||
(address + 0xfff) & 0xffff_ffff_ffff_f000
|
||||
}
|
||||
|
||||
fn is_4k_aligned(address: u64) -> bool {
|
||||
(address & 0xfff) == 0
|
||||
}
|
||||
|
||||
fn is_4k_multiple(size: u64) -> bool {
|
||||
(size & 0xfff) == 0
|
||||
}
|
||||
|
||||
fn generate_sparse_areas(
|
||||
caps: &[VfioRegionInfoCap],
|
||||
region_index: u32,
|
||||
@@ -1339,14 +1387,14 @@ impl VfioPciDevice {
|
||||
match cap {
|
||||
VfioRegionInfoCap::SparseMmap(sparse_mmap) => return Ok(sparse_mmap.areas.clone()),
|
||||
VfioRegionInfoCap::MsixMappable => {
|
||||
if !Self::is_4k_aligned(region_start) {
|
||||
if !is_4k_aligned(region_start) {
|
||||
error!(
|
||||
"Region start address 0x{:x} must be at least aligned on 4KiB",
|
||||
region_start
|
||||
);
|
||||
return Err(VfioPciError::RegionAlignment);
|
||||
}
|
||||
if !Self::is_4k_multiple(region_size) {
|
||||
if !is_4k_multiple(region_size) {
|
||||
error!(
|
||||
"Region size 0x{:x} must be at least a multiple of 4KiB",
|
||||
region_size
|
||||
@@ -1358,7 +1406,8 @@ impl VfioPciDevice {
|
||||
// the MSI-X PBA table, we must calculate the subregions
|
||||
// around them, leading to a list of sparse areas.
|
||||
// We want to make sure we will still trap MMIO accesses
|
||||
// to these MSI-X specific ranges.
|
||||
// to these MSI-X specific ranges. If these region don't align
|
||||
// with pagesize, we can achive it by enlarging its range.
|
||||
//
|
||||
// Using a BtreeMap as the list provided through the iterator is sorted
|
||||
// by key. This ensures proper split of the whole region.
|
||||
@@ -1366,10 +1415,14 @@ impl VfioPciDevice {
|
||||
if let Some(msix) = vfio_msix {
|
||||
if region_index == msix.cap.table_bir() {
|
||||
let (offset, size) = msix.cap.table_range();
|
||||
let offset = align_page_size_down(offset);
|
||||
let size = align_page_size_up(size);
|
||||
inter_ranges.insert(offset, size);
|
||||
}
|
||||
if region_index == msix.cap.pba_bir() {
|
||||
let (offset, size) = msix.cap.pba_range();
|
||||
let offset = align_page_size_down(offset);
|
||||
let size = align_page_size_up(size);
|
||||
inter_ranges.insert(offset, size);
|
||||
}
|
||||
}
|
||||
@@ -1383,8 +1436,7 @@ impl VfioPciDevice {
|
||||
size: range_offset - current_offset,
|
||||
});
|
||||
}
|
||||
|
||||
current_offset = Self::align_4k(range_offset + range_size);
|
||||
current_offset = align_page_size_down(range_offset + range_size);
|
||||
}
|
||||
|
||||
if region_size > current_offset {
|
||||
@@ -1482,6 +1534,15 @@ impl VfioPciDevice {
|
||||
return Err(VfioPciError::MmapArea);
|
||||
}
|
||||
|
||||
if !is_page_size_aligned(area.size) || !is_page_size_aligned(area.offset) {
|
||||
warn!(
|
||||
"Could not mmap sparse area that is not page size aligned (offset = 0x{:x}, size = 0x{:x})",
|
||||
area.offset,
|
||||
area.size,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let user_memory_region = UserMemoryRegion {
|
||||
slot: (self.memory_slot)(),
|
||||
start: region.start.0 + area.offset,
|
||||
|
||||
@@ -7,9 +7,9 @@ build = "../build.rs"
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.9"
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.95"
|
||||
dirs = "5.0.0"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
test_infra = { path = "../test_infra" }
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -671,7 +671,7 @@ fn main() {
|
||||
let opts: Options = argh::from_env();
|
||||
|
||||
if opts.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ impl QcowRawFile {
|
||||
non_zero_flags: u64,
|
||||
) -> io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &mut self.file);
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
for addr in table {
|
||||
let val = if *addr == 0 {
|
||||
0
|
||||
@@ -91,7 +91,8 @@ impl QcowRawFile {
|
||||
/// Writes a refcount block to the file.
|
||||
pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(offset))?;
|
||||
let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &mut self.file);
|
||||
let mut buffer = BufWriter::with_capacity(std::mem::size_of_val(table), &mut self.file);
|
||||
|
||||
for count in table {
|
||||
buffer.write_u16::<BigEndian>(*count)?;
|
||||
}
|
||||
|
||||
204
release-notes.md
204
release-notes.md
@@ -1,18 +1,29 @@
|
||||
- [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.0](#v320)
|
||||
- [Increased PCI Segment Limit](#increased-pci-segment-limit)
|
||||
- [API Changes](#api-changes)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [v31.1](#v311)
|
||||
- [v31.0](#v310)
|
||||
- [Update to Latest `acpi_tables`](#update-to-latest-acpi_tables)
|
||||
- [Update Reference Kernel to 6.2](#update-reference-kernel-to-62)
|
||||
- [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)
|
||||
- [Contributors](#contributors)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-2)
|
||||
- [Contributors](#contributors-2)
|
||||
- [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-1)
|
||||
- [Contributors](#contributors-1)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-3)
|
||||
- [Contributors](#contributors-3)
|
||||
- [v28.2](#v282)
|
||||
- [v29.0](#v290)
|
||||
- [Release Binary Supports Both MSHV and KVM](#release-binary-supports-both-mshv-and-kvm)
|
||||
@@ -22,10 +33,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-2)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-4)
|
||||
- [Removals](#removals)
|
||||
- [Deprecations](#deprecations)
|
||||
- [Contributors](#contributors-2)
|
||||
- [Contributors](#contributors-4)
|
||||
- [v28.1](#v281)
|
||||
- [v28.0](#v280)
|
||||
- [Community Engagement (Reminder)](#community-engagement-reminder)
|
||||
@@ -33,9 +44,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-3)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-5)
|
||||
- [Removals](#removals-1)
|
||||
- [Contributors](#contributors-3)
|
||||
- [Contributors](#contributors-5)
|
||||
- [v27.0](#v270)
|
||||
- [Community Engagement](#community-engagement)
|
||||
- [Prebuilt Packages](#prebuilt-packages)
|
||||
@@ -44,41 +55,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-4)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Deprecations](#deprecations-1)
|
||||
- [Contributors](#contributors-4)
|
||||
- [Contributors](#contributors-6)
|
||||
- [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-5)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-7)
|
||||
- [Deprecations](#deprecations-2)
|
||||
- [Removals](#removals-2)
|
||||
- [Contributors](#contributors-5)
|
||||
- [Contributors](#contributors-7)
|
||||
- [v25.0](#v250)
|
||||
- [`ch-remote` Improvements](#ch-remote-improvements-1)
|
||||
- [VM "Coredump" Support](#vm-coredump-support)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-6)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-8)
|
||||
- [Removals](#removals-3)
|
||||
- [Contributors](#contributors-6)
|
||||
- [Contributors](#contributors-8)
|
||||
- [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-7)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-9)
|
||||
- [Notable Improvements](#notable-improvements)
|
||||
- [Deprecations](#deprecations-3)
|
||||
- [New on the Website](#new-on-the-website)
|
||||
- [Contributors](#contributors-7)
|
||||
- [Contributors](#contributors-9)
|
||||
- [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-8)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-10)
|
||||
- [Deprecations](#deprecations-4)
|
||||
- [Contributors](#contributors-8)
|
||||
- [Contributors](#contributors-10)
|
||||
- [v22.1](#v221)
|
||||
- [v22.0](#v220)
|
||||
- [GDB Debug Stub Support](#gdb-debug-stub-support)
|
||||
@@ -89,13 +100,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-9)
|
||||
- [Contributors](#contributors-9)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [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-10)
|
||||
- [Contributors](#contributors-10)
|
||||
- [Notable Bug fixes](#notable-bug-fixes-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [v20.2](#v202)
|
||||
- [v20.1](#v201)
|
||||
- [v20.0](#v200)
|
||||
@@ -104,8 +115,8 @@
|
||||
- [Improved VFIO support](#improved-vfio-support)
|
||||
- [Safer code](#safer-code)
|
||||
- [Extended documentation](#extended-documentation)
|
||||
- [Notable bug fixes](#notable-bug-fixes-11)
|
||||
- [Contributors](#contributors-11)
|
||||
- [Notable bug fixes](#notable-bug-fixes-13)
|
||||
- [Contributors](#contributors-13)
|
||||
- [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)
|
||||
@@ -113,8 +124,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-12)
|
||||
- [Contributors](#contributors-12)
|
||||
- [Notable bug fixes](#notable-bug-fixes-14)
|
||||
- [Contributors](#contributors-14)
|
||||
- [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)
|
||||
@@ -124,23 +135,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-13)
|
||||
- [Contributors](#contributors-13)
|
||||
- [Notable bug fixes](#notable-bug-fixes-15)
|
||||
- [Contributors](#contributors-15)
|
||||
- [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-14)
|
||||
- [Contributors](#contributors-14)
|
||||
- [Notable bug fixes](#notable-bug-fixes-16)
|
||||
- [Contributors](#contributors-16)
|
||||
- [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-15)
|
||||
- [Notable bug fixes](#notable-bug-fixes-17)
|
||||
- [Removed functionality](#removed-functionality)
|
||||
- [Contributors](#contributors-15)
|
||||
- [Contributors](#contributors-17)
|
||||
- [v15.0](#v150)
|
||||
- [Version numbering and stability guarantees](#version-numbering-and-stability-guarantees)
|
||||
- [Network device rate limiting](#network-device-rate-limiting)
|
||||
@@ -148,7 +159,7 @@
|
||||
- [`--api-socket` supports file descriptor parameter](#--api-socket-supports-file-descriptor-parameter)
|
||||
- [Bug fixes](#bug-fixes)
|
||||
- [Deprecations](#deprecations-5)
|
||||
- [Contributors](#contributors-16)
|
||||
- [Contributors](#contributors-18)
|
||||
- [v0.14.1](#v0141)
|
||||
- [v0.14.0](#v0140)
|
||||
- [Structured event monitoring](#structured-event-monitoring)
|
||||
@@ -158,7 +169,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-17)
|
||||
- [Contributors](#contributors-19)
|
||||
- [v0.13.0](#v0130)
|
||||
- [Wider VFIO device support](#wider-vfio-device-support)
|
||||
- [Improved huge page support](#improved-huge-page-support)
|
||||
@@ -166,13 +177,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-18)
|
||||
- [Contributors](#contributors-20)
|
||||
- [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-19)
|
||||
- [Contributors](#contributors-21)
|
||||
- [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)
|
||||
@@ -184,15 +195,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-16)
|
||||
- [Contributors](#contributors-20)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-18)
|
||||
- [Contributors](#contributors-22)
|
||||
- [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-17)
|
||||
- [Contributors](#contributors-21)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-19)
|
||||
- [Contributors](#contributors-23)
|
||||
- [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)
|
||||
@@ -205,17 +216,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-18)
|
||||
- [Contributors](#contributors-22)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-20)
|
||||
- [Contributors](#contributors-24)
|
||||
- [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-19)
|
||||
- [Notable Bug Fixes](#notable-bug-fixes-21)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes)
|
||||
- [Contributors](#contributors-23)
|
||||
- [Contributors](#contributors-25)
|
||||
- [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)
|
||||
@@ -225,14 +236,14 @@
|
||||
- [`Seccomp` Sandboxing](#seccomp-sandboxing)
|
||||
- [Updated Distribution Support](#updated-distribution-support)
|
||||
- [Command Line and API Changes](#command-line-and-api-changes-1)
|
||||
- [Contributors](#contributors-24)
|
||||
- [Contributors](#contributors-26)
|
||||
- [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-25)
|
||||
- [Contributors](#contributors-27)
|
||||
- [v0.5.1](#v051)
|
||||
- [v0.5.0](#v050)
|
||||
- [Virtual Machine Dynamic Resizing](#virtual-machine-dynamic-resizing)
|
||||
@@ -240,7 +251,7 @@
|
||||
- [New Interrupt Management Framework](#new-interrupt-management-framework)
|
||||
- [Development Tools](#development-tools)
|
||||
- [Kata Containers Integration](#kata-containers-integration)
|
||||
- [Contributors](#contributors-26)
|
||||
- [Contributors](#contributors-28)
|
||||
- [v0.4.0](#v040)
|
||||
- [Dynamic virtual CPUs addition](#dynamic-virtual-cpus-addition)
|
||||
- [Programmatic firmware tables generation](#programmatic-firmware-tables-generation)
|
||||
@@ -249,7 +260,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-27)
|
||||
- [Contributors](#contributors-29)
|
||||
- [v0.3.0](#v030)
|
||||
- [Block device offloading](#block-device-offloading)
|
||||
- [Network device backend](#network-device-backend)
|
||||
@@ -276,6 +287,103 @@
|
||||
- [Unit testing](#unit-testing)
|
||||
- [Integration tests parallelization](#integration-tests-parallelization)
|
||||
|
||||
# v33.0
|
||||
|
||||
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
|
||||
|
||||
* 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)
|
||||
* 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
|
||||
project](https://github.com/orgs/cloud-hypervisor/projects/6) as iteration
|
||||
v32.0. The following user visible changes have been made:
|
||||
|
||||
### Increased PCI Segment Limit
|
||||
|
||||
The maximum number of PCI segments that can be used is now 96 (up from 16).
|
||||
|
||||
### API Changes
|
||||
|
||||
* The VmmPingResponse now includes the PID as well as the build details.
|
||||
(#5348)
|
||||
|
||||
### Notable Bug Fixes
|
||||
|
||||
* Ignore and warn TAP FDs sent via the HTTP request body (#5350)
|
||||
* Properly preserve and close valid FDs for TAP devices (#5373)
|
||||
* Only use `KVM_ARM_VCPU_PMU_V3` if available (#5360)
|
||||
* Only touch the tty flags if it's being used (#5343)
|
||||
* Fix seccomp filter lists for vhost-user devices (#5361)
|
||||
* The number of vCPUs is capped at the hypervisor maximum (#5357)
|
||||
* Fixes for TTY reset (#5414)
|
||||
* CPU topology fixes on MSHV (#5325)
|
||||
* Seccomp fixes for older distributions (#5397)
|
||||
|
||||
### 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>
|
||||
* Hao Xu <howeyxu@tencent.com>
|
||||
* Muminul Islam <muislam@microsoft.com>
|
||||
* Omer Faruk Bayram <omer.faruk@sartura.hr>
|
||||
* Rafael Mendonca <rafaelmendsr@gmail.com>
|
||||
* Rob Bradford <rbradford@rivosinc.com>
|
||||
* Ruslan Mstoi <ruslan.mstoi@intel.com>
|
||||
* Smit Gardhariya <gardhariya.smit@gmail.com>
|
||||
* Wei Liu <liuwe@microsoft.com>
|
||||
|
||||
# v31.1
|
||||
|
||||
This is a bug fix release. The following issues have been addressed:
|
||||
|
||||
* Ignore and warn TAP FDs sent via the HTTP request body (#5350)
|
||||
* Properly preserve and close valid FDs for TAP devices (#5373)
|
||||
* Only use `KVM_ARM_VCPU_PMU_V3` if available (#5360)
|
||||
* Only touch the tty flags if it's being used (#5343)
|
||||
* Fix seccomp filter lists for vhost-user devices (#5361)
|
||||
|
||||
# v31.0
|
||||
|
||||
This release has been tracked in our [roadmap
|
||||
|
||||
@@ -15,58 +15,67 @@ 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 \
|
||||
&& 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 -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 clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
&& rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old
|
||||
|
||||
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 \
|
||||
&& 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
|
||||
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
|
||||
|
||||
# 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.
|
||||
@@ -74,7 +83,7 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \
|
||||
# kernel (any version) image in `/boot` and modules in `/lib/modules`.
|
||||
apt-get update \
|
||||
&& apt-get -yq upgrade \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq \
|
||||
&& apt-get install --no-install-recommends -yq \
|
||||
libcap2-bin \
|
||||
libguestfs-tools \
|
||||
linux-image-generic \
|
||||
@@ -84,11 +93,12 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \
|
||||
perl \
|
||||
texinfo \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*; fi
|
||||
&& rm -rf /var/lib/apt/lists/* /var/log/*log /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old; 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/
|
||||
@@ -96,9 +106,10 @@ 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 \
|
||||
@@ -108,12 +119,14 @@ RUN export ARCH="$(uname -m)" \
|
||||
&& ln -s "$CARGO_GIT_REGISTRY_DIR" "$CARGO_HOME/git"
|
||||
|
||||
# Set the rust environment
|
||||
RUN echo 'source $CARGO_HOME/env' >> $HOME/.bashrc \
|
||||
&& mkdir $HOME/.cargo \
|
||||
&& ln -s $CARGO_HOME/env $HOME/.cargo/env
|
||||
# hadolint ignore=SC2016
|
||||
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 \
|
||||
@@ -123,7 +136,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 \
|
||||
@@ -131,6 +144,7 @@ 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 \
|
||||
@@ -151,7 +165,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
|
||||
# install ethr tool for performance tests
|
||||
RUN if [ "$TARGETARCH" = "amd64" ]; then \
|
||||
wget https://github.com/microsoft/ethr/releases/latest/download/ethr_linux.zip \
|
||||
wget -nv 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
|
||||
|
||||
@@ -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/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 ::
|
||||
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 ::
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
CLI_NAME="Cloud Hypervisor"
|
||||
|
||||
CTR_IMAGE_TAG="ghcr.io/cloud-hypervisor/cloud-hypervisor"
|
||||
CTR_IMAGE_VERSION="20230316-0"
|
||||
CTR_IMAGE_VERSION="20230620-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" \
|
||||
./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
|
||||
dbus-run-session ./scripts/run_integration_tests_"$(uname -m)".sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
|
||||
if [ "$integration_sgx" = true ]; then
|
||||
@@ -539,6 +539,7 @@ cmd_tests() {
|
||||
--volume "$CLH_INTEGRATION_WORKLOADS:$CTR_CLH_INTEGRATION_WORKLOADS" \
|
||||
--env USER="root" \
|
||||
--env CH_LIBC="${libc}" \
|
||||
--env RUST_BACKTRACE="${RUST_BACKTRACE}" \
|
||||
"$CTR_IMAGE" \
|
||||
./scripts/run_metrics.sh "$@" || fix_dir_perms $? || exit $?
|
||||
fi
|
||||
|
||||
@@ -225,8 +225,8 @@ fi
|
||||
|
||||
BUILD_TARGET="aarch64-unknown-linux-${CH_LIBC}"
|
||||
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
|
||||
export TARGET_CC="musl-gcc"
|
||||
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
export TARGET_CC="musl-gcc"
|
||||
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
fi
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
@@ -281,4 +281,12 @@ 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
|
||||
|
||||
@@ -42,8 +42,8 @@ BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
TARGET_CC=""
|
||||
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||
TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
cargo build --no-default-features --features "kvm,mshv" --all --release --target $BUILD_TARGET
|
||||
|
||||
@@ -7,9 +7,42 @@ source $(dirname "$0")/test-util.sh
|
||||
process_common_args "$@"
|
||||
|
||||
WORKLOADS_DIR="$HOME/workloads"
|
||||
FOCAL_OS_IMAGE_NAME="focal-server-cloudimg-amd64-custom-20210609-0.raw"
|
||||
FOCAL_OS_IMAGE="$WORKLOADS_DIR/$FOCAL_OS_IMAGE_NAME"
|
||||
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="$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
|
||||
|
||||
VMLINUX_IMAGE="$WORKLOADS_DIR/vmlinux"
|
||||
build_custom_linux
|
||||
@@ -31,7 +64,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_IMAGE $VFIO_DIR
|
||||
cp $FOCAL_OS_RAW_IMAGE $VFIO_DIR
|
||||
cp $FW $VFIO_DIR
|
||||
cp $VMLINUX_IMAGE $VFIO_DIR || exit 1
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
TARGET_CC=""
|
||||
if [[ "${BUILD_TARGET}" == "aarch64-unknown-linux-musl" ]]; then
|
||||
export TARGET_CC="musl-gcc"
|
||||
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
export TARGET_CC="musl-gcc"
|
||||
export RUSTFLAGS="-C link-arg=-lgcc -C link_arg=-specs -C link_arg=/usr/lib/aarch64-linux-musl/musl-gcc.specs"
|
||||
fi
|
||||
|
||||
# Check if the images are present
|
||||
|
||||
@@ -26,8 +26,8 @@ BUILD_TARGET="$(uname -m)-unknown-linux-${CH_LIBC}"
|
||||
CFLAGS=""
|
||||
TARGET_CC=""
|
||||
if [[ "${BUILD_TARGET}" == "x86_64-unknown-linux-musl" ]]; then
|
||||
TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
TARGET_CC="musl-gcc"
|
||||
CFLAGS="-I /usr/include/x86_64-linux-musl/ -idirafter /usr/include/"
|
||||
fi
|
||||
|
||||
# Check if the images are present
|
||||
|
||||
@@ -189,4 +189,13 @@ 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
|
||||
|
||||
@@ -107,7 +107,12 @@ fi
|
||||
# Ensure that git commands can be run in this directory (for metrics report)
|
||||
git config --global --add safe.directory $PWD
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
RUST_BACKTRACE_VALUE=`echo $RUST_BACKTRACE`
|
||||
if [ -z $RUST_BACKTRACE_VALUE ];then
|
||||
export RUST_BACKTRACE=1
|
||||
else
|
||||
echo "RUST_BACKTRACE is set to: $RUST_BACKTRACE_VALUE"
|
||||
fi
|
||||
time target/$BUILD_TARGET/release/performance-metrics ${test_binary_args[*]}
|
||||
RES=$?
|
||||
|
||||
|
||||
@@ -3,4 +3,3 @@ 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
|
||||
|
||||
|
||||
@@ -11,13 +11,19 @@ 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 {
|
||||
Connect(std::io::Error),
|
||||
ApiClient(ApiClientError),
|
||||
HttpApiClient(ApiClientError),
|
||||
#[cfg(feature = "dbus_api")]
|
||||
DBusApiClient(zbus::Error),
|
||||
InvalidMemorySize(ByteSizedParseError),
|
||||
InvalidBalloonSize(ByteSizedParseError),
|
||||
AddDeviceConfig(vmm::config::Error),
|
||||
@@ -37,8 +43,9 @@ impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
ApiClient(e) => e.fmt(f),
|
||||
Connect(e) => write!(f, "Error opening HTTP socket: {e}"),
|
||||
HttpApiClient(e) => e.fmt(f),
|
||||
#[cfg(feature = "dbus_api")]
|
||||
DBusApiClient(e) => write!(f, "Error D-Bus proxy: {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}"),
|
||||
@@ -56,12 +63,430 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_api_command(
|
||||
socket: &mut UnixStream,
|
||||
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(
|
||||
desired_vcpus: Option<u8>,
|
||||
memory: &Option<String>,
|
||||
balloon: &Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<String, Error> {
|
||||
let desired_ram: Option<u64> = if let Some(memory) = memory {
|
||||
Some(
|
||||
memory
|
||||
@@ -90,16 +515,10 @@ fn resize_api_command(
|
||||
desired_balloon,
|
||||
};
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"resize",
|
||||
Some(&serde_json::to_string(&resize).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(serde_json::to_string(&resize).unwrap())
|
||||
}
|
||||
|
||||
fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Result<(), Error> {
|
||||
fn resize_zone_config(id: &str, size: &str) -> Result<String, Error> {
|
||||
let resize_zone = vmm::api::VmResizeZoneData {
|
||||
id: id.to_owned(),
|
||||
desired_ram: size
|
||||
@@ -108,89 +527,52 @@ fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Res
|
||||
.0,
|
||||
};
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"resize-zone",
|
||||
Some(&serde_json::to_string(&resize_zone).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(serde_json::to_string(&resize_zone).unwrap())
|
||||
}
|
||||
|
||||
fn add_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_device_config(config: &str) -> Result<String, Error> {
|
||||
let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?;
|
||||
let device_config = serde_json::to_string(&device_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-device",
|
||||
Some(&serde_json::to_string(&device_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(device_config)
|
||||
}
|
||||
|
||||
fn add_user_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_user_device_config(config: &str) -> Result<String, Error> {
|
||||
let device_config =
|
||||
vmm::config::UserDeviceConfig::parse(config).map_err(Error::AddUserDeviceConfig)?;
|
||||
let device_config = serde_json::to_string(&device_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-user-device",
|
||||
Some(&serde_json::to_string(&device_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(device_config)
|
||||
}
|
||||
|
||||
fn remove_device_api_command(socket: &mut UnixStream, id: &str) -> Result<(), Error> {
|
||||
fn remove_device_config(id: &str) -> String {
|
||||
let remove_device_data = vmm::api::VmRemoveDeviceData { id: id.to_owned() };
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"remove-device",
|
||||
Some(&serde_json::to_string(&remove_device_data).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
serde_json::to_string(&remove_device_data).unwrap()
|
||||
}
|
||||
|
||||
fn add_disk_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_disk_config(config: &str) -> Result<String, Error> {
|
||||
let disk_config = vmm::config::DiskConfig::parse(config).map_err(Error::AddDiskConfig)?;
|
||||
let disk_config = serde_json::to_string(&disk_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-disk",
|
||||
Some(&serde_json::to_string(&disk_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(disk_config)
|
||||
}
|
||||
|
||||
fn add_fs_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_fs_config(config: &str) -> Result<String, Error> {
|
||||
let fs_config = vmm::config::FsConfig::parse(config).map_err(Error::AddFsConfig)?;
|
||||
let fs_config = serde_json::to_string(&fs_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-fs",
|
||||
Some(&serde_json::to_string(&fs_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(fs_config)
|
||||
}
|
||||
|
||||
fn add_pmem_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_pmem_config(config: &str) -> Result<String, Error> {
|
||||
let pmem_config = vmm::config::PmemConfig::parse(config).map_err(Error::AddPmemConfig)?;
|
||||
let pmem_config = serde_json::to_string(&pmem_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-pmem",
|
||||
Some(&serde_json::to_string(&pmem_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(pmem_config)
|
||||
}
|
||||
|
||||
fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_net_config(config: &str) -> Result<(String, Vec<i32>), 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
|
||||
@@ -198,113 +580,66 @@ fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Erro
|
||||
// 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();
|
||||
|
||||
simple_api_command_with_fds(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-net",
|
||||
Some(&serde_json::to_string(&net_config).unwrap()),
|
||||
fds,
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok((net_config, fds))
|
||||
}
|
||||
|
||||
fn add_vdpa_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_vdpa_config(config: &str) -> Result<String, Error> {
|
||||
let vdpa_config = vmm::config::VdpaConfig::parse(config).map_err(Error::AddVdpaConfig)?;
|
||||
let vdpa_config = serde_json::to_string(&vdpa_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-vdpa",
|
||||
Some(&serde_json::to_string(&vdpa_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(vdpa_config)
|
||||
}
|
||||
|
||||
fn add_vsock_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn add_vsock_config(config: &str) -> Result<String, Error> {
|
||||
let vsock_config = vmm::config::VsockConfig::parse(config).map_err(Error::AddVsockConfig)?;
|
||||
let vsock_config = serde_json::to_string(&vsock_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"add-vsock",
|
||||
Some(&serde_json::to_string(&vsock_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(vsock_config)
|
||||
}
|
||||
|
||||
fn snapshot_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
|
||||
fn snapshot_api_config(url: &str) -> String {
|
||||
let snapshot_config = vmm::api::VmSnapshotConfig {
|
||||
destination_url: String::from(url),
|
||||
};
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"snapshot",
|
||||
Some(&serde_json::to_string(&snapshot_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
serde_json::to_string(&snapshot_config).unwrap()
|
||||
}
|
||||
|
||||
fn restore_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> {
|
||||
fn restore_config(config: &str) -> Result<String, Error> {
|
||||
let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?;
|
||||
let restore_config = serde_json::to_string(&restore_config).unwrap();
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"restore",
|
||||
Some(&serde_json::to_string(&restore_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
Ok(restore_config)
|
||||
}
|
||||
|
||||
fn coredump_api_command(socket: &mut UnixStream, destination_url: &str) -> Result<(), Error> {
|
||||
fn coredump_config(destination_url: &str) -> String {
|
||||
let coredump_config = vmm::api::VmCoredumpData {
|
||||
destination_url: String::from(destination_url),
|
||||
};
|
||||
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"coredump",
|
||||
Some(&serde_json::to_string(&coredump_config).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
serde_json::to_string(&coredump_config).unwrap()
|
||||
}
|
||||
|
||||
fn receive_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> {
|
||||
fn receive_migration_data(url: &str) -> String {
|
||||
let receive_migration_data = vmm::api::VmReceiveMigrationData {
|
||||
receiver_url: url.to_owned(),
|
||||
};
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"receive-migration",
|
||||
Some(&serde_json::to_string(&receive_migration_data).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
|
||||
serde_json::to_string(&receive_migration_data).unwrap()
|
||||
}
|
||||
|
||||
fn send_migration_api_command(
|
||||
socket: &mut UnixStream,
|
||||
url: &str,
|
||||
local: bool,
|
||||
) -> Result<(), Error> {
|
||||
fn send_migration_data(url: &str, local: bool) -> String {
|
||||
let send_migration_data = vmm::api::VmSendMigrationData {
|
||||
destination_url: url.to_owned(),
|
||||
local,
|
||||
};
|
||||
simple_api_command(
|
||||
socket,
|
||||
"PUT",
|
||||
"send-migration",
|
||||
Some(&serde_json::to_string(&send_migration_data).unwrap()),
|
||||
)
|
||||
.map_err(Error::ApiClient)
|
||||
|
||||
serde_json::to_string(&send_migration_data).unwrap()
|
||||
}
|
||||
|
||||
fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error> {
|
||||
fn create_data(path: &str) -> Result<String, Error> {
|
||||
let mut data = String::default();
|
||||
if path == "-" {
|
||||
std::io::stdin()
|
||||
@@ -314,100 +649,7 @@ fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error>
|
||||
data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?;
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
}
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
@@ -419,6 +661,21 @@ 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)]
|
||||
@@ -688,16 +945,56 @@ fn main() {
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
if matches!(toplevel.command, SubCommandEnum::Version(_)) {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
if toplevel.api_socket.is_none() {
|
||||
println!("Please specify --api-socket");
|
||||
process::exit(1)
|
||||
}
|
||||
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 let Err(e) = do_command(&toplevel) {
|
||||
if let Err(e) = target_api.do_command(&toplevel) {
|
||||
eprintln!("Error running command: {e}");
|
||||
process::exit(1)
|
||||
};
|
||||
|
||||
170
src/main.rs
170
src/main.rs
@@ -8,7 +8,7 @@ extern crate event_monitor;
|
||||
|
||||
use argh::FromArgs;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::LevelFilter;
|
||||
use log::{warn, LevelFilter};
|
||||
use option_parser::OptionParser;
|
||||
use seccompiler::SeccompAction;
|
||||
use signal_hook::consts::SIGSYS;
|
||||
@@ -18,10 +18,11 @@ 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;
|
||||
use vmm_sys_util::terminal::Terminal;
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
#[global_allocator]
|
||||
@@ -34,6 +35,8 @@ enum Error {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
#[error("Failed to create Debug EventFd: {0}")]
|
||||
CreateDebugEventFd(#[source] std::io::Error),
|
||||
#[error("Failed to create exit EventFd: {0}")]
|
||||
CreateExitEventFd(#[source] std::io::Error),
|
||||
#[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")]
|
||||
CreateHypervisor(#[source] hypervisor::HypervisorError),
|
||||
#[error("Failed to start the VMM thread: {0}")]
|
||||
@@ -56,6 +59,12 @@ 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}")]
|
||||
@@ -91,9 +100,9 @@ impl log::Log for Logger {
|
||||
let duration = now.duration_since(self.start);
|
||||
|
||||
if record.file().is_some() && record.line().is_some() {
|
||||
writeln!(
|
||||
write!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}",
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{}:{} -- {}\r\n",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
@@ -102,9 +111,9 @@ impl log::Log for Logger {
|
||||
record.args()
|
||||
)
|
||||
} else {
|
||||
writeln!(
|
||||
write!(
|
||||
*(*(self.output.lock().unwrap())),
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}",
|
||||
"cloud-hypervisor: {:.6?}: <{}> {}:{} -- {}\r\n",
|
||||
duration,
|
||||
std::thread::current().name().unwrap_or("anonymous"),
|
||||
record.level(),
|
||||
@@ -137,19 +146,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")]
|
||||
@@ -169,27 +178,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\")")]
|
||||
@@ -197,27 +206,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")]
|
||||
@@ -236,12 +245,27 @@ 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\")")]
|
||||
@@ -254,7 +278,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")]
|
||||
@@ -415,6 +439,18 @@ 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");
|
||||
@@ -444,7 +480,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 http_sender = api_request_sender.clone();
|
||||
let api_request_sender_clone = api_request_sender.clone();
|
||||
let seccomp_action = match &toplevel.seccomp as &str {
|
||||
"true" => SeccompAction::Trap,
|
||||
"false" => SeccompAction::Allow,
|
||||
@@ -510,12 +546,16 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
||||
|
||||
let vmm_thread = vmm::start_vmm_thread(
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
|
||||
|
||||
let vmm_thread_handle = 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(),
|
||||
http_sender,
|
||||
api_request_sender_clone,
|
||||
api_request_receiver,
|
||||
#[cfg(feature = "guest_debug")]
|
||||
gdb_socket_path,
|
||||
@@ -523,41 +563,60 @@ fn start_vmm(toplevel: TopLevel) -> Result<Option<String>, Error> {
|
||||
debug_evt.try_clone().unwrap(),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
vm_debug_evt.try_clone().unwrap(),
|
||||
exit_evt.try_clone().unwrap(),
|
||||
&seccomp_action,
|
||||
hypervisor,
|
||||
)
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some();
|
||||
let r: Result<(), Error> = (|| {
|
||||
let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some();
|
||||
|
||||
if payload_present {
|
||||
let vm_params = toplevel.to_vm_params();
|
||||
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||
if payload_present {
|
||||
let vm_params = toplevel.to_vm_params();
|
||||
let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?;
|
||||
|
||||
// Create and boot the VM based off the VM config we just built.
|
||||
let sender = api_request_sender.clone();
|
||||
vmm::api::vm_create(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(Mutex::new(vm_config)),
|
||||
)
|
||||
.map_err(Error::VmCreate)?;
|
||||
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
|
||||
} else if let Some(restore_params) = toplevel.restore {
|
||||
vmm::api::vm_restore(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?),
|
||||
)
|
||||
.map_err(Error::VmRestore)?;
|
||||
// Create and boot the VM based off the VM config we just built.
|
||||
let sender = api_request_sender.clone();
|
||||
vmm::api::vm_create(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(Mutex::new(vm_config)),
|
||||
)
|
||||
.map_err(Error::VmCreate)?;
|
||||
vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?;
|
||||
} else if let Some(restore_params) = toplevel.restore {
|
||||
vmm::api::vm_restore(
|
||||
api_evt.try_clone().unwrap(),
|
||||
api_request_sender,
|
||||
Arc::new(
|
||||
config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?,
|
||||
),
|
||||
)
|
||||
.map_err(Error::VmRestore)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
if r.is_err() {
|
||||
if let Err(e) = exit_evt.write(1) {
|
||||
warn!("writing to exit EventFd: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
vmm_thread
|
||||
vmm_thread_handle
|
||||
.thread_handle
|
||||
.join()
|
||||
.map_err(Error::ThreadJoin)?
|
||||
.map_err(Error::VmmThread)?;
|
||||
|
||||
Ok(api_socket_path)
|
||||
#[cfg(feature = "dbus_api")]
|
||||
if let Some(chs) = vmm_thread_handle.dbus_shutdown_chs {
|
||||
dbus_api_graceful_shutdown(chs);
|
||||
}
|
||||
|
||||
r.map(|_| api_socket_path)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -571,7 +630,7 @@ fn main() {
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
if toplevel.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -586,14 +645,6 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: trivially safe
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
|
||||
if on_tty {
|
||||
// Don't forget to set the terminal in canonical mode
|
||||
// before to exit.
|
||||
std::io::stdin().lock().set_canon_mode().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
drop(_profiler);
|
||||
|
||||
@@ -731,6 +782,7 @@ mod unit_tests {
|
||||
gdb: false,
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
};
|
||||
|
||||
assert_eq!(expected_vm_config, result_vm_config);
|
||||
|
||||
2
test_data/cloud-init/ubuntu/local/meta-data
Normal file
2
test_data/cloud-init/ubuntu/local/meta-data
Normal file
@@ -0,0 +1,2 @@
|
||||
instance-id: cloud
|
||||
local-hostname: cloud
|
||||
7
test_data/cloud-init/ubuntu/local/network-config
Normal file
7
test_data/cloud-init/ubuntu/local/network-config
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
ethernets:
|
||||
ens4:
|
||||
match:
|
||||
macaddress: 12:34:56:78:90:ab
|
||||
addresses: [192.168.249.2/24]
|
||||
gateway4: 192.168.249.1
|
||||
10
test_data/cloud-init/ubuntu/local/user-data
Normal file
10
test_data/cloud-init/ubuntu/local/user-data
Normal file
@@ -0,0 +1,10 @@
|
||||
#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
|
||||
@@ -5,12 +5,12 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
dirs = "4.0.0"
|
||||
dirs = "5.0.0"
|
||||
epoll = "4.3.1"
|
||||
libc = "0.2.139"
|
||||
once_cell = "1.17.1"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.95"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
ssh2 = { version = "0.9.4", features = ["vendored-openssl"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
wait-timeout = "0.2.0"
|
||||
|
||||
@@ -249,7 +249,8 @@ impl DiskConfig for UbuntuDiskConfig {
|
||||
.unwrap()
|
||||
.join("test_data")
|
||||
.join("cloud-init")
|
||||
.join("ubuntu");
|
||||
.join("ubuntu")
|
||||
.join("ci");
|
||||
|
||||
vec!["meta-data"].iter().for_each(|x| {
|
||||
rate_limited_copy(source_file_dir.join(x), cloud_init_directory.join(x))
|
||||
@@ -868,14 +869,6 @@ impl Guest {
|
||||
.map_err(Error::Parsing)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn get_initial_apicid(&self) -> Result<u32, Error> {
|
||||
self.ssh_command("grep \"initial apicid\" /proc/cpuinfo | grep -o \"[0-9]*\"")?
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(Error::Parsing)
|
||||
}
|
||||
|
||||
pub fn get_total_memory(&self) -> Result<u32, Error> {
|
||||
self.ssh_command("grep MemTotal /proc/meminfo | grep -o \"[0-9]*\"")?
|
||||
.trim()
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
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;
|
||||
@@ -32,6 +30,9 @@ use test_infra::*;
|
||||
use vmm_sys_util::{tempdir::TempDir, tempfile::TempFile};
|
||||
use wait_timeout::ChildExt;
|
||||
|
||||
// Constant taken from the VMM crate.
|
||||
const MAX_NUM_PCI_SEGMENTS: u16 = 96;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod x86_64 {
|
||||
pub const FOCAL_IMAGE_NAME: &str = "focal-server-cloudimg-amd64-custom-20210609-0.raw";
|
||||
@@ -72,6 +73,324 @@ 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");
|
||||
@@ -1206,7 +1525,10 @@ fn _test_virtio_fs(
|
||||
.default_net()
|
||||
.args(["--api-socket", &api_socket]);
|
||||
if pci_segment.is_some() {
|
||||
guest_command.args(["--platform", "num_pci_segments=16"]);
|
||||
guest_command.args([
|
||||
"--platform",
|
||||
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
|
||||
]);
|
||||
}
|
||||
|
||||
let fs_params = format!(
|
||||
@@ -1904,7 +2226,7 @@ fn enable_guest_watchdog(guest: &Guest, watchdog_sec: u32) {
|
||||
}
|
||||
|
||||
mod common_parallel {
|
||||
use std::{fs::OpenOptions, io::SeekFrom, os::unix::net::UnixStream};
|
||||
use std::{fs::OpenOptions, io::SeekFrom};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -1942,7 +2264,6 @@ mod common_parallel {
|
||||
guest.wait_vm_boot(Some(120)).unwrap();
|
||||
|
||||
assert_eq!(guest.get_cpu_count().unwrap_or_default(), 1);
|
||||
assert_eq!(guest.get_initial_apicid().unwrap_or(1), 0);
|
||||
assert!(guest.get_total_memory().unwrap_or_default() > 480_000);
|
||||
assert_eq!(guest.get_pci_bridge_class().unwrap_or_default(), "0x060000");
|
||||
|
||||
@@ -2046,19 +2367,16 @@ mod common_parallel {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_cpu_topology_421() {
|
||||
test_cpu_topology(4, 2, 1, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_cpu_topology_142() {
|
||||
test_cpu_topology(1, 4, 2, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
fn test_cpu_topology_262() {
|
||||
test_cpu_topology(2, 6, 2, false);
|
||||
}
|
||||
@@ -2319,7 +2637,10 @@ mod common_parallel {
|
||||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(["--platform", "num_pci_segments=16,iommu_segments=[1]"])
|
||||
.args([
|
||||
"--platform",
|
||||
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS},iommu_segments=[1]"),
|
||||
])
|
||||
.default_disks()
|
||||
.capture_output()
|
||||
.default_net();
|
||||
@@ -2475,7 +2796,10 @@ mod common_parallel {
|
||||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args(["--platform", "num_pci_segments=16"])
|
||||
.args([
|
||||
"--platform",
|
||||
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
|
||||
])
|
||||
.args([
|
||||
"--disk",
|
||||
format!(
|
||||
@@ -2502,15 +2826,15 @@ mod common_parallel {
|
||||
let grep_cmd = "lspci | grep \"Host bridge\" | wc -l";
|
||||
|
||||
let r = std::panic::catch_unwind(|| {
|
||||
// There should be 16 PCI host bridges in the guest.
|
||||
// There should be MAX_NUM_PCI_SEGMENTS PCI host bridges in the guest.
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command(grep_cmd)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.parse::<u32>()
|
||||
.parse::<u16>()
|
||||
.unwrap_or_default(),
|
||||
16
|
||||
MAX_NUM_PCI_SEGMENTS
|
||||
);
|
||||
|
||||
// Check both if /dev/vdc exists and if the block size is 4M.
|
||||
@@ -3971,7 +4295,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);
|
||||
@@ -4030,279 +4354,35 @@ mod common_parallel {
|
||||
}
|
||||
|
||||
#[test]
|
||||
// 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() {
|
||||
fn test_api_http_shutdown() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
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_api_shutdown(TargetApi::new_http_api(&guest.tmp_dir), guest)
|
||||
}
|
||||
|
||||
#[test]
|
||||
// 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() {
|
||||
fn test_api_http_delete() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
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_api_delete(TargetApi::new_http_api(&guest.tmp_dir), guest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// 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() {
|
||||
fn test_api_http_pause_resume() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
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_api_pause_resume(TargetApi::new_http_api(&guest.tmp_dir), guest)
|
||||
}
|
||||
|
||||
#[test]
|
||||
// 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() {
|
||||
fn test_api_http_create_boot() {
|
||||
let focal = UbuntuDiskConfig::new(FOCAL_IMAGE_NAME.to_string());
|
||||
let guest = Guest::new(Box::new(focal));
|
||||
|
||||
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_api_create_boot(TargetApi::new_http_api(&guest.tmp_dir), guest)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4916,7 +4996,6 @@ 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;
|
||||
@@ -4977,12 +5056,9 @@ 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.try_into().unwrap(),
|
||||
)
|
||||
};
|
||||
let loop_device_number =
|
||||
unsafe { libc::ioctl(loop_ctl_file.as_raw_fd(), LOOP_CTL_GET_FREE as _) };
|
||||
|
||||
if loop_device_number < 0 {
|
||||
panic!("Couldn't find a free loop device");
|
||||
}
|
||||
@@ -5014,7 +5090,7 @@ mod common_parallel {
|
||||
let ret = unsafe {
|
||||
libc::ioctl(
|
||||
loop_device_file.as_raw_fd(),
|
||||
LOOP_CONFIGURE.try_into().unwrap(),
|
||||
LOOP_CONFIGURE as _,
|
||||
&loop_config,
|
||||
)
|
||||
};
|
||||
@@ -5299,7 +5375,10 @@ mod common_parallel {
|
||||
.capture_output();
|
||||
|
||||
if pci_segment.is_some() {
|
||||
cmd.args(["--platform", "num_pci_segments=16"]);
|
||||
cmd.args([
|
||||
"--platform",
|
||||
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
|
||||
]);
|
||||
}
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
@@ -5435,7 +5514,10 @@ mod common_parallel {
|
||||
.capture_output();
|
||||
|
||||
if pci_segment.is_some() {
|
||||
cmd.args(["--platform", "num_pci_segments=16"]);
|
||||
cmd.args([
|
||||
"--platform",
|
||||
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS}"),
|
||||
]);
|
||||
}
|
||||
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
@@ -6238,6 +6320,18 @@ mod common_parallel {
|
||||
.unwrap_or_default(),
|
||||
2
|
||||
);
|
||||
|
||||
guest.reboot_linux(0, None);
|
||||
|
||||
assert_eq!(
|
||||
guest
|
||||
.ssh_command("ip -o link | wc -l")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.parse::<u32>()
|
||||
.unwrap_or_default(),
|
||||
2
|
||||
);
|
||||
});
|
||||
|
||||
let _ = child.kill();
|
||||
@@ -6251,11 +6345,13 @@ mod common_parallel {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "aarch64", ignore = "See #5443")]
|
||||
fn test_macvtap() {
|
||||
_test_macvtap(false, "guestmacvtap0", "hostmacvtap0")
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "aarch64", ignore = "See #5443")]
|
||||
fn test_macvtap_hotplug() {
|
||||
_test_macvtap(true, "guestmacvtap1", "hostmacvtap1")
|
||||
}
|
||||
@@ -6765,6 +6861,116 @@ 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::*;
|
||||
@@ -8076,7 +8282,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);
|
||||
|
||||
@@ -309,11 +309,7 @@ impl Emulator {
|
||||
}
|
||||
|
||||
self.established_flag_cached = true;
|
||||
if est.resp.bit != 0 {
|
||||
self.established_flag = false;
|
||||
} else {
|
||||
self.established_flag = true;
|
||||
}
|
||||
self.established_flag = est.resp.bit == 0;
|
||||
|
||||
self.established_flag
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ edition = "2021"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
once_cell = "1.17.1"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.95"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
|
||||
[features]
|
||||
tracing = []
|
||||
|
||||
@@ -11,5 +11,5 @@ crc32c = "0.6.3"
|
||||
libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
remain = "0.2.6"
|
||||
thiserror = "1.0.39"
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
thiserror = "1.0.40"
|
||||
uuid = { version = "1.3.4", features = ["v4"] }
|
||||
|
||||
@@ -145,11 +145,7 @@ impl DiskSpec {
|
||||
let bits = f
|
||||
.read_u32::<LittleEndian>()
|
||||
.map_err(VhdxMetadataError::ReadMetadata)?;
|
||||
if bits & BLOCK_HAS_PARENT != 0 {
|
||||
disk_spec.has_parent = true;
|
||||
} else {
|
||||
disk_spec.has_parent = false;
|
||||
}
|
||||
disk_spec.has_parent = bits & BLOCK_HAS_PARENT != 0;
|
||||
|
||||
metadata_presence |= METADATA_FILE_PARAMETER_PRESENT;
|
||||
} else if metadata_entry.item_id
|
||||
|
||||
@@ -14,10 +14,10 @@ libc = "0.2.139"
|
||||
log = "0.4.17"
|
||||
option_parser = { path = "../option_parser" }
|
||||
qcow = { path = "../qcow" }
|
||||
vhost = { version = "0.6.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.8.0"
|
||||
vhost = { version = "0.7.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.9.0"
|
||||
virtio-bindings = "0.2.0"
|
||||
virtio-queue = "0.7.1"
|
||||
vm-memory = "0.10.0"
|
||||
virtio-queue = "0.8.0"
|
||||
vm-memory = "0.11.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ fn main() {
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
if toplevel.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.6.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.8.0"
|
||||
vhost = { version = "0.7.0", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = "0.9.0"
|
||||
virtio-bindings = "0.2.0"
|
||||
vm-memory = "0.10.0"
|
||||
vm-memory = "0.11.0"
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ fn main() {
|
||||
let toplevel: TopLevel = argh::from_env();
|
||||
|
||||
if toplevel.version {
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILT_VERSION"));
|
||||
println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
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.151", features = ["derive"] }
|
||||
serde_json = "1.0.95"
|
||||
serde = { version = "1.0.164", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
serial_buffer = { path = "../serial_buffer" }
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vhost = { version = "0.6.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern", "vhost-vdpa"] }
|
||||
vhost = { version = "0.7.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.7.1"
|
||||
virtio-queue = "0.8.0"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-migration = { path = "../vm-migration" }
|
||||
vm-virtio = { path = "../vm-virtio" }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
@@ -39,19 +39,24 @@ pub mod vhost_user;
|
||||
pub mod vsock;
|
||||
pub mod watchdog;
|
||||
|
||||
pub use self::balloon::*;
|
||||
pub use self::block::*;
|
||||
pub use self::console::*;
|
||||
pub use self::device::*;
|
||||
pub use self::epoll_helper::*;
|
||||
pub use self::iommu::*;
|
||||
pub use self::mem::*;
|
||||
pub use self::net::*;
|
||||
pub use self::pmem::*;
|
||||
pub use self::rng::*;
|
||||
pub use self::vdpa::*;
|
||||
pub use self::vsock::*;
|
||||
pub use self::watchdog::*;
|
||||
pub use self::balloon::Balloon;
|
||||
pub use self::block::{Block, BlockState};
|
||||
pub use self::console::{Console, ConsoleResizer, Endpoint};
|
||||
pub use self::device::{
|
||||
DmaRemapping, UserspaceMapping, VirtioCommon, VirtioDevice, VirtioInterrupt,
|
||||
VirtioInterruptType, VirtioSharedMemoryList,
|
||||
};
|
||||
pub use self::epoll_helper::{
|
||||
EpollHelper, EpollHelperError, EpollHelperHandler, EPOLL_HELPER_EVENT_LAST,
|
||||
};
|
||||
pub use self::iommu::{AccessPlatformMapping, Iommu, IommuMapping};
|
||||
pub use self::mem::{BlocksState, Mem, VirtioMemMappingSource, VIRTIO_MEM_ALIGN_SIZE};
|
||||
pub use self::net::{Net, NetCtrlEpollHandler};
|
||||
pub use self::pmem::Pmem;
|
||||
pub use self::rng::Rng;
|
||||
pub use self::vdpa::{Vdpa, VdpaDmaMapping};
|
||||
pub use self::vsock::Vsock;
|
||||
pub use self::watchdog::Watchdog;
|
||||
use vm_memory::{bitmap::AtomicBitmap, GuestAddress, GuestMemory};
|
||||
use vm_virtio::VirtioDeviceType;
|
||||
|
||||
@@ -86,8 +91,8 @@ pub enum ActivateError {
|
||||
ThreadSpawn(std::io::Error),
|
||||
#[error("Failed to setup vhost-user-fs daemon: {0}")]
|
||||
VhostUserFsSetup(vhost_user::Error),
|
||||
#[error("Failed to setup vhost-user-blk daemon: {0}")]
|
||||
VhostUserBlkSetup(vhost_user::Error),
|
||||
#[error("Failed to setup vhost-user daemon: {0}")]
|
||||
VhostUserSetup(vhost_user::Error),
|
||||
#[error("Failed to create seccomp filter: {0}")]
|
||||
CreateSeccompFilter(seccompiler::Error),
|
||||
#[error("Failed to create rate limiter: {0}")]
|
||||
|
||||
@@ -151,6 +151,7 @@ fn virtio_rng_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
|
||||
fn virtio_vhost_fs_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![
|
||||
(libc::SYS_clock_nanosleep, vec![]),
|
||||
(libc::SYS_connect, vec![]),
|
||||
(libc::SYS_nanosleep, vec![]),
|
||||
(libc::SYS_pread64, vec![]),
|
||||
@@ -170,8 +171,11 @@ fn virtio_vhost_net_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![
|
||||
(libc::SYS_accept4, vec![]),
|
||||
(libc::SYS_bind, vec![]),
|
||||
(libc::SYS_clock_nanosleep, vec![]),
|
||||
(libc::SYS_connect, vec![]),
|
||||
(libc::SYS_getcwd, vec![]),
|
||||
(libc::SYS_listen, vec![]),
|
||||
(libc::SYS_nanosleep, vec![]),
|
||||
(libc::SYS_recvmsg, vec![]),
|
||||
(libc::SYS_sendmsg, vec![]),
|
||||
(libc::SYS_sendto, vec![]),
|
||||
@@ -184,7 +188,14 @@ fn virtio_vhost_net_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
}
|
||||
|
||||
fn virtio_vhost_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
vec![]
|
||||
vec![
|
||||
(libc::SYS_clock_nanosleep, vec![]),
|
||||
(libc::SYS_connect, vec![]),
|
||||
(libc::SYS_nanosleep, vec![]),
|
||||
(libc::SYS_recvmsg, vec![]),
|
||||
(libc::SYS_sendmsg, vec![]),
|
||||
(libc::SYS_socket, vec![]),
|
||||
]
|
||||
}
|
||||
|
||||
fn create_vsock_ioctl_seccomp_rule() -> Vec<SeccompRule> {
|
||||
@@ -248,6 +259,7 @@ fn virtio_thread_common() -> Vec<(i64, Vec<SeccompRule>)> {
|
||||
(libc::SYS_madvise, vec![]),
|
||||
(libc::SYS_mmap, vec![]),
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_mremap, vec![]),
|
||||
(libc::SYS_munmap, vec![]),
|
||||
(libc::SYS_openat, vec![]),
|
||||
(libc::SYS_read, vec![]),
|
||||
|
||||
@@ -48,14 +48,13 @@ 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 {
|
||||
CommonConfig = 1,
|
||||
NotifyConfig = 2,
|
||||
IsrConfig = 3,
|
||||
DeviceConfig = 4,
|
||||
PciConfig = 5,
|
||||
SharedMemoryConfig = 8,
|
||||
Common = 1,
|
||||
Notify = 2,
|
||||
Isr = 3,
|
||||
Device = 4,
|
||||
Pci = 5,
|
||||
SharedMemory = 8,
|
||||
}
|
||||
|
||||
// This offset represents the 2 bytes omitted from the VirtioPciCap structure
|
||||
@@ -210,7 +209,7 @@ impl PciCapability for VirtioPciCfgCap {
|
||||
impl VirtioPciCfgCap {
|
||||
fn new() -> Self {
|
||||
VirtioPciCfgCap {
|
||||
cap: VirtioPciCap::new(PciCapabilityType::PciConfig, 0, 0, 0),
|
||||
cap: VirtioPciCap::new(PciCapabilityType::Pci, 0, 0, 0),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -667,7 +666,7 @@ impl VirtioPciDevice {
|
||||
) -> std::result::Result<(), PciDeviceError> {
|
||||
// Add pointers to the different configuration structures from the PCI capabilities.
|
||||
let common_cap = VirtioPciCap::new(
|
||||
PciCapabilityType::CommonConfig,
|
||||
PciCapabilityType::Common,
|
||||
settings_bar,
|
||||
COMMON_CONFIG_BAR_OFFSET as u32,
|
||||
COMMON_CONFIG_SIZE as u32,
|
||||
@@ -677,7 +676,7 @@ impl VirtioPciDevice {
|
||||
.map_err(PciDeviceError::CapabilitiesSetup)?;
|
||||
|
||||
let isr_cap = VirtioPciCap::new(
|
||||
PciCapabilityType::IsrConfig,
|
||||
PciCapabilityType::Isr,
|
||||
settings_bar,
|
||||
ISR_CONFIG_BAR_OFFSET as u32,
|
||||
ISR_CONFIG_SIZE as u32,
|
||||
@@ -688,7 +687,7 @@ impl VirtioPciDevice {
|
||||
|
||||
// TODO(dgreid) - set based on device's configuration size?
|
||||
let device_cap = VirtioPciCap::new(
|
||||
PciCapabilityType::DeviceConfig,
|
||||
PciCapabilityType::Device,
|
||||
settings_bar,
|
||||
DEVICE_CONFIG_BAR_OFFSET as u32,
|
||||
DEVICE_CONFIG_SIZE as u32,
|
||||
@@ -698,7 +697,7 @@ impl VirtioPciDevice {
|
||||
.map_err(PciDeviceError::CapabilitiesSetup)?;
|
||||
|
||||
let notify_cap = VirtioPciNotifyCap::new(
|
||||
PciCapabilityType::NotifyConfig,
|
||||
PciCapabilityType::Notify,
|
||||
settings_bar,
|
||||
NOTIFICATION_BAR_OFFSET as u32,
|
||||
NOTIFICATION_SIZE as u32,
|
||||
@@ -1057,7 +1056,7 @@ impl PciDevice for VirtioPciDevice {
|
||||
|
||||
for (idx, shm) in shm_list.region_list.iter().enumerate() {
|
||||
let shm_cap = VirtioPciCap64::new(
|
||||
PciCapabilityType::SharedMemoryConfig,
|
||||
PciCapabilityType::SharedMemory,
|
||||
VIRTIO_SHM_BAR_INDEX as u8,
|
||||
idx as u8,
|
||||
shm.offset,
|
||||
|
||||
@@ -605,7 +605,7 @@ impl VirtioDevice for Fs {
|
||||
&mut self,
|
||||
shm_regions: VirtioSharedMemoryList,
|
||||
) -> std::result::Result<(), crate::Error> {
|
||||
if let Some(mut cache) = self.cache.as_mut() {
|
||||
if let Some(cache) = self.cache.as_mut() {
|
||||
cache.0 = shm_regions;
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
@@ -237,7 +237,7 @@ impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
|
||||
.map_err(|e| {
|
||||
EpollHelperError::IoError(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("failed reconnecting vhost-user backend{e:?}"),
|
||||
format!("failed reconnecting vhost-user backend: {e:?}"),
|
||||
))
|
||||
})?;
|
||||
|
||||
@@ -340,7 +340,7 @@ impl VhostUserCommon {
|
||||
&slave_req_handler,
|
||||
inflight.as_mut(),
|
||||
)
|
||||
.map_err(ActivateError::VhostUserBlkSetup)?;
|
||||
.map_err(ActivateError::VhostUserSetup)?;
|
||||
|
||||
Ok(VhostUserEpollHandler {
|
||||
vu: vu.clone(),
|
||||
@@ -413,7 +413,7 @@ impl VhostUserCommon {
|
||||
pub fn pause(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(vu) = &self.vu {
|
||||
vu.lock().unwrap().pause_vhost_user().map_err(|e| {
|
||||
MigratableError::Pause(anyhow!("Error pausing vhost-user-blk backend: {:?}", e))
|
||||
MigratableError::Pause(anyhow!("Error pausing vhost-user backend: {:?}", e))
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -423,7 +423,7 @@ impl VhostUserCommon {
|
||||
pub fn resume(&mut self) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(vu) = &self.vu {
|
||||
vu.lock().unwrap().resume_vhost_user().map_err(|e| {
|
||||
MigratableError::Resume(anyhow!("Error resuming vhost-user-blk backend: {:?}", e))
|
||||
MigratableError::Resume(anyhow!("Error resuming vhost-user backend: {:?}", e))
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
|
||||
@@ -436,7 +436,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_tx_packet_assembly() {
|
||||
// Test case: successful TX packet assembly.
|
||||
{
|
||||
@@ -581,7 +580,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_packet_hdr_accessors() {
|
||||
const SRC_CID: u64 = 1;
|
||||
const DST_CID: u64 = 2;
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.139"
|
||||
vm-memory = "0.10.0"
|
||||
vm-memory = "0.11.0"
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
arch = { path = "../arch" }
|
||||
|
||||
@@ -63,7 +63,6 @@ impl GsiAllocator {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(clippy::new_without_default)]
|
||||
/// New GSI allocator
|
||||
pub fn new() -> Self {
|
||||
GsiAllocator {
|
||||
@@ -106,3 +105,10 @@ impl GsiAllocator {
|
||||
Ok(irq)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
impl Default for GsiAllocator {
|
||||
fn default() -> Self {
|
||||
GsiAllocator::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
mod address;
|
||||
mod gsi;
|
||||
/// page size related utility funtions
|
||||
pub mod page_size;
|
||||
mod system;
|
||||
|
||||
pub use crate::address::AddressAllocator;
|
||||
|
||||
38
vm-allocator/src/page_size.rs
Normal file
38
vm-allocator/src/page_size.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2023 Arm Limited (or its affiliates). All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use libc::{sysconf, _SC_PAGESIZE};
|
||||
|
||||
/// get host page size
|
||||
pub fn get_page_size() -> u64 {
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
unsafe { sysconf(_SC_PAGESIZE) as u64 }
|
||||
}
|
||||
|
||||
/// round up address to let it align page size
|
||||
pub fn align_page_size_up(address: u64) -> u64 {
|
||||
let page_size = get_page_size();
|
||||
(address + page_size - 1) & !(page_size - 1)
|
||||
}
|
||||
|
||||
/// round down address to let it align page size
|
||||
pub fn align_page_size_down(address: u64) -> u64 {
|
||||
let page_size = get_page_size();
|
||||
address & !(page_size - 1)
|
||||
}
|
||||
|
||||
/// Test if address is 4k aligned
|
||||
pub fn is_4k_aligned(address: u64) -> bool {
|
||||
(address & 0xfff) == 0
|
||||
}
|
||||
|
||||
/// Test if size is 4k aligned
|
||||
pub fn is_4k_multiple(size: u64) -> bool {
|
||||
(size & 0xfff) == 0
|
||||
}
|
||||
|
||||
/// Test if address is page size aligned
|
||||
pub fn is_page_size_aligned(address: u64) -> bool {
|
||||
let page_size = get_page_size();
|
||||
address & (page_size - 1) == 0
|
||||
}
|
||||
@@ -14,14 +14,7 @@ use crate::gsi::GsiAllocator;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::gsi::GsiApic;
|
||||
|
||||
use libc::{sysconf, _SC_PAGESIZE};
|
||||
|
||||
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
|
||||
#[inline(always)]
|
||||
fn pagesize() -> usize {
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
unsafe { sysconf(_SC_PAGESIZE) as usize }
|
||||
}
|
||||
use crate::page_size::get_page_size;
|
||||
|
||||
/// Manages allocating system resources such as address space and interrupt numbers.
|
||||
///
|
||||
@@ -126,7 +119,7 @@ impl SystemAllocator {
|
||||
self.platform_mmio_address_space.allocate(
|
||||
address,
|
||||
size,
|
||||
Some(align_size.unwrap_or(pagesize() as u64)),
|
||||
Some(align_size.unwrap_or(get_page_size())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,7 +133,7 @@ impl SystemAllocator {
|
||||
self.mmio_hole_address_space.allocate(
|
||||
address,
|
||||
size,
|
||||
Some(align_size.unwrap_or(pagesize() as u64)),
|
||||
Some(align_size.unwrap_or(get_page_size())),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ kvm = ["vfio-ioctls/kvm"]
|
||||
mshv = ["vfio-ioctls/mshv"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
hypervisor = { path = "../hypervisor" }
|
||||
thiserror = "1.0.39"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
thiserror = "1.0.40"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
vfio-ioctls = { git = "https://github.com/rust-vmm/vfio", branch = "main", default-features = false }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap"] }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap"] }
|
||||
vmm-sys-util = "0.11.0"
|
||||
|
||||
|
||||
@@ -110,8 +110,7 @@ impl Bus {
|
||||
let devices = self.devices.read().unwrap();
|
||||
let (range, dev) = devices
|
||||
.range(..=BusRange { base: addr, len: 1 })
|
||||
.rev()
|
||||
.next()?;
|
||||
.next_back()?;
|
||||
dev.upgrade().map(|d| (*range, d.clone()))
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ authors = ["The Cloud Hypervisor Authors"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
thiserror = "1.0.39"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.95"
|
||||
anyhow = "1.0.71"
|
||||
thiserror = "1.0.40"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
versionize = "0.1.10"
|
||||
versionize_derive = "0.1.4"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic"] }
|
||||
|
||||
@@ -12,7 +12,7 @@ use versionize::{VersionMap, Versionize};
|
||||
pub mod protocol;
|
||||
|
||||
/// Global VMM version for versioning
|
||||
const MAJOR_VERSION: u16 = 31;
|
||||
const MAJOR_VERSION: u16 = 33;
|
||||
const MINOR_VERSION: u16 = 0;
|
||||
const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@ default = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.17"
|
||||
virtio-queue = "0.7.1"
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
virtio-queue = "0.8.0"
|
||||
vm-memory = { version = "0.11.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
|
||||
@@ -6,6 +6,7 @@ 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"]
|
||||
@@ -14,19 +15,21 @@ tracing = ["tracer/tracing"]
|
||||
|
||||
[dependencies]
|
||||
acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main" }
|
||||
anyhow = "1.0.69"
|
||||
anyhow = "1.0.71"
|
||||
arc-swap = "1.5.1"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "1.3.2"
|
||||
bitflags = "2.3.3"
|
||||
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.8.1", features = ["elf", "bzimage", "pe"] }
|
||||
linux-loader = { version = "0.9.0", 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" }
|
||||
@@ -35,24 +38,25 @@ option_parser = { path = "../option_parser" }
|
||||
pci = { path = "../pci" }
|
||||
qcow = { path = "../qcow" }
|
||||
seccompiler = "0.3.0"
|
||||
serde = { version = "1.0.151", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.95"
|
||||
serde = { version = "1.0.164", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.96"
|
||||
serial_buffer = { path = "../serial_buffer" }
|
||||
signal-hook = "0.3.15"
|
||||
thiserror = "1.0.39"
|
||||
thiserror = "1.0.40"
|
||||
tracer = { path = "../tracer" }
|
||||
uuid = "1.3.0"
|
||||
uuid = "1.3.4"
|
||||
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.7.1"
|
||||
virtio-queue = "0.8.0"
|
||||
vm-allocator = { path = "../vm-allocator" }
|
||||
vm-device = { path = "../vm-device" }
|
||||
vm-memory = { version = "0.10.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
|
||||
vm-memory = { version = "0.11.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"] }
|
||||
zerocopy = "0.6.1"
|
||||
zbus = { version = "3.11.1", optional = true }
|
||||
zerocopy = "0.6.1"
|
||||
|
||||
@@ -41,7 +41,7 @@ pub const ACPI_APIC_GENERIC_TRANSLATOR: u8 = 15;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct PciRangeEntry {
|
||||
pub base_address: u64,
|
||||
pub segment: u16,
|
||||
@@ -52,7 +52,7 @@ struct PciRangeEntry {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct MemoryAffinity {
|
||||
pub type_: u8,
|
||||
pub length: u8,
|
||||
@@ -69,7 +69,7 @@ struct MemoryAffinity {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct ProcessorLocalX2ApicAffinity {
|
||||
pub type_: u8,
|
||||
pub length: u8,
|
||||
@@ -83,7 +83,7 @@ struct ProcessorLocalX2ApicAffinity {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct ProcessorGiccAffinity {
|
||||
pub type_: u8,
|
||||
pub length: u8,
|
||||
@@ -143,7 +143,7 @@ impl MemoryAffinity {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct ViotVirtioPciNode {
|
||||
pub type_: u8,
|
||||
_reserved: u8,
|
||||
@@ -155,7 +155,7 @@ struct ViotVirtioPciNode {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct ViotPciRangeNode {
|
||||
pub type_: u8,
|
||||
_reserved: u8,
|
||||
@@ -239,7 +239,7 @@ fn create_facp_table(dsdt_offset: GuestAddress, device_manager: &Arc<Mutex<Devic
|
||||
// X_DSDT
|
||||
facp.write(140, dsdt_offset.0);
|
||||
// Hypervisor Vendor Identity
|
||||
facp.write(268, b"CLOUDHYP");
|
||||
facp.write_bytes(268, b"CLOUDHYP");
|
||||
|
||||
facp.update_checksum();
|
||||
|
||||
|
||||
377
vmm/src/api/dbus/mod.rs
Normal file
377
vmm/src/api/dbus/mod.rs
Normal file
@@ -0,0 +1,377 @@
|
||||
// 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)))
|
||||
}
|
||||
@@ -36,13 +36,22 @@ impl EndpointHandler for VmCreate {
|
||||
match &req.body {
|
||||
Some(body) => {
|
||||
// Deserialize into a VmConfig
|
||||
let vm_config: VmConfig = match serde_json::from_slice(body.raw())
|
||||
let mut vm_config: VmConfig = match serde_json::from_slice(body.raw())
|
||||
.map_err(HttpError::SerdeJsonDeserialize)
|
||||
{
|
||||
Ok(config) => config,
|
||||
Err(e) => return error_response(e, StatusCode::BadRequest),
|
||||
};
|
||||
|
||||
if let Some(ref mut nets) = vm_config.net {
|
||||
if nets.iter().any(|net| net.fds.is_some()) {
|
||||
warn!("Ignoring FDs sent via the HTTP request body");
|
||||
}
|
||||
for net in nets {
|
||||
net.fds = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Call vm_create()
|
||||
match vm_create(api_notifier, api_sender, Arc::new(Mutex::new(vm_config)))
|
||||
.map_err(HttpError::ApiError)
|
||||
@@ -105,6 +114,10 @@ impl EndpointHandler for VmActionHandler {
|
||||
),
|
||||
AddNet(_) => {
|
||||
let mut net_cfg: NetConfig = serde_json::from_slice(body.raw())?;
|
||||
if net_cfg.fds.is_some() {
|
||||
warn!("Ignoring FDs sent via the HTTP request body");
|
||||
net_cfg.fds = None;
|
||||
}
|
||||
// Update network config with optional files that might have
|
||||
// been sent through control message.
|
||||
if !files.is_empty() {
|
||||
@@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::api::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown};
|
||||
use self::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,6 +23,8 @@ 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 {
|
||||
@@ -282,7 +284,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::Api, hypervisor_type)
|
||||
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
|
||||
.map_err(VmmError::CreateSeccompFilter)?;
|
||||
|
||||
thread::Builder::new()
|
||||
@@ -28,12 +28,15 @@
|
||||
//! 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,
|
||||
@@ -165,7 +168,9 @@ pub struct VmInfo {
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct VmmPingResponse {
|
||||
pub build_version: String,
|
||||
pub version: String,
|
||||
pub pid: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Default, Debug)]
|
||||
@@ -230,7 +235,6 @@ pub enum ApiResponsePayload {
|
||||
/// This is the response sent by the VMM API server through the mpsc channel.
|
||||
pub type ApiResponse = std::result::Result<ApiResponsePayload, ApiError>;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
pub enum ApiRequest {
|
||||
/// Create the virtual machine. This request payload is a VM configuration
|
||||
|
||||
@@ -438,8 +438,13 @@ components:
|
||||
- version
|
||||
type: object
|
||||
properties:
|
||||
build_version:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
pid:
|
||||
type: integer
|
||||
format: int64
|
||||
description: Virtual Machine Monitor information
|
||||
|
||||
VmInfo:
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use virtio_devices::{RateLimiterConfig, TokenBucketConfig};
|
||||
|
||||
const MAX_NUM_PCI_SEGMENTS: u16 = 16;
|
||||
const MAX_NUM_PCI_SEGMENTS: u16 = 96;
|
||||
|
||||
/// Errors associated with VM configuration parameters.
|
||||
#[derive(Debug, Error)]
|
||||
@@ -1134,38 +1134,6 @@ impl NetConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NetConfig {
|
||||
fn clone(&self) -> Self {
|
||||
NetConfig {
|
||||
tap: self.tap.clone(),
|
||||
vhost_socket: self.vhost_socket.clone(),
|
||||
id: self.id.clone(),
|
||||
fds: self
|
||||
.fds
|
||||
.as_ref()
|
||||
// SAFETY: We have been handed these FDs through the API
|
||||
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NetConfig {
|
||||
fn drop(&mut self) {
|
||||
if let Some(mut fds) = self.fds.take() {
|
||||
for fd in fds.drain(..) {
|
||||
// Skip reserved FDs
|
||||
if fd <= 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// SAFETY: Safe as the fd was given to the config by the API
|
||||
unsafe { libc::close(fd) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RngConfig {
|
||||
pub fn parse(rng: &str) -> Result<Self> {
|
||||
let mut parser = OptionParser::new();
|
||||
@@ -2125,23 +2093,83 @@ impl VmConfig {
|
||||
gdb,
|
||||
platform,
|
||||
tpm,
|
||||
preserved_fds: None,
|
||||
};
|
||||
config.validate().map_err(Error::Validation)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// To use this safely, the caller must guarantee that the input
|
||||
/// fds are all valid.
|
||||
pub unsafe fn add_preserved_fds(&mut self, mut fds: Vec<i32>) {
|
||||
if fds.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(preserved_fds) = &self.preserved_fds {
|
||||
fds.append(&mut preserved_fds.clone());
|
||||
}
|
||||
|
||||
self.preserved_fds = Some(fds);
|
||||
}
|
||||
|
||||
#[cfg(feature = "tdx")]
|
||||
pub fn is_tdx_enabled(&self) -> bool {
|
||||
self.platform.as_ref().map(|p| p.tdx).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for VmConfig {
|
||||
fn clone(&self) -> Self {
|
||||
VmConfig {
|
||||
cpus: self.cpus.clone(),
|
||||
memory: self.memory.clone(),
|
||||
payload: self.payload.clone(),
|
||||
disks: self.disks.clone(),
|
||||
net: self.net.clone(),
|
||||
rng: self.rng.clone(),
|
||||
balloon: self.balloon.clone(),
|
||||
fs: self.fs.clone(),
|
||||
pmem: self.pmem.clone(),
|
||||
serial: self.serial.clone(),
|
||||
console: self.console.clone(),
|
||||
devices: self.devices.clone(),
|
||||
user_devices: self.user_devices.clone(),
|
||||
vdpa: self.vdpa.clone(),
|
||||
vsock: self.vsock.clone(),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
sgx_epc: self.sgx_epc.clone(),
|
||||
numa: self.numa.clone(),
|
||||
platform: self.platform.clone(),
|
||||
tpm: self.tpm.clone(),
|
||||
preserved_fds: self
|
||||
.preserved_fds
|
||||
.as_ref()
|
||||
// SAFETY: FFI call with valid FDs
|
||||
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VmConfig {
|
||||
fn drop(&mut self) {
|
||||
if let Some(mut fds) = self.preserved_fds.take() {
|
||||
for fd in fds.drain(..) {
|
||||
// SAFETY: FFI call with valid FDs
|
||||
unsafe { libc::close(fd) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{fs::File, os::fd::AsRawFd};
|
||||
|
||||
use super::*;
|
||||
use net_util::MacAddr;
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
#[test]
|
||||
fn test_cpu_parsing() -> Result<()> {
|
||||
@@ -2361,10 +2389,6 @@ mod tests {
|
||||
NetConfig {
|
||||
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
||||
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
@@ -2375,9 +2399,6 @@ mod tests {
|
||||
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
||||
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
||||
id: Some("mynet0".to_owned()),
|
||||
fds: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
@@ -2392,9 +2413,6 @@ mod tests {
|
||||
tap: Some("tap0".to_owned()),
|
||||
ip: "192.168.100.1".parse().unwrap(),
|
||||
mask: "255.255.255.128".parse().unwrap(),
|
||||
fds: None,
|
||||
id: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
@@ -2408,9 +2426,6 @@ mod tests {
|
||||
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
||||
vhost_user: true,
|
||||
vhost_socket: Some("/tmp/sock".to_owned()),
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
@@ -2423,31 +2438,18 @@ mod tests {
|
||||
num_queues: 4,
|
||||
queue_size: 1024,
|
||||
iommu: true,
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
// SAFETY: Safe as the file was just opened
|
||||
let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
|
||||
// SAFETY: Safe as the file was just opened
|
||||
let fd2 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
|
||||
|
||||
assert_eq!(
|
||||
&format!(
|
||||
"{:?}",
|
||||
NetConfig::parse(&format!(
|
||||
"mac=de:ad:be:ef:12:34,fd=[{fd1},{fd2}],num_queues=4"
|
||||
))?
|
||||
),
|
||||
&format!("NetConfig {{ tap: None, ip: 192.168.249.1, mask: 255.255.255.0, \
|
||||
mac: MacAddr {{ bytes: [222, 173, 190, 239, 18, 52] }}, host_mac: None, mtu: None, \
|
||||
iommu: false, num_queues: 4, queue_size: 256, vhost_user: false, vhost_socket: None, \
|
||||
vhost_mode: Client, id: None, fds: Some([{fd1}, {fd2}]), \
|
||||
rate_limiter_config: None, pci_segment: 0, offload_tso: true, offload_ufo: true, offload_csum: true }}")
|
||||
NetConfig::parse("mac=de:ad:be:ef:12:34,fd=[3,7],num_queues=4")?,
|
||||
NetConfig {
|
||||
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
||||
fds: Some(vec![3, 7]),
|
||||
num_queues: 4,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -2774,6 +2776,7 @@ mod tests {
|
||||
gdb: false,
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
};
|
||||
|
||||
assert!(valid_config.validate().is_ok());
|
||||
@@ -2868,10 +2871,6 @@ mod tests {
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.net = Some(vec![NetConfig {
|
||||
vhost_user: true,
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}]);
|
||||
assert_eq!(
|
||||
@@ -2883,9 +2882,6 @@ mod tests {
|
||||
still_valid_config.net = Some(vec![NetConfig {
|
||||
vhost_user: true,
|
||||
vhost_socket: Some("/path/to/sock".to_owned()),
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
..Default::default()
|
||||
}]);
|
||||
still_valid_config.memory.shared = true;
|
||||
@@ -2894,9 +2890,6 @@ mod tests {
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.net = Some(vec![NetConfig {
|
||||
fds: Some(vec![0]),
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}]);
|
||||
assert_eq!(
|
||||
@@ -2907,10 +2900,6 @@ mod tests {
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.net = Some(vec![NetConfig {
|
||||
offload_csum: false,
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}]);
|
||||
assert_eq!(
|
||||
@@ -2958,24 +2947,26 @@ mod tests {
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
..Default::default()
|
||||
});
|
||||
assert!(still_valid_config.validate().is_ok());
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 17,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS + 1,
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(
|
||||
invalid_config.validate(),
|
||||
Err(ValidationError::InvalidNumPciSegments(17))
|
||||
Err(ValidationError::InvalidNumPciSegments(
|
||||
MAX_NUM_PCI_SEGMENTS + 1
|
||||
))
|
||||
);
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -2983,18 +2974,18 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
iommu_segments: Some(vec![17, 18]),
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![MAX_NUM_PCI_SEGMENTS + 1, MAX_NUM_PCI_SEGMENTS + 2]),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(
|
||||
invalid_config.validate(),
|
||||
Err(ValidationError::InvalidPciSegment(17))
|
||||
Err(ValidationError::InvalidPciSegment(MAX_NUM_PCI_SEGMENTS + 1))
|
||||
);
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3007,24 +2998,20 @@ mod tests {
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
still_valid_config.net = Some(vec![NetConfig {
|
||||
iommu: true,
|
||||
pci_segment: 1,
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}]);
|
||||
assert!(still_valid_config.validate().is_ok());
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3037,7 +3024,7 @@ mod tests {
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3050,7 +3037,7 @@ mod tests {
|
||||
|
||||
let mut still_valid_config = valid_config.clone();
|
||||
still_valid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3063,7 +3050,7 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3079,17 +3066,13 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
invalid_config.net = Some(vec![NetConfig {
|
||||
iommu: false,
|
||||
pci_segment: 1,
|
||||
fds: None,
|
||||
id: None,
|
||||
tap: None,
|
||||
vhost_socket: None,
|
||||
..Default::default()
|
||||
}]);
|
||||
assert_eq!(
|
||||
@@ -3099,7 +3082,7 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3115,7 +3098,7 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3131,7 +3114,7 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3148,7 +3131,7 @@ mod tests {
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.memory.shared = true;
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3163,7 +3146,7 @@ mod tests {
|
||||
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3179,7 +3162,7 @@ mod tests {
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.memory.shared = true;
|
||||
invalid_config.platform = Some(PlatformConfig {
|
||||
num_pci_segments: 16,
|
||||
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
|
||||
iommu_segments: Some(vec![1, 2, 3]),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -3205,7 +3188,7 @@ mod tests {
|
||||
]);
|
||||
assert!(still_valid_config.validate().is_ok());
|
||||
|
||||
let mut invalid_config = valid_config;
|
||||
let mut invalid_config = valid_config.clone();
|
||||
invalid_config.devices = Some(vec![
|
||||
DeviceConfig {
|
||||
path: "/device1".into(),
|
||||
@@ -3217,5 +3200,16 @@ mod tests {
|
||||
},
|
||||
]);
|
||||
assert!(invalid_config.validate().is_err());
|
||||
|
||||
let mut still_valid_config = valid_config;
|
||||
// SAFETY: Safe as the file was just opened
|
||||
let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
|
||||
// SAFETY: Safe as the file was just opened
|
||||
let fd2 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) };
|
||||
// SAFETY: safe as both FDs are valid
|
||||
unsafe {
|
||||
still_valid_config.add_preserved_fds(vec![fd1, fd2]);
|
||||
}
|
||||
let _still_valid_config = still_valid_config.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ use hypervisor::arch::x86::MsrEntry;
|
||||
use hypervisor::arch::x86::{SpecialRegisters, StandardRegisters};
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use hypervisor::kvm::kvm_bindings;
|
||||
#[cfg(all(target_arch = "aarch64", feature = "kvm"))]
|
||||
use hypervisor::kvm::kvm_ioctls::Cap;
|
||||
#[cfg(feature = "tdx")]
|
||||
use hypervisor::kvm::{TdxExitDetails, TdxExitStatus};
|
||||
use hypervisor::{CpuState, HypervisorCpuError, HypervisorType, VmExit, VmOps};
|
||||
@@ -82,6 +84,7 @@ use vm_migration::{
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN};
|
||||
use zerocopy::AsBytes;
|
||||
|
||||
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
|
||||
/// Extract the specified bits of a 64-bit integer.
|
||||
@@ -95,6 +98,13 @@ 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)]
|
||||
@@ -165,12 +175,16 @@ pub enum Error {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[error("Error setting up AMX: {0}")]
|
||||
AmxEnable(#[source] anyhow::Error),
|
||||
|
||||
#[error("Maximum number of vCPUs exceeds host limit")]
|
||||
MaximumVcpusExceeded,
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes)]
|
||||
struct LocalApic {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -181,7 +195,7 @@ struct LocalApic {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct Ioapic {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -194,6 +208,7 @@ struct Ioapic {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes)]
|
||||
struct GicC {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -218,6 +233,7 @@ struct GicC {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes)]
|
||||
struct GicD {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -232,6 +248,7 @@ struct GicD {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes)]
|
||||
struct GicR {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -243,6 +260,7 @@ struct GicR {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes)]
|
||||
struct GicIts {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -255,6 +273,7 @@ struct GicIts {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes)]
|
||||
struct ProcessorHierarchyNode {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -267,7 +286,7 @@ struct ProcessorHierarchyNode {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, AsBytes)]
|
||||
struct InterruptSourceOverride {
|
||||
pub r#type: u8,
|
||||
pub length: u8,
|
||||
@@ -370,7 +389,14 @@ impl Vcpu {
|
||||
.map_err(Error::VcpuArmPreferredTarget)?;
|
||||
// We already checked that the capability is supported.
|
||||
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2;
|
||||
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3;
|
||||
if vm
|
||||
.as_any()
|
||||
.downcast_ref::<hypervisor::kvm::KvmVm>()
|
||||
.unwrap()
|
||||
.check_extension(Cap::ArmPmuV3)
|
||||
{
|
||||
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3;
|
||||
}
|
||||
// Non-boot cpus are powered off initially.
|
||||
if self.id > 0 {
|
||||
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF;
|
||||
@@ -580,6 +606,10 @@ impl CpuManager {
|
||||
#[cfg(feature = "tdx")] tdx_enabled: bool,
|
||||
numa_nodes: &NumaNodes,
|
||||
) -> Result<Arc<Mutex<CpuManager>>> {
|
||||
if u32::from(config.max_vcpus) > hypervisor.get_max_vcpus() {
|
||||
return Err(Error::MaximumVcpusExceeded);
|
||||
}
|
||||
|
||||
let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus));
|
||||
vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default);
|
||||
let hypervisor_type = hypervisor.hypervisor_type();
|
||||
@@ -680,14 +710,23 @@ impl CpuManager {
|
||||
.sgx_epc_region()
|
||||
.as_ref()
|
||||
.map(|sgx_epc_region| sgx_epc_region.epc_sections().values().cloned().collect());
|
||||
|
||||
let topology = self.config.topology.clone().map_or_else(
|
||||
|| {
|
||||
#[cfg(feature = "mshv")]
|
||||
if matches!(hypervisor.hypervisor_type(), HypervisorType::Mshv) {
|
||||
return Some((1, self.boot_vcpus(), 1));
|
||||
}
|
||||
None
|
||||
},
|
||||
|t| Some((t.threads_per_core, t.cores_per_die, t.dies_per_package)),
|
||||
);
|
||||
|
||||
self.cpuid = {
|
||||
let phys_bits = physical_bits(self.config.max_phys_bits);
|
||||
let phys_bits = physical_bits(hypervisor, self.config.max_phys_bits);
|
||||
arch::generate_common_cpuid(
|
||||
hypervisor,
|
||||
self.config
|
||||
.topology
|
||||
.clone()
|
||||
.map(|t| (t.threads_per_core, t.cores_per_die, t.dies_per_package)),
|
||||
topology,
|
||||
sgx_epc_sections,
|
||||
phys_bits,
|
||||
self.config.kvm_hyperv,
|
||||
@@ -1099,7 +1138,7 @@ impl CpuManager {
|
||||
|
||||
fn remove_vcpu(&mut self, cpu_id: u8) -> Result<()> {
|
||||
info!("Removing vCPU: cpu_id = {}", cpu_id);
|
||||
let mut state = &mut self.vcpu_states[usize::from(cpu_id)];
|
||||
let state = &mut self.vcpu_states[usize::from(cpu_id)];
|
||||
state.kill.store(true, Ordering::SeqCst);
|
||||
state.signal_thread();
|
||||
state.join_thread()?;
|
||||
@@ -1251,7 +1290,7 @@ impl CpuManager {
|
||||
let mut madt = Sdt::new(*b"APIC", 44, 5, *b"CLOUDH", *b"CHMADT ", 1);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
madt.write(36, arch::layout::APIC_START);
|
||||
madt.write(36, arch::layout::APIC_START.0);
|
||||
|
||||
for cpu in 0..self.config.max_vcpus {
|
||||
let lapic = LocalApic {
|
||||
@@ -1579,8 +1618,7 @@ impl CpuManager {
|
||||
|
||||
// PA or IPA size is determined
|
||||
let tcr_ips = extract_bits_64!(tcr_el1, 32, 3);
|
||||
#[allow(clippy::identity_op)]
|
||||
let pa_range = extract_bits_64!(id_aa64mmfr0_el1, 0, 4);
|
||||
let pa_range = extract_bits_64_without_offset!(id_aa64mmfr0_el1, 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);
|
||||
@@ -1613,8 +1651,7 @@ impl CpuManager {
|
||||
let descaddrmask = descaddrmask & !indexmask_grainsize;
|
||||
|
||||
// Translation table base address
|
||||
#[allow(clippy::identity_op)]
|
||||
let mut descaddr: u64 = extract_bits_64!(ttbr1_el1, 0, 48);
|
||||
let mut descaddr: u64 = extract_bits_64_without_offset!(ttbr1_el1, 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 {
|
||||
@@ -1955,6 +1992,7 @@ 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
|
||||
|
||||
@@ -831,6 +831,9 @@ pub struct DeviceManager {
|
||||
// pty foreground status,
|
||||
console_resize_pipe: Option<Arc<File>>,
|
||||
|
||||
// To restore on exit.
|
||||
original_termios_opt: Arc<Mutex<Option<termios>>>,
|
||||
|
||||
// Interrupt controller
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
interrupt_controller: Option<Arc<Mutex<ioapic::Ioapic>>>,
|
||||
@@ -1115,6 +1118,7 @@ impl DeviceManager {
|
||||
serial_manager: None,
|
||||
console_pty: None,
|
||||
console_resize_pipe: None,
|
||||
original_termios_opt: Arc::new(Mutex::new(None)),
|
||||
virtio_mem_devices: Vec::new(),
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
gpio_device: None,
|
||||
@@ -1162,6 +1166,7 @@ impl DeviceManager {
|
||||
serial_pty: Option<PtyPair>,
|
||||
console_pty: Option<PtyPair>,
|
||||
console_resize_pipe: Option<File>,
|
||||
original_termios_opt: Arc<Mutex<Option<termios>>>,
|
||||
) -> DeviceManagerResult<()> {
|
||||
trace_scoped!("create_devices");
|
||||
|
||||
@@ -1217,6 +1222,8 @@ impl DeviceManager {
|
||||
)?;
|
||||
}
|
||||
|
||||
self.original_termios_opt = original_termios_opt;
|
||||
|
||||
self.console = self.add_console_device(
|
||||
&legacy_interrupt_manager,
|
||||
&mut virtio_devices,
|
||||
@@ -1836,7 +1843,7 @@ impl DeviceManager {
|
||||
}
|
||||
|
||||
fn modify_mode<F: FnOnce(&mut termios)>(
|
||||
&self,
|
||||
&mut self,
|
||||
fd: RawFd,
|
||||
f: F,
|
||||
) -> vmm_sys_util::errno::Result<()> {
|
||||
@@ -1853,6 +1860,10 @@ impl DeviceManager {
|
||||
if ret < 0 {
|
||||
return vmm_sys_util::errno::errno_result();
|
||||
}
|
||||
let mut original_termios_opt = self.original_termios_opt.lock().unwrap();
|
||||
if original_termios_opt.is_none() {
|
||||
*original_termios_opt = Some(termios);
|
||||
}
|
||||
f(&mut termios);
|
||||
// SAFETY: Safe because the syscall will only read the extent of termios and we check
|
||||
// the return result.
|
||||
@@ -1864,7 +1875,7 @@ impl DeviceManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_raw_mode(&self, f: &mut File) -> vmm_sys_util::errno::Result<()> {
|
||||
fn set_raw_mode(&mut self, f: &mut dyn AsRawFd) -> vmm_sys_util::errno::Result<()> {
|
||||
// SAFETY: FFI call. Variable t is guaranteed to be a valid termios from modify_mode.
|
||||
self.modify_mode(f.as_raw_fd(), |t| unsafe { cfmakeraw(t) })
|
||||
}
|
||||
@@ -1926,7 +1937,10 @@ impl DeviceManager {
|
||||
return vmm_sys_util::errno::errno_result().map_err(DeviceManagerError::DupFd);
|
||||
}
|
||||
// SAFETY: stdout is valid and owned solely by us.
|
||||
let stdout = unsafe { File::from_raw_fd(stdout) };
|
||||
let mut stdout = unsafe { File::from_raw_fd(stdout) };
|
||||
|
||||
// Make sure stdout is in raw mode, if it's a terminal.
|
||||
let _ = self.set_raw_mode(&mut stdout);
|
||||
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
if unsafe { libc::isatty(libc::STDOUT_FILENO) } == 1 {
|
||||
@@ -2025,7 +2039,11 @@ impl DeviceManager {
|
||||
}
|
||||
None
|
||||
}
|
||||
ConsoleOutputMode::Tty => Some(Box::new(stdout())),
|
||||
ConsoleOutputMode::Tty => {
|
||||
let mut out = stdout();
|
||||
let _ = self.set_raw_mode(&mut out);
|
||||
Some(Box::new(out))
|
||||
}
|
||||
ConsoleOutputMode::Off | ConsoleOutputMode::Null => None,
|
||||
};
|
||||
if serial_config.mode != ConsoleOutputMode::Off {
|
||||
@@ -2388,26 +2406,31 @@ impl DeviceManager {
|
||||
.map_err(DeviceManagerError::CreateVirtioNet)?,
|
||||
))
|
||||
} else if let Some(fds) = &net_cfg.fds {
|
||||
Arc::new(Mutex::new(
|
||||
virtio_devices::Net::from_tap_fds(
|
||||
id.clone(),
|
||||
fds,
|
||||
Some(net_cfg.mac),
|
||||
net_cfg.mtu,
|
||||
self.force_iommu | net_cfg.iommu,
|
||||
net_cfg.queue_size,
|
||||
self.seccomp_action.clone(),
|
||||
net_cfg.rate_limiter_config,
|
||||
self.exit_evt
|
||||
.try_clone()
|
||||
.map_err(DeviceManagerError::EventFd)?,
|
||||
state,
|
||||
net_cfg.offload_tso,
|
||||
net_cfg.offload_ufo,
|
||||
net_cfg.offload_csum,
|
||||
)
|
||||
.map_err(DeviceManagerError::CreateVirtioNet)?,
|
||||
))
|
||||
let net = virtio_devices::Net::from_tap_fds(
|
||||
id.clone(),
|
||||
fds,
|
||||
Some(net_cfg.mac),
|
||||
net_cfg.mtu,
|
||||
self.force_iommu | net_cfg.iommu,
|
||||
net_cfg.queue_size,
|
||||
self.seccomp_action.clone(),
|
||||
net_cfg.rate_limiter_config,
|
||||
self.exit_evt
|
||||
.try_clone()
|
||||
.map_err(DeviceManagerError::EventFd)?,
|
||||
state,
|
||||
net_cfg.offload_tso,
|
||||
net_cfg.offload_ufo,
|
||||
net_cfg.offload_csum,
|
||||
)
|
||||
.map_err(DeviceManagerError::CreateVirtioNet)?;
|
||||
|
||||
// SAFETY: 'fds' are valid because TAP devices are created successfully
|
||||
unsafe {
|
||||
self.config.lock().unwrap().add_preserved_fds(fds.clone());
|
||||
}
|
||||
|
||||
Arc::new(Mutex::new(net))
|
||||
} else {
|
||||
Arc::new(Mutex::new(
|
||||
virtio_devices::Net::new(
|
||||
@@ -4255,7 +4278,7 @@ impl Aml for DeviceManager {
|
||||
let mut pci_scan_methods = Vec::new();
|
||||
for i in 0..self.pci_segments.len() {
|
||||
pci_scan_methods.push(aml::MethodCall::new(
|
||||
format!("\\_SB_.PCI{i:X}.PCNT").as_str().into(),
|
||||
format!("\\_SB_.PC{i:02X}.PCNT").as_str().into(),
|
||||
vec![],
|
||||
));
|
||||
}
|
||||
@@ -4279,6 +4302,7 @@ 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
|
||||
@@ -4631,5 +4655,10 @@ impl Drop for DeviceManager {
|
||||
for handle in self.virtio_devices.drain(..) {
|
||||
handle.virtio_device.lock().unwrap().shutdown();
|
||||
}
|
||||
|
||||
if let Some(termios) = *self.original_termios_opt.lock().unwrap() {
|
||||
// SAFETY: FFI call
|
||||
let _ = unsafe { tcsetattr(stdout().lock().as_raw_fd(), TCSANOW, &termios) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,8 @@ pub struct BftIter<'a> {
|
||||
|
||||
impl<'a> BftIter<'a> {
|
||||
fn new(hash_map: &'a HashMap<String, DeviceNode>) -> Self {
|
||||
let mut nodes = Vec::new();
|
||||
let mut nodes = Vec::with_capacity(hash_map.len());
|
||||
let mut i = 0;
|
||||
|
||||
for (_, node) in hash_map.iter() {
|
||||
if node.parent.is_none() {
|
||||
@@ -117,26 +118,13 @@ impl<'a> BftIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut node_layer = nodes.as_slice();
|
||||
loop {
|
||||
let mut next_node_layer = Vec::new();
|
||||
|
||||
for node in node_layer.iter() {
|
||||
for child_node_id in node.children.iter() {
|
||||
if let Some(child_node) = hash_map.get(child_node_id) {
|
||||
next_node_layer.push(child_node);
|
||||
}
|
||||
while i < nodes.len() {
|
||||
for child_node_id in nodes[i].children.iter() {
|
||||
if let Some(child_node) = hash_map.get(child_node_id) {
|
||||
nodes.push(child_node);
|
||||
}
|
||||
}
|
||||
|
||||
if next_node_layer.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let pos = nodes.len();
|
||||
nodes.extend(next_node_layer);
|
||||
|
||||
node_layer = &nodes[pos..];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
BftIter { nodes }
|
||||
|
||||
@@ -435,7 +435,6 @@ impl run_blocking::BlockingEventLoop for GdbEventLoop {
|
||||
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
|
||||
type StopReason = MultiThreadStopReason<ArchUsize>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn wait_for_stop_reason(
|
||||
target: &mut Self::Target,
|
||||
conn: &mut Self::Connection,
|
||||
|
||||
143
vmm/src/lib.rs
143
vmm/src/lib.rs
@@ -25,7 +25,9 @@ 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;
|
||||
use libc::{EFD_NONBLOCK, SIGINT, SIGTERM};
|
||||
#[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;
|
||||
use seccompiler::{apply_filter, SeccompAction};
|
||||
@@ -35,7 +37,7 @@ use signal_hook::iterator::{Handle, Signals};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::{stdout, Read, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::os::unix::net::UnixStream;
|
||||
@@ -53,7 +55,6 @@ use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable, Transport
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::signal::unblock_signal;
|
||||
use vmm_sys_util::sock_ctrl_msg::ScmSocket;
|
||||
use vmm_sys_util::terminal::Terminal;
|
||||
|
||||
mod acpi;
|
||||
pub mod api;
|
||||
@@ -81,7 +82,6 @@ type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||
|
||||
/// Errors associated with VMM management
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Error {
|
||||
/// API request receive error
|
||||
#[error("Error receiving API request: {0}")]
|
||||
@@ -115,6 +115,16 @@ 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),
|
||||
@@ -281,18 +291,20 @@ impl Serialize for PciDeviceInfo {
|
||||
#[allow(unused_variables)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn start_vmm_thread(
|
||||
vmm_version: String,
|
||||
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>,
|
||||
#[cfg(feature = "guest_debug")] debug_path: Option<PathBuf>,
|
||||
#[cfg(feature = "guest_debug")] debug_event: EventFd,
|
||||
#[cfg(feature = "guest_debug")] vm_debug_event: EventFd,
|
||||
exit_event: EventFd,
|
||||
seccomp_action: &SeccompAction,
|
||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
||||
) -> Result<VmmThreadHandle> {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
|
||||
#[cfg(feature = "guest_debug")]
|
||||
@@ -302,7 +314,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 http_api_event = api_event.try_clone().map_err(Error::EventFdClone)?;
|
||||
let api_event_clone = api_event.try_clone().map_err(Error::EventFdClone)?;
|
||||
let hypervisor_type = hypervisor.hypervisor_type();
|
||||
|
||||
// Retrieve seccomp filter
|
||||
@@ -310,9 +322,8 @@ pub fn start_vmm_thread(
|
||||
.map_err(Error::CreateSeccompFilter)?;
|
||||
|
||||
let vmm_seccomp_action = seccomp_action.clone();
|
||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
|
||||
let thread = {
|
||||
let exit_evt = exit_evt.try_clone().map_err(Error::EventFdClone)?;
|
||||
let exit_event = exit_event.try_clone().map_err(Error::EventFdClone)?;
|
||||
thread::Builder::new()
|
||||
.name("vmm".to_string())
|
||||
.spawn(move || {
|
||||
@@ -322,7 +333,7 @@ pub fn start_vmm_thread(
|
||||
}
|
||||
|
||||
let mut vmm = Vmm::new(
|
||||
vmm_version.to_string(),
|
||||
vmm_version,
|
||||
api_event,
|
||||
#[cfg(feature = "guest_debug")]
|
||||
debug_event,
|
||||
@@ -330,7 +341,7 @@ pub fn start_vmm_thread(
|
||||
vm_debug_event,
|
||||
vmm_seccomp_action,
|
||||
hypervisor,
|
||||
exit_evt,
|
||||
exit_event,
|
||||
)?;
|
||||
|
||||
vmm.setup_signal_handler()?;
|
||||
@@ -344,23 +355,40 @@ pub fn start_vmm_thread(
|
||||
.map_err(Error::VmmThreadSpawn)?
|
||||
};
|
||||
|
||||
// The VMM thread is started, we can start serving HTTP requests
|
||||
// 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,
|
||||
};
|
||||
|
||||
if let Some(http_path) = http_path {
|
||||
api::start_http_path_thread(
|
||||
http_path,
|
||||
http_api_event,
|
||||
api_event_clone,
|
||||
api_sender,
|
||||
seccomp_action,
|
||||
exit_evt,
|
||||
exit_event,
|
||||
hypervisor_type,
|
||||
)?;
|
||||
} else if let Some(http_fd) = http_fd {
|
||||
api::start_http_fd_thread(
|
||||
http_fd,
|
||||
http_api_event,
|
||||
api_event_clone,
|
||||
api_sender,
|
||||
seccomp_action,
|
||||
exit_evt,
|
||||
exit_event,
|
||||
hypervisor_type,
|
||||
)?;
|
||||
}
|
||||
@@ -379,7 +407,11 @@ pub fn start_vmm_thread(
|
||||
.map_err(Error::GdbThreadSpawn)?;
|
||||
}
|
||||
|
||||
Ok(thread)
|
||||
Ok(VmmThreadHandle {
|
||||
thread_handle: thread,
|
||||
#[cfg(feature = "dbus_api")]
|
||||
dbus_shutdown_chs,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
@@ -390,6 +422,27 @@ struct VmMigrationConfig {
|
||||
memory_manager_data: MemoryManagerSnapshotData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VmmVersionInfo {
|
||||
pub build_version: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl VmmVersionInfo {
|
||||
pub fn new(build_version: &str, version: &str) -> Self {
|
||||
Self {
|
||||
build_version: build_version.to_owned(),
|
||||
version: version.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -399,7 +452,7 @@ pub struct Vmm {
|
||||
debug_evt: EventFd,
|
||||
#[cfg(feature = "guest_debug")]
|
||||
vm_debug_evt: EventFd,
|
||||
version: String,
|
||||
version: VmmVersionInfo,
|
||||
vm: Option<Vm>,
|
||||
vm_config: Option<Arc<Mutex<VmConfig>>>,
|
||||
seccomp_action: SeccompAction,
|
||||
@@ -407,12 +460,17 @@ pub struct Vmm {
|
||||
activate_evt: EventFd,
|
||||
signals: Option<Handle>,
|
||||
threads: Vec<thread::JoinHandle<()>>,
|
||||
original_termios_opt: Arc<Mutex<Option<termios>>>,
|
||||
}
|
||||
|
||||
impl Vmm {
|
||||
pub const HANDLED_SIGNALS: [i32; 2] = [SIGTERM, SIGINT];
|
||||
|
||||
fn signal_handler(mut signals: Signals, on_tty: bool, exit_evt: &EventFd) {
|
||||
fn signal_handler(
|
||||
mut signals: Signals,
|
||||
original_termios_opt: Arc<Mutex<Option<termios>>>,
|
||||
exit_evt: &EventFd,
|
||||
) {
|
||||
for sig in &Self::HANDLED_SIGNALS {
|
||||
unblock_signal(*sig).unwrap();
|
||||
}
|
||||
@@ -422,12 +480,17 @@ impl Vmm {
|
||||
SIGTERM | SIGINT => {
|
||||
if exit_evt.write(1).is_err() {
|
||||
// Resetting the terminal is usually done as the VMM exits
|
||||
if on_tty {
|
||||
io::stdin()
|
||||
.lock()
|
||||
.set_canon_mode()
|
||||
.expect("failed to restore terminal mode");
|
||||
if let Ok(lock) = original_termios_opt.lock() {
|
||||
if let Some(termios) = *lock {
|
||||
// SAFETY: FFI call
|
||||
let _ = unsafe {
|
||||
tcsetattr(stdout().lock().as_raw_fd(), TCSANOW, &termios)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
warn!("Failed to lock original termios");
|
||||
}
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -442,8 +505,7 @@ impl Vmm {
|
||||
Ok(signals) => {
|
||||
self.signals = Some(signals.handle());
|
||||
let exit_evt = self.exit_evt.try_clone().map_err(Error::EventFdClone)?;
|
||||
// SAFETY: trivially safe
|
||||
let on_tty = unsafe { libc::isatty(libc::STDIN_FILENO) } != 0;
|
||||
let original_termios_opt = Arc::clone(&self.original_termios_opt);
|
||||
|
||||
let signal_handler_seccomp_filter = get_seccomp_filter(
|
||||
&self.seccomp_action,
|
||||
@@ -465,7 +527,7 @@ impl Vmm {
|
||||
}
|
||||
}
|
||||
std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
Vmm::signal_handler(signals, on_tty, &exit_evt);
|
||||
Vmm::signal_handler(signals, original_termios_opt, &exit_evt);
|
||||
}))
|
||||
.map_err(|_| {
|
||||
error!("vmm signal_handler thread panicked");
|
||||
@@ -482,7 +544,7 @@ impl Vmm {
|
||||
}
|
||||
|
||||
fn new(
|
||||
vmm_version: String,
|
||||
vmm_version: VmmVersionInfo,
|
||||
api_evt: EventFd,
|
||||
#[cfg(feature = "guest_debug")] debug_evt: EventFd,
|
||||
#[cfg(feature = "guest_debug")] vm_debug_evt: EventFd,
|
||||
@@ -532,6 +594,7 @@ impl Vmm {
|
||||
activate_evt,
|
||||
signals: None,
|
||||
threads: vec![],
|
||||
original_termios_opt: Arc::new(Mutex::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -582,6 +645,7 @@ impl Vmm {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Arc::clone(&self.original_termios_opt),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -680,6 +744,7 @@ impl Vmm {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Arc::clone(&self.original_termios_opt),
|
||||
Some(snapshot),
|
||||
Some(source_url),
|
||||
Some(restore_cfg.prefault),
|
||||
@@ -760,6 +825,7 @@ impl Vmm {
|
||||
serial_pty,
|
||||
console_pty,
|
||||
console_resize_pipe,
|
||||
Arc::clone(&self.original_termios_opt),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -802,8 +868,15 @@ impl Vmm {
|
||||
}
|
||||
|
||||
fn vmm_ping(&self) -> VmmPingResponse {
|
||||
let VmmVersionInfo {
|
||||
build_version,
|
||||
version,
|
||||
} = self.version.clone();
|
||||
|
||||
VmmPingResponse {
|
||||
version: self.version.clone(),
|
||||
build_version,
|
||||
version,
|
||||
pid: std::process::id() as i64,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1186,7 +1259,8 @@ impl Vmm {
|
||||
))
|
||||
})?;
|
||||
|
||||
let phys_bits = vm::physical_bits(config.lock().unwrap().cpus.max_phys_bits);
|
||||
let phys_bits =
|
||||
vm::physical_bits(&self.hypervisor, config.lock().unwrap().cpus.max_phys_bits);
|
||||
|
||||
let memory_manager = MemoryManager::new(
|
||||
vm,
|
||||
@@ -1262,6 +1336,7 @@ impl Vmm {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Arc::clone(&self.original_termios_opt),
|
||||
Some(snapshot),
|
||||
)
|
||||
.map_err(|e| {
|
||||
@@ -1501,7 +1576,8 @@ 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(vm_config.lock().unwrap().cpus.max_phys_bits);
|
||||
let phys_bits =
|
||||
vm::physical_bits(&hypervisor, vm_config.lock().unwrap().cpus.max_phys_bits);
|
||||
arch::generate_common_cpuid(
|
||||
&hypervisor,
|
||||
None,
|
||||
@@ -1691,7 +1767,7 @@ impl Vmm {
|
||||
let dest_cpuid = &{
|
||||
let vm_config = &src_vm_config.lock().unwrap();
|
||||
|
||||
let phys_bits = vm::physical_bits(vm_config.cpus.max_phys_bits);
|
||||
let phys_bits = vm::physical_bits(&self.hypervisor, vm_config.cpus.max_phys_bits);
|
||||
arch::generate_common_cpuid(
|
||||
&self.hypervisor.clone(),
|
||||
None,
|
||||
@@ -2057,7 +2133,7 @@ mod unit_tests {
|
||||
|
||||
fn create_dummy_vmm() -> Vmm {
|
||||
Vmm::new(
|
||||
"dummy".to_string(),
|
||||
VmmVersionInfo::new("dummy", "dummy"),
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
#[cfg(feature = "guest_debug")]
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
@@ -2130,6 +2206,7 @@ mod unit_tests {
|
||||
gdb: false,
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ use std::convert::TryInto;
|
||||
use std::ffi;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{self, Read};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{BitAnd, Deref, Not, Sub};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::result;
|
||||
@@ -330,6 +330,12 @@ pub enum Error {
|
||||
|
||||
/// Using a directory as a backing file for memory is not supported
|
||||
DirectoryAsBackingFileForMemory,
|
||||
|
||||
/// Failed to stat filesystem
|
||||
GetFileSystemBlockSize(io::Error),
|
||||
|
||||
/// Memory size is misaligned with default page size or its hugepage size
|
||||
MisalignedMemorySize,
|
||||
}
|
||||
|
||||
const ENABLE_FLAG: usize = 0;
|
||||
@@ -353,6 +359,77 @@ fn mmio_address_space_size(phys_bits: u8) -> u64 {
|
||||
(1 << phys_bits) - (1 << 16)
|
||||
}
|
||||
|
||||
// The `statfs` function can get information of hugetlbfs, and the hugepage size is in the
|
||||
// `f_bsize` field.
|
||||
//
|
||||
// See: https://github.com/torvalds/linux/blob/v6.3/fs/hugetlbfs/inode.c#L1169
|
||||
fn statfs_get_bsize(path: &str) -> Result<u64, Error> {
|
||||
let path = std::ffi::CString::new(path).map_err(|_| Error::InvalidMemoryParameters)?;
|
||||
let mut buf = std::mem::MaybeUninit::<libc::statfs>::uninit();
|
||||
|
||||
// SAFETY: FFI call with a valid path and buffer
|
||||
let ret = unsafe { libc::statfs(path.as_ptr(), buf.as_mut_ptr()) };
|
||||
if ret != 0 {
|
||||
return Err(Error::GetFileSystemBlockSize(
|
||||
std::io::Error::last_os_error(),
|
||||
));
|
||||
}
|
||||
|
||||
// SAFETY: `buf` is valid at this point
|
||||
// Because this value is always positive, just convert it directly.
|
||||
// Note that the `f_bsize` is `i64` in glibc and `u64` in musl, using `as u64` will be warned
|
||||
// by `clippy` on musl target. To avoid the warning, there should be `as _` instead of
|
||||
// `as u64`.
|
||||
let bsize = unsafe { (*buf.as_ptr()).f_bsize } as _;
|
||||
Ok(bsize)
|
||||
}
|
||||
|
||||
fn memory_zone_get_align_size(zone: &MemoryZoneConfig) -> Result<u64, Error> {
|
||||
// SAFETY: FFI call. Trivially safe.
|
||||
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as u64 };
|
||||
|
||||
// There is no backend file and the `hugepages` is disabled, just use system page size.
|
||||
if zone.file.is_none() && !zone.hugepages {
|
||||
return Ok(page_size);
|
||||
}
|
||||
|
||||
// The `hugepages` is enabled and the `hugepage_size` is specified, just use it directly.
|
||||
if zone.hugepages && zone.hugepage_size.is_some() {
|
||||
return Ok(zone.hugepage_size.unwrap());
|
||||
}
|
||||
|
||||
// There are two scenarios here:
|
||||
// - `hugepages` is enabled but `hugepage_size` is not specified:
|
||||
// Call `statfs` for `/dev/hugepages` for getting the default size of hugepage
|
||||
// - The backing file is specified:
|
||||
// Call `statfs` for the file and get its `f_bsize`. If the value is larger than the page
|
||||
// size of normal page, just use the `f_bsize` because the file is in a hugetlbfs. If the
|
||||
// value is less than or equal to the page size, just use the page size.
|
||||
let path = zone.file.as_ref().map_or(Ok("/dev/hugepages"), |pathbuf| {
|
||||
pathbuf.to_str().ok_or(Error::InvalidMemoryParameters)
|
||||
})?;
|
||||
|
||||
let align_size = std::cmp::max(page_size, statfs_get_bsize(path)?);
|
||||
|
||||
Ok(align_size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn align_down<T>(val: T, align: T) -> T
|
||||
where
|
||||
T: BitAnd<Output = T> + Not<Output = T> + Sub<Output = T> + From<u8>,
|
||||
{
|
||||
val & !(align - 1u8.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_aligned<T>(val: T, align: T) -> bool
|
||||
where
|
||||
T: BitAnd<Output = T> + Sub<Output = T> + From<u8> + PartialEq,
|
||||
{
|
||||
(val & (align - 1u8.into())) == 0u8.into()
|
||||
}
|
||||
|
||||
impl BusDevice for MemoryManager {
|
||||
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||
if self.selected_slot < self.hotplug_slots.len() {
|
||||
@@ -442,6 +519,9 @@ impl MemoryManager {
|
||||
/// - First one mapping entirely the first memory zone on 0-1G range
|
||||
/// - Second one mapping partially the second memory zone on 1G-3G range
|
||||
/// - Third one mapping partially the second memory zone on 4G-6G range
|
||||
/// Also, all memory regions are page-size aligned (e.g. their sizes must
|
||||
/// be multiple of page-size), which may leave an additional hole in the
|
||||
/// address space when hugepage is used.
|
||||
fn create_memory_regions_from_zones(
|
||||
ram_regions: &[(GuestAddress, usize)],
|
||||
zones: &[MemoryZoneConfig],
|
||||
@@ -451,9 +531,14 @@ impl MemoryManager {
|
||||
let mut zones = zones.to_owned();
|
||||
let mut mem_regions = Vec::new();
|
||||
let mut zone = zones.remove(0);
|
||||
let mut zone_offset = 0;
|
||||
let mut zone_align_size = memory_zone_get_align_size(&zone)?;
|
||||
let mut zone_offset = 0u64;
|
||||
let mut memory_zones = HashMap::new();
|
||||
|
||||
if !is_aligned(zone.size, zone_align_size) {
|
||||
return Err(Error::MisalignedMemorySize);
|
||||
}
|
||||
|
||||
// Add zone id to the list of memory zones.
|
||||
memory_zones.insert(zone.id.clone(), MemoryZone::default());
|
||||
|
||||
@@ -465,16 +550,20 @@ impl MemoryManager {
|
||||
let mut ram_region_consumed = false;
|
||||
let mut pull_next_zone = false;
|
||||
|
||||
let ram_region_sub_size = ram_region.1 - ram_region_offset;
|
||||
let zone_sub_size = zone.size as usize - zone_offset;
|
||||
let ram_region_available_size =
|
||||
align_down(ram_region.1 as u64 - ram_region_offset, zone_align_size);
|
||||
if ram_region_available_size == 0 {
|
||||
break;
|
||||
}
|
||||
let zone_sub_size = zone.size - zone_offset;
|
||||
|
||||
let file_offset = zone_offset as u64;
|
||||
let file_offset = zone_offset;
|
||||
let region_start = ram_region
|
||||
.0
|
||||
.checked_add(ram_region_offset as u64)
|
||||
.checked_add(ram_region_offset)
|
||||
.ok_or(Error::GuestAddressOverFlow)?;
|
||||
let region_size = if zone_sub_size <= ram_region_sub_size {
|
||||
if zone_sub_size == ram_region_sub_size {
|
||||
let region_size = if zone_sub_size <= ram_region_available_size {
|
||||
if zone_sub_size == ram_region_available_size {
|
||||
ram_region_consumed = true;
|
||||
}
|
||||
|
||||
@@ -483,21 +572,24 @@ impl MemoryManager {
|
||||
|
||||
zone_sub_size
|
||||
} else {
|
||||
zone_offset += ram_region_sub_size;
|
||||
zone_offset += ram_region_available_size;
|
||||
ram_region_consumed = true;
|
||||
|
||||
ram_region_sub_size
|
||||
ram_region_available_size
|
||||
};
|
||||
|
||||
info!(
|
||||
"create ram region for zone {}, region_start: {:#x}, region_size: {:#x}",
|
||||
zone.id,
|
||||
region_start.raw_value(),
|
||||
region_size
|
||||
);
|
||||
let region = MemoryManager::create_ram_region(
|
||||
&zone.file,
|
||||
file_offset,
|
||||
region_start,
|
||||
region_size,
|
||||
match prefault {
|
||||
Some(pf) => pf,
|
||||
None => zone.prefault,
|
||||
},
|
||||
region_size as usize,
|
||||
prefault.unwrap_or(zone.prefault),
|
||||
zone.shared,
|
||||
zone.hugepages,
|
||||
zone.hugepage_size,
|
||||
@@ -522,6 +614,10 @@ impl MemoryManager {
|
||||
break;
|
||||
}
|
||||
zone = zones.remove(0);
|
||||
zone_align_size = memory_zone_get_align_size(&zone)?;
|
||||
if !is_aligned(zone.size, zone_align_size) {
|
||||
return Err(Error::MisalignedMemorySize);
|
||||
}
|
||||
|
||||
// Check if zone id already exist. In case it does, throw
|
||||
// an error as we need unique identifiers. Otherwise, add
|
||||
@@ -573,10 +669,7 @@ impl MemoryManager {
|
||||
guest_ram_mapping.file_offset,
|
||||
GuestAddress(guest_ram_mapping.gpa),
|
||||
guest_ram_mapping.size as usize,
|
||||
match prefault {
|
||||
Some(pf) => pf,
|
||||
None => zone_config.prefault,
|
||||
},
|
||||
prefault.unwrap_or(zone_config.prefault),
|
||||
zone_config.shared,
|
||||
zone_config.hugepages,
|
||||
zone_config.hugepage_size,
|
||||
@@ -939,7 +1032,7 @@ impl MemoryManager {
|
||||
)
|
||||
} else {
|
||||
// Init guest memory
|
||||
let arch_mem_regions = arch::arch_memory_regions(ram_size);
|
||||
let arch_mem_regions = arch::arch_memory_regions();
|
||||
|
||||
let ram_regions: Vec<(GuestAddress, usize)> = arch_mem_regions
|
||||
.iter()
|
||||
@@ -997,10 +1090,7 @@ impl MemoryManager {
|
||||
0,
|
||||
start_addr,
|
||||
hotplug_size as usize,
|
||||
match prefault {
|
||||
Some(pf) => pf,
|
||||
None => zone.prefault,
|
||||
},
|
||||
prefault.unwrap_or(zone.prefault),
|
||||
zone.shared,
|
||||
zone.hugepages,
|
||||
zone.hugepage_size,
|
||||
@@ -1501,7 +1591,7 @@ impl MemoryManager {
|
||||
.ok_or(Error::MemoryRangeAllocation)?;
|
||||
|
||||
// Update the slot so that it can be queried via the I/O port
|
||||
let mut slot = &mut self.hotplug_slots[self.next_hotplug_slot];
|
||||
let slot = &mut self.hotplug_slots[self.next_hotplug_slot];
|
||||
slot.active = true;
|
||||
slot.inserting = true;
|
||||
slot.base = region.start_addr().0;
|
||||
@@ -2244,6 +2334,7 @@ impl Aml for MemoryMethods {
|
||||
true,
|
||||
0x0000_0000_0000_0000u64,
|
||||
0xFFFF_FFFF_FFFF_FFFEu64,
|
||||
None,
|
||||
)]),
|
||||
),
|
||||
&aml::CreateQWordField::new(
|
||||
@@ -2326,6 +2417,7 @@ 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
|
||||
@@ -2421,6 +2513,7 @@ impl Aml for MemoryManager {
|
||||
true,
|
||||
min,
|
||||
max,
|
||||
None,
|
||||
)]),
|
||||
),
|
||||
&aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0xfu8)]),
|
||||
|
||||
@@ -353,17 +353,19 @@ 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),
|
||||
&aml::AddressSpace::new_io(0u16, 0x0cf7u16, None),
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
&aml::AddressSpace::new_io(0x0d00u16, 0xffffu16),
|
||||
&aml::AddressSpace::new_io(0x0d00u16, 0xffffu16, None),
|
||||
]),
|
||||
)
|
||||
} else {
|
||||
@@ -381,6 +383,7 @@ impl Aml for PciSegment {
|
||||
true,
|
||||
self.start_of_device_area,
|
||||
self.end_of_device_area,
|
||||
None,
|
||||
),
|
||||
]),
|
||||
)
|
||||
@@ -418,7 +421,7 @@ impl Aml for PciSegment {
|
||||
pci_dsdt_inner_data.push(&prt);
|
||||
|
||||
aml::Device::new(
|
||||
format!("_SB_.PCI{:X}", self.id).as_str().into(),
|
||||
format!("_SB_.PC{:02X}", self.id).as_str().into(),
|
||||
pci_dsdt_inner_data,
|
||||
)
|
||||
.to_aml_bytes(sink)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user