Skip to content

Commit 3eb420e

Browse files
Sai PraneethIngo Molnar
authored andcommitted
efi: Use a work queue to invoke EFI Runtime Services
Presently, when a user process requests the kernel to execute any UEFI runtime service, the kernel temporarily switches to a separate set of page tables that describe the virtual mapping of the UEFI runtime services regions in memory. Since UEFI runtime services are typically invoked with interrupts enabled, any code that may be called during this time, will have an incorrect view of the process's address space. Although it is unusual for code running in interrupt context to make assumptions about the process context it runs in, there are cases (such as the perf subsystem taking samples) where this causes problems. So let's set up a work queue for calling UEFI runtime services, so that the actual calls are made when the work queue items are dispatched by a work queue worker running in a separate kernel thread. Such threads are not expected to have userland mappings in the first place, and so the additional mappings created for the UEFI runtime services can never clash with any. The ResetSystem() runtime service is not covered by the work queue handling, since it is not expected to return, and may be called at a time when the kernel is torn down to the point where we cannot expect work queues to still be operational. The non-blocking variants of SetVariable() and QueryVariableInfo() are also excluded: these are intended to be used from atomic context, which obviously rules out waiting for a completion to be signalled by another thread. Note that these variants are currently only used for UEFI runtime services calls that occur very early in the boot, and for ones that occur in critical conditions, e.g., to flush kernel logs to UEFI variables via efi-pstore. Suggested-by: Andy Lutomirski <luto@kernel.org> Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com> [ardb: exclude ResetSystem() from the workqueue treatment merge from 2 separate patches and rewrite commit log] Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20180711094040.12506-4-ard.biesheuvel@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 5a58bc1 commit 3eb420e

File tree

3 files changed

+204
-15
lines changed

3 files changed

+204
-15
lines changed

drivers/firmware/efi/efi.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ struct mm_struct efi_mm = {
8484
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
8585
};
8686

87+
struct workqueue_struct *efi_rts_wq;
88+
8789
static bool disable_runtime;
8890
static int __init setup_noefi(char *arg)
8991
{
@@ -337,6 +339,18 @@ static int __init efisubsys_init(void)
337339
if (!efi_enabled(EFI_BOOT))
338340
return 0;
339341

342+
/*
343+
* Since we process only one efi_runtime_service() at a time, an
344+
* ordered workqueue (which creates only one execution context)
345+
* should suffice all our needs.
346+
*/
347+
efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0);
348+
if (!efi_rts_wq) {
349+
pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n");
350+
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
351+
return 0;
352+
}
353+
340354
/* We register the efi directory at /sys/firmware/efi */
341355
efi_kobj = kobject_create_and_add("efi", firmware_kobj);
342356
if (!efi_kobj) {

drivers/firmware/efi/runtime-wrappers.c

Lines changed: 187 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
/*
22
* runtime-wrappers.c - Runtime Services function call wrappers
33
*
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+
*
413
* Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
514
*
615
* Split off from arch/x86/platform/efi/efi.c
@@ -22,6 +31,9 @@
2231
#include <linux/mutex.h>
2332
#include <linux/semaphore.h>
2433
#include <linux/stringify.h>
34+
#include <linux/workqueue.h>
35+
#include <linux/completion.h>
36+
2537
#include <asm/efi.h>
2638

2739
/*
@@ -33,6 +45,76 @@
3345
#define __efi_call_virt(f, args...) \
3446
__efi_call_virt_pointer(efi.systab->runtime, f, args)
3547

48+
/* efi_runtime_service() function identifiers */
49+
enum efi_rts_ids {
50+
GET_TIME,
51+
SET_TIME,
52+
GET_WAKEUP_TIME,
53+
SET_WAKEUP_TIME,
54+
GET_VARIABLE,
55+
GET_NEXT_VARIABLE,
56+
SET_VARIABLE,
57+
QUERY_VARIABLE_INFO,
58+
GET_NEXT_HIGH_MONO_COUNT,
59+
UPDATE_CAPSULE,
60+
QUERY_CAPSULE_CAPS,
61+
};
62+
63+
/*
64+
* efi_runtime_work: Details of EFI Runtime Service work
65+
* @arg<1-5>: EFI Runtime Service function arguments
66+
* @status: Status of executing EFI Runtime Service
67+
* @efi_rts_id: EFI Runtime Service function identifier
68+
* @efi_rts_comp: Struct used for handling completions
69+
*/
70+
struct efi_runtime_work {
71+
void *arg1;
72+
void *arg2;
73+
void *arg3;
74+
void *arg4;
75+
void *arg5;
76+
efi_status_t status;
77+
struct work_struct work;
78+
enum efi_rts_ids efi_rts_id;
79+
struct completion efi_rts_comp;
80+
};
81+
82+
/*
83+
* efi_queue_work: Queue efi_runtime_service() and wait until it's done
84+
* @rts: efi_runtime_service() function identifier
85+
* @rts_arg<1-5>: efi_runtime_service() function arguments
86+
*
87+
* Accesses to efi_runtime_services() are serialized by a binary
88+
* semaphore (efi_runtime_lock) and caller waits until the work is
89+
* finished, hence _only_ one work is queued at a time and the caller
90+
* thread waits for completion.
91+
*/
92+
#define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \
93+
({ \
94+
struct efi_runtime_work efi_rts_work; \
95+
efi_rts_work.status = EFI_ABORTED; \
96+
\
97+
init_completion(&efi_rts_work.efi_rts_comp); \
98+
INIT_WORK_ONSTACK(&efi_rts_work.work, efi_call_rts); \
99+
efi_rts_work.arg1 = _arg1; \
100+
efi_rts_work.arg2 = _arg2; \
101+
efi_rts_work.arg3 = _arg3; \
102+
efi_rts_work.arg4 = _arg4; \
103+
efi_rts_work.arg5 = _arg5; \
104+
efi_rts_work.efi_rts_id = _rts; \
105+
\
106+
/* \
107+
* queue_work() returns 0 if work was already on queue, \
108+
* _ideally_ this should never happen. \
109+
*/ \
110+
if (queue_work(efi_rts_wq, &efi_rts_work.work)) \
111+
wait_for_completion(&efi_rts_work.efi_rts_comp); \
112+
else \
113+
pr_err("Failed to queue work to efi_rts_wq.\n"); \
114+
\
115+
efi_rts_work.status; \
116+
})
117+
36118
void efi_call_virt_check_flags(unsigned long flags, const char *call)
37119
{
38120
unsigned long cur_flags, mismatch;
@@ -90,13 +172,98 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call)
90172
*/
91173
static DEFINE_SEMAPHORE(efi_runtime_lock);
92174

175+
/*
176+
* Calls the appropriate efi_runtime_service() with the appropriate
177+
* arguments.
178+
*
179+
* Semantics followed by efi_call_rts() to understand efi_runtime_work:
180+
* 1. If argument was a pointer, recast it from void pointer to original
181+
* pointer type.
182+
* 2. If argument was a value, recast it from void pointer to original
183+
* pointer type and dereference it.
184+
*/
185+
static void efi_call_rts(struct work_struct *work)
186+
{
187+
struct efi_runtime_work *efi_rts_work;
188+
void *arg1, *arg2, *arg3, *arg4, *arg5;
189+
efi_status_t status = EFI_NOT_FOUND;
190+
191+
efi_rts_work = container_of(work, struct efi_runtime_work, work);
192+
arg1 = efi_rts_work->arg1;
193+
arg2 = efi_rts_work->arg2;
194+
arg3 = efi_rts_work->arg3;
195+
arg4 = efi_rts_work->arg4;
196+
arg5 = efi_rts_work->arg5;
197+
198+
switch (efi_rts_work->efi_rts_id) {
199+
case GET_TIME:
200+
status = efi_call_virt(get_time, (efi_time_t *)arg1,
201+
(efi_time_cap_t *)arg2);
202+
break;
203+
case SET_TIME:
204+
status = efi_call_virt(set_time, (efi_time_t *)arg1);
205+
break;
206+
case GET_WAKEUP_TIME:
207+
status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1,
208+
(efi_bool_t *)arg2, (efi_time_t *)arg3);
209+
break;
210+
case SET_WAKEUP_TIME:
211+
status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1,
212+
(efi_time_t *)arg2);
213+
break;
214+
case GET_VARIABLE:
215+
status = efi_call_virt(get_variable, (efi_char16_t *)arg1,
216+
(efi_guid_t *)arg2, (u32 *)arg3,
217+
(unsigned long *)arg4, (void *)arg5);
218+
break;
219+
case GET_NEXT_VARIABLE:
220+
status = efi_call_virt(get_next_variable, (unsigned long *)arg1,
221+
(efi_char16_t *)arg2,
222+
(efi_guid_t *)arg3);
223+
break;
224+
case SET_VARIABLE:
225+
status = efi_call_virt(set_variable, (efi_char16_t *)arg1,
226+
(efi_guid_t *)arg2, *(u32 *)arg3,
227+
*(unsigned long *)arg4, (void *)arg5);
228+
break;
229+
case QUERY_VARIABLE_INFO:
230+
status = efi_call_virt(query_variable_info, *(u32 *)arg1,
231+
(u64 *)arg2, (u64 *)arg3, (u64 *)arg4);
232+
break;
233+
case GET_NEXT_HIGH_MONO_COUNT:
234+
status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1);
235+
break;
236+
case UPDATE_CAPSULE:
237+
status = efi_call_virt(update_capsule,
238+
(efi_capsule_header_t **)arg1,
239+
*(unsigned long *)arg2,
240+
*(unsigned long *)arg3);
241+
break;
242+
case QUERY_CAPSULE_CAPS:
243+
status = efi_call_virt(query_capsule_caps,
244+
(efi_capsule_header_t **)arg1,
245+
*(unsigned long *)arg2, (u64 *)arg3,
246+
(int *)arg4);
247+
break;
248+
default:
249+
/*
250+
* Ideally, we should never reach here because a caller of this
251+
* function should have put the right efi_runtime_service()
252+
* function identifier into efi_rts_work->efi_rts_id
253+
*/
254+
pr_err("Requested executing invalid EFI Runtime Service.\n");
255+
}
256+
efi_rts_work->status = status;
257+
complete(&efi_rts_work->efi_rts_comp);
258+
}
259+
93260
static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
94261
{
95262
efi_status_t status;
96263

97264
if (down_interruptible(&efi_runtime_lock))
98265
return EFI_ABORTED;
99-
status = efi_call_virt(get_time, tm, tc);
266+
status = efi_queue_work(GET_TIME, tm, tc, NULL, NULL, NULL);
100267
up(&efi_runtime_lock);
101268
return status;
102269
}
@@ -107,7 +274,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
107274

108275
if (down_interruptible(&efi_runtime_lock))
109276
return EFI_ABORTED;
110-
status = efi_call_virt(set_time, tm);
277+
status = efi_queue_work(SET_TIME, tm, NULL, NULL, NULL, NULL);
111278
up(&efi_runtime_lock);
112279
return status;
113280
}
@@ -120,7 +287,8 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
120287

121288
if (down_interruptible(&efi_runtime_lock))
122289
return EFI_ABORTED;
123-
status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
290+
status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm, NULL,
291+
NULL);
124292
up(&efi_runtime_lock);
125293
return status;
126294
}
@@ -131,7 +299,8 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
131299

132300
if (down_interruptible(&efi_runtime_lock))
133301
return EFI_ABORTED;
134-
status = efi_call_virt(set_wakeup_time, enabled, tm);
302+
status = efi_queue_work(SET_WAKEUP_TIME, &enabled, tm, NULL, NULL,
303+
NULL);
135304
up(&efi_runtime_lock);
136305
return status;
137306
}
@@ -146,8 +315,8 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
146315

147316
if (down_interruptible(&efi_runtime_lock))
148317
return EFI_ABORTED;
149-
status = efi_call_virt(get_variable, name, vendor, attr, data_size,
150-
data);
318+
status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size,
319+
data);
151320
up(&efi_runtime_lock);
152321
return status;
153322
}
@@ -160,7 +329,8 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
160329

161330
if (down_interruptible(&efi_runtime_lock))
162331
return EFI_ABORTED;
163-
status = efi_call_virt(get_next_variable, name_size, name, vendor);
332+
status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor,
333+
NULL, NULL);
164334
up(&efi_runtime_lock);
165335
return status;
166336
}
@@ -175,8 +345,8 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
175345

176346
if (down_interruptible(&efi_runtime_lock))
177347
return EFI_ABORTED;
178-
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
179-
data);
348+
status = efi_queue_work(SET_VARIABLE, name, vendor, &attr, &data_size,
349+
data);
180350
up(&efi_runtime_lock);
181351
return status;
182352
}
@@ -210,8 +380,8 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
210380

211381
if (down_interruptible(&efi_runtime_lock))
212382
return EFI_ABORTED;
213-
status = efi_call_virt(query_variable_info, attr, storage_space,
214-
remaining_space, max_variable_size);
383+
status = efi_queue_work(QUERY_VARIABLE_INFO, &attr, storage_space,
384+
remaining_space, max_variable_size, NULL);
215385
up(&efi_runtime_lock);
216386
return status;
217387
}
@@ -242,7 +412,8 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
242412

243413
if (down_interruptible(&efi_runtime_lock))
244414
return EFI_ABORTED;
245-
status = efi_call_virt(get_next_high_mono_count, count);
415+
status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL,
416+
NULL, NULL);
246417
up(&efi_runtime_lock);
247418
return status;
248419
}
@@ -272,7 +443,8 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
272443

273444
if (down_interruptible(&efi_runtime_lock))
274445
return EFI_ABORTED;
275-
status = efi_call_virt(update_capsule, capsules, count, sg_list);
446+
status = efi_queue_work(UPDATE_CAPSULE, capsules, &count, &sg_list,
447+
NULL, NULL);
276448
up(&efi_runtime_lock);
277449
return status;
278450
}
@@ -289,8 +461,8 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
289461

290462
if (down_interruptible(&efi_runtime_lock))
291463
return EFI_ABORTED;
292-
status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
293-
reset_type);
464+
status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, &count,
465+
max_size, reset_type, NULL);
294466
up(&efi_runtime_lock);
295467
return status;
296468
}

include/linux/efi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,4 +1651,7 @@ struct linux_efi_tpm_eventlog {
16511651

16521652
extern int efi_tpm_eventlog_init(void);
16531653

1654+
/* Workqueue to queue EFI Runtime Services */
1655+
extern struct workqueue_struct *efi_rts_wq;
1656+
16541657
#endif /* _LINUX_EFI_H */

0 commit comments

Comments
 (0)