crosvm: x86_64 guest support for android device-tree

This device tree is derived from the Android fstab file which is
provided via command line flag.

BUG=chromium:922737
TEST=None
CQ-DEPEND=CL:1415390
CQ-DEPEND=CL:1415270

Change-Id: Idd007c844f84cab3ff37be16a718f14e5f630312
Reviewed-on: https://chromium-review.googlesource.com/1370058
Commit-Ready: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Tristan Muntsinger 2018-12-21 16:01:56 -08:00 committed by chrome-bot
parent f052cfefc8
commit 4133b0120d
12 changed files with 430 additions and 243 deletions

2
Cargo.lock generated
View file

@ -3,7 +3,6 @@ name = "aarch64"
version = "0.1.0"
dependencies = [
"arch 0.1.0",
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"data_model 0.1.0",
"devices 0.1.0",
"io_jail 0.1.0",
@ -20,6 +19,7 @@ dependencies = [
name = "arch"
version = "0.1.0"
dependencies = [
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"devices 0.1.0",
"io_jail 0.1.0",
"kernel_cmdline 0.1.0",

View file

@ -15,4 +15,3 @@ sys_util = { path = "../sys_util" }
resources = { path = "../resources" }
sync = { path = "../sync" }
libc = "*"
byteorder = "*"

View file

@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use byteorder::{BigEndian, ByteOrder};
use libc::{c_char, c_int, c_void};
use std::error::{self, Error as FdtError};
use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr::null;
use std::ffi::CStr;
use arch::fdt::{
begin_node, end_node, finish_fdt, generate_prop32, generate_prop64, property, property_cstring,
property_null, property_string, property_u32, property_u64, start_fdt, Error,
};
use devices::PciInterruptPin;
use sys_util::{GuestAddress, GuestMemory};
@ -55,197 +54,6 @@ const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001;
const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008;
// This links to libfdt which handles the creation of the binary blob
// flattened device tree (fdt) that is passed to the kernel and indicates
// the hardware configuration of the machine.
#[link(name = "fdt")]
extern "C" {
fn fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int;
fn fdt_finish_reservemap(fdt: *mut c_void) -> c_int;
fn fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int;
fn fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int)
-> c_int;
fn fdt_end_node(fdt: *mut c_void) -> c_int;
fn fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int;
fn fdt_finish(fdt: *const c_void) -> c_int;
fn fdt_pack(fdt: *mut c_void) -> c_int;
}
#[derive(Debug)]
pub enum Error {
FdtCreateError(c_int),
FdtFinishReservemapError(c_int),
FdtBeginNodeError(c_int),
FdtPropertyError(c_int),
FdtEndNodeError(c_int),
FdtOpenIntoError(c_int),
FdtFinishError(c_int),
FdtPackError(c_int),
FdtGuestMemoryWriteError,
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
&Error::FdtCreateError(_) => "Error creating FDT",
&Error::FdtFinishReservemapError(_) => "Error finishing reserve map",
&Error::FdtBeginNodeError(_) => "Error beginning FDT node",
&Error::FdtPropertyError(_) => "Error adding FDT property",
&Error::FdtEndNodeError(_) => "Error ending FDT node",
&Error::FdtOpenIntoError(_) => "Error copying FDT to Guest",
&Error::FdtFinishError(_) => "Error performing FDT finish",
&Error::FdtPackError(_) => "Error packing FDT",
&Error::FdtGuestMemoryWriteError => "Error writing FDT to Guest Memory",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prefix = "Libfdt Error: ";
match self {
&Error::FdtCreateError(fdt_ret)
| &Error::FdtFinishReservemapError(fdt_ret)
| &Error::FdtBeginNodeError(fdt_ret)
| &Error::FdtPropertyError(fdt_ret)
| &Error::FdtEndNodeError(fdt_ret)
| &Error::FdtOpenIntoError(fdt_ret)
| &Error::FdtFinishError(fdt_ret)
| &Error::FdtPackError(fdt_ret) => write!(
f,
"{} {} code: {}",
prefix,
Error::description(self),
fdt_ret
),
&Error::FdtGuestMemoryWriteError => {
write!(f, "{} {}", prefix, Error::description(self))
}
}
}
}
// Our ARM systems are little-endian whereas fdts are big-endian, so we need
// to convert integers
fn cpu_to_fdt32(input: u32) -> [u8; 4] {
let mut buf = [0; 4];
BigEndian::write_u32(&mut buf, input);
buf
}
fn cpu_to_fdt64(input: u64) -> [u8; 8] {
let mut buf = [0; 8];
BigEndian::write_u64(&mut buf, input);
buf
}
fn begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<(), Box<Error>> {
let cstr_name = CString::new(name).unwrap();
// Safe because we allocated fdt and converted name to a CString
let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtBeginNodeError(fdt_ret)));
}
Ok(())
}
fn end_node(fdt: &mut Vec<u8>) -> Result<(), Box<Error>> {
// Safe because we allocated fdt
let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtEndNodeError(fdt_ret)));
}
Ok(())
}
fn property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<(), Box<Error>> {
let cstr_name = CString::new(name).unwrap();
let val_ptr = val.as_ptr() as *const c_void;
// Safe because we allocated fdt and converted name to a CString
let fdt_ret = unsafe {
fdt_property(
fdt.as_mut_ptr() as *mut c_void,
cstr_name.as_ptr(),
val_ptr,
val.len() as i32,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
}
Ok(())
}
fn property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<(), Box<Error>> {
let value_bytes = cstr_value.to_bytes_with_nul();
let cstr_name = CString::new(name).unwrap();
// Safe because we allocated fdt, converted name and value to CStrings
let fdt_ret = unsafe {
fdt_property(
fdt.as_mut_ptr() as *mut c_void,
cstr_name.as_ptr(),
value_bytes.as_ptr() as *mut c_void,
value_bytes.len() as i32,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
}
Ok(())
}
fn property_null(fdt: &mut Vec<u8>, name: &str) -> Result<(), Box<Error>> {
let cstr_name = CString::new(name).unwrap();
// Safe because we allocated fdt, converted name to a CString
let fdt_ret = unsafe {
fdt_property(
fdt.as_mut_ptr() as *mut c_void,
cstr_name.as_ptr(),
null(),
0,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
}
Ok(())
}
fn property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<(), Box<Error>> {
let cstr_value = CString::new(value).unwrap();
property_cstring(fdt, name, &cstr_value)
}
fn property_u32(fdt: &mut Vec<u8>, name: &str, val: u32) -> Result<(), Box<Error>> {
property(fdt, name, &cpu_to_fdt32(val))
}
fn property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<(), Box<Error>> {
property(fdt, name, &cpu_to_fdt64(val))
}
// Helper to generate a properly formatted byte vector using 32-bit cells
fn generate_prop32(cells: &[u32]) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::new();
for &e in cells {
ret.extend(cpu_to_fdt32(e).into_iter());
}
ret
}
// Helper to generate a properly formatted byte vector using 64-bit cells
fn generate_prop64(cells: &[u64]) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::new();
for &e in cells {
ret.extend(cpu_to_fdt64(e).into_iter());
}
ret
}
fn create_memory_node(fdt: &mut Vec<u8>, guest_mem: &GuestMemory) -> Result<(), Box<Error>> {
let mem_size = guest_mem.memory_size();
let mem_reg_prop = generate_prop64(&[AARCH64_PHYS_MEM_START, mem_size]);
@ -487,18 +295,7 @@ pub fn create_fdt(
cmdline: &CStr,
) -> Result<(), Box<Error>> {
let mut fdt = vec![0; fdt_max_size];
// Safe since we allocated this array with fdt_max_size
let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, fdt_max_size as c_int) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtCreateError(fdt_ret)));
}
// Safe since we allocated this array
fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtFinishReservemapError(fdt_ret)));
}
start_fdt(&mut fdt, fdt_max_size)?;
// The whole thing is put into one giant node with some top level properties
begin_node(&mut fdt, "")?;
@ -519,32 +316,10 @@ pub fn create_fdt(
// End giant node
end_node(&mut fdt)?;
// Safe since we allocated fdt_final and previously passed in it's size
fdt_ret = unsafe { fdt_finish(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtFinishError(fdt_ret)));
}
// Allocate another buffer so we can format and then write fdt to guest
let mut fdt_final = vec![0; fdt_max_size];
finish_fdt(&mut fdt, &mut fdt_final, fdt_max_size)?;
// Safe because we allocated both arrays with the correct size
fdt_ret = unsafe {
fdt_open_into(
fdt.as_mut_ptr() as *mut c_void,
fdt_final.as_mut_ptr() as *mut c_void,
fdt_max_size as i32,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtOpenIntoError(fdt_ret)));
}
// Safe since we allocated fdt_final
fdt_ret = unsafe { fdt_pack(fdt_final.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPackError(fdt_ret)));
}
let fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset);
let written = guest_mem
.write_at_addr(fdt_final.as_slice(), fdt_address)

View file

@ -3,7 +3,6 @@
// found in the LICENSE file.
extern crate arch;
extern crate byteorder;
extern crate data_model;
extern crate devices;
extern crate io_jail;

View file

@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["The Chromium OS Authors"]
[dependencies]
byteorder = "*"
devices = { path = "../devices" }
io_jail = { path = "../io_jail" }
kvm = { path = "../kvm" }

249
arch/src/fdt.rs Normal file
View file

@ -0,0 +1,249 @@
// Copyright 2018 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 byteorder::{BigEndian, ByteOrder};
use libc::{c_char, c_int, c_void};
use std::error::{self, Error as FdtError};
use std::ffi::{CStr, CString};
use std::fmt;
use std::ptr::null;
// This links to libfdt which handles the creation of the binary blob
// flattened device tree (fdt) that is passed to the kernel and indicates
// the hardware configuration of the machine.
#[link(name = "fdt")]
extern "C" {
fn fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int;
fn fdt_finish_reservemap(fdt: *mut c_void) -> c_int;
fn fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int;
fn fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int)
-> c_int;
fn fdt_end_node(fdt: *mut c_void) -> c_int;
fn fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int;
fn fdt_finish(fdt: *const c_void) -> c_int;
fn fdt_pack(fdt: *mut c_void) -> c_int;
}
#[derive(Debug)]
pub enum Error {
FdtCreateError(c_int),
FdtFinishReservemapError(c_int),
FdtBeginNodeError(c_int),
FdtPropertyError(c_int),
FdtEndNodeError(c_int),
FdtOpenIntoError(c_int),
FdtFinishError(c_int),
FdtPackError(c_int),
FdtGuestMemoryWriteError,
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
&Error::FdtCreateError(_) => "Error creating FDT",
&Error::FdtFinishReservemapError(_) => "Error finishing reserve map",
&Error::FdtBeginNodeError(_) => "Error beginning FDT node",
&Error::FdtPropertyError(_) => "Error adding FDT property",
&Error::FdtEndNodeError(_) => "Error ending FDT node",
&Error::FdtOpenIntoError(_) => "Error copying FDT to Guest",
&Error::FdtFinishError(_) => "Error performing FDT finish",
&Error::FdtPackError(_) => "Error packing FDT",
&Error::FdtGuestMemoryWriteError => "Error writing FDT to Guest Memory",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prefix = "Libfdt Error: ";
match self {
&Error::FdtCreateError(fdt_ret)
| &Error::FdtFinishReservemapError(fdt_ret)
| &Error::FdtBeginNodeError(fdt_ret)
| &Error::FdtPropertyError(fdt_ret)
| &Error::FdtEndNodeError(fdt_ret)
| &Error::FdtOpenIntoError(fdt_ret)
| &Error::FdtFinishError(fdt_ret)
| &Error::FdtPackError(fdt_ret) => write!(
f,
"{} {} code: {}",
prefix,
Error::description(self),
fdt_ret
),
&Error::FdtGuestMemoryWriteError => {
write!(f, "{} {}", prefix, Error::description(self))
}
}
}
}
pub fn begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<(), Box<Error>> {
let cstr_name = CString::new(name).unwrap();
// Safe because we allocated fdt and converted name to a CString
let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtBeginNodeError(fdt_ret)));
}
Ok(())
}
pub fn end_node(fdt: &mut Vec<u8>) -> Result<(), Box<Error>> {
// Safe because we allocated fdt
let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtEndNodeError(fdt_ret)));
}
Ok(())
}
pub fn property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<(), Box<Error>> {
let cstr_name = CString::new(name).unwrap();
let val_ptr = val.as_ptr() as *const c_void;
// Safe because we allocated fdt and converted name to a CString
let fdt_ret = unsafe {
fdt_property(
fdt.as_mut_ptr() as *mut c_void,
cstr_name.as_ptr(),
val_ptr,
val.len() as i32,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
}
Ok(())
}
fn cpu_to_fdt32(input: u32) -> [u8; 4] {
let mut buf = [0; 4];
BigEndian::write_u32(&mut buf, input);
buf
}
fn cpu_to_fdt64(input: u64) -> [u8; 8] {
let mut buf = [0; 8];
BigEndian::write_u64(&mut buf, input);
buf
}
pub fn property_u32(fdt: &mut Vec<u8>, name: &str, val: u32) -> Result<(), Box<Error>> {
property(fdt, name, &cpu_to_fdt32(val))
}
pub fn property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<(), Box<Error>> {
property(fdt, name, &cpu_to_fdt64(val))
}
// Helper to generate a properly formatted byte vector using 32-bit cells
pub fn generate_prop32(cells: &[u32]) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::new();
for &e in cells {
ret.extend(cpu_to_fdt32(e).into_iter());
}
ret
}
// Helper to generate a properly formatted byte vector using 64-bit cells
pub fn generate_prop64(cells: &[u64]) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::new();
for &e in cells {
ret.extend(cpu_to_fdt64(e).into_iter());
}
ret
}
pub fn property_null(fdt: &mut Vec<u8>, name: &str) -> Result<(), Box<Error>> {
let cstr_name = CString::new(name).unwrap();
// Safe because we allocated fdt, converted name to a CString
let fdt_ret = unsafe {
fdt_property(
fdt.as_mut_ptr() as *mut c_void,
cstr_name.as_ptr(),
null(),
0,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
}
Ok(())
}
pub fn property_cstring(
fdt: &mut Vec<u8>,
name: &str,
cstr_value: &CStr,
) -> Result<(), Box<Error>> {
let value_bytes = cstr_value.to_bytes_with_nul();
let cstr_name = CString::new(name).unwrap();
// Safe because we allocated fdt, converted name and value to CStrings
let fdt_ret = unsafe {
fdt_property(
fdt.as_mut_ptr() as *mut c_void,
cstr_name.as_ptr(),
value_bytes.as_ptr() as *mut c_void,
value_bytes.len() as i32,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
}
Ok(())
}
pub fn property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<(), Box<Error>> {
let cstr_value = CString::new(value).unwrap();
property_cstring(fdt, name, &cstr_value)
}
pub fn start_fdt(fdt: &mut Vec<u8>, fdt_max_size: usize) -> Result<(), Box<Error>> {
// Safe since we allocated this array with fdt_max_size
let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, fdt_max_size as c_int) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtCreateError(fdt_ret)));
}
// Safe since we allocated this array
fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtFinishReservemapError(fdt_ret)));
}
Ok(())
}
pub fn finish_fdt(
fdt: &mut Vec<u8>,
fdt_final: &mut Vec<u8>,
fdt_max_size: usize,
) -> Result<(), Box<Error>> {
// Safe since we allocated fdt_final and previously passed in it's size
let mut fdt_ret = unsafe { fdt_finish(fdt.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtFinishError(fdt_ret)));
}
// Safe because we allocated both arrays with the correct size
fdt_ret = unsafe {
fdt_open_into(
fdt.as_mut_ptr() as *mut c_void,
fdt_final.as_mut_ptr() as *mut c_void,
fdt_max_size as i32,
)
};
if fdt_ret != 0 {
return Err(Box::new(Error::FdtOpenIntoError(fdt_ret)));
}
// Safe since we allocated fdt_final
fdt_ret = unsafe { fdt_pack(fdt_final.as_mut_ptr() as *mut c_void) };
if fdt_ret != 0 {
return Err(Box::new(Error::FdtPackError(fdt_ret)));
}
Ok(())
}

View file

@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod fdt;
extern crate byteorder;
extern crate devices;
extern crate io_jail;
extern crate kernel_cmdline;
@ -38,6 +41,7 @@ pub struct VmComponents {
pub memory_mb: u64,
pub vcpu_count: u32,
pub kernel_image: File,
pub android_fstab: Option<File>,
pub extra_kernel_params: Vec<String>,
pub wayland_dmabuf: bool,
}

View file

@ -76,7 +76,7 @@ pub fn load_kernel<F>(
guest_mem: &GuestMemory,
kernel_start: GuestAddress,
kernel_image: &mut F,
) -> Result<()>
) -> Result<u64>
where
F: Read + Seek,
{
@ -117,6 +117,8 @@ where
.map_err(|_| Error::ReadProgramHeader)?
};
let mut kernel_end = 0;
// Read in each section pointed to by the program headers.
for phdr in &phdrs {
if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
@ -133,9 +135,11 @@ where
guest_mem
.read_to_memory(mem_offset, kernel_image, phdr.p_filesz as usize)
.map_err(|_| Error::ReadKernelImage)?;
kernel_end = mem_offset.offset() + phdr.p_memsz;
}
Ok(())
Ok(kernel_end)
}
/// Writes the command line string to the given memory slice.
@ -236,7 +240,7 @@ mod test {
let kernel_addr = GuestAddress(0x0);
let image = make_elf_bin();
assert_eq!(
Ok(()),
Ok(16613),
load_kernel(&gm, kernel_addr, &mut Cursor::new(&image))
);
}

View file

@ -68,6 +68,7 @@ pub enum Error {
InvalidWaylandPath,
NetDeviceNew(devices::virtio::NetError),
NoVarEmpty,
OpenAndroidFstab(PathBuf, io::Error),
OpenKernel(PathBuf, io::Error),
P9DeviceNew(devices::virtio::P9Error),
PollContextAdd(sys_util::Error),
@ -124,6 +125,9 @@ impl fmt::Display for Error {
Error::NetDeviceNew(e) => write!(f, "failed to set up virtio networking: {:?}", e),
Error::NoVarEmpty => write!(f, "/var/empty doesn't exist, can't jail devices."),
Error::OpenKernel(p, e) => write!(f, "failed to open kernel image {:?}: {}", p, e),
Error::OpenAndroidFstab(ref p, ref e) => {
write!(f, "failed to open android fstab file {:?}: {}", p, e)
}
Error::P9DeviceNew(e) => write!(f, "failed to create 9p device: {}", e),
Error::PollContextAdd(e) => write!(f, "failed to add fd to poll context: {:?}", e),
Error::PollContextDelete(e) => {
@ -851,6 +855,13 @@ pub fn run_config(cfg: Config) -> Result<()> {
vcpu_count: cfg.vcpu_count.unwrap_or(1),
kernel_image: File::open(cfg.kernel_path.as_path())
.map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?,
android_fstab: cfg
.android_fstab
.as_ref()
.map(|x| {
File::open(x.as_path()).map_err(|e| Error::OpenAndroidFstab(x.to_path_buf(), e))
})
.map_or(Ok(None), |v| v.map(Some))?,
extra_kernel_params: cfg.params.clone(),
wayland_dmabuf: cfg.wayland_dmabuf,
};

View file

@ -76,6 +76,7 @@ pub struct Config {
vcpu_count: Option<u32>,
memory: Option<usize>,
kernel_path: PathBuf,
android_fstab: Option<PathBuf>,
params: Vec<String>,
socket_path: Option<PathBuf>,
plugin: Option<PathBuf>,
@ -104,6 +105,7 @@ impl Default for Config {
vcpu_count: None,
memory: None,
kernel_path: PathBuf::default(),
android_fstab: None,
params: Vec::new(),
socket_path: None,
plugin: None,
@ -178,6 +180,24 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
cfg.kernel_path = kernel_path;
}
}
"android-fstab" => {
if cfg.android_fstab.is_some()
&& !cfg.android_fstab.as_ref().unwrap().as_os_str().is_empty()
{
return Err(argument::Error::TooManyArguments(
"expected exactly one android fstab path".to_owned(),
));
} else {
let android_fstab = PathBuf::from(value.unwrap());
if !android_fstab.exists() {
return Err(argument::Error::InvalidValue {
value: value.unwrap().to_owned(),
expected: "this android fstab path does not exist",
});
}
cfg.android_fstab = Some(android_fstab);
}
}
"params" => {
cfg.params.push(value.unwrap().to_owned());
}
@ -481,6 +501,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
let arguments =
&[Argument::positional("KERNEL", "bzImage of kernel to run"),
Argument::value("android-fstab", "PATH", "Path to Android fstab"),
Argument::short_value('p',
"params",
"PARAMS",

91
x86_64/src/fdt.rs Normal file
View file

@ -0,0 +1,91 @@
// Copyright 2018 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.
extern crate arch;
use arch::fdt::{begin_node, end_node, finish_fdt, property_string, start_fdt, Error};
use bootparam::setup_data;
use bootparam::SETUP_DTB;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::mem;
use sys_util::{GuestAddress, GuestMemory};
use X86_64_FDT_MAX_SIZE;
/// Creates a flattened device tree containing all of the parameters for the
/// kernel and loads it into the guest memory at the specified offset.
///
/// # Arguments
///
/// * `fdt_max_size` - The amount of space reserved for the device tree
/// * `guest_mem` - The guest memory object
/// * `fdt_load_offset` - The offset into physical memory for the device tree
/// * `android_fstab` - the File object for the android fstab
pub fn create_fdt(
fdt_max_size: usize,
guest_mem: &GuestMemory,
fdt_load_offset: u64,
android_fstab: &mut File,
) -> Result<(), Box<Error>> {
// Reserve space for the setup_data
let fdt_data_size = fdt_max_size - mem::size_of::<setup_data>();
let mut fdt = vec![0; fdt_data_size];
start_fdt(&mut fdt, fdt_data_size)?;
// The whole thing is put into one giant node with some top level properties
begin_node(&mut fdt, "")?;
begin_node(&mut fdt, "firmware")?;
begin_node(&mut fdt, "android")?;
property_string(&mut fdt, "compatible", "android,firmware")?;
begin_node(&mut fdt, "fstab")?;
property_string(&mut fdt, "compatible", "android,fstab")?;
let file = BufReader::new(android_fstab);
for line in file.lines().filter_map(|l| l.ok()) {
let vec = line.split(" ").collect::<Vec<&str>>();
assert_eq!(vec.len(), 5);
let partition = &vec[1][1..];
begin_node(&mut fdt, partition)?;
property_string(&mut fdt, "compatible", &("android,".to_owned() + partition))?;
property_string(&mut fdt, "dev", vec[0])?;
property_string(&mut fdt, "type", vec[2])?;
property_string(&mut fdt, "mnt_flags", vec[3])?;
property_string(&mut fdt, "fsmgr_flags", vec[4])?;
end_node(&mut fdt)?;
}
end_node(&mut fdt)?;
end_node(&mut fdt)?;
end_node(&mut fdt)?;
end_node(&mut fdt)?;
// Allocate another buffer so we can format and then write fdt to guest
let mut fdt_final = vec![0; fdt_data_size];
finish_fdt(&mut fdt, &mut fdt_final, fdt_data_size)?;
let mut hdr: setup_data = Default::default();
hdr.next = 0;
hdr.type_ = SETUP_DTB;
hdr.len = fdt_data_size as u32;
assert!(fdt_data_size as u64 <= X86_64_FDT_MAX_SIZE);
let fdt_address = GuestAddress(fdt_load_offset);
guest_mem
.checked_offset(fdt_address, fdt_data_size as u64)
.ok_or(Error::FdtGuestMemoryWriteError)?;
guest_mem
.write_obj_at_addr(hdr, fdt_address)
.map_err(|_| Error::FdtGuestMemoryWriteError)?;
let fdt_data_address = GuestAddress(fdt_load_offset + mem::size_of::<setup_data>() as u64);
let written = guest_mem
.write_at_addr(fdt_final.as_slice(), fdt_data_address)
.map_err(|_| Error::FdtGuestMemoryWriteError)?;
if written < fdt_data_size {
return Err(Box::new(Error::FdtGuestMemoryWriteError));
}
Ok(())
}

View file

@ -16,6 +16,10 @@ extern crate resources;
extern crate sync;
extern crate sys_util;
mod fdt;
const X86_64_FDT_MAX_SIZE: u64 = 0x200000;
#[allow(dead_code)]
#[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)]
@ -36,6 +40,7 @@ impl Clone for bootparam::boot_params {
}
// boot_params is just a series of ints, it is safe to initialize it.
unsafe impl data_model::DataInit for bootparam::boot_params {}
unsafe impl data_model::DataInit for bootparam::setup_data {}
#[allow(dead_code)]
#[allow(non_upper_case_globals)]
@ -159,11 +164,14 @@ const X86_64_IRQ_BASE: u32 = 5;
fn configure_system(
guest_mem: &GuestMemory,
_mem_size: u64,
kernel_addr: GuestAddress,
cmdline_addr: GuestAddress,
cmdline_size: usize,
num_cpus: u8,
pci_irqs: Vec<(u32, PciInterruptPin)>,
android_fstab: &mut Option<File>,
kernel_end: u64,
) -> Result<()> {
const EBDA_START: u64 = 0x0009fc00;
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
@ -178,12 +186,18 @@ fn configure_system(
let mut params: boot_params = Default::default();
let kernel_end_aligned = (((kernel_end + 64 - 1) / 64) * 64) + 64;
let dtb_start = GuestAddress(kernel_end_aligned);
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
params.hdr.header = KERNEL_HDR_MAGIC;
params.hdr.cmd_line_ptr = cmdline_addr.offset() as u32;
params.hdr.cmdline_size = cmdline_size as u32;
params.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
if android_fstab.is_some() {
params.hdr.setup_data = dtb_start.offset();
}
add_e820_entry(&mut params, 0, EBDA_START, E820_RAM)?;
@ -220,6 +234,14 @@ fn configure_system(
.write_obj_at_addr(params, zero_page_addr)
.map_err(|_| Error::ZeroPageSetup)?;
if let Some(fstab) = android_fstab {
fdt::create_fdt(
X86_64_FDT_MAX_SIZE as usize,
guest_mem,
dtb_start.offset(),
fstab,
)?;
}
Ok(())
}
@ -317,13 +339,16 @@ impl arch::LinuxArch for X8664arch {
// separate out load_kernel from other setup to get a specific error for
// kernel loading
Self::load_kernel(&mem, &mut components.kernel_image)?;
let kernel_end = Self::load_kernel(&mem, &mut components.kernel_image)?;
Self::setup_system_memory(
&mem,
components.memory_mb,
vcpu_count,
&CString::new(cmdline).unwrap(),
pci_irqs,
components.android_fstab,
kernel_end,
)?;
Ok(RunnableLinuxVm {
@ -348,9 +373,12 @@ impl X8664arch {
///
/// * `mem` - The memory to be used by the guest.
/// * `kernel_image` - the File object for the specified kernel.
fn load_kernel(mem: &GuestMemory, mut kernel_image: &mut File) -> Result<()> {
kernel_loader::load_kernel(mem, GuestAddress(KERNEL_START_OFFSET), &mut kernel_image)?;
Ok(())
fn load_kernel(mem: &GuestMemory, mut kernel_image: &mut File) -> Result<u64> {
Ok(kernel_loader::load_kernel(
mem,
GuestAddress(KERNEL_START_OFFSET),
&mut kernel_image,
)?)
}
/// Configures the system memory space should be called once per vm before
@ -363,19 +391,24 @@ impl X8664arch {
/// * `cmdline` - the kernel commandline
fn setup_system_memory(
mem: &GuestMemory,
_mem_size: u64,
mem_size: u64,
vcpu_count: u32,
cmdline: &CStr,
pci_irqs: Vec<(u32, PciInterruptPin)>,
mut android_fstab: Option<File>,
kernel_end: u64,
) -> Result<()> {
kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)?;
configure_system(
mem,
mem_size,
GuestAddress(KERNEL_START_OFFSET),
GuestAddress(CMDLINE_OFFSET),
cmdline.to_bytes().len() + 1,
vcpu_count as u8,
pci_irqs,
&mut android_fstab,
kernel_end,
)?;
Ok(())
}