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