x86/efistub: Avoid legacy decompressor when doing EFI boot
[linux-2.6-microblaze.git] / drivers / firmware / efi / libstub / x86-stub.c
index b4685da..e976288 100644 (file)
 #include <asm/setup.h>
 #include <asm/desc.h>
 #include <asm/boot.h>
+#include <asm/kaslr.h>
 #include <asm/sev.h>
 
 #include "efistub.h"
 #include "x86-stub.h"
 
-/* Maximum physical address for 64-bit kernel with 4-level paging */
-#define MAXMEM_X86_64_4LEVEL (1ull << 46)
-
 const efi_system_table_t *efi_system_table;
 const efi_dxe_services_table_t *efi_dxe_table;
-u32 image_offset __section(".data");
 static efi_loaded_image_t *image = NULL;
 static efi_memory_attribute_protocol_t *memattr;
 
@@ -287,28 +284,6 @@ void efi_adjust_memory_range_protection(unsigned long start,
        }
 }
 
-extern const u8 startup_32[], startup_64[];
-
-static void
-setup_memory_protection(unsigned long image_base, unsigned long image_size)
-{
-#ifdef CONFIG_64BIT
-       if (image_base != (unsigned long)startup_32)
-               efi_adjust_memory_range_protection(image_base, image_size);
-#else
-       /*
-        * Clear protection flags on a whole range of possible
-        * addresses used for KASLR. We don't need to do that
-        * on x86_64, since KASLR/extraction is performed after
-        * dedicated identity page tables are built and we only
-        * need to remove possible protection on relocated image
-        * itself disregarding further relocations.
-        */
-       efi_adjust_memory_range_protection(LOAD_PHYSICAL_ADDR,
-                                          KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR);
-#endif
-}
-
 static void setup_unaccepted_memory(void)
 {
        efi_guid_t mem_acceptance_proto = OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL_GUID;
@@ -334,9 +309,7 @@ static void setup_unaccepted_memory(void)
 
 static const efi_char16_t apple[] = L"Apple";
 
-static void setup_quirks(struct boot_params *boot_params,
-                        unsigned long image_base,
-                        unsigned long image_size)
+static void setup_quirks(struct boot_params *boot_params)
 {
        efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
                efi_table_attr(efi_system_table, fw_vendor);
@@ -345,9 +318,6 @@ static void setup_quirks(struct boot_params *boot_params,
                if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
                        retrieve_apple_device_properties(boot_params);
        }
-
-       if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES))
-               setup_memory_protection(image_base, image_size);
 }
 
 /*
@@ -500,7 +470,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
        }
 
        image_base = efi_table_attr(image, image_base);
-       image_offset = (void *)startup_32 - image_base;
 
        status = efi_allocate_pages(sizeof(struct boot_params),
                                    (unsigned long *)&boot_params, ULONG_MAX);
@@ -804,6 +773,61 @@ static bool have_unsupported_snp_features(void)
        return false;
 }
 
+static void efi_get_seed(void *seed, int size)
+{
+       efi_get_random_bytes(size, seed);
+
+       /*
+        * This only updates seed[0] when running on 32-bit, but in that case,
+        * seed[1] is not used anyway, as there is no virtual KASLR on 32-bit.
+        */
+       *(unsigned long *)seed ^= kaslr_get_random_long("EFI");
+}
+
+static void error(char *str)
+{
+       efi_warn("Decompression failed: %s\n", str);
+}
+
+static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry)
+{
+       unsigned long virt_addr = LOAD_PHYSICAL_ADDR;
+       unsigned long addr, alloc_size, entry;
+       efi_status_t status;
+       u32 seed[2] = {};
+
+       /* determine the required size of the allocation */
+       alloc_size = ALIGN(max_t(unsigned long, output_len, kernel_total_size),
+                          MIN_KERNEL_ALIGN);
+
+       if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) {
+               u64 range = KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR - kernel_total_size;
+
+               efi_get_seed(seed, sizeof(seed));
+
+               virt_addr += (range * seed[1]) >> 32;
+               virt_addr &= ~(CONFIG_PHYSICAL_ALIGN - 1);
+       }
+
+       status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr,
+                                 seed[0], EFI_LOADER_CODE,
+                                 EFI_X86_KERNEL_ALLOC_LIMIT);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       entry = decompress_kernel((void *)addr, virt_addr, error);
+       if (entry == ULONG_MAX) {
+               efi_free(alloc_size, addr);
+               return EFI_LOAD_ERROR;
+       }
+
+       *kernel_entry = addr + entry;
+
+       efi_adjust_memory_range_protection(addr, kernel_total_size);
+
+       return EFI_SUCCESS;
+}
+
 static void __noreturn enter_kernel(unsigned long kernel_addr,
                                    struct boot_params *boot_params)
 {
@@ -823,10 +847,9 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
                               struct boot_params *boot_params)
 {
        efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
-       unsigned long bzimage_addr = (unsigned long)startup_32;
-       unsigned long buffer_start, buffer_end;
        struct setup_header *hdr = &boot_params->hdr;
        const struct linux_efi_initrd *initrd = NULL;
+       unsigned long kernel_entry;
        efi_status_t status;
 
        efi_system_table = sys_table_arg;
@@ -855,60 +878,6 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
                goto fail;
        }
 
-       /*
-        * If the kernel isn't already loaded at a suitable address,
-        * relocate it.
-        *
-        * It must be loaded above LOAD_PHYSICAL_ADDR.
-        *
-        * The maximum address for 64-bit is 1 << 46 for 4-level paging. This
-        * is defined as the macro MAXMEM, but unfortunately that is not a
-        * compile-time constant if 5-level paging is configured, so we instead
-        * define our own macro for use here.
-        *
-        * For 32-bit, the maximum address is complicated to figure out, for
-        * now use KERNEL_IMAGE_SIZE, which will be 512MiB, the same as what
-        * KASLR uses.
-        *
-        * Also relocate it if image_offset is zero, i.e. the kernel wasn't
-        * loaded by LoadImage, but rather by a bootloader that called the
-        * handover entry. The reason we must always relocate in this case is
-        * to handle the case of systemd-boot booting a unified kernel image,
-        * which is a PE executable that contains the bzImage and an initrd as
-        * COFF sections. The initrd section is placed after the bzImage
-        * without ensuring that there are at least init_size bytes available
-        * for the bzImage, and thus the compressed kernel's startup code may
-        * overwrite the initrd unless it is moved out of the way.
-        */
-
-       buffer_start = ALIGN(bzimage_addr - image_offset,
-                            hdr->kernel_alignment);
-       buffer_end = buffer_start + hdr->init_size;
-
-       if ((buffer_start < LOAD_PHYSICAL_ADDR)                              ||
-           (IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE)    ||
-           (IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) ||
-           (image_offset == 0)) {
-               extern char _bss[];
-
-               status = efi_relocate_kernel(&bzimage_addr,
-                                            (unsigned long)_bss - bzimage_addr,
-                                            hdr->init_size,
-                                            hdr->pref_address,
-                                            hdr->kernel_alignment,
-                                            LOAD_PHYSICAL_ADDR);
-               if (status != EFI_SUCCESS) {
-                       efi_err("efi_relocate_kernel() failed!\n");
-                       goto fail;
-               }
-               /*
-                * Now that we've copied the kernel elsewhere, we no longer
-                * have a set up block before startup_32(), so reset image_offset
-                * to zero in case it was set earlier.
-                */
-               image_offset = 0;
-       }
-
 #ifdef CONFIG_CMDLINE_BOOL
        status = efi_parse_options(CONFIG_CMDLINE);
        if (status != EFI_SUCCESS) {
@@ -926,6 +895,12 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
                }
        }
 
+       status = efi_decompress_kernel(&kernel_entry);
+       if (status != EFI_SUCCESS) {
+               efi_err("Failed to decompress kernel\n");
+               goto fail;
+       }
+
        /*
         * At this point, an initrd may already have been loaded by the
         * bootloader and passed via bootparams. We permit an initrd loaded
@@ -965,7 +940,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
 
        setup_efi_pci(boot_params);
 
-       setup_quirks(boot_params, bzimage_addr, buffer_end - buffer_start);
+       setup_quirks(boot_params);
 
        setup_unaccepted_memory();
 
@@ -975,12 +950,15 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
                goto fail;
        }
 
-       efi_5level_switch();
+       /*
+        * Call the SEV init code while still running with the firmware's
+        * GDT/IDT, so #VC exceptions will be handled by EFI.
+        */
+       sev_enable(boot_params);
 
-       if (IS_ENABLED(CONFIG_X86_64))
-               bzimage_addr += startup_64 - startup_32;
+       efi_5level_switch();
 
-       enter_kernel(bzimage_addr, boot_params);
+       enter_kernel(kernel_entry, boot_params);
 fail:
        efi_err("efi_stub_entry() failed!\n");