]> Git Repo - J-linux.git/blob - arch/x86/boot/compressed/efi.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / arch / x86 / boot / compressed / efi.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Helpers for early access to EFI configuration table.
4  *
5  * Originally derived from arch/x86/boot/compressed/acpi.c
6  */
7
8 #include "misc.h"
9
10 #include <asm/bootparam.h>
11
12 /**
13  * efi_get_type - Given a pointer to boot_params, determine the type of EFI environment.
14  *
15  * @bp:         pointer to boot_params
16  *
17  * Return: EFI_TYPE_{32,64} for valid EFI environments, EFI_TYPE_NONE otherwise.
18  */
19 enum efi_type efi_get_type(struct boot_params *bp)
20 {
21         struct efi_info *ei;
22         enum efi_type et;
23         const char *sig;
24
25         ei = &bp->efi_info;
26         sig = (char *)&ei->efi_loader_signature;
27
28         if (!strncmp(sig, EFI64_LOADER_SIGNATURE, 4)) {
29                 et = EFI_TYPE_64;
30         } else if (!strncmp(sig, EFI32_LOADER_SIGNATURE, 4)) {
31                 et = EFI_TYPE_32;
32         } else {
33                 debug_putstr("No EFI environment detected.\n");
34                 et = EFI_TYPE_NONE;
35         }
36
37 #ifndef CONFIG_X86_64
38         /*
39          * Existing callers like acpi.c treat this case as an indicator to
40          * fall-through to non-EFI, rather than an error, so maintain that
41          * functionality here as well.
42          */
43         if (ei->efi_systab_hi || ei->efi_memmap_hi) {
44                 debug_putstr("EFI system table is located above 4GB and cannot be accessed.\n");
45                 et = EFI_TYPE_NONE;
46         }
47 #endif
48
49         return et;
50 }
51
52 /**
53  * efi_get_system_table - Given a pointer to boot_params, retrieve the physical address
54  *                        of the EFI system table.
55  *
56  * @bp:         pointer to boot_params
57  *
58  * Return: EFI system table address on success. On error, return 0.
59  */
60 unsigned long efi_get_system_table(struct boot_params *bp)
61 {
62         unsigned long sys_tbl_pa;
63         struct efi_info *ei;
64         enum efi_type et;
65
66         /* Get systab from boot params. */
67         ei = &bp->efi_info;
68 #ifdef CONFIG_X86_64
69         sys_tbl_pa = ei->efi_systab | ((__u64)ei->efi_systab_hi << 32);
70 #else
71         sys_tbl_pa = ei->efi_systab;
72 #endif
73         if (!sys_tbl_pa) {
74                 debug_putstr("EFI system table not found.");
75                 return 0;
76         }
77
78         return sys_tbl_pa;
79 }
80
81 /*
82  * EFI config table address changes to virtual address after boot, which may
83  * not be accessible for the kexec'd kernel. To address this, kexec provides
84  * the initial physical address via a struct setup_data entry, which is
85  * checked for here, along with some sanity checks.
86  */
87 static struct efi_setup_data *get_kexec_setup_data(struct boot_params *bp,
88                                                    enum efi_type et)
89 {
90 #ifdef CONFIG_X86_64
91         struct efi_setup_data *esd = NULL;
92         struct setup_data *data;
93         u64 pa_data;
94
95         pa_data = bp->hdr.setup_data;
96         while (pa_data) {
97                 data = (struct setup_data *)pa_data;
98                 if (data->type == SETUP_EFI) {
99                         esd = (struct efi_setup_data *)(pa_data + sizeof(struct setup_data));
100                         break;
101                 }
102
103                 pa_data = data->next;
104         }
105
106         /*
107          * Original ACPI code falls back to attempting normal EFI boot in these
108          * cases, so maintain existing behavior by indicating non-kexec
109          * environment to the caller, but print them for debugging.
110          */
111         if (esd && !esd->tables) {
112                 debug_putstr("kexec EFI environment missing valid configuration table.\n");
113                 return NULL;
114         }
115
116         return esd;
117 #endif
118         return NULL;
119 }
120
121 /**
122  * efi_get_conf_table - Given a pointer to boot_params, locate and return the physical
123  *                      address of EFI configuration table.
124  *
125  * @bp:                 pointer to boot_params
126  * @cfg_tbl_pa:         location to store physical address of config table
127  * @cfg_tbl_len:        location to store number of config table entries
128  *
129  * Return: 0 on success. On error, return params are left unchanged.
130  */
131 int efi_get_conf_table(struct boot_params *bp, unsigned long *cfg_tbl_pa,
132                        unsigned int *cfg_tbl_len)
133 {
134         unsigned long sys_tbl_pa;
135         enum efi_type et;
136         int ret;
137
138         if (!cfg_tbl_pa || !cfg_tbl_len)
139                 return -EINVAL;
140
141         sys_tbl_pa = efi_get_system_table(bp);
142         if (!sys_tbl_pa)
143                 return -EINVAL;
144
145         /* Handle EFI bitness properly */
146         et = efi_get_type(bp);
147         if (et == EFI_TYPE_64) {
148                 efi_system_table_64_t *stbl = (efi_system_table_64_t *)sys_tbl_pa;
149                 struct efi_setup_data *esd;
150
151                 /* kexec provides an alternative EFI conf table, check for it. */
152                 esd = get_kexec_setup_data(bp, et);
153
154                 *cfg_tbl_pa = esd ? esd->tables : stbl->tables;
155                 *cfg_tbl_len = stbl->nr_tables;
156         } else if (et == EFI_TYPE_32) {
157                 efi_system_table_32_t *stbl = (efi_system_table_32_t *)sys_tbl_pa;
158
159                 *cfg_tbl_pa = stbl->tables;
160                 *cfg_tbl_len = stbl->nr_tables;
161         } else {
162                 return -EINVAL;
163         }
164
165         return 0;
166 }
167
168 /* Get vendor table address/guid from EFI config table at the given index */
169 static int get_vendor_table(void *cfg_tbl, unsigned int idx,
170                             unsigned long *vendor_tbl_pa,
171                             efi_guid_t *vendor_tbl_guid,
172                             enum efi_type et)
173 {
174         if (et == EFI_TYPE_64) {
175                 efi_config_table_64_t *tbl_entry = (efi_config_table_64_t *)cfg_tbl + idx;
176
177                 if (!IS_ENABLED(CONFIG_X86_64) && tbl_entry->table >> 32) {
178                         debug_putstr("Error: EFI config table entry located above 4GB.\n");
179                         return -EINVAL;
180                 }
181
182                 *vendor_tbl_pa = tbl_entry->table;
183                 *vendor_tbl_guid = tbl_entry->guid;
184
185         } else if (et == EFI_TYPE_32) {
186                 efi_config_table_32_t *tbl_entry = (efi_config_table_32_t *)cfg_tbl + idx;
187
188                 *vendor_tbl_pa = tbl_entry->table;
189                 *vendor_tbl_guid = tbl_entry->guid;
190         } else {
191                 return -EINVAL;
192         }
193
194         return 0;
195 }
196
197 /**
198  * efi_find_vendor_table - Given EFI config table, search it for the physical
199  *                         address of the vendor table associated with GUID.
200  *
201  * @bp:                pointer to boot_params
202  * @cfg_tbl_pa:        pointer to EFI configuration table
203  * @cfg_tbl_len:       number of entries in EFI configuration table
204  * @guid:              GUID of vendor table
205  *
206  * Return: vendor table address on success. On error, return 0.
207  */
208 unsigned long efi_find_vendor_table(struct boot_params *bp,
209                                     unsigned long cfg_tbl_pa,
210                                     unsigned int cfg_tbl_len,
211                                     efi_guid_t guid)
212 {
213         enum efi_type et;
214         unsigned int i;
215
216         et = efi_get_type(bp);
217         if (et == EFI_TYPE_NONE)
218                 return 0;
219
220         for (i = 0; i < cfg_tbl_len; i++) {
221                 unsigned long vendor_tbl_pa;
222                 efi_guid_t vendor_tbl_guid;
223                 int ret;
224
225                 ret = get_vendor_table((void *)cfg_tbl_pa, i,
226                                        &vendor_tbl_pa,
227                                        &vendor_tbl_guid, et);
228                 if (ret)
229                         return 0;
230
231                 if (!efi_guidcmp(guid, vendor_tbl_guid))
232                         return vendor_tbl_pa;
233         }
234
235         return 0;
236 }
This page took 0.03908 seconds and 4 git commands to generate.