1
+ // Copyright © 2020, Oracle and/or its affiliates.
2
+ //
1
3
// Copyright (c) 2019 Intel Corporation. All rights reserved.
2
4
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
5
//
@@ -36,6 +38,13 @@ use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize};
36
38
#[ allow( missing_docs) ]
37
39
#[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: all) ) ]
38
40
pub mod bootparam;
41
+
42
+ #[ cfg( feature = "elf" ) ]
43
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
44
+ #[ allow( missing_docs) ]
45
+ #[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: all) ) ]
46
+ pub mod start_info;
47
+
39
48
#[ allow( dead_code) ]
40
49
#[ allow( non_camel_case_types) ]
41
50
#[ allow( non_snake_case) ]
@@ -93,6 +102,10 @@ pub enum Error {
93
102
SeekBzImageHeader ,
94
103
/// Unable to seek to bzImage compressed kernel.
95
104
SeekBzImageCompressedKernel ,
105
+ /// Unable to seek to note header.
106
+ SeekNoteHeader ,
107
+ /// Unable to read note header.
108
+ ReadNoteHeader ,
96
109
}
97
110
98
111
/// A specialized `Result` type for the kernel loader.
@@ -125,6 +138,8 @@ impl error::Error for Error {
125
138
Error :: SeekBzImageEnd => "Unable to seek bzImage end" ,
126
139
Error :: SeekBzImageHeader => "Unable to seek bzImage header" ,
127
140
Error :: SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel" ,
141
+ Error :: SeekNoteHeader => "Unable to seek to note header" ,
142
+ Error :: ReadNoteHeader => "Unable to read note header" ,
128
143
}
129
144
}
130
145
}
@@ -150,6 +165,10 @@ pub struct KernelLoaderResult {
150
165
/// This field is only for bzImage following https://www.kernel.org/doc/Documentation/x86/boot.txt
151
166
/// VMM should make use of it to fill zero page for bzImage direct boot.
152
167
pub setup_header : Option < bootparam:: setup_header > ,
168
+ /// This field optionally holds the address of a PVH entry point, indicating that
169
+ /// the kernel supports the PVH boot protocol as described in:
170
+ /// https://xenbits.xen.org/docs/unstable/misc/pvh.html
171
+ pub pvh_entry_addr : Option < GuestAddress > ,
153
172
}
154
173
155
174
/// A kernel image loading support must implement the KernelLoader trait.
@@ -247,6 +266,10 @@ impl KernelLoader for Elf {
247
266
// Read in each section pointed to by the program headers.
248
267
for phdr in & phdrs {
249
268
if phdr. p_type != elf:: PT_LOAD || phdr. p_filesz == 0 {
269
+ if phdr. p_type == elf:: PT_NOTE {
270
+ // This segment describes a Note, check if PVH entry point is encoded.
271
+ loader_result. pvh_entry_addr = parse_elf_note ( phdr, kernel_image) ?;
272
+ }
250
273
continue ;
251
274
}
252
275
@@ -280,6 +303,72 @@ impl KernelLoader for Elf {
280
303
}
281
304
}
282
305
306
+ #[ cfg( feature = "elf" ) ]
307
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
308
+ fn parse_elf_note < F > ( phdr : & elf:: Elf64_Phdr , kernel_image : & mut F ) -> Result < Option < GuestAddress > >
309
+ where
310
+ F : Read + Seek ,
311
+ {
312
+ let n_align = phdr. p_align ;
313
+
314
+ // Seek to the beginning of the note segment
315
+ kernel_image
316
+ . seek ( SeekFrom :: Start ( phdr. p_offset ) )
317
+ . map_err ( |_| Error :: SeekNoteHeader ) ?;
318
+
319
+ // Now that the segment has been found, we must locate an ELF note with the
320
+ // correct type that encodes the PVH entry point if there is one.
321
+ let mut nhdr: elf:: Elf64_Nhdr = Default :: default ( ) ;
322
+ let mut read_size: usize = 0 ;
323
+
324
+ while read_size < phdr. p_filesz as usize {
325
+ unsafe {
326
+ // read_struct is safe when reading a POD struct.
327
+ // It can be used and dropped without issue.
328
+ struct_util:: read_struct ( kernel_image, & mut nhdr) . map_err ( |_| Error :: ReadNoteHeader ) ?;
329
+ }
330
+ // If the note header found is not the desired one, keep reading until
331
+ // the end of the segment
332
+ if nhdr. n_type == elf:: XEN_ELFNOTE_PHYS32_ENTRY {
333
+ break ;
334
+ }
335
+ // Skip the note header plus the size of its fields (with alignment)
336
+ read_size += mem:: size_of :: < elf:: Elf64_Nhdr > ( )
337
+ + align_up ( u64:: from ( nhdr. n_namesz ) , n_align)
338
+ + align_up ( u64:: from ( nhdr. n_descsz ) , n_align) ;
339
+
340
+ kernel_image
341
+ . seek ( SeekFrom :: Start ( phdr. p_offset + read_size as u64 ) )
342
+ . map_err ( |_| Error :: SeekNoteHeader ) ?;
343
+ }
344
+
345
+ if read_size >= phdr. p_filesz as usize {
346
+ return Ok ( None ) ; // PVH ELF note not found, nothing else to do.
347
+ }
348
+ // Otherwise the correct note type was found.
349
+ // The note header struct has already been read, so we can seek from the
350
+ // current position and just skip the name field contents.
351
+ kernel_image
352
+ . seek ( SeekFrom :: Current (
353
+ align_up ( u64:: from ( nhdr. n_namesz ) , n_align) as i64 ,
354
+ ) )
355
+ . map_err ( |_| Error :: SeekNoteHeader ) ?;
356
+
357
+ // We support 64bit kernels only and the PVH entry point is a 32bit address
358
+ // encoded in a 64 bit field, so we'll grab all 8 bytes.
359
+ let mut pvh_addr_bytes = [ 0 ; 8usize ] ;
360
+
361
+ if nhdr. n_descsz as usize != pvh_addr_bytes. len ( ) {
362
+ return Err ( Error :: ReadNoteHeader ) ;
363
+ }
364
+
365
+ kernel_image
366
+ . read_exact ( & mut pvh_addr_bytes)
367
+ . map_err ( |_| Error :: ReadNoteHeader ) ?;
368
+
369
+ Ok ( Some ( GuestAddress ( <u64 >:: from_le_bytes ( pvh_addr_bytes) ) ) )
370
+ }
371
+
283
372
#[ cfg( feature = "bzimage" ) ]
284
373
#[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
285
374
/// Big zImage (bzImage) kernel image support.
@@ -409,6 +498,22 @@ pub fn load_cmdline<M: GuestMemory>(
409
498
Ok ( ( ) )
410
499
}
411
500
501
+ /// Align address upwards. Taken from x86_64 crate.
502
+ ///
503
+ /// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be
504
+ /// a power of 2.
505
+ #[ cfg( feature = "elf" ) ]
506
+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
507
+ fn align_up ( addr : u64 , align : u64 ) -> usize {
508
+ assert ! ( align. is_power_of_two( ) , "`align` must be a power of two" ) ;
509
+ let align_mask = align - 1 ;
510
+ if addr & align_mask == 0 {
511
+ addr as usize // already aligned
512
+ } else {
513
+ ( ( addr | align_mask) + 1 ) as usize
514
+ }
515
+ }
516
+
412
517
#[ cfg( test) ]
413
518
mod test {
414
519
use super :: * ;
0 commit comments