Skip to content

Commit 3db98cc

Browse files
committed
Support raw image (vmlinux) loading for Aarch64.
Change-Id: I12b8f50de9013e126980056444ca241fdfe7a0ee Signed-off-by: Michael Zhao <michael.zhao@arm.com>
1 parent 6cf23a8 commit 3db98cc

File tree

3 files changed

+189
-13
lines changed

3 files changed

+189
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ license = "Apache-2.0 AND BSD-3-Clause"
99
default = ["elf"]
1010
elf = []
1111
bzimage = []
12+
arm64_pe = []
1213

1314
[dependencies.vm-memory]
1415
git = "https://github.com/rust-vmm/vm-memory"

src/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,22 @@
1111

1212
//! A Linux kernel image loading crate.
1313
//!
14-
//! This crate offers support for loading raw ELF (vmlinux) and compressed
14+
//! This crate offers support for loading raw ELF/PE (vmlinux) and compressed
1515
//! big zImage (bzImage) kernel images.
1616
//! Support for any other kernel image format can be added by implementing
1717
//! the KernelLoader.
1818
//!
1919
//! # Platform support
2020
//!
2121
//! - x86_64
22+
//! - aarch64
2223
//!
23-
//! This crates only supports x86_64 platforms because it implements support
24-
//! for kernel image formats (vmlinux and bzImage) that are x86 specific.
24+
//! This crates supports kernel image in format:
25+
//! - vmlinux and bzImage on x86_64 platform.
26+
//! - PE on aarch64 platform.
2527
//!
26-
//! Extending it to support other kernel image formats (e.g. ARM's Image)
27-
//! will make it consumable by other platforms.
28+
//! Extending it to support other kernel image formats will make it consumable
29+
//! by other platforms.
2830
2931
pub mod cmdline;
3032
pub mod loader;

src/loader/mod.rs

Lines changed: 181 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,20 @@
1212
//! - [KernelLoaderResult](struct.KernelLoaderResult.html): the structure which loader
1313
//! returns to VMM to assist zero page construction and boot environment setup
1414
//! - [Elf](struct.Elf.html): elf image loader
15+
//! - [Arm64Pe](struct.Arm64Pe.html): arm64_pe image loader
1516
//! - [BzImage](struct.BzImage.html): bzImage loader
1617
1718
extern crate vm_memory;
1819

1920
use std::error::{self, Error as KernelLoaderError};
2021
use std::ffi::CStr;
2122
use std::fmt::{self, Display};
22-
#[cfg(any(feature = "elf", feature = "bzimage"))]
23-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
23+
#[cfg(any(feature = "elf", feature = "bzimage", feature = "arm64_pe"))]
24+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
2425
use std::io::SeekFrom;
2526
use std::io::{Read, Seek};
26-
#[cfg(feature = "elf")]
27-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
27+
#[cfg(any(feature = "elf", feature = "bzimage", feature = "arm64_pe"))]
28+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
2829
use std::mem;
2930

3031
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize};
@@ -42,8 +43,6 @@ pub mod bootparam;
4243
#[allow(non_upper_case_globals)]
4344
#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))]
4445
mod elf;
45-
#[cfg(any(feature = "elf", feature = "bzimage"))]
46-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
4746
mod struct_util;
4847

4948
#[derive(Debug, PartialEq)]
@@ -81,6 +80,10 @@ pub enum Error {
8180
ReadBzImageHeader,
8281
/// Unable to read bzImage compressed image.
8382
ReadBzImageCompressedKernel,
83+
/// Unable to read image magic number.
84+
ReadImageMagicNumber,
85+
/// Unable to read entry address.
86+
ReadEntryAddress,
8487
/// Unable to seek to kernel start.
8588
SeekKernelStart,
8689
/// Unable to seek to ELF start.
@@ -93,6 +96,14 @@ pub enum Error {
9396
SeekBzImageHeader,
9497
/// Unable to seek to bzImage compressed kernel.
9598
SeekBzImageCompressedKernel,
99+
/// Unable to seek to image start.
100+
SeekImageStart,
101+
/// Unable to seek to image end.
102+
SeekImageEnd,
103+
/// Unable to seek to magic image number.
104+
SeekImageMagicNumber,
105+
/// Unable to seek to entry address.
106+
SeekEntryAddress,
96107
}
97108

98109
/// A specialized `Result` type for the kernel loader.
@@ -119,12 +130,18 @@ impl error::Error for Error {
119130
Error::ReadProgramHeader => "Unable to read program header",
120131
Error::ReadBzImageHeader => "Unable to read bzImage header",
121132
Error::ReadBzImageCompressedKernel => "Unable to read bzImage compressed kernel",
133+
Error::ReadImageMagicNumber => "Unable to read image magic number",
134+
Error::ReadEntryAddress => "Unable to read entry address",
122135
Error::SeekKernelStart => "Unable to seek to kernel start",
123136
Error::SeekElfStart => "Unable to seek to elf start",
124137
Error::SeekProgramHeader => "Unable to seek to program header",
125138
Error::SeekBzImageEnd => "Unable to seek bzImage end",
126139
Error::SeekBzImageHeader => "Unable to seek bzImage header",
127140
Error::SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel",
141+
Error::SeekImageStart => "Unable to seek to image start",
142+
Error::SeekImageEnd => "Unable to seek to image end",
143+
Error::SeekImageMagicNumber => "Unable to seek to magic image number",
144+
Error::SeekEntryAddress => "Unable to seek to entry address",
128145
}
129146
}
130147
}
@@ -378,6 +395,117 @@ impl KernelLoader for BzImage {
378395
}
379396
}
380397

398+
#[cfg(feature = "arm64_pe")]
399+
#[cfg(target_arch = "aarch64")]
400+
/// Aarch64 kernel image support.
401+
pub struct Arm64Pe;
402+
403+
#[cfg(feature = "arm64_pe")]
404+
#[cfg(target_arch = "aarch64")]
405+
impl KernelLoader for Arm64Pe {
406+
/// Loads a Aarch64 kernel image
407+
///
408+
/// Aarch64 kernel boot protocol is specified in the kernel docs
409+
/// Documentation/arm/Booting and Documentation/arm64/booting.txt.
410+
///
411+
/// ======aarch64 kernel header========
412+
/// u32 code0; /* Executable code */
413+
/// u32 code1; /* Executable code */
414+
/// u64 text_offset; /* Image load offset, little endian */
415+
/// u64 image_size; /* Effective Image size, little endian */
416+
/// u64 flags; /* kernel flags, little endian */
417+
/// u64 res2 = 0; /* reserved */
418+
/// u64 res3 = 0; /* reserved */
419+
/// u64 res4 = 0; /* reserved */
420+
/// u32 magic = 0x644d5241; /* Magic number, little endian, "ARM\x64" */
421+
/// u32 res5; /* reserved (used for PE COFF offset) */
422+
/// ====================================
423+
///
424+
/// # Arguments
425+
///
426+
/// * `guest_mem` - The guest memory region the kernel is written to.
427+
/// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel.
428+
/// * `kernel_image` - Input vmlinux image.
429+
/// * `highmem_start_address` - This is the start of the high memory, kernel should above it.
430+
///
431+
/// # Returns
432+
/// * KernelLoaderResult
433+
fn load<F, M: GuestMemory>(
434+
guest_mem: &M,
435+
kernel_start: Option<GuestAddress>,
436+
kernel_image: &mut F,
437+
_highmem_start_address: Option<GuestAddress>,
438+
) -> Result<KernelLoaderResult>
439+
where
440+
F: Read + Seek,
441+
{
442+
const AARCH64_KERNEL_LOAD_ADDR: usize = 0x80000;
443+
const AARCH64_MAGIC_NUMBER: u32 = 0x644d_5241;
444+
const AARCH64_MAGIC_OFFSET: u64 =
445+
2 * mem::size_of::<u32>() as u64 + 6 * mem::size_of::<u64>() as u64; // This should total 56.
446+
const AARCH64_TEXT_OFFSET: u64 = 2 * mem::size_of::<u32>() as u64;
447+
let mut kernel_load_offset = AARCH64_KERNEL_LOAD_ADDR;
448+
449+
/* Look for the magic number inside the elf header. */
450+
kernel_image
451+
.seek(SeekFrom::Start(AARCH64_MAGIC_OFFSET))
452+
.map_err(|_| Error::SeekImageMagicNumber)?;
453+
let mut magic_number: u32 = 0;
454+
unsafe {
455+
struct_util::read_struct(kernel_image, &mut magic_number)
456+
.map_err(|_| Error::ReadImageMagicNumber)?
457+
}
458+
if u32::from_le(magic_number) != AARCH64_MAGIC_NUMBER {
459+
return Err(Error::InvalidElfMagicNumber);
460+
}
461+
462+
/* Look for the `text_offset` from the elf header. */
463+
kernel_image
464+
.seek(SeekFrom::Start(AARCH64_TEXT_OFFSET)) // This should total 8.
465+
.map_err(|_| Error::SeekEntryAddress)?;
466+
let mut hdrvals: [u64; 2] = [0; 2];
467+
unsafe {
468+
struct_util::read_struct(kernel_image, &mut hdrvals)
469+
.map_err(|_| Error::ReadEntryAddress)?;
470+
}
471+
/* Following the boot protocol mentioned above. */
472+
if u64::from_le(hdrvals[1]) != 0 {
473+
kernel_load_offset = u64::from_le(hdrvals[0]) as usize;
474+
}
475+
/* Get the total size of kernel image. */
476+
let kernel_size = kernel_image
477+
.seek(SeekFrom::End(0))
478+
.map_err(|_| Error::SeekImageEnd)?;
479+
480+
/* Last `seek` will leave the image with the cursor at its end, rewind it to start. */
481+
kernel_image
482+
.seek(SeekFrom::Start(0))
483+
.map_err(|_| Error::SeekImageStart)?;
484+
485+
let mut loader_result: KernelLoaderResult = Default::default();
486+
// where the kernel will be start loaded.
487+
let mem_offset = match kernel_start {
488+
Some(start) => GuestAddress(start.raw_value() + (kernel_load_offset as u64)),
489+
None => GuestAddress(kernel_load_offset as u64),
490+
};
491+
492+
loader_result.kernel_load = mem_offset;
493+
494+
guest_mem
495+
.read_exact_from(mem_offset, kernel_image, kernel_size as usize)
496+
.map_err(|_| Error::ReadKernelImage)?;
497+
498+
loader_result.kernel_end = mem_offset
499+
.raw_value()
500+
.checked_add(kernel_size as GuestUsize)
501+
.ok_or(Error::MemoryOverflow)?;
502+
503+
loader_result.setup_header = None;
504+
505+
Ok(loader_result)
506+
}
507+
}
508+
381509
/// Writes the command line string to the given memory slice.
382510
///
383511
/// # Arguments
@@ -412,8 +540,6 @@ pub fn load_cmdline<M: GuestMemory>(
412540
#[cfg(test)]
413541
mod test {
414542
use super::*;
415-
#[cfg(any(feature = "elf", feature = "bzimage"))]
416-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
417543
use std::io::Cursor;
418544
use vm_memory::{Address, GuestAddress, GuestMemoryMmap};
419545

@@ -440,6 +566,15 @@ mod test {
440566
v
441567
}
442568

569+
// Aarch64 image.
570+
#[cfg(feature = "arm64_pe")]
571+
#[cfg(target_arch = "aarch64")]
572+
fn make_aarch64_bin() -> Vec<u8> {
573+
let mut v = Vec::new();
574+
v.extend_from_slice(include_bytes!("test_arm64_pe.bin"));
575+
v
576+
}
577+
443578
#[allow(safe_packed_borrows)]
444579
#[allow(non_snake_case)]
445580
#[test]
@@ -562,6 +697,29 @@ mod test {
562697
);
563698
}
564699

700+
// Aarch64 image.
701+
#[cfg(feature = "arm64_pe")]
702+
#[cfg(target_arch = "aarch64")]
703+
#[test]
704+
fn load_aarch64() {
705+
const TEST_IMAGE_KERNEL_OFFSET: u64 = 0x8_0000; // test binary specific
706+
const TEST_IMAGE_KERNEL_SIZE: u64 = 0x50; // test binary specific
707+
let gm = create_guest_mem();
708+
let image = make_aarch64_bin();
709+
let kernel_addr = GuestAddress(0x80000);
710+
711+
assert_eq!(
712+
Ok(KernelLoaderResult {
713+
kernel_load: GuestAddress(kernel_addr.raw_value() + TEST_IMAGE_KERNEL_OFFSET),
714+
kernel_end: (kernel_addr.raw_value()
715+
+ TEST_IMAGE_KERNEL_OFFSET
716+
+ TEST_IMAGE_KERNEL_SIZE) as u64,
717+
setup_header: None
718+
}),
719+
Arm64Pe::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None)
720+
);
721+
}
722+
565723
#[test]
566724
fn cmdline_overflow() {
567725
let gm = create_guest_mem();
@@ -618,6 +776,21 @@ mod test {
618776
);
619777
}
620778

779+
#[cfg(feature = "arm64_pe")]
780+
#[cfg(target_arch = "aarch64")]
781+
#[test]
782+
fn bad_magic() {
783+
let gm = create_guest_mem();
784+
let kernel_addr = GuestAddress(0x0);
785+
let mut bad_image = make_aarch64_bin();
786+
bad_image[0x38] = 0x33;
787+
788+
assert_eq!(
789+
Err(Error::InvalidElfMagicNumber),
790+
Arm64Pe::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None)
791+
);
792+
}
793+
621794
#[cfg(feature = "elf")]
622795
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
623796
#[test]

0 commit comments

Comments
 (0)