summary refs log tree commit diff
diff options
context:
space:
mode:
authorKansho Nishida <kansho@google.com>2019-12-18 13:13:14 +0900
committerCommit Bot <commit-bot@chromium.org>2020-02-03 05:45:14 +0000
commit282115bcdb3842e507357ec131d1c3685551fb1b (patch)
treecdf9e5068b1b913cf8be1fd8189851c388ecefbe
parent95885316c25147ffe85700e4db5477aa8e516005 (diff)
downloadcrosvm-282115bcdb3842e507357ec131d1c3685551fb1b.tar
crosvm-282115bcdb3842e507357ec131d1c3685551fb1b.tar.gz
crosvm-282115bcdb3842e507357ec131d1c3685551fb1b.tar.bz2
crosvm-282115bcdb3842e507357ec131d1c3685551fb1b.tar.lz
crosvm-282115bcdb3842e507357ec131d1c3685551fb1b.tar.xz
crosvm-282115bcdb3842e507357ec131d1c3685551fb1b.tar.zst
crosvm-282115bcdb3842e507357ec131d1c3685551fb1b.zip
crosvm: pstore works for ARCVM on x86_64
Adds support for pstore on ARCVM on x86_64.
The backend file of the buffer will be passed via argument of the crosvm.

BUG=b:144962428
TEST=kernel crash on eve-arcvm, check /sys/fs/pstore/console-ramoops-0
     Launch crostini manually on eve-arcvm

Change-Id: I29492ac7a9067aa2ae23eb03fbb942ab7dd3aa8d
Signed-off-by: Kansho Nishida <kansho@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1973391
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Kansho Nishida <kansho@chromium.org>
-rw-r--r--arch/src/lib.rs9
-rw-r--r--arch/src/pstore.rs74
-rw-r--r--resources/src/lib.rs2
-rw-r--r--src/crosvm.rs3
-rw-r--r--src/linux.rs1
-rw-r--r--src/main.rs43
-rw-r--r--x86_64/src/lib.rs29
7 files changed, 161 insertions, 0 deletions
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index ca9cd66..16cd6fa 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -4,6 +4,7 @@
 
 pub mod android;
 pub mod fdt;
+pub mod pstore;
 
 use std::collections::BTreeMap;
 use std::error::Error as StdError;
@@ -11,6 +12,7 @@ use std::fmt::{self, Display};
 use std::fs::File;
 use std::io::{self, Read, Seek, SeekFrom};
 use std::os::unix::io::AsRawFd;
+use std::path::PathBuf;
 use std::sync::Arc;
 
 use devices::virtio::VirtioDevice;
@@ -29,6 +31,12 @@ pub enum VmImage {
     Bios(File),
 }
 
+#[derive(Clone)]
+pub struct Pstore {
+    pub path: PathBuf,
+    pub size: u32,
+}
+
 /// Holds the pieces needed to build a VM. Passed to `build_vm` in the `LinuxArch` trait below to
 /// create a `RunnableLinuxVm`.
 pub struct VmComponents {
@@ -37,6 +45,7 @@ pub struct VmComponents {
     pub vcpu_affinity: Vec<usize>,
     pub vm_image: VmImage,
     pub android_fstab: Option<File>,
+    pub pstore: Option<Pstore>,
     pub initrd_image: Option<File>,
     pub extra_kernel_params: Vec<String>,
     pub wayland_dmabuf: bool,
diff --git a/arch/src/pstore.rs b/arch/src/pstore.rs
new file mode 100644
index 0000000..a06ea1b
--- /dev/null
+++ b/arch/src/pstore.rs
@@ -0,0 +1,74 @@
+// Copyright 2020 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 std::fmt::{self, Display};
+use std::fs::OpenOptions;
+use std::io;
+
+use crate::Pstore;
+use kvm::Vm;
+use resources::SystemAllocator;
+use resources::{Alloc, MmioType};
+use sys_util::{GuestAddress, MemoryMapping};
+
+/// Error for pstore.
+#[derive(Debug)]
+pub enum Error {
+    IoError(io::Error),
+    MmapError(sys_util::MmapError),
+    ResourcesError(resources::Error),
+    SysUtilError(sys_util::Error),
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            IoError(e) => write!(f, "failed to create pstore backend file: {}", e),
+            MmapError(e) => write!(f, "failed to get file mapped address: {}", e),
+            ResourcesError(e) => write!(f, "failed to allocate pstore region: {}", e),
+            SysUtilError(e) => write!(f, "file to add pstore region to mmio: {}", e),
+        }
+    }
+}
+
+impl std::error::Error for Error {}
+type Result<T> = std::result::Result<T, Error>;
+
+pub struct RamoopsRegion {
+    pub address: u64,
+    pub size: u32,
+}
+
+/// Creates a mmio memory region for pstore.
+pub fn create_memory_region(
+    vm: &mut Vm,
+    resources: &mut SystemAllocator,
+    pstore: &Pstore,
+) -> Result<RamoopsRegion> {
+    let file = OpenOptions::new()
+        .read(true)
+        .write(true)
+        .create(true)
+        .open(&pstore.path)
+        .map_err(Error::IoError)?;
+    file.set_len(pstore.size as u64).map_err(Error::IoError)?;
+
+    let address = resources
+        .mmio_allocator(MmioType::High)
+        .allocate(pstore.size as u64, Alloc::Pstore, "pstore".to_owned())
+        .map_err(Error::ResourcesError)?;
+
+    let memory_mapping =
+        MemoryMapping::from_fd(&file, pstore.size as usize).map_err(Error::MmapError)?;
+
+    vm.add_mmio_memory(GuestAddress(address), memory_mapping, false, false)
+        .map_err(Error::SysUtilError)?;
+
+    Ok(RamoopsRegion {
+        address,
+        size: pstore.size,
+    })
+}
diff --git a/resources/src/lib.rs b/resources/src/lib.rs
index 0e16786..a0c6c98 100644
--- a/resources/src/lib.rs
+++ b/resources/src/lib.rs
@@ -36,6 +36,8 @@ pub enum Alloc {
     GpuRenderNode,
     /// Pmem device region with associated device index.
     PmemDevice(usize),
+    /// pstore region.
+    Pstore,
 }
 
 #[derive(Debug, Eq, PartialEq)]
diff --git a/src/crosvm.rs b/src/crosvm.rs
index ad3d4d1..082e43c 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -16,6 +16,7 @@ use std::os::unix::io::RawFd;
 use std::path::PathBuf;
 use std::str::FromStr;
 
+use arch::Pstore;
 use devices::virtio::fs::passthrough;
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::GpuParameters;
@@ -136,6 +137,7 @@ pub struct Config {
     pub plugin_gid_maps: Vec<GidMap>,
     pub disks: Vec<DiskOption>,
     pub pmem_devices: Vec<DiskOption>,
+    pub pstore: Option<Pstore>,
     pub host_ip: Option<net::Ipv4Addr>,
     pub netmask: Option<net::Ipv4Addr>,
     pub mac_address: Option<net_util::MacAddress>,
@@ -184,6 +186,7 @@ impl Default for Config {
             plugin_gid_maps: Vec::new(),
             disks: Vec::new(),
             pmem_devices: Vec::new(),
+            pstore: None,
             host_ip: None,
             netmask: None,
             mac_address: None,
diff --git a/src/linux.rs b/src/linux.rs
index 144345e..a26e7bb 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -1513,6 +1513,7 @@ pub fn run_config(cfg: Config) -> Result<()> {
             .as_ref()
             .map(|x| File::open(x).map_err(|e| Error::OpenAndroidFstab(x.to_path_buf(), e)))
             .map_or(Ok(None), |v| v.map(Some))?,
+        pstore: cfg.pstore.clone(),
         initrd_image,
         extra_kernel_params: cfg.params.clone(),
         wayland_dmabuf: cfg.wayland_dmabuf,
diff --git a/src/main.rs b/src/main.rs
index 45efc0f..e053082 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,6 +16,7 @@ use std::string::String;
 use std::thread::sleep;
 use std::time::Duration;
 
+use arch::Pstore;
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
     linux, BindMount, Config, DiskOption, Executable, GidMap, SharedDir, TouchDeviceOption,
@@ -577,6 +578,47 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 block_size: sys_util::pagesize() as u32,
             });
         }
+        "pstore" => {
+            if cfg.pstore.is_some() {
+                return Err(argument::Error::TooManyArguments(
+                    "`pstore` already given".to_owned(),
+                ));
+            }
+
+            let value = value.unwrap();
+            let components: Vec<&str> = value.split(',').collect();
+            if components.len() != 2 {
+                return Err(argument::Error::InvalidValue {
+                    value: value.to_owned(),
+                    expected: "pstore must have exactly 2 components: path=<path>,size=<size>",
+                });
+            }
+            cfg.pstore = Some(Pstore {
+                path: {
+                    if components[0].len() <= 5 || !components[0].starts_with("path=") {
+                        return Err(argument::Error::InvalidValue {
+                            value: components[0].to_owned(),
+                            expected: "pstore path must follow with `path=`",
+                        });
+                    };
+                    PathBuf::from(&components[0][5..])
+                },
+                size: {
+                    if components[1].len() <= 5 || !components[1].starts_with("size=") {
+                        return Err(argument::Error::InvalidValue {
+                            value: components[1].to_owned(),
+                            expected: "pstore size must follow with `size=`",
+                        });
+                    };
+                    components[1][5..]
+                        .parse()
+                        .map_err(|_| argument::Error::InvalidValue {
+                            value: value.to_owned(),
+                            expected: "pstore size must be an integer",
+                        })?
+                },
+            });
+        }
         "host_ip" => {
             if cfg.host_ip.is_some() {
                 return Err(argument::Error::TooManyArguments(
@@ -1059,6 +1101,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
           Argument::value("rwqcow", "PATH", "Path to a writable qcow2 disk image. (Deprecated; use --rwdisk instead.)"),
           Argument::value("rw-pmem-device", "PATH", "Path to a writable disk image."),
           Argument::value("pmem-device", "PATH", "Path to a disk image."),
+          Argument::value("pstore", "path=PATH,size=SIZE", "Path to pstore buffer backend file follewed by size."),
           Argument::value("host_ip",
                           "IP",
                           "IP address to assign to host tap interface."),
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index fa5bc85..84f7dfd 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -87,6 +87,7 @@ pub enum Error {
     LoadCmdline(kernel_loader::Error),
     LoadInitrd(arch::LoadImageError),
     LoadKernel(kernel_loader::Error),
+    Pstore(arch::pstore::Error),
     RegisterIrqfd(sys_util::Error),
     RegisterVsock(arch::DeviceRegistrationError),
     SetLint(interrupts::Error),
@@ -132,6 +133,7 @@ impl Display for Error {
             LoadCmdline(e) => write!(f, "error loading command line: {}", e),
             LoadInitrd(e) => write!(f, "error loading initrd: {}", e),
             LoadKernel(e) => write!(f, "error loading Kernel: {}", e),
+            Pstore(e) => write!(f, "failed to allocate pstore region: {}", e),
             RegisterIrqfd(e) => write!(f, "error registering an IrqFd: {}", e),
             RegisterVsock(e) => write!(f, "error registering virtual socket device: {}", e),
             SetLint(e) => write!(f, "failed to set interrupts: {}", e),
@@ -369,6 +371,14 @@ impl arch::LinuxArch for X8664arch {
         let stdio_serial_num =
             Self::setup_serial_devices(&mut vm, &mut io_bus, serial_parameters, serial_jail)?;
 
+        let ramoops_region = match components.pstore {
+            Some(pstore) => Some(
+                arch::pstore::create_memory_region(&mut vm, &mut resources, &pstore)
+                    .map_err(Error::Pstore)?,
+            ),
+            None => None,
+        };
+
         match components.vm_image {
             VmImage::Bios(ref mut bios) => Self::load_bios(&mem, bios)?,
             VmImage::Kernel(ref mut kernel_image) => {
@@ -377,6 +387,25 @@ impl arch::LinuxArch for X8664arch {
                     cmdline.insert_str(&param).map_err(Error::Cmdline)?;
                 }
 
+                // It seems that default record_size is only 4096 byte even if crosvm allocates
+                // more memory. It means that one crash can only 4096 byte.
+                // Set record_size and console_size to 1/4 of allocated memory size.
+                // This configulation is same as the host.
+                if let Some(ramoops_region) = ramoops_region {
+                    let ramoops_opts = [
+                        ("mem_address", ramoops_region.address),
+                        ("mem_size", ramoops_region.size as u64),
+                        ("console_size", (ramoops_region.size / 4) as u64),
+                        ("record_size", (ramoops_region.size / 4) as u64),
+                        ("dump_oops", 1_u64),
+                    ];
+                    for (name, val) in &ramoops_opts {
+                        cmdline
+                            .insert_str(format!("ramoops.{}={:#x}", name, val))
+                            .map_err(Error::Cmdline)?;
+                    }
+                }
+
                 // separate out load_kernel from other setup to get a specific error for
                 // kernel loading
                 let (params, kernel_end) = Self::load_kernel(&mem, kernel_image)?;