Skip to content

Commit c7c7bce

Browse files
committed
efi/runtime-wrappers: Use type safe encapsulation of call arguments
The current code that marshalls the EFI runtime call arguments to hand them off to a async helper does so in a type unsafe and slightly messy manner - everything is cast to void* except for some integral types that are passed by reference and dereferenced on the receiver end. Let's clean this up a bit, and record the arguments of each runtime service invocation exactly as they are issued, in a manner that permits the compiler to check the types of the arguments at both ends. Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
1 parent d8ea2ff commit c7c7bce

File tree

2 files changed

+138
-76
lines changed

2 files changed

+138
-76
lines changed

drivers/firmware/efi/runtime-wrappers.c

Lines changed: 130 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,90 @@
4444
#define __efi_call_virt(f, args...) \
4545
__efi_call_virt_pointer(efi.runtime, f, args)
4646

47+
union efi_rts_args {
48+
struct {
49+
efi_time_t *time;
50+
efi_time_cap_t *capabilities;
51+
} GET_TIME;
52+
53+
struct {
54+
efi_time_t *time;
55+
} SET_TIME;
56+
57+
struct {
58+
efi_bool_t *enabled;
59+
efi_bool_t *pending;
60+
efi_time_t *time;
61+
} GET_WAKEUP_TIME;
62+
63+
struct {
64+
efi_bool_t enable;
65+
efi_time_t *time;
66+
} SET_WAKEUP_TIME;
67+
68+
struct {
69+
efi_char16_t *name;
70+
efi_guid_t *vendor;
71+
u32 *attr;
72+
unsigned long *data_size;
73+
void *data;
74+
} GET_VARIABLE;
75+
76+
struct {
77+
unsigned long *name_size;
78+
efi_char16_t *name;
79+
efi_guid_t *vendor;
80+
} GET_NEXT_VARIABLE;
81+
82+
struct {
83+
efi_char16_t *name;
84+
efi_guid_t *vendor;
85+
u32 attr;
86+
unsigned long data_size;
87+
void *data;
88+
} SET_VARIABLE;
89+
90+
struct {
91+
u32 attr;
92+
u64 *storage_space;
93+
u64 *remaining_space;
94+
u64 *max_variable_size;
95+
} QUERY_VARIABLE_INFO;
96+
97+
struct {
98+
u32 *high_count;
99+
} GET_NEXT_HIGH_MONO_COUNT;
100+
101+
struct {
102+
efi_capsule_header_t **capsules;
103+
unsigned long count;
104+
unsigned long sg_list;
105+
} UPDATE_CAPSULE;
106+
107+
struct {
108+
efi_capsule_header_t **capsules;
109+
unsigned long count;
110+
u64 *max_size;
111+
int *reset_type;
112+
} QUERY_CAPSULE_CAPS;
113+
};
114+
47115
struct efi_runtime_work efi_rts_work;
48116

49117
/*
50-
* efi_queue_work: Queue efi_runtime_service() and wait until it's done
51-
* @rts: efi_runtime_service() function identifier
52-
* @rts_arg<1-5>: efi_runtime_service() function arguments
118+
* efi_queue_work: Queue EFI runtime service call and wait for completion
119+
* @_rts: EFI runtime service function identifier
120+
* @_args: Arguments to pass to the EFI runtime service
53121
*
54122
* Accesses to efi_runtime_services() are serialized by a binary
55123
* semaphore (efi_runtime_lock) and caller waits until the work is
56124
* finished, hence _only_ one work is queued at a time and the caller
57125
* thread waits for completion.
58126
*/
59-
#define efi_queue_work(_rts, _arg1, _arg2, _arg3, _arg4, _arg5) \
127+
#define efi_queue_work(_rts, _args...) \
60128
({ \
129+
efi_rts_work.efi_rts_id = EFI_ ## _rts; \
130+
efi_rts_work.args = &(union efi_rts_args){ ._rts = { _args }}; \
61131
efi_rts_work.status = EFI_ABORTED; \
62132
\
63133
if (!efi_enabled(EFI_RUNTIME_SERVICES)) { \
@@ -68,12 +138,6 @@ struct efi_runtime_work efi_rts_work;
68138
\
69139
init_completion(&efi_rts_work.efi_rts_comp); \
70140
INIT_WORK(&efi_rts_work.work, efi_call_rts); \
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; \
77141
\
78142
/* \
79143
* queue_work() returns 0 if work was already on queue, \
@@ -170,73 +234,78 @@ extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock);
170234
/*
171235
* Calls the appropriate efi_runtime_service() with the appropriate
172236
* arguments.
173-
*
174-
* Semantics followed by efi_call_rts() to understand efi_runtime_work:
175-
* 1. If argument was a pointer, recast it from void pointer to original
176-
* pointer type.
177-
* 2. If argument was a value, recast it from void pointer to original
178-
* pointer type and dereference it.
179237
*/
180238
static void efi_call_rts(struct work_struct *work)
181239
{
182-
void *arg1, *arg2, *arg3, *arg4, *arg5;
240+
const union efi_rts_args *args = efi_rts_work.args;
183241
efi_status_t status = EFI_NOT_FOUND;
184242

185-
arg1 = efi_rts_work.arg1;
186-
arg2 = efi_rts_work.arg2;
187-
arg3 = efi_rts_work.arg3;
188-
arg4 = efi_rts_work.arg4;
189-
arg5 = efi_rts_work.arg5;
190-
191243
switch (efi_rts_work.efi_rts_id) {
192244
case EFI_GET_TIME:
193-
status = efi_call_virt(get_time, (efi_time_t *)arg1,
194-
(efi_time_cap_t *)arg2);
245+
status = efi_call_virt(get_time,
246+
args->GET_TIME.time,
247+
args->GET_TIME.capabilities);
195248
break;
196249
case EFI_SET_TIME:
197-
status = efi_call_virt(set_time, (efi_time_t *)arg1);
250+
status = efi_call_virt(set_time,
251+
args->SET_TIME.time);
198252
break;
199253
case EFI_GET_WAKEUP_TIME:
200-
status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1,
201-
(efi_bool_t *)arg2, (efi_time_t *)arg3);
254+
status = efi_call_virt(get_wakeup_time,
255+
args->GET_WAKEUP_TIME.enabled,
256+
args->GET_WAKEUP_TIME.pending,
257+
args->GET_WAKEUP_TIME.time);
202258
break;
203259
case EFI_SET_WAKEUP_TIME:
204-
status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1,
205-
(efi_time_t *)arg2);
260+
status = efi_call_virt(set_wakeup_time,
261+
args->SET_WAKEUP_TIME.enable,
262+
args->SET_WAKEUP_TIME.time);
206263
break;
207264
case EFI_GET_VARIABLE:
208-
status = efi_call_virt(get_variable, (efi_char16_t *)arg1,
209-
(efi_guid_t *)arg2, (u32 *)arg3,
210-
(unsigned long *)arg4, (void *)arg5);
265+
status = efi_call_virt(get_variable,
266+
args->GET_VARIABLE.name,
267+
args->GET_VARIABLE.vendor,
268+
args->GET_VARIABLE.attr,
269+
args->GET_VARIABLE.data_size,
270+
args->GET_VARIABLE.data);
211271
break;
212272
case EFI_GET_NEXT_VARIABLE:
213-
status = efi_call_virt(get_next_variable, (unsigned long *)arg1,
214-
(efi_char16_t *)arg2,
215-
(efi_guid_t *)arg3);
273+
status = efi_call_virt(get_next_variable,
274+
args->GET_NEXT_VARIABLE.name_size,
275+
args->GET_NEXT_VARIABLE.name,
276+
args->GET_NEXT_VARIABLE.vendor);
216277
break;
217278
case EFI_SET_VARIABLE:
218-
status = efi_call_virt(set_variable, (efi_char16_t *)arg1,
219-
(efi_guid_t *)arg2, *(u32 *)arg3,
220-
*(unsigned long *)arg4, (void *)arg5);
279+
status = efi_call_virt(set_variable,
280+
args->SET_VARIABLE.name,
281+
args->SET_VARIABLE.vendor,
282+
args->SET_VARIABLE.attr,
283+
args->SET_VARIABLE.data_size,
284+
args->SET_VARIABLE.data);
221285
break;
222286
case EFI_QUERY_VARIABLE_INFO:
223-
status = efi_call_virt(query_variable_info, *(u32 *)arg1,
224-
(u64 *)arg2, (u64 *)arg3, (u64 *)arg4);
287+
status = efi_call_virt(query_variable_info,
288+
args->QUERY_VARIABLE_INFO.attr,
289+
args->QUERY_VARIABLE_INFO.storage_space,
290+
args->QUERY_VARIABLE_INFO.remaining_space,
291+
args->QUERY_VARIABLE_INFO.max_variable_size);
225292
break;
226293
case EFI_GET_NEXT_HIGH_MONO_COUNT:
227-
status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1);
294+
status = efi_call_virt(get_next_high_mono_count,
295+
args->GET_NEXT_HIGH_MONO_COUNT.high_count);
228296
break;
229297
case EFI_UPDATE_CAPSULE:
230298
status = efi_call_virt(update_capsule,
231-
(efi_capsule_header_t **)arg1,
232-
*(unsigned long *)arg2,
233-
*(unsigned long *)arg3);
299+
args->UPDATE_CAPSULE.capsules,
300+
args->UPDATE_CAPSULE.count,
301+
args->UPDATE_CAPSULE.sg_list);
234302
break;
235303
case EFI_QUERY_CAPSULE_CAPS:
236304
status = efi_call_virt(query_capsule_caps,
237-
(efi_capsule_header_t **)arg1,
238-
*(unsigned long *)arg2, (u64 *)arg3,
239-
(int *)arg4);
305+
args->QUERY_CAPSULE_CAPS.capsules,
306+
args->QUERY_CAPSULE_CAPS.count,
307+
args->QUERY_CAPSULE_CAPS.max_size,
308+
args->QUERY_CAPSULE_CAPS.reset_type);
240309
break;
241310
default:
242311
/*
@@ -256,7 +325,7 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
256325

257326
if (down_interruptible(&efi_runtime_lock))
258327
return EFI_ABORTED;
259-
status = efi_queue_work(EFI_GET_TIME, tm, tc, NULL, NULL, NULL);
328+
status = efi_queue_work(GET_TIME, tm, tc);
260329
up(&efi_runtime_lock);
261330
return status;
262331
}
@@ -267,7 +336,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
267336

268337
if (down_interruptible(&efi_runtime_lock))
269338
return EFI_ABORTED;
270-
status = efi_queue_work(EFI_SET_TIME, tm, NULL, NULL, NULL, NULL);
339+
status = efi_queue_work(SET_TIME, tm);
271340
up(&efi_runtime_lock);
272341
return status;
273342
}
@@ -280,8 +349,7 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
280349

281350
if (down_interruptible(&efi_runtime_lock))
282351
return EFI_ABORTED;
283-
status = efi_queue_work(EFI_GET_WAKEUP_TIME, enabled, pending, tm, NULL,
284-
NULL);
352+
status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm);
285353
up(&efi_runtime_lock);
286354
return status;
287355
}
@@ -292,8 +360,7 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
292360

293361
if (down_interruptible(&efi_runtime_lock))
294362
return EFI_ABORTED;
295-
status = efi_queue_work(EFI_SET_WAKEUP_TIME, &enabled, tm, NULL, NULL,
296-
NULL);
363+
status = efi_queue_work(SET_WAKEUP_TIME, enabled, tm);
297364
up(&efi_runtime_lock);
298365
return status;
299366
}
@@ -308,7 +375,7 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
308375

309376
if (down_interruptible(&efi_runtime_lock))
310377
return EFI_ABORTED;
311-
status = efi_queue_work(EFI_GET_VARIABLE, name, vendor, attr, data_size,
378+
status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size,
312379
data);
313380
up(&efi_runtime_lock);
314381
return status;
@@ -322,8 +389,7 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
322389

323390
if (down_interruptible(&efi_runtime_lock))
324391
return EFI_ABORTED;
325-
status = efi_queue_work(EFI_GET_NEXT_VARIABLE, name_size, name, vendor,
326-
NULL, NULL);
392+
status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor);
327393
up(&efi_runtime_lock);
328394
return status;
329395
}
@@ -338,7 +404,7 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
338404

339405
if (down_interruptible(&efi_runtime_lock))
340406
return EFI_ABORTED;
341-
status = efi_queue_work(EFI_SET_VARIABLE, name, vendor, &attr, &data_size,
407+
status = efi_queue_work(SET_VARIABLE, name, vendor, attr, data_size,
342408
data);
343409
up(&efi_runtime_lock);
344410
return status;
@@ -373,8 +439,8 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
373439

374440
if (down_interruptible(&efi_runtime_lock))
375441
return EFI_ABORTED;
376-
status = efi_queue_work(EFI_QUERY_VARIABLE_INFO, &attr, storage_space,
377-
remaining_space, max_variable_size, NULL);
442+
status = efi_queue_work(QUERY_VARIABLE_INFO, attr, storage_space,
443+
remaining_space, max_variable_size);
378444
up(&efi_runtime_lock);
379445
return status;
380446
}
@@ -405,8 +471,7 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
405471

406472
if (down_interruptible(&efi_runtime_lock))
407473
return EFI_ABORTED;
408-
status = efi_queue_work(EFI_GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL,
409-
NULL, NULL);
474+
status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count);
410475
up(&efi_runtime_lock);
411476
return status;
412477
}
@@ -437,8 +502,7 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
437502

438503
if (down_interruptible(&efi_runtime_lock))
439504
return EFI_ABORTED;
440-
status = efi_queue_work(EFI_UPDATE_CAPSULE, capsules, &count, &sg_list,
441-
NULL, NULL);
505+
status = efi_queue_work(UPDATE_CAPSULE, capsules, count, sg_list);
442506
up(&efi_runtime_lock);
443507
return status;
444508
}
@@ -455,8 +519,8 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
455519

456520
if (down_interruptible(&efi_runtime_lock))
457521
return EFI_ABORTED;
458-
status = efi_queue_work(EFI_QUERY_CAPSULE_CAPS, capsules, &count,
459-
max_size, reset_type, NULL);
522+
status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, count,
523+
max_size, reset_type);
460524
up(&efi_runtime_lock);
461525
return status;
462526
}

include/linux/efi.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,23 +1264,21 @@ enum efi_rts_ids {
12641264
EFI_QUERY_CAPSULE_CAPS,
12651265
};
12661266

1267+
union efi_rts_args;
1268+
12671269
/*
12681270
* efi_runtime_work: Details of EFI Runtime Service work
1269-
* @arg<1-5>: EFI Runtime Service function arguments
1271+
* @args: Pointer to union describing the arguments
12701272
* @status: Status of executing EFI Runtime Service
12711273
* @efi_rts_id: EFI Runtime Service function identifier
12721274
* @efi_rts_comp: Struct used for handling completions
12731275
*/
12741276
struct efi_runtime_work {
1275-
void *arg1;
1276-
void *arg2;
1277-
void *arg3;
1278-
void *arg4;
1279-
void *arg5;
1280-
efi_status_t status;
1281-
struct work_struct work;
1282-
enum efi_rts_ids efi_rts_id;
1283-
struct completion efi_rts_comp;
1277+
union efi_rts_args *args;
1278+
efi_status_t status;
1279+
struct work_struct work;
1280+
enum efi_rts_ids efi_rts_id;
1281+
struct completion efi_rts_comp;
12841282
};
12851283

12861284
extern struct efi_runtime_work efi_rts_work;

0 commit comments

Comments
 (0)