Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/arm64.c
26483 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2013, 2014 Linaro Ltd; <[email protected]>
4
*
5
* This file implements the EFI boot stub for the arm64 kernel.
6
* Adapted from ARM version by Mark Salter <[email protected]>
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
18
static 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
58
efi_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
91
u32 __weak code_size;
92
93
void 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
119
unsigned 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
131
void __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
}
140
141