1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/firmware/efi/libstub/arm64.c
Ard Biesheuvel 026b85796a efi/zboot: arm64: Grab code size from ELF symbol in payload
Instead of relying on a dodgy dd hack to copy the image code size from
the uncompressed image's PE header to the end of the compressed image,
let's grab the code size from the symbol that is injected into the ELF
object by the Kbuild rules that generate the compressed payload.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
2023-04-26 23:06:48 +02:00

140 lines
3.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org>
*
* This file implements the EFI boot stub for the arm64 kernel.
* Adapted from ARM version by Mark Salter <msalter@redhat.com>
*/
#include <linux/efi.h>
#include <asm/efi.h>
#include <asm/image.h>
#include <asm/memory.h>
#include <asm/sysreg.h>
#include "efistub.h"
static bool system_needs_vamap(void)
{
const struct efi_smbios_type4_record *record;
const u32 __aligned(1) *socid;
const u8 *version;
/*
* Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
* SetVirtualAddressMap() has not been called prior. Most Altra systems
* can be identified by the SMCCC soc ID, which is conveniently exposed
* via the type 4 SMBIOS records. Otherwise, test the processor version
* field. eMAG systems all appear to have the processor version field
* set to "eMAG".
*/
record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4);
if (!record)
return false;
socid = (u32 *)record->processor_id;
switch (*socid & 0xffff000f) {
static char const altra[] = "Ampere(TM) Altra(TM) Processor";
static char const emag[] = "eMAG";
default:
version = efi_get_smbios_string(&record->header, 4,
processor_version);
if (!version || (strncmp(version, altra, sizeof(altra) - 1) &&
strncmp(version, emag, sizeof(emag) - 1)))
break;
fallthrough;
case 0x0a160001: // Altra
case 0x0a160002: // Altra Max
efi_warn("Working around broken SetVirtualAddressMap()\n");
return true;
}
return false;
}
efi_status_t check_platform_features(void)
{
u64 tg;
/*
* If we have 48 bits of VA space for TTBR0 mappings, we can map the
* UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is
* unnecessary.
*/
if (VA_BITS_MIN >= 48 && !system_needs_vamap())
efi_novamap = true;
/* UEFI mandates support for 4 KB granularity, no need to check */
if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
return EFI_SUCCESS;
tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf;
if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) {
if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
efi_err("This 64 KB granular kernel is not supported by your CPU\n");
else
efi_err("This 16 KB granular kernel is not supported by your CPU\n");
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
#define DCTYPE "civac"
#else
#define DCTYPE "cvau"
#endif
u32 __weak code_size;
void efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size)
{
u32 ctr = read_cpuid_effective_cachetype();
u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
CTR_EL0_DminLine_SHIFT);
/* only perform the cache maintenance if needed for I/D coherency */
if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
unsigned long base = image_base;
unsigned long size = code_size;
do {
asm("dc " DCTYPE ", %0" :: "r"(base));
base += lsize;
size -= lsize;
} while (size >= lsize);
}
asm("ic ialluis");
dsb(ish);
isb();
efi_remap_image(image_base, alloc_size, code_size);
}
unsigned long __weak primary_entry_offset(void)
{
/*
* By default, we can invoke the kernel via the branch instruction in
* the image header, so offset #0. This will be overridden by the EFI
* stub build that is linked into the core kernel, as in that case, the
* image header may not have been loaded into memory, or may be mapped
* with non-executable permissions.
*/
return 0;
}
void __noreturn efi_enter_kernel(unsigned long entrypoint,
unsigned long fdt_addr,
unsigned long fdt_size)
{
void (* __noreturn enter_kernel)(u64, u64, u64, u64);
enter_kernel = (void *)entrypoint + primary_entry_offset();
enter_kernel(fdt_addr, 0, 0, 0);
}