190075Sobrien// SPDX-License-Identifier: GPL-2.0 290075Sobrien/* 390075Sobrien * Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org> 490075Sobrien * 590075Sobrien * This file implements the EFI boot stub for the arm64 kernel. 690075Sobrien * Adapted from ARM version by Mark Salter <msalter@redhat.com> 790075Sobrien */ 8132718Skan 990075Sobrien 10132718Skan#include <linux/efi.h> 11132718Skan#include <asm/efi.h> 12132718Skan#include <asm/image.h> 13132718Skan#include <asm/memory.h> 1490075Sobrien#include <asm/sysreg.h> 15132718Skan 16132718Skan#include "efistub.h" 17132718Skan 18132718Skanstatic bool system_needs_vamap(void) 1990075Sobrien{ 20132718Skan const struct efi_smbios_type4_record *record; 21132718Skan const u32 __aligned(1) *socid; 22132718Skan const u8 *version; 23132718Skan 2490075Sobrien /* 2590075Sobrien * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if 2690075Sobrien * SetVirtualAddressMap() has not been called prior. Most Altra systems 2790075Sobrien * can be identified by the SMCCC soc ID, which is conveniently exposed 2890075Sobrien * via the type 4 SMBIOS records. Otherwise, test the processor version 2990075Sobrien * field. eMAG systems all appear to have the processor version field 3090075Sobrien * set to "eMAG". 3190075Sobrien */ 3290075Sobrien record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4); 3390075Sobrien if (!record) 3490075Sobrien return false; 3590075Sobrien 3690075Sobrien socid = (u32 *)record->processor_id; 3790075Sobrien switch (*socid & 0xffff000f) { 3890075Sobrien static char const altra[] = "Ampere(TM) Altra(TM) Processor"; 39117395Skan static char const emag[] = "eMAG"; 40117395Skan 4190075Sobrien default: 4290075Sobrien version = efi_get_smbios_string(&record->header, 4, 4390075Sobrien processor_version); 4490075Sobrien if (!version || (strncmp(version, altra, sizeof(altra) - 1) && 4590075Sobrien strncmp(version, emag, sizeof(emag) - 1))) 4690075Sobrien break; 47117395Skan 48117395Skan fallthrough; 49132718Skan 50117395Skan case 0x0a160001: // Altra 51117395Skan case 0x0a160002: // Altra Max 5290075Sobrien efi_warn("Working around broken SetVirtualAddressMap()\n"); 5390075Sobrien return true; 5490075Sobrien } 55117395Skan 56117395Skan return false; 57117395Skan} 5890075Sobrien 59117395Skanefi_status_t check_platform_features(void) 6090075Sobrien{ 6190075Sobrien u64 tg; 6290075Sobrien 6390075Sobrien /* 6490075Sobrien * If we have 48 bits of VA space for TTBR0 mappings, we can map the 6590075Sobrien * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is 6690075Sobrien * unnecessary. 67117395Skan */ 6890075Sobrien if (VA_BITS_MIN >= 48 && !system_needs_vamap()) 6990075Sobrien efi_novamap = true; 7090075Sobrien 71117395Skan /* UEFI mandates support for 4 KB granularity, no need to check */ 72117395Skan if (IS_ENABLED(CONFIG_ARM64_4K_PAGES)) 73117395Skan return EFI_SUCCESS; 74117395Skan 75117395Skan tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf; 76117395Skan if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) { 77117395Skan if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) 78117395Skan efi_err("This 64 KB granular kernel is not supported by your CPU\n"); 7990075Sobrien else 8090075Sobrien efi_err("This 16 KB granular kernel is not supported by your CPU\n"); 8190075Sobrien return EFI_UNSUPPORTED; 8290075Sobrien } 8390075Sobrien return EFI_SUCCESS; 8490075Sobrien} 8590075Sobrien 8690075Sobrien#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE 8790075Sobrien#define DCTYPE "civac" 88117395Skan#else 8990075Sobrien#define DCTYPE "cvau" 9090075Sobrien#endif 9190075Sobrien 9290075Sobrienu32 __weak code_size; 9390075Sobrien 9490075Sobrienvoid efi_cache_sync_image(unsigned long image_base, 9596263Sobrien unsigned long alloc_size) 9696263Sobrien{ 9796263Sobrien u32 ctr = read_cpuid_effective_cachetype(); 9890075Sobrien u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr, 9990075Sobrien CTR_EL0_DminLine_SHIFT); 100117395Skan 10190075Sobrien /* only perform the cache maintenance if needed for I/D coherency */ 10290075Sobrien if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) { 10390075Sobrien unsigned long base = image_base; 10490075Sobrien unsigned long size = code_size; 10590075Sobrien 10690075Sobrien do { 10790075Sobrien asm("dc " DCTYPE ", %0" :: "r"(base)); 10890075Sobrien base += lsize; 109132718Skan size -= lsize; 11090075Sobrien } while (size >= lsize); 11190075Sobrien } 11290075Sobrien 11390075Sobrien asm("ic ialluis"); 11490075Sobrien dsb(ish); 11590075Sobrien isb(); 11690075Sobrien 117132718Skan efi_remap_image(image_base, alloc_size, code_size); 118132718Skan} 11990075Sobrien 12090075Sobrienunsigned long __weak primary_entry_offset(void) 12190075Sobrien{ 12290075Sobrien /* 12390075Sobrien * By default, we can invoke the kernel via the branch instruction in 12490075Sobrien * the image header, so offset #0. This will be overridden by the EFI 12590075Sobrien * stub build that is linked into the core kernel, as in that case, the 12690075Sobrien * image header may not have been loaded into memory, or may be mapped 12790075Sobrien * with non-executable permissions. 12890075Sobrien */ 12990075Sobrien return 0; 13090075Sobrien} 13190075Sobrien 13290075Sobrienvoid __noreturn efi_enter_kernel(unsigned long entrypoint, 13390075Sobrien unsigned long fdt_addr, 13490075Sobrien unsigned long fdt_size) 13590075Sobrien{ 13690075Sobrien void (* __noreturn enter_kernel)(u64, u64, u64, u64); 13790075Sobrien 13890075Sobrien enter_kernel = (void *)entrypoint + primary_entry_offset(); 13990075Sobrien enter_kernel(fdt_addr, 0, 0, 0); 14090075Sobrien} 14190075Sobrien