linux/drivers/firmware/efi/libstub/arm32-stub.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
   4 */
   5#include <linux/efi.h>
   6#include <asm/efi.h>
   7
   8#include "efistub.h"
   9
  10static efi_guid_t cpu_state_guid = LINUX_EFI_ARM_CPU_STATE_TABLE_GUID;
  11
  12struct efi_arm_entry_state *efi_entry_state;
  13
  14static void get_cpu_state(u32 *cpsr, u32 *sctlr)
  15{
  16        asm("mrs %0, cpsr" : "=r"(*cpsr));
  17        if ((*cpsr & MODE_MASK) == HYP_MODE)
  18                asm("mrc p15, 4, %0, c1, c0, 0" : "=r"(*sctlr));
  19        else
  20                asm("mrc p15, 0, %0, c1, c0, 0" : "=r"(*sctlr));
  21}
  22
  23efi_status_t check_platform_features(void)
  24{
  25        efi_status_t status;
  26        u32 cpsr, sctlr;
  27        int block;
  28
  29        get_cpu_state(&cpsr, &sctlr);
  30
  31        efi_info("Entering in %s mode with MMU %sabled\n",
  32                 ((cpsr & MODE_MASK) == HYP_MODE) ? "HYP" : "SVC",
  33                 (sctlr & 1) ? "en" : "dis");
  34
  35        status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
  36                             sizeof(*efi_entry_state),
  37                             (void **)&efi_entry_state);
  38        if (status != EFI_SUCCESS) {
  39                efi_err("allocate_pool() failed\n");
  40                return status;
  41        }
  42
  43        efi_entry_state->cpsr_before_ebs = cpsr;
  44        efi_entry_state->sctlr_before_ebs = sctlr;
  45
  46        status = efi_bs_call(install_configuration_table, &cpu_state_guid,
  47                             efi_entry_state);
  48        if (status != EFI_SUCCESS) {
  49                efi_err("install_configuration_table() failed\n");
  50                goto free_state;
  51        }
  52
  53        /* non-LPAE kernels can run anywhere */
  54        if (!IS_ENABLED(CONFIG_ARM_LPAE))
  55                return EFI_SUCCESS;
  56
  57        /* LPAE kernels need compatible hardware */
  58        block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
  59        if (block < 5) {
  60                efi_err("This LPAE kernel is not supported by your CPU\n");
  61                status = EFI_UNSUPPORTED;
  62                goto drop_table;
  63        }
  64        return EFI_SUCCESS;
  65
  66drop_table:
  67        efi_bs_call(install_configuration_table, &cpu_state_guid, NULL);
  68free_state:
  69        efi_bs_call(free_pool, efi_entry_state);
  70        return status;
  71}
  72
  73void efi_handle_post_ebs_state(void)
  74{
  75        get_cpu_state(&efi_entry_state->cpsr_after_ebs,
  76                      &efi_entry_state->sctlr_after_ebs);
  77}
  78
  79static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
  80
  81struct screen_info *alloc_screen_info(void)
  82{
  83        struct screen_info *si;
  84        efi_status_t status;
  85
  86        /*
  87         * Unlike on arm64, where we can directly fill out the screen_info
  88         * structure from the stub, we need to allocate a buffer to hold
  89         * its contents while we hand over to the kernel proper from the
  90         * decompressor.
  91         */
  92        status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
  93                             sizeof(*si), (void **)&si);
  94
  95        if (status != EFI_SUCCESS)
  96                return NULL;
  97
  98        status = efi_bs_call(install_configuration_table,
  99                             &screen_info_guid, si);
 100        if (status == EFI_SUCCESS)
 101                return si;
 102
 103        efi_bs_call(free_pool, si);
 104        return NULL;
 105}
 106
 107void free_screen_info(struct screen_info *si)
 108{
 109        if (!si)
 110                return;
 111
 112        efi_bs_call(install_configuration_table, &screen_info_guid, NULL);
 113        efi_bs_call(free_pool, si);
 114}
 115
 116efi_status_t handle_kernel_image(unsigned long *image_addr,
 117                                 unsigned long *image_size,
 118                                 unsigned long *reserve_addr,
 119                                 unsigned long *reserve_size,
 120                                 efi_loaded_image_t *image)
 121{
 122        const int slack = TEXT_OFFSET - 5 * PAGE_SIZE;
 123        int alloc_size = MAX_UNCOMP_KERNEL_SIZE + EFI_PHYS_ALIGN;
 124        unsigned long alloc_base, kernel_base;
 125        efi_status_t status;
 126
 127        /*
 128         * Allocate space for the decompressed kernel as low as possible.
 129         * The region should be 16 MiB aligned, but the first 'slack' bytes
 130         * are not used by Linux, so we allow those to be occupied by the
 131         * firmware.
 132         */
 133        status = efi_low_alloc_above(alloc_size, EFI_PAGE_SIZE, &alloc_base, 0x0);
 134        if (status != EFI_SUCCESS) {
 135                efi_err("Unable to allocate memory for uncompressed kernel.\n");
 136                return status;
 137        }
 138
 139        if ((alloc_base % EFI_PHYS_ALIGN) > slack) {
 140                /*
 141                 * More than 'slack' bytes are already occupied at the base of
 142                 * the allocation, so we need to advance to the next 16 MiB block.
 143                 */
 144                kernel_base = round_up(alloc_base, EFI_PHYS_ALIGN);
 145                efi_info("Free memory starts at 0x%lx, setting kernel_base to 0x%lx\n",
 146                         alloc_base, kernel_base);
 147        } else {
 148                kernel_base = round_down(alloc_base, EFI_PHYS_ALIGN);
 149        }
 150
 151        *reserve_addr = kernel_base + slack;
 152        *reserve_size = MAX_UNCOMP_KERNEL_SIZE;
 153
 154        /* now free the parts that we will not use */
 155        if (*reserve_addr > alloc_base) {
 156                efi_bs_call(free_pages, alloc_base,
 157                            (*reserve_addr - alloc_base) / EFI_PAGE_SIZE);
 158                alloc_size -= *reserve_addr - alloc_base;
 159        }
 160        efi_bs_call(free_pages, *reserve_addr + MAX_UNCOMP_KERNEL_SIZE,
 161                    (alloc_size - MAX_UNCOMP_KERNEL_SIZE) / EFI_PAGE_SIZE);
 162
 163        *image_addr = kernel_base + TEXT_OFFSET;
 164        *image_size = 0;
 165
 166        efi_debug("image addr == 0x%lx, reserve_addr == 0x%lx\n",
 167                  *image_addr, *reserve_addr);
 168
 169        return EFI_SUCCESS;
 170}
 171