mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-10-24 13:12:57 +00:00
Initial BIOS support.
The --bios argument is added as an alternative to the kernel positional argument. The BIOS runs in unreal mode (16-bit cs selector set to the end of 32-bit address space), which matches the default state KVM puts the segment and data registers into. Example usage: Build u-boot with "make qemu-x86_defconfig && make" Run crosvm with "crosvm_wrapper.sh run --bios=u-boot.rom" This produces the following message: """ U-Boot 2019.01-00017-gdc76aabe6a-dirty (May 21 2019 - 12:17:02 -0700) CPU: DRAM: 16 MiB unable to get online cpu number: -19 Warning: MP init failure Model: QEMU x86 (I440FX) Net: No ethernet found. error: can't find etc/table-loader Hit any key to stop autoboot: 0 => """ At this point the u-boot shell works with stdin/stdout, but virtual disks passed with --rwdisk weren't immediately visible from running "virtio scan" and "virtio info". This change puts the bios loading together with the linux kernel loading code since there is a lot of overlap in functionality. Bug: b/133358982 Test: ./crosvm_wrapper.sh run --mem=4097 --bios=u-boot.rom Change-Id: I65b0e1044233af662a642c592d35b106217f3c13 Reviewed-on: https://chromium-review.googlesource.com/1622648 Commit-Ready: Daniel Verkamp <dverkamp@chromium.org> Tested-by: Daniel Verkamp <dverkamp@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
580d418656
commit
6d1ab50943
6 changed files with 198 additions and 92 deletions
|
@ -11,7 +11,7 @@ use std::io;
|
|||
use std::os::unix::io::FromRawFd;
|
||||
use std::sync::Arc;
|
||||
|
||||
use arch::{RunnableLinuxVm, VmComponents};
|
||||
use arch::{RunnableLinuxVm, VmComponents, VmImage};
|
||||
use devices::{
|
||||
get_serial_tty_string, Bus, BusError, PciConfigMmio, PciDevice, PciInterruptPin,
|
||||
SerialParameters,
|
||||
|
@ -124,6 +124,7 @@ pub enum Error {
|
|||
CreateVm(sys_util::Error),
|
||||
InitrdLoadFailure(arch::LoadImageError),
|
||||
KernelLoadFailure(arch::LoadImageError),
|
||||
KernelMissing,
|
||||
ReadPreferredTarget(sys_util::Error),
|
||||
RegisterIrqfd(sys_util::Error),
|
||||
RegisterPci(BusError),
|
||||
|
@ -155,6 +156,7 @@ impl Display for Error {
|
|||
CreateVm(e) => write!(f, "failed to create vm: {}", e),
|
||||
InitrdLoadFailure(e) => write!(f, "initrd cound not be loaded: {}", e),
|
||||
KernelLoadFailure(e) => write!(f, "kernel cound not be loaded: {}", e),
|
||||
KernelMissing => write!(f, "aarch64 requires a kernel"),
|
||||
ReadPreferredTarget(e) => write!(f, "failed to read preferred target: {}", e),
|
||||
RegisterIrqfd(e) => write!(f, "failed to register irq fd: {}", e),
|
||||
RegisterPci(e) => write!(f, "error registering PCI bus: {}", e),
|
||||
|
@ -272,15 +274,16 @@ impl arch::LinuxArch for AArch64 {
|
|||
cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
|
||||
}
|
||||
|
||||
let kernel_image = if let VmImage::Kernel(ref mut img) = components.vm_image {
|
||||
img
|
||||
} else {
|
||||
return Err(Error::KernelMissing);
|
||||
};
|
||||
|
||||
// separate out kernel loading from other setup to get a specific error for
|
||||
// kernel loading
|
||||
let kernel_size = arch::load_image(
|
||||
&mem,
|
||||
&mut components.kernel_image,
|
||||
get_kernel_addr(),
|
||||
u64::max_value(),
|
||||
)
|
||||
.map_err(Error::KernelLoadFailure)?;
|
||||
let kernel_size = arch::load_image(&mem, kernel_image, get_kernel_addr(), u64::max_value())
|
||||
.map_err(Error::KernelLoadFailure)?;
|
||||
let kernel_end = get_kernel_addr().offset() + kernel_size as u64;
|
||||
Self::setup_system_memory(
|
||||
&mem,
|
||||
|
|
|
@ -24,13 +24,18 @@ use resources::SystemAllocator;
|
|||
use sync::Mutex;
|
||||
use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
|
||||
|
||||
pub enum VmImage {
|
||||
Kernel(File),
|
||||
Bios(File),
|
||||
}
|
||||
|
||||
/// Holds the pieces needed to build a VM. Passed to `build_vm` in the `LinuxArch` trait below to
|
||||
/// create a `RunnableLinuxVm`.
|
||||
pub struct VmComponents {
|
||||
pub memory_size: u64,
|
||||
pub vcpu_count: u32,
|
||||
pub vcpu_affinity: Vec<usize>,
|
||||
pub kernel_image: File,
|
||||
pub vm_image: VmImage,
|
||||
pub android_fstab: Option<File>,
|
||||
pub initrd_image: Option<File>,
|
||||
pub extra_kernel_params: Vec<String>,
|
||||
|
|
19
src/linux.rs
19
src/linux.rs
|
@ -52,9 +52,9 @@ use vm_control::{
|
|||
VmMemoryControlResponseSocket, VmMemoryRequest, VmMemoryResponse, VmRunMode,
|
||||
};
|
||||
|
||||
use crate::{Config, DiskOption, TouchDeviceOption};
|
||||
use crate::{Config, DiskOption, Executable, TouchDeviceOption};
|
||||
|
||||
use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents};
|
||||
use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents, VmImage};
|
||||
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
use aarch64::AArch64 as Arch;
|
||||
|
@ -100,6 +100,7 @@ pub enum Error {
|
|||
LoadKernel(Box<dyn StdError>),
|
||||
NetDeviceNew(virtio::NetError),
|
||||
OpenAndroidFstab(PathBuf, io::Error),
|
||||
OpenBios(PathBuf, io::Error),
|
||||
OpenInitrd(PathBuf, io::Error),
|
||||
OpenKernel(PathBuf, io::Error),
|
||||
OpenVinput(PathBuf, io::Error),
|
||||
|
@ -179,6 +180,7 @@ impl Display for Error {
|
|||
p.display(),
|
||||
e
|
||||
),
|
||||
OpenBios(p, e) => write!(f, "failed to open bios {}: {}", p.display(), e),
|
||||
OpenInitrd(p, e) => write!(f, "failed to open initrd {}: {}", p.display(), e),
|
||||
OpenKernel(p, e) => write!(f, "failed to open kernel image {}: {}", p.display(), e),
|
||||
OpenVinput(p, e) => write!(f, "failed to open vinput device {}: {}", p.display(), e),
|
||||
|
@ -1147,12 +1149,21 @@ pub fn run_config(cfg: Config) -> Result<()> {
|
|||
None
|
||||
};
|
||||
|
||||
let vm_image = match cfg.executable_path {
|
||||
Some(Executable::Kernel(ref kernel_path)) => VmImage::Kernel(
|
||||
File::open(kernel_path).map_err(|e| Error::OpenKernel(kernel_path.to_path_buf(), e))?,
|
||||
),
|
||||
Some(Executable::Bios(ref bios_path)) => VmImage::Bios(
|
||||
File::open(bios_path).map_err(|e| Error::OpenBios(bios_path.to_path_buf(), e))?,
|
||||
),
|
||||
_ => panic!("Did not receive a bios or kernel, should be impossible."),
|
||||
};
|
||||
|
||||
let components = VmComponents {
|
||||
memory_size: (cfg.memory.unwrap_or(256) << 20) as u64,
|
||||
vcpu_count: cfg.vcpu_count.unwrap_or(1),
|
||||
vcpu_affinity: cfg.vcpu_affinity.clone(),
|
||||
kernel_image: File::open(&cfg.kernel_path)
|
||||
.map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?,
|
||||
vm_image,
|
||||
android_fstab: cfg
|
||||
.android_fstab
|
||||
.as_ref()
|
||||
|
|
81
src/main.rs
81
src/main.rs
|
@ -75,16 +75,29 @@ impl TouchDeviceOption {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Executable {
|
||||
Bios(PathBuf),
|
||||
Kernel(PathBuf),
|
||||
Plugin(PathBuf),
|
||||
}
|
||||
|
||||
fn executable_is_plugin(executable: &Option<Executable>) -> bool {
|
||||
match executable {
|
||||
Some(Executable::Plugin(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
vcpu_count: Option<u32>,
|
||||
vcpu_affinity: Vec<usize>,
|
||||
memory: Option<usize>,
|
||||
kernel_path: PathBuf,
|
||||
executable_path: Option<Executable>,
|
||||
android_fstab: Option<PathBuf>,
|
||||
initrd_path: Option<PathBuf>,
|
||||
params: Vec<String>,
|
||||
socket_path: Option<PathBuf>,
|
||||
plugin: Option<PathBuf>,
|
||||
plugin_root: Option<PathBuf>,
|
||||
plugin_mounts: Vec<BindMount>,
|
||||
plugin_gid_maps: Vec<GidMap>,
|
||||
|
@ -121,12 +134,11 @@ impl Default for Config {
|
|||
vcpu_count: None,
|
||||
vcpu_affinity: Vec::new(),
|
||||
memory: None,
|
||||
kernel_path: PathBuf::default(),
|
||||
executable_path: None,
|
||||
android_fstab: None,
|
||||
initrd_path: None,
|
||||
params: Vec::new(),
|
||||
socket_path: None,
|
||||
plugin: None,
|
||||
plugin_root: None,
|
||||
plugin_mounts: Vec::new(),
|
||||
plugin_gid_maps: Vec::new(),
|
||||
|
@ -285,24 +297,20 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
|
|||
fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
|
||||
match name {
|
||||
"" => {
|
||||
if cfg.plugin.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"`plugin` can not be used with kernel".to_owned(),
|
||||
));
|
||||
} else if !cfg.kernel_path.as_os_str().is_empty() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"expected exactly one kernel path".to_owned(),
|
||||
));
|
||||
} else {
|
||||
let kernel_path = PathBuf::from(value.unwrap());
|
||||
if !kernel_path.exists() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: "this kernel path does not exist",
|
||||
});
|
||||
}
|
||||
cfg.kernel_path = kernel_path;
|
||||
if cfg.executable_path.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(format!(
|
||||
"A VM executable was already specified: {:?}",
|
||||
cfg.executable_path
|
||||
)));
|
||||
}
|
||||
let kernel_path = PathBuf::from(value.unwrap());
|
||||
if !kernel_path.exists() {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: value.unwrap().to_owned(),
|
||||
expected: "this kernel path does not exist",
|
||||
});
|
||||
}
|
||||
cfg.executable_path = Some(Executable::Kernel(kernel_path));
|
||||
}
|
||||
"android-fstab" => {
|
||||
if cfg.android_fstab.is_some()
|
||||
|
@ -572,14 +580,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
cfg.seccomp_policy_dir = PathBuf::from(value.unwrap());
|
||||
}
|
||||
"plugin" => {
|
||||
if !cfg.kernel_path.as_os_str().is_empty() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"`plugin` can not be used with kernel".to_owned(),
|
||||
));
|
||||
} else if cfg.plugin.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"`plugin` already given".to_owned(),
|
||||
));
|
||||
if cfg.executable_path.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(format!(
|
||||
"A VM executable was already specified: {:?}",
|
||||
cfg.executable_path
|
||||
)));
|
||||
}
|
||||
let plugin = PathBuf::from(value.unwrap().to_owned());
|
||||
if plugin.is_relative() {
|
||||
|
@ -588,7 +593,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
expected: "the plugin path must be an absolute path",
|
||||
});
|
||||
}
|
||||
cfg.plugin = Some(plugin);
|
||||
cfg.executable_path = Some(Executable::Plugin(plugin));
|
||||
}
|
||||
"plugin-root" => {
|
||||
cfg.plugin_root = Some(PathBuf::from(value.unwrap().to_owned()));
|
||||
|
@ -762,6 +767,15 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
"initrd" => {
|
||||
cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned()));
|
||||
}
|
||||
"bios" => {
|
||||
if cfg.executable_path.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(format!(
|
||||
"A VM executable was already specified: {:?}",
|
||||
cfg.executable_path
|
||||
)));
|
||||
}
|
||||
cfg.executable_path = Some(Executable::Bios(PathBuf::from(value.unwrap().to_owned())));
|
||||
}
|
||||
"help" => return Err(argument::Error::PrintHelp),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -844,6 +858,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
|||
Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
Argument::flag("split-irqchip", "(EXPERIMENTAL) enable split-irqchip support"),
|
||||
Argument::value("bios", "PATH", "Path to BIOS/firmware ROM"),
|
||||
Argument::short_flag('h', "help", "Print help message.")];
|
||||
|
||||
let mut cfg = Config::default();
|
||||
|
@ -851,7 +866,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
|||
set_argument(&mut cfg, name, value)
|
||||
})
|
||||
.and_then(|_| {
|
||||
if cfg.kernel_path.as_os_str().is_empty() && cfg.plugin.is_none() {
|
||||
if cfg.executable_path.is_none() {
|
||||
return Err(argument::Error::ExpectedArgument("`KERNEL`".to_owned()));
|
||||
}
|
||||
if cfg.host_ip.is_some() || cfg.netmask.is_some() || cfg.mac_address.is_some() {
|
||||
|
@ -871,7 +886,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
|||
));
|
||||
}
|
||||
}
|
||||
if cfg.plugin_root.is_some() && cfg.plugin.is_none() {
|
||||
if cfg.plugin_root.is_some() && !executable_is_plugin(&cfg.executable_path) {
|
||||
return Err(argument::Error::ExpectedArgument(
|
||||
"`plugin-root` requires `plugin`".to_owned(),
|
||||
));
|
||||
|
@ -881,7 +896,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
|||
|
||||
match match_res {
|
||||
#[cfg(feature = "plugin")]
|
||||
Ok(()) if cfg.plugin.is_some() => match plugin::run_config(cfg) {
|
||||
Ok(()) if executable_is_plugin(&cfg.executable_path) => match plugin::run_config(cfg) {
|
||||
Ok(_) => {
|
||||
info!("crosvm and plugin have exited normally");
|
||||
Ok(())
|
||||
|
|
|
@ -38,7 +38,7 @@ use sys_util::{
|
|||
|
||||
use self::process::*;
|
||||
use self::vcpu::*;
|
||||
use crate::Config;
|
||||
use crate::{Config, Executable};
|
||||
|
||||
const MAX_DATAGRAM_SIZE: usize = 4096;
|
||||
const MAX_VCPU_DATAGRAM_SIZE: usize = 0x40000;
|
||||
|
@ -598,7 +598,10 @@ pub fn run_config(cfg: Config) -> Result<()> {
|
|||
|
||||
let plugin_args: Vec<&str> = cfg.params.iter().map(|s| &s[..]).collect();
|
||||
|
||||
let plugin_path = cfg.plugin.as_ref().unwrap().as_path();
|
||||
let plugin_path = match cfg.executable_path {
|
||||
Some(Executable::Plugin(ref plugin_path)) => plugin_path.as_path(),
|
||||
_ => panic!("Executable was not a plugin"),
|
||||
};
|
||||
let vcpu_count = cfg.vcpu_count.unwrap_or(1);
|
||||
let mem = GuestMemory::new(&[]).unwrap();
|
||||
let kvm = Kvm::new().map_err(Error::CreateKvm)?;
|
||||
|
|
|
@ -48,12 +48,12 @@ use std::error::Error as StdError;
|
|||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{self, Seek};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::bootparam::boot_params;
|
||||
use arch::{RunnableLinuxVm, VmComponents};
|
||||
use arch::{RunnableLinuxVm, VmComponents, VmImage};
|
||||
use devices::{get_serial_tty_string, PciConfigIo, PciDevice, PciInterruptPin, SerialParameters};
|
||||
use io_jail::Minijail;
|
||||
use kvm::*;
|
||||
|
@ -82,6 +82,7 @@ pub enum Error {
|
|||
CreateVm(sys_util::Error),
|
||||
E820Configuration,
|
||||
KernelOffsetPastEnd,
|
||||
LoadBios(io::Error),
|
||||
LoadBzImage(bzimage::Error),
|
||||
LoadCmdline(kernel_loader::Error),
|
||||
LoadInitrd(arch::LoadImageError),
|
||||
|
@ -126,6 +127,7 @@ impl Display for Error {
|
|||
CreateVm(e) => write!(f, "failed to create VM: {}", e),
|
||||
E820Configuration => write!(f, "invalid e820 setup params"),
|
||||
KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"),
|
||||
LoadBios(e) => write!(f, "error loading bios: {}", e),
|
||||
LoadBzImage(e) => write!(f, "error loading kernel bzImage: {}", e),
|
||||
LoadCmdline(e) => write!(f, "error loading command line: {}", e),
|
||||
LoadInitrd(e) => write!(f, "error loading initrd: {}", e),
|
||||
|
@ -159,6 +161,11 @@ const MEM_32BIT_GAP_SIZE: u64 = (768 << 20);
|
|||
const FIRST_ADDR_PAST_32BITS: u64 = (1 << 32);
|
||||
const KERNEL_64BIT_ENTRY_OFFSET: u64 = 0x200;
|
||||
const ZERO_PAGE_OFFSET: u64 = 0x7000;
|
||||
/// The x86 reset vector for i386+ and x86_64 puts the processor into an "unreal mode" where it
|
||||
/// can access the last 1 MB of the 32-bit address space in 16-bit mode, and starts the instruction
|
||||
/// pointer at the effective physical address 0xFFFFFFF0.
|
||||
const BIOS_LEN: usize = 1 << 20;
|
||||
const BIOS_START: u64 = FIRST_ADDR_PAST_32BITS - (BIOS_LEN as u64);
|
||||
|
||||
const KERNEL_START_OFFSET: u64 = 0x200000;
|
||||
const CMDLINE_OFFSET: u64 = 0x20000;
|
||||
|
@ -259,10 +266,10 @@ fn add_e820_entry(params: &mut boot_params, addr: u64, size: u64, mem_type: u32)
|
|||
}
|
||||
|
||||
/// Returns a Vec of the valid memory addresses.
|
||||
/// These should be used to configure the GuestMemory structure for the platfrom.
|
||||
/// For x86_64 all addresses are valid from the start of the kenel except a
|
||||
/// 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.
|
||||
fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
|
||||
fn arch_memory_regions(size: u64, has_bios: bool) -> Vec<(GuestAddress, u64)> {
|
||||
let mem_end = GuestAddress(size);
|
||||
let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
|
||||
let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
|
||||
|
@ -270,13 +277,20 @@ fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
|
|||
let mut regions = Vec::new();
|
||||
if mem_end < end_32bit_gap_start {
|
||||
regions.push((GuestAddress(0), size));
|
||||
if has_bios {
|
||||
regions.push((GuestAddress(BIOS_START), BIOS_LEN as u64));
|
||||
}
|
||||
} else {
|
||||
regions.push((GuestAddress(0), end_32bit_gap_start.offset()));
|
||||
if mem_end > first_addr_past_32bits {
|
||||
regions.push((
|
||||
first_addr_past_32bits,
|
||||
mem_end.offset_from(first_addr_past_32bits),
|
||||
));
|
||||
let region_start = if has_bios {
|
||||
GuestAddress(BIOS_START)
|
||||
} else {
|
||||
first_addr_past_32bits
|
||||
};
|
||||
regions.push((region_start, mem_end.offset_from(first_addr_past_32bits)));
|
||||
} else if has_bios {
|
||||
regions.push((GuestAddress(BIOS_START), BIOS_LEN as u64));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,7 +315,11 @@ impl arch::LinuxArch for X8664arch {
|
|||
{
|
||||
let mut resources =
|
||||
Self::get_resource_allocator(components.memory_size, components.wayland_dmabuf);
|
||||
let mem = Self::setup_memory(components.memory_size)?;
|
||||
let has_bios = match components.vm_image {
|
||||
VmImage::Bios(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
let mem = Self::setup_memory(components.memory_size, has_bios)?;
|
||||
let kvm = Kvm::new().map_err(Error::CreateKvm)?;
|
||||
let mut vm = Self::create_vm(&kvm, split_irqchip, mem.clone())?;
|
||||
|
||||
|
@ -309,14 +327,16 @@ impl arch::LinuxArch for X8664arch {
|
|||
let mut vcpus = Vec::with_capacity(vcpu_count as usize);
|
||||
for cpu_id in 0..vcpu_count {
|
||||
let vcpu = Vcpu::new(cpu_id as libc::c_ulong, &kvm, &vm).map_err(Error::CreateVcpu)?;
|
||||
Self::configure_vcpu(
|
||||
vm.get_memory(),
|
||||
&kvm,
|
||||
&vm,
|
||||
&vcpu,
|
||||
cpu_id as u64,
|
||||
vcpu_count as u64,
|
||||
)?;
|
||||
if let VmImage::Kernel(_) = components.vm_image {
|
||||
Self::configure_vcpu(
|
||||
vm.get_memory(),
|
||||
&kvm,
|
||||
&vm,
|
||||
&vcpu,
|
||||
cpu_id as u64,
|
||||
vcpu_count as u64,
|
||||
)?;
|
||||
}
|
||||
vcpus.push(vcpu);
|
||||
}
|
||||
|
||||
|
@ -346,27 +366,31 @@ impl arch::LinuxArch for X8664arch {
|
|||
let (stdio_serial_num, stdio_serial) =
|
||||
Self::setup_serial_devices(&mut vm, &mut io_bus, &serial_parameters)?;
|
||||
|
||||
let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
|
||||
for param in components.extra_kernel_params {
|
||||
cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
|
||||
match components.vm_image {
|
||||
VmImage::Bios(ref mut bios) => Self::load_bios(&mem, bios)?,
|
||||
VmImage::Kernel(ref mut kernel_image) => {
|
||||
let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
|
||||
for param in components.extra_kernel_params {
|
||||
cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
|
||||
}
|
||||
|
||||
// separate out load_kernel from other setup to get a specific error for
|
||||
// kernel loading
|
||||
let (params, kernel_end) = Self::load_kernel(&mem, kernel_image)?;
|
||||
|
||||
Self::setup_system_memory(
|
||||
&mem,
|
||||
components.memory_size,
|
||||
vcpu_count,
|
||||
&CString::new(cmdline).unwrap(),
|
||||
components.initrd_image,
|
||||
pci_irqs,
|
||||
components.android_fstab,
|
||||
kernel_end,
|
||||
params,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// separate out load_kernel from other setup to get a specific error for
|
||||
// kernel loading
|
||||
let (params, kernel_end) = Self::load_kernel(&mem, &mut components.kernel_image)?;
|
||||
|
||||
Self::setup_system_memory(
|
||||
&mem,
|
||||
components.memory_size,
|
||||
vcpu_count,
|
||||
&CString::new(cmdline).unwrap(),
|
||||
components.initrd_image,
|
||||
pci_irqs,
|
||||
components.android_fstab,
|
||||
kernel_end,
|
||||
params,
|
||||
)?;
|
||||
|
||||
Ok(RunnableLinuxVm {
|
||||
vm,
|
||||
kvm,
|
||||
|
@ -384,6 +408,33 @@ impl arch::LinuxArch for X8664arch {
|
|||
}
|
||||
|
||||
impl X8664arch {
|
||||
/// Loads the bios from an open file.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `mem` - The memory to be used by the guest.
|
||||
/// * `bios_image` - the File object for the specified bios
|
||||
fn load_bios(mem: &GuestMemory, bios_image: &mut File) -> Result<()> {
|
||||
let bios_image_length = bios_image
|
||||
.seek(io::SeekFrom::End(0))
|
||||
.map_err(Error::LoadBios)?;
|
||||
if bios_image_length != BIOS_LEN as u64 {
|
||||
return Err(Error::LoadBios(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"bios was {} bytes, expected {}",
|
||||
bios_image_length, BIOS_LEN
|
||||
),
|
||||
)));
|
||||
}
|
||||
bios_image
|
||||
.seek(io::SeekFrom::Start(0))
|
||||
.map_err(Error::LoadBios)?;
|
||||
mem.read_to_memory(GuestAddress(BIOS_START), bios_image, BIOS_LEN)
|
||||
.map_err(Error::SetupGuestMemory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads the kernel from an open file.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -507,8 +558,8 @@ impl X8664arch {
|
|||
/// This creates a GuestMemory object for this VM
|
||||
///
|
||||
/// * `mem_size` - Desired physical memory size in bytes for this VM
|
||||
fn setup_memory(mem_size: u64) -> Result<GuestMemory> {
|
||||
let arch_mem_regions = arch_memory_regions(mem_size);
|
||||
fn setup_memory(mem_size: u64, has_bios: bool) -> Result<GuestMemory> {
|
||||
let arch_mem_regions = arch_memory_regions(mem_size, has_bios);
|
||||
let mem = GuestMemory::new(&arch_mem_regions).map_err(Error::SetupGuestMemory)?;
|
||||
Ok(mem)
|
||||
}
|
||||
|
@ -722,18 +773,36 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn regions_lt_4gb() {
|
||||
let regions = arch_memory_regions(1u64 << 29);
|
||||
fn regions_lt_4gb_nobios() {
|
||||
let regions = arch_memory_regions(1u64 << 29, /* has_bios */ false);
|
||||
assert_eq!(1, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1u64 << 29, regions[0].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_gt_4gb() {
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000);
|
||||
fn regions_gt_4gb_nobios() {
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* has_bios */ false);
|
||||
assert_eq!(2, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(1u64 << 32), regions[1].0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_lt_4gb_bios() {
|
||||
let regions = arch_memory_regions(1u64 << 29, /* has_bios */ true);
|
||||
assert_eq!(2, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(1u64 << 29, regions[0].1);
|
||||
assert_eq!(GuestAddress(BIOS_START), regions[1].0);
|
||||
assert_eq!(BIOS_LEN as u64, regions[1].1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regions_gt_4gb_bios() {
|
||||
let regions = arch_memory_regions((1u64 << 32) + 0x8000, /* has_bios */ true);
|
||||
assert_eq!(2, regions.len());
|
||||
assert_eq!(GuestAddress(0), regions[0].0);
|
||||
assert_eq!(GuestAddress(BIOS_START), regions[1].0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue