Skip to content

Commit e8b3ead

Browse files
RuoqingHerbradford
authored andcommitted
loader: Introduce riscv64 architecture support
RISC-V uses the same image format ARM64 did, reuse the PE image loader and add support for loading a `riscv64` PE image. Signed-off-by: Ruoqing He <heruoqing@iscas.ac.cn>
1 parent 5b17a7f commit e8b3ead

File tree

4 files changed

+98
-26
lines changed

4 files changed

+98
-26
lines changed

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
//!
2323
//! - `x86_64`
2424
//! - `ARM64`
25+
//! - `RISC-V64`
2526
//!
2627
//! # Example - load an ELF kernel and configure boot params with the PVH protocol
2728
//!
@@ -91,7 +92,7 @@
9192
//! PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_mem).unwrap();
9293
//! }
9394
//!
94-
//! # #[cfg(target_arch = "aarch64")]
95+
//! # #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
9596
//! # fn main() {}
9697
//! ```
9798
//!

src/loader/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ pub use crate::loader_gen::bootparam;
3131

3232
pub use crate::cmdline::Cmdline;
3333

34-
#[cfg(all(target_arch = "aarch64", feature = "pe"))]
34+
#[cfg(all(any(target_arch = "aarch64", target_arch = "riscv64"), feature = "pe"))]
3535
pub mod pe;
36-
#[cfg(all(target_arch = "aarch64", feature = "pe"))]
36+
#[cfg(all(any(target_arch = "aarch64", target_arch = "riscv64"), feature = "pe"))]
3737
pub use pe::*;
3838

3939
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "elf"))]
@@ -58,7 +58,7 @@ pub enum Error {
5858
Elf(elf::Error),
5959

6060
/// Failed to load PE image.
61-
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
61+
#[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
6262
Pe(pe::Error),
6363

6464
/// Invalid command line.
@@ -86,7 +86,7 @@ impl fmt::Display for Error {
8686
Error::Bzimage(ref e) => write!(f, "failed to load bzImage kernel image: {e}"),
8787
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
8888
Error::Elf(ref e) => write!(f, "failed to load ELF kernel image: {e}"),
89-
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
89+
#[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
9090
Error::Pe(ref e) => write!(f, "failed to load PE kernel image: {e}"),
9191

9292
Error::InvalidCommandLine => write!(f, "invalid command line provided"),
@@ -105,7 +105,7 @@ impl std::error::Error for Error {
105105
Error::Bzimage(ref e) => Some(e),
106106
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
107107
Error::Elf(ref e) => Some(e),
108-
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
108+
#[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
109109
Error::Pe(ref e) => Some(e),
110110

111111
Error::InvalidCommandLine => None,
@@ -131,7 +131,7 @@ impl From<bzimage::Error> for Error {
131131
}
132132
}
133133

134-
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
134+
#[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
135135
impl From<pe::Error> for Error {
136136
fn from(err: pe::Error) -> Self {
137137
Error::Pe(err)

src/loader/pe/mod.rs

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Copyright 2024 © Institute of Software, CAS. All rights reserved.
12
// Copyright © 2020, Oracle and/or its affiliates.
23
// Copyright (c) 2019 Intel Corporation. All rights reserved.
34
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
@@ -17,13 +18,9 @@ use vm_memory::{Address, ByteValued, GuestAddress, GuestMemory, GuestUsize, Read
1718

1819
use crate::loader::{Error as KernelLoaderError, KernelLoader, KernelLoaderResult, Result};
1920

20-
/// ARM64 Image (PE) format support
21+
/// ARM64 and RISC-V64 Image (PE) format support
2122
pub struct PE;
2223

23-
// SAFETY: The layout of the structure is fixed and can be initialized by
24-
// reading its content from byte array.
25-
unsafe impl ByteValued for arm64_image_header {}
26-
2724
#[derive(Debug, PartialEq, Eq)]
2825
/// PE kernel loader errors.
2926
pub enum Error {
@@ -73,6 +70,7 @@ impl fmt::Display for Error {
7370

7471
impl std::error::Error for Error {}
7572

73+
#[cfg(target_arch = "aarch64")]
7674
#[repr(C)]
7775
#[derive(Debug, Copy, Clone, Default)]
7876
// See kernel doc Documentation/arm64/booting.txt for more information.
@@ -90,6 +88,35 @@ struct arm64_image_header {
9088
res5: u32,
9189
}
9290

91+
#[cfg(target_arch = "aarch64")]
92+
// SAFETY: The layout of the structure is fixed and can be initialized by
93+
// reading its content from byte array.
94+
unsafe impl ByteValued for arm64_image_header {}
95+
96+
#[cfg(target_arch = "riscv64")]
97+
#[repr(C)]
98+
#[derive(Debug, Copy, Clone, Default)]
99+
// See kernel doc Documentation/arch/riscv/boot-image-header.rst
100+
// All these fields should be little endian.
101+
struct riscv64_image_header {
102+
code0: u32,
103+
code1: u32,
104+
text_offset: u64,
105+
image_size: u64,
106+
flags: u64,
107+
version: u32,
108+
res1: u32,
109+
res2: u64,
110+
magic: u64,
111+
magic2: u32,
112+
res3: u32,
113+
}
114+
115+
#[cfg(target_arch = "riscv64")]
116+
// SAFETY: The layout of the structure is fixed and can be initialized by
117+
// reading its content from byte array.
118+
unsafe impl ByteValued for riscv64_image_header {}
119+
93120
impl KernelLoader for PE {
94121
/// Loads a PE Image into guest memory.
95122
///
@@ -98,7 +125,7 @@ impl KernelLoader for PE {
98125
/// * `guest_mem` - The guest memory where the kernel image is loaded.
99126
/// * `kernel_offset` - 2MB-aligned base addres in guest memory at which to load the kernel.
100127
/// * `kernel_image` - Input Image format kernel image.
101-
/// * `highmem_start_address` - ignored on ARM64.
128+
/// * `highmem_start_address` - ignored on ARM64 and RISC-V64.
102129
///
103130
/// # Returns
104131
/// * KernelLoaderResult
@@ -114,26 +141,38 @@ impl KernelLoader for PE {
114141
let kernel_size = kernel_image
115142
.seek(SeekFrom::End(0))
116143
.map_err(|_| Error::SeekImageEnd)? as usize;
117-
let mut arm64_header: arm64_image_header = Default::default();
144+
145+
#[cfg(target_arch = "aarch64")]
146+
let mut image_header: arm64_image_header = Default::default();
147+
#[cfg(target_arch = "riscv64")]
148+
let mut image_header: riscv64_image_header = Default::default();
149+
118150
kernel_image.rewind().map_err(|_| Error::SeekImageHeader)?;
119151

120152
kernel_image
121-
.read_exact(arm64_header.as_mut_slice())
153+
.read_exact(image_header.as_mut_slice())
122154
.map_err(|_| Error::ReadImageHeader)?;
123155

124-
if u32::from_le(arm64_header.magic) != 0x644d_5241 {
156+
#[cfg(target_arch = "aarch64")]
157+
if u32::from_le(image_header.magic) != 0x644d_5241 {
125158
return Err(Error::InvalidImageMagicNumber.into());
126159
}
127-
128-
let image_size = u64::from_le(arm64_header.image_size);
129-
let mut text_offset = u64::from_le(arm64_header.text_offset);
130-
131-
if image_size == 0 {
132-
text_offset = 0x80000;
160+
#[cfg(target_arch = "riscv64")]
161+
if u32::from_le(image_header.magic2) != 0x0543_5352 {
162+
return Err(Error::InvalidImageMagicNumber.into());
133163
}
134164

165+
#[cfg(target_arch = "aarch64")]
166+
let text_offset = if u64::from_le(image_header.image_size) == 0 {
167+
0x80000
168+
} else {
169+
u64::from_le(image_header.text_offset)
170+
};
171+
#[cfg(target_arch = "riscv64")]
172+
let text_offset = u64::from_le(image_header.text_offset);
173+
135174
// Validate that kernel_offset is 2 MB aligned, as required by the
136-
// arm64 boot protocol
175+
// arm64 and riscv64 boot protocol
137176
if let Some(kernel_offset) = kernel_offset {
138177
if kernel_offset.raw_value() % 0x0020_0000 != 0 {
139178
return Err(Error::InvalidBaseAddrAlignment.into());
@@ -171,7 +210,7 @@ impl KernelLoader for PE {
171210
/// * `guest_mem` - A u8 slice that will be partially overwritten by the device tree blob.
172211
/// * `guest_addr` - The address in `guest_mem` at which to load the device tree blob.
173212
/// * `dtb_image` - The device tree blob.
174-
#[cfg(target_arch = "aarch64")]
213+
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
175214
pub fn load_dtb<F, M: GuestMemory>(
176215
guest_mem: &M,
177216
guest_addr: GuestAddress,
@@ -207,12 +246,16 @@ mod tests {
207246

208247
fn make_image_bin() -> Vec<u8> {
209248
let mut v = Vec::new();
210-
v.extend_from_slice(include_bytes!("test_image.bin"));
249+
#[cfg(target_arch = "aarch64")]
250+
v.extend_from_slice(include_bytes!("test_arm64_image.bin"));
251+
#[cfg(target_arch = "riscv64")]
252+
v.extend_from_slice(include_bytes!("test_riscv64_image.bin"));
211253
v
212254
}
213255

256+
#[cfg(target_arch = "aarch64")]
214257
#[test]
215-
fn load_image() {
258+
fn load_arm64_image() {
216259
let gm = create_guest_mem();
217260
let mut image = make_image_bin();
218261
let kernel_addr = GuestAddress(0x200000);
@@ -237,4 +280,32 @@ mod tests {
237280
Err(KernelLoaderError::Pe(Error::InvalidImageMagicNumber))
238281
);
239282
}
283+
284+
#[cfg(target_arch = "riscv64")]
285+
#[test]
286+
fn load_riscv64_image() {
287+
let gm = create_guest_mem();
288+
let mut image = make_image_bin();
289+
let kernel_addr = GuestAddress(0x400000);
290+
291+
let loader_result =
292+
PE::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap();
293+
assert_eq!(loader_result.kernel_load.raw_value(), 0x600000);
294+
assert_eq!(loader_result.kernel_end, 0x601000);
295+
296+
// Attempt to load the kernel at an address that is not aligned to 2MB boundary
297+
let kernel_offset = GuestAddress(0x0030_0000);
298+
let loader_result = PE::load(&gm, Some(kernel_offset), &mut Cursor::new(&image), None);
299+
assert_eq!(
300+
loader_result,
301+
Err(KernelLoaderError::Pe(Error::InvalidBaseAddrAlignment))
302+
);
303+
304+
image[0x38] = 0x0;
305+
let loader_result = PE::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None);
306+
assert_eq!(
307+
loader_result,
308+
Err(KernelLoaderError::Pe(Error::InvalidImageMagicNumber))
309+
);
310+
}
240311
}
File renamed without changes.

0 commit comments

Comments
 (0)