summary refs log tree commit diff
diff options
context:
space:
mode:
authorSonny Rao <sonnyrao@chromium.org>2018-02-13 22:09:43 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-03-15 17:58:33 -0700
commited517d1bfe028564076ee45bd9412882c85ebbe7 (patch)
tree2a1c2ef008f7c8c8b9873bac3ffc39fdc31741de
parent8f73ccc45d0db10c7769f046fd99889cd9a8230d (diff)
downloadcrosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.tar
crosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.tar.gz
crosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.tar.bz2
crosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.tar.lz
crosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.tar.xz
crosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.tar.zst
crosvm-ed517d1bfe028564076ee45bd9412882c85ebbe7.zip
crosvm: create a LinuxArch trait and use it for x86
This creates a trait that different architectures can implement to
support running Linux VMs.

In the implementation on X86 we remove some error and return errors
from lower-level modules as appropriate.  These modules now implement
the Error trait so we can get meaningful descriptions without an extra
error from the calling function. This still keeps all the ifdefs in
linux.rs for now until we have another implementation to use for ARM.

BUG=chromium:797868
TEST=./build_test passes on all architectures
TEST=crosvm runs on caroline

Change-Id: If24bcc83e25f9127d6aea68f9272e639296aad8b
Reviewed-on: https://chromium-review.googlesource.com/952368
Commit-Ready: Sonny Rao <sonnyrao@chromium.org>
Tested-by: Sonny Rao <sonnyrao@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r--Cargo.lock14
-rw-r--r--Cargo.toml1
-rw-r--r--arch/Cargo.toml12
-rw-r--r--arch/src/lib.rs114
-rw-r--r--src/linux.rs102
-rw-r--r--src/main.rs1
-rw-r--r--x86_64/Cargo.toml1
-rw-r--r--x86_64/src/lib.rs453
8 files changed, 421 insertions, 277 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9e15b85..a911969 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,4 +1,16 @@
 [[package]]
+name = "arch"
+version = "0.1.0"
+dependencies = [
+ "device_manager 0.1.0",
+ "devices 0.1.0",
+ "kernel_cmdline 0.1.0",
+ "kvm 0.1.0",
+ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sys_util 0.1.0",
+]
+
+[[package]]
 name = "bitflags"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -17,6 +29,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 name = "crosvm"
 version = "0.1.0"
 dependencies = [
+ "arch 0.1.0",
  "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "crosvm_plugin 0.12.0",
  "data_model 0.1.0",
@@ -289,6 +302,7 @@ dependencies = [
 name = "x86_64"
 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",
  "device_manager 0.1.0",
diff --git a/Cargo.toml b/Cargo.toml
index c0d1a1e..e720a57 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ panic = 'abort'
 plugin = ["plugin_proto", "crosvm_plugin", "protobuf"]
 
 [dependencies]
+arch = { path = "arch" }
 devices = { path = "devices" }
 device_manager = { path = "device_manager" }
 io_jail = { path = "io_jail" }
diff --git a/arch/Cargo.toml b/arch/Cargo.toml
new file mode 100644
index 0000000..2b2aa38
--- /dev/null
+++ b/arch/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "arch"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+
+[dependencies]
+devices = { path = "../devices" }
+device_manager = { path = "../device_manager" }
+kvm = { path = "../kvm" }
+sys_util = { path = "../sys_util" }
+kernel_cmdline = { path = "../kernel_cmdline" }
+libc = "*"
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
new file mode 100644
index 0000000..1fece03
--- /dev/null
+++ b/arch/src/lib.rs
@@ -0,0 +1,114 @@
+// 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 sys_util;
+extern crate kernel_cmdline;
+extern crate kvm;
+extern crate libc;
+extern crate device_manager;
+extern crate devices;
+
+use std::ffi::CStr;
+use std::fs::File;
+use std::result;
+use std::sync::{Arc, Mutex};
+
+use kvm::{Kvm, Vm, Vcpu};
+use sys_util::{EventFd, GuestMemory};
+
+pub type Result<T> = result::Result<T, Box<std::error::Error>>;
+
+/// Trait which is implemented for each Linux Architecture in order to
+/// set up the memory, cpus, and system devices and to boot the kernel.
+pub trait LinuxArch {
+    /// Loads the kernel from an open file.
+    ///
+    /// # Arguments
+    ///
+    /// * `mem` - The memory to be used by the guest.
+    /// * `kernel_image` - the File object for the specified kernel.
+    fn load_kernel(mem: &GuestMemory, kernel_image: &mut File) -> Result<()>;
+
+    /// Configures the system memory space should be called once per vm before
+    /// starting vcpu threads.
+    ///
+    /// # Arguments
+    ///
+    /// * `mem` - The memory to be used by the guest
+    /// * `mem_size` - The size in bytes of system memory
+    /// * `vcpu_count` - Number of virtual CPUs the guest will have
+    /// * `cmdline` - the kernel commandline
+    fn setup_system_memory(mem: &GuestMemory,
+                           mem_size: u64,
+                           vcpu_count: u32,
+                           cmdline: &CStr) -> Result<()>;
+
+    /// Creates a new VM object and initializes architecture specific devices
+    ///
+    /// # Arguments
+    ///
+    /// * `kvm` - The opened /dev/kvm object.
+    /// * `mem` - The memory to be used by the guest.
+    fn create_vm(kvm: &Kvm, mem: GuestMemory) -> Result<Vm>;
+
+    /// This creates a GuestMemory object for this VM
+    ///
+    /// * `mem_size` - Desired physical memory size in bytes for this VM
+    fn setup_memory(mem_size: u64) -> Result<GuestMemory>;
+
+    /// The creates the interrupt controller device and optionally returns the fd for it.
+    /// Some architectures may not have a separate descriptor for the interrupt
+    /// controller, so they would return None even on success.
+    ///
+    /// # Arguments
+    ///
+    /// * `vm` - the vm object
+    fn create_irq_chip(vm: &kvm::Vm) -> Result<Option<File>>;
+
+    /// This returns the first page frame number for use by the balloon driver.
+    ///
+    /// # Arguments
+    ///
+    /// * `mem_size` - the size in bytes of physical ram for the guest
+    fn get_base_dev_pfn(mem_size: u64) -> u64;
+
+    /// This returns a minimal kernel command for this architecture.
+    fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline;
+
+    /// This creates and returns a device_manager object for this vm.
+    ///
+    /// # Arguments
+    ///
+    /// * `vm` - the vm object
+    /// * `mem` - A copy of the GuestMemory object for this VM.
+    fn get_device_manager(vm: &mut Vm, mem: GuestMemory)
+                          -> Result<device_manager::DeviceManager>;
+
+    /// Sets up the IO bus for this platform
+    ///
+    /// # Arguments
+    ///
+    /// * - `vm` the vm object
+    /// * - `exit_evt` - the event fd object which should receive exit events
+    fn setup_io_bus(vm: &mut Vm, exit_evt: EventFd)
+                    -> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)>;
+
+    /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
+    ///
+    /// # Arguments
+    ///
+    /// * `guest_mem` - The memory to be used by the guest.
+    /// * `kernel_load_offset` - Offset in bytes from `guest_mem` at which the
+    ///                          kernel starts.
+    /// * `kvm` - The /dev/kvm object that created vcpu.
+    /// * `vcpu` - The VCPU object to configure.
+    /// * `cpu_id` - The id of the given `vcpu`.
+    /// * `num_cpus` - Number of virtual CPUs the guest will have.
+    fn configure_vcpu(guest_mem: &GuestMemory,
+                      kvm: &Kvm,
+                      vcpu: &Vcpu,
+                      cpu_id: u64,
+                      num_cpus: u64)
+                      -> Result<()>;
+}
diff --git a/src/linux.rs b/src/linux.rs
index 4c14c68..e599740 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -5,8 +5,11 @@
 use std;
 use std::ffi::{CString, CStr};
 use std::fmt;
+use std::error;
 use std::fs::{File, OpenOptions, remove_file};
-use std::io::{self, stdin, stdout};
+use std::io::{self, stdin};
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+use std::io::stdout;
 use std::os::unix::net::UnixDatagram;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicBool, Ordering};
@@ -32,7 +35,10 @@ use Config;
 use DiskType;
 
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-use x86_64;
+use arch::LinuxArch;
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use x86_64::X8664arch as Arch;
 
 pub enum Error {
     BalloonDeviceNew(devices::virtio::BalloonError),
@@ -41,14 +47,13 @@ pub enum Error {
     CloneEventFd(sys_util::Error),
     Cmdline(kernel_cmdline::Error),
     CreateEventFd(sys_util::Error),
-    CreateGuestMemory(sys_util::GuestMemoryError),
-    CreateIrqChip(sys_util::Error),
+    CreateGuestMemory(Box<error::Error>),
+    CreateIrqChip(Box<error::Error>),
     CreateKvm(sys_util::Error),
-    CreatePit(sys_util::Error),
     CreateSignalFd(sys_util::SignalFdError),
     CreateSocket(io::Error),
     CreateVcpu(sys_util::Error),
-    CreateVm(sys_util::Error),
+    CreateVm(Box<error::Error>),
     DeviceJail(io_jail::Error),
     DevicePivotRoot(io_jail::Error),
     Disk(io::Error),
@@ -76,13 +81,15 @@ pub enum Error {
     WaylandDeviceNew(sys_util::Error),
     WaylandTempDir(sys_util::Error),
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    SetupSystemMemory(x86_64::Error),
+    SetupSystemMemory(Box<error::Error>),
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    ConfigureVcpu(x86_64::Error),
+    ConfigureVcpu(Box<error::Error>),
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    LoadKernel(x86_64::Error),
+    LoadKernel(Box<error::Error>),
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    SetupIoBus(x86_64::Error),
+    SetupIoBus(Box<error::Error>),
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    SetupMMIOBus(Box<error::Error>),
 }
 
 impl fmt::Display for Error {
@@ -101,7 +108,6 @@ impl fmt::Display for Error {
                 write!(f, "failed to create in-kernel IRQ chip: {:?}", e)
             }
             &Error::CreateKvm(ref e) => write!(f, "failed to open /dev/kvm: {:?}", e),
-            &Error::CreatePit(ref e) => write!(f, "failed to create in-kernel PIT: {:?}", e),
             &Error::CreateSignalFd(ref e) => write!(f, "failed to create signalfd: {:?}", e),
             &Error::CreateSocket(ref e) => write!(f, "failed to create socket: {}", e),
             &Error::CreateVcpu(ref e) => write!(f, "failed to create VCPU: {:?}", e),
@@ -151,14 +157,15 @@ impl fmt::Display for Error {
                 write!(f, "failed to create wayland device jail directroy: {:?}", e)
             }
             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-            &Error::SetupSystemMemory(ref e) => write!(f, "error setting up system memory: {:?}", e),
+            &Error::SetupSystemMemory(ref e) => write!(f, "error setting up system memory: {}", e),
             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-            &Error::ConfigureVcpu(ref e) => write!(f, "failed to configure vcpu: {:?}", e),
+            &Error::ConfigureVcpu(ref e) => write!(f, "failed to configure vcpu: {}", e),
             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-            &Error::LoadKernel(ref e) => write!(f, "failed to load kernel: {:?}", e),
+            &Error::LoadKernel(ref e) => write!(f, "failed to load kernel: {}", e),
             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-            &Error::SetupIoBus(ref e) => write!(f, "failed to setup iobus: {:?}", e),
-
+            &Error::SetupIoBus(ref e) => write!(f, "failed to setup iobus: {}", e),
+            #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+            &Error::SetupMMIOBus(ref e) => write!(f, "failed to setup mmio bus: {}", e),
         }
     }
 }
@@ -220,9 +227,12 @@ fn setup_mmio_bus(cfg: &Config,
                   -> Result<devices::Bus> {
     static DEFAULT_PIVOT_ROOT: &'static str = "/var/empty";
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    let mut device_manager = x86_64::get_device_manager(vm, mem.clone());
+    let mut device_manager = Arch::get_device_manager(vm, mem.clone()).
+        map_err(|e| Error::SetupMMIOBus(e))?;
     #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
-    let mut device_manager = device_manager::DeviceManager::new(vm, mem.clone(), 0, 0, 0);
+    let mut device_manager = device_manager::DeviceManager::new(vm,
+                                                                mem.clone(),
+                                                                0, 0, 0);
 
     // An empty directory for jailed device's pivot root.
     let empty_root_path = Path::new(DEFAULT_PIVOT_ROOT);
@@ -416,12 +426,8 @@ fn setup_vcpu(kvm: &Kvm,
     let vcpu = Vcpu::new(cpu_id as libc::c_ulong, &kvm, &vm)
         .map_err(Error::CreateVcpu)?;
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    x86_64::configure_vcpu(vm.get_memory(),
-                           &kvm,
-                           &vcpu,
-                           cpu_id as u64,
-                           vcpu_count as u64)
-        .map_err(Error::ConfigureVcpu)?;
+    Arch::configure_vcpu(vm.get_memory(), &kvm, &vcpu, cpu_id as u64, vcpu_count as u64).
+        map_err(Error::ConfigureVcpu)?;
     Ok(vcpu)
 }
 
@@ -494,7 +500,8 @@ fn run_control(vm: &mut Vm,
                sigchld_fd: SignalFd,
                kill_signaled: Arc<AtomicBool>,
                vcpu_handles: Vec<JoinHandle<()>>,
-               balloon_host_socket: UnixDatagram)
+               balloon_host_socket: UnixDatagram,
+               _irqchip_fd: Option<File>)
                -> Result<()> {
     const MAX_VM_FD_RECV: usize = 1;
 
@@ -641,52 +648,53 @@ pub fn run_config(cfg: Config) -> Result<()> {
 
     let mem_size = cfg.memory.unwrap_or(256) << 20;
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    let mem = x86_64::setup_memory(mem_size).map_err(|e| Error::CreateGuestMemory(e))?;
+    let mem = Arch::setup_memory(mem_size as u64).map_err(|e| Error::CreateGuestMemory(e))?;
     #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
     let mem = GuestMemory::new(&vec![(GuestAddress(0), mem_size as u64)]).
-        map_err(|e| Error::CreateGuestMemory(e))?;
+        map_err(|e| Error::CreateGuestMemory(Box::new(e)))?;
 
     let kvm = Kvm::new().map_err(Error::CreateKvm)?;
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    let mut vm = x86_64::create_vm(&kvm, mem.clone()).map_err(|e| Error::CreateVm(e))?;
+    let mut vm = Arch::create_vm(&kvm, mem.clone()).map_err(|e| Error::CreateVm(e))?;
     #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
-    let mut vm = Vm::new(&kvm, mem.clone()).map_err(|e| Error::CreateVm(e))?;
+    let mut vm = Vm::new(&kvm, mem.clone()).map_err(|e| Error::CreateVm(Box::new(e)))?;
 
     let vcpu_count = cfg.vcpu_count.unwrap_or(1);
     let mut vcpu_handles = Vec::with_capacity(vcpu_count as usize);
     let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize));
     let mut vcpus = Vec::with_capacity(vcpu_count as usize);
     for cpu_id in 0..vcpu_count {
-        let vcpu = setup_vcpu(&kvm,
-                              &vm,
-                              cpu_id,
-                              vcpu_count)?;
+        let vcpu = setup_vcpu(&kvm, &vm, cpu_id, vcpu_count)?;
         vcpus.push(vcpu);
     }
 
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    let mut cmdline = x86_64::get_base_linux_cmdline();
+    let irq_chip = Arch::create_irq_chip(&vm).map_err(|e| Error::CreateIrqChip(e))?;
+    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+    let irq_chip = None;
+
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    let mut cmdline = Arch::get_base_linux_cmdline();
     #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
     let mut cmdline = kernel_cmdline::Cmdline::new(128);
 
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    let mut next_dev_pfn = x86_64::get_base_dev_pfn(mem_size as u64);
+    let mut next_dev_pfn = Arch::get_base_dev_pfn(mem_size as u64);
     #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
     let mut next_dev_pfn = 0;
 
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    let (io_bus, stdio_serial) = x86_64::setup_io_bus(&mut vm,
-                                                      exit_evt.try_clone().
-                                                      map_err(Error::CloneEventFd)?).
+    let (io_bus, stdio_serial) = Arch::setup_io_bus(&mut vm,
+                                                    exit_evt.try_clone().
+                                                    map_err(Error::CloneEventFd)?).
         map_err(|e| Error::SetupIoBus(e))?;
     // The non x86 case is kind of bogus using the exit_evt as an fd for serial
     // It's purpose is just to make the build happy since it doesn't actually run anyway
     #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
     let (io_bus, stdio_serial) = (devices::Bus::new(),
-                                  Arc::new(Mutex::new(
-                                      devices::Serial::new_out(exit_evt.try_clone().
-                                                               map_err(Error::CloneEventFd)?,
-                                                               Box::new(stdout())))));
+                                  Arc::new(Mutex::new(devices::Serial::new_out(
+                                      exit_evt.try_clone().map_err(Error::CloneEventFd)?,
+                                      Box::new(stdout())))));
 
     let (balloon_host_socket, balloon_device_socket) = UnixDatagram::pair()
         .map_err(Error::CreateSocket)?;
@@ -701,15 +709,16 @@ pub fn run_config(cfg: Config) -> Result<()> {
         cmdline.insert_str(&param).map_err(Error::Cmdline)?;
     }
 
-    let kernel_image = File::open(cfg.kernel_path.as_path())
+    let mut kernel_image = File::open(cfg.kernel_path.as_path())
         .map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?;
 
     // separate out load_kernel from other setup to get a specific error for
     // kernel loading
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    x86_64::load_kernel(&mem, kernel_image).map_err(|e| Error::LoadKernel(e))?;
+    Arch::load_kernel(&mem, &mut kernel_image).map_err(|e| Error::LoadKernel(e))?;
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    x86_64::setup_system_memory(&mem, vcpu_count, &CString::new(cmdline).unwrap()).
+    Arch::setup_system_memory(&mem, mem_size as u64, vcpu_count,
+                              &CString::new(cmdline).unwrap()).
         map_err(|e| Error::SetupSystemMemory(e))?;
 
     for (cpu_id, vcpu) in vcpus.into_iter().enumerate() {
@@ -732,5 +741,6 @@ pub fn run_config(cfg: Config) -> Result<()> {
                 sigchld_fd,
                 kill_signaled,
                 vcpu_handles,
-                balloon_host_socket)
+                balloon_host_socket,
+                irq_chip)
 }
diff --git a/src/main.rs b/src/main.rs
index 110435e..ea161cf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@
 
 //! Runs a virtual machine under KVM
 
+extern crate arch;
 extern crate devices;
 extern crate device_manager;
 extern crate libc;
diff --git a/x86_64/Cargo.toml b/x86_64/Cargo.toml
index edad6b6..3a88f98 100644
--- a/x86_64/Cargo.toml
+++ b/x86_64/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
 authors = ["The Chromium OS Authors"]
 
 [dependencies]
+arch = { path = "../arch" }
 data_model = { path = "../data_model" }
 devices = { path = "../devices" }
 device_manager = { path = "../device_manager" }
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 3eae049..9a16776 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+extern crate arch;
 extern crate byteorder;
 extern crate data_model;
 extern crate devices;
@@ -71,36 +72,20 @@ use bootparam::E820_RAM;
 use sys_util::{EventFd, GuestAddress, GuestMemory};
 use kvm::*;
 
-pub use regs::Error as RegError;
-pub use interrupts::Error as IntError;
-pub use mptable::Error as MpTableError;
-
 #[derive(Debug)]
 pub enum Error {
     /// Error configuring the system
     ConfigureSystem,
-    /// Error configuring the VCPU.
-    CpuSetup(cpuid::Error),
     /// Unable to clone an EventFd
     CloneEventFd(sys_util::Error),
     /// Unable to make an EventFd
     CreateEventFd(sys_util::Error),
     /// The kernel extends past the end of RAM
     KernelOffsetPastEnd,
-    /// Error configuring the VCPU registers.
-    RegisterConfiguration(RegError),
-    /// Error configuring the VCPU floating point registers.
-    FpuRegisterConfiguration(RegError),
     /// Error registering an IrqFd
     RegisterIrqfd(sys_util::Error),
-    /// Error configuring the VCPU segment registers.
-    SegmentRegisterConfiguration(RegError),
     LoadCmdline(kernel_loader::Error),
     LoadKernel(kernel_loader::Error),
-    /// Error configuring the VCPU local interrupt.
-    LocalIntConfiguration(IntError),
-    /// Error writing MP table to memory.
-    MpTableSetup(MpTableError),
     /// Error writing the zero page of guest memory.
     ZeroPageSetup,
     /// The zero page extends past the end of guest_mem.
@@ -113,24 +98,13 @@ impl error::Error for Error {
     fn description(&self) -> &str {
         match self {
             &Error::ConfigureSystem => "Error configuring the system",
-            &Error::CpuSetup(_) => "Error configuring the VCPU",
             &Error::CloneEventFd(_) => "Unable to clone an EventFd",
             &Error::CreateEventFd(_) => "Unable to make an EventFd",
             &Error::KernelOffsetPastEnd =>
                 "The kernel extends past the end of RAM",
-            &Error::RegisterConfiguration(_) =>
-                "Error configuring the VCPU registers",
-            &Error::FpuRegisterConfiguration(_) =>
-                "Error configuring the VCPU floating point registers",
             &Error::RegisterIrqfd(_) => "Error registering an IrqFd",
-            &Error::SegmentRegisterConfiguration(_) =>
-                "Error configuring the VCPU segment registers",
             &Error::LoadCmdline(_) => "Error Loading command line",
             &Error::LoadKernel(_) => "Error Loading Kernel",
-            &Error::LocalIntConfiguration(_) =>
-                "Error configuring the VCPU local interrupt",
-            &Error::MpTableSetup(_) =>
-                "Error writing MP table to memory",
             &Error::ZeroPageSetup =>
                 "Error writing the zero page of guest memory",
             &Error::ZeroPagePastRamEnd =>
@@ -146,7 +120,8 @@ impl Display for Error {
     }
 }
 
-pub type Result<T> = result::Result<T, Error>;
+pub struct X8664arch;
+pub type Result<T> = result::Result<T, Box<std::error::Error>>;
 
 const BOOT_STACK_POINTER: u64 = 0x8000;
 const MEM_32BIT_GAP_SIZE: u64 = (768 << 20);
@@ -158,207 +133,6 @@ const KERNEL_START_OFFSET: u64 = 0x200000;
 const CMDLINE_OFFSET: u64 = 0x20000;
 const CMDLINE_MAX_SIZE: u64 = KERNEL_START_OFFSET - CMDLINE_OFFSET;
 
-/// Loads the kernel from an open file.
-///
-/// # Arguments
-///
-/// * `mem` - The memory to be used by the guest.
-/// * `kernel_image` - the File object for the specified kernel.
-pub fn load_kernel(mem: &GuestMemory, mut kernel_image: File) -> Result<()> {
-    kernel_loader::load_kernel(mem, GuestAddress(KERNEL_START_OFFSET), &mut kernel_image)
-        .map_err(|e| Error::LoadKernel(e))?;
-    Ok(())
-}
-
-/// Configures the system memory space should be called once per vm before
-/// starting vcpu threads.
-///
-/// # Arguments
-///
-/// * `mem` - The memory to be used by the guest.
-/// * `vcpu_count` - Number of virtual CPUs the guest will have.
-/// * `cmdline` - the kernel commandline
-pub fn setup_system_memory(mem: &GuestMemory, vcpu_count: u32, cmdline: &CStr) -> Result<()> {
-    kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)
-        .map_err(|e| Error::LoadCmdline(e))?;
-    configure_system(mem, GuestAddress(KERNEL_START_OFFSET), GuestAddress(CMDLINE_OFFSET),
-                     cmdline.to_bytes().len() + 1, vcpu_count as u8)
-        .map_err(|_| Error::ConfigureSystem)?;
-    Ok(())
-}
-
-/// Creates a new VM object and initializes architecture specific devices
-///
-/// # Arguments
-///
-/// * `kvm` - The opened /dev/kvm object.
-/// * `mem` - The memory to be used by the guest.
-pub fn create_vm(kvm: &Kvm, mem: GuestMemory) -> result::Result<Vm, sys_util::Error> {
-    let vm = Vm::new(&kvm, mem)?;
-    let tss_addr = GuestAddress(0xfffbd000);
-    vm.set_tss_addr(tss_addr).expect("set tss addr failed");
-    vm.create_pit().expect("create pit failed");
-    vm.create_irq_chip()?;
-    Ok(vm)
-}
-
-/// This creates a GuestMemory object for this VM
-///
-/// * `mem_size` - Desired physical memory size for this VM
-pub fn setup_memory(mem_size: usize) -> result::Result<sys_util::GuestMemory, sys_util::GuestMemoryError> {
-    let arch_mem_regions = arch_memory_regions(mem_size as u64);
-    GuestMemory::new(&arch_mem_regions)
-}
-
-/// This returns the first page frame number for use by the balloon driver.
-pub fn get_base_dev_pfn(mem_size: u64) -> u64 {
-    // Put device memory at nearest 2MB boundary after physical memory
-    const MB: u64 = 1024 * 1024;
-    let mem_size_round_2mb = (mem_size + 2*MB - 1) / (2*MB) * (2*MB);
-    mem_size_round_2mb / sys_util::pagesize() as u64
-}
-
-/// This returns a base part of the kernel command for this architecture
-pub fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
-    let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
-    cmdline.insert_str("console=ttyS0 noacpi reboot=k panic=1 pci=off").
-        unwrap();
-    cmdline
-}
-
-/// This creates and returns a device_manager object for this vm.
-///
-/// # Arguments
-///
-/// * `vm` - the vm object
-/// * `mem` - A copy of the GuestMemory object for this VM.
-pub fn get_device_manager(vm: &mut Vm, mem: GuestMemory) -> device_manager::DeviceManager {
-    const MMIO_BASE: u64 = 0xd0000000;
-    const MMIO_LEN: u64 = 0x1000;
-    const IRQ_BASE: u32 = 5;
-
-    device_manager::DeviceManager::new(vm, mem, MMIO_LEN, MMIO_BASE, IRQ_BASE)
-}
-
-/// Sets up the IO bus for this platform
-///
-/// # Arguments
-///
-/// * - `vm` the vm object
-/// * - `exit_evt` - the event fd object which should receive exit events
-pub fn setup_io_bus(vm: &mut Vm, exit_evt: EventFd)
-                    -> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)> {
-    struct NoDevice;
-    impl devices::BusDevice for NoDevice {}
-
-    let mut io_bus = devices::Bus::new();
-
-    let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
-    let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
-    let stdio_serial =
-        Arc::new(Mutex::new(devices::Serial::new_out(com_evt_1_3
-                                                         .try_clone()
-                                                         .map_err(Error::CloneEventFd)?,
-                                                     Box::new(stdout()))));
-    let nul_device = Arc::new(Mutex::new(NoDevice));
-    io_bus.insert(stdio_serial.clone(), 0x3f8, 0x8).unwrap();
-    io_bus
-        .insert(Arc::new(Mutex::new(devices::Serial::new_sink(com_evt_2_4
-                                                                  .try_clone()
-                                                                  .map_err(Error::CloneEventFd)?))),
-                0x2f8,
-                0x8)
-        .unwrap();
-    io_bus
-        .insert(Arc::new(Mutex::new(devices::Serial::new_sink(com_evt_1_3
-                                                                  .try_clone()
-                                                                  .map_err(Error::CloneEventFd)?))),
-                0x3e8,
-                0x8)
-        .unwrap();
-    io_bus
-        .insert(Arc::new(Mutex::new(devices::Serial::new_sink(com_evt_2_4
-                                                                  .try_clone()
-                                                                  .map_err(Error::CloneEventFd)?))),
-                0x2e8,
-                0x8)
-        .unwrap();
-    io_bus
-        .insert(Arc::new(Mutex::new(devices::Cmos::new())), 0x70, 0x2)
-        .unwrap();
-    io_bus
-        .insert(Arc::new(Mutex::new(devices::I8042Device::new(exit_evt
-                                                                  .try_clone()
-                                                                  .map_err(Error::CloneEventFd)?))),
-                0x061,
-                0x4)
-        .unwrap();
-    io_bus.insert(nul_device.clone(), 0x040, 0x8).unwrap(); // ignore pit
-    io_bus.insert(nul_device.clone(), 0x0ed, 0x1).unwrap(); // most likely this one does nothing
-    io_bus.insert(nul_device.clone(), 0x0f0, 0x2).unwrap(); // ignore fpu
-    io_bus.insert(nul_device.clone(), 0xcf8, 0x8).unwrap(); // ignore pci
-
-    vm.register_irqfd(&com_evt_1_3, 4)
-        .map_err(Error::RegisterIrqfd)?;
-    vm.register_irqfd(&com_evt_2_4, 3)
-        .map_err(Error::RegisterIrqfd)?;
-
-    Ok((io_bus, stdio_serial))
-}
-
-/// Returns a Vec of the valid memory addresses.
-/// These should be used to configure the GuestMemory structure for the platfrom.
-/// For x86_64 all addresses are valid from the start of the kenel except a
-/// carve out at the end of 32bit address space.
-fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
-    let mem_end = GuestAddress(size);
-    let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
-    let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
-
-    let mut regions = Vec::new();
-    if mem_end < end_32bit_gap_start {
-        regions.push((GuestAddress(0), size));
-    } else {
-        regions.push((GuestAddress(0), end_32bit_gap_start.offset()));
-        if mem_end > first_addr_past_32bits {
-            regions.push((first_addr_past_32bits, mem_end.offset_from(first_addr_past_32bits)));
-        }
-    }
-
-    regions
-}
-
-/// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
-///
-/// # Arguments
-///
-/// * `guest_mem` - The memory to be used by the guest.
-/// * `kernel_load_offset` - Offset from `guest_mem` at which the kernel starts.
-/// * `kvm` - The /dev/kvm object that created vcpu.
-/// * `vcpu` - The VCPU object to configure.
-/// * `cpu_id` - The id of the given `vcpu`.
-/// * `num_cpus` - Number of virtual CPUs the guest will have.
-pub fn configure_vcpu(guest_mem: &GuestMemory,
-                      kvm: &kvm::Kvm,
-                      vcpu: &kvm::Vcpu,
-                      cpu_id: u64,
-                      num_cpus: u64)
-                      -> Result<()> {
-    let kernel_load_addr = GuestAddress(KERNEL_START_OFFSET);
-    cpuid::setup_cpuid(kvm, vcpu, cpu_id, num_cpus).map_err(Error::CpuSetup)?;
-    regs::setup_msrs(vcpu).map_err(Error::RegisterConfiguration)?;
-    let kernel_end = guest_mem.checked_offset(kernel_load_addr, KERNEL_64BIT_ENTRY_OFFSET)
-        .ok_or(Error::KernelOffsetPastEnd)?;
-    regs::setup_regs(vcpu,
-                     (kernel_end).offset() as u64,
-                     BOOT_STACK_POINTER as u64,
-                     ZERO_PAGE_OFFSET as u64).map_err(Error::RegisterConfiguration)?;
-    regs::setup_fpu(vcpu).map_err(Error::FpuRegisterConfiguration)?;
-    regs::setup_sregs(guest_mem, vcpu).map_err(Error::SegmentRegisterConfiguration)?;
-    interrupts::set_lint(vcpu).map_err(Error::LocalIntConfiguration)?;
-    Ok(())
-}
-
 fn configure_system(guest_mem: &GuestMemory,
                     kernel_addr: GuestAddress,
                     cmdline_addr: GuestAddress,
@@ -374,7 +148,7 @@ fn configure_system(guest_mem: &GuestMemory,
     let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
 
     // Note that this puts the mptable at 0x0 in guest physical memory.
-    mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?;
+    mptable::setup_mptable(guest_mem, num_cpus)?;
 
     let mut params: boot_params = Default::default();
 
@@ -419,7 +193,7 @@ fn configure_system(guest_mem: &GuestMemory,
 /// Returns Ok(()) if successful, or an error if there is no space left in the map.
 fn add_e820_entry(params: &mut boot_params, addr: u64, size: u64, mem_type: u32) -> Result<()> {
     if params.e820_entries >= params.e820_map.len() as u8 {
-        return Err(Error::E820Configuration);
+        return Err(Box::new(Error::E820Configuration));
     }
 
     params.e820_map[params.e820_entries as usize].addr = addr;
@@ -430,6 +204,223 @@ fn add_e820_entry(params: &mut boot_params, addr: u64, size: u64, mem_type: u32)
     Ok(())
 }
 
+/// Returns a Vec of the valid memory addresses.
+/// These should be used to configure the GuestMemory structure for the platfrom.
+/// For x86_64 all addresses are valid from the start of the kenel except a
+/// carve out at the end of 32bit address space.
+fn arch_memory_regions(size: u64) -> Vec<(GuestAddress, u64)> {
+    let mem_end = GuestAddress(size);
+    let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
+    let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
+
+    let mut regions = Vec::new();
+    if mem_end < end_32bit_gap_start {
+        regions.push((GuestAddress(0), size));
+    } else {
+        regions.push((GuestAddress(0), end_32bit_gap_start.offset()));
+        if mem_end > first_addr_past_32bits {
+            regions.push((first_addr_past_32bits, mem_end.offset_from(first_addr_past_32bits)));
+        }
+    }
+
+    regions
+}
+
+impl arch::LinuxArch for X8664arch {
+    /// Loads the kernel from an open file.
+    ///
+    /// # Arguments
+    ///
+    /// * `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(())
+    }
+
+    /// Configures the system memory space should be called once per vm before
+    /// starting vcpu threads.
+    ///
+    /// # Arguments
+    ///
+    /// * `mem` - The memory to be used by the guest.
+    /// * `vcpu_count` - Number of virtual CPUs the guest will have.
+    /// * `cmdline` - the kernel commandline
+    fn setup_system_memory(mem: &GuestMemory, _mem_size: u64,
+                           vcpu_count: u32, cmdline: &CStr) -> Result<()> {
+        kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)?;
+        configure_system(mem, GuestAddress(KERNEL_START_OFFSET),
+                         GuestAddress(CMDLINE_OFFSET),
+                         cmdline.to_bytes().len() + 1, vcpu_count as u8)?;
+        Ok(())
+    }
+
+    /// Creates a new VM object and initializes architecture specific devices
+    ///
+    /// # Arguments
+    ///
+    /// * `kvm` - The opened /dev/kvm object.
+    /// * `mem` - The memory to be used by the guest.
+    fn create_vm(kvm: &Kvm, mem: GuestMemory) -> Result<Vm> {
+        let vm = Vm::new(&kvm, mem)?;
+        let tss_addr = GuestAddress(0xfffbd000);
+        vm.set_tss_addr(tss_addr).expect("set tss addr failed");
+        vm.create_pit().expect("create pit failed");
+        vm.create_irq_chip()?;
+        Ok(vm)
+    }
+
+    /// This creates a GuestMemory object for this VM
+    ///
+    /// * `mem_size` - Desired physical memory size in bytes for this VM
+    fn setup_memory(mem_size: u64) -> Result<sys_util::GuestMemory> {
+        let arch_mem_regions = arch_memory_regions(mem_size);
+        let mem = GuestMemory::new(&arch_mem_regions)?;
+        Ok(mem)
+    }
+
+    /// The creates the interrupt controller device and optionally returns the fd for it.
+    /// Some architectures may not have a separate descriptor for the interrupt
+    /// controller, so they would return None even on success.
+    ///
+    /// # Arguments
+    ///
+    /// * `vm` - the vm object
+    fn create_irq_chip(_vm: &kvm::Vm) -> Result<Option<File>> {
+        // Unfortunately X86 and ARM have to do this in completely different order
+        // X86 needs to create the irq chip before creating cpus and
+        // ARM needs to do it afterwards.
+        Ok(None)
+    }
+
+    /// This returns the first page frame number for use by the balloon driver.
+    ///
+    /// # Arguments
+    ///
+    /// * `mem_size` - the size in bytes of physical ram for the guest
+    fn get_base_dev_pfn(mem_size: u64) -> u64 {
+        // Put device memory at nearest 2MB boundary after physical memory
+        const MB: u64 = 1024 * 1024;
+        let mem_size_round_2mb = (mem_size + 2*MB - 1) / (2*MB) * (2*MB);
+        mem_size_round_2mb / sys_util::pagesize() as u64
+    }
+
+    /// This returns a minimal kernel command for this architecture
+    fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
+        let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
+        cmdline.insert_str("console=ttyS0 noacpi reboot=k panic=1 pci=off").
+            unwrap();
+        cmdline
+    }
+
+    /// This creates and returns a device_manager object for this vm.
+    ///
+    /// # Arguments
+    ///
+    /// * `vm` - the vm object
+    /// * `mem` - A copy of the GuestMemory object for this VM.
+    fn get_device_manager(vm: &mut Vm, mem: GuestMemory) ->
+        Result<device_manager::DeviceManager> {
+        const MMIO_BASE: u64 = 0xd0000000;
+        const MMIO_LEN: u64 = 0x1000;
+        const IRQ_BASE: u32 = 5;
+
+        Ok(device_manager::DeviceManager::new(vm, mem, MMIO_LEN, MMIO_BASE, IRQ_BASE))
+    }
+
+    /// Sets up the IO bus for this platform
+    ///
+    /// # Arguments
+    ///
+    /// * - `vm` the vm object
+    /// * - `exit_evt` - the event fd object which should receive exit events
+    fn setup_io_bus(vm: &mut Vm, exit_evt: EventFd)
+                    -> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)> {
+        struct NoDevice;
+        impl devices::BusDevice for NoDevice {}
+
+        let mut io_bus = devices::Bus::new();
+
+        let com_evt_1_3 = EventFd::new().map_err(|e| Error::CreateEventFd(e))?;
+        let com_evt_2_4 = EventFd::new().map_err(|e| Error::CreateEventFd(e))?;
+        let stdio_serial =
+            Arc::new(Mutex::new(
+                devices::Serial::new_out(com_evt_1_3.try_clone().
+                                         map_err(|e| Error::CloneEventFd(e))?,
+                                         Box::new(stdout()))));
+        let nul_device = Arc::new(Mutex::new(NoDevice));
+        io_bus.insert(stdio_serial.clone(), 0x3f8, 0x8).unwrap();
+        io_bus.insert(Arc::new(Mutex::new(
+            devices::Serial::new_sink(com_evt_2_4.try_clone().
+                                      map_err(|e| Error::CloneEventFd(e))?))),
+                      0x2f8,
+                      0x8)
+            .unwrap();
+        io_bus.insert(Arc::new(Mutex::new(
+            devices::Serial::new_sink(com_evt_1_3.try_clone().
+                                      map_err(|e| Error::CloneEventFd(e))?))),
+                      0x3e8,
+                      0x8)
+            .unwrap();
+        io_bus.insert(Arc::new(Mutex::new(
+            devices::Serial::new_sink(com_evt_2_4.try_clone().
+                                      map_err(|e| Error::CloneEventFd(e))?))),
+                      0x2e8,
+                      0x8)
+            .unwrap();
+        io_bus.insert(Arc::new(Mutex::new(devices::Cmos::new())), 0x70, 0x2)
+            .unwrap();
+        io_bus.insert(Arc::new(Mutex::new(
+            devices::I8042Device::new(exit_evt.try_clone().
+                                      map_err(|e| Error::CloneEventFd(e))?))),
+                      0x061,
+                      0x4)
+            .unwrap();
+        io_bus.insert(nul_device.clone(), 0x040, 0x8).unwrap(); // ignore pit
+        io_bus.insert(nul_device.clone(), 0x0ed, 0x1).unwrap(); // most likely this one does nothing
+        io_bus.insert(nul_device.clone(), 0x0f0, 0x2).unwrap(); // ignore fpu
+        io_bus.insert(nul_device.clone(), 0xcf8, 0x8).unwrap(); // ignore pci
+
+        vm.register_irqfd(&com_evt_1_3, 4).map_err(Error::RegisterIrqfd)?;
+        vm.register_irqfd(&com_evt_2_4, 3).map_err(Error::RegisterIrqfd)?;
+
+        Ok((io_bus, stdio_serial))
+    }
+
+    /// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
+    ///
+    /// # Arguments
+    ///
+    /// * `guest_mem` - The memory to be used by the guest.
+    /// * `kernel_load_offset` - Offset in bytes from `guest_mem` at which the
+    ///                          kernel starts.
+    /// * `kvm` - The /dev/kvm object that created vcpu.
+    /// * `vcpu` - The VCPU object to configure.
+    /// * `cpu_id` - The id of the given `vcpu`.
+    /// * `num_cpus` - Number of virtual CPUs the guest will have.
+    fn configure_vcpu(guest_mem: &GuestMemory,
+                      kvm: &Kvm,
+                      vcpu: &Vcpu,
+                      cpu_id: u64,
+                      num_cpus: u64)
+                      -> Result<()> {
+        let kernel_load_addr = GuestAddress(KERNEL_START_OFFSET);
+        cpuid::setup_cpuid(kvm, vcpu, cpu_id, num_cpus)?;
+        regs::setup_msrs(vcpu)?;
+        let kernel_end = guest_mem.checked_offset(kernel_load_addr, KERNEL_64BIT_ENTRY_OFFSET)
+            .ok_or(Error::KernelOffsetPastEnd)?;
+        regs::setup_regs(vcpu,
+                         (kernel_end).offset() as u64,
+                         BOOT_STACK_POINTER as u64,
+                         ZERO_PAGE_OFFSET as u64)?;
+        regs::setup_fpu(vcpu)?;
+        regs::setup_sregs(guest_mem, vcpu)?;
+        interrupts::set_lint(vcpu)?;
+        Ok(())
+    }
+
+}
 #[cfg(test)]
 mod tests {
     use super::*;