Skip to contentSkip to conversation

efi/arm64: execute the kernel in place if possible

PATCH In review Ard Biesheuvel 3 participants 7 messages Mar 26, 2020 – Mar 28, 2020

Subsystems (2)
lore
Share & tools
lore

Ard Biesheuvel

author maintainer
#0 lore raw

cover efi/arm64: execute the kernel in place if possible

Update the relocation logic in the EFI stub for arm64 so it runs the kernel in place if it was loaded by firmware at an address which allows doing so. Then, update the PE/COFF header metadata and increase the section alignment to force the UEFI image loader to honour the minimal alignment requirement imposed by the kernel proper.

Ard Biesheuvel (2):
efi/libstub/arm64: avoid copying the kernel unnecessarily efi/arm64: increase the PE/COFF alignment so the kernel can run in
place

 arch/arm64/kernel/efi-header.S            | 2 +-
 arch/arm64/kernel/image-vars.h            | 7 +++++++
 drivers/firmware/efi/libstub/arm64-stub.c | 9 +++++++++
 3 files changed, 17 insertions(+), 1 deletion(-)
Signature · 1 line
2.17.1
author maintainer
#1 lore raw

PATCH 1/2 efi/libstub/arm64: avoid copying the kernel unnecessarily

If the UEFI firmware has loaded the kernel at the right alignment, and we are running a relocatable kernel, there is no point in copying the kernel to a different place in memory, and instead, we can just run it in place.

---
 drivers/firmware/efi/libstub/arm64-stub.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/firmware/efi/libstub/arm64-stub.c +9 -0
index 9254cd8ab2d3..d263f504dcf0 100644
@@ -104,6 +104,15 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
 					  (u32)phys_seed);
 
 		*image_addr = *reserve_addr + offset;
+	} else if (IS_ENABLED(CONFIG_RELOCATABLE) &&
+		   IS_ALIGNED((u64)_text - TEXT_OFFSET, EFI_KIMG_ALIGN)) {
+		/*
+		 * If we are relocatable and were loaded at a suitable offset,
+		 * there is no need to do anything and we can just execute in
+		 * place.
+		 */
+		*image_addr = (u64)_text;
+		return EFI_SUCCESS;
 	} else {
 		/*
 		 * Else, try a straight allocation at the preferred offset.
Signature · 1 line
2.17.1
author maintainer
#2 lore raw

PATCH 2/2 efi/arm64: increase the PE/COFF alignment so the kernel can run in place

In reply to Ard Biesheuvel

Update the PE/COFF metadata so that the UEFI image loader will load the kernel image at an offset that allows it to execute in place.

---
 arch/arm64/kernel/efi-header.S | 2 +-
 arch/arm64/kernel/image-vars.h | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)
arch/arm64/kernel/efi-header.S +1 -1
index 914999ccaf8a..f9ee1c2a5fd6 100644
@@ -32,7 +32,7 @@ optional_header:
 
 extra_header_fields:
 	.quad	0					// ImageBase
-	.long	SZ_4K					// SectionAlignment
+	.long	PECOFF_SECTION_ALIGNMENT		// SectionAlignment
 	.long	PECOFF_FILE_ALIGNMENT			// FileAlignment
 	.short	0					// MajorOperatingSystemVersion
 	.short	0					// MinorOperatingSystemVersion
arch/arm64/kernel/image-vars.h +7 -0
index be0a63ffed23..7a7fa3ba7b2f 100644
@@ -15,6 +15,13 @@
 __efistub_kernel_size		= _edata - _text;
 __efistub_primary_entry_offset	= primary_entry - _text;
 
+#ifndef CONFIG_RELOCATABLE
+PECOFF_SECTION_ALIGNMENT = SZ_4K;
+#elif THREAD_ALIGN > SEGMENT_ALIGN
+PECOFF_SECTION_ALIGNMENT = THREAD_ALIGN;
+#else
+PECOFF_SECTION_ALIGNMENT = SEGMENT_ALIGN;
+#endif
 
 /*
  * The EFI stub has its own symbol namespace prefixed by __efistub_, to
Signature · 1 line
2.17.1
#3 lore raw

Re: [PATCH 2/2] efi/arm64: increase the PE/COFF alignment so the kernel can run in place

Quoted 43 lines · Ard Biesheuvel

Update the PE/COFF metadata so that the UEFI image loader will load the kernel image at an offset that allows it to execute in place.

---
 arch/arm64/kernel/efi-header.S | 2 +-
 arch/arm64/kernel/image-vars.h | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)
arch/arm64/kernel/efi-header.S +1 -1
index 914999ccaf8a..f9ee1c2a5fd6 100644
@@ -32,7 +32,7 @@ optional_header:
 
 extra_header_fields:
 	.quad	0					// ImageBase
-	.long	SZ_4K					// SectionAlignment
+	.long	PECOFF_SECTION_ALIGNMENT		// SectionAlignment
 	.long	PECOFF_FILE_ALIGNMENT			// FileAlignment
 	.short	0					// MajorOperatingSystemVersion
 	.short	0					// MinorOperatingSystemVersion
arch/arm64/kernel/image-vars.h +7 -0
index be0a63ffed23..7a7fa3ba7b2f 100644
@@ -15,6 +15,13 @@
 __efistub_kernel_size		= _edata - _text;
 __efistub_primary_entry_offset	= primary_entry - _text;
 
+#ifndef CONFIG_RELOCATABLE
+PECOFF_SECTION_ALIGNMENT = SZ_4K;
+#elif THREAD_ALIGN > SEGMENT_ALIGN
+PECOFF_SECTION_ALIGNMENT = THREAD_ALIGN;
+#else
+PECOFF_SECTION_ALIGNMENT = SEGMENT_ALIGN;
+#endif
 
 /*
  * The EFI stub has its own symbol namespace prefixed by __efistub_, to
Signature · 1 line
2.17.1

The section virtual addresses and (possibly) size of image need to be updated to be a multiple of PECOFF_SECTION_ALIGNMENT, no?

author maintainer
#4 lore raw

Re: [PATCH 2/2] efi/arm64: increase the PE/COFF alignment so the kernel can run in place

Hi Arvind,

Thanks for taking a look.

Quoted 40 lines · Arvind Sankar
Quoted 38 lines · Ard Biesheuvel

Update the PE/COFF metadata so that the UEFI image loader will load the kernel image at an offset that allows it to execute in place.

---
 arch/arm64/kernel/efi-header.S | 2 +-
 arch/arm64/kernel/image-vars.h | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)
arch/arm64/kernel/efi-header.S +1 -1
index 914999ccaf8a..f9ee1c2a5fd6 100644
@@ -32,7 +32,7 @@ optional_header:
 
 extra_header_fields:
      .quad   0                                       // ImageBase
-     .long   SZ_4K                                   // SectionAlignment
+     .long   PECOFF_SECTION_ALIGNMENT                // SectionAlignment
      .long   PECOFF_FILE_ALIGNMENT                   // FileAlignment
      .short  0                                       // MajorOperatingSystemVersion
      .short  0                                       // MinorOperatingSystemVersion
arch/arm64/kernel/image-vars.h +7 -0
index be0a63ffed23..7a7fa3ba7b2f 100644
@@ -15,6 +15,13 @@
 __efistub_kernel_size                = _edata - _text;
 __efistub_primary_entry_offset       = primary_entry - _text;
 
+#ifndef CONFIG_RELOCATABLE
+PECOFF_SECTION_ALIGNMENT = SZ_4K;
+#elif THREAD_ALIGN > SEGMENT_ALIGN
+PECOFF_SECTION_ALIGNMENT = THREAD_ALIGN;
+#else
+PECOFF_SECTION_ALIGNMENT = SEGMENT_ALIGN;
+#endif
 
Quoted 8 lines
Quoted 5 lines
arch/arm64/kernel/image-vars.h +0 -1
 /*
  * The EFI stub has its own symbol namespace prefixed by __efistub_, to
--

2.17.1

The section virtual addresses and (possibly) size of image need to be updated to be a multiple of PECOFF_SECTION_ALIGNMENT, no?

Indeed. I spotted that after sending this patch - both _end and _initdata_begin need to be aligned to this value. I also noticed that it is unclear whether values over 64 KB are permitted: the PE/COFF spec mentions that 64 KB is the max for FileAlignment, and that SectionAlignment should be larger or equal to that.

So I think it would be better to set this value to 64 KB unconditionally, and round up the sections to 64 KB. This means 64k pagesize kernels with vmap'ed stack have a 50% chance of ending up at an offset that requires moving the image, but this is still an improvement over doing it all the time.

#5 lore raw
In reply to Ard Biesheuvel
Quoted 11 lines · On Thu, 26 Mar 2020 17:59:03 +0100 Ard Biesheuvel <[email protected]> wrote:

Update the relocation logic in the EFI stub for arm64 so it runs the kernel in place if it was loaded by firmware at an address which allows doing so. Then, update the PE/COFF header metadata and increase the section alignment to force the UEFI image loader to honour the minimal alignment requirement imposed by the kernel proper.

Ard Biesheuvel (2):
efi/libstub/arm64: avoid copying the kernel unnecessarily efi/arm64: increase the PE/COFF alignment so the kernel can run in
place

Quoted 5 lines
 arch/arm64/kernel/efi-header.S            | 2 +-
 arch/arm64/kernel/image-vars.h            | 7 +++++++
 drivers/firmware/efi/libstub/arm64-stub.c | 9 +++++++++
 3 files changed, 17 insertions(+), 1 deletion(-)

Hi Ard.

Seems sensible to me so I decided to give it a quick test. As things stand RELOCATABLE is only selectable by selecting
RANDOMIZE_BASE. Probably want to be able to configure it
separately (needs some help text, or a specific option to select CONFIG_RELOCATABLE).

Otherwise, superficially seems to work good for me doing a boot via pxe/grub on a Kunpeng 920 with some prints added to make sure it's taking the right paths.

Thanks,

Jonathan

author maintainer
#6 lore raw
Quoted 20 lines · Jonathan Cameron
Quoted 11 lines · On Thu, 26 Mar 2020 17:59:03 +0100 Ard Biesheuvel <[email protected]> wrote:

Update the relocation logic in the EFI stub for arm64 so it runs the kernel in place if it was loaded by firmware at an address which allows doing so. Then, update the PE/COFF header metadata and increase the section alignment to force the UEFI image loader to honour the minimal alignment requirement imposed by the kernel proper.

Ard Biesheuvel (2):
efi/libstub/arm64: avoid copying the kernel unnecessarily efi/arm64: increase the PE/COFF alignment so the kernel can run in
place

Quoted 5 lines
 arch/arm64/kernel/efi-header.S            | 2 +-
 arch/arm64/kernel/image-vars.h            | 7 +++++++
 drivers/firmware/efi/libstub/arm64-stub.c | 9 +++++++++
 3 files changed, 17 insertions(+), 1 deletion(-)
Quoted 8 lines

Hi Ard.

Seems sensible to me so I decided to give it a quick test. As things stand RELOCATABLE is only selectable by selecting
RANDOMIZE_BASE. Probably want to be able to configure it
separately (needs some help text, or a specific option to select CONFIG_RELOCATABLE).

The idea is really that KASLR kernels that were booted without a seed (or with 'nokaslr' on the command line) take this path. But in fact, I realized that the same logic applies to non-relocatable kernels, just with an alignment of 2 MB rather than 64 KB or 128 KB. So I will be sending a v2 that looks slightly different. (Another thing that I realized is that /if/ the KASLR path fails for any reason, we could still take this path instead of doing the allocation)

Quoted 4 lines

Otherwise, superficially seems to work good for me doing a boot via pxe/grub on a Kunpeng 920 with some prints added to make sure it's taking the right paths.

Excellent! Thanks for testing, and I will be cc'ing you on v2.