efi_loader: Add runtime services
[oweals/u-boot.git] / lib / efi_loader / efi_runtime.c
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 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void);
20 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void);
21 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void);
22
23 #if defined(CONFIG_ARM64)
24 #define R_RELATIVE      1027
25 #define R_MASK          0xffffffffULL
26 #define IS_RELA         1
27 #elif defined(CONFIG_ARM)
28 #define R_RELATIVE      23
29 #define R_MASK          0xffULL
30 #else
31 #error Need to add relocation awareness
32 #endif
33
34 struct elf_rel {
35         ulong *offset;
36         ulong info;
37 };
38
39 struct elf_rela {
40         ulong *offset;
41         ulong info;
42         long addend;
43 };
44
45 /*
46  * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI
47  * payload are running concurrently at the same time. In this mode, we can
48  * handle a good number of runtime callbacks
49  */
50
51 static void EFIAPI efi_reset_system(enum efi_reset_type reset_type,
52                                     efi_status_t reset_status,
53                                     unsigned long data_size, void *reset_data)
54 {
55         EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
56                   reset_data);
57
58         switch (reset_type) {
59         case EFI_RESET_COLD:
60         case EFI_RESET_WARM:
61                 do_reset(NULL, 0, 0, NULL);
62                 break;
63         case EFI_RESET_SHUTDOWN:
64                 /* We don't have anything to map this to */
65                 break;
66         }
67
68         EFI_EXIT(EFI_SUCCESS);
69 }
70
71 static efi_status_t EFIAPI efi_get_time(struct efi_time *time,
72                                         struct efi_time_cap *capabilities)
73 {
74 #if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC)
75         struct rtc_time tm;
76         int r;
77         struct udevice *dev;
78
79         EFI_ENTRY("%p %p", time, capabilities);
80
81         r = uclass_get_device(UCLASS_RTC, 0, &dev);
82         if (r)
83                 return EFI_EXIT(EFI_DEVICE_ERROR);
84
85         r = dm_rtc_get(dev, &tm);
86         if (r)
87                 return EFI_EXIT(EFI_DEVICE_ERROR);
88
89         memset(time, 0, sizeof(*time));
90         time->year = tm.tm_year;
91         time->month = tm.tm_mon;
92         time->day = tm.tm_mday;
93         time->hour = tm.tm_hour;
94         time->minute = tm.tm_min;
95         time->daylight = tm.tm_isdst;
96
97         return EFI_EXIT(EFI_SUCCESS);
98 #else
99         return EFI_DEVICE_ERROR;
100 #endif
101 }
102
103 struct efi_runtime_detach_list_struct {
104         void *ptr;
105         void *patchto;
106 };
107
108 static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
109         {
110                 /* do_reset is gone */
111                 .ptr = &efi_runtime_services.reset_system,
112                 .patchto = NULL,
113         }, {
114                 /* invalidate_*cache_all are gone */
115                 .ptr = &efi_runtime_services.set_virtual_address_map,
116                 .patchto = &efi_invalid_parameter,
117         }, {
118                 /* RTC accessors are gone */
119                 .ptr = &efi_runtime_services.get_time,
120                 .patchto = &efi_device_error,
121         },
122 };
123
124 static bool efi_runtime_tobedetached(void *p)
125 {
126         int i;
127
128         for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++)
129                 if (efi_runtime_detach_list[i].ptr == p)
130                         return true;
131
132         return false;
133 }
134
135 static void efi_runtime_detach(ulong offset)
136 {
137         int i;
138         ulong patchoff = offset - (ulong)gd->relocaddr;
139
140         for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
141                 ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
142                 ulong *p = efi_runtime_detach_list[i].ptr;
143                 ulong newaddr = patchto ? (patchto + patchoff) : 0;
144
145 #ifdef DEBUG_EFI
146                 printf("%s: Setting %p to %lx\n", __func__, p, newaddr);
147 #endif
148                 *p = newaddr;
149         }
150 }
151
152 /* Relocate EFI runtime to uboot_reloc_base = offset */
153 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
154 {
155 #ifdef IS_RELA
156         struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
157 #else
158         struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
159         static ulong lastoff = CONFIG_SYS_TEXT_BASE;
160 #endif
161
162 #ifdef DEBUG_EFI
163         printf("%s: Relocating to offset=%lx\n", __func__, offset);
164 #endif
165
166         for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
167                 ulong base = CONFIG_SYS_TEXT_BASE;
168                 ulong *p;
169                 ulong newaddr;
170
171                 p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
172
173                 if ((rel->info & R_MASK) != R_RELATIVE) {
174                         continue;
175                 }
176
177 #ifdef IS_RELA
178                 newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
179 #else
180                 newaddr = *p - lastoff + offset;
181 #endif
182
183                 /* Check if the relocation is inside bounds */
184                 if (map && ((newaddr < map->virtual_start) ||
185                     newaddr > (map->virtual_start + (map->num_pages << 12)))) {
186                         if (!efi_runtime_tobedetached(p))
187                                 printf("U-Boot EFI: Relocation at %p is out of "
188                                        "range (%lx)\n", p, newaddr);
189                         continue;
190                 }
191
192 #ifdef DEBUG_EFI
193                 printf("%s: Setting %p to %lx\n", __func__, p, newaddr);
194 #endif
195
196                 *p = newaddr;
197                 flush_dcache_range((ulong)p, (ulong)&p[1]);
198         }
199
200 #ifndef IS_RELA
201         lastoff = offset;
202 #endif
203
204         invalidate_icache_all();
205 }
206
207 static efi_status_t EFIAPI efi_set_virtual_address_map(
208                         unsigned long memory_map_size,
209                         unsigned long descriptor_size,
210                         uint32_t descriptor_version,
211                         struct efi_mem_desc *virtmap)
212 {
213         ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
214         int n = memory_map_size / descriptor_size;
215         int i;
216
217         EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
218                   descriptor_version, virtmap);
219
220         for (i = 0; i < n; i++) {
221                 struct efi_mem_desc *map;
222
223                 map = (void*)virtmap + (descriptor_size * i);
224                 if (map->type == EFI_RUNTIME_SERVICES_CODE) {
225                         ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr);
226
227                         efi_runtime_relocate(new_offset, map);
228                         /* Once we're virtual, we can no longer handle
229                            complex callbacks */
230                         efi_runtime_detach(new_offset);
231                         return EFI_EXIT(EFI_SUCCESS);
232                 }
233         }
234
235         return EFI_EXIT(EFI_INVALID_PARAMETER);
236 }
237
238 /*
239  * In the second stage, U-Boot has disappeared. To isolate our runtime code
240  * that at this point still exists from the rest, we put it into a special
241  * section.
242  *
243  *        !!WARNING!!
244  *
245  * This means that we can not rely on any code outside of this file in any
246  * function or variable below this line.
247  *
248  * Please keep everything fully self-contained and annotated with
249  * EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers.
250  */
251
252 /*
253  * Relocate the EFI runtime stub to a different place. We need to call this
254  * the first time we expose the runtime interface to a user and on set virtual
255  * address map calls.
256  */
257
258 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void)
259 {
260         return EFI_UNSUPPORTED;
261 }
262
263 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void)
264 {
265         return EFI_DEVICE_ERROR;
266 }
267
268 static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void)
269 {
270         return EFI_INVALID_PARAMETER;
271 }
272
273 struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
274         .hdr = {
275                 .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
276                 .revision = EFI_RUNTIME_SERVICES_REVISION,
277                 .headersize = sizeof(struct efi_table_hdr),
278         },
279         .get_time = &efi_get_time,
280         .set_time = (void *)&efi_device_error,
281         .get_wakeup_time = (void *)&efi_unimplemented,
282         .set_wakeup_time = (void *)&efi_unimplemented,
283         .set_virtual_address_map = &efi_set_virtual_address_map,
284         .convert_pointer = (void *)&efi_invalid_parameter,
285         .get_variable = (void *)&efi_device_error,
286         .get_next_variable = (void *)&efi_device_error,
287         .set_variable = (void *)&efi_device_error,
288         .get_next_high_mono_count = (void *)&efi_device_error,
289         .reset_system = &efi_reset_system,
290 };