]>
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. <ard.biesheuvel@linaro.org> |
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 <drummond@valinux.com> | |
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 | |
f331e766 HB |
149 | /* |
150 | * Expose the EFI runtime lock to the UV platform | |
151 | */ | |
152 | #ifdef CONFIG_X86_UV | |
153 | extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); | |
154 | #endif | |
155 | ||
3eb420e7 SP |
156 | /* |
157 | * Calls the appropriate efi_runtime_service() with the appropriate | |
158 | * arguments. | |
159 | * | |
160 | * Semantics followed by efi_call_rts() to understand efi_runtime_work: | |
161 | * 1. If argument was a pointer, recast it from void pointer to original | |
162 | * pointer type. | |
163 | * 2. If argument was a value, recast it from void pointer to original | |
164 | * pointer type and dereference it. | |
165 | */ | |
166 | static void efi_call_rts(struct work_struct *work) | |
167 | { | |
3eb420e7 SP |
168 | void *arg1, *arg2, *arg3, *arg4, *arg5; |
169 | efi_status_t status = EFI_NOT_FOUND; | |
170 | ||
9dbbedaa SP |
171 | arg1 = efi_rts_work.arg1; |
172 | arg2 = efi_rts_work.arg2; | |
173 | arg3 = efi_rts_work.arg3; | |
174 | arg4 = efi_rts_work.arg4; | |
175 | arg5 = efi_rts_work.arg5; | |
3eb420e7 | 176 | |
9dbbedaa | 177 | switch (efi_rts_work.efi_rts_id) { |
3eb420e7 SP |
178 | case GET_TIME: |
179 | status = efi_call_virt(get_time, (efi_time_t *)arg1, | |
180 | (efi_time_cap_t *)arg2); | |
181 | break; | |
182 | case SET_TIME: | |
183 | status = efi_call_virt(set_time, (efi_time_t *)arg1); | |
184 | break; | |
185 | case GET_WAKEUP_TIME: | |
186 | status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1, | |
187 | (efi_bool_t *)arg2, (efi_time_t *)arg3); | |
188 | break; | |
189 | case SET_WAKEUP_TIME: | |
190 | status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1, | |
191 | (efi_time_t *)arg2); | |
192 | break; | |
193 | case GET_VARIABLE: | |
194 | status = efi_call_virt(get_variable, (efi_char16_t *)arg1, | |
195 | (efi_guid_t *)arg2, (u32 *)arg3, | |
196 | (unsigned long *)arg4, (void *)arg5); | |
197 | break; | |
198 | case GET_NEXT_VARIABLE: | |
199 | status = efi_call_virt(get_next_variable, (unsigned long *)arg1, | |
200 | (efi_char16_t *)arg2, | |
201 | (efi_guid_t *)arg3); | |
202 | break; | |
203 | case SET_VARIABLE: | |
204 | status = efi_call_virt(set_variable, (efi_char16_t *)arg1, | |
205 | (efi_guid_t *)arg2, *(u32 *)arg3, | |
206 | *(unsigned long *)arg4, (void *)arg5); | |
207 | break; | |
208 | case QUERY_VARIABLE_INFO: | |
209 | status = efi_call_virt(query_variable_info, *(u32 *)arg1, | |
210 | (u64 *)arg2, (u64 *)arg3, (u64 *)arg4); | |
211 | break; | |
212 | case GET_NEXT_HIGH_MONO_COUNT: | |
213 | status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1); | |
214 | break; | |
215 | case UPDATE_CAPSULE: | |
216 | status = efi_call_virt(update_capsule, | |
217 | (efi_capsule_header_t **)arg1, | |
218 | *(unsigned long *)arg2, | |
219 | *(unsigned long *)arg3); | |
220 | break; | |
221 | case QUERY_CAPSULE_CAPS: | |
222 | status = efi_call_virt(query_capsule_caps, | |
223 | (efi_capsule_header_t **)arg1, | |
224 | *(unsigned long *)arg2, (u64 *)arg3, | |
225 | (int *)arg4); | |
226 | break; | |
227 | default: | |
228 | /* | |
229 | * Ideally, we should never reach here because a caller of this | |
230 | * function should have put the right efi_runtime_service() | |
231 | * function identifier into efi_rts_work->efi_rts_id | |
232 | */ | |
233 | pr_err("Requested executing invalid EFI Runtime Service.\n"); | |
234 | } | |
9dbbedaa SP |
235 | efi_rts_work.status = status; |
236 | complete(&efi_rts_work.efi_rts_comp); | |
3eb420e7 SP |
237 | } |
238 | ||
022ee6c5 AB |
239 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) |
240 | { | |
022ee6c5 AB |
241 | efi_status_t status; |
242 | ||
dce48e35 AB |
243 | if (down_interruptible(&efi_runtime_lock)) |
244 | return EFI_ABORTED; | |
3eb420e7 | 245 | status = efi_queue_work(GET_TIME, tm, tc, NULL, NULL, NULL); |
dce48e35 | 246 | up(&efi_runtime_lock); |
022ee6c5 AB |
247 | return status; |
248 | } | |
249 | ||
250 | static efi_status_t virt_efi_set_time(efi_time_t *tm) | |
251 | { | |
022ee6c5 AB |
252 | efi_status_t status; |
253 | ||
dce48e35 AB |
254 | if (down_interruptible(&efi_runtime_lock)) |
255 | return EFI_ABORTED; | |
3eb420e7 | 256 | status = efi_queue_work(SET_TIME, tm, NULL, NULL, NULL, NULL); |
dce48e35 | 257 | up(&efi_runtime_lock); |
022ee6c5 AB |
258 | return status; |
259 | } | |
260 | ||
261 | static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, | |
262 | efi_bool_t *pending, | |
263 | efi_time_t *tm) | |
264 | { | |
022ee6c5 AB |
265 | efi_status_t status; |
266 | ||
dce48e35 AB |
267 | if (down_interruptible(&efi_runtime_lock)) |
268 | return EFI_ABORTED; | |
3eb420e7 SP |
269 | status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm, NULL, |
270 | NULL); | |
dce48e35 | 271 | up(&efi_runtime_lock); |
022ee6c5 AB |
272 | return status; |
273 | } | |
274 | ||
275 | static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) | |
276 | { | |
022ee6c5 AB |
277 | efi_status_t status; |
278 | ||
dce48e35 AB |
279 | if (down_interruptible(&efi_runtime_lock)) |
280 | return EFI_ABORTED; | |
3eb420e7 SP |
281 | status = efi_queue_work(SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, |
282 | NULL); | |
dce48e35 | 283 | up(&efi_runtime_lock); |
022ee6c5 AB |
284 | return status; |
285 | } | |
286 | ||
287 | static efi_status_t virt_efi_get_variable(efi_char16_t *name, | |
288 | efi_guid_t *vendor, | |
289 | u32 *attr, | |
290 | unsigned long *data_size, | |
291 | void *data) | |
292 | { | |
161485e8 AB |
293 | efi_status_t status; |
294 | ||
dce48e35 AB |
295 | if (down_interruptible(&efi_runtime_lock)) |
296 | return EFI_ABORTED; | |
3eb420e7 SP |
297 | status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, |
298 | data); | |
dce48e35 | 299 | up(&efi_runtime_lock); |
161485e8 | 300 | return status; |
022ee6c5 AB |
301 | } |
302 | ||
303 | static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, | |
304 | efi_char16_t *name, | |
305 | efi_guid_t *vendor) | |
306 | { | |
161485e8 AB |
307 | efi_status_t status; |
308 | ||
dce48e35 AB |
309 | if (down_interruptible(&efi_runtime_lock)) |
310 | return EFI_ABORTED; | |
3eb420e7 SP |
311 | status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor, |
312 | NULL, NULL); | |
dce48e35 | 313 | up(&efi_runtime_lock); |
161485e8 | 314 | return status; |
022ee6c5 AB |
315 | } |
316 | ||
317 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, | |
318 | efi_guid_t *vendor, | |
319 | u32 attr, | |
320 | unsigned long data_size, | |
321 | void *data) | |
322 | { | |
161485e8 | 323 | efi_status_t status; |
161485e8 | 324 | |
dce48e35 AB |
325 | if (down_interruptible(&efi_runtime_lock)) |
326 | return EFI_ABORTED; | |
3eb420e7 SP |
327 | status = efi_queue_work(SET_VARIABLE, name, vendor, &attr, &data_size, |
328 | data); | |
dce48e35 | 329 | up(&efi_runtime_lock); |
161485e8 | 330 | return status; |
022ee6c5 AB |
331 | } |
332 | ||
6d80dba1 MF |
333 | static efi_status_t |
334 | virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, | |
335 | u32 attr, unsigned long data_size, | |
336 | void *data) | |
337 | { | |
6d80dba1 MF |
338 | efi_status_t status; |
339 | ||
dce48e35 | 340 | if (down_trylock(&efi_runtime_lock)) |
6d80dba1 MF |
341 | return EFI_NOT_READY; |
342 | ||
343 | status = efi_call_virt(set_variable, name, vendor, attr, data_size, | |
344 | data); | |
dce48e35 | 345 | up(&efi_runtime_lock); |
6d80dba1 MF |
346 | return status; |
347 | } | |
348 | ||
349 | ||
022ee6c5 AB |
350 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
351 | u64 *storage_space, | |
352 | u64 *remaining_space, | |
353 | u64 *max_variable_size) | |
354 | { | |
161485e8 | 355 | efi_status_t status; |
161485e8 | 356 | |
022ee6c5 AB |
357 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
358 | return EFI_UNSUPPORTED; | |
359 | ||
dce48e35 AB |
360 | if (down_interruptible(&efi_runtime_lock)) |
361 | return EFI_ABORTED; | |
3eb420e7 SP |
362 | status = efi_queue_work(QUERY_VARIABLE_INFO, &attr, storage_space, |
363 | remaining_space, max_variable_size, NULL); | |
dce48e35 | 364 | up(&efi_runtime_lock); |
161485e8 | 365 | return status; |
022ee6c5 AB |
366 | } |
367 | ||
d3cac1f8 AB |
368 | static efi_status_t |
369 | virt_efi_query_variable_info_nonblocking(u32 attr, | |
370 | u64 *storage_space, | |
371 | u64 *remaining_space, | |
372 | u64 *max_variable_size) | |
373 | { | |
d3cac1f8 AB |
374 | efi_status_t status; |
375 | ||
376 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) | |
377 | return EFI_UNSUPPORTED; | |
378 | ||
dce48e35 | 379 | if (down_trylock(&efi_runtime_lock)) |
d3cac1f8 AB |
380 | return EFI_NOT_READY; |
381 | ||
382 | status = efi_call_virt(query_variable_info, attr, storage_space, | |
383 | remaining_space, max_variable_size); | |
dce48e35 | 384 | up(&efi_runtime_lock); |
d3cac1f8 AB |
385 | return status; |
386 | } | |
387 | ||
022ee6c5 AB |
388 | static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) |
389 | { | |
161485e8 AB |
390 | efi_status_t status; |
391 | ||
dce48e35 AB |
392 | if (down_interruptible(&efi_runtime_lock)) |
393 | return EFI_ABORTED; | |
3eb420e7 SP |
394 | status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, |
395 | NULL, NULL); | |
dce48e35 | 396 | up(&efi_runtime_lock); |
161485e8 | 397 | return status; |
022ee6c5 AB |
398 | } |
399 | ||
400 | static void virt_efi_reset_system(int reset_type, | |
401 | efi_status_t status, | |
402 | unsigned long data_size, | |
403 | efi_char16_t *data) | |
404 | { | |
dce48e35 AB |
405 | if (down_interruptible(&efi_runtime_lock)) { |
406 | pr_warn("failed to invoke the reset_system() runtime service:\n" | |
407 | "could not get exclusive access to the firmware\n"); | |
408 | return; | |
409 | } | |
3425d934 | 410 | efi_rts_work.efi_rts_id = RESET_SYSTEM; |
022ee6c5 | 411 | __efi_call_virt(reset_system, reset_type, status, data_size, data); |
dce48e35 | 412 | up(&efi_runtime_lock); |
022ee6c5 AB |
413 | } |
414 | ||
415 | static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, | |
416 | unsigned long count, | |
417 | unsigned long sg_list) | |
418 | { | |
161485e8 AB |
419 | efi_status_t status; |
420 | ||
022ee6c5 AB |
421 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
422 | return EFI_UNSUPPORTED; | |
423 | ||
dce48e35 AB |
424 | if (down_interruptible(&efi_runtime_lock)) |
425 | return EFI_ABORTED; | |
3eb420e7 SP |
426 | status = efi_queue_work(UPDATE_CAPSULE, capsules, &count, &sg_list, |
427 | NULL, NULL); | |
dce48e35 | 428 | up(&efi_runtime_lock); |
161485e8 | 429 | return status; |
022ee6c5 AB |
430 | } |
431 | ||
432 | static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, | |
433 | unsigned long count, | |
434 | u64 *max_size, | |
435 | int *reset_type) | |
436 | { | |
161485e8 AB |
437 | efi_status_t status; |
438 | ||
022ee6c5 AB |
439 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
440 | return EFI_UNSUPPORTED; | |
441 | ||
dce48e35 AB |
442 | if (down_interruptible(&efi_runtime_lock)) |
443 | return EFI_ABORTED; | |
3eb420e7 SP |
444 | status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, &count, |
445 | max_size, reset_type, NULL); | |
dce48e35 | 446 | up(&efi_runtime_lock); |
161485e8 | 447 | return status; |
022ee6c5 AB |
448 | } |
449 | ||
450 | void efi_native_runtime_setup(void) | |
451 | { | |
452 | efi.get_time = virt_efi_get_time; | |
453 | efi.set_time = virt_efi_set_time; | |
454 | efi.get_wakeup_time = virt_efi_get_wakeup_time; | |
455 | efi.set_wakeup_time = virt_efi_set_wakeup_time; | |
456 | efi.get_variable = virt_efi_get_variable; | |
457 | efi.get_next_variable = virt_efi_get_next_variable; | |
458 | efi.set_variable = virt_efi_set_variable; | |
6d80dba1 | 459 | efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; |
022ee6c5 AB |
460 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; |
461 | efi.reset_system = virt_efi_reset_system; | |
462 | efi.query_variable_info = virt_efi_query_variable_info; | |
d3cac1f8 | 463 | efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nonblocking; |
022ee6c5 AB |
464 | efi.update_capsule = virt_efi_update_capsule; |
465 | efi.query_capsule_caps = virt_efi_query_capsule_caps; | |
466 | } |