mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-02-23 07:24:24 +01:00
CCv0: Merge from main -- August 1st
Conflicts: src/runtime/pkg/katautils/config.go src/runtime/virtcontainers/container.go src/runtime/virtcontainers/hypervisor.go src/runtime/virtcontainers/qemu_arch_base.go src/runtime/virtcontainers/sandbox.go tests/integration/kubernetes/gha-run.sh tests/integration/kubernetes/setup.sh tools/packaging/kata-deploy/kata-deploy/base/kata-deploy.yaml tools/packaging/kata-deploy/local-build/kata-deploy-binaries.sh tools/packaging/kata-deploy/scripts/kata-deploy.sh tools/packaging/kernel/kata_config_version versions.yaml Fixes: #7433 Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@intel.com>
This commit is contained in:
119
src/libs/Cargo.lock
generated
119
src/libs/Cargo.lock
generated
@@ -34,6 +34,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@@ -130,7 +141,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"time 0.1.43",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
@@ -172,6 +183,27 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
@@ -472,6 +504,7 @@ dependencies = [
|
||||
name = "kata-sys-util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"cgroups-rs",
|
||||
"chrono",
|
||||
@@ -559,6 +592,7 @@ dependencies = [
|
||||
"slog-async",
|
||||
"slog-json",
|
||||
"slog-scope",
|
||||
"slog-term",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
@@ -681,6 +715,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oci"
|
||||
version = "0.1.0"
|
||||
@@ -950,13 +993,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
@@ -983,6 +1037,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
@@ -1113,6 +1173,19 @@ dependencies = [
|
||||
"slog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slog-term"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87d29185c55b7b258b4f120eab00f48557d4d9bc814f41713f449d35b0f8977c"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"slog",
|
||||
"term",
|
||||
"thread_local",
|
||||
"time 0.3.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
@@ -1170,6 +1243,17 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-utils"
|
||||
version = "0.1.0"
|
||||
@@ -1216,6 +1300,35 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.17.0"
|
||||
|
||||
@@ -11,6 +11,7 @@ license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.31"
|
||||
byteorder = "1.4.3"
|
||||
cgroups = { package = "cgroups-rs", version = "0.3.2" }
|
||||
chrono = "0.4.0"
|
||||
|
||||
414
src/libs/kata-sys-util/src/cpu.rs
Normal file
414
src/libs/kata-sys-util/src/cpu.rs
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
#[cfg(target_arch = "s390x")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(target_arch = "s390x")]
|
||||
use std::io::BufRead;
|
||||
#[cfg(target_arch = "s390x")]
|
||||
use std::io::BufReader;
|
||||
|
||||
#[allow(dead_code)]
|
||||
const ERR_NO_CPUINFO: &str = "cpu_info string is empty";
|
||||
|
||||
pub const PROC_CPUINFO: &str = "/proc/cpuinfo";
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const CPUINFO_DELIMITER: &str = "\nprocessor";
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const CPUINFO_FLAGS_TAG: &str = "flags";
|
||||
|
||||
fn read_file_contents(file_path: &str) -> Result<String> {
|
||||
let contents = std::fs::read_to_string(file_path)?;
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
// get_single_cpu_info returns the contents of the first cpu from
|
||||
// the specified cpuinfo file by parsing based on a specified delimiter
|
||||
pub fn get_single_cpu_info(cpu_info_file: &str, substring: &str) -> Result<String> {
|
||||
let contents = read_file_contents(cpu_info_file)?;
|
||||
|
||||
if contents.is_empty() {
|
||||
return Err(anyhow!(ERR_NO_CPUINFO));
|
||||
}
|
||||
|
||||
let subcontents: Vec<&str> = contents.split(substring).collect();
|
||||
let result = subcontents
|
||||
.first()
|
||||
.ok_or("error splitting contents of cpuinfo")
|
||||
.map_err(|e| anyhow!(e))?
|
||||
.to_string();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// get_cpu_flags returns a string of cpu flags from cpuinfo, passed in
|
||||
// as a string
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> {
|
||||
if cpu_info.is_empty() {
|
||||
return Err(anyhow!(ERR_NO_CPUINFO));
|
||||
}
|
||||
|
||||
if cpu_flags_tag.is_empty() {
|
||||
return Err(anyhow!("cpu flags delimiter string is empty"))?;
|
||||
}
|
||||
|
||||
get_cpu_flags_from_file(cpu_info, cpu_flags_tag)
|
||||
}
|
||||
|
||||
// get a list of cpu flags in cpu_info_flags
|
||||
//
|
||||
// cpu_info is the content of cpuinfo file passed in as a string
|
||||
// returns empty Vec if no flags are found
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
pub fn get_cpu_flags_vec(cpu_info: &str, cpu_flags_tag: &str) -> Result<Vec<String>> {
|
||||
if cpu_info.is_empty() {
|
||||
return Err(anyhow!(ERR_NO_CPUINFO));
|
||||
}
|
||||
|
||||
if cpu_flags_tag.is_empty() {
|
||||
return Err(anyhow!("cpu flags delimiter string is empty"))?;
|
||||
}
|
||||
|
||||
let flags = get_cpu_flags_from_file(cpu_info, cpu_flags_tag)?;
|
||||
|
||||
// take each flag, trim whitespace, convert to String, and add to list
|
||||
// skip the first token in the iterator since it is empty
|
||||
let flags_vec: Vec<String> = flags
|
||||
.split(' ')
|
||||
.skip(1)
|
||||
.map(|f| f.trim().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
Ok(flags_vec)
|
||||
}
|
||||
|
||||
// check if the given flag exists in the given flags_vec
|
||||
//
|
||||
// flags_vec can be created by calling get_cpu_flags_vec
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
pub fn contains_cpu_flag(flags_vec: &[String], flag: &str) -> Result<bool> {
|
||||
if flag.is_empty() {
|
||||
return Err(anyhow!("parameter specifying flag to look for is empty"))?;
|
||||
}
|
||||
|
||||
Ok(flags_vec.iter().any(|f| f == flag))
|
||||
}
|
||||
|
||||
// get a String containing the cpu flags in cpu_info
|
||||
//
|
||||
// this function returns the list of flags as a single String
|
||||
// if no flags are found, returns an empty String
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
fn get_cpu_flags_from_file(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> {
|
||||
let subcontents: Vec<&str> = cpu_info.split('\n').collect();
|
||||
for line in subcontents {
|
||||
if line.starts_with(cpu_flags_tag) {
|
||||
let line_data: Vec<&str> = line.split(':').collect();
|
||||
let flags = line_data
|
||||
.last()
|
||||
.ok_or("error splitting flags in cpuinfo")
|
||||
.map_err(|e| anyhow!(e))?
|
||||
.to_string();
|
||||
return Ok(flags);
|
||||
}
|
||||
}
|
||||
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "s390x")]
|
||||
pub fn retrieve_cpu_facilities() -> Result<HashMap<i32, bool>> {
|
||||
let f = std::fs::File::open(PROC_CPUINFO)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
let mut contents = String::new();
|
||||
let facilities_field = "facilities";
|
||||
let mut facilities = HashMap::new();
|
||||
|
||||
while reader.read_line(&mut contents)? > 0 {
|
||||
let fields: Vec<&str> = contents.split_whitespace().collect();
|
||||
if fields.len() < 2 {
|
||||
contents.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if !fields[0].starts_with(facilities_field) {
|
||||
contents.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut start = 1;
|
||||
if fields[1] == ":" {
|
||||
start = 2;
|
||||
}
|
||||
|
||||
for field in fields.iter().skip(start) {
|
||||
let bit = field.parse::<i32>()?;
|
||||
facilities.insert(bit, true);
|
||||
}
|
||||
return Ok(facilities);
|
||||
}
|
||||
|
||||
Ok(facilities)
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_get_single_cpu_info() {
|
||||
// Valid cpuinfo example
|
||||
let dir = tempdir().unwrap();
|
||||
let file_path_full = dir.path().join("cpuinfo_full");
|
||||
let path_full = file_path_full.clone();
|
||||
let mut file_full = fs::File::create(file_path_full).unwrap();
|
||||
let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n".to_string();
|
||||
writeln!(file_full, "{}", contents).unwrap();
|
||||
|
||||
// Empty cpuinfo example
|
||||
let file_path_empty = dir.path().join("cpuinfo_empty");
|
||||
let path_empty = file_path_empty.clone();
|
||||
let mut _file_empty = fs::File::create(file_path_empty).unwrap();
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData<'a> {
|
||||
cpuinfo_path: &'a str,
|
||||
processor_delimiter_str: &'a str,
|
||||
result: Result<String>,
|
||||
}
|
||||
let tests = &[
|
||||
// Failure scenarios
|
||||
TestData {
|
||||
cpuinfo_path: "",
|
||||
processor_delimiter_str: "",
|
||||
result: Err(anyhow!("No such file or directory (os error 2)")),
|
||||
},
|
||||
TestData {
|
||||
cpuinfo_path: &path_empty.as_path().display().to_string(),
|
||||
processor_delimiter_str: "\nprocessor",
|
||||
result: Err(anyhow!(ERR_NO_CPUINFO)),
|
||||
},
|
||||
// Success scenarios
|
||||
TestData {
|
||||
cpuinfo_path: &path_full.as_path().display().to_string(),
|
||||
processor_delimiter_str: "\nprocessor",
|
||||
result: Ok(
|
||||
"processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4"
|
||||
.to_string(),
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
for (i, d) in tests.iter().enumerate() {
|
||||
let msg = format!("test[{}]: {:?}", i, d);
|
||||
let result = get_single_cpu_info(d.cpuinfo_path, d.processor_delimiter_str);
|
||||
let msg = format!("{}, result: {:?}", msg, result);
|
||||
|
||||
if d.result.is_ok() {
|
||||
assert_eq!(
|
||||
result.as_ref().unwrap(),
|
||||
d.result.as_ref().unwrap(),
|
||||
"{}",
|
||||
msg
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
|
||||
let actual_error = format!("{}", result.unwrap_err());
|
||||
assert!(actual_error == expected_error, "{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cpu_flags() {
|
||||
let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData<'a> {
|
||||
cpu_info_str: &'a str,
|
||||
cpu_flags_tag: &'a str,
|
||||
result: Result<String>,
|
||||
}
|
||||
let tests = &[
|
||||
// Failure scenarios
|
||||
TestData {
|
||||
cpu_info_str: "",
|
||||
cpu_flags_tag: "",
|
||||
result: Err(anyhow!(ERR_NO_CPUINFO)),
|
||||
},
|
||||
TestData {
|
||||
cpu_info_str: "",
|
||||
cpu_flags_tag: "flags",
|
||||
result: Err(anyhow!(ERR_NO_CPUINFO)),
|
||||
},
|
||||
TestData {
|
||||
cpu_info_str: contents,
|
||||
cpu_flags_tag: "",
|
||||
result: Err(anyhow!("cpu flags delimiter string is empty")),
|
||||
},
|
||||
// Success scenarios
|
||||
TestData {
|
||||
cpu_info_str: contents,
|
||||
cpu_flags_tag: "flags",
|
||||
result: Ok(" flag_1 flag_2 flag_3 flag_4".to_string()),
|
||||
},
|
||||
TestData {
|
||||
cpu_info_str: contents,
|
||||
cpu_flags_tag: "flags_err",
|
||||
result: Ok("".to_string()),
|
||||
},
|
||||
];
|
||||
|
||||
for (i, d) in tests.iter().enumerate() {
|
||||
let msg = format!("test[{}]: {:?}", i, d);
|
||||
let result = get_cpu_flags(d.cpu_info_str, d.cpu_flags_tag);
|
||||
let msg = format!("{}, result: {:?}", msg, result);
|
||||
|
||||
if d.result.is_ok() {
|
||||
assert_eq!(
|
||||
result.as_ref().unwrap(),
|
||||
d.result.as_ref().unwrap(),
|
||||
"{}",
|
||||
msg
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
|
||||
let actual_error = format!("{}", result.unwrap_err());
|
||||
assert!(actual_error == expected_error, "{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cpu_flags_vec() {
|
||||
let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData<'a> {
|
||||
cpu_info_str: &'a str,
|
||||
cpu_flags_tag: &'a str,
|
||||
result: Result<Vec<String>>,
|
||||
}
|
||||
let tests = &[
|
||||
// Failure scenarios
|
||||
TestData {
|
||||
cpu_info_str: "",
|
||||
cpu_flags_tag: "",
|
||||
result: Err(anyhow!(ERR_NO_CPUINFO)),
|
||||
},
|
||||
TestData {
|
||||
cpu_info_str: "",
|
||||
cpu_flags_tag: "flags",
|
||||
result: Err(anyhow!(ERR_NO_CPUINFO)),
|
||||
},
|
||||
TestData {
|
||||
cpu_info_str: contents,
|
||||
cpu_flags_tag: "",
|
||||
result: Err(anyhow!("cpu flags delimiter string is empty")),
|
||||
},
|
||||
// Success scenarios
|
||||
TestData {
|
||||
cpu_info_str: contents,
|
||||
cpu_flags_tag: "flags",
|
||||
result: Ok(vec![
|
||||
"flag_1".to_string(),
|
||||
"flag_2".to_string(),
|
||||
"flag_3".to_string(),
|
||||
"flag_4".to_string(),
|
||||
]),
|
||||
},
|
||||
TestData {
|
||||
cpu_info_str: contents,
|
||||
cpu_flags_tag: "flags_err",
|
||||
result: Ok(Vec::new()),
|
||||
},
|
||||
];
|
||||
|
||||
for (i, d) in tests.iter().enumerate() {
|
||||
let msg = format!("test[{}]: {:?}", i, d);
|
||||
let result = get_cpu_flags_vec(d.cpu_info_str, d.cpu_flags_tag);
|
||||
let msg = format!("{}, result: {:?}", msg, result);
|
||||
|
||||
if d.result.is_ok() {
|
||||
assert_eq!(
|
||||
result.as_ref().unwrap(),
|
||||
d.result.as_ref().unwrap(),
|
||||
"{}",
|
||||
msg
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
|
||||
let actual_error = format!("{}", result.unwrap_err());
|
||||
assert!(actual_error == expected_error, "{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_cpu_flag() {
|
||||
let flags_vec = vec![
|
||||
"flag_1".to_string(),
|
||||
"flag_2".to_string(),
|
||||
"flag_3".to_string(),
|
||||
"flag_4".to_string(),
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData<'a> {
|
||||
cpu_flags_vec: &'a Vec<String>,
|
||||
cpu_flag: &'a str,
|
||||
result: Result<bool>,
|
||||
}
|
||||
let tests = &[
|
||||
// Failure scenarios
|
||||
TestData {
|
||||
cpu_flags_vec: &flags_vec,
|
||||
cpu_flag: "flag_5",
|
||||
result: Ok(false),
|
||||
},
|
||||
TestData {
|
||||
cpu_flags_vec: &flags_vec,
|
||||
cpu_flag: "",
|
||||
result: Err(anyhow!("parameter specifying flag to look for is empty")),
|
||||
},
|
||||
// Success scenarios
|
||||
TestData {
|
||||
cpu_flags_vec: &flags_vec,
|
||||
cpu_flag: "flag_1",
|
||||
result: Ok(true),
|
||||
},
|
||||
];
|
||||
|
||||
for (i, d) in tests.iter().enumerate() {
|
||||
let msg = format!("test[{}]: {:?}", i, d);
|
||||
let result = contains_cpu_flag(d.cpu_flags_vec, d.cpu_flag);
|
||||
let msg = format!("{}, result: {:?}", msg, result);
|
||||
|
||||
if d.result.is_ok() {
|
||||
assert_eq!(
|
||||
result.as_ref().unwrap(),
|
||||
d.result.as_ref().unwrap(),
|
||||
"{}",
|
||||
msg
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_error = format!("{}", d.result.as_ref().unwrap_err());
|
||||
let actual_error = format!("{}", result.unwrap_err());
|
||||
assert!(actual_error == expected_error, "{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,22 @@
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
|
||||
pub mod cpu;
|
||||
pub mod device;
|
||||
pub mod fs;
|
||||
pub mod hooks;
|
||||
pub mod k8s;
|
||||
pub mod mount;
|
||||
pub mod numa;
|
||||
pub mod protection;
|
||||
pub mod rand;
|
||||
pub mod spec;
|
||||
pub mod validate;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
|
||||
// Convenience macro to obtain the scoped logger
|
||||
#[macro_export]
|
||||
macro_rules! sl {
|
||||
@@ -31,3 +37,41 @@ macro_rules! eother {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, format!($fmt, $($arg)*))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_kernel_cmd_line(
|
||||
kernel_cmdline_path: &str,
|
||||
search_param: &str,
|
||||
search_values: &[&str],
|
||||
) -> Result<bool> {
|
||||
let f = std::fs::File::open(kernel_cmdline_path)?;
|
||||
let reader = BufReader::new(f);
|
||||
|
||||
let check_fn = if search_values.is_empty() {
|
||||
|param: &str, search_param: &str, _search_values: &[&str]| {
|
||||
param.eq_ignore_ascii_case(search_param)
|
||||
}
|
||||
} else {
|
||||
|param: &str, search_param: &str, search_values: &[&str]| {
|
||||
let split: Vec<&str> = param.splitn(2, '=').collect();
|
||||
if split.len() < 2 || split[0] != search_param {
|
||||
return false;
|
||||
}
|
||||
|
||||
for value in search_values {
|
||||
if value.eq_ignore_ascii_case(split[1]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
for line in reader.lines() {
|
||||
for field in line?.split_whitespace() {
|
||||
if check_fn(field, search_param, search_values) {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
@@ -103,6 +103,8 @@ pub enum Error {
|
||||
MountOptionTooBig,
|
||||
#[error("Path for mountpoint is null")]
|
||||
NullMountPointPath,
|
||||
#[error("Invalid Propagation type Flag")]
|
||||
InvalidPgMountFlag,
|
||||
#[error("Faile to open file {0} by path, {1}")]
|
||||
OpenByPath(PathBuf, io::Error),
|
||||
#[error("Can not read metadata of {0}, {1}")]
|
||||
@@ -227,7 +229,13 @@ pub fn bind_remount<P: AsRef<Path>>(dst: P, readonly: bool) -> Result<()> {
|
||||
do_rebind_mount(dst, readonly, MsFlags::empty())
|
||||
}
|
||||
|
||||
/// Bind mount `src` to `dst` in slave mode, optionally in readonly mode if `readonly` is true.
|
||||
/// Bind mount `src` to `dst` with a custom propagation type, optionally in readonly mode if
|
||||
/// `readonly` is true.
|
||||
///
|
||||
/// Propagation type: MsFlags::MS_SHARED or MsFlags::MS_SLAVE
|
||||
/// MsFlags::MS_SHARED is used to bind mount the sandbox path to enable `exec` (in case of FC
|
||||
/// jailer).
|
||||
/// MsFlags::MS_SLAVE is used on all other cases.
|
||||
///
|
||||
/// # Safety
|
||||
/// Caller needs to ensure:
|
||||
@@ -238,6 +246,7 @@ pub fn bind_mount_unchecked<S: AsRef<Path>, D: AsRef<Path>>(
|
||||
src: S,
|
||||
dst: D,
|
||||
readonly: bool,
|
||||
pgflag: MsFlags,
|
||||
) -> Result<()> {
|
||||
fail::fail_point!("bind_mount", |_| {
|
||||
Err(Error::FailureInject(
|
||||
@@ -268,8 +277,11 @@ pub fn bind_mount_unchecked<S: AsRef<Path>, D: AsRef<Path>>(
|
||||
)
|
||||
.map_err(|e| Error::BindMount(abs_src, dst.to_path_buf(), e))?;
|
||||
|
||||
// Change into slave propagation mode.
|
||||
mount(Some(""), dst, Some(""), MsFlags::MS_SLAVE, Some(""))
|
||||
// Change into the chosen propagation mode.
|
||||
if !(pgflag == MsFlags::MS_SHARED || pgflag == MsFlags::MS_SLAVE) {
|
||||
return Err(Error::InvalidPgMountFlag);
|
||||
}
|
||||
mount(Some(""), dst, Some(""), pgflag, Some(""))
|
||||
.map_err(|e| Error::Mount(PathBuf::new(), dst.to_path_buf(), e))?;
|
||||
|
||||
// Optionally rebind into readonly mode.
|
||||
@@ -828,7 +840,7 @@ mod tests {
|
||||
Err(Error::InvalidPath(_))
|
||||
));
|
||||
|
||||
bind_mount_unchecked(tmpdir2.path(), tmpdir.path(), true).unwrap();
|
||||
bind_mount_unchecked(tmpdir2.path(), tmpdir.path(), true, MsFlags::MS_SLAVE).unwrap();
|
||||
bind_remount(tmpdir.path(), true).unwrap();
|
||||
umount_timeout(tmpdir.path().to_str().unwrap(), 0).unwrap();
|
||||
}
|
||||
@@ -844,25 +856,26 @@ mod tests {
|
||||
dst.push("src");
|
||||
|
||||
assert!(matches!(
|
||||
bind_mount_unchecked(Path::new(""), Path::new(""), false),
|
||||
bind_mount_unchecked(Path::new(""), Path::new(""), false, MsFlags::MS_SLAVE),
|
||||
Err(Error::NullMountPointPath)
|
||||
));
|
||||
assert!(matches!(
|
||||
bind_mount_unchecked(tmpdir2.path(), Path::new(""), false),
|
||||
bind_mount_unchecked(tmpdir2.path(), Path::new(""), false, MsFlags::MS_SLAVE),
|
||||
Err(Error::NullMountPointPath)
|
||||
));
|
||||
assert!(matches!(
|
||||
bind_mount_unchecked(
|
||||
Path::new("/_does_not_exist_/___aahhhh"),
|
||||
Path::new("/tmp/_does_not_exist/___bbb"),
|
||||
false
|
||||
false,
|
||||
MsFlags::MS_SLAVE
|
||||
),
|
||||
Err(Error::InvalidPath(_))
|
||||
));
|
||||
|
||||
let dst = create_mount_destination(tmpdir2.path(), &dst, tmpdir.path(), "bind").unwrap();
|
||||
bind_mount_unchecked(tmpdir2.path(), dst.as_ref(), true).unwrap();
|
||||
bind_mount_unchecked(&src, dst.as_ref(), false).unwrap();
|
||||
bind_mount_unchecked(tmpdir2.path(), dst.as_ref(), true, MsFlags::MS_SLAVE).unwrap();
|
||||
bind_mount_unchecked(&src, dst.as_ref(), false, MsFlags::MS_SLAVE).unwrap();
|
||||
umount_all(dst.as_ref(), false).unwrap();
|
||||
|
||||
let mut src = tmpdir.path().to_owned();
|
||||
@@ -871,7 +884,7 @@ mod tests {
|
||||
let mut dst = tmpdir.path().to_owned();
|
||||
dst.push("file");
|
||||
let dst = create_mount_destination(&src, &dst, tmpdir.path(), "bind").unwrap();
|
||||
bind_mount_unchecked(&src, dst.as_ref(), false).unwrap();
|
||||
bind_mount_unchecked(&src, dst.as_ref(), false, MsFlags::MS_SLAVE).unwrap();
|
||||
assert!(dst.as_ref().is_file());
|
||||
umount_timeout(dst.as_ref(), 0).unwrap();
|
||||
}
|
||||
|
||||
270
src/libs/kata-sys-util/src/protection.rs
Normal file
270
src/libs/kata-sys-util/src/protection.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
// Copyright (c) 2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use anyhow::anyhow;
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64"))]
|
||||
use anyhow::Result;
|
||||
use std::fmt;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))]
|
||||
use nix::unistd::Uid;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use std::fs;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum GuestProtection {
|
||||
NoProtection,
|
||||
Tdx,
|
||||
Sev,
|
||||
Snp,
|
||||
Pef,
|
||||
Se,
|
||||
}
|
||||
|
||||
impl fmt::Display for GuestProtection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GuestProtection::Tdx => write!(f, "tdx"),
|
||||
GuestProtection::Sev => write!(f, "sev"),
|
||||
GuestProtection::Snp => write!(f, "snp"),
|
||||
GuestProtection::Pef => write!(f, "pef"),
|
||||
GuestProtection::Se => write!(f, "se"),
|
||||
GuestProtection::NoProtection => write!(f, "none"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ProtectionError {
|
||||
#[error("No permission to check guest protection")]
|
||||
NoPerms,
|
||||
|
||||
#[error("Failed to check guest protection: {0}")]
|
||||
CheckFailed(String),
|
||||
|
||||
#[error("Invalid guest protection value: {0}")]
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const TDX_SYS_FIRMWARE_DIR: &str = "/sys/firmware/tdx_seam/";
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const TDX_CPU_FLAG: &str = "tdx";
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const SEV_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev";
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const SNP_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev_snp";
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn available_guest_protection() -> Result<GuestProtection, ProtectionError> {
|
||||
if !Uid::effective().is_root() {
|
||||
return Err(ProtectionError::NoPerms);
|
||||
}
|
||||
|
||||
arch_guest_protection(
|
||||
TDX_SYS_FIRMWARE_DIR,
|
||||
TDX_CPU_FLAG,
|
||||
SEV_KVM_PARAMETER_PATH,
|
||||
SNP_KVM_PARAMETER_PATH,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn retrieve_cpu_flags() -> Result<String> {
|
||||
let cpu_info =
|
||||
crate::cpu::get_single_cpu_info(crate::cpu::PROC_CPUINFO, crate::cpu::CPUINFO_DELIMITER)?;
|
||||
|
||||
let cpu_flags =
|
||||
crate::cpu::get_cpu_flags(&cpu_info, crate::cpu::CPUINFO_FLAGS_TAG).map_err(|e| {
|
||||
anyhow!(
|
||||
"Error parsing CPU flags, file {:?}, {:?}",
|
||||
crate::cpu::PROC_CPUINFO,
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(cpu_flags)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn arch_guest_protection(
|
||||
tdx_path: &str,
|
||||
tdx_flag: &str,
|
||||
sev_path: &str,
|
||||
snp_path: &str,
|
||||
) -> Result<GuestProtection, ProtectionError> {
|
||||
let flags =
|
||||
retrieve_cpu_flags().map_err(|err| ProtectionError::CheckFailed(err.to_string()))?;
|
||||
|
||||
let metadata = fs::metadata(tdx_path);
|
||||
|
||||
if metadata.is_ok() && metadata.unwrap().is_dir() && flags.contains(tdx_flag) {
|
||||
return Ok(GuestProtection::Tdx);
|
||||
}
|
||||
|
||||
let check_contents = |file_name: &str| -> Result<bool, ProtectionError> {
|
||||
let file_path = Path::new(file_name);
|
||||
if !file_path.exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let contents = fs::read_to_string(file_name).map_err(|err| {
|
||||
ProtectionError::CheckFailed(format!("Error reading file {} : {}", file_name, err))
|
||||
})?;
|
||||
|
||||
if contents.trim() == "Y" {
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
};
|
||||
|
||||
if check_contents(snp_path)? {
|
||||
return Ok(GuestProtection::Snp);
|
||||
}
|
||||
|
||||
if check_contents(sev_path)? {
|
||||
return Ok(GuestProtection::Sev);
|
||||
}
|
||||
|
||||
Ok(GuestProtection::NoProtection)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[allow(dead_code)]
|
||||
// Guest protection is not supported on ARM64.
|
||||
pub fn available_guest_protection() -> Result<GuestProtection, ProtectionError> {
|
||||
if !Uid::effective().is_root() {
|
||||
return Err(ProtectionError::NoPerms)?;
|
||||
}
|
||||
|
||||
let facilities = crate::cpu::retrieve_cpu_facilities().map_err(|err| {
|
||||
ProtectionError::CheckFailed(format!(
|
||||
"Error retrieving cpu facilities file : {}",
|
||||
err.to_string()
|
||||
))
|
||||
})?;
|
||||
|
||||
// Secure Execution
|
||||
// https://www.kernel.org/doc/html/latest/virt/kvm/s390-pv.html
|
||||
let se_cpu_facility_bit: i32 = 158;
|
||||
if !facilities.contains_key(&se_cpu_facility_bit) {
|
||||
return Ok(GuestProtection::NoProtection);
|
||||
}
|
||||
|
||||
let cmd_line_values = vec!["1", "on", "y", "yes"];
|
||||
let se_cmdline_param = "prot_virt";
|
||||
|
||||
let se_cmdline_present =
|
||||
crate::check_kernel_cmd_line("/proc/cmdline", se_cmdline_param, &cmd_line_values)
|
||||
.map_err(|err| ProtectionError::CheckFailed(err.to_string()))?;
|
||||
|
||||
if !se_cmdline_present {
|
||||
return Err(ProtectionError::InvalidValue(String::from(
|
||||
"Protected Virtualization is not enabled on kernel command line!",
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(GuestProtection::Se)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "powerpc64le")]
|
||||
pub fn available_guest_protection() -> Result<check::GuestProtection, check::ProtectionError> {
|
||||
if !Uid::effective().is_root() {
|
||||
return Err(check::ProtectionError::NoPerms);
|
||||
}
|
||||
|
||||
let metadata = fs::metadata(PEF_SYS_FIRMWARE_DIR);
|
||||
if metadata.is_ok() && metadata.unwrap().is_dir() {
|
||||
Ok(check::GuestProtection::Pef)
|
||||
}
|
||||
|
||||
Ok(check::GuestProtection::NoProtection)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[allow(dead_code)]
|
||||
// Guest protection is not supported on ARM64.
|
||||
pub fn available_guest_protection() -> Result<GuestProtection, ProtectionError> {
|
||||
Ok(GuestProtection::NoProtection)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nix::unistd::Uid;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_available_guest_protection_no_privileges() {
|
||||
if !Uid::effective().is_root() {
|
||||
let res = available_guest_protection();
|
||||
assert!(res.is_err());
|
||||
assert_eq!(
|
||||
"No permission to check guest protection",
|
||||
res.unwrap_err().to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arch_guest_protection_snp() {
|
||||
// Test snp
|
||||
let dir = tempdir().unwrap();
|
||||
let snp_file_path = dir.path().join("sev_snp");
|
||||
let path = snp_file_path.clone();
|
||||
let mut snp_file = fs::File::create(snp_file_path).unwrap();
|
||||
writeln!(snp_file, "Y").unwrap();
|
||||
|
||||
let actual =
|
||||
arch_guest_protection("/xyz/tmp", TDX_CPU_FLAG, "/xyz/tmp", path.to_str().unwrap());
|
||||
assert!(actual.is_ok());
|
||||
assert_eq!(actual.unwrap(), GuestProtection::Snp);
|
||||
|
||||
writeln!(snp_file, "N").unwrap();
|
||||
let actual =
|
||||
arch_guest_protection("/xyz/tmp", TDX_CPU_FLAG, "/xyz/tmp", path.to_str().unwrap());
|
||||
assert!(actual.is_ok());
|
||||
assert_eq!(actual.unwrap(), GuestProtection::NoProtection);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arch_guest_protection_sev() {
|
||||
// Test sev
|
||||
let dir = tempdir().unwrap();
|
||||
let sev_file_path = dir.path().join("sev");
|
||||
let sev_path = sev_file_path.clone();
|
||||
let mut sev_file = fs::File::create(sev_file_path).unwrap();
|
||||
writeln!(sev_file, "Y").unwrap();
|
||||
|
||||
let actual = arch_guest_protection(
|
||||
"/xyz/tmp",
|
||||
TDX_CPU_FLAG,
|
||||
sev_path.to_str().unwrap(),
|
||||
"/xyz/tmp",
|
||||
);
|
||||
assert!(actual.is_ok());
|
||||
assert_eq!(actual.unwrap(), GuestProtection::Sev);
|
||||
|
||||
writeln!(sev_file, "N").unwrap();
|
||||
let actual = arch_guest_protection(
|
||||
"/xyz/tmp",
|
||||
TDX_CPU_FLAG,
|
||||
sev_path.to_str().unwrap(),
|
||||
"/xyz/tmp",
|
||||
);
|
||||
assert!(actual.is_ok());
|
||||
assert_eq!(actual.unwrap(), GuestProtection::NoProtection);
|
||||
}
|
||||
}
|
||||
@@ -474,8 +474,8 @@ impl Annotation {
|
||||
let u32_err = io::Error::new(io::ErrorKind::InvalidData, "parse u32 error".to_string());
|
||||
let u64_err = io::Error::new(io::ErrorKind::InvalidData, "parse u64 error".to_string());
|
||||
let i32_err = io::Error::new(io::ErrorKind::InvalidData, "parse i32 error".to_string());
|
||||
let mut hv = config.hypervisor.get_mut(hypervisor_name).unwrap();
|
||||
let mut ag = config.agent.get_mut(agent_name).unwrap();
|
||||
let hv = config.hypervisor.get_mut(hypervisor_name).unwrap();
|
||||
let ag = config.agent.get_mut(agent_name).unwrap();
|
||||
for (key, value) in &self.annotations {
|
||||
if hv.security_info.is_annotation_enabled(key) {
|
||||
match key.as_str() {
|
||||
|
||||
@@ -32,7 +32,7 @@ pub const DEFAULT_HYPERVISOR: &str = HYPERVISOR_NAME_DRAGONBALL;
|
||||
|
||||
pub const DEFAULT_INTERNETWORKING_MODEL: &str = "tcfilter";
|
||||
|
||||
pub const DEFAULT_BLOCK_DEVICE_TYPE: &str = "virtio-blk";
|
||||
pub const DEFAULT_BLOCK_DEVICE_TYPE: &str = "virtio-blk-pci";
|
||||
pub const DEFAULT_VHOST_USER_STORE_PATH: &str = "/var/run/vhost-user";
|
||||
pub const DEFAULT_BLOCK_NVDIMM_MEM_OFFSET: u64 = 0;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ const VIRTIO_BLK_PCI: &str = "virtio-blk-pci";
|
||||
const VIRTIO_BLK_MMIO: &str = "virtio-blk-mmio";
|
||||
const VIRTIO_BLK_CCW: &str = "virtio-blk-ccw";
|
||||
const VIRTIO_SCSI: &str = "virtio-scsi";
|
||||
const VIRTIO_PMEM: &str = "nvdimm";
|
||||
const VIRTIO_PMEM: &str = "virtio-pmem";
|
||||
const VIRTIO_9P: &str = "virtio-9p";
|
||||
const VIRTIO_FS: &str = "virtio-fs";
|
||||
const VIRTIO_FS_INLINE: &str = "inline-virtio-fs";
|
||||
@@ -221,6 +221,10 @@ pub struct BootInfo {
|
||||
/// If you want that qemu uses the default firmware leave this option empty.
|
||||
#[serde(default)]
|
||||
pub firmware: String,
|
||||
/// Block storage driver to be used for the VM rootfs is backed
|
||||
/// by a block device. This is virtio-pmem, virtio-blk-pci or virtio-blk-mmio
|
||||
#[serde(default)]
|
||||
pub vm_rootfs_driver: String,
|
||||
}
|
||||
|
||||
impl BootInfo {
|
||||
@@ -230,6 +234,11 @@ impl BootInfo {
|
||||
resolve_path!(self.image, "guest boot image file {} is invalid: {}")?;
|
||||
resolve_path!(self.initrd, "guest initrd image file {} is invalid: {}")?;
|
||||
resolve_path!(self.firmware, "firmware image file {} is invalid: {}")?;
|
||||
|
||||
if self.vm_rootfs_driver.is_empty() {
|
||||
self.vm_rootfs_driver = default::DEFAULT_BLOCK_DEVICE_TYPE.to_string();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -242,6 +251,21 @@ impl BootInfo {
|
||||
if !self.image.is_empty() && !self.initrd.is_empty() {
|
||||
return Err(eother!("Can not configure both initrd and image for boot"));
|
||||
}
|
||||
|
||||
let l = [
|
||||
VIRTIO_BLK_PCI,
|
||||
VIRTIO_BLK_CCW,
|
||||
VIRTIO_BLK_MMIO,
|
||||
VIRTIO_PMEM,
|
||||
VIRTIO_SCSI,
|
||||
];
|
||||
if !l.contains(&self.vm_rootfs_driver.as_str()) {
|
||||
return Err(eother!(
|
||||
"{} is unsupported block device type.",
|
||||
self.vm_rootfs_driver
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user