mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 10:10:41 +00:00
crosvm: x86_64 guest support for android device-tree
This device tree is derived from the Android fstab file which is provided via command line flag. BUG=chromium:922737 TEST=None CQ-DEPEND=CL:1415390 CQ-DEPEND=CL:1415270 Change-Id: Idd007c844f84cab3ff37be16a718f14e5f630312 Reviewed-on: https://chromium-review.googlesource.com/1370058 Commit-Ready: Daniel Verkamp <dverkamp@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
parent
f052cfefc8
commit
4133b0120d
12 changed files with 430 additions and 243 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3,7 +3,6 @@ name = "aarch64"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arch 0.1.0",
|
"arch 0.1.0",
|
||||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"data_model 0.1.0",
|
"data_model 0.1.0",
|
||||||
"devices 0.1.0",
|
"devices 0.1.0",
|
||||||
"io_jail 0.1.0",
|
"io_jail 0.1.0",
|
||||||
|
@ -20,6 +19,7 @@ dependencies = [
|
||||||
name = "arch"
|
name = "arch"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"devices 0.1.0",
|
"devices 0.1.0",
|
||||||
"io_jail 0.1.0",
|
"io_jail 0.1.0",
|
||||||
"kernel_cmdline 0.1.0",
|
"kernel_cmdline 0.1.0",
|
||||||
|
|
|
@ -15,4 +15,3 @@ sys_util = { path = "../sys_util" }
|
||||||
resources = { path = "../resources" }
|
resources = { path = "../resources" }
|
||||||
sync = { path = "../sync" }
|
sync = { path = "../sync" }
|
||||||
libc = "*"
|
libc = "*"
|
||||||
byteorder = "*"
|
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use std::ffi::CStr;
|
||||||
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 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 devices::PciInterruptPin;
|
||||||
use sys_util::{GuestAddress, GuestMemory};
|
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_HIGH: u32 = 0x00000004;
|
||||||
const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008;
|
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>> {
|
fn create_memory_node(fdt: &mut Vec<u8>, guest_mem: &GuestMemory) -> Result<(), Box<Error>> {
|
||||||
let mem_size = guest_mem.memory_size();
|
let mem_size = guest_mem.memory_size();
|
||||||
let mem_reg_prop = generate_prop64(&[AARCH64_PHYS_MEM_START, mem_size]);
|
let mem_reg_prop = generate_prop64(&[AARCH64_PHYS_MEM_START, mem_size]);
|
||||||
|
@ -487,18 +295,7 @@ pub fn create_fdt(
|
||||||
cmdline: &CStr,
|
cmdline: &CStr,
|
||||||
) -> Result<(), Box<Error>> {
|
) -> Result<(), Box<Error>> {
|
||||||
let mut fdt = vec![0; fdt_max_size];
|
let mut fdt = vec![0; fdt_max_size];
|
||||||
|
start_fdt(&mut fdt, 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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The whole thing is put into one giant node with some top level properties
|
// The whole thing is put into one giant node with some top level properties
|
||||||
begin_node(&mut fdt, "")?;
|
begin_node(&mut fdt, "")?;
|
||||||
|
@ -519,32 +316,10 @@ pub fn create_fdt(
|
||||||
// End giant node
|
// End giant node
|
||||||
end_node(&mut fdt)?;
|
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
|
// Allocate another buffer so we can format and then write fdt to guest
|
||||||
let mut fdt_final = vec![0; fdt_max_size];
|
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 fdt_address = GuestAddress(AARCH64_PHYS_MEM_START + fdt_load_offset);
|
||||||
let written = guest_mem
|
let written = guest_mem
|
||||||
.write_at_addr(fdt_final.as_slice(), fdt_address)
|
.write_at_addr(fdt_final.as_slice(), fdt_address)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
extern crate arch;
|
extern crate arch;
|
||||||
extern crate byteorder;
|
|
||||||
extern crate data_model;
|
extern crate data_model;
|
||||||
extern crate devices;
|
extern crate devices;
|
||||||
extern crate io_jail;
|
extern crate io_jail;
|
||||||
|
|
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||||
authors = ["The Chromium OS Authors"]
|
authors = ["The Chromium OS Authors"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
byteorder = "*"
|
||||||
devices = { path = "../devices" }
|
devices = { path = "../devices" }
|
||||||
io_jail = { path = "../io_jail" }
|
io_jail = { path = "../io_jail" }
|
||||||
kvm = { path = "../kvm" }
|
kvm = { path = "../kvm" }
|
||||||
|
|
249
arch/src/fdt.rs
Normal file
249
arch/src/fdt.rs
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use libc::{c_char, c_int, c_void};
|
||||||
|
use std::error::{self, Error as FdtError};
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::fmt;
|
||||||
|
use std::ptr::null;
|
||||||
|
|
||||||
|
// This links to libfdt which handles the creation of the binary blob
|
||||||
|
// flattened device tree (fdt) that is passed to the kernel and indicates
|
||||||
|
// the hardware configuration of the machine.
|
||||||
|
#[link(name = "fdt")]
|
||||||
|
extern "C" {
|
||||||
|
fn fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int;
|
||||||
|
fn fdt_finish_reservemap(fdt: *mut c_void) -> c_int;
|
||||||
|
fn fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int;
|
||||||
|
fn fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int)
|
||||||
|
-> c_int;
|
||||||
|
fn fdt_end_node(fdt: *mut c_void) -> c_int;
|
||||||
|
fn fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int;
|
||||||
|
fn fdt_finish(fdt: *const c_void) -> c_int;
|
||||||
|
fn fdt_pack(fdt: *mut c_void) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
FdtCreateError(c_int),
|
||||||
|
FdtFinishReservemapError(c_int),
|
||||||
|
FdtBeginNodeError(c_int),
|
||||||
|
FdtPropertyError(c_int),
|
||||||
|
FdtEndNodeError(c_int),
|
||||||
|
FdtOpenIntoError(c_int),
|
||||||
|
FdtFinishError(c_int),
|
||||||
|
FdtPackError(c_int),
|
||||||
|
FdtGuestMemoryWriteError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
&Error::FdtCreateError(_) => "Error creating FDT",
|
||||||
|
&Error::FdtFinishReservemapError(_) => "Error finishing reserve map",
|
||||||
|
&Error::FdtBeginNodeError(_) => "Error beginning FDT node",
|
||||||
|
&Error::FdtPropertyError(_) => "Error adding FDT property",
|
||||||
|
&Error::FdtEndNodeError(_) => "Error ending FDT node",
|
||||||
|
&Error::FdtOpenIntoError(_) => "Error copying FDT to Guest",
|
||||||
|
&Error::FdtFinishError(_) => "Error performing FDT finish",
|
||||||
|
&Error::FdtPackError(_) => "Error packing FDT",
|
||||||
|
&Error::FdtGuestMemoryWriteError => "Error writing FDT to Guest Memory",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let prefix = "Libfdt Error: ";
|
||||||
|
match self {
|
||||||
|
&Error::FdtCreateError(fdt_ret)
|
||||||
|
| &Error::FdtFinishReservemapError(fdt_ret)
|
||||||
|
| &Error::FdtBeginNodeError(fdt_ret)
|
||||||
|
| &Error::FdtPropertyError(fdt_ret)
|
||||||
|
| &Error::FdtEndNodeError(fdt_ret)
|
||||||
|
| &Error::FdtOpenIntoError(fdt_ret)
|
||||||
|
| &Error::FdtFinishError(fdt_ret)
|
||||||
|
| &Error::FdtPackError(fdt_ret) => write!(
|
||||||
|
f,
|
||||||
|
"{} {} code: {}",
|
||||||
|
prefix,
|
||||||
|
Error::description(self),
|
||||||
|
fdt_ret
|
||||||
|
),
|
||||||
|
&Error::FdtGuestMemoryWriteError => {
|
||||||
|
write!(f, "{} {}", prefix, Error::description(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<(), Box<Error>> {
|
||||||
|
let cstr_name = CString::new(name).unwrap();
|
||||||
|
|
||||||
|
// Safe because we allocated fdt and converted name to a CString
|
||||||
|
let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtBeginNodeError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_node(fdt: &mut Vec<u8>) -> Result<(), Box<Error>> {
|
||||||
|
// Safe because we allocated fdt
|
||||||
|
let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtEndNodeError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<(), Box<Error>> {
|
||||||
|
let cstr_name = CString::new(name).unwrap();
|
||||||
|
let val_ptr = val.as_ptr() as *const c_void;
|
||||||
|
|
||||||
|
// Safe because we allocated fdt and converted name to a CString
|
||||||
|
let fdt_ret = unsafe {
|
||||||
|
fdt_property(
|
||||||
|
fdt.as_mut_ptr() as *mut c_void,
|
||||||
|
cstr_name.as_ptr(),
|
||||||
|
val_ptr,
|
||||||
|
val.len() as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_to_fdt32(input: u32) -> [u8; 4] {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
BigEndian::write_u32(&mut buf, input);
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_to_fdt64(input: u64) -> [u8; 8] {
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
BigEndian::write_u64(&mut buf, input);
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_u32(fdt: &mut Vec<u8>, name: &str, val: u32) -> Result<(), Box<Error>> {
|
||||||
|
property(fdt, name, &cpu_to_fdt32(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<(), Box<Error>> {
|
||||||
|
property(fdt, name, &cpu_to_fdt64(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to generate a properly formatted byte vector using 32-bit cells
|
||||||
|
pub fn generate_prop32(cells: &[u32]) -> Vec<u8> {
|
||||||
|
let mut ret: Vec<u8> = Vec::new();
|
||||||
|
for &e in cells {
|
||||||
|
ret.extend(cpu_to_fdt32(e).into_iter());
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to generate a properly formatted byte vector using 64-bit cells
|
||||||
|
pub fn generate_prop64(cells: &[u64]) -> Vec<u8> {
|
||||||
|
let mut ret: Vec<u8> = Vec::new();
|
||||||
|
for &e in cells {
|
||||||
|
ret.extend(cpu_to_fdt64(e).into_iter());
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_null(fdt: &mut Vec<u8>, name: &str) -> Result<(), Box<Error>> {
|
||||||
|
let cstr_name = CString::new(name).unwrap();
|
||||||
|
|
||||||
|
// Safe because we allocated fdt, converted name to a CString
|
||||||
|
let fdt_ret = unsafe {
|
||||||
|
fdt_property(
|
||||||
|
fdt.as_mut_ptr() as *mut c_void,
|
||||||
|
cstr_name.as_ptr(),
|
||||||
|
null(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_cstring(
|
||||||
|
fdt: &mut Vec<u8>,
|
||||||
|
name: &str,
|
||||||
|
cstr_value: &CStr,
|
||||||
|
) -> Result<(), Box<Error>> {
|
||||||
|
let value_bytes = cstr_value.to_bytes_with_nul();
|
||||||
|
let cstr_name = CString::new(name).unwrap();
|
||||||
|
|
||||||
|
// Safe because we allocated fdt, converted name and value to CStrings
|
||||||
|
let fdt_ret = unsafe {
|
||||||
|
fdt_property(
|
||||||
|
fdt.as_mut_ptr() as *mut c_void,
|
||||||
|
cstr_name.as_ptr(),
|
||||||
|
value_bytes.as_ptr() as *mut c_void,
|
||||||
|
value_bytes.len() as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtPropertyError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<(), Box<Error>> {
|
||||||
|
let cstr_value = CString::new(value).unwrap();
|
||||||
|
property_cstring(fdt, name, &cstr_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_fdt(fdt: &mut Vec<u8>, fdt_max_size: usize) -> Result<(), Box<Error>> {
|
||||||
|
// Safe since we allocated this array with fdt_max_size
|
||||||
|
let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, fdt_max_size as c_int) };
|
||||||
|
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtCreateError(fdt_ret)));
|
||||||
|
}
|
||||||
|
// Safe since we allocated this array
|
||||||
|
fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtFinishReservemapError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish_fdt(
|
||||||
|
fdt: &mut Vec<u8>,
|
||||||
|
fdt_final: &mut Vec<u8>,
|
||||||
|
fdt_max_size: usize,
|
||||||
|
) -> Result<(), Box<Error>> {
|
||||||
|
// Safe since we allocated fdt_final and previously passed in it's size
|
||||||
|
let mut fdt_ret = unsafe { fdt_finish(fdt.as_mut_ptr() as *mut c_void) };
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtFinishError(fdt_ret)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because we allocated both arrays with the correct size
|
||||||
|
fdt_ret = unsafe {
|
||||||
|
fdt_open_into(
|
||||||
|
fdt.as_mut_ptr() as *mut c_void,
|
||||||
|
fdt_final.as_mut_ptr() as *mut c_void,
|
||||||
|
fdt_max_size as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtOpenIntoError(fdt_ret)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe since we allocated fdt_final
|
||||||
|
fdt_ret = unsafe { fdt_pack(fdt_final.as_mut_ptr() as *mut c_void) };
|
||||||
|
if fdt_ret != 0 {
|
||||||
|
return Err(Box::new(Error::FdtPackError(fdt_ret)));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
pub mod fdt;
|
||||||
|
|
||||||
|
extern crate byteorder;
|
||||||
extern crate devices;
|
extern crate devices;
|
||||||
extern crate io_jail;
|
extern crate io_jail;
|
||||||
extern crate kernel_cmdline;
|
extern crate kernel_cmdline;
|
||||||
|
@ -38,6 +41,7 @@ pub struct VmComponents {
|
||||||
pub memory_mb: u64,
|
pub memory_mb: u64,
|
||||||
pub vcpu_count: u32,
|
pub vcpu_count: u32,
|
||||||
pub kernel_image: File,
|
pub kernel_image: File,
|
||||||
|
pub android_fstab: Option<File>,
|
||||||
pub extra_kernel_params: Vec<String>,
|
pub extra_kernel_params: Vec<String>,
|
||||||
pub wayland_dmabuf: bool,
|
pub wayland_dmabuf: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub fn load_kernel<F>(
|
||||||
guest_mem: &GuestMemory,
|
guest_mem: &GuestMemory,
|
||||||
kernel_start: GuestAddress,
|
kernel_start: GuestAddress,
|
||||||
kernel_image: &mut F,
|
kernel_image: &mut F,
|
||||||
) -> Result<()>
|
) -> Result<u64>
|
||||||
where
|
where
|
||||||
F: Read + Seek,
|
F: Read + Seek,
|
||||||
{
|
{
|
||||||
|
@ -117,6 +117,8 @@ where
|
||||||
.map_err(|_| Error::ReadProgramHeader)?
|
.map_err(|_| Error::ReadProgramHeader)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut kernel_end = 0;
|
||||||
|
|
||||||
// Read in each section pointed to by the program headers.
|
// Read in each section pointed to by the program headers.
|
||||||
for phdr in &phdrs {
|
for phdr in &phdrs {
|
||||||
if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
|
if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
|
||||||
|
@ -133,9 +135,11 @@ where
|
||||||
guest_mem
|
guest_mem
|
||||||
.read_to_memory(mem_offset, kernel_image, phdr.p_filesz as usize)
|
.read_to_memory(mem_offset, kernel_image, phdr.p_filesz as usize)
|
||||||
.map_err(|_| Error::ReadKernelImage)?;
|
.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.
|
/// Writes the command line string to the given memory slice.
|
||||||
|
@ -236,7 +240,7 @@ mod test {
|
||||||
let kernel_addr = GuestAddress(0x0);
|
let kernel_addr = GuestAddress(0x0);
|
||||||
let image = make_elf_bin();
|
let image = make_elf_bin();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(()),
|
Ok(16613),
|
||||||
load_kernel(&gm, kernel_addr, &mut Cursor::new(&image))
|
load_kernel(&gm, kernel_addr, &mut Cursor::new(&image))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
11
src/linux.rs
11
src/linux.rs
|
@ -68,6 +68,7 @@ pub enum Error {
|
||||||
InvalidWaylandPath,
|
InvalidWaylandPath,
|
||||||
NetDeviceNew(devices::virtio::NetError),
|
NetDeviceNew(devices::virtio::NetError),
|
||||||
NoVarEmpty,
|
NoVarEmpty,
|
||||||
|
OpenAndroidFstab(PathBuf, io::Error),
|
||||||
OpenKernel(PathBuf, io::Error),
|
OpenKernel(PathBuf, io::Error),
|
||||||
P9DeviceNew(devices::virtio::P9Error),
|
P9DeviceNew(devices::virtio::P9Error),
|
||||||
PollContextAdd(sys_util::Error),
|
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::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::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::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::P9DeviceNew(e) => write!(f, "failed to create 9p device: {}", e),
|
||||||
Error::PollContextAdd(e) => write!(f, "failed to add fd to poll context: {:?}", e),
|
Error::PollContextAdd(e) => write!(f, "failed to add fd to poll context: {:?}", e),
|
||||||
Error::PollContextDelete(e) => {
|
Error::PollContextDelete(e) => {
|
||||||
|
@ -851,6 +855,13 @@ pub fn run_config(cfg: Config) -> Result<()> {
|
||||||
vcpu_count: cfg.vcpu_count.unwrap_or(1),
|
vcpu_count: cfg.vcpu_count.unwrap_or(1),
|
||||||
kernel_image: File::open(cfg.kernel_path.as_path())
|
kernel_image: File::open(cfg.kernel_path.as_path())
|
||||||
.map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?,
|
.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(),
|
extra_kernel_params: cfg.params.clone(),
|
||||||
wayland_dmabuf: cfg.wayland_dmabuf,
|
wayland_dmabuf: cfg.wayland_dmabuf,
|
||||||
};
|
};
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -76,6 +76,7 @@ pub struct Config {
|
||||||
vcpu_count: Option<u32>,
|
vcpu_count: Option<u32>,
|
||||||
memory: Option<usize>,
|
memory: Option<usize>,
|
||||||
kernel_path: PathBuf,
|
kernel_path: PathBuf,
|
||||||
|
android_fstab: Option<PathBuf>,
|
||||||
params: Vec<String>,
|
params: Vec<String>,
|
||||||
socket_path: Option<PathBuf>,
|
socket_path: Option<PathBuf>,
|
||||||
plugin: Option<PathBuf>,
|
plugin: Option<PathBuf>,
|
||||||
|
@ -104,6 +105,7 @@ impl Default for Config {
|
||||||
vcpu_count: None,
|
vcpu_count: None,
|
||||||
memory: None,
|
memory: None,
|
||||||
kernel_path: PathBuf::default(),
|
kernel_path: PathBuf::default(),
|
||||||
|
android_fstab: None,
|
||||||
params: Vec::new(),
|
params: Vec::new(),
|
||||||
socket_path: None,
|
socket_path: None,
|
||||||
plugin: None,
|
plugin: None,
|
||||||
|
@ -178,6 +180,24 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
||||||
cfg.kernel_path = kernel_path;
|
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" => {
|
"params" => {
|
||||||
cfg.params.push(value.unwrap().to_owned());
|
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<(), ()> {
|
fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
||||||
let arguments =
|
let arguments =
|
||||||
&[Argument::positional("KERNEL", "bzImage of kernel to run"),
|
&[Argument::positional("KERNEL", "bzImage of kernel to run"),
|
||||||
|
Argument::value("android-fstab", "PATH", "Path to Android fstab"),
|
||||||
Argument::short_value('p',
|
Argument::short_value('p',
|
||||||
"params",
|
"params",
|
||||||
"PARAMS",
|
"PARAMS",
|
||||||
|
|
91
x86_64/src/fdt.rs
Normal file
91
x86_64/src/fdt.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
extern crate arch;
|
||||||
|
|
||||||
|
use arch::fdt::{begin_node, end_node, finish_fdt, property_string, start_fdt, Error};
|
||||||
|
use bootparam::setup_data;
|
||||||
|
use bootparam::SETUP_DTB;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufRead;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::mem;
|
||||||
|
use sys_util::{GuestAddress, GuestMemory};
|
||||||
|
|
||||||
|
use X86_64_FDT_MAX_SIZE;
|
||||||
|
|
||||||
|
/// Creates a flattened device tree containing all of the parameters for the
|
||||||
|
/// kernel and loads it into the guest memory at the specified offset.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `fdt_max_size` - The amount of space reserved for the device tree
|
||||||
|
/// * `guest_mem` - The guest memory object
|
||||||
|
/// * `fdt_load_offset` - The offset into physical memory for the device tree
|
||||||
|
/// * `android_fstab` - the File object for the android fstab
|
||||||
|
pub fn create_fdt(
|
||||||
|
fdt_max_size: usize,
|
||||||
|
guest_mem: &GuestMemory,
|
||||||
|
fdt_load_offset: u64,
|
||||||
|
android_fstab: &mut File,
|
||||||
|
) -> Result<(), Box<Error>> {
|
||||||
|
// Reserve space for the setup_data
|
||||||
|
let fdt_data_size = fdt_max_size - mem::size_of::<setup_data>();
|
||||||
|
|
||||||
|
let mut fdt = vec![0; fdt_data_size];
|
||||||
|
start_fdt(&mut fdt, fdt_data_size)?;
|
||||||
|
|
||||||
|
// The whole thing is put into one giant node with some top level properties
|
||||||
|
begin_node(&mut fdt, "")?;
|
||||||
|
begin_node(&mut fdt, "firmware")?;
|
||||||
|
begin_node(&mut fdt, "android")?;
|
||||||
|
property_string(&mut fdt, "compatible", "android,firmware")?;
|
||||||
|
begin_node(&mut fdt, "fstab")?;
|
||||||
|
property_string(&mut fdt, "compatible", "android,fstab")?;
|
||||||
|
let file = BufReader::new(android_fstab);
|
||||||
|
for line in file.lines().filter_map(|l| l.ok()) {
|
||||||
|
let vec = line.split(" ").collect::<Vec<&str>>();
|
||||||
|
assert_eq!(vec.len(), 5);
|
||||||
|
let partition = &vec[1][1..];
|
||||||
|
begin_node(&mut fdt, partition)?;
|
||||||
|
property_string(&mut fdt, "compatible", &("android,".to_owned() + partition))?;
|
||||||
|
property_string(&mut fdt, "dev", vec[0])?;
|
||||||
|
property_string(&mut fdt, "type", vec[2])?;
|
||||||
|
property_string(&mut fdt, "mnt_flags", vec[3])?;
|
||||||
|
property_string(&mut fdt, "fsmgr_flags", vec[4])?;
|
||||||
|
end_node(&mut fdt)?;
|
||||||
|
}
|
||||||
|
end_node(&mut fdt)?;
|
||||||
|
end_node(&mut fdt)?;
|
||||||
|
end_node(&mut fdt)?;
|
||||||
|
end_node(&mut fdt)?;
|
||||||
|
|
||||||
|
// Allocate another buffer so we can format and then write fdt to guest
|
||||||
|
let mut fdt_final = vec![0; fdt_data_size];
|
||||||
|
finish_fdt(&mut fdt, &mut fdt_final, fdt_data_size)?;
|
||||||
|
|
||||||
|
let mut hdr: setup_data = Default::default();
|
||||||
|
hdr.next = 0;
|
||||||
|
hdr.type_ = SETUP_DTB;
|
||||||
|
hdr.len = fdt_data_size as u32;
|
||||||
|
|
||||||
|
assert!(fdt_data_size as u64 <= X86_64_FDT_MAX_SIZE);
|
||||||
|
|
||||||
|
let fdt_address = GuestAddress(fdt_load_offset);
|
||||||
|
guest_mem
|
||||||
|
.checked_offset(fdt_address, fdt_data_size as u64)
|
||||||
|
.ok_or(Error::FdtGuestMemoryWriteError)?;
|
||||||
|
guest_mem
|
||||||
|
.write_obj_at_addr(hdr, fdt_address)
|
||||||
|
.map_err(|_| Error::FdtGuestMemoryWriteError)?;
|
||||||
|
|
||||||
|
let fdt_data_address = GuestAddress(fdt_load_offset + mem::size_of::<setup_data>() as u64);
|
||||||
|
let written = guest_mem
|
||||||
|
.write_at_addr(fdt_final.as_slice(), fdt_data_address)
|
||||||
|
.map_err(|_| Error::FdtGuestMemoryWriteError)?;
|
||||||
|
if written < fdt_data_size {
|
||||||
|
return Err(Box::new(Error::FdtGuestMemoryWriteError));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ extern crate resources;
|
||||||
extern crate sync;
|
extern crate sync;
|
||||||
extern crate sys_util;
|
extern crate sys_util;
|
||||||
|
|
||||||
|
mod fdt;
|
||||||
|
|
||||||
|
const X86_64_FDT_MAX_SIZE: u64 = 0x200000;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#[allow(non_camel_case_types)]
|
#[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.
|
// 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::boot_params {}
|
||||||
|
unsafe impl data_model::DataInit for bootparam::setup_data {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
|
@ -159,11 +164,14 @@ const X86_64_IRQ_BASE: u32 = 5;
|
||||||
|
|
||||||
fn configure_system(
|
fn configure_system(
|
||||||
guest_mem: &GuestMemory,
|
guest_mem: &GuestMemory,
|
||||||
|
_mem_size: u64,
|
||||||
kernel_addr: GuestAddress,
|
kernel_addr: GuestAddress,
|
||||||
cmdline_addr: GuestAddress,
|
cmdline_addr: GuestAddress,
|
||||||
cmdline_size: usize,
|
cmdline_size: usize,
|
||||||
num_cpus: u8,
|
num_cpus: u8,
|
||||||
pci_irqs: Vec<(u32, PciInterruptPin)>,
|
pci_irqs: Vec<(u32, PciInterruptPin)>,
|
||||||
|
android_fstab: &mut Option<File>,
|
||||||
|
kernel_end: u64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
const EBDA_START: u64 = 0x0009fc00;
|
const EBDA_START: u64 = 0x0009fc00;
|
||||||
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
|
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
|
||||||
|
@ -178,12 +186,18 @@ fn configure_system(
|
||||||
|
|
||||||
let mut params: boot_params = Default::default();
|
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.type_of_loader = KERNEL_LOADER_OTHER;
|
||||||
params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
|
params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
|
||||||
params.hdr.header = KERNEL_HDR_MAGIC;
|
params.hdr.header = KERNEL_HDR_MAGIC;
|
||||||
params.hdr.cmd_line_ptr = cmdline_addr.offset() as u32;
|
params.hdr.cmd_line_ptr = cmdline_addr.offset() as u32;
|
||||||
params.hdr.cmdline_size = cmdline_size as u32;
|
params.hdr.cmdline_size = cmdline_size as u32;
|
||||||
params.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
|
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)?;
|
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)
|
.write_obj_at_addr(params, zero_page_addr)
|
||||||
.map_err(|_| Error::ZeroPageSetup)?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,13 +339,16 @@ impl arch::LinuxArch for X8664arch {
|
||||||
|
|
||||||
// separate out load_kernel from other setup to get a specific error for
|
// separate out load_kernel from other setup to get a specific error for
|
||||||
// kernel loading
|
// 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(
|
Self::setup_system_memory(
|
||||||
&mem,
|
&mem,
|
||||||
components.memory_mb,
|
components.memory_mb,
|
||||||
vcpu_count,
|
vcpu_count,
|
||||||
&CString::new(cmdline).unwrap(),
|
&CString::new(cmdline).unwrap(),
|
||||||
pci_irqs,
|
pci_irqs,
|
||||||
|
components.android_fstab,
|
||||||
|
kernel_end,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(RunnableLinuxVm {
|
Ok(RunnableLinuxVm {
|
||||||
|
@ -348,9 +373,12 @@ impl X8664arch {
|
||||||
///
|
///
|
||||||
/// * `mem` - The memory to be used by the guest.
|
/// * `mem` - The memory to be used by the guest.
|
||||||
/// * `kernel_image` - the File object for the specified kernel.
|
/// * `kernel_image` - the File object for the specified kernel.
|
||||||
fn load_kernel(mem: &GuestMemory, mut kernel_image: &mut File) -> Result<()> {
|
fn load_kernel(mem: &GuestMemory, mut kernel_image: &mut File) -> Result<u64> {
|
||||||
kernel_loader::load_kernel(mem, GuestAddress(KERNEL_START_OFFSET), &mut kernel_image)?;
|
Ok(kernel_loader::load_kernel(
|
||||||
Ok(())
|
mem,
|
||||||
|
GuestAddress(KERNEL_START_OFFSET),
|
||||||
|
&mut kernel_image,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the system memory space should be called once per vm before
|
/// Configures the system memory space should be called once per vm before
|
||||||
|
@ -363,19 +391,24 @@ impl X8664arch {
|
||||||
/// * `cmdline` - the kernel commandline
|
/// * `cmdline` - the kernel commandline
|
||||||
fn setup_system_memory(
|
fn setup_system_memory(
|
||||||
mem: &GuestMemory,
|
mem: &GuestMemory,
|
||||||
_mem_size: u64,
|
mem_size: u64,
|
||||||
vcpu_count: u32,
|
vcpu_count: u32,
|
||||||
cmdline: &CStr,
|
cmdline: &CStr,
|
||||||
pci_irqs: Vec<(u32, PciInterruptPin)>,
|
pci_irqs: Vec<(u32, PciInterruptPin)>,
|
||||||
|
mut android_fstab: Option<File>,
|
||||||
|
kernel_end: u64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)?;
|
kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)?;
|
||||||
configure_system(
|
configure_system(
|
||||||
mem,
|
mem,
|
||||||
|
mem_size,
|
||||||
GuestAddress(KERNEL_START_OFFSET),
|
GuestAddress(KERNEL_START_OFFSET),
|
||||||
GuestAddress(CMDLINE_OFFSET),
|
GuestAddress(CMDLINE_OFFSET),
|
||||||
cmdline.to_bytes().len() + 1,
|
cmdline.to_bytes().len() + 1,
|
||||||
vcpu_count as u8,
|
vcpu_count as u8,
|
||||||
pci_irqs,
|
pci_irqs,
|
||||||
|
&mut android_fstab,
|
||||||
|
kernel_end,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue