use std::cmp::max;
use std::io;
use std::io::BufRead;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::mem::size_of_val;
use base::warn;
use base::FileGetLen;
use base::FileReadWriteAtVolatile;
use base::VolatileSlice;
use data_model::Le32;
use data_model::Le64;
use lz4_flex::frame::FrameDecoder as Lz4FrameDecoder;
use resources::AddressRange;
use vm_memory::GuestAddress;
use vm_memory::GuestMemory;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
use crate::Error;
use crate::LoadedKernel;
use crate::Result;
#[derive(Copy, Clone, AsBytes, FromZeroes, FromBytes)]
#[allow(unused)]
#[repr(C)]
struct Arm64ImageHeader {
code0: Le32,
code1: Le32,
text_offset: Le64,
image_size: Le64,
flags: Le64,
res2: Le64,
res3: Le64,
res4: Le64,
magic: Le32,
res5: Le32,
}
const ARM64_IMAGE_MAGIC: u32 = 0x644d5241; const ARM64_IMAGE_FLAG_BE_MASK: u64 = 0x1;
const ARM64_TEXT_OFFSET_DEFAULT: u64 = 0x80000;
impl Arm64ImageHeader {
fn parse_load_addr(&self, kernel_start: GuestAddress) -> Result<GuestAddress> {
let magic: u32 = self.magic.into();
if magic != ARM64_IMAGE_MAGIC {
return Err(Error::InvalidMagicNumber);
}
let flags: u64 = self.flags.into();
if flags & ARM64_IMAGE_FLAG_BE_MASK != 0 {
return Err(Error::BigEndianOnLittle);
}
let mut text_offset: u64 = self.text_offset.into();
let image_size: u64 = self.image_size.into();
if image_size == 0 {
warn!("arm64 Image header has an effective size of zero");
text_offset = ARM64_TEXT_OFFSET_DEFAULT;
}
kernel_start
.checked_add(text_offset)
.ok_or(Error::InvalidKernelOffset)
}
}
pub fn load_arm64_kernel<F>(
guest_mem: &GuestMemory,
kernel_start: GuestAddress,
kernel_image: &mut F,
) -> Result<LoadedKernel>
where
F: FileReadWriteAtVolatile + FileGetLen,
{
let mut header = Arm64ImageHeader::new_zeroed();
kernel_image
.read_exact_at_volatile(VolatileSlice::new(header.as_bytes_mut()), 0)
.map_err(|_| Error::ReadHeader)?;
let load_addr = header.parse_load_addr(kernel_start)?;
let file_size = kernel_image.get_len().map_err(|_| Error::SeekKernelEnd)?;
let load_size = usize::try_from(file_size).map_err(|_| Error::InvalidKernelSize)?;
let range_size = max(file_size, u64::from(header.image_size));
let guest_slice = guest_mem
.get_slice_at_addr(load_addr, load_size)
.map_err(|_| Error::ReadKernelImage)?;
kernel_image
.read_exact_at_volatile(guest_slice, 0)
.map_err(|_| Error::ReadKernelImage)?;
Ok(LoadedKernel {
size: file_size,
address_range: AddressRange::from_start_and_size(load_addr.offset(), range_size)
.ok_or(Error::InvalidKernelSize)?,
entry: load_addr,
})
}
fn load_arm64_kernel_from_reader<F: BufRead>(
guest_mem: &GuestMemory,
kernel_start: GuestAddress,
mut kernel_image: F,
) -> Result<LoadedKernel> {
let mut header = Arm64ImageHeader::new_zeroed();
let header_size = u64::try_from(size_of_val(&header)).unwrap();
kernel_image
.read_exact(header.as_bytes_mut())
.map_err(|_| Error::ReadHeader)?;
let load_addr = header.parse_load_addr(kernel_start)?;
guest_mem
.write_all_at_addr(header.as_bytes(), load_addr)
.map_err(|_| Error::ReadKernelImage)?;
let mut current_addr = load_addr
.checked_add(header_size)
.ok_or(Error::InvalidKernelSize)?;
loop {
let buf = match kernel_image.fill_buf() {
Ok([]) => break,
Ok(buf) => buf,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(_) => return Err(Error::ReadKernelImage),
};
guest_mem
.write_all_at_addr(buf, current_addr)
.map_err(|_| Error::ReadKernelImage)?;
let consumed = buf.len();
kernel_image.consume(consumed);
let offset = u64::try_from(consumed).map_err(|_| Error::InvalidKernelSize)?;
current_addr = current_addr
.checked_add(offset)
.ok_or(Error::InvalidKernelSize)?;
}
let file_size = current_addr.offset_from(load_addr);
let range_size = max(file_size, u64::from(header.image_size));
Ok(LoadedKernel {
size: file_size,
address_range: AddressRange::from_start_and_size(load_addr.offset(), range_size)
.ok_or(Error::InvalidKernelSize)?,
entry: load_addr,
})
}
pub fn load_arm64_kernel_lz4<F: Read + Seek>(
guest_mem: &GuestMemory,
kernel_start: GuestAddress,
mut kernel_image: F,
) -> Result<LoadedKernel> {
kernel_image
.seek(SeekFrom::Start(0))
.map_err(|_| Error::SeekKernelStart)?;
load_arm64_kernel_from_reader(
guest_mem,
kernel_start,
&mut Lz4FrameDecoder::new(kernel_image),
)
}
#[cfg(test)]
mod test {
use std::fs::File;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use tempfile::tempfile;
use vm_memory::GuestAddress;
use vm_memory::GuestMemory;
use crate::load_arm64_kernel;
use crate::load_arm64_kernel_lz4;
use crate::Error;
const MEM_SIZE: u64 = 0x200_0000;
fn create_guest_mem() -> GuestMemory {
GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
}
#[allow(clippy::unusual_byte_groupings)]
fn write_valid_kernel() -> File {
let mut f = tempfile().expect("failed to create tempfile");
f.write_all(&[0x00, 0xC0, 0x2E, 0x14]).unwrap(); f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap(); f.write_all(&0x00000000_00E70000u64.to_le_bytes()).unwrap(); f.write_all(&0x00000000_0000000Au64.to_le_bytes()).unwrap(); f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); f.write_all(&0x644D5241u32.to_le_bytes()).unwrap(); f.write_all(&0x00000000u32.to_le_bytes()).unwrap(); f.set_len(0xDC3808).unwrap();
f
}
fn mutate_file(mut f: &File, offset: u64, val: &[u8]) {
f.seek(SeekFrom::Start(offset))
.expect("failed to seek file");
f.write_all(val)
.expect("failed to write mutated value to file");
}
#[test]
fn load_arm64_valid() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel();
let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
assert_eq!(kernel.address_range.start, 0x107_0000);
assert_eq!(kernel.address_range.end, 0x1E3_3807);
assert_eq!(kernel.size, 0xDC_3808);
assert_eq!(kernel.entry, GuestAddress(0x107_0000));
}
#[test]
fn load_arm64_image_size_zero() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel();
mutate_file(&f, 16, &0u64.to_le_bytes());
let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
assert_eq!(kernel.address_range.start, 0x28_0000);
assert_eq!(kernel.address_range.end, 0x104_3807);
assert_eq!(kernel.size, 0xDC_3808);
assert_eq!(kernel.entry, GuestAddress(0x28_0000));
}
#[test]
fn load_arm64_bad_magic() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel();
mutate_file(&f, 56, &[0xCC, 0xCC, 0xCC, 0xCC]);
assert_eq!(
load_arm64_kernel(&gm, kernel_addr, &mut f),
Err(Error::InvalidMagicNumber)
);
}
fn write_valid_kernel_lz4() -> File {
let mut f = tempfile().expect("failed to create tempfile");
f.write_all(&0x184d2204u32.to_le_bytes()).unwrap(); f.write_all(&[0x44, 0x70, 0x1d]).unwrap(); f.write_all(&0x00004065u32.to_le_bytes()).unwrap();
f.write_all(&[
0x51, 0x00, 0xc0, 0x2e, 0x14, 0x00, 0x01, 0x00, 0x11, 0xe7, 0x06, 0x00, 0x11, 0x0a,
0x06, 0x00, 0x0f, 0x02, 0x00, 0x0f, 0x4f, 0x41, 0x52, 0x4d, 0x64, 0x26, 0x00, 0x0f,
0x0f, 0x02, 0x00,
])
.unwrap();
f.write_all(&[0xff; 16447]).unwrap();
f.write_all(&0x000050c9u32.to_le_bytes()).unwrap();
f.write_all(&[
0x00, 0x00, 0x00, 0x4b, 0x40, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
])
.unwrap();
f.write_all(&[0xff; 16448]).unwrap();
f.write_all(&0x00005027u32.to_le_bytes()).unwrap();
f.write_all(&[
0x00, 0x00, 0x00, 0x4b, 0x40, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
])
.unwrap();
f.write_all(&[0xff; 16448]).unwrap();
f.write_all(&0x00005027u32.to_le_bytes()).unwrap();
f.write_all(&[
0x00, 0x00, 0x00, 0x5f, 0x1c, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
])
.unwrap();
f.write_all(&[0xff; 7252]).unwrap();
f.write_all(&[0x43, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00])
.unwrap();
f.write_all(&0x00000000u32.to_le_bytes()).unwrap();
f.write_all(&0x22a9944cu32.to_le_bytes()).unwrap();
f
}
fn write_valid_kernel_lz4_legacy() -> File {
let mut f = tempfile().expect("failed to create tempfile");
f.write_all(&0x184c2102u32.to_le_bytes()).unwrap(); f.write_all(&0x000080a6u32.to_le_bytes()).unwrap();
f.write_all(&[
0x51, 0x00, 0xc0, 0x2e, 0x14, 0x00, 0x01, 0x00, 0x11, 0xe7, 0x06, 0x00, 0x11, 0x0a,
0x06, 0x00, 0x0f, 0x02, 0x00, 0x0f, 0x4f, 0x41, 0x52, 0x4d, 0x64, 0x26, 0x00, 0x0f,
0x0f, 0x02, 0x00,
])
.unwrap();
f.write_all(&[0xff; 32896]).unwrap();
f.write_all(&0x0000500au32.to_le_bytes()).unwrap();
f.write_all(&[
0x00, 0x00, 0x00, 0x9f, 0x5c, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
])
.unwrap();
f.write_all(&[0xff; 23700]).unwrap();
f.write_all(&[0x83, 0x50, 0x00]).unwrap();
f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap();
f
}
#[test]
fn load_arm64_lz4_valid() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel_lz4();
let kernel = load_arm64_kernel_lz4(&gm, kernel_addr, &mut f).unwrap();
assert_eq!(kernel.address_range.start, 0x107_0000);
assert_eq!(kernel.address_range.end, 0x1E3_3807);
assert_eq!(kernel.size, 0xDC_3808);
assert_eq!(kernel.entry, GuestAddress(0x107_0000));
}
#[test]
fn load_arm64_lz4_bad_magic() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel_lz4();
mutate_file(&f, 0, &[0xCC, 0xCC, 0xCC, 0xCC]);
assert_eq!(
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
Err(Error::ReadHeader)
);
}
#[test]
fn load_arm64_lz4_bad_block() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel_lz4();
mutate_file(&f, 7, &[0xCC, 0xCC, 0xCC, 0xCC]);
assert_eq!(
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
Err(Error::ReadHeader)
);
}
#[test]
fn load_arm64_lz4_legacy_valid() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel_lz4_legacy();
let kernel = load_arm64_kernel_lz4(&gm, kernel_addr, &mut f).unwrap();
assert_eq!(kernel.address_range.start, 0x107_0000);
assert_eq!(kernel.address_range.end, 0x1E3_3807);
assert_eq!(kernel.size, 0xDC_3808);
assert_eq!(kernel.entry, GuestAddress(0x107_0000));
}
#[test]
fn load_arm64_lz4_legacy_bad_magic() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel_lz4_legacy();
mutate_file(&f, 0, &[0xCC, 0xCC, 0xCC, 0xCC]);
assert_eq!(
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
Err(Error::ReadHeader)
);
}
#[test]
fn load_arm64_lz4_legacy_bad_block() {
let gm = create_guest_mem();
let kernel_addr = GuestAddress(2 * 1024 * 1024);
let mut f = write_valid_kernel_lz4_legacy();
mutate_file(&f, 4, &[0xCC, 0xCC, 0xCC, 0xCC]);
assert_eq!(
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
Err(Error::ReadHeader)
);
}
}