From 3007ff3cf408f9e6a2e0731a4fa7d6f8f65dfa47 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Fri, 17 May 2019 10:55:45 -0700 Subject: x86_64: load initrd at max address This matches behavior of other bootloaders (grub2, iPXE), and the kernel seems to be relying on this; decompression of the initrd fails if the initrd is loaded right after the kernel as before, but succeeds if loaded at the maximum address. BUG=None TEST=Boot Debian kernel + initrd on workstation Change-Id: If7712efb05f55ef413a419dfe276ed3f68c335b7 Signed-off-by: Daniel Verkamp Reviewed-on: https://chromium-review.googlesource.com/1616989 Tested-by: kokoro Legacy-Commit-Queue: Commit Bot Reviewed-by: Dylan Reid --- arch/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ x86_64/src/lib.rs | 22 ++++++++++++++++------ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/arch/src/lib.rs b/arch/src/lib.rs index b9791c8..433d89a 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -271,6 +271,7 @@ pub fn add_serial_devices( /// Errors for image loading. #[derive(Debug)] pub enum LoadImageError { + BadAlignment(u64), Seek(io::Error), ImageSizeTooLarge(u64), ReadToMemory(GuestMemoryError), @@ -281,6 +282,7 @@ impl Display for LoadImageError { use self::LoadImageError::*; match self { + BadAlignment(a) => write!(f, "Alignment not a power of two: {}", a), Seek(e) => write!(f, "Seek failed: {}", e), ImageSizeTooLarge(size) => write!(f, "Image size too large: {}", size), ReadToMemory(e) => write!(f, "Reading image into memory failed: {}", e), @@ -326,3 +328,54 @@ where Ok(size) } + +/// Load an image from a file into guest memory at the highest possible address. +/// +/// # Arguments +/// +/// * `guest_mem` - The memory to be used by the guest. +/// * `image` - The file containing the image to be loaded. +/// * `min_guest_addr` - The minimum address of the start of the image. +/// * `max_guest_addr` - The address to load the last byte of the image. +/// * `align` - The minimum alignment of the start address of the image in bytes +/// (must be a power of two). +/// +/// The guest address and size in bytes of the loaded image are returned. +pub fn load_image_high( + guest_mem: &GuestMemory, + image: &mut F, + min_guest_addr: GuestAddress, + max_guest_addr: GuestAddress, + align: u64, +) -> Result<(GuestAddress, usize), LoadImageError> +where + F: Read + Seek, +{ + if !align.is_power_of_two() { + return Err(LoadImageError::BadAlignment(align)); + } + + let max_size = max_guest_addr.offset_from(min_guest_addr) & !(align - 1); + let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?; + + if size > usize::max_value() as u64 || size > max_size { + return Err(LoadImageError::ImageSizeTooLarge(size)); + } + + image + .seek(SeekFrom::Start(0)) + .map_err(LoadImageError::Seek)?; + + // Load image at the maximum aligned address allowed. + // The subtraction cannot underflow because of the size checks above. + let guest_addr = GuestAddress((max_guest_addr.offset() - size) & !(align - 1)); + + // This is safe due to the bounds check above. + let size = size as usize; + + guest_mem + .read_to_memory(guest_addr, image, size) + .map_err(LoadImageError::ReadToMemory)?; + + Ok((guest_addr, size)) +} diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index eafdc61..0a2185c 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -458,16 +458,26 @@ impl X8664arch { 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( + let mut initrd_addr_max = u64::from(params.hdr.initrd_addr_max); + // Default initrd_addr_max for old kernels (see Documentation/x86/boot.txt). + if initrd_addr_max == 0 { + initrd_addr_max = 0x37FFFFFF; + } + + let mem_max = mem.end_addr().offset() - 1; + if initrd_addr_max > mem_max { + initrd_addr_max = mem_max; + } + + let (initrd_start, initrd_size) = arch::load_image_high( mem, &mut initrd_file, - GuestAddress(initrd_start), - initrd_max_size, + GuestAddress(free_addr), + GuestAddress(initrd_addr_max), + sys_util::pagesize() as u64, ) .map_err(Error::LoadInitrd)?; - Some((GuestAddress(initrd_start), initrd_size)) + Some((initrd_start, initrd_size)) } None => None, }; -- cgit 1.4.1