Skip to content

Commit 05f9eae

Browse files
committed
Support raw image (vmlinux) loading for Aarch64.
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
1 parent 48d527e commit 05f9eae

File tree

3 files changed

+186
-24
lines changed

3 files changed

+186
-24
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+
aarch64_pe = []
1213

1314
[dependencies]
1415
vm-memory = {version = "0.1.0", features = ["backend-mmap"]}

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: 178 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,16 @@
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+
//! - [Aarch64Pe](struct.Aarch64Pe.html): aarch64_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"))]
2423
use std::io::SeekFrom;
2524
use std::io::{Read, Seek};
26-
#[cfg(feature = "elf")]
27-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
2825
use std::mem;
2926

3027
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize};
@@ -42,8 +39,6 @@ pub mod bootparam;
4239
#[allow(non_upper_case_globals)]
4340
#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))]
4441
mod elf;
45-
#[cfg(any(feature = "elf", feature = "bzimage"))]
46-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
4742
mod struct_util;
4843

4944
#[derive(Debug, PartialEq)]
@@ -55,8 +50,8 @@ pub enum Error {
5550
CommandLineCopy,
5651
/// Command line overflowed guest memory.
5752
CommandLineOverflow,
58-
/// Invalid ELF magic number
59-
InvalidElfMagicNumber,
53+
/// Invalid magic number
54+
InvalidMagicNumber,
6055
/// Invalid program header size.
6156
InvalidProgramHeaderSize,
6257
/// Invalid program header offset.
@@ -67,6 +62,8 @@ pub enum Error {
6762
InvalidEntryAddress,
6863
/// Invalid bzImage binary.
6964
InvalidBzImage,
65+
/// Invalid PE image binary.
66+
InvalidPeImage,
7067
/// Invalid kernel start address.
7168
InvalidKernelStartAddress,
7269
/// Memory to load kernel image is too small.
@@ -93,6 +90,14 @@ pub enum Error {
9390
SeekBzImageHeader,
9491
/// Unable to seek to bzImage compressed kernel.
9592
SeekBzImageCompressedKernel,
93+
/// Unable to seek to image start.
94+
SeekImageStart,
95+
/// Unable to seek to image end.
96+
SeekImageEnd,
97+
/// Unable to seek to magic image number.
98+
SeekImageMagicNumber,
99+
/// Unable to seek to entry address.
100+
SeekEntryAddress,
96101
}
97102

98103
/// A specialized `Result` type for the kernel loader.
@@ -106,13 +111,14 @@ impl error::Error for Error {
106111
}
107112
Error::CommandLineCopy => "Failed writing command line to guest memory",
108113
Error::CommandLineOverflow => "Command line overflowed guest memory",
109-
Error::InvalidElfMagicNumber => "Invalid Elf magic number",
114+
Error::InvalidMagicNumber => "Invalid magic number",
110115
Error::InvalidProgramHeaderSize => "Invalid program header size",
111116
Error::InvalidProgramHeaderOffset => "Invalid program header offset",
112117
Error::InvalidProgramHeaderAddress => "Invalid Program Header Address",
113118
Error::InvalidEntryAddress => "Invalid entry address",
114119
Error::InvalidBzImage => "Invalid bzImage",
115120
Error::InvalidKernelStartAddress => "Invalid kernel start address",
121+
Error::InvalidPeImage => "Invalid PE image",
116122
Error::MemoryOverflow => "Memory to load kernel image is not enough",
117123
Error::ReadElfHeader => "Unable to read elf header",
118124
Error::ReadKernelImage => "Unable to read kernel image",
@@ -125,6 +131,10 @@ impl error::Error for Error {
125131
Error::SeekBzImageEnd => "Unable to seek bzImage end",
126132
Error::SeekBzImageHeader => "Unable to seek bzImage header",
127133
Error::SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel",
134+
Error::SeekImageStart => "Unable to seek to image start",
135+
Error::SeekImageEnd => "Unable to seek to image end",
136+
Error::SeekImageMagicNumber => "Unable to seek to magic image number",
137+
Error::SeekEntryAddress => "Unable to seek to entry address",
128138
}
129139
}
130140
}
@@ -184,9 +194,6 @@ impl KernelLoader for Elf {
184194
/// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel.
185195
/// * `kernel_image` - Input vmlinux image.
186196
/// * `highmem_start_address` - This is the start of the high memory, kernel should above it.
187-
///
188-
/// # Returns
189-
/// * KernelLoaderResult
190197
fn load<F, M: GuestMemory>(
191198
guest_mem: &M,
192199
kernel_start: Option<GuestAddress>,
@@ -211,7 +218,7 @@ impl KernelLoader for Elf {
211218
|| ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
212219
|| ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
213220
{
214-
return Err(Error::InvalidElfMagicNumber);
221+
return Err(Error::InvalidMagicNumber);
215222
}
216223
if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
217224
return Err(Error::BigEndianElfOnLittle);
@@ -299,9 +306,6 @@ impl KernelLoader for BzImage {
299306
/// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel.
300307
/// * `kernel_image` - Input bzImage image.
301308
/// * `highmem_start_address` - This is the start of the high memory, kernel should above it.
302-
///
303-
/// # Returns
304-
/// * KernelLoaderResult
305309
fn load<F, M: GuestMemory>(
306310
guest_mem: &M,
307311
kernel_start: Option<GuestAddress>,
@@ -378,6 +382,116 @@ impl KernelLoader for BzImage {
378382
}
379383
}
380384

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

@@ -440,6 +552,15 @@ mod test {
440552
v
441553
}
442554

555+
// Aarch64 image.
556+
#[cfg(feature = "aarch64_pe")]
557+
#[cfg(target_arch = "aarch64")]
558+
fn make_aarch64_bin() -> Vec<u8> {
559+
let mut v = Vec::new();
560+
v.extend_from_slice(include_bytes!("test_pe.bin"));
561+
v
562+
}
563+
443564
#[allow(safe_packed_borrows)]
444565
#[allow(non_snake_case)]
445566
#[test]
@@ -562,6 +683,29 @@ mod test {
562683
);
563684
}
564685

686+
// Aarch64 image.
687+
#[cfg(feature = "aarch64_pe")]
688+
#[cfg(target_arch = "aarch64")]
689+
#[test]
690+
fn load_aarch64() {
691+
const TEST_IMAGE_KERNEL_OFFSET: u64 = 0x8_0000; // test binary specific
692+
const TEST_IMAGE_KERNEL_SIZE: u64 = 0x50; // test binary specific
693+
let gm = create_guest_mem();
694+
let image = make_aarch64_bin();
695+
let kernel_addr = GuestAddress(0x80000);
696+
697+
assert_eq!(
698+
Ok(KernelLoaderResult {
699+
kernel_load: GuestAddress(kernel_addr.raw_value() + TEST_IMAGE_KERNEL_OFFSET),
700+
kernel_end: (kernel_addr.raw_value()
701+
+ TEST_IMAGE_KERNEL_OFFSET
702+
+ TEST_IMAGE_KERNEL_SIZE) as u64,
703+
setup_header: None
704+
}),
705+
Aarch64Pe::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None)
706+
);
707+
}
708+
565709
#[test]
566710
fn cmdline_overflow() {
567711
let gm = create_guest_mem();
@@ -613,11 +757,26 @@ mod test {
613757
let mut bad_image = make_elf_bin();
614758
bad_image[0x1] = 0x33;
615759
assert_eq!(
616-
Err(Error::InvalidElfMagicNumber),
760+
Err(Error::InvalidMagicNumber),
617761
Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None)
618762
);
619763
}
620764

765+
#[cfg(feature = "aarch64_pe")]
766+
#[cfg(target_arch = "aarch64")]
767+
#[test]
768+
fn bad_magic() {
769+
let gm = create_guest_mem();
770+
let kernel_addr = GuestAddress(0x0);
771+
let mut bad_image = make_aarch64_bin();
772+
bad_image[0x38] = 0x33;
773+
774+
assert_eq!(
775+
Err(Error::InvalidMagicNumber),
776+
Aarch64Pe::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None)
777+
);
778+
}
779+
621780
#[cfg(feature = "elf")]
622781
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
623782
#[test]

0 commit comments

Comments
 (0)