1+ // Copyright © 2020, Oracle and/or its affiliates.
2+ //
13// Copyright (c) 2019 Intel Corporation. All rights reserved.
24// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
35//
@@ -36,6 +38,11 @@ use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize};
3638#[ allow( missing_docs) ]
3739#[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: all) ) ]
3840pub mod bootparam;
41+
42+ #[ allow( missing_docs) ]
43+ #[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: all) ) ]
44+ pub mod start_info;
45+
3946#[ allow( dead_code) ]
4047#[ allow( non_camel_case_types) ]
4148#[ allow( non_snake_case) ]
@@ -93,6 +100,10 @@ pub enum Error {
93100 SeekBzImageHeader ,
94101 /// Unable to seek to bzImage compressed kernel.
95102 SeekBzImageCompressedKernel ,
103+ /// Unable to seek to note header.
104+ SeekNoteHeader ,
105+ /// Unable to read note header.
106+ ReadNoteHeader ,
96107}
97108
98109/// A specialized `Result` type for the kernel loader.
@@ -125,6 +136,8 @@ impl error::Error for Error {
125136 Error :: SeekBzImageEnd => "Unable to seek bzImage end" ,
126137 Error :: SeekBzImageHeader => "Unable to seek bzImage header" ,
127138 Error :: SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel" ,
139+ Error :: SeekNoteHeader => "Unable to seek to note header" ,
140+ Error :: ReadNoteHeader => "Unable to read note header" ,
128141 }
129142 }
130143}
@@ -150,6 +163,10 @@ pub struct KernelLoaderResult {
150163 /// This field is only for bzImage following https://www.kernel.org/doc/Documentation/x86/boot.txt
151164 /// VMM should make use of it to fill zero page for bzImage direct boot.
152165 pub setup_header : Option < bootparam:: setup_header > ,
166+ /// This field optionally holds the address of a PVH entry point, indicating that
167+ /// the kernel supports the PVH boot protocol as described in:
168+ /// https://xenbits.xen.org/docs/unstable/misc/pvh.html
169+ pub pvh_entry_addr : Option < GuestAddress > ,
153170}
154171
155172/// A kernel image loading support must implement the KernelLoader trait.
@@ -247,6 +264,10 @@ impl KernelLoader for Elf {
247264 // Read in each section pointed to by the program headers.
248265 for phdr in & phdrs {
249266 if phdr. p_type != elf:: PT_LOAD || phdr. p_filesz == 0 {
267+ if phdr. p_type == elf:: PT_NOTE {
268+ // This segment describes a Note, check if PVH entry point is encoded.
269+ loader_result. pvh_entry_addr = parse_elf_note ( phdr, kernel_image) ?;
270+ }
250271 continue ;
251272 }
252273
@@ -280,6 +301,71 @@ impl KernelLoader for Elf {
280301 }
281302}
282303
304+ fn parse_elf_note < F > ( phdr : & elf:: Elf64_Phdr , kernel_image : & mut F ) -> Result < Option < GuestAddress > >
305+ where
306+ F : Read + Seek ,
307+ {
308+ let n_align = phdr. p_align ;
309+
310+ // Seek to the beginning of the note segment
311+ kernel_image
312+ . seek ( SeekFrom :: Start ( phdr. p_offset ) )
313+ . map_err ( |_| Error :: SeekNoteHeader ) ?;
314+
315+ // Now that the segment has been found, we must locate an ELF note with the
316+ // correct type that encodes the PVH entry point if there is one.
317+ let mut nhdr: elf:: Elf64_Nhdr = Default :: default ( ) ;
318+ let mut read_size: usize = 0 ;
319+
320+ while read_size < phdr. p_filesz as usize {
321+ unsafe {
322+ // read_struct is safe when reading a POD struct.
323+ // It can be used and dropped without issue.
324+ struct_util:: read_struct ( kernel_image, & mut nhdr) . map_err ( |_| Error :: ReadNoteHeader ) ?;
325+ }
326+ // If the note header found is not the desired one, keep reading until
327+ // the end of the segment
328+ if nhdr. n_type == elf:: XEN_ELFNOTE_PHYS32_ENTRY {
329+ break ;
330+ }
331+ // Skip the note header plus the size of its fields (with alignment)
332+ read_size += mem:: size_of :: < elf:: Elf64_Nhdr > ( )
333+ + align_up ( nhdr. n_namesz as u64 , n_align)
334+ + align_up ( nhdr. n_descsz as u64 , n_align) ;
335+
336+ kernel_image
337+ . seek ( SeekFrom :: Start ( phdr. p_offset + read_size as u64 ) )
338+ . map_err ( |_| Error :: SeekNoteHeader ) ?;
339+ }
340+
341+ if read_size >= phdr. p_filesz as usize {
342+ return Ok ( None ) ; // PVH ELF note not found, nothing else to do.
343+ }
344+ // Otherwise the correct note type was found.
345+ // The note header struct has already been read, so we can seek from the
346+ // current position and just skip the name field contents.
347+ kernel_image
348+ . seek ( SeekFrom :: Current (
349+ align_up ( nhdr. n_namesz as u64 , n_align) as i64
350+ ) )
351+ . map_err ( |_| Error :: SeekNoteHeader ) ?;
352+
353+ // We support 64bit kernels only and the PVH entry point is a 32bit address
354+ // encoded in a 64 bit field, so we'll grab all 8 bytes.
355+ let mut pvh_addr_bytes = [ 0 ; 8usize ] ;
356+
357+ if nhdr. n_descsz as usize != pvh_addr_bytes. len ( ) {
358+ return Err ( Error :: ReadNoteHeader ) ;
359+ }
360+
361+ kernel_image
362+ . read_exact ( & mut pvh_addr_bytes)
363+ . map_err ( |_| Error :: ReadNoteHeader )
364+ . ok ( ) ;
365+
366+ Ok ( Some ( GuestAddress ( <u64 >:: from_le_bytes ( pvh_addr_bytes) ) ) )
367+ }
368+
283369#[ cfg( feature = "bzimage" ) ]
284370#[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
285371/// Big zImage (bzImage) kernel image support.
@@ -409,6 +495,20 @@ pub fn load_cmdline<M: GuestMemory>(
409495 Ok ( ( ) )
410496}
411497
498+ /// Align address upwards. Taken from x86_64 crate.
499+ ///
500+ /// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be
501+ /// a power of 2.
502+ fn align_up ( addr : u64 , align : u64 ) -> usize {
503+ assert ! ( align. is_power_of_two( ) , "`align` must be a power of two" ) ;
504+ let align_mask = align - 1 ;
505+ if addr & align_mask == 0 {
506+ addr as usize // already aligned
507+ } else {
508+ ( ( addr | align_mask) + 1 ) as usize
509+ }
510+ }
511+
412512#[ cfg( test) ]
413513mod test {
414514 use super :: * ;
0 commit comments