mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-25 13:23:08 +00:00
6b19695c81
For now, this crate simply re-exports all of sys_util, but it will be updated to provide new interfaces when needed. This is the first step to making crosvm not directly depend on sys_util, so that we can make the interface changes we need without fear of negatively affecting (i.e. completely breaking) other usages within chromeos. BUG=b:162363783 TEST=./build_test Change-Id: I7d0aa3d8a1f66af1c7fee8fd649723ef17027150 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2325168 Tested-by: Michael Hoyle <mikehoyle@google.com> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Michael Hoyle <mikehoyle@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
248 lines
8.6 KiB
Rust
248 lines
8.6 KiB
Rust
// Copyright 2019 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
use std::env;
|
|
use std::fs;
|
|
use std::io::{stdout, Write};
|
|
use std::mem;
|
|
use std::os::unix::fs::symlink;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
use std::sync::Once;
|
|
|
|
use libc::{cpu_set_t, sched_getaffinity};
|
|
|
|
use arch::{set_default_serial_parameters, SerialHardware, SerialParameters, SerialType};
|
|
use base::syslog;
|
|
use crosvm::{linux, Config, Executable};
|
|
|
|
const CHROOT_KERNEL_PATH: &str = "/mnt/host/source/src/third_party/kernel/v4.19/";
|
|
const CONTAINER_VM_DEFCONFIG: &str = "arch/x86/configs/chromiumos-container-vm-x86_64_defconfig";
|
|
const KERNEL_REPO: &str = "https://chromium.googlesource.com/chromiumos/third_party/kernel";
|
|
const KERNEL_REPO_BRANCH: &str = "chromeos-4.19";
|
|
// TODO(zachr): this URL is a placeholder until the automated builder is running and we've settled
|
|
// on a location.
|
|
const KERNEL_PREBUILT: &str = "http://storage.googleapis.com/crosvm-testing";
|
|
|
|
/// Returns the number of CPUs that this process and its children can use by querying our process's
|
|
/// CPU affinity.
|
|
fn get_cpu_count() -> usize {
|
|
unsafe {
|
|
let mut set: cpu_set_t = mem::zeroed();
|
|
let ret = sched_getaffinity(0, mem::size_of::<cpu_set_t>(), &mut set);
|
|
if ret != 0 {
|
|
// A good guess.
|
|
4
|
|
} else {
|
|
// The cpu_set_t is normally counted using the CPU_COUNT macro, but we don't have that
|
|
// in Rust. Because it's always a bitset, we will treat it like one here.
|
|
let set: [u8; mem::size_of::<cpu_set_t>()] = mem::transmute(set);
|
|
set.iter().map(|b| b.count_ones() as usize).sum()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Clones a chrome os kernel into the given path.
|
|
fn clone_kernel_source(dir: &Path) {
|
|
let status = Command::new("git")
|
|
.args(&[
|
|
"clone",
|
|
"--depth",
|
|
"1",
|
|
"--branch",
|
|
KERNEL_REPO_BRANCH,
|
|
KERNEL_REPO,
|
|
])
|
|
.arg(dir)
|
|
.status()
|
|
.expect("failed to execute git");
|
|
if !status.success() {
|
|
panic!("failed to clone kernel source: {}", status);
|
|
}
|
|
}
|
|
|
|
// Kernel binary algorithm.
|
|
// 1: If CROSVM_CARGO_TEST_KERNEL_BINARY is in the env:
|
|
// If CROSVM_CARGO_TEST_KERNEL_BINARY is empty, skip step 3.
|
|
// If CROSVM_CARGO_TEST_KERNEL_BINARY does not exist, panic.
|
|
// 2: If "bzImage" exists in the target directory use that.
|
|
// 3: Download "bzImage" from the KERNEL_PREBUILT url and use that.
|
|
// If the download does not work, go to the kernel source algorithm.
|
|
//
|
|
// Kernel source algorithm
|
|
// 1: If CROSVM_CARGO_TEST_KERNEL_SOURCE is in the env, use that.
|
|
// If CROSVM_CARGO_TEST_KERNEL_SOURCE does not exist, panic
|
|
// 2: If CHROOT_KERNEL_PATH exists, use that.
|
|
// 3: Checkout and use the chromeos kernel.
|
|
//
|
|
// Kernel config algorithm
|
|
// 1: If the .config already exists in the kernel source, use that.
|
|
// 2: If the CONTAINER_VM_DEFCONFIG exists in the kernel source, use that.
|
|
// 3: Use `make defconfig`.
|
|
fn prepare_kernel_once(dir: &Path) {
|
|
let kernel_binary = dir.join("bzImage");
|
|
|
|
let mut download_prebuilt = true;
|
|
if let Ok(env_kernel_binary) = env::var("CROSVM_CARGO_TEST_KERNEL_BINARY") {
|
|
if env_kernel_binary.is_empty() {
|
|
download_prebuilt = false;
|
|
} else {
|
|
println!(
|
|
"using kernel binary from enviroment `{}`",
|
|
env_kernel_binary
|
|
);
|
|
let env_kernel_binary = PathBuf::from(env_kernel_binary);
|
|
if env_kernel_binary.exists() {
|
|
symlink(env_kernel_binary, &kernel_binary)
|
|
.expect("failed to create symlink for kernel binary");
|
|
return;
|
|
} else {
|
|
panic!(
|
|
"expected kernel binary at `{}`",
|
|
env_kernel_binary.display()
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("looking for kernel binary at `{}`", kernel_binary.display());
|
|
if kernel_binary.exists() {
|
|
println!("using kernel binary at `{}`", kernel_binary.display());
|
|
return;
|
|
}
|
|
|
|
if download_prebuilt {
|
|
// Resolve the base URL into a specific path for this architecture.
|
|
let kernel_prebuilt = format!(
|
|
"{}/{}/{}",
|
|
KERNEL_PREBUILT,
|
|
env::consts::ARCH,
|
|
"latest-bzImage"
|
|
);
|
|
println!(
|
|
"downloading prebuilt kernel binary from `{}`",
|
|
kernel_prebuilt
|
|
);
|
|
let status = Command::new("curl")
|
|
.args(&["--fail", "--location"])
|
|
.arg("--output")
|
|
.arg(&kernel_binary)
|
|
.arg(kernel_prebuilt)
|
|
.status();
|
|
if let Ok(status) = status {
|
|
if status.success() {
|
|
println!("using prebuilt kernel binary");
|
|
return;
|
|
}
|
|
}
|
|
|
|
println!("failed to download prebuilt kernel binary");
|
|
}
|
|
|
|
let kernel_source = if let Ok(env_kernel_source) = env::var("CROSVM_CARGO_TEST_KERNEL_SOURCE") {
|
|
if Path::new(&env_kernel_source).is_dir() {
|
|
PathBuf::from(env_kernel_source)
|
|
} else {
|
|
panic!("expected kernel source at `{}`", env_kernel_source);
|
|
}
|
|
} else if Path::new(CHROOT_KERNEL_PATH).is_dir() {
|
|
PathBuf::from(CHROOT_KERNEL_PATH)
|
|
} else {
|
|
let kernel_source = dir.join("kernel-source");
|
|
// Check for kernel source
|
|
if !kernel_source.is_dir() {
|
|
clone_kernel_source(&kernel_source);
|
|
}
|
|
kernel_source
|
|
};
|
|
|
|
println!("building kernel from source `{}`", kernel_source.display());
|
|
|
|
// Special provisions for using the ChromeOS kernel source and its config used in crostini.
|
|
let current_config = kernel_source.join(".config");
|
|
let container_vm_defconfig = kernel_source.join(CONTAINER_VM_DEFCONFIG);
|
|
if current_config.exists() {
|
|
fs::copy(current_config, dir.join(".config"))
|
|
.expect("failed to copy existing kernel config");
|
|
} else if container_vm_defconfig.exists() {
|
|
fs::copy(container_vm_defconfig, dir.join(".config"))
|
|
.expect("failed to copy chromiumos container vm kernel config");
|
|
} else {
|
|
// TODO(zachr): the defconfig for vanilla kernels is probably inadequate. There should
|
|
// probably be a step where additional options are added to the resulting .config.
|
|
let status = Command::new("make")
|
|
.current_dir(&kernel_source)
|
|
.arg(format!("O={}", dir.display()))
|
|
.arg("defconfig")
|
|
.status()
|
|
.expect("failed to execute make");
|
|
if !status.success() {
|
|
panic!("failed to default config kernel: {}", status);
|
|
}
|
|
}
|
|
|
|
let output = Command::new("make")
|
|
.current_dir(&kernel_source)
|
|
.arg(format!("O={}", dir.display()))
|
|
.args(&["bzImage", "-j"])
|
|
.arg(format!("{}", get_cpu_count()))
|
|
.output()
|
|
.expect("failed to execute make");
|
|
if !output.status.success() {
|
|
let _ = stdout().lock().write(&output.stderr);
|
|
panic!("failed to build kernel: {}", output.status);
|
|
}
|
|
|
|
fs::copy(dir.join("arch/x86/boot/bzImage"), &kernel_binary)
|
|
.expect("failed to copy kernel binary");
|
|
}
|
|
|
|
/// Gets the target directory path for artifacts.
|
|
fn get_target_path() -> PathBuf {
|
|
env::current_exe()
|
|
.ok()
|
|
.map(|mut path| {
|
|
path.pop();
|
|
path
|
|
})
|
|
.expect("failed to get target dir")
|
|
}
|
|
|
|
/// Thread-safe method for preparing a kernel and returning the path to its binary.
|
|
fn prepare_kernel() -> PathBuf {
|
|
// Lots of unit tests need the kernel, but it should only get prepared once by any arbitrary
|
|
// test. The rest of the tests should wait until the arbitrary one finishes.
|
|
let default_linux_dir = get_target_path();
|
|
static PREP_ONCE: Once = Once::new();
|
|
PREP_ONCE.call_once(|| prepare_kernel_once(&default_linux_dir));
|
|
default_linux_dir.join("bzImage")
|
|
}
|
|
|
|
#[test]
|
|
fn boot() {
|
|
syslog::init().unwrap();
|
|
|
|
let kernel_path = prepare_kernel();
|
|
|
|
let mut c = Config::default();
|
|
c.sandbox = false;
|
|
c.serial_parameters.insert(
|
|
(SerialHardware::Serial, 1),
|
|
SerialParameters {
|
|
type_: SerialType::Sink,
|
|
hardware: SerialHardware::Serial,
|
|
path: None,
|
|
input: None,
|
|
num: 1,
|
|
console: false,
|
|
earlycon: false,
|
|
stdin: false,
|
|
},
|
|
);
|
|
set_default_serial_parameters(&mut c.serial_parameters);
|
|
c.executable_path = Some(Executable::Kernel(kernel_path));
|
|
|
|
let r = linux::run_config(c);
|
|
r.expect("failed to run linux");
|
|
}
|