]>
Commit | Line | Data |
---|---|---|
022ee6c5 AB |
1 | /* |
2 | * runtime-wrappers.c - Runtime Services function call wrappers | |
3 | * | |
3eb420e7 SP |
4 | * Implementation summary: |
5 | * ----------------------- | |
6 | * 1. When user/kernel thread requests to execute efi_runtime_service(), | |
7 | * enqueue work to efi_rts_wq. | |
8 | * 2. Caller thread waits for completion until the work is finished | |
9 | * because it's dependent on the return status and execution of | |
10 | * efi_runtime_service(). | |
11 | * For instance, get_variable() and get_next_variable(). | |
12 | * | |
022ee6c5 AB |
13 | * Copyright (C) 2014 Linaro Ltd. <[email protected]> |
14 | * | |
15 | * Split off from arch/x86/platform/efi/efi.c | |
16 | * | |
17 | * Copyright (C) 1999 VA Linux Systems | |
18 | * Copyright (C) 1999 Walt Drummond <[email protected]> | |
19 | * Copyright (C) 1999-2002 Hewlett-Packard Co. | |
20 | * Copyright (C) 2005-2008 Intel Co. | |
21 | * Copyright (C) 2013 SuSE Labs | |
22 | * | |
23 | * This file is released under the GPLv2. | |
24 | */ | |
25 | ||
dce48e35 AB |
26 | #define pr_fmt(fmt) "efi: " fmt |
27 | ||
161485e8 | 28 | #include <linux/bug.h> |
022ee6c5 | 29 | #include <linux/efi.h> |
1d04ba17 | 30 | #include <linux/irqflags.h> |
161485e8 | 31 | #include <linux/mutex.h> |
dce48e35 | 32 | #include <linux/semaphore.h> |
1d04ba17 | 33 | #include <linux/stringify.h> |
3eb420e7 SP |
34 | #include <linux/workqueue.h> |
35 | #include <linux/completion.h> | |
36 | ||
022ee6c5 AB |
37 | #include <asm/efi.h> |
38 | ||
80e75596 AT |
39 | /* |
40 | * Wrap around the new efi_call_virt_generic() macros so that the | |
41 | * code doesn't get too cluttered: | |
42 | */ | |
43 | #define efi_call_virt(f, args...) \ | |
44 | efi_call_virt_pointer(efi.systab->runtime, f, args) | |
45 | #define __efi_call_virt(f, args...) \ | |
46 | __efi_call_virt_pointer(efi.systab->runtime, f, args) | |
47 | ||
9dbbedaa | 48 | struct efi_runtime_work efi_rts_work; |
3eb420e7 SP |
49 | |
50 | /* | |
51 | * efi_queue_work: Queue efi_runtime_service() and wait until it's done | |
52 | * @rts: efi_runtime_service() function identifier | |
53 | * @rts_arg<1-5>: efi_runtime_service() function arguments | |
54 | * | |
55 | * Accesses to efi_runtime_services() are serialized by a binary | |
56 | * semaphore (efi_runtime_lock) and caller waits until the work is | |
57 | * finished, hence _only_ one work is queued at a time and the caller | |
58 | * thread waits for completion. | |
59 | */ | |
60 | #define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \ | |
61 | ({ \ | |
3eb420e7 SP |
62 | efi_rts_work.status = EFI_ABORTED; \ |
63 | \ | |
3425d934 SP |
64 | if (!efi_enabled(EFI_RUNTIME_SERVICES)) { \ |
65 | pr_warn_once("EFI Runtime Services are disabled!\n"); \ | |
66 | goto exit; \ | |
67 | } \ | |
68 | \ | |
3eb420e7 | 69 | init_completion(&efi_rts_work.efi_rts_comp); \ |
ef1491e7 | 70 | INIT_WORK(&efi_rts_work.work, efi_call_rts); \ |
3eb420e7 SP |
71 | efi_rts_work.arg1 = _arg1; \ |
72 | efi_rts_work.arg2 = _arg2; \ | |
73 | efi_rts_work.arg3 = _arg3; \ | |
74 | efi_rts_work.arg4 = _arg4; \ | |
75 | efi_rts_work.arg5 = _arg5; \ | |
76 | efi_rts_work.efi_rts_id = _rts; \ | |
77 | \ | |
78 | /* \ | |
79 | * queue_work() returns 0 if work was already on queue, \ | |
80 | * _ideally_ this should never happen. \ | |
81 | */ \ | |
82 | if (queue_work(efi_rts_wq, &efi_rts_work.work)) \ | |
83 | wait_for_completion(&efi_rts_work.efi_rts_comp); \ | |
84 | else \ | |
85 | pr_err("Failed to queue work to efi_rts_wq.\n"); \ | |
86 | \ | |
3425d934 SP |
87 | exit: \ |
88 | efi_rts_work.efi_rts_id = NONE; \ | |
3eb420e7 SP |
89 | efi_rts_work.status; \ |
90 | }) | |
91 | ||
80e75596 | 92 | void efi_call_virt_check_flags(unsigned long flags, const char *call) |
1d04ba17 MR |
93 | { |
94 | unsigned long cur_flags, mismatch; | |
95 | ||
96 | local_save_flags(cur_flags); | |
97 | ||
98 | mismatch = flags ^ cur_flags; | |
99 | if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK)) | |
100 | return; | |
101 | ||
102 | add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); | |
103 | pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n", | |
104 | flags, cur_flags, call); | |
105 | local_irq_restore(flags); | |
106 | } | |
1d04ba17 | 107 | |
161485e8 AB |
108 | /* |
109 | * According to section 7.1 of the UEFI spec, Runtime Services are not fully | |
110 | * reentrant, and there are particular combinations of calls that need to be | |
111 | * serialized. (source: UEFI Specification v2.4A) | |
112 | * | |
113 | * Table 31. Rules for Reentry Into Runtime Services | |
114 | * +------------------------------------+-------------------------------+ | |
115 | * | If previous call is busy in | Forbidden to call | | |
116 | * +------------------------------------+-------------------------------+ | |
117 | * | Any | SetVirtualAddressMap() | | |
118 | * +------------------------------------+-------------------------------+ | |
119 | * | ConvertPointer() | ConvertPointer() | | |
120 | * +------------------------------------+-------------------------------+ | |
121 | * | SetVariable() | ResetSystem() | | |
122 | * | UpdateCapsule() | | | |
123 | * | SetTime() | | | |
124 | * | SetWakeupTime() | | | |
125 | * | GetNextHighMonotonicCount() | | | |
126 | * +------------------------------------+-------------------------------+ | |
127 | * | GetVariable() | GetVariable() | | |
128 | * | GetNextVariableName() | GetNextVariableName() | | |
129 | * | SetVariable() | SetVariable() | | |
130 | * | QueryVariableInfo() | QueryVariableInfo() | | |
131 | * | UpdateCapsule() | UpdateCapsule() | | |
132 | * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() | | |
133 | * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() | | |
134 | * +------------------------------------+-------------------------------+ | |
135 | * | GetTime() | GetTime() | | |
136 | * | SetTime() | SetTime() | | |
137 | * | GetWakeupTime() | GetWakeupTime() | | |
138 | * | SetWakeupTime() | SetWakeupTime() | | |
139 | * +------------------------------------+-------------------------------+ | |
140 | * | |
141 | * Due to the fact that the EFI pstore may write to the variable store in | |
dce48e35 | 142 | * interrupt context, we need to use a lock for at least the groups that |
161485e8 AB |
143 | * contain SetVariable() and QueryVariableInfo(). That leaves little else, as |
144 | * none of the remaining functions are actually ever called at runtime. | |
dce48e35 | 145 | * So let's just use a single lock to serialize all Runtime Services calls. |
161485e8 | 146 | */ |
dce48e35 | 147 | static DEFINE_SEMAPHORE(efi_runtime_lock); |
161485e8 | 148 | |
3eb420e7 SP |
149 | /* |
150 | * Calls the appropriate efi_runtime_service() with the appropriate | |
151 | * arguments. | |
152 | * | |
153 | * Semantics followed by efi_call_rts() to understand efi_runtime_work: | |
154 | * 1. If argument was a pointer, recast it from void pointer to original | |
155 | * pointer type. | |
156 | * 2. If argument was a value, recast it from void pointer to original | |
157 | * pointer type and dereference it. | |
158 | */ | |
159 | static void efi_call_rts(struct work_struct *work) | |
160 | { | |
3eb420e7 SP |
161 | void *arg1, *arg2, *arg3, *arg4, *arg5; |
162 | efi_status_t status = EFI_NOT_FOUND; | |
163 | ||
9dbbedaa SP |
164 | arg1 = efi_rts_work.arg1; |
165 | arg2 = efi_rts_work.arg2; | |
166 | arg3 = efi_rts_work.arg3; | |
167 | arg4 = efi_rts_work.arg4; | |
168 | arg5 = efi_rts_work.arg5; | |
3eb420e7 | 169 | |
9dbbedaa | 170 | switch (efi_rts_work.efi_rts_id) { |
3eb420e7 SP |
171 | case GET_TIME: |
172 | status = efi_call_virt(get_time, (efi_time_t *)arg1, | |
173 | (efi_time_cap_t *)arg2); | |
174 | break; | |
175 | case SET_TIME: | |
176 | status = efi_call_virt(set_time, (efi_time_t *)arg1); | |
177 | break; | |
178 | case GET_WAKEUP_TIME: | |
179 | status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1, | |
180 | (efi_bool_t *)arg2, (efi_time_t *)arg3); | |
181 | break; | |
182 | case SET_WAKEUP_TIME: | |
183 | status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1, | |
184 | (efi_time_t *)arg2); | |
185 | break; | |
186 | case GET_VARIABLE: | |
187 | status = efi_call_virt(get_variable, (efi_char16_t *)arg1, | |
188 | (efi_guid_t *)arg2, (u32 *)arg3, | |
189 | (unsigned long *)arg4, (void *)arg5); | |
190 | break; | |
191 | case GET_NEXT_VARIABLE: | |
192 | status = efi_call_virt(get_next_variable, (unsigned long *)arg1, | |
193 | (efi_char16_t *)arg2, | |
194 | (efi_guid_t *)arg3); | |
195 | break; | |
196 | case SET_VARIABLE: | |
197 | status = efi_call_virt(set_variable, (efi_char16_t *)arg1, | |
198 | (efi_guid_t *)arg2, *(u32 *)arg3, | |
199 | *(unsigned long *)arg4, (void *)arg5); | |
200 | break; | |
201 | case QUERY_VARIABLE_INFO: | |
202 | status = efi_call_virt(query_variable_info, *(u32 *)arg1, | |
203 | (u64 *)arg2, (u64 *)arg3, (u64 *)arg4); | |
204 | break; | |
205 | case GET_NEXT_HIGH_MONO_COUNT: | |
206 | status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1); | |
207 | break; | |
208 | case UPDATE_CAPSULE: | |
209 | status = efi_call_virt(update_capsule, | |
210 | (efi_capsule_header_t **)arg1, | |
211 | *(unsigned long *)arg2, | |
212 | *(unsigned long *)arg3); | |
213 | break; | |
214 | case QUERY_CAPSULE_CAPS: | |
215 | status = efi_call_virt(query_capsule_caps, | |
216 | (efi_capsule_header_t **)arg1, | |
217 | *(unsigned long *)arg2, (u64 *)arg3, | |
218 | (int *)arg4); | |
219 | break; | |
220 | default: | |
221 | /* | |
222 | * Ideally, we should never reach here because a caller of this | |
223 | * function should have put the right efi_runtime_service() | |
224 | * function identifier into efi_rts_work->efi_rts_id | |
225 | */ | |
226 | pr_err("Requested executing invalid EFI Runtime Service.\n"); | |
227 | } | |
9dbbedaa SP |
228 | efi_rts_work.status = status; |
229 | complete(&efi_rts_work.efi_rts_comp); | |
3eb420e7 SP |
230 | } |
231 | ||
022ee6c5 AB |
232 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) |
233 | { | |
022ee6c5 AB |
234 | efi_status_t status; |
235 | ||
dce48e35 AB |
236 | if (down_interruptible(&efi_runtime_lock)) |
237 | return EFI_ABORTED; | |
3eb420e7 | 238 | status = efi_queue_work(GET_TIME, tm, tc, NULL, NULL, NULL); |
dce48e35 | 239 | up(&efi_runtime_lock); |
022ee6c5 AB |
240 | return status; |
241 | } | |
242 | ||
243 | static efi_status_t virt_efi_set_time(efi_time_t *tm) | |
244 | { | |
022ee6c5 AB |
245 | efi_status_t status; |
246 | ||
dce48e35 AB |
247 | if (down_interruptible(&efi_runtime_lock)) |
248 | return EFI_ABORTED; | |
3eb420e7 | 249 | status = efi_queue_work(SET_TIME, tm, NULL, NULL, NULL, NULL); |
dce48e35 | 250 | up(&efi_runtime_lock); |
022ee6c5 AB |
251 | return status; |
252 | } | |
253 | ||
254 | static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, | |
255 | efi_bool_t *pending, | |
256 | efi_time_t *tm) | |
257 | { | |
022ee6c5 AB |
258 | efi_status_t status; |
259 | ||
dce48e35 AB |
260 | if (down_interruptible(&efi_runtime_lock)) |
261 | return EFI_ABORTED; | |
3eb420e7 SP |
262 | status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm, NULL, |
263 | NULL); | |
dce48e35 | 264 | up(&efi_runtime_lock); |
022ee6c5 AB |
265 | return status; |
266 | } | |
267 | ||
268 | static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) | |
269 | { | |
022ee6c5 AB |
270 | efi_status_t status; |
271 | ||
dce48e35 AB |
272 | if (down_interruptible(&efi_runtime_lock)) |
273 | return EFI_ABORTED; | |
3eb420e7 SP |
274 | status = efi_queue_work(SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, |
275 | NULL); | |
dce48e35 | 276 | up(&efi_runtime_lock); |
022ee6c5 AB |
277 | return status; |
278 | } | |
279 | ||
280 | static efi_status_t virt_efi_get_variable(efi_char16_t *name, | |
281 | efi_guid_t *vendor, | |
282 | u32 *attr, | |
283 | unsigned long *data_size, | |
284 | void *data) | |
285 | { | |
161485e8 AB |
286 | efi_status_t status; |
287 | ||
dce48e35 AB |
288 | if (down_interruptible(&efi_runtime_lock)) |
289 | return EFI_ABORTED; | |
3eb420e7 SP |
290 | status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, |
291 | data); | |
dce48e35 | 292 | up(&efi_runtime_lock); |
161485e8 | 293 | return status; |
022ee6c5 AB |
294 | } |
295 | ||
296 | static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, | |
297 | efi_char16_t *name, | |
298 | efi_guid_t *vendor) | |
299 | { | |
161485e8 AB |
300 | efi_status_t status; |
301 | ||
dce48e35 AB |
302 | if (down_interruptible(&efi_runtime_lock)) |
303 | return EFI_ABORTED; | |
3eb420e7 SP |
304 | status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor, |
305 | NULL, NULL); | |
dce48e35 | 306 | up(&efi_runtime_lock); |
161485e8 | 307 | return status; |
022ee6c5 AB |
308 | } |
309 | ||
310 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, | |
311 | efi_guid_t *vendor, | |
312 | u32 attr, | |
313 | unsigned long data_size, | |
314 | void *data) | |
315 | { | |
161485e8 | 316 | efi_status_t status; |
161485e8 | 317 | |
dce48e35 AB |
318 | if (down_interruptible(&efi_runtime_lock)) |
319 | return EFI_ABORTED; | |
3eb420e7 SP |
320 | status = efi_queue_work(SET_VARIABLE, name, vendor, &attr, &data_size, |
321 | data); | |
dce48e35 | 322 | up(&efi_runtime_lock); |
161485e8 | 323 | return status; |
022ee6c5 AB |
324 | } |
325 | ||
6d80dba1 MF |
326 | static efi_status_t |
327 | virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, | |
328 | u32 attr, unsigned long data_size, | |
329 | void *data) | |
330 | { | |
6d80dba1 MF |
331 | efi_status_t status; |
332 | ||
dce48e35 | 333 | if (down_trylock(&efi_runtime_lock)) |
6d80dba1 MF |
334 | return EFI_NOT_READY; |
335 | ||
336 | status = efi_call_virt(set_variable, name, vendor, attr, data_size, | |
337 | data); | |
dce48e35 | 338 | up(&efi_runtime_lock); |
6d80dba1 MF |
339 | return status; |
340 | } | |
341 | ||
342 | ||
022ee6c5 AB |
343 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
344 | u64 *storage_space, | |
345 | u64 *remaining_space, | |
346 | u64 *max_variable_size) | |
347 | { | |
161485e8 | 348 | efi_status_t status; |
161485e8 | 349 | |
022ee6c5 AB |
350 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
351 | return EFI_UNSUPPORTED; | |
352 | ||
dce48e35 AB |
353 | if (down_interruptible(&efi_runtime_lock)) |
354 | return EFI_ABORTED; | |
3eb420e7 SP |
355 | status = efi_queue_work(QUERY_VARIABLE_INFO, &attr, storage_space, |
356 | remaining_space, max_variable_size, NULL); | |
dce48e35 | 357 | up(&efi_runtime_lock); |
161485e8 | 358 | return status; |
022ee6c5 AB |
359 | } |
360 | ||
d3cac1f8 AB |
361 | static efi_status_t |
362 | virt_efi_query_variable_info_nonblocking(u32 attr, | |
363 | u64 *storage_space, | |
364 | u64 *remaining_space, | |
365 | u64 *max_variable_size) | |
366 | { | |
d3cac1f8 AB |
367 | efi_status_t status; |
368 | ||
369 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) | |
370 | return EFI_UNSUPPORTED; | |
371 | ||
dce48e35 | 372 | if (down_trylock(&efi_runtime_lock)) |
d3cac1f8 AB |
373 | return EFI_NOT_READY; |
374 | ||
375 | status = efi_call_virt(query_variable_info, attr, storage_space, | |
376 | remaining_space, max_variable_size); | |
dce48e35 | 377 | up(&efi_runtime_lock); |
d3cac1f8 AB |
378 | return status; |
379 | } | |
380 | ||
022ee6c5 AB |
381 | static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) |
382 | { | |
161485e8 AB |
383 | efi_status_t status; |
384 | ||
dce48e35 AB |
385 | if (down_interruptible(&efi_runtime_lock)) |
386 | return EFI_ABORTED; | |
3eb420e7 SP |
387 | status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, |
388 | NULL, NULL); | |
dce48e35 | 389 | up(&efi_runtime_lock); |
161485e8 | 390 | return status; |
022ee6c5 AB |
391 | } |
392 | ||
393 | static void virt_efi_reset_system(int reset_type, | |
394 | efi_status_t status, | |
395 | unsigned long data_size, | |
396 | efi_char16_t *data) | |
397 | { | |
dce48e35 AB |
398 | if (down_interruptible(&efi_runtime_lock)) { |
399 | pr_warn("failed to invoke the reset_system() runtime service:\n" | |
400 | "could not get exclusive access to the firmware\n"); | |
401 | return; | |
402 | } | |
3425d934 | 403 | efi_rts_work.efi_rts_id = RESET_SYSTEM; |
022ee6c5 | 404 | __efi_call_virt(reset_system, reset_type, status, data_size, data); |
dce48e35 | 405 | up(&efi_runtime_lock); |
022ee6c5 AB |
406 | } |
407 | ||
408 | static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, | |
409 | unsigned long count, | |
410 | unsigned long sg_list) | |
411 | { | |
161485e8 AB |
412 | efi_status_t status; |
413 | ||
022ee6c5 AB |
414 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
415 | return EFI_UNSUPPORTED; | |
416 | ||
dce48e35 AB |
417 | if (down_interruptible(&efi_runtime_lock)) |
418 | return EFI_ABORTED; | |
3eb420e7 SP |
419 | status = efi_queue_work(UPDATE_CAPSULE, capsules, &count, &sg_list, |
420 | NULL, NULL); | |
dce48e35 | 421 | up(&efi_runtime_lock); |
161485e8 | 422 | return status; |
022ee6c5 AB |
423 | } |
424 | ||
425 | static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, | |
426 | unsigned long count, | |
427 | u64 *max_size, | |
428 | int *reset_type) | |
429 | { | |
161485e8 AB |
430 | efi_status_t status; |
431 | ||
022ee6c5 AB |
432 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
433 | return EFI_UNSUPPORTED; | |
434 | ||
dce48e35 AB |
435 | if (down_interruptible(&efi_runtime_lock)) |
436 | return EFI_ABORTED; | |
3eb420e7 SP |
437 | status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, &count, |
438 | max_size, reset_type, NULL); | |
dce48e35 | 439 | up(&efi_runtime_lock); |
161485e8 | 440 | return status; |
022ee6c5 AB |
441 | } |
442 | ||
443 | void efi_native_runtime_setup(void) | |
444 | { | |
445 | efi.get_time = virt_efi_get_time; | |
446 | efi.set_time = virt_efi_set_time; | |
447 | efi.get_wakeup_time = virt_efi_get_wakeup_time; | |
448 | efi.set_wakeup_time = virt_efi_set_wakeup_time; | |
449 | efi.get_variable = virt_efi_get_variable; | |
450 | efi.get_next_variable = virt_efi_get_next_variable; | |
451 | efi.set_variable = virt_efi_set_variable; | |
6d80dba1 | 452 | efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; |
022ee6c5 AB |
453 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; |
454 | efi.reset_system = virt_efi_reset_system; | |
455 | efi.query_variable_info = virt_efi_query_variable_info; | |
d3cac1f8 | 456 | efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking; |
022ee6c5 AB |
457 | efi.update_capsule = virt_efi_update_capsule; |
458 | efi.query_capsule_caps = virt_efi_query_capsule_caps; | |
459 | } |