]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
50149ea3 AG |
2 | /* |
3 | * EFI application runtime services | |
4 | * | |
5 | * Copyright (c) 2016 Alexander Graf | |
50149ea3 AG |
6 | */ |
7 | ||
50149ea3 | 8 | #include <command.h> |
1eb69ae4 | 9 | #include <cpu_func.h> |
50149ea3 | 10 | #include <dm.h> |
b34662d0 | 11 | #include <elf.h> |
50149ea3 | 12 | #include <efi_loader.h> |
bc3dd249 | 13 | #include <efi_variable.h> |
f7ae49fc | 14 | #include <log.h> |
336d4615 | 15 | #include <malloc.h> |
50149ea3 | 16 | #include <rtc.h> |
401d1c4f | 17 | #include <asm/global_data.h> |
3db71108 | 18 | #include <u-boot/crc.h> |
6b7f91cd | 19 | #include <asm/sections.h> |
50149ea3 AG |
20 | |
21 | /* For manual relocation support */ | |
22 | DECLARE_GLOBAL_DATA_PTR; | |
23 | ||
76be6872 HS |
24 | /* GUID of the runtime properties table */ |
25 | static const efi_guid_t efi_rt_properties_table_guid = | |
26 | EFI_RT_PROPERTIES_TABLE_GUID; | |
27 | ||
80a4800e AG |
28 | struct efi_runtime_mmio_list { |
29 | struct list_head link; | |
30 | void **ptr; | |
31 | u64 paddr; | |
32 | u64 len; | |
33 | }; | |
34 | ||
35 | /* This list contains all runtime available mmio regions */ | |
6fc4fc38 | 36 | static LIST_HEAD(efi_runtime_mmio); |
80a4800e | 37 | |
3c63db9c | 38 | static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void); |
50149ea3 | 39 | |
2d2b5b2d | 40 | /* |
250b3254 HS |
41 | * TODO([email protected]): These defines and structures should come from the ELF |
42 | * header for each architecture (or a generic header) rather than being repeated | |
43 | * here. | |
2d2b5b2d | 44 | */ |
bc028919 | 45 | #if defined(__aarch64__) |
b34662d0 | 46 | #define R_RELATIVE R_AARCH64_RELATIVE |
50149ea3 AG |
47 | #define R_MASK 0xffffffffULL |
48 | #define IS_RELA 1 | |
bc028919 | 49 | #elif defined(__arm__) |
b34662d0 | 50 | #define R_RELATIVE R_ARM_RELATIVE |
50149ea3 | 51 | #define R_MASK 0xffULL |
3ce78297 | 52 | #elif defined(__i386__) |
65e4c0b1 SG |
53 | #define R_RELATIVE R_386_RELATIVE |
54 | #define R_MASK 0xffULL | |
3ce78297 HS |
55 | #elif defined(__x86_64__) |
56 | #define R_RELATIVE R_X86_64_RELATIVE | |
57 | #define R_MASK 0xffffffffULL | |
58 | #define IS_RELA 1 | |
bc028919 | 59 | #elif defined(__riscv) |
6836adbe RC |
60 | #define R_RELATIVE R_RISCV_RELATIVE |
61 | #define R_MASK 0xffULL | |
62 | #define IS_RELA 1 | |
63 | ||
64 | struct dyn_sym { | |
65 | ulong foo1; | |
66 | ulong addr; | |
67 | u32 foo2; | |
68 | u32 foo3; | |
69 | }; | |
bc028919 | 70 | #if (__riscv_xlen == 32) |
6836adbe RC |
71 | #define R_ABSOLUTE R_RISCV_32 |
72 | #define SYM_INDEX 8 | |
bc028919 | 73 | #elif (__riscv_xlen == 64) |
6836adbe RC |
74 | #define R_ABSOLUTE R_RISCV_64 |
75 | #define SYM_INDEX 32 | |
bc028919 AG |
76 | #else |
77 | #error unknown riscv target | |
6836adbe | 78 | #endif |
50149ea3 AG |
79 | #else |
80 | #error Need to add relocation awareness | |
81 | #endif | |
82 | ||
83 | struct elf_rel { | |
84 | ulong *offset; | |
85 | ulong info; | |
86 | }; | |
87 | ||
88 | struct elf_rela { | |
89 | ulong *offset; | |
90 | ulong info; | |
91 | long addend; | |
92 | }; | |
93 | ||
7be56e86 HS |
94 | static __efi_runtime_data struct efi_mem_desc *efi_virtmap; |
95 | static __efi_runtime_data efi_uintn_t efi_descriptor_count; | |
96 | static __efi_runtime_data efi_uintn_t efi_descriptor_size; | |
97 | ||
50149ea3 | 98 | /* |
250b3254 | 99 | * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI |
50149ea3 AG |
100 | * payload are running concurrently at the same time. In this mode, we can |
101 | * handle a good number of runtime callbacks | |
102 | */ | |
103 | ||
76be6872 HS |
104 | /** |
105 | * efi_init_runtime_supported() - create runtime properties table | |
106 | * | |
107 | * Create a configuration table specifying which services are available at | |
108 | * runtime. | |
109 | * | |
110 | * Return: status code | |
111 | */ | |
e771b4b3 AT |
112 | efi_status_t efi_init_runtime_supported(void) |
113 | { | |
bc3dd249 | 114 | const efi_guid_t efi_guid_efi_rt_var_file = U_BOOT_EFI_RT_VAR_FILE_GUID; |
76be6872 HS |
115 | efi_status_t ret; |
116 | struct efi_rt_properties_table *rt_table; | |
117 | ||
118 | ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA, | |
119 | sizeof(struct efi_rt_properties_table), | |
120 | (void **)&rt_table); | |
121 | if (ret != EFI_SUCCESS) | |
122 | return ret; | |
123 | ||
124 | rt_table->version = EFI_RT_PROPERTIES_TABLE_VERSION; | |
125 | rt_table->length = sizeof(struct efi_rt_properties_table); | |
126 | rt_table->runtime_services_supported = | |
b02a7071 HS |
127 | EFI_RT_SUPPORTED_GET_VARIABLE | |
128 | EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME | | |
7be56e86 HS |
129 | EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP | |
130 | EFI_RT_SUPPORTED_CONVERT_POINTER; | |
e771b4b3 | 131 | |
9677192c IA |
132 | if (IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE)) |
133 | rt_table->runtime_services_supported |= | |
134 | EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO; | |
135 | ||
bc3dd249 | 136 | if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) { |
00da8d65 IA |
137 | u8 s = 0; |
138 | ||
bc3dd249 IA |
139 | ret = efi_set_variable_int(u"RTStorageVolatile", |
140 | &efi_guid_efi_rt_var_file, | |
141 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
142 | EFI_VARIABLE_RUNTIME_ACCESS | | |
143 | EFI_VARIABLE_READ_ONLY, | |
144 | sizeof(EFI_VAR_FILE_NAME), | |
145 | EFI_VAR_FILE_NAME, false); | |
146 | if (ret != EFI_SUCCESS) { | |
147 | log_err("Failed to set RTStorageVolatile\n"); | |
148 | return ret; | |
149 | } | |
00da8d65 IA |
150 | /* |
151 | * This variable needs to be visible so users can read it, | |
152 | * but the real contents are going to be filled during | |
153 | * GetVariable | |
154 | */ | |
155 | ret = efi_set_variable_int(u"VarToFile", | |
156 | &efi_guid_efi_rt_var_file, | |
157 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
158 | EFI_VARIABLE_RUNTIME_ACCESS | | |
159 | EFI_VARIABLE_READ_ONLY, | |
160 | sizeof(s), | |
161 | &s, false); | |
162 | if (ret != EFI_SUCCESS) { | |
163 | log_err("Failed to set VarToFile\n"); | |
164 | efi_set_variable_int(u"RTStorageVolatile", | |
165 | &efi_guid_efi_rt_var_file, | |
166 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
167 | EFI_VARIABLE_RUNTIME_ACCESS | | |
168 | EFI_VARIABLE_READ_ONLY, | |
169 | 0, NULL, false); | |
170 | ||
171 | return ret; | |
172 | } | |
bc3dd249 IA |
173 | rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_SET_VARIABLE; |
174 | } | |
c28d32f9 | 175 | |
e771b4b3 AT |
176 | /* |
177 | * This value must be synced with efi_runtime_detach_list | |
178 | * as well as efi_runtime_services. | |
179 | */ | |
953661a9 | 180 | #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET |
76be6872 | 181 | rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM; |
e771b4b3 | 182 | #endif |
7be56e86 | 183 | |
76be6872 HS |
184 | ret = efi_install_configuration_table(&efi_rt_properties_table_guid, |
185 | rt_table); | |
186 | return ret; | |
e771b4b3 AT |
187 | } |
188 | ||
b0dd8cb4 HS |
189 | /** |
190 | * efi_memcpy_runtime() - copy memory area | |
191 | * | |
192 | * At runtime memcpy() is not available. | |
193 | * | |
ebbad02c HS |
194 | * Overlapping memory areas can be copied safely if src >= dest. |
195 | * | |
b0dd8cb4 HS |
196 | * @dest: destination buffer |
197 | * @src: source buffer | |
198 | * @n: number of bytes to copy | |
199 | * Return: pointer to destination buffer | |
200 | */ | |
201 | void __efi_runtime efi_memcpy_runtime(void *dest, const void *src, size_t n) | |
202 | { | |
203 | u8 *d = dest; | |
204 | const u8 *s = src; | |
205 | ||
206 | for (; n; --n) | |
207 | *d++ = *s++; | |
208 | } | |
209 | ||
a39f39cd HS |
210 | /** |
211 | * efi_update_table_header_crc32() - Update crc32 in table header | |
212 | * | |
213 | * @table: EFI table | |
214 | */ | |
215 | void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table) | |
216 | { | |
217 | table->crc32 = 0; | |
218 | table->crc32 = crc32(0, (const unsigned char *)table, | |
219 | table->headersize); | |
220 | } | |
221 | ||
bcfb0e22 | 222 | /** |
250b3254 | 223 | * efi_reset_system_boottime() - reset system at boot time |
bcfb0e22 HS |
224 | * |
225 | * This function implements the ResetSystem() runtime service before | |
226 | * SetVirtualAddressMap() is called. | |
227 | * | |
228 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
229 | * details. | |
230 | * | |
231 | * @reset_type: type of reset to perform | |
232 | * @reset_status: status code for the reset | |
233 | * @data_size: size of reset_data | |
234 | * @reset_data: information about the reset | |
235 | */ | |
80a4800e AG |
236 | static void EFIAPI efi_reset_system_boottime( |
237 | enum efi_reset_type reset_type, | |
238 | efi_status_t reset_status, | |
239 | unsigned long data_size, void *reset_data) | |
50149ea3 | 240 | { |
b095f3c8 HS |
241 | struct efi_event *evt; |
242 | ||
50149ea3 AG |
243 | EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, |
244 | reset_data); | |
245 | ||
b095f3c8 HS |
246 | /* Notify reset */ |
247 | list_for_each_entry(evt, &efi_events, link) { | |
248 | if (evt->group && | |
249 | !guidcmp(evt->group, | |
250 | &efi_guid_event_group_reset_system)) { | |
7eaa900e | 251 | efi_signal_event(evt); |
b095f3c8 HS |
252 | break; |
253 | } | |
254 | } | |
50149ea3 AG |
255 | switch (reset_type) { |
256 | case EFI_RESET_COLD: | |
257 | case EFI_RESET_WARM: | |
482fc90c | 258 | case EFI_RESET_PLATFORM_SPECIFIC: |
50149ea3 AG |
259 | do_reset(NULL, 0, 0, NULL); |
260 | break; | |
261 | case EFI_RESET_SHUTDOWN: | |
e706ed7f HS |
262 | #ifdef CONFIG_CMD_POWEROFF |
263 | do_poweroff(NULL, 0, 0, NULL); | |
264 | #endif | |
50149ea3 AG |
265 | break; |
266 | } | |
267 | ||
80a4800e | 268 | while (1) { } |
50149ea3 AG |
269 | } |
270 | ||
49de2455 | 271 | /** |
250b3254 | 272 | * efi_get_time_boottime() - get current time at boot time |
bcfb0e22 HS |
273 | * |
274 | * This function implements the GetTime runtime service before | |
275 | * SetVirtualAddressMap() is called. | |
49de2455 | 276 | * |
49de2455 HS |
277 | * See the Unified Extensible Firmware Interface (UEFI) specification |
278 | * for details. | |
279 | * | |
280 | * @time: pointer to structure to receive current time | |
281 | * @capabilities: pointer to structure to receive RTC properties | |
bcfb0e22 | 282 | * Returns: status code |
49de2455 | 283 | */ |
80a4800e AG |
284 | static efi_status_t EFIAPI efi_get_time_boottime( |
285 | struct efi_time *time, | |
286 | struct efi_time_cap *capabilities) | |
50149ea3 | 287 | { |
5ec48e38 | 288 | #ifdef CONFIG_EFI_GET_TIME |
49de2455 | 289 | efi_status_t ret = EFI_SUCCESS; |
49de2455 | 290 | struct rtc_time tm; |
50149ea3 AG |
291 | struct udevice *dev; |
292 | ||
293 | EFI_ENTRY("%p %p", time, capabilities); | |
294 | ||
49de2455 HS |
295 | if (!time) { |
296 | ret = EFI_INVALID_PARAMETER; | |
297 | goto out; | |
298 | } | |
bb2b13d5 HS |
299 | if (uclass_get_device(UCLASS_RTC, 0, &dev) || |
300 | dm_rtc_get(dev, &tm)) { | |
301 | ret = EFI_UNSUPPORTED; | |
302 | goto out; | |
303 | } | |
304 | if (dm_rtc_get(dev, &tm)) { | |
49de2455 HS |
305 | ret = EFI_DEVICE_ERROR; |
306 | goto out; | |
307 | } | |
50149ea3 AG |
308 | |
309 | memset(time, 0, sizeof(*time)); | |
310 | time->year = tm.tm_year; | |
311 | time->month = tm.tm_mon; | |
312 | time->day = tm.tm_mday; | |
313 | time->hour = tm.tm_hour; | |
314 | time->minute = tm.tm_min; | |
49de2455 | 315 | time->second = tm.tm_sec; |
0eae552d | 316 | if (tm.tm_isdst > 0) |
38b9a79c HS |
317 | time->daylight = |
318 | EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT; | |
0eae552d HS |
319 | else if (!tm.tm_isdst) |
320 | time->daylight = EFI_TIME_ADJUST_DAYLIGHT; | |
321 | else | |
322 | time->daylight = 0; | |
49de2455 HS |
323 | time->timezone = EFI_UNSPECIFIED_TIMEZONE; |
324 | ||
325 | if (capabilities) { | |
326 | /* Set reasonable dummy values */ | |
327 | capabilities->resolution = 1; /* 1 Hz */ | |
328 | capabilities->accuracy = 100000000; /* 100 ppm */ | |
329 | capabilities->sets_to_zero = false; | |
330 | } | |
331 | out: | |
332 | return EFI_EXIT(ret); | |
50149ea3 | 333 | #else |
49de2455 | 334 | EFI_ENTRY("%p %p", time, capabilities); |
bb2b13d5 | 335 | return EFI_EXIT(EFI_UNSUPPORTED); |
50149ea3 AG |
336 | #endif |
337 | } | |
338 | ||
5ec48e38 | 339 | #ifdef CONFIG_EFI_SET_TIME |
e6bcc354 HS |
340 | |
341 | /** | |
342 | * efi_validate_time() - checks if timestamp is valid | |
343 | * | |
344 | * @time: timestamp to validate | |
345 | * Returns: 0 if timestamp is valid, 1 otherwise | |
346 | */ | |
347 | static int efi_validate_time(struct efi_time *time) | |
348 | { | |
349 | return (!time || | |
350 | time->year < 1900 || time->year > 9999 || | |
351 | !time->month || time->month > 12 || !time->day || | |
352 | time->day > rtc_month_days(time->month - 1, time->year) || | |
353 | time->hour > 23 || time->minute > 59 || time->second > 59 || | |
354 | time->nanosecond > 999999999 || | |
355 | time->daylight & | |
356 | ~(EFI_TIME_IN_DAYLIGHT | EFI_TIME_ADJUST_DAYLIGHT) || | |
357 | ((time->timezone < -1440 || time->timezone > 1440) && | |
358 | time->timezone != EFI_UNSPECIFIED_TIMEZONE)); | |
359 | } | |
360 | ||
361 | #endif | |
362 | ||
433bfe7b HS |
363 | /** |
364 | * efi_set_time_boottime() - set current time | |
365 | * | |
366 | * This function implements the SetTime() runtime service before | |
367 | * SetVirtualAddressMap() is called. | |
368 | * | |
369 | * See the Unified Extensible Firmware Interface (UEFI) specification | |
370 | * for details. | |
371 | * | |
372 | * @time: pointer to structure to with current time | |
373 | * Returns: status code | |
374 | */ | |
375 | static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time) | |
376 | { | |
5ec48e38 | 377 | #ifdef CONFIG_EFI_SET_TIME |
433bfe7b HS |
378 | efi_status_t ret = EFI_SUCCESS; |
379 | struct rtc_time tm; | |
380 | struct udevice *dev; | |
381 | ||
382 | EFI_ENTRY("%p", time); | |
383 | ||
e6bcc354 | 384 | if (efi_validate_time(time)) { |
433bfe7b HS |
385 | ret = EFI_INVALID_PARAMETER; |
386 | goto out; | |
387 | } | |
388 | ||
389 | if (uclass_get_device(UCLASS_RTC, 0, &dev)) { | |
390 | ret = EFI_UNSUPPORTED; | |
391 | goto out; | |
392 | } | |
80a4800e | 393 | |
433bfe7b HS |
394 | memset(&tm, 0, sizeof(tm)); |
395 | tm.tm_year = time->year; | |
396 | tm.tm_mon = time->month; | |
397 | tm.tm_mday = time->day; | |
398 | tm.tm_hour = time->hour; | |
399 | tm.tm_min = time->minute; | |
400 | tm.tm_sec = time->second; | |
0eae552d HS |
401 | switch (time->daylight) { |
402 | case EFI_TIME_ADJUST_DAYLIGHT: | |
403 | tm.tm_isdst = 0; | |
404 | break; | |
405 | case EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT: | |
406 | tm.tm_isdst = 1; | |
407 | break; | |
408 | default: | |
409 | tm.tm_isdst = -1; | |
410 | break; | |
411 | } | |
433bfe7b HS |
412 | /* Calculate day of week */ |
413 | rtc_calc_weekday(&tm); | |
414 | ||
415 | if (dm_rtc_set(dev, &tm)) | |
416 | ret = EFI_DEVICE_ERROR; | |
417 | out: | |
418 | return EFI_EXIT(ret); | |
419 | #else | |
420 | EFI_ENTRY("%p", time); | |
421 | return EFI_EXIT(EFI_UNSUPPORTED); | |
422 | #endif | |
423 | } | |
bcfb0e22 HS |
424 | /** |
425 | * efi_reset_system() - reset system | |
426 | * | |
427 | * This function implements the ResetSystem() runtime service after | |
f03a879d HS |
428 | * SetVirtualAddressMap() is called. As this placeholder cannot reset the |
429 | * system it simply return to the caller. | |
430 | * | |
bcfb0e22 HS |
431 | * Boards may override the helpers below to implement reset functionality. |
432 | * | |
433 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
434 | * details. | |
435 | * | |
436 | * @reset_type: type of reset to perform | |
437 | * @reset_status: status code for the reset | |
438 | * @data_size: size of reset_data | |
439 | * @reset_data: information about the reset | |
440 | */ | |
3c63db9c | 441 | void __weak __efi_runtime EFIAPI efi_reset_system( |
80a4800e AG |
442 | enum efi_reset_type reset_type, |
443 | efi_status_t reset_status, | |
444 | unsigned long data_size, void *reset_data) | |
445 | { | |
f03a879d | 446 | return; |
80a4800e AG |
447 | } |
448 | ||
bcfb0e22 HS |
449 | /** |
450 | * efi_reset_system_init() - initialize the reset driver | |
451 | * | |
452 | * Boards may override this function to initialize the reset driver. | |
453 | */ | |
22c793e6 | 454 | efi_status_t __weak efi_reset_system_init(void) |
80a4800e | 455 | { |
22c793e6 | 456 | return EFI_SUCCESS; |
80a4800e AG |
457 | } |
458 | ||
bcfb0e22 HS |
459 | /** |
460 | * efi_get_time() - get current time | |
461 | * | |
462 | * This function implements the GetTime runtime service after | |
463 | * SetVirtualAddressMap() is called. As the U-Boot driver are not available | |
464 | * anymore only an error code is returned. | |
465 | * | |
466 | * See the Unified Extensible Firmware Interface (UEFI) specification | |
467 | * for details. | |
468 | * | |
469 | * @time: pointer to structure to receive current time | |
470 | * @capabilities: pointer to structure to receive RTC properties | |
471 | * Returns: status code | |
472 | */ | |
3c63db9c | 473 | efi_status_t __weak __efi_runtime EFIAPI efi_get_time( |
80a4800e AG |
474 | struct efi_time *time, |
475 | struct efi_time_cap *capabilities) | |
476 | { | |
c5b63bec | 477 | return EFI_UNSUPPORTED; |
80a4800e AG |
478 | } |
479 | ||
433bfe7b HS |
480 | /** |
481 | * efi_set_time() - set current time | |
482 | * | |
483 | * This function implements the SetTime runtime service after | |
484 | * SetVirtualAddressMap() is called. As the U-Boot driver are not available | |
485 | * anymore only an error code is returned. | |
486 | * | |
487 | * See the Unified Extensible Firmware Interface (UEFI) specification | |
488 | * for details. | |
489 | * | |
490 | * @time: pointer to structure to with current time | |
491 | * Returns: status code | |
492 | */ | |
493 | efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time) | |
494 | { | |
495 | return EFI_UNSUPPORTED; | |
496 | } | |
497 | ||
2bc27ca8 AT |
498 | /** |
499 | * efi_update_capsule_unsupported() - process information from operating system | |
500 | * | |
501 | * This function implements the UpdateCapsule() runtime service. | |
502 | * | |
503 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
504 | * details. | |
505 | * | |
506 | * @capsule_header_array: pointer to array of virtual pointers | |
507 | * @capsule_count: number of pointers in capsule_header_array | |
508 | * @scatter_gather_list: pointer to array of physical pointers | |
509 | * Returns: status code | |
510 | */ | |
6c2377f9 | 511 | static efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported( |
2bc27ca8 AT |
512 | struct efi_capsule_header **capsule_header_array, |
513 | efi_uintn_t capsule_count, | |
514 | u64 scatter_gather_list) | |
515 | { | |
516 | return EFI_UNSUPPORTED; | |
517 | } | |
518 | ||
519 | /** | |
520 | * efi_query_capsule_caps_unsupported() - check if capsule is supported | |
521 | * | |
522 | * This function implements the QueryCapsuleCapabilities() runtime service. | |
523 | * | |
524 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
525 | * details. | |
526 | * | |
527 | * @capsule_header_array: pointer to array of virtual pointers | |
528 | * @capsule_count: number of pointers in capsule_header_array | |
529 | * @maximum_capsule_size: maximum capsule size | |
530 | * @reset_type: type of reset needed for capsule update | |
531 | * Returns: status code | |
532 | */ | |
6c2377f9 | 533 | static efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported( |
2bc27ca8 AT |
534 | struct efi_capsule_header **capsule_header_array, |
535 | efi_uintn_t capsule_count, | |
536 | u64 *maximum_capsule_size, | |
537 | u32 *reset_type) | |
538 | { | |
539 | return EFI_UNSUPPORTED; | |
540 | } | |
541 | ||
24a238f7 HS |
542 | /** |
543 | * efi_is_runtime_service_pointer() - check if pointer points to runtime table | |
544 | * | |
545 | * @p: pointer to check | |
546 | * Return: true if the pointer points to a service function pointer in the | |
547 | * runtime table | |
548 | */ | |
549 | static bool efi_is_runtime_service_pointer(void *p) | |
50149ea3 | 550 | { |
14b40487 HS |
551 | return (p >= (void *)&efi_runtime_services.get_time && |
552 | p <= (void *)&efi_runtime_services.query_variable_info) || | |
553 | p == (void *)&efi_events.prev || | |
554 | p == (void *)&efi_events.next; | |
50149ea3 AG |
555 | } |
556 | ||
b23ffcbe HS |
557 | /** |
558 | * efi_runtime_detach() - detach unimplemented runtime functions | |
559 | */ | |
7f95104d | 560 | void efi_runtime_detach(void) |
50149ea3 | 561 | { |
b23ffcbe HS |
562 | efi_runtime_services.reset_system = efi_reset_system; |
563 | efi_runtime_services.get_time = efi_get_time; | |
564 | efi_runtime_services.set_time = efi_set_time; | |
2bc27ca8 AT |
565 | if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) { |
566 | /* won't support at runtime */ | |
567 | efi_runtime_services.update_capsule = | |
568 | efi_update_capsule_unsupported; | |
569 | efi_runtime_services.query_capsule_caps = | |
570 | efi_query_capsule_caps_unsupported; | |
571 | } | |
50149ea3 | 572 | |
b23ffcbe HS |
573 | /* Update CRC32 */ |
574 | efi_update_table_header_crc32(&efi_runtime_services.hdr); | |
24a238f7 HS |
575 | } |
576 | ||
ee8ebaaa HS |
577 | /** |
578 | * efi_set_virtual_address_map_runtime() - change from physical to virtual | |
579 | * mapping | |
580 | * | |
581 | * This function implements the SetVirtualAddressMap() runtime service after | |
582 | * it is first called. | |
583 | * | |
584 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
585 | * details. | |
586 | * | |
587 | * @memory_map_size: size of the virtual map | |
588 | * @descriptor_size: size of an entry in the map | |
589 | * @descriptor_version: version of the map entries | |
590 | * @virtmap: virtual address mapping information | |
591 | * Return: status code EFI_UNSUPPORTED | |
592 | */ | |
96185603 | 593 | static __efi_runtime efi_status_t EFIAPI efi_set_virtual_address_map_runtime( |
24e6722b HS |
594 | efi_uintn_t memory_map_size, |
595 | efi_uintn_t descriptor_size, | |
ee8ebaaa HS |
596 | uint32_t descriptor_version, |
597 | struct efi_mem_desc *virtmap) | |
598 | { | |
599 | return EFI_UNSUPPORTED; | |
600 | } | |
601 | ||
602 | /** | |
603 | * efi_convert_pointer_runtime() - convert from physical to virtual pointer | |
604 | * | |
605 | * This function implements the ConvertPointer() runtime service after | |
606 | * the first call to SetVirtualAddressMap(). | |
607 | * | |
608 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
609 | * details. | |
610 | * | |
611 | * @debug_disposition: indicates if pointer may be converted to NULL | |
612 | * @address: pointer to be converted | |
613 | * Return: status code EFI_UNSUPPORTED | |
614 | */ | |
615 | static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime( | |
616 | efi_uintn_t debug_disposition, void **address) | |
617 | { | |
618 | return EFI_UNSUPPORTED; | |
619 | } | |
620 | ||
7be56e86 | 621 | /** |
7aeceffb | 622 | * efi_convert_pointer() - convert from physical to virtual pointer |
7be56e86 HS |
623 | * |
624 | * This function implements the ConvertPointer() runtime service until | |
625 | * the first call to SetVirtualAddressMap(). | |
626 | * | |
627 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
628 | * details. | |
629 | * | |
630 | * @debug_disposition: indicates if pointer may be converted to NULL | |
631 | * @address: pointer to be converted | |
7aeceffb | 632 | * Return: status code |
7be56e86 | 633 | */ |
a44d2a23 HS |
634 | __efi_runtime efi_status_t EFIAPI |
635 | efi_convert_pointer(efi_uintn_t debug_disposition, void **address) | |
7be56e86 | 636 | { |
a27c78fd | 637 | efi_physical_addr_t addr; |
7be56e86 HS |
638 | efi_uintn_t i; |
639 | efi_status_t ret = EFI_NOT_FOUND; | |
640 | ||
7be56e86 HS |
641 | if (!efi_virtmap) { |
642 | ret = EFI_UNSUPPORTED; | |
643 | goto out; | |
644 | } | |
645 | ||
646 | if (!address) { | |
647 | ret = EFI_INVALID_PARAMETER; | |
648 | goto out; | |
649 | } | |
724d2817 HS |
650 | if (!*address) { |
651 | if (debug_disposition & EFI_OPTIONAL_PTR) | |
652 | return EFI_SUCCESS; | |
653 | else | |
654 | return EFI_INVALID_PARAMETER; | |
655 | } | |
7be56e86 | 656 | |
a27c78fd | 657 | addr = (uintptr_t)*address; |
7be56e86 HS |
658 | for (i = 0; i < efi_descriptor_count; i++) { |
659 | struct efi_mem_desc *map = (void *)efi_virtmap + | |
660 | (efi_descriptor_size * i); | |
661 | ||
662 | if (addr >= map->physical_start && | |
663 | (addr < map->physical_start | |
664 | + (map->num_pages << EFI_PAGE_SHIFT))) { | |
665 | *address = (void *)(uintptr_t) | |
666 | (addr + map->virtual_start - | |
667 | map->physical_start); | |
668 | ||
669 | ret = EFI_SUCCESS; | |
670 | break; | |
671 | } | |
672 | } | |
673 | ||
674 | out: | |
a44d2a23 | 675 | return ret; |
7be56e86 HS |
676 | } |
677 | ||
24a238f7 HS |
678 | static __efi_runtime void efi_relocate_runtime_table(ulong offset) |
679 | { | |
680 | ulong patchoff; | |
681 | void **pos; | |
682 | ||
683 | /* Relocate the runtime services pointers */ | |
684 | patchoff = offset - gd->relocaddr; | |
685 | for (pos = (void **)&efi_runtime_services.get_time; | |
686 | pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) { | |
ee8ebaaa | 687 | if (*pos) |
24a238f7 | 688 | *pos += patchoff; |
50149ea3 | 689 | } |
a39f39cd | 690 | |
ee8ebaaa HS |
691 | /* |
692 | * The entry for SetVirtualAddress() must point to a physical address. | |
693 | * After the first execution the service must return EFI_UNSUPPORTED. | |
694 | */ | |
695 | efi_runtime_services.set_virtual_address_map = | |
696 | &efi_set_virtual_address_map_runtime; | |
697 | ||
698 | /* | |
699 | * The entry for ConvertPointer() must point to a physical address. | |
700 | * The service is not usable after SetVirtualAddress(). | |
701 | */ | |
702 | efi_runtime_services.convert_pointer = &efi_convert_pointer_runtime; | |
703 | ||
7be56e86 HS |
704 | /* |
705 | * TODO: Update UEFI variable RuntimeServicesSupported removing flags | |
706 | * EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP and | |
707 | * EFI_RT_SUPPORTED_CONVERT_POINTER as required by the UEFI spec 2.8. | |
708 | */ | |
709 | ||
250b3254 | 710 | /* Update CRC32 */ |
a39f39cd | 711 | efi_update_table_header_crc32(&efi_runtime_services.hdr); |
50149ea3 AG |
712 | } |
713 | ||
714 | /* Relocate EFI runtime to uboot_reloc_base = offset */ | |
715 | void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) | |
716 | { | |
717 | #ifdef IS_RELA | |
3f8d1304 | 718 | struct elf_rela *rel = (void *)__efi_runtime_rel_start; |
50149ea3 | 719 | #else |
3f8d1304 | 720 | struct elf_rel *rel = (void *)__efi_runtime_rel_start; |
98463903 | 721 | static ulong lastoff = CONFIG_TEXT_BASE; |
50149ea3 AG |
722 | #endif |
723 | ||
edcef3ba | 724 | debug("%s: Relocating to offset=%lx\n", __func__, offset); |
3f8d1304 | 725 | for (; (uintptr_t)rel < (uintptr_t)__efi_runtime_rel_stop; rel++) { |
98463903 | 726 | ulong base = CONFIG_TEXT_BASE; |
50149ea3 AG |
727 | ulong *p; |
728 | ulong newaddr; | |
729 | ||
730 | p = (void*)((ulong)rel->offset - base) + gd->relocaddr; | |
731 | ||
9f8932d0 HS |
732 | /* |
733 | * The runtime services table is updated in | |
734 | * efi_relocate_runtime_table() | |
735 | */ | |
24a238f7 HS |
736 | if (map && efi_is_runtime_service_pointer(p)) |
737 | continue; | |
738 | ||
3ce78297 HS |
739 | debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__, |
740 | rel->info, *p, rel->offset); | |
50149ea3 | 741 | |
6836adbe RC |
742 | switch (rel->info & R_MASK) { |
743 | case R_RELATIVE: | |
50149ea3 | 744 | #ifdef IS_RELA |
98463903 | 745 | newaddr = rel->addend + offset - CONFIG_TEXT_BASE; |
50149ea3 AG |
746 | #else |
747 | newaddr = *p - lastoff + offset; | |
748 | #endif | |
6836adbe RC |
749 | break; |
750 | #ifdef R_ABSOLUTE | |
751 | case R_ABSOLUTE: { | |
752 | ulong symidx = rel->info >> SYM_INDEX; | |
753 | extern struct dyn_sym __dyn_sym_start[]; | |
754 | newaddr = __dyn_sym_start[symidx].addr + offset; | |
afdc4fcc | 755 | #ifdef IS_RELA |
98463903 | 756 | newaddr -= CONFIG_TEXT_BASE; |
afdc4fcc | 757 | #endif |
6836adbe RC |
758 | break; |
759 | } | |
760 | #endif | |
761 | default: | |
24a238f7 HS |
762 | printf("%s: Unknown relocation type %llx\n", |
763 | __func__, rel->info & R_MASK); | |
6836adbe RC |
764 | continue; |
765 | } | |
50149ea3 AG |
766 | |
767 | /* Check if the relocation is inside bounds */ | |
768 | if (map && ((newaddr < map->virtual_start) || | |
591cf2e1 HS |
769 | newaddr > (map->virtual_start + |
770 | (map->num_pages << EFI_PAGE_SHIFT)))) { | |
24a238f7 HS |
771 | printf("%s: Relocation at %p is out of range (%lx)\n", |
772 | __func__, p, newaddr); | |
50149ea3 AG |
773 | continue; |
774 | } | |
775 | ||
edcef3ba | 776 | debug("%s: Setting %p to %lx\n", __func__, p, newaddr); |
50149ea3 | 777 | *p = newaddr; |
36c37a84 AG |
778 | flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1), |
779 | ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE)); | |
50149ea3 AG |
780 | } |
781 | ||
782 | #ifndef IS_RELA | |
783 | lastoff = offset; | |
784 | #endif | |
785 | ||
5225060b HS |
786 | /* |
787 | * If on x86 a write affects a prefetched instruction, | |
788 | * the prefetch queue is invalidated. | |
789 | */ | |
790 | if (!CONFIG_IS_ENABLED(X86)) | |
791 | invalidate_icache_all(); | |
50149ea3 AG |
792 | } |
793 | ||
bcfb0e22 HS |
794 | /** |
795 | * efi_set_virtual_address_map() - change from physical to virtual mapping | |
796 | * | |
797 | * This function implements the SetVirtualAddressMap() runtime service. | |
798 | * | |
799 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
800 | * details. | |
801 | * | |
802 | * @memory_map_size: size of the virtual map | |
803 | * @descriptor_size: size of an entry in the map | |
804 | * @descriptor_version: version of the map entries | |
805 | * @virtmap: virtual address mapping information | |
806 | * Return: status code | |
807 | */ | |
50149ea3 | 808 | static efi_status_t EFIAPI efi_set_virtual_address_map( |
24e6722b HS |
809 | efi_uintn_t memory_map_size, |
810 | efi_uintn_t descriptor_size, | |
50149ea3 AG |
811 | uint32_t descriptor_version, |
812 | struct efi_mem_desc *virtmap) | |
813 | { | |
24e6722b HS |
814 | efi_uintn_t n = memory_map_size / descriptor_size; |
815 | efi_uintn_t i; | |
53e1d8fa | 816 | efi_status_t ret = EFI_INVALID_PARAMETER; |
5c38e05e | 817 | int rt_code_sections = 0; |
14b40487 | 818 | struct efi_event *event; |
50149ea3 | 819 | |
24e6722b | 820 | EFI_ENTRY("%zx %zx %x %p", memory_map_size, descriptor_size, |
50149ea3 AG |
821 | descriptor_version, virtmap); |
822 | ||
53e1d8fa HS |
823 | if (descriptor_version != EFI_MEMORY_DESCRIPTOR_VERSION || |
824 | descriptor_size < sizeof(struct efi_mem_desc)) | |
825 | goto out; | |
826 | ||
7be56e86 HS |
827 | efi_virtmap = virtmap; |
828 | efi_descriptor_size = descriptor_size; | |
829 | efi_descriptor_count = n; | |
830 | ||
5c38e05e AG |
831 | /* |
832 | * TODO: | |
833 | * Further down we are cheating. While really we should implement | |
834 | * SetVirtualAddressMap() events and ConvertPointer() to allow | |
835 | * dynamically loaded drivers to expose runtime services, we don't | |
836 | * today. | |
837 | * | |
838 | * So let's ensure we see exactly one single runtime section, as | |
839 | * that is the built-in one. If we see more (or less), someone must | |
840 | * have tried adding or removing to that which we don't support yet. | |
841 | * In that case, let's better fail rather than expose broken runtime | |
842 | * services. | |
843 | */ | |
844 | for (i = 0; i < n; i++) { | |
845 | struct efi_mem_desc *map = (void*)virtmap + | |
846 | (descriptor_size * i); | |
847 | ||
848 | if (map->type == EFI_RUNTIME_SERVICES_CODE) | |
849 | rt_code_sections++; | |
850 | } | |
851 | ||
852 | if (rt_code_sections != 1) { | |
853 | /* | |
854 | * We expose exactly one single runtime code section, so | |
855 | * something is definitely going wrong. | |
856 | */ | |
53e1d8fa | 857 | goto out; |
5c38e05e AG |
858 | } |
859 | ||
14b40487 HS |
860 | /* Notify EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */ |
861 | list_for_each_entry(event, &efi_events, link) { | |
862 | if (event->notify_function) | |
863 | EFI_CALL_VOID(event->notify_function( | |
864 | event, event->notify_context)); | |
865 | } | |
866 | ||
80a4800e AG |
867 | /* Rebind mmio pointers */ |
868 | for (i = 0; i < n; i++) { | |
869 | struct efi_mem_desc *map = (void*)virtmap + | |
870 | (descriptor_size * i); | |
871 | struct list_head *lhandle; | |
872 | efi_physical_addr_t map_start = map->physical_start; | |
873 | efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT; | |
874 | efi_physical_addr_t map_end = map_start + map_len; | |
07240da2 | 875 | u64 off = map->virtual_start - map_start; |
80a4800e AG |
876 | |
877 | /* Adjust all mmio pointers in this region */ | |
878 | list_for_each(lhandle, &efi_runtime_mmio) { | |
879 | struct efi_runtime_mmio_list *lmmio; | |
880 | ||
881 | lmmio = list_entry(lhandle, | |
882 | struct efi_runtime_mmio_list, | |
883 | link); | |
884 | if ((map_start <= lmmio->paddr) && | |
885 | (map_end >= lmmio->paddr)) { | |
80a4800e AG |
886 | uintptr_t new_addr = lmmio->paddr + off; |
887 | *lmmio->ptr = (void *)new_addr; | |
888 | } | |
889 | } | |
07240da2 HS |
890 | if ((map_start <= (uintptr_t)systab.tables) && |
891 | (map_end >= (uintptr_t)systab.tables)) { | |
892 | char *ptr = (char *)systab.tables; | |
893 | ||
894 | ptr += off; | |
895 | systab.tables = (struct efi_configuration_table *)ptr; | |
896 | } | |
80a4800e AG |
897 | } |
898 | ||
24a238f7 | 899 | /* Relocate the runtime. See TODO above */ |
50149ea3 AG |
900 | for (i = 0; i < n; i++) { |
901 | struct efi_mem_desc *map; | |
902 | ||
903 | map = (void*)virtmap + (descriptor_size * i); | |
904 | if (map->type == EFI_RUNTIME_SERVICES_CODE) { | |
80a4800e | 905 | ulong new_offset = map->virtual_start - |
5c38e05e | 906 | map->physical_start + gd->relocaddr; |
50149ea3 | 907 | |
24a238f7 | 908 | efi_relocate_runtime_table(new_offset); |
50149ea3 | 909 | efi_runtime_relocate(new_offset, map); |
53e1d8fa HS |
910 | ret = EFI_SUCCESS; |
911 | goto out; | |
50149ea3 AG |
912 | } |
913 | } | |
914 | ||
53e1d8fa HS |
915 | out: |
916 | return EFI_EXIT(ret); | |
50149ea3 AG |
917 | } |
918 | ||
bcfb0e22 HS |
919 | /** |
920 | * efi_add_runtime_mmio() - add memory-mapped IO region | |
921 | * | |
922 | * This function adds a memory-mapped IO region to the memory map to make it | |
923 | * available at runtime. | |
924 | * | |
fb34f298 HS |
925 | * @mmio_ptr: pointer to a pointer to the start of the memory-mapped |
926 | * IO region | |
250b3254 | 927 | * @len: size of the memory-mapped IO region |
bcfb0e22 HS |
928 | * Returns: status code |
929 | */ | |
22c793e6 | 930 | efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) |
80a4800e AG |
931 | { |
932 | struct efi_runtime_mmio_list *newmmio; | |
813468cd | 933 | uint64_t addr = *(uintptr_t *)mmio_ptr; |
b225c92f | 934 | efi_status_t ret; |
813468cd | 935 | |
714497e3 | 936 | ret = efi_add_memory_map(addr, len, EFI_MMAP_IO); |
b225c92f | 937 | if (ret != EFI_SUCCESS) |
813468cd | 938 | return EFI_OUT_OF_RESOURCES; |
80a4800e AG |
939 | |
940 | newmmio = calloc(1, sizeof(*newmmio)); | |
22c793e6 HS |
941 | if (!newmmio) |
942 | return EFI_OUT_OF_RESOURCES; | |
80a4800e AG |
943 | newmmio->ptr = mmio_ptr; |
944 | newmmio->paddr = *(uintptr_t *)mmio_ptr; | |
945 | newmmio->len = len; | |
946 | list_add_tail(&newmmio->link, &efi_runtime_mmio); | |
22c793e6 | 947 | |
813468cd | 948 | return EFI_SUCCESS; |
80a4800e AG |
949 | } |
950 | ||
50149ea3 AG |
951 | /* |
952 | * In the second stage, U-Boot has disappeared. To isolate our runtime code | |
953 | * that at this point still exists from the rest, we put it into a special | |
954 | * section. | |
955 | * | |
956 | * !!WARNING!! | |
957 | * | |
958 | * This means that we can not rely on any code outside of this file in any | |
959 | * function or variable below this line. | |
960 | * | |
961 | * Please keep everything fully self-contained and annotated with | |
3c63db9c | 962 | * __efi_runtime and __efi_runtime_data markers. |
50149ea3 AG |
963 | */ |
964 | ||
965 | /* | |
966 | * Relocate the EFI runtime stub to a different place. We need to call this | |
967 | * the first time we expose the runtime interface to a user and on set virtual | |
968 | * address map calls. | |
969 | */ | |
970 | ||
bcfb0e22 HS |
971 | /** |
972 | * efi_unimplemented() - replacement function, returns EFI_UNSUPPORTED | |
973 | * | |
974 | * This function is used after SetVirtualAddressMap() is called as replacement | |
975 | * for services that are not available anymore due to constraints of the U-Boot | |
976 | * implementation. | |
977 | * | |
978 | * Return: EFI_UNSUPPORTED | |
979 | */ | |
3c63db9c | 980 | static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) |
50149ea3 AG |
981 | { |
982 | return EFI_UNSUPPORTED; | |
983 | } | |
984 | ||
3c63db9c | 985 | struct efi_runtime_services __efi_runtime_data efi_runtime_services = { |
50149ea3 AG |
986 | .hdr = { |
987 | .signature = EFI_RUNTIME_SERVICES_SIGNATURE, | |
112f2430 | 988 | .revision = EFI_SPECIFICATION_VERSION, |
71c846ab | 989 | .headersize = sizeof(struct efi_runtime_services), |
50149ea3 | 990 | }, |
80a4800e | 991 | .get_time = &efi_get_time_boottime, |
433bfe7b | 992 | .set_time = &efi_set_time_boottime, |
50149ea3 AG |
993 | .get_wakeup_time = (void *)&efi_unimplemented, |
994 | .set_wakeup_time = (void *)&efi_unimplemented, | |
995 | .set_virtual_address_map = &efi_set_virtual_address_map, | |
7be56e86 | 996 | .convert_pointer = efi_convert_pointer, |
ad644e7c | 997 | .get_variable = efi_get_variable, |
45c66f9c | 998 | .get_next_variable_name = efi_get_next_variable_name, |
ad644e7c | 999 | .set_variable = efi_set_variable, |
b94461c2 | 1000 | .get_next_high_mono_count = (void *)&efi_unimplemented, |
80a4800e | 1001 | .reset_system = &efi_reset_system_boottime, |
2bc27ca8 | 1002 | #ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE |
0c230743 HS |
1003 | .update_capsule = efi_update_capsule, |
1004 | .query_capsule_caps = efi_query_capsule_caps, | |
2bc27ca8 AT |
1005 | #else |
1006 | .update_capsule = efi_update_capsule_unsupported, | |
1007 | .query_capsule_caps = efi_query_capsule_caps_unsupported, | |
1008 | #endif | |
0c230743 | 1009 | .query_variable_info = efi_query_variable_info, |
50149ea3 | 1010 | }; |