diff options
author | Daniel Verkamp <dverkamp@chromium.org> | 2018-12-11 16:29:26 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-02-07 03:02:08 -0800 |
commit | e403f5ccd0581ec62fbfb86de00b8c01958ffa67 (patch) | |
tree | 8bc44cbdeba1e57780a27d8092918b00a870bb54 | |
parent | e54b33834c6adba8921947330583afa19fbd100a (diff) | |
download | crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.tar crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.tar.gz crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.tar.bz2 crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.tar.lz crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.tar.xz crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.tar.zst crosvm-e403f5ccd0581ec62fbfb86de00b8c01958ffa67.zip |
linux: add support for loading an initrd
Based on Linux boot protocol references: - x86: Documentation/x86/boot.txt - arm: Documentation/devicetree/bindings/chosen.txt BUG=None TEST=Boot Alpine Linux netboot initrd on x86_64 and aarch64 Change-Id: If4730765638f0a0b8bb8f63203c98e4765a354ee Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1407221 Tested-by: kokoro <noreply+kokoro@google.com>
-rw-r--r-- | aarch64/src/fdt.rs | 16 | ||||
-rw-r--r-- | aarch64/src/lib.rs | 26 | ||||
-rw-r--r-- | arch/src/lib.rs | 1 | ||||
-rw-r--r-- | src/linux.rs | 12 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | x86_64/src/lib.rs | 34 |
6 files changed, 89 insertions, 6 deletions
diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs index 0780a55..e8ab335 100644 --- a/aarch64/src/fdt.rs +++ b/aarch64/src/fdt.rs @@ -165,11 +165,21 @@ fn create_psci_node(fdt: &mut Vec<u8>) -> Result<(), Box<Error>> { Ok(()) } -fn create_chosen_node(fdt: &mut Vec<u8>, cmdline: &CStr) -> Result<(), Box<Error>> { +fn create_chosen_node( + fdt: &mut Vec<u8>, + cmdline: &CStr, + initrd: Option<(GuestAddress, usize)>, +) -> Result<(), Box<Error>> { begin_node(fdt, "chosen")?; property_u32(fdt, "linux,pci-probe-only", 1)?; property_cstring(fdt, "bootargs", cmdline)?; property_u64(fdt, "kaslr", 0)?; + if let Some((initrd_addr, initrd_size)) = initrd { + let initrd_start = initrd_addr.offset() as u32; + let initrd_end = initrd_start + initrd_size as u32; + property_u32(fdt, "linux,initrd-start", initrd_start)?; + property_u32(fdt, "linux,initrd-end", initrd_end)?; + } end_node(fdt)?; Ok(()) @@ -286,6 +296,7 @@ fn create_rtc_node(fdt: &mut Vec<u8>) -> Result<(), Box<Error>> { /// * `num_cpus` - Number of virtual CPUs the guest will have /// * `fdt_load_offset` - The offset into physical memory for the device tree /// * `cmdline` - The kernel commandline +/// * `initrd` - An optional tuple of initrd guest physical address and size pub fn create_fdt( fdt_max_size: usize, guest_mem: &GuestMemory, @@ -293,6 +304,7 @@ pub fn create_fdt( num_cpus: u32, fdt_load_offset: u64, cmdline: &CStr, + initrd: Option<(GuestAddress, usize)>, ) -> Result<(), Box<Error>> { let mut fdt = vec![0; fdt_max_size]; start_fdt(&mut fdt, fdt_max_size)?; @@ -304,7 +316,7 @@ pub fn create_fdt( property_u32(&mut fdt, "#address-cells", 0x2)?; property_u32(&mut fdt, "#size-cells", 0x2)?; - create_chosen_node(&mut fdt, cmdline)?; + create_chosen_node(&mut fdt, cmdline, initrd)?; create_memory_node(&mut fdt, guest_mem)?; create_cpu_nodes(&mut fdt, num_cpus)?; create_gic_node(&mut fdt)?; diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index 6bbc775..b03c3a4 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -38,6 +38,7 @@ mod fdt; // We place the kernel at offset 8MB const AARCH64_KERNEL_OFFSET: u64 = 0x80000; const AARCH64_FDT_MAX_SIZE: u64 = 0x200000; +const AARCH64_INITRD_ALIGN: u64 = 0x1000000; // These constants indicate the address space used by the ARM vGIC. const AARCH64_GIC_DIST_SIZE: u64 = 0x10000; @@ -135,6 +136,8 @@ pub enum Error { FDTCreateFailure(Box<error::Error>), /// Kernel could not be loaded KernelLoadFailure(arch::LoadImageError), + /// Initrd could not be loaded + InitrdLoadFailure(arch::LoadImageError), /// Failure to Create GIC CreateGICFailure(sys_util::Error), /// Couldn't register PCI bus. @@ -159,6 +162,7 @@ impl error::Error for Error { &Error::CreateVcpu(_) => "failed to create VCPU", &Error::FDTCreateFailure(_) => "FDT could not be created", &Error::KernelLoadFailure(_) => "Kernel cound not be loaded", + &Error::InitrdLoadFailure(_) => "initrd cound not be loaded", &Error::CreateGICFailure(_) => "Failure to create GIC", &Error::RegisterPci(_) => "error registering PCI bus", &Error::RegisterVsock(_) => "error registering virtual socket device", @@ -255,19 +259,22 @@ impl arch::LinuxArch for AArch64 { // separate out kernel loading from other setup to get a specific error for // kernel loading - arch::load_image( + let kernel_size = arch::load_image( &mem, &mut components.kernel_image, get_kernel_addr(), u64::max_value(), ) .map_err(Error::KernelLoadFailure)?; + let kernel_end = get_kernel_addr().offset() + kernel_size as u64; Self::setup_system_memory( &mem, components.memory_mb, vcpu_count, &CString::new(cmdline).unwrap(), + components.initrd_image, pci_irqs, + kernel_end, )?; Ok(RunnableLinuxVm { @@ -291,8 +298,24 @@ impl AArch64 { mem_size: u64, vcpu_count: u32, cmdline: &CStr, + initrd_file: Option<File>, pci_irqs: Vec<(u32, PciInterruptPin)>, + kernel_end: u64, ) -> Result<()> { + let initrd = match initrd_file { + Some(initrd_file) => { + let mut initrd_file = initrd_file; + let initrd_addr = + (kernel_end + (AARCH64_INITRD_ALIGN - 1)) & !(AARCH64_INITRD_ALIGN - 1); + let initrd_max_size = mem_size - (initrd_addr - AARCH64_PHYS_MEM_START); + let initrd_addr = GuestAddress(initrd_addr); + let initrd_size = + arch::load_image(mem, &mut initrd_file, initrd_addr, initrd_max_size) + .map_err(Error::InitrdLoadFailure)?; + Some((initrd_addr, initrd_size)) + } + None => None, + }; fdt::create_fdt( AARCH64_FDT_MAX_SIZE as usize, mem, @@ -300,6 +323,7 @@ impl AArch64 { vcpu_count, fdt_offset(mem_size), cmdline, + initrd, )?; Ok(()) } diff --git a/arch/src/lib.rs b/arch/src/lib.rs index e6814bc..8a9c2d8 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -42,6 +42,7 @@ pub struct VmComponents { pub vcpu_count: u32, pub kernel_image: File, pub android_fstab: Option<File>, + pub initrd_image: Option<File>, pub extra_kernel_params: Vec<String>, pub wayland_dmabuf: bool, } diff --git a/src/linux.rs b/src/linux.rs index beb1021..b81bc71 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -69,6 +69,7 @@ pub enum Error { NetDeviceNew(devices::virtio::NetError), NoVarEmpty, OpenAndroidFstab(PathBuf, io::Error), + OpenInitrd(PathBuf, io::Error), OpenKernel(PathBuf, io::Error), P9DeviceNew(devices::virtio::P9Error), PollContextAdd(sys_util::Error), @@ -126,6 +127,7 @@ 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::OpenInitrd(p, e) => write!(f, "failed to open initrd {:?}: {}", 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) @@ -966,6 +968,15 @@ pub fn run_config(cfg: Config) -> Result<()> { // quickly. let sigchld_fd = SignalFd::new(libc::SIGCHLD).map_err(Error::CreateSignalFd)?; + let initrd_image = if let Some(ref initrd_path) = cfg.initrd_path { + Some( + File::open(initrd_path.as_path()) + .map_err(|e| Error::OpenInitrd(initrd_path.clone(), e))?, + ) + } else { + None + }; + let components = VmComponents { memory_mb: (cfg.memory.unwrap_or(256) << 20) as u64, vcpu_count: cfg.vcpu_count.unwrap_or(1), @@ -978,6 +989,7 @@ pub fn run_config(cfg: Config) -> Result<()> { File::open(x.as_path()).map_err(|e| Error::OpenAndroidFstab(x.to_path_buf(), e)) }) .map_or(Ok(None), |v| v.map(Some))?, + initrd_image, extra_kernel_params: cfg.params.clone(), wayland_dmabuf: cfg.wayland_dmabuf, }; diff --git a/src/main.rs b/src/main.rs index 31019f9..8a4fce0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,6 +96,7 @@ pub struct Config { memory: Option<usize>, kernel_path: PathBuf, android_fstab: Option<PathBuf>, + initrd_path: Option<PathBuf>, params: Vec<String>, socket_path: Option<PathBuf>, plugin: Option<PathBuf>, @@ -130,6 +131,7 @@ impl Default for Config { memory: None, kernel_path: PathBuf::default(), android_fstab: None, + initrd_path: None, params: Vec::new(), socket_path: None, plugin: None, @@ -569,6 +571,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: "split-irqchip" => { cfg.split_irqchip = true; } + "initrd" => { + cfg.initrd_path = Some(PathBuf::from(value.unwrap().to_owned())); + } "help" => return Err(argument::Error::PrintHelp), _ => unreachable!(), } @@ -579,6 +584,7 @@ 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('i', "initrd", "PATH", "Initial ramdisk to load."), Argument::short_value('p', "params", "PARAMS", diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index a56accf..69dcf76 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -113,6 +113,7 @@ pub enum Error { RegisterVsock(arch::DeviceRegistrationError), LoadCmdline(kernel_loader::Error), LoadKernel(kernel_loader::Error), + LoadInitrd(arch::LoadImageError), /// Error writing the zero page of guest memory. ZeroPageSetup, /// The zero page extends past the end of guest_mem. @@ -138,6 +139,7 @@ impl error::Error for Error { Error::RegisterVsock(_) => "error registering virtual socket device", Error::LoadCmdline(_) => "Error Loading command line", Error::LoadKernel(_) => "Error Loading Kernel", + Error::LoadInitrd(_) => "Error loading initrd", Error::ZeroPageSetup => "Error writing the zero page of guest memory", Error::ZeroPagePastRamEnd => "The zero page extends past the end of guest_mem", Error::E820Configuration => "Invalid e820 setup params", @@ -174,6 +176,7 @@ fn configure_system( num_cpus: u8, pci_irqs: Vec<(u32, PciInterruptPin)>, setup_data: Option<GuestAddress>, + initrd: Option<(GuestAddress, usize)>, ) -> Result<()> { const EBDA_START: u64 = 0x0009fc00; const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55; @@ -197,6 +200,10 @@ fn configure_system( if let Some(setup_data) = setup_data { params.hdr.setup_data = setup_data.offset(); } + if let Some((initrd_addr, initrd_size)) = initrd { + params.hdr.ramdisk_image = initrd_addr.offset() as u32; + params.hdr.ramdisk_size = initrd_size as u32; + } add_e820_entry(&mut params, 0, EBDA_START, E820_RAM)?; @@ -341,6 +348,7 @@ impl arch::LinuxArch for X8664arch { components.memory_mb, vcpu_count, &CString::new(cmdline).unwrap(), + components.initrd_image, pci_irqs, components.android_fstab, kernel_end, @@ -384,11 +392,13 @@ impl X8664arch { /// * `mem` - The memory to be used by the guest. /// * `vcpu_count` - Number of virtual CPUs the guest will have. /// * `cmdline` - the kernel commandline + /// * `initrd_file` - an initial ramdisk image fn setup_system_memory( mem: &GuestMemory, mem_size: u64, vcpu_count: u32, cmdline: &CStr, + initrd_file: Option<File>, pci_irqs: Vec<(u32, PciInterruptPin)>, android_fstab: Option<File>, kernel_end: u64, @@ -396,24 +406,41 @@ impl X8664arch { kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)?; // Track the first free address after the kernel - this is where extra - // data like the device tree blob will be loaded. - let free_addr = kernel_end; + // data like the device tree blob and initrd will be loaded. + let mut free_addr = kernel_end; let setup_data = if let Some(fstab) = android_fstab { let mut fstab = fstab; let free_addr_aligned = (((free_addr + 64 - 1) / 64) * 64) + 64; let dtb_start = GuestAddress(free_addr_aligned); - let _dtb_size = fdt::create_fdt( + let dtb_size = fdt::create_fdt( X86_64_FDT_MAX_SIZE as usize, mem, dtb_start.offset(), &mut fstab, )?; + free_addr = dtb_start.offset() + dtb_size as u64; Some(dtb_start) } else { None }; + let initrd = match initrd_file { + Some(mut initrd_file) => { + let initrd_start = free_addr; + let initrd_max_size = mem_size - initrd_start; + let initrd_size = arch::load_image( + mem, + &mut initrd_file, + GuestAddress(initrd_start), + initrd_max_size, + ) + .map_err(Error::LoadInitrd)?; + Some((GuestAddress(initrd_start), initrd_size)) + } + None => None, + }; + configure_system( mem, mem_size, @@ -423,6 +450,7 @@ impl X8664arch { vcpu_count as u8, pci_irqs, setup_data, + initrd, )?; Ok(()) } |