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"
|
||||
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",
|
||||
|
|
|
@ -15,4 +15,3 @@ sys_util = { path = "../sys_util" }
|
|||
resources = { path = "../resources" }
|
||||
sync = { path = "../sync" }
|
||||
libc = "*"
|
||||
byteorder = "*"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" }
|
||||
|
|
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
|
||||
// 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,
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
|
|
11
src/linux.rs
11
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,
|
||||
};
|
||||
|
|
21
src/main.rs
21
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",
|
||||
|
|
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 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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue