1extern crate vm_memory;
21
22use std::fmt;
23use std::io::{Read, Seek};
24
25#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
26use vm_memory::ByteValued;
27use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize, ReadVolatile};
28
29#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
30pub use crate::loader_gen::bootparam;
31
32pub use crate::cmdline::Cmdline;
33
34#[cfg(all(any(target_arch = "aarch64", target_arch = "riscv64"), feature = "pe"))]
35pub mod pe;
36#[cfg(all(any(target_arch = "aarch64", target_arch = "riscv64"), feature = "pe"))]
37pub use pe::*;
38
39#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "elf"))]
40pub mod elf;
41#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "elf"))]
42pub use elf::*;
43
44#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "bzimage"))]
45pub mod bzimage;
46#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "bzimage"))]
47pub use bzimage::*;
48
49#[derive(Debug, PartialEq, Eq)]
50pub enum Error {
52 #[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
54 Bzimage(bzimage::Error),
55
56 #[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
58 Elf(elf::Error),
59
60 #[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
62 Pe(pe::Error),
63
64 InvalidCommandLine,
66 CommandLineCopy,
68 CommandLineOverflow,
70 InvalidKernelStartAddress,
72 MemoryOverflow,
74}
75
76pub type Result<T> = std::result::Result<T, Error>;
80
81impl fmt::Display for Error {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "Kernel Loader: ")?;
84 match self {
85 #[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
86 Error::Bzimage(ref e) => write!(f, "failed to load bzImage kernel image: {e}"),
87 #[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
88 Error::Elf(ref e) => write!(f, "failed to load ELF kernel image: {e}"),
89 #[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
90 Error::Pe(ref e) => write!(f, "failed to load PE kernel image: {e}"),
91
92 Error::InvalidCommandLine => write!(f, "invalid command line provided"),
93 Error::CommandLineCopy => write!(f, "failed writing command line to guest memory"),
94 Error::CommandLineOverflow => write!(f, "command line overflowed guest memory"),
95 Error::InvalidKernelStartAddress => write!(f, "invalid kernel start address"),
96 Error::MemoryOverflow => write!(f, "memory to load kernel image is not enough"),
97 }
98 }
99}
100
101impl std::error::Error for Error {
102 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
103 match self {
104 #[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
105 Error::Bzimage(ref e) => Some(e),
106 #[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
107 Error::Elf(ref e) => Some(e),
108 #[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
109 Error::Pe(ref e) => Some(e),
110
111 Error::InvalidCommandLine => None,
112 Error::CommandLineCopy => None,
113 Error::CommandLineOverflow => None,
114 Error::InvalidKernelStartAddress => None,
115 Error::MemoryOverflow => None,
116 }
117 }
118}
119
120#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
121impl From<elf::Error> for Error {
122 fn from(err: elf::Error) -> Self {
123 Error::Elf(err)
124 }
125}
126
127#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
128impl From<bzimage::Error> for Error {
129 fn from(err: bzimage::Error) -> Self {
130 Error::Bzimage(err)
131 }
132}
133
134#[cfg(all(feature = "pe", any(target_arch = "aarch64", target_arch = "riscv64")))]
135impl From<pe::Error> for Error {
136 fn from(err: pe::Error) -> Self {
137 Error::Pe(err)
138 }
139}
140
141#[derive(Clone, Copy, Debug, Default, PartialEq)]
147pub struct KernelLoaderResult {
148 pub kernel_load: GuestAddress,
150 pub kernel_end: GuestUsize,
153 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
156 pub setup_header: Option<bootparam::setup_header>,
157 #[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
161 pub pvh_boot_cap: elf::PvhBootCapability,
162}
163
164pub trait KernelLoader {
166 fn load<F, M: GuestMemory>(
177 guest_mem: &M,
178 kernel_offset: Option<GuestAddress>,
179 kernel_image: &mut F,
180 highmem_start_address: Option<GuestAddress>,
181 ) -> Result<KernelLoaderResult>
182 where
183 F: Read + ReadVolatile + Seek;
184}
185
186#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
187unsafe impl ByteValued for bootparam::setup_header {}
190
191#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
192unsafe impl ByteValued for bootparam::boot_params {}
195
196pub fn load_cmdline<M: GuestMemory>(
223 guest_mem: &M,
224 guest_addr: GuestAddress,
225 cmdline: &Cmdline,
226) -> Result<()> {
227 let cmdline_string = cmdline
231 .as_cstring()
232 .map_err(|_| Error::InvalidCommandLine)?;
233
234 let cmdline_bytes = cmdline_string.as_bytes_with_nul();
235
236 let end = guest_addr
237 .checked_add((cmdline_bytes.len() - 1) as u64)
240 .ok_or(Error::CommandLineOverflow)?;
241 if end > guest_mem.last_addr() {
242 return Err(Error::CommandLineOverflow);
243 }
244
245 guest_mem
246 .write_slice(cmdline_bytes, guest_addr)
247 .map_err(|_| Error::CommandLineCopy)?;
248
249 Ok(())
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use vm_memory::{Address, GuestAddress};
256 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
257
258 const MEM_SIZE: u64 = 0x100_0000;
259
260 fn create_guest_mem() -> GuestMemoryMmap {
261 GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
262 }
263
264 #[test]
265 fn test_cmdline_overflow() {
266 let gm = create_guest_mem();
267 let mut cl = Cmdline::new(10).unwrap();
268 cl.insert_str("12345").unwrap();
269
270 let cmdline_address = GuestAddress(u64::MAX - 5);
271 assert_eq!(
272 Err(Error::CommandLineOverflow),
273 load_cmdline(&gm, cmdline_address, &cl)
274 );
275
276 let cmdline_address = GuestAddress(MEM_SIZE - 5);
277 assert_eq!(
278 Err(Error::CommandLineOverflow),
279 load_cmdline(&gm, cmdline_address, &cl)
280 );
281 let cmdline_address = GuestAddress(MEM_SIZE - 6);
282 assert!(load_cmdline(&gm, cmdline_address, &cl).is_ok());
283 }
284
285 #[test]
286 fn test_cmdline_write_end_regresion() {
287 let gm = create_guest_mem();
288 let mut cmdline_address = GuestAddress(45);
289 let sample_buf = &[1; 100];
290
291 gm.write(sample_buf, cmdline_address).unwrap();
293
294 let mut cl = Cmdline::new(10).unwrap();
295
296 load_cmdline(&gm, cmdline_address, &cl).unwrap();
298 let val: u8 = gm.read_obj(cmdline_address).unwrap();
299 assert_eq!(val, b'\0');
300
301 cl.insert_str("123").unwrap();
303 load_cmdline(&gm, cmdline_address, &cl).unwrap();
304
305 let val: u8 = gm.read_obj(cmdline_address).unwrap();
306 assert_eq!(val, b'1');
307 cmdline_address = cmdline_address.unchecked_add(1);
308 let val: u8 = gm.read_obj(cmdline_address).unwrap();
309 assert_eq!(val, b'2');
310 cmdline_address = cmdline_address.unchecked_add(1);
311 let val: u8 = gm.read_obj(cmdline_address).unwrap();
312 assert_eq!(val, b'3');
313 cmdline_address = cmdline_address.unchecked_add(1);
314 let val: u8 = gm.read_obj(cmdline_address).unwrap();
315 assert_eq!(val, b'\0');
316 }
317}