xref: /openbmc/linux/arch/x86/platform/efi/efi_64.c (revision 5804c19b80bf625c6a9925317f845e497434d6d3)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b17ed480SThomas Gleixner /*
3b17ed480SThomas Gleixner  * x86_64 specific EFI support functions
4b17ed480SThomas Gleixner  * Based on Extensible Firmware Interface Specification version 1.0
5b17ed480SThomas Gleixner  *
6b17ed480SThomas Gleixner  * Copyright (C) 2005-2008 Intel Co.
7b17ed480SThomas Gleixner  *	Fenghua Yu <fenghua.yu@intel.com>
8b17ed480SThomas Gleixner  *	Bibo Mao <bibo.mao@intel.com>
9b17ed480SThomas Gleixner  *	Chandramouli Narayanan <mouli@linux.intel.com>
10b17ed480SThomas Gleixner  *	Huang Ying <ying.huang@intel.com>
11b17ed480SThomas Gleixner  *
12b17ed480SThomas Gleixner  * Code to convert EFI to E820 map has been implemented in elilo bootloader
13b17ed480SThomas Gleixner  * based on a EFI patch by Edgar Hucek. Based on the E820 map, the page table
14b17ed480SThomas Gleixner  * is setup appropriately for EFI runtime code.
15b17ed480SThomas Gleixner  * - mouli 06/14/2007.
16b17ed480SThomas Gleixner  *
17b17ed480SThomas Gleixner  */
18b17ed480SThomas Gleixner 
1926d7f65fSMatt Fleming #define pr_fmt(fmt) "efi: " fmt
2026d7f65fSMatt Fleming 
21b17ed480SThomas Gleixner #include <linux/kernel.h>
22b17ed480SThomas Gleixner #include <linux/init.h>
23b17ed480SThomas Gleixner #include <linux/mm.h>
24b17ed480SThomas Gleixner #include <linux/types.h>
25b17ed480SThomas Gleixner #include <linux/spinlock.h>
2657c8a661SMike Rapoport #include <linux/memblock.h>
27b17ed480SThomas Gleixner #include <linux/ioport.h>
285ab788d7SArnd Bergmann #include <linux/mc146818rtc.h>
29b17ed480SThomas Gleixner #include <linux/efi.h>
30116fef64SAndy Lutomirski #include <linux/export.h>
31b17ed480SThomas Gleixner #include <linux/uaccess.h>
32b17ed480SThomas Gleixner #include <linux/io.h>
33b17ed480SThomas Gleixner #include <linux/reboot.h>
340d01ff25SDavid Howells #include <linux/slab.h>
35f6697df3SMatt Fleming #include <linux/ucs2_string.h>
364d96f910STom Lendacky #include <linux/cc_platform.h>
3703781e40SSai Praneeth #include <linux/sched/task.h>
38b17ed480SThomas Gleixner 
39b17ed480SThomas Gleixner #include <asm/setup.h>
40b17ed480SThomas Gleixner #include <asm/page.h>
4166441bd3SIngo Molnar #include <asm/e820/api.h>
42b17ed480SThomas Gleixner #include <asm/tlbflush.h>
43b17ed480SThomas Gleixner #include <asm/proto.h>
44b17ed480SThomas Gleixner #include <asm/efi.h>
45b17ed480SThomas Gleixner #include <asm/cacheflush.h>
46b17ed480SThomas Gleixner #include <asm/fixmap.h>
47d2f7cbe7SBorislav Petkov #include <asm/realmode.h>
484f9dbcfcSMatt Fleming #include <asm/time.h>
4967a9108eSMatt Fleming #include <asm/pgalloc.h>
50e759959fSBrijesh Singh #include <asm/sev.h>
51b17ed480SThomas Gleixner 
52d2f7cbe7SBorislav Petkov /*
53b1d17761SBaoquan He  * We allocate runtime services regions top-down, starting from -4G, i.e.
54d2f7cbe7SBorislav Petkov  * 0xffff_ffff_0000_0000 and limit EFI VA mapping space to 64G.
55d2f7cbe7SBorislav Petkov  */
568266e31eSMathias Krause static u64 efi_va = EFI_VA_START;
57514b1a84SArd Biesheuvel static struct mm_struct *efi_prev_mm;
5867a9108eSMatt Fleming 
5967a9108eSMatt Fleming /*
6067a9108eSMatt Fleming  * We need our own copy of the higher levels of the page tables
6167a9108eSMatt Fleming  * because we want to avoid inserting EFI region mappings (EFI_VA_END
6267a9108eSMatt Fleming  * to EFI_VA_START) into the standard kernel page tables. Everything
6367a9108eSMatt Fleming  * else can be shared, see efi_sync_low_kernel_mappings().
64d9e9a641SDave Hansen  *
65d9e9a641SDave Hansen  * We don't want the pgd on the pgd_list and cannot use pgd_alloc() for the
66d9e9a641SDave Hansen  * allocation.
6767a9108eSMatt Fleming  */
efi_alloc_page_tables(void)6867a9108eSMatt Fleming int __init efi_alloc_page_tables(void)
6967a9108eSMatt Fleming {
703ede3417SSai Praneeth 	pgd_t *pgd, *efi_pgd;
71e981316fSKirill A. Shutemov 	p4d_t *p4d;
7267a9108eSMatt Fleming 	pud_t *pud;
7367a9108eSMatt Fleming 	gfp_t gfp_mask;
7467a9108eSMatt Fleming 
7575f296d9SLevin, Alexander (Sasha Levin) 	gfp_mask = GFP_KERNEL | __GFP_ZERO;
76d9e9a641SDave Hansen 	efi_pgd = (pgd_t *)__get_free_pages(gfp_mask, PGD_ALLOCATION_ORDER);
7767a9108eSMatt Fleming 	if (!efi_pgd)
78c2fe61d8SArvind Sankar 		goto fail;
7967a9108eSMatt Fleming 
8067a9108eSMatt Fleming 	pgd = efi_pgd + pgd_index(EFI_VA_END);
81e981316fSKirill A. Shutemov 	p4d = p4d_alloc(&init_mm, pgd, EFI_VA_END);
82c2fe61d8SArvind Sankar 	if (!p4d)
83c2fe61d8SArvind Sankar 		goto free_pgd;
8467a9108eSMatt Fleming 
85e981316fSKirill A. Shutemov 	pud = pud_alloc(&init_mm, p4d, EFI_VA_END);
86c2fe61d8SArvind Sankar 	if (!pud)
87c2fe61d8SArvind Sankar 		goto free_p4d;
8867a9108eSMatt Fleming 
893ede3417SSai Praneeth 	efi_mm.pgd = efi_pgd;
907e904a91SSai Praneeth 	mm_init_cpumask(&efi_mm);
917e904a91SSai Praneeth 	init_new_context(NULL, &efi_mm);
927e904a91SSai Praneeth 
9367a9108eSMatt Fleming 	return 0;
94c2fe61d8SArvind Sankar 
95c2fe61d8SArvind Sankar free_p4d:
96c2fe61d8SArvind Sankar 	if (pgtable_l5_enabled())
97c2fe61d8SArvind Sankar 		free_page((unsigned long)pgd_page_vaddr(*pgd));
98c2fe61d8SArvind Sankar free_pgd:
99c2fe61d8SArvind Sankar 	free_pages((unsigned long)efi_pgd, PGD_ALLOCATION_ORDER);
100c2fe61d8SArvind Sankar fail:
101c2fe61d8SArvind Sankar 	return -ENOMEM;
10267a9108eSMatt Fleming }
10367a9108eSMatt Fleming 
104d2f7cbe7SBorislav Petkov /*
105d2f7cbe7SBorislav Petkov  * Add low kernel mappings for passing arguments to EFI functions.
106d2f7cbe7SBorislav Petkov  */
efi_sync_low_kernel_mappings(void)107d2f7cbe7SBorislav Petkov void efi_sync_low_kernel_mappings(void)
108d2f7cbe7SBorislav Petkov {
10967a9108eSMatt Fleming 	unsigned num_entries;
11067a9108eSMatt Fleming 	pgd_t *pgd_k, *pgd_efi;
111e0c4f675SKirill A. Shutemov 	p4d_t *p4d_k, *p4d_efi;
11267a9108eSMatt Fleming 	pud_t *pud_k, *pud_efi;
1133ede3417SSai Praneeth 	pgd_t *efi_pgd = efi_mm.pgd;
114d2f7cbe7SBorislav Petkov 
11567a9108eSMatt Fleming 	pgd_efi = efi_pgd + pgd_index(PAGE_OFFSET);
11667a9108eSMatt Fleming 	pgd_k = pgd_offset_k(PAGE_OFFSET);
11767a9108eSMatt Fleming 
11867a9108eSMatt Fleming 	num_entries = pgd_index(EFI_VA_END) - pgd_index(PAGE_OFFSET);
11967a9108eSMatt Fleming 	memcpy(pgd_efi, pgd_k, sizeof(pgd_t) * num_entries);
12067a9108eSMatt Fleming 
121e981316fSKirill A. Shutemov 	pgd_efi = efi_pgd + pgd_index(EFI_VA_END);
122e981316fSKirill A. Shutemov 	pgd_k = pgd_offset_k(EFI_VA_END);
123e981316fSKirill A. Shutemov 	p4d_efi = p4d_offset(pgd_efi, 0);
124e981316fSKirill A. Shutemov 	p4d_k = p4d_offset(pgd_k, 0);
125e981316fSKirill A. Shutemov 
126e981316fSKirill A. Shutemov 	num_entries = p4d_index(EFI_VA_END);
127e981316fSKirill A. Shutemov 	memcpy(p4d_efi, p4d_k, sizeof(p4d_t) * num_entries);
128e981316fSKirill A. Shutemov 
129e981316fSKirill A. Shutemov 	/*
13067a9108eSMatt Fleming 	 * We share all the PUD entries apart from those that map the
13167a9108eSMatt Fleming 	 * EFI regions. Copy around them.
13267a9108eSMatt Fleming 	 */
13367a9108eSMatt Fleming 	BUILD_BUG_ON((EFI_VA_START & ~PUD_MASK) != 0);
13467a9108eSMatt Fleming 	BUILD_BUG_ON((EFI_VA_END & ~PUD_MASK) != 0);
13567a9108eSMatt Fleming 
136e981316fSKirill A. Shutemov 	p4d_efi = p4d_offset(pgd_efi, EFI_VA_END);
137e981316fSKirill A. Shutemov 	p4d_k = p4d_offset(pgd_k, EFI_VA_END);
138e0c4f675SKirill A. Shutemov 	pud_efi = pud_offset(p4d_efi, 0);
139e0c4f675SKirill A. Shutemov 	pud_k = pud_offset(p4d_k, 0);
14067a9108eSMatt Fleming 
14167a9108eSMatt Fleming 	num_entries = pud_index(EFI_VA_END);
14267a9108eSMatt Fleming 	memcpy(pud_efi, pud_k, sizeof(pud_t) * num_entries);
14367a9108eSMatt Fleming 
144e0c4f675SKirill A. Shutemov 	pud_efi = pud_offset(p4d_efi, EFI_VA_START);
145e0c4f675SKirill A. Shutemov 	pud_k = pud_offset(p4d_k, EFI_VA_START);
14667a9108eSMatt Fleming 
14767a9108eSMatt Fleming 	num_entries = PTRS_PER_PUD - pud_index(EFI_VA_START);
14867a9108eSMatt Fleming 	memcpy(pud_efi, pud_k, sizeof(pud_t) * num_entries);
149d2f7cbe7SBorislav Petkov }
150d2f7cbe7SBorislav Petkov 
151f6697df3SMatt Fleming /*
152f6697df3SMatt Fleming  * Wrapper for slow_virt_to_phys() that handles NULL addresses.
153f6697df3SMatt Fleming  */
154f6697df3SMatt Fleming static inline phys_addr_t
virt_to_phys_or_null_size(void * va,unsigned long size)155f6697df3SMatt Fleming virt_to_phys_or_null_size(void *va, unsigned long size)
156f6697df3SMatt Fleming {
1578319e9d5SArd Biesheuvel 	phys_addr_t pa;
158f6697df3SMatt Fleming 
159f6697df3SMatt Fleming 	if (!va)
160f6697df3SMatt Fleming 		return 0;
161f6697df3SMatt Fleming 
162f6697df3SMatt Fleming 	if (virt_addr_valid(va))
163f6697df3SMatt Fleming 		return virt_to_phys(va);
164f6697df3SMatt Fleming 
1658319e9d5SArd Biesheuvel 	pa = slow_virt_to_phys(va);
166f6697df3SMatt Fleming 
1678319e9d5SArd Biesheuvel 	/* check if the object crosses a page boundary */
1688319e9d5SArd Biesheuvel 	if (WARN_ON((pa ^ (pa + size - 1)) & PAGE_MASK))
1698319e9d5SArd Biesheuvel 		return 0;
170f6697df3SMatt Fleming 
1718319e9d5SArd Biesheuvel 	return pa;
172f6697df3SMatt Fleming }
173f6697df3SMatt Fleming 
174f6697df3SMatt Fleming #define virt_to_phys_or_null(addr)				\
175f6697df3SMatt Fleming 	virt_to_phys_or_null_size((addr), sizeof(*(addr)))
176f6697df3SMatt Fleming 
efi_setup_page_tables(unsigned long pa_memmap,unsigned num_pages)1774e78eb05SMathias Krause int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
178d2f7cbe7SBorislav Petkov {
1796c3a9c9aSArd Biesheuvel 	extern const u8 __efi64_thunk_ret_tramp[];
1806c3a9c9aSArd Biesheuvel 	unsigned long pfn, text, pf, rodata, tramp;
1814f9dbcfcSMatt Fleming 	struct page *page;
182994448f1SMatt Fleming 	unsigned npages;
1833ede3417SSai Praneeth 	pgd_t *pgd = efi_mm.pgd;
184d2f7cbe7SBorislav Petkov 
18538eecccdSTom Lendacky 	/*
186b7b898aeSBorislav Petkov 	 * It can happen that the physical address of new_memmap lands in memory
187b7b898aeSBorislav Petkov 	 * which is not mapped in the EFI page table. Therefore we need to go
188b7b898aeSBorislav Petkov 	 * and ident-map those pages containing the map before calling
189b7b898aeSBorislav Petkov 	 * phys_efi_set_virtual_address_map().
190b7b898aeSBorislav Petkov 	 */
191edc3b912SMatt Fleming 	pfn = pa_memmap >> PAGE_SHIFT;
19238eecccdSTom Lendacky 	pf = _PAGE_NX | _PAGE_RW | _PAGE_ENC;
19338eecccdSTom Lendacky 	if (kernel_map_pages_in_pgd(pgd, pfn, pa_memmap, num_pages, pf)) {
194b7b898aeSBorislav Petkov 		pr_err("Error ident-mapping new memmap (0x%lx)!\n", pa_memmap);
195b7b898aeSBorislav Petkov 		return 1;
196b7b898aeSBorislav Petkov 	}
197b7b898aeSBorislav Petkov 
1984f9dbcfcSMatt Fleming 	/*
199d9f6e12fSIngo Molnar 	 * Certain firmware versions are way too sentimental and still believe
200bf29bddfSJiri Kosina 	 * they are exclusive and unquestionable owners of the first physical page,
201bf29bddfSJiri Kosina 	 * even though they explicitly mark it as EFI_CONVENTIONAL_MEMORY
202bf29bddfSJiri Kosina 	 * (but then write-access it later during SetVirtualAddressMap()).
203bf29bddfSJiri Kosina 	 *
204bf29bddfSJiri Kosina 	 * Create a 1:1 mapping for this page, to avoid triple faults during early
205bf29bddfSJiri Kosina 	 * boot with such firmware. We are free to hand this page to the BIOS,
206bf29bddfSJiri Kosina 	 * as trim_bios_range() will reserve the first page and isolate it away
207bf29bddfSJiri Kosina 	 * from memory allocators anyway.
208bf29bddfSJiri Kosina 	 */
2091379edd5STom Lendacky 	if (kernel_map_pages_in_pgd(pgd, 0x0, 0x0, 1, pf)) {
210bf29bddfSJiri Kosina 		pr_err("Failed to create 1:1 mapping for the first page!\n");
211bf29bddfSJiri Kosina 		return 1;
212bf29bddfSJiri Kosina 	}
213bf29bddfSJiri Kosina 
214bf29bddfSJiri Kosina 	/*
21539336f4fSTom Lendacky 	 * When SEV-ES is active, the GHCB as set by the kernel will be used
21639336f4fSTom Lendacky 	 * by firmware. Create a 1:1 unencrypted mapping for each GHCB.
21739336f4fSTom Lendacky 	 */
21839336f4fSTom Lendacky 	if (sev_es_efi_map_ghcbs(pgd)) {
21939336f4fSTom Lendacky 		pr_err("Failed to create 1:1 mapping for the GHCBs!\n");
22039336f4fSTom Lendacky 		return 1;
22139336f4fSTom Lendacky 	}
22239336f4fSTom Lendacky 
22339336f4fSTom Lendacky 	/*
2244f9dbcfcSMatt Fleming 	 * When making calls to the firmware everything needs to be 1:1
2254f9dbcfcSMatt Fleming 	 * mapped and addressable with 32-bit pointers. Map the kernel
2264f9dbcfcSMatt Fleming 	 * text and allocate a new stack because we can't rely on the
2274f9dbcfcSMatt Fleming 	 * stack pointer being < 4GB.
2284f9dbcfcSMatt Fleming 	 */
229a8147dbaSArd Biesheuvel 	if (!efi_is_mixed())
230994448f1SMatt Fleming 		return 0;
2314f9dbcfcSMatt Fleming 
2324f9dbcfcSMatt Fleming 	page = alloc_page(GFP_KERNEL|__GFP_DMA32);
233e2d68a95SArd Biesheuvel 	if (!page) {
234e2d68a95SArd Biesheuvel 		pr_err("Unable to allocate EFI runtime stack < 4GB\n");
235e2d68a95SArd Biesheuvel 		return 1;
236e2d68a95SArd Biesheuvel 	}
2374f9dbcfcSMatt Fleming 
2383e1e00c0SArd Biesheuvel 	efi_mixed_mode_stack_pa = page_to_phys(page + 1); /* stack grows down */
2394f9dbcfcSMatt Fleming 
240f6103162SArd Biesheuvel 	npages = (_etext - _text) >> PAGE_SHIFT;
2414f9dbcfcSMatt Fleming 	text = __pa(_text);
2424f9dbcfcSMatt Fleming 
2436c3a9c9aSArd Biesheuvel 	if (kernel_unmap_pages_in_pgd(pgd, text, npages)) {
2446c3a9c9aSArd Biesheuvel 		pr_err("Failed to unmap kernel text 1:1 mapping\n");
245994448f1SMatt Fleming 		return 1;
2464f9dbcfcSMatt Fleming 	}
247b7b898aeSBorislav Petkov 
248f6103162SArd Biesheuvel 	npages = (__end_rodata - __start_rodata) >> PAGE_SHIFT;
249f6103162SArd Biesheuvel 	rodata = __pa(__start_rodata);
250f6103162SArd Biesheuvel 	pfn = rodata >> PAGE_SHIFT;
251c8502eb2SArvind Sankar 
252c8502eb2SArvind Sankar 	pf = _PAGE_NX | _PAGE_ENC;
253f6103162SArd Biesheuvel 	if (kernel_map_pages_in_pgd(pgd, pfn, rodata, npages, pf)) {
254f6103162SArd Biesheuvel 		pr_err("Failed to map kernel rodata 1:1\n");
255f6103162SArd Biesheuvel 		return 1;
256f6103162SArd Biesheuvel 	}
257f6103162SArd Biesheuvel 
2586c3a9c9aSArd Biesheuvel 	tramp = __pa(__efi64_thunk_ret_tramp);
2596c3a9c9aSArd Biesheuvel 	pfn = tramp >> PAGE_SHIFT;
2606c3a9c9aSArd Biesheuvel 
2616c3a9c9aSArd Biesheuvel 	pf = _PAGE_ENC;
2626c3a9c9aSArd Biesheuvel 	if (kernel_map_pages_in_pgd(pgd, pfn, tramp, 1, pf)) {
2636c3a9c9aSArd Biesheuvel 		pr_err("Failed to map mixed mode return trampoline\n");
2646c3a9c9aSArd Biesheuvel 		return 1;
2656c3a9c9aSArd Biesheuvel 	}
2666c3a9c9aSArd Biesheuvel 
267b7b898aeSBorislav Petkov 	return 0;
268b7b898aeSBorislav Petkov }
269b7b898aeSBorislav Petkov 
__map_region(efi_memory_desc_t * md,u64 va)270d2f7cbe7SBorislav Petkov static void __init __map_region(efi_memory_desc_t *md, u64 va)
271d2f7cbe7SBorislav Petkov {
27215f003d2SSai Praneeth 	unsigned long flags = _PAGE_RW;
273edc3b912SMatt Fleming 	unsigned long pfn;
2743ede3417SSai Praneeth 	pgd_t *pgd = efi_mm.pgd;
275d2f7cbe7SBorislav Petkov 
27697bb9cdcSArd Biesheuvel 	/*
27797bb9cdcSArd Biesheuvel 	 * EFI_RUNTIME_SERVICES_CODE regions typically cover PE/COFF
27897bb9cdcSArd Biesheuvel 	 * executable images in memory that consist of both R-X and
27997bb9cdcSArd Biesheuvel 	 * RW- sections, so we cannot apply read-only or non-exec
28097bb9cdcSArd Biesheuvel 	 * permissions just yet. However, modern EFI systems provide
28197bb9cdcSArd Biesheuvel 	 * a memory attributes table that describes those sections
28297bb9cdcSArd Biesheuvel 	 * with the appropriate restricted permissions, which are
28397bb9cdcSArd Biesheuvel 	 * applied in efi_runtime_update_mappings() below. All other
28497bb9cdcSArd Biesheuvel 	 * regions can be mapped non-executable at this point, with
28597bb9cdcSArd Biesheuvel 	 * the exception of boot services code regions, but those will
28697bb9cdcSArd Biesheuvel 	 * be unmapped again entirely in efi_free_boot_services().
28797bb9cdcSArd Biesheuvel 	 */
28897bb9cdcSArd Biesheuvel 	if (md->type != EFI_BOOT_SERVICES_CODE &&
28997bb9cdcSArd Biesheuvel 	    md->type != EFI_RUNTIME_SERVICES_CODE)
29097bb9cdcSArd Biesheuvel 		flags |= _PAGE_NX;
29197bb9cdcSArd Biesheuvel 
292d2f7cbe7SBorislav Petkov 	if (!(md->attribute & EFI_MEMORY_WB))
293edc3b912SMatt Fleming 		flags |= _PAGE_PCD;
294d2f7cbe7SBorislav Petkov 
2954d96f910STom Lendacky 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT) &&
2964d96f910STom Lendacky 	    md->type != EFI_MEMORY_MAPPED_IO)
2971379edd5STom Lendacky 		flags |= _PAGE_ENC;
2981379edd5STom Lendacky 
299edc3b912SMatt Fleming 	pfn = md->phys_addr >> PAGE_SHIFT;
300edc3b912SMatt Fleming 	if (kernel_map_pages_in_pgd(pgd, pfn, va, md->num_pages, flags))
301d2f7cbe7SBorislav Petkov 		pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
302d2f7cbe7SBorislav Petkov 			   md->phys_addr, va);
303d2f7cbe7SBorislav Petkov }
304d2f7cbe7SBorislav Petkov 
efi_map_region(efi_memory_desc_t * md)305d2f7cbe7SBorislav Petkov void __init efi_map_region(efi_memory_desc_t *md)
306d2f7cbe7SBorislav Petkov {
307d2f7cbe7SBorislav Petkov 	unsigned long size = md->num_pages << PAGE_SHIFT;
308d2f7cbe7SBorislav Petkov 	u64 pa = md->phys_addr;
309d2f7cbe7SBorislav Petkov 
310d2f7cbe7SBorislav Petkov 	/*
311d2f7cbe7SBorislav Petkov 	 * Make sure the 1:1 mappings are present as a catch-all for b0rked
312d2f7cbe7SBorislav Petkov 	 * firmware which doesn't update all internal pointers after switching
313d2f7cbe7SBorislav Petkov 	 * to virtual mode and would otherwise crap on us.
314d2f7cbe7SBorislav Petkov 	 */
315d2f7cbe7SBorislav Petkov 	__map_region(md, md->phys_addr);
316d2f7cbe7SBorislav Petkov 
3174f9dbcfcSMatt Fleming 	/*
3184f9dbcfcSMatt Fleming 	 * Enforce the 1:1 mapping as the default virtual address when
3194f9dbcfcSMatt Fleming 	 * booting in EFI mixed mode, because even though we may be
3204f9dbcfcSMatt Fleming 	 * running a 64-bit kernel, the firmware may only be 32-bit.
3214f9dbcfcSMatt Fleming 	 */
322a8147dbaSArd Biesheuvel 	if (efi_is_mixed()) {
3234f9dbcfcSMatt Fleming 		md->virt_addr = md->phys_addr;
3244f9dbcfcSMatt Fleming 		return;
3254f9dbcfcSMatt Fleming 	}
3264f9dbcfcSMatt Fleming 
327d2f7cbe7SBorislav Petkov 	efi_va -= size;
328d2f7cbe7SBorislav Petkov 
329d2f7cbe7SBorislav Petkov 	/* Is PA 2M-aligned? */
330d2f7cbe7SBorislav Petkov 	if (!(pa & (PMD_SIZE - 1))) {
331d2f7cbe7SBorislav Petkov 		efi_va &= PMD_MASK;
332d2f7cbe7SBorislav Petkov 	} else {
333d2f7cbe7SBorislav Petkov 		u64 pa_offset = pa & (PMD_SIZE - 1);
334d2f7cbe7SBorislav Petkov 		u64 prev_va = efi_va;
335d2f7cbe7SBorislav Petkov 
336d2f7cbe7SBorislav Petkov 		/* get us the same offset within this 2M page */
337d2f7cbe7SBorislav Petkov 		efi_va = (efi_va & PMD_MASK) + pa_offset;
338d2f7cbe7SBorislav Petkov 
339d2f7cbe7SBorislav Petkov 		if (efi_va > prev_va)
340d2f7cbe7SBorislav Petkov 			efi_va -= PMD_SIZE;
341d2f7cbe7SBorislav Petkov 	}
342d2f7cbe7SBorislav Petkov 
343d2f7cbe7SBorislav Petkov 	if (efi_va < EFI_VA_END) {
344d2f7cbe7SBorislav Petkov 		pr_warn(FW_WARN "VA address range overflow!\n");
345d2f7cbe7SBorislav Petkov 		return;
346d2f7cbe7SBorislav Petkov 	}
347d2f7cbe7SBorislav Petkov 
348d2f7cbe7SBorislav Petkov 	/* Do the VA map */
349d2f7cbe7SBorislav Petkov 	__map_region(md, efi_va);
350d2f7cbe7SBorislav Petkov 	md->virt_addr = efi_va;
351d2f7cbe7SBorislav Petkov }
352d2f7cbe7SBorislav Petkov 
3533b266496SDave Young /*
3543b266496SDave Young  * kexec kernel will use efi_map_region_fixed to map efi runtime memory ranges.
3553b266496SDave Young  * md->virt_addr is the original virtual address which had been mapped in kexec
3563b266496SDave Young  * 1st kernel.
3573b266496SDave Young  */
efi_map_region_fixed(efi_memory_desc_t * md)3583b266496SDave Young void __init efi_map_region_fixed(efi_memory_desc_t *md)
3593b266496SDave Young {
3600513fe1dSAlex Thorlton 	__map_region(md, md->phys_addr);
3613b266496SDave Young 	__map_region(md, md->virt_addr);
3623b266496SDave Young }
3633b266496SDave Young 
parse_efi_setup(u64 phys_addr,u32 data_len)3641fec0533SDave Young void __init parse_efi_setup(u64 phys_addr, u32 data_len)
3651fec0533SDave Young {
3661fec0533SDave Young 	efi_setup = phys_addr + sizeof(struct setup_data);
3671fec0533SDave Young }
368c55d016fSBorislav Petkov 
efi_update_mappings(efi_memory_desc_t * md,unsigned long pf)36918141e89SSai Praneeth static int __init efi_update_mappings(efi_memory_desc_t *md, unsigned long pf)
370c55d016fSBorislav Petkov {
3716d0cc887SSai Praneeth 	unsigned long pfn;
3723ede3417SSai Praneeth 	pgd_t *pgd = efi_mm.pgd;
37318141e89SSai Praneeth 	int err1, err2;
37418141e89SSai Praneeth 
37518141e89SSai Praneeth 	/* Update the 1:1 mapping */
37618141e89SSai Praneeth 	pfn = md->phys_addr >> PAGE_SHIFT;
37718141e89SSai Praneeth 	err1 = kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, md->num_pages, pf);
37818141e89SSai Praneeth 	if (err1) {
37918141e89SSai Praneeth 		pr_err("Error while updating 1:1 mapping PA 0x%llx -> VA 0x%llx!\n",
38018141e89SSai Praneeth 			   md->phys_addr, md->virt_addr);
38118141e89SSai Praneeth 	}
38218141e89SSai Praneeth 
38318141e89SSai Praneeth 	err2 = kernel_map_pages_in_pgd(pgd, pfn, md->virt_addr, md->num_pages, pf);
38418141e89SSai Praneeth 	if (err2) {
38518141e89SSai Praneeth 		pr_err("Error while updating VA mapping PA 0x%llx -> VA 0x%llx!\n",
38618141e89SSai Praneeth 			   md->phys_addr, md->virt_addr);
38718141e89SSai Praneeth 	}
38818141e89SSai Praneeth 
38918141e89SSai Praneeth 	return err1 || err2;
39018141e89SSai Praneeth }
39118141e89SSai Praneeth 
39293be2859SArd Biesheuvel bool efi_disable_ibt_for_runtime __ro_after_init = true;
39393be2859SArd Biesheuvel 
efi_update_mem_attr(struct mm_struct * mm,efi_memory_desc_t * md,bool has_ibt)394cf1d2ffcSArd Biesheuvel static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *md,
395cf1d2ffcSArd Biesheuvel 				      bool has_ibt)
39618141e89SSai Praneeth {
39718141e89SSai Praneeth 	unsigned long pf = 0;
39818141e89SSai Praneeth 
39993be2859SArd Biesheuvel 	efi_disable_ibt_for_runtime |= !has_ibt;
40093be2859SArd Biesheuvel 
40118141e89SSai Praneeth 	if (md->attribute & EFI_MEMORY_XP)
40218141e89SSai Praneeth 		pf |= _PAGE_NX;
40318141e89SSai Praneeth 
40418141e89SSai Praneeth 	if (!(md->attribute & EFI_MEMORY_RO))
40518141e89SSai Praneeth 		pf |= _PAGE_RW;
40618141e89SSai Praneeth 
4074d96f910STom Lendacky 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
4081379edd5STom Lendacky 		pf |= _PAGE_ENC;
4091379edd5STom Lendacky 
41018141e89SSai Praneeth 	return efi_update_mappings(md, pf);
41118141e89SSai Praneeth }
41218141e89SSai Praneeth 
efi_runtime_update_mappings(void)41318141e89SSai Praneeth void __init efi_runtime_update_mappings(void)
41418141e89SSai Praneeth {
4156d0cc887SSai Praneeth 	efi_memory_desc_t *md;
416c55d016fSBorislav Petkov 
41718141e89SSai Praneeth 	/*
41818141e89SSai Praneeth 	 * Use the EFI Memory Attribute Table for mapping permissions if it
41918141e89SSai Praneeth 	 * exists, since it is intended to supersede EFI_PROPERTIES_TABLE.
42018141e89SSai Praneeth 	 */
42118141e89SSai Praneeth 	if (efi_enabled(EFI_MEM_ATTR)) {
42293be2859SArd Biesheuvel 		efi_disable_ibt_for_runtime = false;
42318141e89SSai Praneeth 		efi_memattr_apply_permissions(NULL, efi_update_mem_attr);
42418141e89SSai Praneeth 		return;
42518141e89SSai Praneeth 	}
42618141e89SSai Praneeth 
42718141e89SSai Praneeth 	/*
42818141e89SSai Praneeth 	 * EFI_MEMORY_ATTRIBUTES_TABLE is intended to replace
42918141e89SSai Praneeth 	 * EFI_PROPERTIES_TABLE. So, use EFI_PROPERTIES_TABLE to update
43018141e89SSai Praneeth 	 * permissions only if EFI_MEMORY_ATTRIBUTES_TABLE is not
43118141e89SSai Praneeth 	 * published by the firmware. Even if we find a buggy implementation of
43218141e89SSai Praneeth 	 * EFI_MEMORY_ATTRIBUTES_TABLE, don't fall back to
43318141e89SSai Praneeth 	 * EFI_PROPERTIES_TABLE, because of the same reason.
43418141e89SSai Praneeth 	 */
43518141e89SSai Praneeth 
4366d0cc887SSai Praneeth 	if (!efi_enabled(EFI_NX_PE_DATA))
4376d0cc887SSai Praneeth 		return;
4386d0cc887SSai Praneeth 
43978ce248fSMatt Fleming 	for_each_efi_memory_desc(md) {
4406d0cc887SSai Praneeth 		unsigned long pf = 0;
4416d0cc887SSai Praneeth 
4426d0cc887SSai Praneeth 		if (!(md->attribute & EFI_MEMORY_RUNTIME))
4436d0cc887SSai Praneeth 			continue;
4446d0cc887SSai Praneeth 
4456d0cc887SSai Praneeth 		if (!(md->attribute & EFI_MEMORY_WB))
4466d0cc887SSai Praneeth 			pf |= _PAGE_PCD;
4476d0cc887SSai Praneeth 
4486d0cc887SSai Praneeth 		if ((md->attribute & EFI_MEMORY_XP) ||
4496d0cc887SSai Praneeth 			(md->type == EFI_RUNTIME_SERVICES_DATA))
4506d0cc887SSai Praneeth 			pf |= _PAGE_NX;
4516d0cc887SSai Praneeth 
4526d0cc887SSai Praneeth 		if (!(md->attribute & EFI_MEMORY_RO) &&
4536d0cc887SSai Praneeth 			(md->type != EFI_RUNTIME_SERVICES_CODE))
4546d0cc887SSai Praneeth 			pf |= _PAGE_RW;
4556d0cc887SSai Praneeth 
4564d96f910STom Lendacky 		if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
4571379edd5STom Lendacky 			pf |= _PAGE_ENC;
4581379edd5STom Lendacky 
45918141e89SSai Praneeth 		efi_update_mappings(md, pf);
4606d0cc887SSai Praneeth 	}
461c55d016fSBorislav Petkov }
46211cc8512SBorislav Petkov 
efi_dump_pagetable(void)46311cc8512SBorislav Petkov void __init efi_dump_pagetable(void)
46411cc8512SBorislav Petkov {
46511cc8512SBorislav Petkov #ifdef CONFIG_EFI_PGT_DUMP
466e455248dSSteven Price 	ptdump_walk_pgd_level(NULL, &efi_mm);
46711cc8512SBorislav Petkov #endif
46811cc8512SBorislav Petkov }
469994448f1SMatt Fleming 
47003781e40SSai Praneeth /*
47103781e40SSai Praneeth  * Makes the calling thread switch to/from efi_mm context. Can be used
4724eda1117SSebastian Andrzej Siewior  * in a kernel thread and user context. Preemption needs to remain disabled
4734eda1117SSebastian Andrzej Siewior  * while the EFI-mm is borrowed. mmgrab()/mmdrop() is not used because the mm
4744eda1117SSebastian Andrzej Siewior  * can not change under us.
475d9f6e12fSIngo Molnar  * It should be ensured that there are no concurrent calls to this function.
47603781e40SSai Praneeth  */
efi_enter_mm(void)477*762f169fSArd Biesheuvel static void efi_enter_mm(void)
47803781e40SSai Praneeth {
479514b1a84SArd Biesheuvel 	efi_prev_mm = current->active_mm;
480514b1a84SArd Biesheuvel 	current->active_mm = &efi_mm;
481514b1a84SArd Biesheuvel 	switch_mm(efi_prev_mm, &efi_mm, NULL);
482514b1a84SArd Biesheuvel }
483514b1a84SArd Biesheuvel 
efi_leave_mm(void)484*762f169fSArd Biesheuvel static void efi_leave_mm(void)
485514b1a84SArd Biesheuvel {
486514b1a84SArd Biesheuvel 	current->active_mm = efi_prev_mm;
487514b1a84SArd Biesheuvel 	switch_mm(&efi_mm, efi_prev_mm, NULL);
48803781e40SSai Praneeth }
48903781e40SSai Praneeth 
arch_efi_call_virt_setup(void)490*762f169fSArd Biesheuvel void arch_efi_call_virt_setup(void)
491*762f169fSArd Biesheuvel {
492*762f169fSArd Biesheuvel 	efi_sync_low_kernel_mappings();
493*762f169fSArd Biesheuvel 	efi_fpu_begin();
494*762f169fSArd Biesheuvel 	firmware_restrict_branch_speculation_start();
495*762f169fSArd Biesheuvel 	efi_enter_mm();
496*762f169fSArd Biesheuvel }
497*762f169fSArd Biesheuvel 
arch_efi_call_virt_teardown(void)498*762f169fSArd Biesheuvel void arch_efi_call_virt_teardown(void)
499*762f169fSArd Biesheuvel {
500*762f169fSArd Biesheuvel 	efi_leave_mm();
501*762f169fSArd Biesheuvel 	firmware_restrict_branch_speculation_end();
502*762f169fSArd Biesheuvel 	efi_fpu_end();
503*762f169fSArd Biesheuvel }
504*762f169fSArd Biesheuvel 
50583a0a2eaSArd Biesheuvel static DEFINE_SPINLOCK(efi_runtime_lock);
50683a0a2eaSArd Biesheuvel 
507ea5e1919SArd Biesheuvel /*
508ea5e1919SArd Biesheuvel  * DS and ES contain user values.  We need to save them.
509ea5e1919SArd Biesheuvel  * The 32-bit EFI code needs a valid DS, ES, and SS.  There's no
510ea5e1919SArd Biesheuvel  * need to save the old SS: __KERNEL_DS is always acceptable.
511ea5e1919SArd Biesheuvel  */
512ea5e1919SArd Biesheuvel #define __efi_thunk(func, ...)						\
5134f9dbcfcSMatt Fleming ({									\
514ea5e1919SArd Biesheuvel 	unsigned short __ds, __es;					\
515ea5e1919SArd Biesheuvel 	efi_status_t ____s;						\
5164f9dbcfcSMatt Fleming 									\
517ea5e1919SArd Biesheuvel 	savesegment(ds, __ds);						\
518ea5e1919SArd Biesheuvel 	savesegment(es, __es);						\
519ea5e1919SArd Biesheuvel 									\
520ea5e1919SArd Biesheuvel 	loadsegment(ss, __KERNEL_DS);					\
521ea5e1919SArd Biesheuvel 	loadsegment(ds, __KERNEL_DS);					\
522ea5e1919SArd Biesheuvel 	loadsegment(es, __KERNEL_DS);					\
523ea5e1919SArd Biesheuvel 									\
52459f2a619SArd Biesheuvel 	____s = efi64_thunk(efi.runtime->mixed_mode.func, __VA_ARGS__);	\
525ea5e1919SArd Biesheuvel 									\
526ea5e1919SArd Biesheuvel 	loadsegment(ds, __ds);						\
527ea5e1919SArd Biesheuvel 	loadsegment(es, __es);						\
528ea5e1919SArd Biesheuvel 									\
529ea5e1919SArd Biesheuvel 	____s ^= (____s & BIT(31)) | (____s & BIT_ULL(31)) << 32;	\
530ea5e1919SArd Biesheuvel 	____s;								\
5314f9dbcfcSMatt Fleming })
5324f9dbcfcSMatt Fleming 
5334f9dbcfcSMatt Fleming /*
5344f9dbcfcSMatt Fleming  * Switch to the EFI page tables early so that we can access the 1:1
5354f9dbcfcSMatt Fleming  * runtime services mappings which are not mapped in any other page
536ea5e1919SArd Biesheuvel  * tables.
5374f9dbcfcSMatt Fleming  *
5384f9dbcfcSMatt Fleming  * Also, disable interrupts because the IDT points to 64-bit handlers,
5394f9dbcfcSMatt Fleming  * which aren't going to function correctly when we switch to 32-bit.
5404f9dbcfcSMatt Fleming  */
541ea5e1919SArd Biesheuvel #define efi_thunk(func...)						\
5424f9dbcfcSMatt Fleming ({									\
5434f9dbcfcSMatt Fleming 	efi_status_t __s;						\
5444f9dbcfcSMatt Fleming 									\
54521f86625SAlex Thorlton 	arch_efi_call_virt_setup();					\
5464f9dbcfcSMatt Fleming 									\
547ea5e1919SArd Biesheuvel 	__s = __efi_thunk(func);					\
5484f9dbcfcSMatt Fleming 									\
54921f86625SAlex Thorlton 	arch_efi_call_virt_teardown();					\
5504f9dbcfcSMatt Fleming 									\
5514f9dbcfcSMatt Fleming 	__s;								\
5524f9dbcfcSMatt Fleming })
5534f9dbcfcSMatt Fleming 
5543cc02861SArd Biesheuvel static efi_status_t __init __no_sanitize_address
efi_thunk_set_virtual_address_map(unsigned long memory_map_size,unsigned long descriptor_size,u32 descriptor_version,efi_memory_desc_t * virtual_map)555ea5e1919SArd Biesheuvel efi_thunk_set_virtual_address_map(unsigned long memory_map_size,
5564f9dbcfcSMatt Fleming 				  unsigned long descriptor_size,
5574f9dbcfcSMatt Fleming 				  u32 descriptor_version,
5584f9dbcfcSMatt Fleming 				  efi_memory_desc_t *virtual_map)
5594f9dbcfcSMatt Fleming {
5604f9dbcfcSMatt Fleming 	efi_status_t status;
5614f9dbcfcSMatt Fleming 	unsigned long flags;
5624f9dbcfcSMatt Fleming 
5634f9dbcfcSMatt Fleming 	efi_sync_low_kernel_mappings();
5644f9dbcfcSMatt Fleming 	local_irq_save(flags);
5654f9dbcfcSMatt Fleming 
566514b1a84SArd Biesheuvel 	efi_enter_mm();
5674f9dbcfcSMatt Fleming 
568ea5e1919SArd Biesheuvel 	status = __efi_thunk(set_virtual_address_map, memory_map_size,
569ea5e1919SArd Biesheuvel 			     descriptor_size, descriptor_version, virtual_map);
5704f9dbcfcSMatt Fleming 
571514b1a84SArd Biesheuvel 	efi_leave_mm();
5724f9dbcfcSMatt Fleming 	local_irq_restore(flags);
5734f9dbcfcSMatt Fleming 
5744f9dbcfcSMatt Fleming 	return status;
5754f9dbcfcSMatt Fleming }
5764f9dbcfcSMatt Fleming 
efi_thunk_get_time(efi_time_t * tm,efi_time_cap_t * tc)5774f9dbcfcSMatt Fleming static efi_status_t efi_thunk_get_time(efi_time_t *tm, efi_time_cap_t *tc)
5784f9dbcfcSMatt Fleming {
579f80c9f64SArd Biesheuvel 	return EFI_UNSUPPORTED;
5804f9dbcfcSMatt Fleming }
5814f9dbcfcSMatt Fleming 
efi_thunk_set_time(efi_time_t * tm)5824f9dbcfcSMatt Fleming static efi_status_t efi_thunk_set_time(efi_time_t *tm)
5834f9dbcfcSMatt Fleming {
584f80c9f64SArd Biesheuvel 	return EFI_UNSUPPORTED;
5854f9dbcfcSMatt Fleming }
5864f9dbcfcSMatt Fleming 
5874f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_get_wakeup_time(efi_bool_t * enabled,efi_bool_t * pending,efi_time_t * tm)5884f9dbcfcSMatt Fleming efi_thunk_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending,
5894f9dbcfcSMatt Fleming 			  efi_time_t *tm)
5904f9dbcfcSMatt Fleming {
591f80c9f64SArd Biesheuvel 	return EFI_UNSUPPORTED;
5924f9dbcfcSMatt Fleming }
5934f9dbcfcSMatt Fleming 
5944f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_set_wakeup_time(efi_bool_t enabled,efi_time_t * tm)5954f9dbcfcSMatt Fleming efi_thunk_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
5964f9dbcfcSMatt Fleming {
597f80c9f64SArd Biesheuvel 	return EFI_UNSUPPORTED;
5984f9dbcfcSMatt Fleming }
5994f9dbcfcSMatt Fleming 
efi_name_size(efi_char16_t * name)600f6697df3SMatt Fleming static unsigned long efi_name_size(efi_char16_t *name)
601f6697df3SMatt Fleming {
602f6697df3SMatt Fleming 	return ucs2_strsize(name, EFI_VAR_NAME_LEN) + 1;
603f6697df3SMatt Fleming }
6044f9dbcfcSMatt Fleming 
6054f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_get_variable(efi_char16_t * name,efi_guid_t * vendor,u32 * attr,unsigned long * data_size,void * data)6064f9dbcfcSMatt Fleming efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
6074f9dbcfcSMatt Fleming 		       u32 *attr, unsigned long *data_size, void *data)
6084f9dbcfcSMatt Fleming {
60963056e8bSArd Biesheuvel 	u8 buf[24] __aligned(8);
61063056e8bSArd Biesheuvel 	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
6114f9dbcfcSMatt Fleming 	efi_status_t status;
6124f9dbcfcSMatt Fleming 	u32 phys_name, phys_vendor, phys_attr;
6134f9dbcfcSMatt Fleming 	u32 phys_data_size, phys_data;
61483a0a2eaSArd Biesheuvel 	unsigned long flags;
61583a0a2eaSArd Biesheuvel 
61683a0a2eaSArd Biesheuvel 	spin_lock_irqsave(&efi_runtime_lock, flags);
6174f9dbcfcSMatt Fleming 
61863056e8bSArd Biesheuvel 	*vnd = *vendor;
61963056e8bSArd Biesheuvel 
620f6697df3SMatt Fleming 	phys_data_size = virt_to_phys_or_null(data_size);
62163056e8bSArd Biesheuvel 	phys_vendor = virt_to_phys_or_null(vnd);
622f6697df3SMatt Fleming 	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
623f6697df3SMatt Fleming 	phys_attr = virt_to_phys_or_null(attr);
624f6697df3SMatt Fleming 	phys_data = virt_to_phys_or_null_size(data, *data_size);
6254f9dbcfcSMatt Fleming 
6268319e9d5SArd Biesheuvel 	if (!phys_name || (data && !phys_data))
6278319e9d5SArd Biesheuvel 		status = EFI_INVALID_PARAMETER;
6288319e9d5SArd Biesheuvel 	else
6294f9dbcfcSMatt Fleming 		status = efi_thunk(get_variable, phys_name, phys_vendor,
6304f9dbcfcSMatt Fleming 				   phys_attr, phys_data_size, phys_data);
6314f9dbcfcSMatt Fleming 
63283a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
63383a0a2eaSArd Biesheuvel 
6344f9dbcfcSMatt Fleming 	return status;
6354f9dbcfcSMatt Fleming }
6364f9dbcfcSMatt Fleming 
6374f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_set_variable(efi_char16_t * name,efi_guid_t * vendor,u32 attr,unsigned long data_size,void * data)6384f9dbcfcSMatt Fleming efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor,
6394f9dbcfcSMatt Fleming 		       u32 attr, unsigned long data_size, void *data)
6404f9dbcfcSMatt Fleming {
64163056e8bSArd Biesheuvel 	u8 buf[24] __aligned(8);
64263056e8bSArd Biesheuvel 	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
6434f9dbcfcSMatt Fleming 	u32 phys_name, phys_vendor, phys_data;
6444f9dbcfcSMatt Fleming 	efi_status_t status;
64583a0a2eaSArd Biesheuvel 	unsigned long flags;
64683a0a2eaSArd Biesheuvel 
64783a0a2eaSArd Biesheuvel 	spin_lock_irqsave(&efi_runtime_lock, flags);
6484f9dbcfcSMatt Fleming 
64963056e8bSArd Biesheuvel 	*vnd = *vendor;
65063056e8bSArd Biesheuvel 
651f6697df3SMatt Fleming 	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
65263056e8bSArd Biesheuvel 	phys_vendor = virt_to_phys_or_null(vnd);
653f6697df3SMatt Fleming 	phys_data = virt_to_phys_or_null_size(data, data_size);
6544f9dbcfcSMatt Fleming 
655a4b81ccfSGary Lin 	if (!phys_name || (data && !phys_data))
6568319e9d5SArd Biesheuvel 		status = EFI_INVALID_PARAMETER;
6578319e9d5SArd Biesheuvel 	else
6584f9dbcfcSMatt Fleming 		status = efi_thunk(set_variable, phys_name, phys_vendor,
6594f9dbcfcSMatt Fleming 				   attr, data_size, phys_data);
6604f9dbcfcSMatt Fleming 
66183a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
66283a0a2eaSArd Biesheuvel 
66383a0a2eaSArd Biesheuvel 	return status;
66483a0a2eaSArd Biesheuvel }
66583a0a2eaSArd Biesheuvel 
66683a0a2eaSArd Biesheuvel static efi_status_t
efi_thunk_set_variable_nonblocking(efi_char16_t * name,efi_guid_t * vendor,u32 attr,unsigned long data_size,void * data)66783a0a2eaSArd Biesheuvel efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
66883a0a2eaSArd Biesheuvel 				   u32 attr, unsigned long data_size,
66983a0a2eaSArd Biesheuvel 				   void *data)
67083a0a2eaSArd Biesheuvel {
67163056e8bSArd Biesheuvel 	u8 buf[24] __aligned(8);
67263056e8bSArd Biesheuvel 	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
67383a0a2eaSArd Biesheuvel 	u32 phys_name, phys_vendor, phys_data;
67483a0a2eaSArd Biesheuvel 	efi_status_t status;
67583a0a2eaSArd Biesheuvel 	unsigned long flags;
67683a0a2eaSArd Biesheuvel 
67783a0a2eaSArd Biesheuvel 	if (!spin_trylock_irqsave(&efi_runtime_lock, flags))
67883a0a2eaSArd Biesheuvel 		return EFI_NOT_READY;
67983a0a2eaSArd Biesheuvel 
68063056e8bSArd Biesheuvel 	*vnd = *vendor;
68163056e8bSArd Biesheuvel 
68283a0a2eaSArd Biesheuvel 	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
68363056e8bSArd Biesheuvel 	phys_vendor = virt_to_phys_or_null(vnd);
68483a0a2eaSArd Biesheuvel 	phys_data = virt_to_phys_or_null_size(data, data_size);
68583a0a2eaSArd Biesheuvel 
686a4b81ccfSGary Lin 	if (!phys_name || (data && !phys_data))
6878319e9d5SArd Biesheuvel 		status = EFI_INVALID_PARAMETER;
6888319e9d5SArd Biesheuvel 	else
68983a0a2eaSArd Biesheuvel 		status = efi_thunk(set_variable, phys_name, phys_vendor,
69083a0a2eaSArd Biesheuvel 				   attr, data_size, phys_data);
69183a0a2eaSArd Biesheuvel 
69283a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
69383a0a2eaSArd Biesheuvel 
6944f9dbcfcSMatt Fleming 	return status;
6954f9dbcfcSMatt Fleming }
6964f9dbcfcSMatt Fleming 
6974f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_get_next_variable(unsigned long * name_size,efi_char16_t * name,efi_guid_t * vendor)6984f9dbcfcSMatt Fleming efi_thunk_get_next_variable(unsigned long *name_size,
6994f9dbcfcSMatt Fleming 			    efi_char16_t *name,
7004f9dbcfcSMatt Fleming 			    efi_guid_t *vendor)
7014f9dbcfcSMatt Fleming {
70263056e8bSArd Biesheuvel 	u8 buf[24] __aligned(8);
70363056e8bSArd Biesheuvel 	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
7044f9dbcfcSMatt Fleming 	efi_status_t status;
7054f9dbcfcSMatt Fleming 	u32 phys_name_size, phys_name, phys_vendor;
70683a0a2eaSArd Biesheuvel 	unsigned long flags;
70783a0a2eaSArd Biesheuvel 
70883a0a2eaSArd Biesheuvel 	spin_lock_irqsave(&efi_runtime_lock, flags);
7094f9dbcfcSMatt Fleming 
71063056e8bSArd Biesheuvel 	*vnd = *vendor;
71163056e8bSArd Biesheuvel 
712f6697df3SMatt Fleming 	phys_name_size = virt_to_phys_or_null(name_size);
71363056e8bSArd Biesheuvel 	phys_vendor = virt_to_phys_or_null(vnd);
714f6697df3SMatt Fleming 	phys_name = virt_to_phys_or_null_size(name, *name_size);
7154f9dbcfcSMatt Fleming 
7168319e9d5SArd Biesheuvel 	if (!phys_name)
7178319e9d5SArd Biesheuvel 		status = EFI_INVALID_PARAMETER;
7188319e9d5SArd Biesheuvel 	else
7194f9dbcfcSMatt Fleming 		status = efi_thunk(get_next_variable, phys_name_size,
7204f9dbcfcSMatt Fleming 				   phys_name, phys_vendor);
7214f9dbcfcSMatt Fleming 
72283a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
72383a0a2eaSArd Biesheuvel 
72463056e8bSArd Biesheuvel 	*vendor = *vnd;
7254f9dbcfcSMatt Fleming 	return status;
7264f9dbcfcSMatt Fleming }
7274f9dbcfcSMatt Fleming 
7284f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_get_next_high_mono_count(u32 * count)7294f9dbcfcSMatt Fleming efi_thunk_get_next_high_mono_count(u32 *count)
7304f9dbcfcSMatt Fleming {
731f80c9f64SArd Biesheuvel 	return EFI_UNSUPPORTED;
7324f9dbcfcSMatt Fleming }
7334f9dbcfcSMatt Fleming 
7344f9dbcfcSMatt Fleming static void
efi_thunk_reset_system(int reset_type,efi_status_t status,unsigned long data_size,efi_char16_t * data)7354f9dbcfcSMatt Fleming efi_thunk_reset_system(int reset_type, efi_status_t status,
7364f9dbcfcSMatt Fleming 		       unsigned long data_size, efi_char16_t *data)
7374f9dbcfcSMatt Fleming {
7384f9dbcfcSMatt Fleming 	u32 phys_data;
73983a0a2eaSArd Biesheuvel 	unsigned long flags;
74083a0a2eaSArd Biesheuvel 
74183a0a2eaSArd Biesheuvel 	spin_lock_irqsave(&efi_runtime_lock, flags);
7424f9dbcfcSMatt Fleming 
743f6697df3SMatt Fleming 	phys_data = virt_to_phys_or_null_size(data, data_size);
7444f9dbcfcSMatt Fleming 
7454f9dbcfcSMatt Fleming 	efi_thunk(reset_system, reset_type, status, data_size, phys_data);
74683a0a2eaSArd Biesheuvel 
74783a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
7484f9dbcfcSMatt Fleming }
7494f9dbcfcSMatt Fleming 
7504f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_update_capsule(efi_capsule_header_t ** capsules,unsigned long count,unsigned long sg_list)7514f9dbcfcSMatt Fleming efi_thunk_update_capsule(efi_capsule_header_t **capsules,
7524f9dbcfcSMatt Fleming 			 unsigned long count, unsigned long sg_list)
7534f9dbcfcSMatt Fleming {
7544f9dbcfcSMatt Fleming 	/*
7554f9dbcfcSMatt Fleming 	 * To properly support this function we would need to repackage
7564f9dbcfcSMatt Fleming 	 * 'capsules' because the firmware doesn't understand 64-bit
7574f9dbcfcSMatt Fleming 	 * pointers.
7584f9dbcfcSMatt Fleming 	 */
7594f9dbcfcSMatt Fleming 	return EFI_UNSUPPORTED;
7604f9dbcfcSMatt Fleming }
7614f9dbcfcSMatt Fleming 
7624f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_query_variable_info(u32 attr,u64 * storage_space,u64 * remaining_space,u64 * max_variable_size)7634f9dbcfcSMatt Fleming efi_thunk_query_variable_info(u32 attr, u64 *storage_space,
7644f9dbcfcSMatt Fleming 			      u64 *remaining_space,
7654f9dbcfcSMatt Fleming 			      u64 *max_variable_size)
7664f9dbcfcSMatt Fleming {
7674f9dbcfcSMatt Fleming 	efi_status_t status;
7684f9dbcfcSMatt Fleming 	u32 phys_storage, phys_remaining, phys_max;
76983a0a2eaSArd Biesheuvel 	unsigned long flags;
7704f9dbcfcSMatt Fleming 
7714f9dbcfcSMatt Fleming 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
7724f9dbcfcSMatt Fleming 		return EFI_UNSUPPORTED;
7734f9dbcfcSMatt Fleming 
77483a0a2eaSArd Biesheuvel 	spin_lock_irqsave(&efi_runtime_lock, flags);
77583a0a2eaSArd Biesheuvel 
776f6697df3SMatt Fleming 	phys_storage = virt_to_phys_or_null(storage_space);
777f6697df3SMatt Fleming 	phys_remaining = virt_to_phys_or_null(remaining_space);
778f6697df3SMatt Fleming 	phys_max = virt_to_phys_or_null(max_variable_size);
7794f9dbcfcSMatt Fleming 
7809a11040fSMatt Fleming 	status = efi_thunk(query_variable_info, attr, phys_storage,
7814f9dbcfcSMatt Fleming 			   phys_remaining, phys_max);
7824f9dbcfcSMatt Fleming 
78383a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
78483a0a2eaSArd Biesheuvel 
78583a0a2eaSArd Biesheuvel 	return status;
78683a0a2eaSArd Biesheuvel }
78783a0a2eaSArd Biesheuvel 
78883a0a2eaSArd Biesheuvel static efi_status_t
efi_thunk_query_variable_info_nonblocking(u32 attr,u64 * storage_space,u64 * remaining_space,u64 * max_variable_size)78983a0a2eaSArd Biesheuvel efi_thunk_query_variable_info_nonblocking(u32 attr, u64 *storage_space,
79083a0a2eaSArd Biesheuvel 					  u64 *remaining_space,
79183a0a2eaSArd Biesheuvel 					  u64 *max_variable_size)
79283a0a2eaSArd Biesheuvel {
79383a0a2eaSArd Biesheuvel 	efi_status_t status;
79483a0a2eaSArd Biesheuvel 	u32 phys_storage, phys_remaining, phys_max;
79583a0a2eaSArd Biesheuvel 	unsigned long flags;
79683a0a2eaSArd Biesheuvel 
79783a0a2eaSArd Biesheuvel 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
79883a0a2eaSArd Biesheuvel 		return EFI_UNSUPPORTED;
79983a0a2eaSArd Biesheuvel 
80083a0a2eaSArd Biesheuvel 	if (!spin_trylock_irqsave(&efi_runtime_lock, flags))
80183a0a2eaSArd Biesheuvel 		return EFI_NOT_READY;
80283a0a2eaSArd Biesheuvel 
80383a0a2eaSArd Biesheuvel 	phys_storage = virt_to_phys_or_null(storage_space);
80483a0a2eaSArd Biesheuvel 	phys_remaining = virt_to_phys_or_null(remaining_space);
80583a0a2eaSArd Biesheuvel 	phys_max = virt_to_phys_or_null(max_variable_size);
80683a0a2eaSArd Biesheuvel 
80783a0a2eaSArd Biesheuvel 	status = efi_thunk(query_variable_info, attr, phys_storage,
80883a0a2eaSArd Biesheuvel 			   phys_remaining, phys_max);
80983a0a2eaSArd Biesheuvel 
81083a0a2eaSArd Biesheuvel 	spin_unlock_irqrestore(&efi_runtime_lock, flags);
81183a0a2eaSArd Biesheuvel 
8124f9dbcfcSMatt Fleming 	return status;
8134f9dbcfcSMatt Fleming }
8144f9dbcfcSMatt Fleming 
8154f9dbcfcSMatt Fleming static efi_status_t
efi_thunk_query_capsule_caps(efi_capsule_header_t ** capsules,unsigned long count,u64 * max_size,int * reset_type)8164f9dbcfcSMatt Fleming efi_thunk_query_capsule_caps(efi_capsule_header_t **capsules,
8174f9dbcfcSMatt Fleming 			     unsigned long count, u64 *max_size,
8184f9dbcfcSMatt Fleming 			     int *reset_type)
8194f9dbcfcSMatt Fleming {
8204f9dbcfcSMatt Fleming 	/*
8214f9dbcfcSMatt Fleming 	 * To properly support this function we would need to repackage
8224f9dbcfcSMatt Fleming 	 * 'capsules' because the firmware doesn't understand 64-bit
8234f9dbcfcSMatt Fleming 	 * pointers.
8244f9dbcfcSMatt Fleming 	 */
8254f9dbcfcSMatt Fleming 	return EFI_UNSUPPORTED;
8264f9dbcfcSMatt Fleming }
8274f9dbcfcSMatt Fleming 
efi_thunk_runtime_setup(void)828ea5e1919SArd Biesheuvel void __init efi_thunk_runtime_setup(void)
8294f9dbcfcSMatt Fleming {
830ea5e1919SArd Biesheuvel 	if (!IS_ENABLED(CONFIG_EFI_MIXED))
831ea5e1919SArd Biesheuvel 		return;
832ea5e1919SArd Biesheuvel 
8334f9dbcfcSMatt Fleming 	efi.get_time = efi_thunk_get_time;
8344f9dbcfcSMatt Fleming 	efi.set_time = efi_thunk_set_time;
8354f9dbcfcSMatt Fleming 	efi.get_wakeup_time = efi_thunk_get_wakeup_time;
8364f9dbcfcSMatt Fleming 	efi.set_wakeup_time = efi_thunk_set_wakeup_time;
8374f9dbcfcSMatt Fleming 	efi.get_variable = efi_thunk_get_variable;
8384f9dbcfcSMatt Fleming 	efi.get_next_variable = efi_thunk_get_next_variable;
8394f9dbcfcSMatt Fleming 	efi.set_variable = efi_thunk_set_variable;
84083a0a2eaSArd Biesheuvel 	efi.set_variable_nonblocking = efi_thunk_set_variable_nonblocking;
8414f9dbcfcSMatt Fleming 	efi.get_next_high_mono_count = efi_thunk_get_next_high_mono_count;
8424f9dbcfcSMatt Fleming 	efi.reset_system = efi_thunk_reset_system;
8434f9dbcfcSMatt Fleming 	efi.query_variable_info = efi_thunk_query_variable_info;
84483a0a2eaSArd Biesheuvel 	efi.query_variable_info_nonblocking = efi_thunk_query_variable_info_nonblocking;
8454f9dbcfcSMatt Fleming 	efi.update_capsule = efi_thunk_update_capsule;
8464f9dbcfcSMatt Fleming 	efi.query_capsule_caps = efi_thunk_query_capsule_caps;
8474f9dbcfcSMatt Fleming }
84869829470SArd Biesheuvel 
8493cc02861SArd Biesheuvel efi_status_t __init __no_sanitize_address
efi_set_virtual_address_map(unsigned long memory_map_size,unsigned long descriptor_size,u32 descriptor_version,efi_memory_desc_t * virtual_map,unsigned long systab_phys)8503cc02861SArd Biesheuvel efi_set_virtual_address_map(unsigned long memory_map_size,
85169829470SArd Biesheuvel 			    unsigned long descriptor_size,
85269829470SArd Biesheuvel 			    u32 descriptor_version,
85359f2a619SArd Biesheuvel 			    efi_memory_desc_t *virtual_map,
85459f2a619SArd Biesheuvel 			    unsigned long systab_phys)
85569829470SArd Biesheuvel {
85659f2a619SArd Biesheuvel 	const efi_system_table_t *systab = (efi_system_table_t *)systab_phys;
85769829470SArd Biesheuvel 	efi_status_t status;
85869829470SArd Biesheuvel 	unsigned long flags;
85969829470SArd Biesheuvel 
860ea5e1919SArd Biesheuvel 	if (efi_is_mixed())
861ea5e1919SArd Biesheuvel 		return efi_thunk_set_virtual_address_map(memory_map_size,
862ea5e1919SArd Biesheuvel 							 descriptor_size,
863ea5e1919SArd Biesheuvel 							 descriptor_version,
864ea5e1919SArd Biesheuvel 							 virtual_map);
865514b1a84SArd Biesheuvel 	efi_enter_mm();
86669829470SArd Biesheuvel 
867b0dc553cSAndy Lutomirski 	efi_fpu_begin();
868e5f930feSArd Biesheuvel 
86969829470SArd Biesheuvel 	/* Disable interrupts around EFI calls: */
87069829470SArd Biesheuvel 	local_irq_save(flags);
8710303c972SThomas Gleixner 	status = arch_efi_call_virt(efi.runtime, set_virtual_address_map,
87269829470SArd Biesheuvel 				    memory_map_size, descriptor_size,
87369829470SArd Biesheuvel 				    descriptor_version, virtual_map);
87469829470SArd Biesheuvel 	local_irq_restore(flags);
87569829470SArd Biesheuvel 
876b0dc553cSAndy Lutomirski 	efi_fpu_end();
87769829470SArd Biesheuvel 
87859f2a619SArd Biesheuvel 	/* grab the virtually remapped EFI runtime services table pointer */
87959f2a619SArd Biesheuvel 	efi.runtime = READ_ONCE(systab->runtime);
88059f2a619SArd Biesheuvel 
881514b1a84SArd Biesheuvel 	efi_leave_mm();
88269829470SArd Biesheuvel 
88369829470SArd Biesheuvel 	return status;
88469829470SArd Biesheuvel }
885