1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * runtime-wrappers.c - Runtime Services function call wrappers |
4 | * |
5 | * Implementation summary: |
6 | * ----------------------- |
7 | * 1. When user/kernel thread requests to execute efi_runtime_service(), |
8 | * enqueue work to efi_rts_wq. |
9 | * 2. Caller thread waits for completion until the work is finished |
10 | * because it's dependent on the return status and execution of |
11 | * efi_runtime_service(). |
12 | * For instance, get_variable() and get_next_variable(). |
13 | * |
14 | * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> |
15 | * |
16 | * Split off from arch/x86/platform/efi/efi.c |
17 | * |
18 | * Copyright (C) 1999 VA Linux Systems |
19 | * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> |
20 | * Copyright (C) 1999-2002 Hewlett-Packard Co. |
21 | * Copyright (C) 2005-2008 Intel Co. |
22 | * Copyright (C) 2013 SuSE Labs |
23 | */ |
24 | |
25 | #define pr_fmt(fmt) "efi: " fmt |
26 | |
27 | #include <linux/bug.h> |
28 | #include <linux/efi.h> |
29 | #include <linux/irqflags.h> |
30 | #include <linux/mutex.h> |
31 | #include <linux/semaphore.h> |
32 | #include <linux/stringify.h> |
33 | #include <linux/workqueue.h> |
34 | #include <linux/completion.h> |
35 | |
36 | #include <asm/efi.h> |
37 | |
38 | /* |
39 | * Wrap around the new efi_call_virt_generic() macros so that the |
40 | * code doesn't get too cluttered: |
41 | */ |
42 | #define efi_call_virt(f, args...) \ |
43 | arch_efi_call_virt(efi.runtime, f, args) |
44 | |
45 | union efi_rts_args { |
46 | struct { |
47 | efi_time_t *time; |
48 | efi_time_cap_t *capabilities; |
49 | } GET_TIME; |
50 | |
51 | struct { |
52 | efi_time_t *time; |
53 | } SET_TIME; |
54 | |
55 | struct { |
56 | efi_bool_t *enabled; |
57 | efi_bool_t *pending; |
58 | efi_time_t *time; |
59 | } GET_WAKEUP_TIME; |
60 | |
61 | struct { |
62 | efi_bool_t enable; |
63 | efi_time_t *time; |
64 | } SET_WAKEUP_TIME; |
65 | |
66 | struct { |
67 | efi_char16_t *name; |
68 | efi_guid_t *vendor; |
69 | u32 *attr; |
70 | unsigned long *data_size; |
71 | void *data; |
72 | } GET_VARIABLE; |
73 | |
74 | struct { |
75 | unsigned long *name_size; |
76 | efi_char16_t *name; |
77 | efi_guid_t *vendor; |
78 | } GET_NEXT_VARIABLE; |
79 | |
80 | struct { |
81 | efi_char16_t *name; |
82 | efi_guid_t *vendor; |
83 | u32 attr; |
84 | unsigned long data_size; |
85 | void *data; |
86 | } SET_VARIABLE; |
87 | |
88 | struct { |
89 | u32 attr; |
90 | u64 *storage_space; |
91 | u64 *remaining_space; |
92 | u64 *max_variable_size; |
93 | } QUERY_VARIABLE_INFO; |
94 | |
95 | struct { |
96 | u32 *high_count; |
97 | } GET_NEXT_HIGH_MONO_COUNT; |
98 | |
99 | struct { |
100 | efi_capsule_header_t **capsules; |
101 | unsigned long count; |
102 | unsigned long sg_list; |
103 | } UPDATE_CAPSULE; |
104 | |
105 | struct { |
106 | efi_capsule_header_t **capsules; |
107 | unsigned long count; |
108 | u64 *max_size; |
109 | int *reset_type; |
110 | } QUERY_CAPSULE_CAPS; |
111 | |
112 | struct { |
113 | efi_status_t (__efiapi *acpi_prm_handler)(u64, void *); |
114 | u64 param_buffer_addr; |
115 | void *context; |
116 | } ACPI_PRM_HANDLER; |
117 | }; |
118 | |
119 | struct efi_runtime_work efi_rts_work; |
120 | |
121 | /* |
122 | * efi_queue_work: Queue EFI runtime service call and wait for completion |
123 | * @_rts: EFI runtime service function identifier |
124 | * @_args: Arguments to pass to the EFI runtime service |
125 | * |
126 | * Accesses to efi_runtime_services() are serialized by a binary |
127 | * semaphore (efi_runtime_lock) and caller waits until the work is |
128 | * finished, hence _only_ one work is queued at a time and the caller |
129 | * thread waits for completion. |
130 | */ |
131 | #define efi_queue_work(_rts, _args...) \ |
132 | __efi_queue_work(EFI_ ## _rts, \ |
133 | &(union efi_rts_args){ ._rts = { _args }}) |
134 | |
135 | #ifndef arch_efi_save_flags |
136 | #define arch_efi_save_flags(state_flags) local_save_flags(state_flags) |
137 | #define arch_efi_restore_flags(state_flags) local_irq_restore(state_flags) |
138 | #endif |
139 | |
140 | unsigned long efi_call_virt_save_flags(void) |
141 | { |
142 | unsigned long flags; |
143 | |
144 | arch_efi_save_flags(flags); |
145 | return flags; |
146 | } |
147 | |
148 | void efi_call_virt_check_flags(unsigned long flags, const void *caller) |
149 | { |
150 | unsigned long cur_flags, mismatch; |
151 | |
152 | cur_flags = efi_call_virt_save_flags(); |
153 | |
154 | mismatch = flags ^ cur_flags; |
155 | if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK)) |
156 | return; |
157 | |
158 | add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); |
159 | pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI call from %pS\n" , |
160 | flags, cur_flags, caller ?: __builtin_return_address(0)); |
161 | arch_efi_restore_flags(flags); |
162 | } |
163 | |
164 | /* |
165 | * According to section 7.1 of the UEFI spec, Runtime Services are not fully |
166 | * reentrant, and there are particular combinations of calls that need to be |
167 | * serialized. (source: UEFI Specification v2.4A) |
168 | * |
169 | * Table 31. Rules for Reentry Into Runtime Services |
170 | * +------------------------------------+-------------------------------+ |
171 | * | If previous call is busy in | Forbidden to call | |
172 | * +------------------------------------+-------------------------------+ |
173 | * | Any | SetVirtualAddressMap() | |
174 | * +------------------------------------+-------------------------------+ |
175 | * | ConvertPointer() | ConvertPointer() | |
176 | * +------------------------------------+-------------------------------+ |
177 | * | SetVariable() | ResetSystem() | |
178 | * | UpdateCapsule() | | |
179 | * | SetTime() | | |
180 | * | SetWakeupTime() | | |
181 | * | GetNextHighMonotonicCount() | | |
182 | * +------------------------------------+-------------------------------+ |
183 | * | GetVariable() | GetVariable() | |
184 | * | GetNextVariableName() | GetNextVariableName() | |
185 | * | SetVariable() | SetVariable() | |
186 | * | QueryVariableInfo() | QueryVariableInfo() | |
187 | * | UpdateCapsule() | UpdateCapsule() | |
188 | * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() | |
189 | * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() | |
190 | * +------------------------------------+-------------------------------+ |
191 | * | GetTime() | GetTime() | |
192 | * | SetTime() | SetTime() | |
193 | * | GetWakeupTime() | GetWakeupTime() | |
194 | * | SetWakeupTime() | SetWakeupTime() | |
195 | * +------------------------------------+-------------------------------+ |
196 | * |
197 | * Due to the fact that the EFI pstore may write to the variable store in |
198 | * interrupt context, we need to use a lock for at least the groups that |
199 | * contain SetVariable() and QueryVariableInfo(). That leaves little else, as |
200 | * none of the remaining functions are actually ever called at runtime. |
201 | * So let's just use a single lock to serialize all Runtime Services calls. |
202 | */ |
203 | static DEFINE_SEMAPHORE(efi_runtime_lock, 1); |
204 | |
205 | /* |
206 | * Expose the EFI runtime lock to the UV platform |
207 | */ |
208 | #ifdef CONFIG_X86_UV |
209 | extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); |
210 | #endif |
211 | |
212 | /* |
213 | * Calls the appropriate efi_runtime_service() with the appropriate |
214 | * arguments. |
215 | */ |
216 | static void efi_call_rts(struct work_struct *work) |
217 | { |
218 | const union efi_rts_args *args = efi_rts_work.args; |
219 | efi_status_t status = EFI_NOT_FOUND; |
220 | unsigned long flags; |
221 | |
222 | arch_efi_call_virt_setup(); |
223 | flags = efi_call_virt_save_flags(); |
224 | |
225 | switch (efi_rts_work.efi_rts_id) { |
226 | case EFI_GET_TIME: |
227 | status = efi_call_virt(get_time, |
228 | args->GET_TIME.time, |
229 | args->GET_TIME.capabilities); |
230 | break; |
231 | case EFI_SET_TIME: |
232 | status = efi_call_virt(set_time, |
233 | args->SET_TIME.time); |
234 | break; |
235 | case EFI_GET_WAKEUP_TIME: |
236 | status = efi_call_virt(get_wakeup_time, |
237 | args->GET_WAKEUP_TIME.enabled, |
238 | args->GET_WAKEUP_TIME.pending, |
239 | args->GET_WAKEUP_TIME.time); |
240 | break; |
241 | case EFI_SET_WAKEUP_TIME: |
242 | status = efi_call_virt(set_wakeup_time, |
243 | args->SET_WAKEUP_TIME.enable, |
244 | args->SET_WAKEUP_TIME.time); |
245 | break; |
246 | case EFI_GET_VARIABLE: |
247 | status = efi_call_virt(get_variable, |
248 | args->GET_VARIABLE.name, |
249 | args->GET_VARIABLE.vendor, |
250 | args->GET_VARIABLE.attr, |
251 | args->GET_VARIABLE.data_size, |
252 | args->GET_VARIABLE.data); |
253 | break; |
254 | case EFI_GET_NEXT_VARIABLE: |
255 | status = efi_call_virt(get_next_variable, |
256 | args->GET_NEXT_VARIABLE.name_size, |
257 | args->GET_NEXT_VARIABLE.name, |
258 | args->GET_NEXT_VARIABLE.vendor); |
259 | break; |
260 | case EFI_SET_VARIABLE: |
261 | status = efi_call_virt(set_variable, |
262 | args->SET_VARIABLE.name, |
263 | args->SET_VARIABLE.vendor, |
264 | args->SET_VARIABLE.attr, |
265 | args->SET_VARIABLE.data_size, |
266 | args->SET_VARIABLE.data); |
267 | break; |
268 | case EFI_QUERY_VARIABLE_INFO: |
269 | status = efi_call_virt(query_variable_info, |
270 | args->QUERY_VARIABLE_INFO.attr, |
271 | args->QUERY_VARIABLE_INFO.storage_space, |
272 | args->QUERY_VARIABLE_INFO.remaining_space, |
273 | args->QUERY_VARIABLE_INFO.max_variable_size); |
274 | break; |
275 | case EFI_GET_NEXT_HIGH_MONO_COUNT: |
276 | status = efi_call_virt(get_next_high_mono_count, |
277 | args->GET_NEXT_HIGH_MONO_COUNT.high_count); |
278 | break; |
279 | case EFI_UPDATE_CAPSULE: |
280 | status = efi_call_virt(update_capsule, |
281 | args->UPDATE_CAPSULE.capsules, |
282 | args->UPDATE_CAPSULE.count, |
283 | args->UPDATE_CAPSULE.sg_list); |
284 | break; |
285 | case EFI_QUERY_CAPSULE_CAPS: |
286 | status = efi_call_virt(query_capsule_caps, |
287 | args->QUERY_CAPSULE_CAPS.capsules, |
288 | args->QUERY_CAPSULE_CAPS.count, |
289 | args->QUERY_CAPSULE_CAPS.max_size, |
290 | args->QUERY_CAPSULE_CAPS.reset_type); |
291 | break; |
292 | case EFI_ACPI_PRM_HANDLER: |
293 | #ifdef CONFIG_ACPI_PRMT |
294 | status = arch_efi_call_virt(args, ACPI_PRM_HANDLER.acpi_prm_handler, |
295 | args->ACPI_PRM_HANDLER.param_buffer_addr, |
296 | args->ACPI_PRM_HANDLER.context); |
297 | break; |
298 | #endif |
299 | default: |
300 | /* |
301 | * Ideally, we should never reach here because a caller of this |
302 | * function should have put the right efi_runtime_service() |
303 | * function identifier into efi_rts_work->efi_rts_id |
304 | */ |
305 | pr_err("Requested executing invalid EFI Runtime Service.\n" ); |
306 | } |
307 | |
308 | efi_call_virt_check_flags(flags, caller: efi_rts_work.caller); |
309 | arch_efi_call_virt_teardown(); |
310 | |
311 | efi_rts_work.status = status; |
312 | complete(&efi_rts_work.efi_rts_comp); |
313 | } |
314 | |
315 | static efi_status_t __efi_queue_work(enum efi_rts_ids id, |
316 | union efi_rts_args *args) |
317 | { |
318 | efi_rts_work.efi_rts_id = id; |
319 | efi_rts_work.args = args; |
320 | efi_rts_work.caller = __builtin_return_address(0); |
321 | efi_rts_work.status = EFI_ABORTED; |
322 | |
323 | if (!efi_enabled(EFI_RUNTIME_SERVICES)) { |
324 | pr_warn_once("EFI Runtime Services are disabled!\n" ); |
325 | efi_rts_work.status = EFI_DEVICE_ERROR; |
326 | goto exit; |
327 | } |
328 | |
329 | init_completion(x: &efi_rts_work.efi_rts_comp); |
330 | INIT_WORK(&efi_rts_work.work, efi_call_rts); |
331 | |
332 | /* |
333 | * queue_work() returns 0 if work was already on queue, |
334 | * _ideally_ this should never happen. |
335 | */ |
336 | if (queue_work(wq: efi_rts_wq, work: &efi_rts_work.work)) |
337 | wait_for_completion(&efi_rts_work.efi_rts_comp); |
338 | else |
339 | pr_err("Failed to queue work to efi_rts_wq.\n" ); |
340 | |
341 | WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); |
342 | exit: |
343 | efi_rts_work.efi_rts_id = EFI_NONE; |
344 | return efi_rts_work.status; |
345 | } |
346 | |
347 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) |
348 | { |
349 | efi_status_t status; |
350 | |
351 | if (down_interruptible(sem: &efi_runtime_lock)) |
352 | return EFI_ABORTED; |
353 | status = efi_queue_work(GET_TIME, tm, tc); |
354 | up(sem: &efi_runtime_lock); |
355 | return status; |
356 | } |
357 | |
358 | static efi_status_t virt_efi_set_time(efi_time_t *tm) |
359 | { |
360 | efi_status_t status; |
361 | |
362 | if (down_interruptible(sem: &efi_runtime_lock)) |
363 | return EFI_ABORTED; |
364 | status = efi_queue_work(SET_TIME, tm); |
365 | up(sem: &efi_runtime_lock); |
366 | return status; |
367 | } |
368 | |
369 | static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, |
370 | efi_bool_t *pending, |
371 | efi_time_t *tm) |
372 | { |
373 | efi_status_t status; |
374 | |
375 | if (down_interruptible(sem: &efi_runtime_lock)) |
376 | return EFI_ABORTED; |
377 | status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm); |
378 | up(sem: &efi_runtime_lock); |
379 | return status; |
380 | } |
381 | |
382 | static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) |
383 | { |
384 | efi_status_t status; |
385 | |
386 | if (down_interruptible(sem: &efi_runtime_lock)) |
387 | return EFI_ABORTED; |
388 | status = efi_queue_work(SET_WAKEUP_TIME, enabled, tm); |
389 | up(sem: &efi_runtime_lock); |
390 | return status; |
391 | } |
392 | |
393 | static efi_status_t virt_efi_get_variable(efi_char16_t *name, |
394 | efi_guid_t *vendor, |
395 | u32 *attr, |
396 | unsigned long *data_size, |
397 | void *data) |
398 | { |
399 | efi_status_t status; |
400 | |
401 | if (down_interruptible(sem: &efi_runtime_lock)) |
402 | return EFI_ABORTED; |
403 | status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, |
404 | data); |
405 | up(sem: &efi_runtime_lock); |
406 | return status; |
407 | } |
408 | |
409 | static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, |
410 | efi_char16_t *name, |
411 | efi_guid_t *vendor) |
412 | { |
413 | efi_status_t status; |
414 | |
415 | if (down_interruptible(sem: &efi_runtime_lock)) |
416 | return EFI_ABORTED; |
417 | status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor); |
418 | up(sem: &efi_runtime_lock); |
419 | return status; |
420 | } |
421 | |
422 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, |
423 | efi_guid_t *vendor, |
424 | u32 attr, |
425 | unsigned long data_size, |
426 | void *data) |
427 | { |
428 | efi_status_t status; |
429 | |
430 | if (down_interruptible(sem: &efi_runtime_lock)) |
431 | return EFI_ABORTED; |
432 | status = efi_queue_work(SET_VARIABLE, name, vendor, attr, data_size, |
433 | data); |
434 | up(sem: &efi_runtime_lock); |
435 | return status; |
436 | } |
437 | |
438 | static efi_status_t |
439 | virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr, |
440 | unsigned long data_size, void *data) |
441 | { |
442 | efi_status_t status; |
443 | |
444 | if (down_trylock(sem: &efi_runtime_lock)) |
445 | return EFI_NOT_READY; |
446 | |
447 | status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor, |
448 | attr, data_size, data); |
449 | up(sem: &efi_runtime_lock); |
450 | return status; |
451 | } |
452 | |
453 | |
454 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
455 | u64 *storage_space, |
456 | u64 *remaining_space, |
457 | u64 *max_variable_size) |
458 | { |
459 | efi_status_t status; |
460 | |
461 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
462 | return EFI_UNSUPPORTED; |
463 | |
464 | if (down_interruptible(sem: &efi_runtime_lock)) |
465 | return EFI_ABORTED; |
466 | status = efi_queue_work(QUERY_VARIABLE_INFO, attr, storage_space, |
467 | remaining_space, max_variable_size); |
468 | up(sem: &efi_runtime_lock); |
469 | return status; |
470 | } |
471 | |
472 | static efi_status_t |
473 | virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space, |
474 | u64 *remaining_space, u64 *max_variable_size) |
475 | { |
476 | efi_status_t status; |
477 | |
478 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
479 | return EFI_UNSUPPORTED; |
480 | |
481 | if (down_trylock(sem: &efi_runtime_lock)) |
482 | return EFI_NOT_READY; |
483 | |
484 | status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr, |
485 | storage_space, remaining_space, |
486 | max_variable_size); |
487 | up(sem: &efi_runtime_lock); |
488 | return status; |
489 | } |
490 | |
491 | static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) |
492 | { |
493 | efi_status_t status; |
494 | |
495 | if (down_interruptible(sem: &efi_runtime_lock)) |
496 | return EFI_ABORTED; |
497 | status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count); |
498 | up(sem: &efi_runtime_lock); |
499 | return status; |
500 | } |
501 | |
502 | static void virt_efi_reset_system(int reset_type, |
503 | efi_status_t status, |
504 | unsigned long data_size, |
505 | efi_char16_t *data) |
506 | { |
507 | if (down_trylock(sem: &efi_runtime_lock)) { |
508 | pr_warn("failed to invoke the reset_system() runtime service:\n" |
509 | "could not get exclusive access to the firmware\n" ); |
510 | return; |
511 | } |
512 | |
513 | arch_efi_call_virt_setup(); |
514 | efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM; |
515 | arch_efi_call_virt(efi.runtime, reset_system, reset_type, status, |
516 | data_size, data); |
517 | arch_efi_call_virt_teardown(); |
518 | |
519 | up(sem: &efi_runtime_lock); |
520 | } |
521 | |
522 | static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, |
523 | unsigned long count, |
524 | unsigned long sg_list) |
525 | { |
526 | efi_status_t status; |
527 | |
528 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
529 | return EFI_UNSUPPORTED; |
530 | |
531 | if (down_interruptible(sem: &efi_runtime_lock)) |
532 | return EFI_ABORTED; |
533 | status = efi_queue_work(UPDATE_CAPSULE, capsules, count, sg_list); |
534 | up(sem: &efi_runtime_lock); |
535 | return status; |
536 | } |
537 | |
538 | static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, |
539 | unsigned long count, |
540 | u64 *max_size, |
541 | int *reset_type) |
542 | { |
543 | efi_status_t status; |
544 | |
545 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
546 | return EFI_UNSUPPORTED; |
547 | |
548 | if (down_interruptible(sem: &efi_runtime_lock)) |
549 | return EFI_ABORTED; |
550 | status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, count, |
551 | max_size, reset_type); |
552 | up(sem: &efi_runtime_lock); |
553 | return status; |
554 | } |
555 | |
556 | void __init efi_native_runtime_setup(void) |
557 | { |
558 | efi.get_time = virt_efi_get_time; |
559 | efi.set_time = virt_efi_set_time; |
560 | efi.get_wakeup_time = virt_efi_get_wakeup_time; |
561 | efi.set_wakeup_time = virt_efi_set_wakeup_time; |
562 | efi.get_variable = virt_efi_get_variable; |
563 | efi.get_next_variable = virt_efi_get_next_variable; |
564 | efi.set_variable = virt_efi_set_variable; |
565 | efi.set_variable_nonblocking = virt_efi_set_variable_nb; |
566 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; |
567 | efi.reset_system = virt_efi_reset_system; |
568 | efi.query_variable_info = virt_efi_query_variable_info; |
569 | efi.query_variable_info_nonblocking = virt_efi_query_variable_info_nb; |
570 | efi.update_capsule = virt_efi_update_capsule; |
571 | efi.query_capsule_caps = virt_efi_query_capsule_caps; |
572 | } |
573 | |
574 | #ifdef CONFIG_ACPI_PRMT |
575 | |
576 | efi_status_t |
577 | efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *), |
578 | u64 param_buffer_addr, void *context) |
579 | { |
580 | efi_status_t status; |
581 | |
582 | if (down_interruptible(sem: &efi_runtime_lock)) |
583 | return EFI_ABORTED; |
584 | status = efi_queue_work(ACPI_PRM_HANDLER, handler_addr, |
585 | param_buffer_addr, context); |
586 | up(sem: &efi_runtime_lock); |
587 | return status; |
588 | } |
589 | |
590 | #endif |
591 | |