From patchwork Thu Oct 1 17:04:15 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 54379 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-la0-f69.google.com (mail-la0-f69.google.com [209.85.215.69]) by patches.linaro.org (Postfix) with ESMTPS id 6467B23010 for ; Thu, 1 Oct 2015 17:06:53 +0000 (UTC) Received: by laer8 with SMTP id r8sf39218849lae.2 for ; Thu, 01 Oct 2015 10:06:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:subject:date:message-id :in-reply-to:references:precedence:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:cc:mime-version :content-type:content-transfer-encoding:sender:errors-to :x-original-sender:x-original-authentication-results:mailing-list; bh=gGjlSu4puSoKe2Z+7EHIVAfvj/CRK6+hlcRUE9WqgKw=; b=ZXV5TeGM2MG+RryxKqXxNSjLvszREl9g8cfujQNMlwf5QnkkZWcwe8VPewUngS96Hn MSZ8WzEQVGSM7fXOEPfst2kOJgPgea2egHf6Np25FvP3qYB4qqweqkw+gALO173Rxj0h fPH99HYRkbaxdMriI/GmjR15TrFyVFUPwlErd33GqA7EVA4yxEPrA8ib+/+EDg8UAmQH KWa22Cw2GL5Yz21m1iK2AKVgRw0r2eHwCp5Jg86js63BdThCgacNe7cWTBYK9R8NV7Ss yakde3QOxmPP10dAMc0uufvHhmzjDiqCi77KtV9BeBoAOhzMqMB2TuWVdzcVa9LV2KuG Yw7Q== X-Gm-Message-State: ALoCoQkOApYrlAbI5DrlSTMWLwYF1/obOMiknAJcTCTMg0RMVQkXUm1yjdiIBzlPGaX5uJksCRvC X-Received: by 10.194.6.170 with SMTP id c10mr1631279wja.7.1443719211694; Thu, 01 Oct 2015 10:06:51 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.25.144.6 with SMTP id s6ls137816lfd.91.gmail; Thu, 01 Oct 2015 10:06:51 -0700 (PDT) X-Received: by 10.112.130.39 with SMTP id ob7mr3489229lbb.66.1443719211524; Thu, 01 Oct 2015 10:06:51 -0700 (PDT) Received: from mail-lb0-f175.google.com (mail-lb0-f175.google.com. [209.85.217.175]) by mx.google.com with ESMTPS id e76si3350291lfe.27.2015.10.01.10.06.51 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 01 Oct 2015 10:06:51 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) client-ip=209.85.217.175; Received: by lbcao8 with SMTP id ao8so13103109lbc.3 for ; Thu, 01 Oct 2015 10:06:51 -0700 (PDT) X-Received: by 10.112.199.137 with SMTP id jk9mr3522016lbc.86.1443719211042; Thu, 01 Oct 2015 10:06:51 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.59.35 with SMTP id w3csp729601lbq; Thu, 1 Oct 2015 10:06:50 -0700 (PDT) X-Received: by 10.194.57.242 with SMTP id l18mr11420122wjq.34.1443719210009; Thu, 01 Oct 2015 10:06:50 -0700 (PDT) Received: from bombadil.infradead.org (bombadil.infradead.org. [2001:1868:205::9]) by mx.google.com with ESMTPS id az6si4841498wib.48.2015.10.01.10.06.49 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 01 Oct 2015 10:06:49 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org designates 2001:1868:205::9 as permitted sender) client-ip=2001:1868:205::9; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZhhI2-0002Qe-Pu; Thu, 01 Oct 2015 17:05:06 +0000 Received: from mail-wi0-f179.google.com ([209.85.212.179]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZhhHq-0001xd-2S for linux-arm-kernel@lists.infradead.org; Thu, 01 Oct 2015 17:04:57 +0000 Received: by wicge5 with SMTP id ge5so39359066wic.0 for ; Thu, 01 Oct 2015 10:04:32 -0700 (PDT) X-Received: by 10.194.77.4 with SMTP id o4mr13376590wjw.4.1443719072000; Thu, 01 Oct 2015 10:04:32 -0700 (PDT) Received: from localhost.localdomain ([83.225.55.104]) by smtp.gmail.com with ESMTPSA id x9sm7036760wjf.44.2015.10.01.10.04.29 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 01 Oct 2015 10:04:31 -0700 (PDT) From: Ard Biesheuvel To: linux-arm-kernel@lists.infradead.org, linux-efi@vger.kernel.org, matt.fleming@intel.com, linux@arm.linux.org.uk, will.deacon@arm.com, grant.likely@linaro.org, leif.lindholm@linaro.org, roy.franz@linaro.org, mark.rutland@arm.com, catalin.marinas@arm.com Subject: [PATCH 1/9] arm64/efi: split off EFI init and runtime code for reuse by 32-bit ARM Date: Thu, 1 Oct 2015 19:04:15 +0200 Message-Id: <1443719063-6832-2-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1443719063-6832-1-git-send-email-ard.biesheuvel@linaro.org> References: <1443719063-6832-1-git-send-email-ard.biesheuvel@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151001_100454_457614_2D18CC18 X-CRM114-Status: GOOD ( 29.70 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.179 listed in list.dnswl.org] -0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) [209.85.212.179 listed in wl.mailspike.net] -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , Cc: ryan.harkin@linaro.org, Ard Biesheuvel , msalter@redhat.com MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patch=linaro.org@lists.infradead.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ard.biesheuvel@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 This splits off the early EFI init and runtime code that - discovers the EFI params and the memory map from the FDT, and installs the memblocks and config tables. - prepares and installs the EFI page tables so that UEFI Runtime Services can be invoked at the virtual address installed by the stub. This will allow it to be reused for 32-bit ARM. Signed-off-by: Ard Biesheuvel --- arch/arm64/kernel/efi.c | 363 +------------------- drivers/firmware/efi/Makefile | 4 +- drivers/firmware/efi/arm-init.c | 214 ++++++++++++ drivers/firmware/efi/arm-runtime.c | 191 ++++++++++ 4 files changed, 409 insertions(+), 363 deletions(-) diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index caca71590984..bd3b2f5adf0c 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -11,348 +11,11 @@ * */ -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include #include -#include -#include -#include -#include - -struct efi_memory_map memmap; - -static u64 efi_system_table; - -static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss; - -static struct mm_struct efi_mm = { - .mm_rb = RB_ROOT, - .pgd = efi_pgd, - .mm_users = ATOMIC_INIT(2), - .mm_count = ATOMIC_INIT(1), - .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem), - .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), - .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), - INIT_MM_CONTEXT(efi_mm) -}; - -static int __init is_normal_ram(efi_memory_desc_t *md) -{ - if (md->attribute & EFI_MEMORY_WB) - return 1; - return 0; -} - -/* - * Translate a EFI virtual address into a physical address: this is necessary, - * as some data members of the EFI system table are virtually remapped after - * SetVirtualAddressMap() has been called. - */ -static phys_addr_t efi_to_phys(unsigned long addr) -{ - efi_memory_desc_t *md; - - for_each_efi_memory_desc(&memmap, md) { - if (!(md->attribute & EFI_MEMORY_RUNTIME)) - continue; - if (md->virt_addr == 0) - /* no virtual mapping has been installed by the stub */ - break; - if (md->virt_addr <= addr && - (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT)) - return md->phys_addr + addr - md->virt_addr; - } - return addr; -} - -static int __init uefi_init(void) -{ - efi_char16_t *c16; - void *config_tables; - u64 table_size; - char vendor[100] = "unknown"; - int i, retval; - - efi.systab = early_memremap(efi_system_table, - sizeof(efi_system_table_t)); - if (efi.systab == NULL) { - pr_warn("Unable to map EFI system table.\n"); - return -ENOMEM; - } - - set_bit(EFI_BOOT, &efi.flags); - set_bit(EFI_64BIT, &efi.flags); - - /* - * Verify the EFI Table - */ - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - pr_err("System table signature incorrect\n"); - retval = -EINVAL; - goto out; - } - if ((efi.systab->hdr.revision >> 16) < 2) - pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); - - /* Show what we know for posterity */ - c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor), - sizeof(vendor) * sizeof(efi_char16_t)); - if (c16) { - for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = c16[i]; - vendor[i] = '\0'; - early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); - } - - pr_info("EFI v%u.%.02u by %s\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); - - table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; - config_tables = early_memremap(efi_to_phys(efi.systab->tables), - table_size); - - retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, - sizeof(efi_config_table_64_t), NULL); - - early_memunmap(config_tables, table_size); -out: - early_memunmap(efi.systab, sizeof(efi_system_table_t)); - return retval; -} - -/* - * Return true for RAM regions we want to permanently reserve. - */ -static __init int is_reserve_region(efi_memory_desc_t *md) -{ - switch (md->type) { - case EFI_LOADER_CODE: - case EFI_LOADER_DATA: - case EFI_BOOT_SERVICES_CODE: - case EFI_BOOT_SERVICES_DATA: - case EFI_CONVENTIONAL_MEMORY: - case EFI_PERSISTENT_MEMORY: - return 0; - default: - break; - } - return is_normal_ram(md); -} - -static __init void reserve_regions(void) -{ - efi_memory_desc_t *md; - u64 paddr, npages, size; - - if (efi_enabled(EFI_DBG)) - pr_info("Processing EFI memory map:\n"); - - for_each_efi_memory_desc(&memmap, md) { - paddr = md->phys_addr; - npages = md->num_pages; - - if (efi_enabled(EFI_DBG)) { - char buf[64]; - - pr_info(" 0x%012llx-0x%012llx %s", - paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1, - efi_md_typeattr_format(buf, sizeof(buf), md)); - } - - memrange_efi_to_native(&paddr, &npages); - size = npages << PAGE_SHIFT; - - if (is_normal_ram(md)) - early_init_dt_add_memory(paddr, size); - - if (is_reserve_region(md)) { - memblock_mark_nomap(paddr, size); - if (efi_enabled(EFI_DBG)) - pr_cont("*"); - } - - if (efi_enabled(EFI_DBG)) - pr_cont("\n"); - } -} - -void __init efi_parse_fdt(void *fdt) -{ - struct efi_fdt_params params; - - /* Grab UEFI information placed in FDT by stub */ - if (!efi_get_fdt_params(fdt, ¶ms)) - return; - - efi_system_table = params.system_table; - - memmap.phys_map = (void *)params.mmap; - memmap.desc_size = params.desc_size; - memmap.desc_version = params.desc_ver; - memmap.nr_map = params.mmap_size / params.desc_size; - - set_bit(EFI_MEMMAP, &efi.flags); -} - -void __init efi_init(void) -{ - int mmap_size = memmap.nr_map * memmap.desc_size; - - if (!efi_enabled(EFI_MEMMAP)) - return; - - memmap.map = early_memremap((u64)memmap.phys_map, mmap_size); - memmap.map_end = memmap.map + mmap_size; - - if (uefi_init() < 0) - return; - - reserve_regions(); - early_memunmap(memmap.map, mmap_size); - memblock_mark_nomap((unsigned long)memmap.phys_map & PAGE_MASK, - PAGE_ALIGN(mmap_size)); -} - -static bool __init efi_virtmap_init(void) -{ - efi_memory_desc_t *md; - - for_each_efi_memory_desc(&memmap, md) { - u64 paddr, npages, size; - pteval_t prot_val; - - if (!(md->attribute & EFI_MEMORY_RUNTIME)) - continue; - if (md->virt_addr == 0) - return false; - - paddr = md->phys_addr; - npages = md->num_pages; - memrange_efi_to_native(&paddr, &npages); - size = npages << PAGE_SHIFT; - - /* - * Order is important here: memory regions may have all of the - * bits below set (and usually do), and any memory that has the - * EFI_MEMORY_WB bit set may be covered by the linear mapping - * and mapped write-back cacheable already. So check the - * EFI_MEMORY_WB bit first. - */ - if (md->attribute & EFI_MEMORY_WB) { - prot_val = pgprot_val(PAGE_KERNEL_EXEC); - } else if (md->attribute & EFI_MEMORY_WT) { - prot_val = PROT_NORMAL_WT; - } else if (md->attribute & EFI_MEMORY_WC) { - prot_val = PROT_NORMAL_NC; - } else if (md->attribute & EFI_MEMORY_UC) { - prot_val = PROT_DEVICE_nGnRnE; - } else { - pr_warn(" EFI remap 0x%012llx: not remapping due to unsupported memory attributes (0x%llx)\n", - md->phys_addr, md->attribute); - continue; - } - - /* - * Since the UEFI spec requires only the type attributes to be - * identical within the same 64 KB page frame, we may encounter - * regions that are not 64 KB aligned, but whose attributes only - * differ from adjacent regions in the permission bits. - * This means we can only enforce any permission restrictions if - * the boundaries of this region are aligned to the OS page - * size. - */ - if (PAGE_SIZE == EFI_PAGE_SIZE || - (PAGE_ALIGNED(md->virt_addr) && - PAGE_ALIGNED(md->virt_addr + md->num_pages * EFI_PAGE_SIZE))) { - - if (md->attribute & EFI_MEMORY_RO) - prot_val |= PTE_RDONLY; - if (md->attribute & EFI_MEMORY_XP) - prot_val |= PTE_PXN; - } - - pr_info(" EFI remap 0x%012llx => %p (R%c%c)\n", - md->phys_addr, (void *)md->virt_addr, - prot_val & PTE_RDONLY ? '-' : 'W', - prot_val & PTE_PXN ? '-' : 'X'); - - create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size, - __pgprot(prot_val)); - } - return true; -} - -/* - * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., - * non-early mapping of the UEFI system table and virtual mappings for all - * EFI_MEMORY_RUNTIME regions. - */ -static int __init arm64_enable_runtime_services(void) -{ - u64 mapsize; - - if (!efi_enabled(EFI_BOOT)) { - pr_info("EFI services will not be available.\n"); - return -1; - } - - if (efi_runtime_disabled()) { - pr_info("EFI runtime services will be disabled.\n"); - return -1; - } - - pr_info("Remapping and enabling EFI services.\n"); - - mapsize = memmap.map_end - memmap.map; - memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map, - mapsize); - if (!memmap.map) { - pr_err("Failed to remap EFI memory map\n"); - return -1; - } - memmap.map_end = memmap.map + mapsize; - efi.memmap = &memmap; - - efi.systab = (__force void *)ioremap_cache(efi_system_table, - sizeof(efi_system_table_t)); - if (!efi.systab) { - pr_err("Failed to remap EFI System Table\n"); - return -1; - } - set_bit(EFI_SYSTEM_TABLES, &efi.flags); - - if (!efi_virtmap_init()) { - pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n"); - return -1; - } - - /* Set up runtime services function pointers */ - efi_native_runtime_setup(); - set_bit(EFI_RUNTIME_SERVICES, &efi.flags); - - efi.runtime_version = efi.systab->hdr.revision; - - return 0; -} -early_initcall(arm64_enable_runtime_services); static int __init arm64_dmi_init(void) { @@ -368,30 +31,6 @@ static int __init arm64_dmi_init(void) } core_initcall(arm64_dmi_init); -static void efi_set_pgd(struct mm_struct *mm) -{ - if (mm == &init_mm) - cpu_set_reserved_ttbr0(); - else - cpu_switch_mm(mm->pgd, mm); - - flush_tlb_all(); - if (icache_is_aivivt()) - __flush_icache_all(); -} - -void efi_virtmap_load(void) -{ - preempt_disable(); - efi_set_pgd(&efi_mm); -} - -void efi_virtmap_unload(void) -{ - efi_set_pgd(current->active_mm); - preempt_enable(); -} - /* * UpdateCapsule() depends on the system being shutdown via * ResetSystem(). diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index d9140208fc1c..62b272375422 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -1,7 +1,9 @@ # # Makefile for linux kernel # -obj-$(CONFIG_EFI) += efi.o vars.o reboot.o +arm-obj-$(CONFIG_ARM64)$(CONFIG_ARM) := arm-init.o arm-runtime.o + +obj-$(CONFIG_EFI) += efi.o vars.o reboot.o $(arm-obj-y) obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c new file mode 100644 index 000000000000..56987a5b9033 --- /dev/null +++ b/drivers/firmware/efi/arm-init.c @@ -0,0 +1,214 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 2.4 + * + * Copyright (C) 2013 - 2015 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +struct efi_memory_map memmap; + +u64 efi_system_table; + +static int uefi_debug __initdata; +static int __init uefi_debug_setup(char *str) +{ + uefi_debug = 1; + + return 0; +} +early_param("uefi_debug", uefi_debug_setup); + +static int __init is_normal_ram(efi_memory_desc_t *md) +{ + if (md->attribute & EFI_MEMORY_WB) + return 1; + return 0; +} + +/* + * Translate a EFI virtual address into a physical address: this is necessary, + * as some data members of the EFI system table are virtually remapped after + * SetVirtualAddressMap() has been called. + */ +static phys_addr_t efi_to_phys(unsigned long addr) +{ + efi_memory_desc_t *md; + + for_each_efi_memory_desc(&memmap, md) { + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + /* no virtual mapping has been installed by the stub */ + break; + if (md->virt_addr <= addr && + (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT)) + return md->phys_addr + addr - md->virt_addr; + } + return addr; +} + +static int __init uefi_init(void) +{ + efi_char16_t *c16; + void *config_tables; + u64 table_size; + char vendor[100] = "unknown"; + int i, retval; + + efi.systab = early_memremap(efi_system_table, + sizeof(efi_system_table_t)); + if (efi.systab == NULL) { + pr_warn("Unable to map EFI system table.\n"); + return -ENOMEM; + } + + set_bit(EFI_BOOT, &efi.flags); + set_bit(EFI_64BIT, &efi.flags); + + /* + * Verify the EFI Table + */ + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + pr_err("System table signature incorrect\n"); + retval = -EINVAL; + goto out; + } + if ((efi.systab->hdr.revision >> 16) < 2) + pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff); + + /* Show what we know for posterity */ + c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor), + sizeof(vendor) * sizeof(efi_char16_t)); + if (c16) { + for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) + vendor[i] = c16[i]; + vendor[i] = '\0'; + early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); + } + + pr_info("EFI v%u.%.02u by %s\n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff, vendor); + + table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; + config_tables = early_memremap(efi_to_phys(efi.systab->tables), + table_size); + + retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, + sizeof(efi_config_table_64_t), NULL); + + early_memunmap(config_tables, table_size); +out: + early_memunmap(efi.systab, sizeof(efi_system_table_t)); + return retval; +} + +/* + * Return true for RAM regions we want to permanently reserve. + */ +static __init int is_reserve_region(efi_memory_desc_t *md) +{ + switch (md->type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_CONVENTIONAL_MEMORY: + case EFI_PERSISTENT_MEMORY: + return 0; + default: + break; + } + return is_normal_ram(md); +} + +static __init void reserve_regions(void) +{ + efi_memory_desc_t *md; + u64 paddr, npages, size; + + if (efi_enabled(EFI_DBG)) + pr_info("Processing EFI memory map:\n"); + + for_each_efi_memory_desc(&memmap, md) { + paddr = md->phys_addr; + npages = md->num_pages; + + if (efi_enabled(EFI_DBG)) { + char buf[64]; + + pr_info(" 0x%012llx-0x%012llx %s", + paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1, + efi_md_typeattr_format(buf, sizeof(buf), md)); + } + + memrange_efi_to_native(&paddr, &npages); + size = npages << PAGE_SHIFT; + + if (is_normal_ram(md)) + early_init_dt_add_memory(paddr, size); + + if (is_reserve_region(md)) { + memblock_mark_nomap(paddr, size); + if (efi_enabled(EFI_DBG)) + pr_cont("*"); + } + + if (efi_enabled(EFI_DBG)) + pr_cont("\n"); + } +} + +void __init efi_parse_fdt(void *fdt) +{ + struct efi_fdt_params params; + + /* Grab UEFI information placed in FDT by stub */ + if (!efi_get_fdt_params(fdt, ¶ms)) + return; + + efi_system_table = params.system_table; + + memmap.phys_map = (void *)params.mmap; + memmap.desc_size = params.desc_size; + memmap.desc_version = params.desc_ver; + memmap.nr_map = params.mmap_size / params.desc_size; + + set_bit(EFI_MEMMAP, &efi.flags); +} + +void __init efi_init(void) +{ + int mmap_size = memmap.nr_map * memmap.desc_size; + + if (!efi_enabled(EFI_MEMMAP)) + return; + + memmap.map = early_memremap((u64)memmap.phys_map, mmap_size); + memmap.map_end = memmap.map + mmap_size; + + if (uefi_init() < 0) + return; + + reserve_regions(); + early_memunmap(memmap.map, mmap_size); + memblock_mark_nomap((unsigned long)memmap.phys_map & PAGE_MASK, + PAGE_ALIGN(mmap_size)); +} diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c new file mode 100644 index 000000000000..5a94ea48670d --- /dev/null +++ b/drivers/firmware/efi/arm-runtime.c @@ -0,0 +1,191 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 2.4 + * + * Copyright (C) 2013, 2014 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss; + +extern u64 efi_system_table; + +static struct mm_struct efi_mm = { + .mm_rb = RB_ROOT, + .pgd = efi_pgd, + .mm_users = ATOMIC_INIT(2), + .mm_count = ATOMIC_INIT(1), + .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem), + .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), + .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), + INIT_MM_CONTEXT(efi_mm) +}; + +static bool __init efi_virtmap_init(void) +{ + efi_memory_desc_t *md; + + for_each_efi_memory_desc(&memmap, md) { + u64 paddr, npages, size; + pteval_t prot_val; + + if (!(md->attribute & EFI_MEMORY_RUNTIME)) + continue; + if (md->virt_addr == 0) + return false; + + paddr = md->phys_addr; + npages = md->num_pages; + memrange_efi_to_native(&paddr, &npages); + size = npages << PAGE_SHIFT; + + /* + * Order is important here: memory regions may have all of the + * bits below set (and usually do), and any memory that has the + * EFI_MEMORY_WB bit set may be covered by the linear mapping + * and mapped write-back cacheable already. So check the + * EFI_MEMORY_WB bit first. + */ + if (md->attribute & EFI_MEMORY_WB) { + prot_val = pgprot_val(PAGE_KERNEL_EXEC); + } else if (md->attribute & EFI_MEMORY_WT) { + prot_val = PROT_NORMAL_WT; + } else if (md->attribute & EFI_MEMORY_WC) { + prot_val = PROT_NORMAL_NC; + } else if (md->attribute & EFI_MEMORY_UC) { + prot_val = PROT_DEVICE_nGnRnE; + } else { + pr_warn(" EFI remap 0x%012llx: not remapping due to unsupported memory attributes (0x%llx)\n", + md->phys_addr, md->attribute); + continue; + } + + /* + * Since the UEFI spec requires only the type attributes to be + * identical within the same 64 KB page frame, we may encounter + * regions that are not 64 KB aligned, but whose attributes only + * differ from adjacent regions in the permission bits. + * This means we can only enforce any permission restrictions if + * the boundaries of this region are aligned to the OS page + * size. + */ + if (PAGE_SIZE == EFI_PAGE_SIZE || + (PAGE_ALIGNED(md->virt_addr) && + PAGE_ALIGNED(md->virt_addr + md->num_pages * EFI_PAGE_SIZE))) { + + if (md->attribute & EFI_MEMORY_RO) + prot_val |= PTE_RDONLY; + if (md->attribute & EFI_MEMORY_XP) + prot_val |= PTE_PXN; + } + + pr_info(" EFI remap 0x%012llx => %p (R%c%c)\n", + md->phys_addr, (void *)md->virt_addr, + prot_val & PTE_RDONLY ? '-' : 'W', + prot_val & PTE_PXN ? '-' : 'X'); + + create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size, + __pgprot(prot_val)); + } + return true; +} + +/* + * Enable the UEFI Runtime Services if all prerequisites are in place, i.e., + * non-early mapping of the UEFI system table and virtual mappings for all + * EFI_MEMORY_RUNTIME regions. + */ +static int __init arm64_enable_runtime_services(void) +{ + u64 mapsize; + + if (!efi_enabled(EFI_BOOT)) { + pr_info("EFI services will not be available.\n"); + return -1; + } + + if (efi_runtime_disabled()) { + pr_info("EFI runtime services will be disabled.\n"); + return -1; + } + + pr_info("Remapping and enabling EFI services.\n"); + + mapsize = memmap.map_end - memmap.map; + memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map, + mapsize); + if (!memmap.map) { + pr_err("Failed to remap EFI memory map\n"); + return -1; + } + memmap.map_end = memmap.map + mapsize; + efi.memmap = &memmap; + + efi.systab = (__force void *)ioremap_cache(efi_system_table, + sizeof(efi_system_table_t)); + if (!efi.systab) { + pr_err("Failed to remap EFI System Table\n"); + return -1; + } + set_bit(EFI_SYSTEM_TABLES, &efi.flags); + + if (!efi_virtmap_init()) { + pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n"); + return -1; + } + + /* Set up runtime services function pointers */ + efi_native_runtime_setup(); + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); + + efi.runtime_version = efi.systab->hdr.revision; + + return 0; +} +early_initcall(arm64_enable_runtime_services); + +static void efi_set_pgd(struct mm_struct *mm) +{ + if (mm == &init_mm) + cpu_set_reserved_ttbr0(); + else + cpu_switch_mm(mm->pgd, mm); + + flush_tlb_all(); + if (icache_is_aivivt()) + __flush_icache_all(); +} + +void efi_virtmap_load(void) +{ + preempt_disable(); + efi_set_pgd(&efi_mm); +} + +void efi_virtmap_unload(void) +{ + efi_set_pgd(current->active_mm); + preempt_enable(); +}