Linux Audio

Check our new training course

Loading...
v6.9.4
  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/image.h>
 13#include <asm/memory.h>
 14#include <asm/sysreg.h>
 15
 16#include "efistub.h"
 17
 18static bool system_needs_vamap(void)
 19{
 20	const struct efi_smbios_type4_record *record;
 21	const u32 __aligned(1) *socid;
 22	const u8 *version;
 23
 24	/*
 25	 * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
 26	 * SetVirtualAddressMap() has not been called prior. Most Altra systems
 27	 * can be identified by the SMCCC soc ID, which is conveniently exposed
 28	 * via the type 4 SMBIOS records. Otherwise, test the processor version
 29	 * field. eMAG systems all appear to have the processor version field
 30	 * set to "eMAG".
 31	 */
 32	record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4);
 33	if (!record)
 34		return false;
 35
 36	socid = (u32 *)record->processor_id;
 37	switch (*socid & 0xffff000f) {
 38		static char const altra[] = "Ampere(TM) Altra(TM) Processor";
 39		static char const emag[] = "eMAG";
 40
 41	default:
 42		version = efi_get_smbios_string(&record->header, 4,
 43						processor_version);
 44		if (!version || (strncmp(version, altra, sizeof(altra) - 1) &&
 45				 strncmp(version, emag, sizeof(emag) - 1)))
 46			break;
 47
 48		fallthrough;
 49
 50	case 0x0a160001:	// Altra
 51	case 0x0a160002:	// Altra Max
 52		efi_warn("Working around broken SetVirtualAddressMap()\n");
 53		return true;
 54	}
 55
 56	return false;
 57}
 58
 59efi_status_t check_platform_features(void)
 60{
 61	u64 tg;
 62
 63	/*
 64	 * If we have 48 bits of VA space for TTBR0 mappings, we can map the
 65	 * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is
 66	 * unnecessary.
 67	 */
 68	if (VA_BITS_MIN >= 48 && !system_needs_vamap())
 69		efi_novamap = true;
 70
 71	/* UEFI mandates support for 4 KB granularity, no need to check */
 72	if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
 73		return EFI_SUCCESS;
 74
 75	tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf;
 76	if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) {
 77		if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
 78			efi_err("This 64 KB granular kernel is not supported by your CPU\n");
 79		else
 80			efi_err("This 16 KB granular kernel is not supported by your CPU\n");
 81		return EFI_UNSUPPORTED;
 82	}
 83	return EFI_SUCCESS;
 84}
 85
 86#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
 87#define DCTYPE	"civac"
 88#else
 89#define DCTYPE	"cvau"
 90#endif
 91
 92u32 __weak code_size;
 93
 94void efi_cache_sync_image(unsigned long image_base,
 95			  unsigned long alloc_size)
 96{
 97	u32 ctr = read_cpuid_effective_cachetype();
 98	u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
 99						CTR_EL0_DminLine_SHIFT);
100
101	/* only perform the cache maintenance if needed for I/D coherency */
102	if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
103		unsigned long base = image_base;
104		unsigned long size = code_size;
105
106		do {
107			asm("dc " DCTYPE ", %0" :: "r"(base));
108			base += lsize;
109			size -= lsize;
110		} while (size >= lsize);
111	}
112
113	asm("ic ialluis");
114	dsb(ish);
115	isb();
116
117	efi_remap_image(image_base, alloc_size, code_size);
118}
119
120unsigned long __weak primary_entry_offset(void)
121{
122	/*
123	 * By default, we can invoke the kernel via the branch instruction in
124	 * the image header, so offset #0. This will be overridden by the EFI
125	 * stub build that is linked into the core kernel, as in that case, the
126	 * image header may not have been loaded into memory, or may be mapped
127	 * with non-executable permissions.
128	 */
129       return 0;
130}
131
132void __noreturn efi_enter_kernel(unsigned long entrypoint,
133				 unsigned long fdt_addr,
134				 unsigned long fdt_size)
135{
136	void (* __noreturn enter_kernel)(u64, u64, u64, u64);
137
138	enter_kernel = (void *)entrypoint + primary_entry_offset();
139	enter_kernel(fdt_addr, 0, 0, 0);
140}
v6.13.7
  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/image.h>
 13#include <asm/memory.h>
 14#include <asm/sysreg.h>
 15
 16#include "efistub.h"
 17
 18static bool system_needs_vamap(void)
 19{
 20	const struct efi_smbios_type4_record *record;
 21	const u32 __aligned(1) *socid;
 22	const u8 *version;
 23
 24	/*
 25	 * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
 26	 * SetVirtualAddressMap() has not been called prior. Most Altra systems
 27	 * can be identified by the SMCCC soc ID, which is conveniently exposed
 28	 * via the type 4 SMBIOS records. Otherwise, test the processor version
 29	 * field. eMAG systems all appear to have the processor version field
 30	 * set to "eMAG".
 31	 */
 32	record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4);
 33	if (!record)
 34		return false;
 35
 36	socid = (u32 *)record->processor_id;
 37	switch (*socid & 0xffff000f) {
 38		static char const altra[] = "Ampere(TM) Altra(TM) Processor";
 39		static char const emag[] = "eMAG";
 40
 41	default:
 42		version = efi_get_smbios_string(record, processor_version);
 
 43		if (!version || (strncmp(version, altra, sizeof(altra) - 1) &&
 44				 strncmp(version, emag, sizeof(emag) - 1)))
 45			break;
 46
 47		fallthrough;
 48
 49	case 0x0a160001:	// Altra
 50	case 0x0a160002:	// Altra Max
 51		efi_warn("Working around broken SetVirtualAddressMap()\n");
 52		return true;
 53	}
 54
 55	return false;
 56}
 57
 58efi_status_t check_platform_features(void)
 59{
 60	u64 tg;
 61
 62	/*
 63	 * If we have 48 bits of VA space for TTBR0 mappings, we can map the
 64	 * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is
 65	 * unnecessary.
 66	 */
 67	if (VA_BITS_MIN >= 48 && !system_needs_vamap())
 68		efi_novamap = true;
 69
 70	/* UEFI mandates support for 4 KB granularity, no need to check */
 71	if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
 72		return EFI_SUCCESS;
 73
 74	tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf;
 75	if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) {
 76		if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
 77			efi_err("This 64 KB granular kernel is not supported by your CPU\n");
 78		else
 79			efi_err("This 16 KB granular kernel is not supported by your CPU\n");
 80		return EFI_UNSUPPORTED;
 81	}
 82	return EFI_SUCCESS;
 83}
 84
 85#ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
 86#define DCTYPE	"civac"
 87#else
 88#define DCTYPE	"cvau"
 89#endif
 90
 91u32 __weak code_size;
 92
 93void efi_cache_sync_image(unsigned long image_base,
 94			  unsigned long alloc_size)
 95{
 96	u32 ctr = read_cpuid_effective_cachetype();
 97	u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
 98						CTR_EL0_DminLine_SHIFT);
 99
100	/* only perform the cache maintenance if needed for I/D coherency */
101	if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
102		unsigned long base = image_base;
103		unsigned long size = code_size;
104
105		do {
106			asm("dc " DCTYPE ", %0" :: "r"(base));
107			base += lsize;
108			size -= lsize;
109		} while (size >= lsize);
110	}
111
112	asm("ic ialluis");
113	dsb(ish);
114	isb();
115
116	efi_remap_image(image_base, alloc_size, code_size);
117}
118
119unsigned long __weak primary_entry_offset(void)
120{
121	/*
122	 * By default, we can invoke the kernel via the branch instruction in
123	 * the image header, so offset #0. This will be overridden by the EFI
124	 * stub build that is linked into the core kernel, as in that case, the
125	 * image header may not have been loaded into memory, or may be mapped
126	 * with non-executable permissions.
127	 */
128       return 0;
129}
130
131void __noreturn efi_enter_kernel(unsigned long entrypoint,
132				 unsigned long fdt_addr,
133				 unsigned long fdt_size)
134{
135	void (* __noreturn enter_kernel)(u64, u64, u64, u64);
136
137	enter_kernel = (void *)entrypoint + primary_entry_offset();
138	enter_kernel(fdt_addr, 0, 0, 0);
139}