Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <nivedita@alum.mit.edu>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Matthew Garrett <mjg59@google.com>
Cc: linux-efi@vger.kernel.org
Link: https://lkml.kernel.org/r/20200103113953.9571-17-ardb@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Arvind Sankar and committed by
Ingo Molnar
ea7d87f9 14b864f4

+67 -25
-16
arch/x86/boot/compressed/eboot.c
··· 891 891 for (;;) 892 892 asm("hlt"); 893 893 } 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
+66 -5
arch/x86/include/asm/efi.h
··· 243 243 : (__typeof__(inst->attr)) \ 244 244 efi_mixed_mode_cast(inst->mixed_mode.attr)) 245 245 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 + 246 307 #define efi_call_proto(inst, func, ...) \ 247 308 (efi_is_native() \ 248 309 ? inst->func(inst, ##__VA_ARGS__) \ 249 - : efi64_thunk(inst->mixed_mode.func, inst, ##__VA_ARGS__)) 310 + : __efi64_thunk_map(inst, func, inst, ##__VA_ARGS__)) 250 311 251 312 #define efi_bs_call(func, ...) \ 252 313 (efi_is_native() \ 253 314 ? 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__)) 256 317 257 318 #define efi_rt_call(func, ...) \ 258 319 (efi_is_native() \ 259 320 ? 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__)) 262 323 263 324 extern bool efi_reboot_required(void); 264 325 extern bool efi_is_table_address(unsigned long phys_addr);
+1 -4
drivers/firmware/efi/libstub/efi-stub-helper.c
··· 344 344 } 345 345 346 346 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) 350 347 { 351 348 unsigned long nr_pages; 352 349 ··· 351 354 return; 352 355 353 356 nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; 354 - efi_system_table()->boottime->free_pages(addr, nr_pages); 357 + efi_bs_call(free_pages, addr, nr_pages); 355 358 } 356 359 357 360 static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,