From 4133b0120d1e16cafbb373b2ae17a214b594038b Mon Sep 17 00:00:00 2001 From: Tristan Muntsinger Date: Fri, 21 Dec 2018 16:01:56 -0800 Subject: [PATCH] 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 Tested-by: kokoro Reviewed-by: Dylan Reid --- Cargo.lock | 2 +- aarch64/Cargo.toml | 1 - aarch64/src/fdt.rs | 239 ++----------------------------------- aarch64/src/lib.rs | 1 - arch/Cargo.toml | 1 + arch/src/fdt.rs | 249 +++++++++++++++++++++++++++++++++++++++ arch/src/lib.rs | 4 + kernel_loader/src/lib.rs | 10 +- src/linux.rs | 11 ++ src/main.rs | 21 ++++ x86_64/src/fdt.rs | 91 ++++++++++++++ x86_64/src/lib.rs | 43 ++++++- 12 files changed, 430 insertions(+), 243 deletions(-) create mode 100644 arch/src/fdt.rs create mode 100644 x86_64/src/fdt.rs diff --git a/Cargo.lock b/Cargo.lock index 741f4e80e5..650354ff7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/aarch64/Cargo.toml b/aarch64/Cargo.toml index ab5bf557bb..c6235458d1 100644 --- a/aarch64/Cargo.toml +++ b/aarch64/Cargo.toml @@ -15,4 +15,3 @@ sys_util = { path = "../sys_util" } resources = { path = "../resources" } sync = { path = "../sync" } libc = "*" -byteorder = "*" diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs index e6cfa0d397..0780a55c65 100644 --- a/aarch64/src/fdt.rs +++ b/aarch64/src/fdt.rs @@ -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, name: &str) -> Result<(), Box> { - 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) -> Result<(), Box> { - // 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, name: &str, val: &[u8]) -> Result<(), Box> { - 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, name: &str, cstr_value: &CStr) -> Result<(), Box> { - 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, name: &str) -> Result<(), Box> { - 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, name: &str, value: &str) -> Result<(), Box> { - let cstr_value = CString::new(value).unwrap(); - property_cstring(fdt, name, &cstr_value) -} - -fn property_u32(fdt: &mut Vec, name: &str, val: u32) -> Result<(), Box> { - property(fdt, name, &cpu_to_fdt32(val)) -} - -fn property_u64(fdt: &mut Vec, name: &str, val: u64) -> Result<(), Box> { - 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 { - let mut ret: Vec = 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 { - let mut ret: Vec = Vec::new(); - for &e in cells { - ret.extend(cpu_to_fdt64(e).into_iter()); - } - ret -} - fn create_memory_node(fdt: &mut Vec, guest_mem: &GuestMemory) -> Result<(), Box> { 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> { 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) diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index d579a173fb..48effe5f6a 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -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; diff --git a/arch/Cargo.toml b/arch/Cargo.toml index cc3858d86b..59d2b1742a 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -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" } diff --git a/arch/src/fdt.rs b/arch/src/fdt.rs new file mode 100644 index 0000000000..20a017b5be --- /dev/null +++ b/arch/src/fdt.rs @@ -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, name: &str) -> Result<(), Box> { + 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) -> Result<(), Box> { + // 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, name: &str, val: &[u8]) -> Result<(), Box> { + 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, name: &str, val: u32) -> Result<(), Box> { + property(fdt, name, &cpu_to_fdt32(val)) +} + +pub fn property_u64(fdt: &mut Vec, name: &str, val: u64) -> Result<(), Box> { + 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 { + let mut ret: Vec = 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 { + let mut ret: Vec = Vec::new(); + for &e in cells { + ret.extend(cpu_to_fdt64(e).into_iter()); + } + ret +} + +pub fn property_null(fdt: &mut Vec, name: &str) -> Result<(), Box> { + 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, + name: &str, + cstr_value: &CStr, +) -> Result<(), Box> { + 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, name: &str, value: &str) -> Result<(), Box> { + let cstr_value = CString::new(value).unwrap(); + property_cstring(fdt, name, &cstr_value) +} + +pub fn start_fdt(fdt: &mut Vec, fdt_max_size: usize) -> Result<(), Box> { + // 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, + fdt_final: &mut Vec, + fdt_max_size: usize, +) -> Result<(), Box> { + // 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(()) +} diff --git a/arch/src/lib.rs b/arch/src/lib.rs index bc326eb036..795591ae27 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -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, pub extra_kernel_params: Vec, pub wayland_dmabuf: bool, } diff --git a/kernel_loader/src/lib.rs b/kernel_loader/src/lib.rs index b0fdf663ff..ecc5a34be9 100644 --- a/kernel_loader/src/lib.rs +++ b/kernel_loader/src/lib.rs @@ -76,7 +76,7 @@ pub fn load_kernel( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, -) -> Result<()> +) -> Result 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)) ); } diff --git a/src/linux.rs b/src/linux.rs index a61392bf6f..fb327ec88d 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -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, }; diff --git a/src/main.rs b/src/main.rs index 5847087b7a..939236018d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,6 +76,7 @@ pub struct Config { vcpu_count: Option, memory: Option, kernel_path: PathBuf, + android_fstab: Option, params: Vec, socket_path: Option, plugin: Option, @@ -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", diff --git a/x86_64/src/fdt.rs b/x86_64/src/fdt.rs new file mode 100644 index 0000000000..afbb602e0c --- /dev/null +++ b/x86_64/src/fdt.rs @@ -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> { + // Reserve space for the setup_data + let fdt_data_size = fdt_max_size - mem::size_of::(); + + 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::>(); + 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::() 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(()) +} diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index 2857bb7515..ed5637da39 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -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, + 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 { + 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, + 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(()) }