@@ -74,6 +74,10 @@ SYM_CODE_START_NOALIGN(startup_64)
*/
.org startup_64 + 0x10 - 3, BYTES_NOP1
nopl (_end - startup_64)(%rax)
+
+ /* put the size of the initial executable mapping at offset 0x20 */
+ .org startup_64 + 0x20 - 3, BYTES_NOP1
+ nopl (_einittext - startup_64)(%rax)
#endif
leaq _text(%rip), %rdi
@@ -60,10 +60,33 @@ efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr)
return status;
}
+static void efi_remap_exec(unsigned long base, unsigned long size)
+{
+ static efi_memory_attribute_protocol_t *memattr = (void *)ULONG_MAX;
+ efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
+ efi_status_t status;
+
+ if (memattr == (void *)ULONG_MAX) {
+ memattr = NULL;
+ status = efi_bs_call(locate_protocol, &guid, NULL,
+ (void **)&memattr);
+ if (status != EFI_SUCCESS)
+ return;
+ } else if (!memattr) {
+ return;
+ }
+
+ status = memattr->clear_memory_attributes(memattr, base, size,
+ EFI_MEMORY_XP);
+ if (status != EFI_SUCCESS)
+ efi_warn("Failed to clear NX attribute on code region\n");
+}
+
void efi_cache_sync_image(unsigned long image_base, unsigned long alloc_size)
{
const u32 payload_size = *(u32 *)(_gzdata_end - 4);
const u32 image_size = *(u32 *)(image_base + 0x10);
+ const u32 code_size = *(u32 *)(image_base + 0x20);
const s32 *reloc = (s32 *)(image_base + payload_size);
u64 va_offset = __START_KERNEL - image_base;
u64 range, delta;
@@ -107,6 +130,8 @@ void efi_cache_sync_image(unsigned long image_base, unsigned long alloc_size)
*(u64 *)((s64)*reloc - va_offset) += delta;
efi_free(alloc_size - image_size, image_base + image_size);
+
+ efi_remap_exec(image_base, PAGE_ALIGN(code_size));
}
static void __naked tmpl_toggle(void *cr3, void *gdt)
@@ -197,6 +222,8 @@ static efi_status_t efi_setup_5level_paging(void)
*/
*(u32 *)&la57_code[tmpl_size - 6] += (u64)la57_code;
+ efi_remap_exec((unsigned long)la57_code, PAGE_SIZE);
+
return EFI_SUCCESS;
}
Future EFI firmware will require the PE/COFF NX_COMPAT header flag to be set in order to retain access to all system facilities while features such as UEFI secure boot or TCG measured boot are enabled. The consequence of setting this flag is that the EFI firmware image loader may configure the page allocator to set the NX attribute on all allocations requested by the image. This means we should clear this attribute on all regions we allocate and expect to be able to execute from. In the x86 EFI zboot case, the only code we execute under EFI's 1:1 mapping that was not loaded by the image loader itself is the trampoline that effectuates the switch between 4 and 5 level paging, and the part of the loaded kernel image that runs before switching to its own page tables. So let's use the EFI memory attributes protocol to clear the NX attribute on these regions. Whether or not setting the read-only attribute first is required is unclear at this point. Given that the kernel startup code uses two different executable sections before switching to its own page tables (normal text and inittext, with a writable data section in between), this would require some minor reorganization of the kernel memory map. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> --- arch/x86/kernel/head_64.S | 4 +++ drivers/firmware/efi/libstub/x86-zboot.c | 27 ++++++++++++++++++++ 2 files changed, 31 insertions(+)