Skip to content

Commit ea7d87f

Browse files
nivedita76Ingo Molnar
authored andcommitted
efi/x86: Allow translating 64-bit arguments for mixed mode calls
Introduce the ability to define macros to perform argument translation for the calls that need it, and define them for the boot services that we currently use. When calling 32-bit firmware methods in mixed mode, all output parameters that are 32-bit according to the firmware, but 64-bit in the kernel (ie OUT UINTN * or OUT VOID **) must be initialized in the kernel, or the upper 32 bits may contain garbage. Define macros that zero out the upper 32 bits of the output before invoking the firmware method. When a 32-bit EFI call takes 64-bit arguments, the mixed-mode call must push the two 32-bit halves as separate arguments onto the stack. This can be achieved by splitting the argument into its two halves when calling the assembler thunk. Define a macro to do this for the free_pages boot service. Signed-off-by: Arvind Sankar <[email protected]> Signed-off-by: Ard Biesheuvel <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Matthew Garrett <[email protected]> Cc: [email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 14b864f commit ea7d87f

File tree

3 files changed

+67
-25
lines changed

3 files changed

+67
-25
lines changed

arch/x86/boot/compressed/eboot.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -891,19 +891,3 @@ struct boot_params *efi_main(efi_handle_t handle,
891891
for (;;)
892892
asm("hlt");
893893
}
894-
895-
#ifdef CONFIG_EFI_MIXED
896-
void efi_free_native(unsigned long size, unsigned long addr);
897-
898-
void efi_free(unsigned long size, unsigned long addr)
899-
{
900-
if (!size)
901-
return;
902-
903-
if (efi_is_native())
904-
efi_free_native(size, addr);
905-
else
906-
efi64_thunk(efi_system_table()->boottime->mixed_mode.free_pages,
907-
addr, 0, DIV_ROUND_UP(size, EFI_PAGE_SIZE));
908-
}
909-
#endif

arch/x86/include/asm/efi.h

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,22 +243,83 @@ static inline bool efi_is_native(void)
243243
: (__typeof__(inst->attr)) \
244244
efi_mixed_mode_cast(inst->mixed_mode.attr))
245245

246+
/*
247+
* The following macros allow translating arguments if necessary from native to
248+
* mixed mode. The use case for this is to initialize the upper 32 bits of
249+
* output parameters, and where the 32-bit method requires a 64-bit argument,
250+
* which must be split up into two arguments to be thunked properly.
251+
*
252+
* As examples, the AllocatePool boot service returns the address of the
253+
* allocation, but it will not set the high 32 bits of the address. To ensure
254+
* that the full 64-bit address is initialized, we zero-init the address before
255+
* calling the thunk.
256+
*
257+
* The FreePages boot service takes a 64-bit physical address even in 32-bit
258+
* mode. For the thunk to work correctly, a native 64-bit call of
259+
* free_pages(addr, size)
260+
* must be translated to
261+
* efi64_thunk(free_pages, addr & U32_MAX, addr >> 32, size)
262+
* so that the two 32-bit halves of addr get pushed onto the stack separately.
263+
*/
264+
265+
static inline void *efi64_zero_upper(void *p)
266+
{
267+
((u32 *)p)[1] = 0;
268+
return p;
269+
}
270+
271+
#define __efi64_argmap_free_pages(addr, size) \
272+
((addr), 0, (size))
273+
274+
#define __efi64_argmap_get_memory_map(mm_size, mm, key, size, ver) \
275+
((mm_size), (mm), efi64_zero_upper(key), efi64_zero_upper(size), (ver))
276+
277+
#define __efi64_argmap_allocate_pool(type, size, buffer) \
278+
((type), (size), efi64_zero_upper(buffer))
279+
280+
#define __efi64_argmap_handle_protocol(handle, protocol, interface) \
281+
((handle), (protocol), efi64_zero_upper(interface))
282+
283+
#define __efi64_argmap_locate_protocol(protocol, reg, interface) \
284+
((protocol), (reg), efi64_zero_upper(interface))
285+
286+
/*
287+
* The macros below handle the plumbing for the argument mapping. To add a
288+
* mapping for a specific EFI method, simply define a macro
289+
* __efi64_argmap_<method name>, following the examples above.
290+
*/
291+
292+
#define __efi64_thunk_map(inst, func, ...) \
293+
efi64_thunk(inst->mixed_mode.func, \
294+
__efi64_argmap(__efi64_argmap_ ## func(__VA_ARGS__), \
295+
(__VA_ARGS__)))
296+
297+
#define __efi64_argmap(mapped, args) \
298+
__PASTE(__efi64_argmap__, __efi_nargs(__efi_eat mapped))(mapped, args)
299+
#define __efi64_argmap__0(mapped, args) __efi_eval mapped
300+
#define __efi64_argmap__1(mapped, args) __efi_eval args
301+
302+
#define __efi_eat(...)
303+
#define __efi_eval(...) __VA_ARGS__
304+
305+
/* The three macros below handle dispatching via the thunk if needed */
306+
246307
#define efi_call_proto(inst, func, ...) \
247308
(efi_is_native() \
248309
? inst->func(inst, ##__VA_ARGS__) \
249-
: efi64_thunk(inst->mixed_mode.func, inst, ##__VA_ARGS__))
310+
: __efi64_thunk_map(inst, func, inst, ##__VA_ARGS__))
250311

251312
#define efi_bs_call(func, ...) \
252313
(efi_is_native() \
253314
? efi_system_table()->boottime->func(__VA_ARGS__) \
254-
: efi64_thunk(efi_table_attr(efi_system_table(), \
255-
boottime)->mixed_mode.func, __VA_ARGS__))
315+
: __efi64_thunk_map(efi_table_attr(efi_system_table(), \
316+
boottime), func, __VA_ARGS__))
256317

257318
#define efi_rt_call(func, ...) \
258319
(efi_is_native() \
259320
? efi_system_table()->runtime->func(__VA_ARGS__) \
260-
: efi64_thunk(efi_table_attr(efi_system_table(), \
261-
runtime)->mixed_mode.func, __VA_ARGS__))
321+
: __efi64_thunk_map(efi_table_attr(efi_system_table(), \
322+
runtime), func, __VA_ARGS__))
262323

263324
extern bool efi_reboot_required(void);
264325
extern bool efi_is_table_address(unsigned long phys_addr);

drivers/firmware/efi/libstub/efi-stub-helper.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,17 +344,14 @@ efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
344344
}
345345

346346
void efi_free(unsigned long size, unsigned long addr)
347-
__weak __alias(efi_free_native);
348-
349-
void efi_free_native(unsigned long size, unsigned long addr)
350347
{
351348
unsigned long nr_pages;
352349

353350
if (!size)
354351
return;
355352

356353
nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
357-
efi_system_table()->boottime->free_pages(addr, nr_pages);
354+
efi_bs_call(free_pages, addr, nr_pages);
358355
}
359356

360357
static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,

0 commit comments

Comments
 (0)