summary refs log tree commit diff
path: root/x86_64
diff options
context:
space:
mode:
authorTristan Muntsinger <muntsinger@google.com>2018-12-21 16:01:56 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-01-28 14:17:10 -0800
commit4133b0120d1e16cafbb373b2ae17a214b594038b (patch)
tree177506988846e4b86e639cdb963bbaba0c4e6ca9 /x86_64
parentf052cfefc8d6d27fa068c34190615db1819b8fef (diff)
downloadcrosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.tar
crosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.tar.gz
crosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.tar.bz2
crosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.tar.lz
crosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.tar.xz
crosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.tar.zst
crosvm-4133b0120d1e16cafbb373b2ae17a214b594038b.zip
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>
Diffstat (limited to 'x86_64')
-rw-r--r--x86_64/src/fdt.rs91
-rw-r--r--x86_64/src/lib.rs43
2 files changed, 129 insertions, 5 deletions
diff --git a/x86_64/src/fdt.rs b/x86_64/src/fdt.rs
new file mode 100644
index 0000000..afbb602
--- /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<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(())
+}
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 2857bb7..ed5637d 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<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(())
     }