diff --git a/docs/elfio_files/bad_align_writer.cpp b/docs/elfio_files/bad_align_writer.cpp index 80f965c2..0804c500 100644 --- a/docs/elfio_files/bad_align_writer.cpp +++ b/docs/elfio_files/bad_align_writer.cpp @@ -56,10 +56,10 @@ int main( void ) // Create a loadable segment segment* text_seg = writer.segments.add(); text_seg->set_type( PT_LOAD ); - text_seg->set_virtual_address( 0x400 ); - text_seg->set_physical_address( 0x400 ); + text_seg->set_virtual_address( 0x200000 ); + text_seg->set_physical_address( 0x200000 ); text_seg->set_flags( PF_X | PF_R ); - text_seg->set_align( 0x100 ); + text_seg->set_align( 0x200000 ); // Add code section into program segment text_seg->add_section_index( text_sec->get_index(), @@ -80,10 +80,10 @@ int main( void ) // Create a read/write segment segment* data_seg = writer.segments.add(); data_seg->set_type( PT_NOTE ); - data_seg->set_virtual_address( 0x8888048020 ); - data_seg->set_physical_address( 0x8888048020 ); + data_seg->set_virtual_address( 0x8888400000 ); + data_seg->set_physical_address( 0x888400000 ); data_seg->set_flags( PF_W | PF_R ); - data_seg->set_align( 13 ); + data_seg->set_align( 0x200000 ); // Add code section into program segment data_seg->add_section_index( data_sec->get_index(), @@ -103,7 +103,7 @@ int main( void ) // In this example, the code starts at the first address of the // 'text_seg' segment. Therefore, the start address is set // to be equal to the segment location - writer.set_entry( 0x400 ); + writer.set_entry( 0x200000 ); // Create ELF file writer.save( "test_bad_align.bin" ); diff --git a/docs/elfio_files/basic_elf.cpp b/docs/elfio_files/basic_elf.cpp index 35922cc3..9515a0d2 100644 --- a/docs/elfio_files/basic_elf.cpp +++ b/docs/elfio_files/basic_elf.cpp @@ -56,10 +56,10 @@ int main( void ) // Create a loadable segment segment* text_seg = writer.segments.add(); text_seg->set_type( PT_LOAD ); - text_seg->set_virtual_address( 0x400 ); - text_seg->set_physical_address( 0x0 ); + text_seg->set_virtual_address( 0x200000 ); + text_seg->set_physical_address( 0x200000 ); text_seg->set_flags( PF_X | PF_R ); - text_seg->set_align( 0x1 ); + text_seg->set_align( 0x200000 ); // Add code section into program segment text_seg->add_section_index( text_sec->get_index(), @@ -80,10 +80,10 @@ int main( void ) // Create a read/write segment segment* data_seg = writer.segments.add(); data_seg->set_type( PT_LOAD ); - data_seg->set_virtual_address( 0x0420 ); - data_seg->set_physical_address( 0x0420 ); + data_seg->set_virtual_address( 0x400000 ); + data_seg->set_physical_address( 0x400000 ); data_seg->set_flags( PF_W | PF_R ); - data_seg->set_align( 0x10 ); + data_seg->set_align( 0x200000 ); // Add code section into program segment data_seg->add_section_index( data_sec->get_index(), @@ -103,7 +103,7 @@ int main( void ) // In this example, the code starts at the first address of the // 'text_seg' segment. Therefore, the start address is set // to be equal to the segment location - writer.set_entry( 0x400 ); + writer.set_entry( 0x200000 ); // Create ELF file writer.save( "test_elf.bin" ); diff --git a/src/loader/elf/mod.rs b/src/loader/elf/mod.rs index ddf8c6fa..42fb07a0 100644 --- a/src/loader/elf/mod.rs +++ b/src/loader/elf/mod.rs @@ -135,6 +135,9 @@ impl fmt::Display for PvhBootCapability { /// Raw ELF (a.k.a. vmlinux) kernel image support. pub struct Elf; +// x86-64 kernel must be aligned to 2MB. +const KERNEL_SEGMENT_ALIGN: u64 = 0x200000; // 2 MiB + impl Elf { /// Verifies that magic numbers are present in the Elf header. fn validate_header(ehdr: &elf::Elf64_Ehdr) -> std::result::Result<(), Error> { @@ -224,12 +227,18 @@ impl KernelLoader for Elf { let mut loader_result = KernelLoaderResult { kernel_load: match kernel_offset { - Some(k_offset) => GuestAddress( - k_offset + Some(k_offset) => { + let load_addr = k_offset .raw_value() .checked_add(ehdr.e_entry) - .ok_or(Error::Overflow)?, - ), + .ok_or(Error::Overflow)?; + // Ensure that kernel_offset is aligned to 2M, otherwise, the kernel may hang during + // booting. Refer to the check in __startup_64() in the Linux kernel source code. + if k_offset.raw_value() & (KERNEL_SEGMENT_ALIGN - 1) != 0 { + return Err(Error::Align.into()); + } + GuestAddress(load_addr) + }, None => GuestAddress(ehdr.e_entry), }, ..Default::default() @@ -271,6 +280,12 @@ impl KernelLoader for Elf { .seek(SeekFrom::Start(phdr.p_offset)) .map_err(|_| Error::SeekKernelStart)?; + // Verify alignment of the LOAD segment. + // See commit c55b8550fa57ba4f5e507be406ff9fc2845713e8 in Linux kernel tree. + if phdr.p_align & (KERNEL_SEGMENT_ALIGN - 1) != 0 { + return Err(Error::Align.into()); + } + // if the vmm does not specify where the kernel should be loaded, just // load it to the physical address p_paddr for each segment. let mem_offset = match kernel_offset { @@ -479,10 +494,10 @@ mod tests { Some(highmem_start_address), ) .unwrap(); - assert_eq!(loader_result.kernel_load.raw_value(), 0x200400); + assert_eq!(loader_result.kernel_load.raw_value(), 0x400000); loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap(); - assert_eq!(loader_result.kernel_load.raw_value(), 0x200400); + assert_eq!(loader_result.kernel_load.raw_value(), 0x400000); loader_result = Elf::load( &gm, @@ -491,7 +506,7 @@ mod tests { Some(highmem_start_address), ) .unwrap(); - assert_eq!(loader_result.kernel_load.raw_value(), 0x400); + assert_eq!(loader_result.kernel_load.raw_value(), 0x200000); highmem_start_address = GuestAddress(0xa00000); assert_eq!( @@ -632,4 +647,20 @@ mod tests { .err() ); } + + #[test] + fn test_unaligned_loadaddr() { + let gm = create_guest_mem(); + let image = make_elf_bin(); + assert_eq!( + Some(KernelLoaderError::Elf(Error::Align)), + Elf::load( + &gm, + Some(GuestAddress(0x1000)), + &mut Cursor::new(&image), + None + ) + .err() + ); + } } diff --git a/src/loader/elf/test_bad_align.bin b/src/loader/elf/test_bad_align.bin index 2fbcc682..1dae1a43 100644 Binary files a/src/loader/elf/test_bad_align.bin and b/src/loader/elf/test_bad_align.bin differ diff --git a/src/loader/elf/test_elf.bin b/src/loader/elf/test_elf.bin index 08fe05c5..7b06074c 100644 Binary files a/src/loader/elf/test_elf.bin and b/src/loader/elf/test_elf.bin differ