summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--aarch64/Cargo.toml1
-rw-r--r--aarch64/src/fdt.rs239
-rw-r--r--aarch64/src/lib.rs1
-rw-r--r--arch/Cargo.toml1
-rw-r--r--arch/src/fdt.rs249
-rw-r--r--arch/src/lib.rs4
-rw-r--r--kernel_loader/src/lib.rs10
-rw-r--r--src/linux.rs11
-rw-r--r--src/main.rs21
-rw-r--r--x86_64/src/fdt.rs91
-rw-r--r--x86_64/src/lib.rs43
12 files changed, 430 insertions, 243 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 741f4e8..650354f 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 ab5bf55..c623545 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 e6cfa0d..0780a55 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<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)
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index d579a17..48effe5 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 cc3858d..59d2b17 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 0000000..20a017b
--- /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<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(())
+}
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index bc326eb..795591a 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<File>,
     pub extra_kernel_params: Vec<String>,
     pub wayland_dmabuf: bool,
 }
diff --git a/kernel_loader/src/lib.rs b/kernel_loader/src/lib.rs
index b0fdf66..ecc5a34 100644
--- a/kernel_loader/src/lib.rs
+++ b/kernel_loader/src/lib.rs
@@ -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))
         );
     }
diff --git a/src/linux.rs b/src/linux.rs
index a61392b..fb327ec 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 5847087..9392360 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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",
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(())
     }