Skip to content

Commit 093dec3

Browse files
committed
Support raw image (vmlinux) loading for Aarch64.
Signed-off-by: Michael Zhao <michael.zhao@arm.com>
1 parent 0c754f3 commit 093dec3

File tree

3 files changed

+185
-24
lines changed

3 files changed

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

@@ -440,6 +551,15 @@ mod test {
440551
v
441552
}
442553

554+
// Aarch64 image.
555+
#[cfg(feature = "aarch64_pe")]
556+
#[cfg(target_arch = "aarch64")]
557+
fn make_aarch64_bin() -> Vec<u8> {
558+
let mut v = Vec::new();
559+
v.extend_from_slice(include_bytes!("test_pe.bin"));
560+
v
561+
}
562+
443563
#[allow(safe_packed_borrows)]
444564
#[allow(non_snake_case)]
445565
#[test]
@@ -562,6 +682,29 @@ mod test {
562682
);
563683
}
564684

685+
// Aarch64 image.
686+
#[cfg(feature = "aarch64_pe")]
687+
#[cfg(target_arch = "aarch64")]
688+
#[test]
689+
fn load_aarch64() {
690+
const TEST_IMAGE_KERNEL_OFFSET: u64 = 0x8_0000; // test binary specific
691+
const TEST_IMAGE_KERNEL_SIZE: u64 = 0x50; // test binary specific
692+
let gm = create_guest_mem();
693+
let image = make_aarch64_bin();
694+
let kernel_addr = GuestAddress(0x80000);
695+
696+
assert_eq!(
697+
Ok(KernelLoaderResult {
698+
kernel_load: GuestAddress(kernel_addr.raw_value() + TEST_IMAGE_KERNEL_OFFSET),
699+
kernel_end: (kernel_addr.raw_value()
700+
+ TEST_IMAGE_KERNEL_OFFSET
701+
+ TEST_IMAGE_KERNEL_SIZE) as u64,
702+
setup_header: None
703+
}),
704+
Aarch64Pe::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None)
705+
);
706+
}
707+
565708
#[test]
566709
fn cmdline_overflow() {
567710
let gm = create_guest_mem();
@@ -613,11 +756,26 @@ mod test {
613756
let mut bad_image = make_elf_bin();
614757
bad_image[0x1] = 0x33;
615758
assert_eq!(
616-
Err(Error::InvalidElfMagicNumber),
759+
Err(Error::InvalidMagicNumber),
617760
Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None)
618761
);
619762
}
620763

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

0 commit comments

Comments
 (0)