linux/drivers/firmware/efi/libstub/arm64-stub.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2013, 2014 Linaro Ltd;  <roy.franz@linaro.org>
   4 *
   5 * This file implements the EFI boot stub for the arm64 kernel.
   6 * Adapted from ARM version by Mark Salter <msalter@redhat.com>
   7 */
   8
   9
  10#include <linux/efi.h>
  11#include <asm/efi.h>
  12#include <asm/memory.h>
  13#include <asm/sections.h>
  14#include <asm/sysreg.h>
  15
  16#include "efistub.h"
  17
  18efi_status_t check_platform_features(void)
  19{
  20        u64 tg;
  21
  22        /* UEFI mandates support for 4 KB granularity, no need to check */
  23        if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
  24                return EFI_SUCCESS;
  25
  26        tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf;
  27        if (tg < ID_AA64MMFR0_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_TGRAN_SUPPORTED_MAX) {
  28                if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
  29                        efi_err("This 64 KB granular kernel is not supported by your CPU\n");
  30                else
  31                        efi_err("This 16 KB granular kernel is not supported by your CPU\n");
  32                return EFI_UNSUPPORTED;
  33        }
  34        return EFI_SUCCESS;
  35}
  36
  37/*
  38 * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail
  39 * to provide space, and fail to zero it). Check for this condition by double
  40 * checking that the first and the last byte of the image are covered by the
  41 * same EFI memory map entry.
  42 */
  43static bool check_image_region(u64 base, u64 size)
  44{
  45        unsigned long map_size, desc_size, buff_size;
  46        efi_memory_desc_t *memory_map;
  47        struct efi_boot_memmap map;
  48        efi_status_t status;
  49        bool ret = false;
  50        int map_offset;
  51
  52        map.map =       &memory_map;
  53        map.map_size =  &map_size;
  54        map.desc_size = &desc_size;
  55        map.desc_ver =  NULL;
  56        map.key_ptr =   NULL;
  57        map.buff_size = &buff_size;
  58
  59        status = efi_get_memory_map(&map);
  60        if (status != EFI_SUCCESS)
  61                return false;
  62
  63        for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
  64                efi_memory_desc_t *md = (void *)memory_map + map_offset;
  65                u64 end = md->phys_addr + md->num_pages * EFI_PAGE_SIZE;
  66
  67                /*
  68                 * Find the region that covers base, and return whether
  69                 * it covers base+size bytes.
  70                 */
  71                if (base >= md->phys_addr && base < end) {
  72                        ret = (base + size) <= end;
  73                        break;
  74                }
  75        }
  76
  77        efi_bs_call(free_pool, memory_map);
  78
  79        return ret;
  80}
  81
  82efi_status_t handle_kernel_image(unsigned long *image_addr,
  83                                 unsigned long *image_size,
  84                                 unsigned long *reserve_addr,
  85                                 unsigned long *reserve_size,
  86                                 efi_loaded_image_t *image)
  87{
  88        efi_status_t status;
  89        unsigned long kernel_size, kernel_memsize = 0;
  90        u32 phys_seed = 0;
  91
  92        /*
  93         * Although relocatable kernels can fix up the misalignment with
  94         * respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are
  95         * subtly out of sync with those recorded in the vmlinux when kaslr is
  96         * disabled but the image required relocation anyway. Therefore retain
  97         * 2M alignment if KASLR was explicitly disabled, even if it was not
  98         * going to be activated to begin with.
  99         */
 100        u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN;
 101
 102        if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
 103                if (!efi_nokaslr) {
 104                        status = efi_get_random_bytes(sizeof(phys_seed),
 105                                                      (u8 *)&phys_seed);
 106                        if (status == EFI_NOT_FOUND) {
 107                                efi_info("EFI_RNG_PROTOCOL unavailable\n");
 108                                efi_nokaslr = true;
 109                        } else if (status != EFI_SUCCESS) {
 110                                efi_err("efi_get_random_bytes() failed (0x%lx)\n",
 111                                        status);
 112                                efi_nokaslr = true;
 113                        }
 114                } else {
 115                        efi_info("KASLR disabled on kernel command line\n");
 116                }
 117        }
 118
 119        if (image->image_base != _text)
 120                efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
 121
 122        if (!IS_ALIGNED((u64)_text, EFI_KIMG_ALIGN))
 123                efi_err("FIRMWARE BUG: kernel image not aligned on %ldk boundary\n",
 124                        EFI_KIMG_ALIGN >> 10);
 125
 126        kernel_size = _edata - _text;
 127        kernel_memsize = kernel_size + (_end - _edata);
 128        *reserve_size = kernel_memsize;
 129
 130        if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
 131                /*
 132                 * If KASLR is enabled, and we have some randomness available,
 133                 * locate the kernel at a randomized offset in physical memory.
 134                 */
 135                status = efi_random_alloc(*reserve_size, min_kimg_align,
 136                                          reserve_addr, phys_seed);
 137                if (status != EFI_SUCCESS)
 138                        efi_warn("efi_random_alloc() failed: 0x%lx\n", status);
 139        } else {
 140                status = EFI_OUT_OF_RESOURCES;
 141        }
 142
 143        if (status != EFI_SUCCESS) {
 144                if (!check_image_region((u64)_text, kernel_memsize)) {
 145                        efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n");
 146                } else if (IS_ALIGNED((u64)_text, min_kimg_align)) {
 147                        /*
 148                         * Just execute from wherever we were loaded by the
 149                         * UEFI PE/COFF loader if the alignment is suitable.
 150                         */
 151                        *image_addr = (u64)_text;
 152                        *reserve_size = 0;
 153                        return EFI_SUCCESS;
 154                }
 155
 156                status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
 157                                                    ULONG_MAX, min_kimg_align);
 158
 159                if (status != EFI_SUCCESS) {
 160                        efi_err("Failed to relocate kernel\n");
 161                        *reserve_size = 0;
 162                        return status;
 163                }
 164        }
 165
 166        *image_addr = *reserve_addr;
 167        memcpy((void *)*image_addr, _text, kernel_size);
 168
 169        return EFI_SUCCESS;
 170}
 171