1 /* 2 * EFI application runtime services 3 * 4 * Copyright (c) 2016 Alexander Graf 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <common.h> 10 #include <command.h> 11 #include <dm.h> 12 #include <efi_loader.h> 13 #include <rtc.h> 14 #include <asm/global_data.h> 15 16 /* For manual relocation support */ 17 DECLARE_GLOBAL_DATA_PTR; 18 19 struct efi_runtime_mmio_list { 20 struct list_head link; 21 void **ptr; 22 u64 paddr; 23 u64 len; 24 }; 25 26 /* This list contains all runtime available mmio regions */ 27 LIST_HEAD(efi_runtime_mmio); 28 29 static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void); 30 static efi_status_t __efi_runtime EFIAPI efi_device_error(void); 31 static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void); 32 33 #ifdef CONFIG_SYS_CACHELINE_SIZE 34 #define EFI_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE 35 #else 36 /* Just use the greatest cache flush alignment requirement I'm aware of */ 37 #define EFI_CACHELINE_SIZE 128 38 #endif 39 40 #if defined(CONFIG_ARM64) 41 #define R_RELATIVE 1027 42 #define R_MASK 0xffffffffULL 43 #define IS_RELA 1 44 #elif defined(CONFIG_ARM) 45 #define R_RELATIVE 23 46 #define R_MASK 0xffULL 47 #elif defined(CONFIG_X86) 48 #include <asm/elf.h> 49 #define R_RELATIVE R_386_RELATIVE 50 #define R_MASK 0xffULL 51 #else 52 #error Need to add relocation awareness 53 #endif 54 55 struct elf_rel { 56 ulong *offset; 57 ulong info; 58 }; 59 60 struct elf_rela { 61 ulong *offset; 62 ulong info; 63 long addend; 64 }; 65 66 /* 67 * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI 68 * payload are running concurrently at the same time. In this mode, we can 69 * handle a good number of runtime callbacks 70 */ 71 72 static void EFIAPI efi_reset_system_boottime( 73 enum efi_reset_type reset_type, 74 efi_status_t reset_status, 75 unsigned long data_size, void *reset_data) 76 { 77 struct efi_event *evt; 78 79 EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, 80 reset_data); 81 82 /* Notify reset */ 83 list_for_each_entry(evt, &efi_events, link) { 84 if (evt->group && 85 !guidcmp(evt->group, 86 &efi_guid_event_group_reset_system)) { 87 efi_signal_event(evt, false); 88 break; 89 } 90 } 91 switch (reset_type) { 92 case EFI_RESET_COLD: 93 case EFI_RESET_WARM: 94 case EFI_RESET_PLATFORM_SPECIFIC: 95 do_reset(NULL, 0, 0, NULL); 96 break; 97 case EFI_RESET_SHUTDOWN: 98 /* We don't have anything to map this to */ 99 break; 100 } 101 102 while (1) { } 103 } 104 105 static efi_status_t EFIAPI efi_get_time_boottime( 106 struct efi_time *time, 107 struct efi_time_cap *capabilities) 108 { 109 #if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC) 110 struct rtc_time tm; 111 int r; 112 struct udevice *dev; 113 114 EFI_ENTRY("%p %p", time, capabilities); 115 116 r = uclass_get_device(UCLASS_RTC, 0, &dev); 117 if (r) 118 return EFI_EXIT(EFI_DEVICE_ERROR); 119 120 r = dm_rtc_get(dev, &tm); 121 if (r) 122 return EFI_EXIT(EFI_DEVICE_ERROR); 123 124 memset(time, 0, sizeof(*time)); 125 time->year = tm.tm_year; 126 time->month = tm.tm_mon; 127 time->day = tm.tm_mday; 128 time->hour = tm.tm_hour; 129 time->minute = tm.tm_min; 130 time->daylight = tm.tm_isdst; 131 132 return EFI_EXIT(EFI_SUCCESS); 133 #else 134 return EFI_DEVICE_ERROR; 135 #endif 136 } 137 138 /* Boards may override the helpers below to implement RTS functionality */ 139 140 void __weak __efi_runtime EFIAPI efi_reset_system( 141 enum efi_reset_type reset_type, 142 efi_status_t reset_status, 143 unsigned long data_size, void *reset_data) 144 { 145 /* Nothing we can do */ 146 while (1) { } 147 } 148 149 efi_status_t __weak efi_reset_system_init(void) 150 { 151 return EFI_SUCCESS; 152 } 153 154 efi_status_t __weak __efi_runtime EFIAPI efi_get_time( 155 struct efi_time *time, 156 struct efi_time_cap *capabilities) 157 { 158 /* Nothing we can do */ 159 return EFI_DEVICE_ERROR; 160 } 161 162 efi_status_t __weak efi_get_time_init(void) 163 { 164 return EFI_SUCCESS; 165 } 166 167 struct efi_runtime_detach_list_struct { 168 void *ptr; 169 void *patchto; 170 }; 171 172 static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { 173 { 174 /* do_reset is gone */ 175 .ptr = &efi_runtime_services.reset_system, 176 .patchto = efi_reset_system, 177 }, { 178 /* invalidate_*cache_all are gone */ 179 .ptr = &efi_runtime_services.set_virtual_address_map, 180 .patchto = &efi_invalid_parameter, 181 }, { 182 /* RTC accessors are gone */ 183 .ptr = &efi_runtime_services.get_time, 184 .patchto = &efi_get_time, 185 }, { 186 /* Clean up system table */ 187 .ptr = &systab.con_in, 188 .patchto = NULL, 189 }, { 190 /* Clean up system table */ 191 .ptr = &systab.con_out, 192 .patchto = NULL, 193 }, { 194 /* Clean up system table */ 195 .ptr = &systab.std_err, 196 .patchto = NULL, 197 }, { 198 /* Clean up system table */ 199 .ptr = &systab.boottime, 200 .patchto = NULL, 201 }, { 202 .ptr = &efi_runtime_services.get_variable, 203 .patchto = &efi_device_error, 204 }, { 205 .ptr = &efi_runtime_services.get_next_variable, 206 .patchto = &efi_device_error, 207 }, { 208 .ptr = &efi_runtime_services.set_variable, 209 .patchto = &efi_device_error, 210 } 211 }; 212 213 static bool efi_runtime_tobedetached(void *p) 214 { 215 int i; 216 217 for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) 218 if (efi_runtime_detach_list[i].ptr == p) 219 return true; 220 221 return false; 222 } 223 224 static void efi_runtime_detach(ulong offset) 225 { 226 int i; 227 ulong patchoff = offset - (ulong)gd->relocaddr; 228 229 for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) { 230 ulong patchto = (ulong)efi_runtime_detach_list[i].patchto; 231 ulong *p = efi_runtime_detach_list[i].ptr; 232 ulong newaddr = patchto ? (patchto + patchoff) : 0; 233 234 debug("%s: Setting %p to %lx\n", __func__, p, newaddr); 235 *p = newaddr; 236 } 237 } 238 239 /* Relocate EFI runtime to uboot_reloc_base = offset */ 240 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) 241 { 242 #ifdef IS_RELA 243 struct elf_rela *rel = (void*)&__efi_runtime_rel_start; 244 #else 245 struct elf_rel *rel = (void*)&__efi_runtime_rel_start; 246 static ulong lastoff = CONFIG_SYS_TEXT_BASE; 247 #endif 248 249 debug("%s: Relocating to offset=%lx\n", __func__, offset); 250 for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) { 251 ulong base = CONFIG_SYS_TEXT_BASE; 252 ulong *p; 253 ulong newaddr; 254 255 p = (void*)((ulong)rel->offset - base) + gd->relocaddr; 256 257 if ((rel->info & R_MASK) != R_RELATIVE) { 258 continue; 259 } 260 261 #ifdef IS_RELA 262 newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE; 263 #else 264 newaddr = *p - lastoff + offset; 265 #endif 266 267 /* Check if the relocation is inside bounds */ 268 if (map && ((newaddr < map->virtual_start) || 269 newaddr > (map->virtual_start + 270 (map->num_pages << EFI_PAGE_SHIFT)))) { 271 if (!efi_runtime_tobedetached(p)) 272 printf("U-Boot EFI: Relocation at %p is out of " 273 "range (%lx)\n", p, newaddr); 274 continue; 275 } 276 277 debug("%s: Setting %p to %lx\n", __func__, p, newaddr); 278 *p = newaddr; 279 flush_dcache_range((ulong)p & ~(EFI_CACHELINE_SIZE - 1), 280 ALIGN((ulong)&p[1], EFI_CACHELINE_SIZE)); 281 } 282 283 #ifndef IS_RELA 284 lastoff = offset; 285 #endif 286 287 invalidate_icache_all(); 288 } 289 290 static efi_status_t EFIAPI efi_set_virtual_address_map( 291 unsigned long memory_map_size, 292 unsigned long descriptor_size, 293 uint32_t descriptor_version, 294 struct efi_mem_desc *virtmap) 295 { 296 ulong runtime_start = (ulong)&__efi_runtime_start & 297 ~(ulong)EFI_PAGE_MASK; 298 int n = memory_map_size / descriptor_size; 299 int i; 300 301 EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size, 302 descriptor_version, virtmap); 303 304 /* Rebind mmio pointers */ 305 for (i = 0; i < n; i++) { 306 struct efi_mem_desc *map = (void*)virtmap + 307 (descriptor_size * i); 308 struct list_head *lhandle; 309 efi_physical_addr_t map_start = map->physical_start; 310 efi_physical_addr_t map_len = map->num_pages << EFI_PAGE_SHIFT; 311 efi_physical_addr_t map_end = map_start + map_len; 312 313 /* Adjust all mmio pointers in this region */ 314 list_for_each(lhandle, &efi_runtime_mmio) { 315 struct efi_runtime_mmio_list *lmmio; 316 317 lmmio = list_entry(lhandle, 318 struct efi_runtime_mmio_list, 319 link); 320 if ((map_start <= lmmio->paddr) && 321 (map_end >= lmmio->paddr)) { 322 u64 off = map->virtual_start - map_start; 323 uintptr_t new_addr = lmmio->paddr + off; 324 *lmmio->ptr = (void *)new_addr; 325 } 326 } 327 } 328 329 /* Move the actual runtime code over */ 330 for (i = 0; i < n; i++) { 331 struct efi_mem_desc *map; 332 333 map = (void*)virtmap + (descriptor_size * i); 334 if (map->type == EFI_RUNTIME_SERVICES_CODE) { 335 ulong new_offset = map->virtual_start - 336 (runtime_start - gd->relocaddr); 337 338 efi_runtime_relocate(new_offset, map); 339 /* Once we're virtual, we can no longer handle 340 complex callbacks */ 341 efi_runtime_detach(new_offset); 342 return EFI_EXIT(EFI_SUCCESS); 343 } 344 } 345 346 return EFI_EXIT(EFI_INVALID_PARAMETER); 347 } 348 349 efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) 350 { 351 struct efi_runtime_mmio_list *newmmio; 352 u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; 353 uint64_t addr = *(uintptr_t *)mmio_ptr; 354 uint64_t retaddr; 355 356 retaddr = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false); 357 if (retaddr != addr) 358 return EFI_OUT_OF_RESOURCES; 359 360 newmmio = calloc(1, sizeof(*newmmio)); 361 if (!newmmio) 362 return EFI_OUT_OF_RESOURCES; 363 newmmio->ptr = mmio_ptr; 364 newmmio->paddr = *(uintptr_t *)mmio_ptr; 365 newmmio->len = len; 366 list_add_tail(&newmmio->link, &efi_runtime_mmio); 367 368 return EFI_SUCCESS; 369 } 370 371 /* 372 * In the second stage, U-Boot has disappeared. To isolate our runtime code 373 * that at this point still exists from the rest, we put it into a special 374 * section. 375 * 376 * !!WARNING!! 377 * 378 * This means that we can not rely on any code outside of this file in any 379 * function or variable below this line. 380 * 381 * Please keep everything fully self-contained and annotated with 382 * __efi_runtime and __efi_runtime_data markers. 383 */ 384 385 /* 386 * Relocate the EFI runtime stub to a different place. We need to call this 387 * the first time we expose the runtime interface to a user and on set virtual 388 * address map calls. 389 */ 390 391 static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) 392 { 393 return EFI_UNSUPPORTED; 394 } 395 396 static efi_status_t __efi_runtime EFIAPI efi_device_error(void) 397 { 398 return EFI_DEVICE_ERROR; 399 } 400 401 static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void) 402 { 403 return EFI_INVALID_PARAMETER; 404 } 405 406 efi_status_t __efi_runtime EFIAPI efi_update_capsule( 407 struct efi_capsule_header **capsule_header_array, 408 efi_uintn_t capsule_count, 409 u64 scatter_gather_list) 410 { 411 return EFI_UNSUPPORTED; 412 } 413 414 efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( 415 struct efi_capsule_header **capsule_header_array, 416 efi_uintn_t capsule_count, 417 u64 maximum_capsule_size, 418 u32 reset_type) 419 { 420 return EFI_UNSUPPORTED; 421 } 422 423 efi_status_t __efi_runtime EFIAPI efi_query_variable_info( 424 u32 attributes, 425 u64 maximum_variable_storage_size, 426 u64 remaining_variable_storage_size, 427 u64 maximum_variable_size) 428 { 429 return EFI_UNSUPPORTED; 430 } 431 432 struct efi_runtime_services __efi_runtime_data efi_runtime_services = { 433 .hdr = { 434 .signature = EFI_RUNTIME_SERVICES_SIGNATURE, 435 .revision = EFI_RUNTIME_SERVICES_REVISION, 436 .headersize = sizeof(struct efi_table_hdr), 437 }, 438 .get_time = &efi_get_time_boottime, 439 .set_time = (void *)&efi_device_error, 440 .get_wakeup_time = (void *)&efi_unimplemented, 441 .set_wakeup_time = (void *)&efi_unimplemented, 442 .set_virtual_address_map = &efi_set_virtual_address_map, 443 .convert_pointer = (void *)&efi_invalid_parameter, 444 .get_variable = efi_get_variable, 445 .get_next_variable = efi_get_next_variable, 446 .set_variable = efi_set_variable, 447 .get_next_high_mono_count = (void *)&efi_device_error, 448 .reset_system = &efi_reset_system_boottime, 449 .update_capsule = efi_update_capsule, 450 .query_capsule_caps = efi_query_capsule_caps, 451 .query_variable_info = efi_query_variable_info, 452 }; 453