Received: by 2002:ac0:a581:0:0:0:0:0 with SMTP id m1-v6csp236287imm; Tue, 19 Jun 2018 20:07:08 -0700 (PDT) X-Google-Smtp-Source: ADUXVKKdGUlHCl3hBJBts3sGk8VVQGHnsDykY38gPazYEm2iN996qA52YFTqp2ogFflrg+AKninb X-Received: by 2002:a17:902:8348:: with SMTP id z8-v6mr21456433pln.239.1529464028251; Tue, 19 Jun 2018 20:07:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529464028; cv=none; d=google.com; s=arc-20160816; b=Q5ZnWxAIc/g6KwoTnTqi6m1a2E8OvtunHABnGsFzrCRdNn/7oR7VaPC3mFLcj6spdT DkMML2GLAuh/Z8AauDGrpdAtCBRUxexDDMedXL72kQf20xSDPMOVBP6cKwADBdJCpT/3 kAhoPFyKyKm+nDrWKL00aj85BCd1mBDpMLzR/7R+zXxzs7ZbvixHVbQ4Achr1G1cUWJM gwM/AzU1Yxixvc62jnS2/JPLNnyWXEJRjLE0fj8oBdMSTFcT7Vpo4ol7T5xW727cRcrj 6PLBxzglA10Wogc9JNsb+m47GWgfibGprZ68UJO7Ih8xBSLxcpUmsWLQkQiH8aMBLBZ1 bF0g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :arc-authentication-results; bh=bHsCKADrURuNBvQi8jhMEMIAp2+6yRZTjhOetlvYLi8=; b=goQN0DuoTFm1dYWiQxDin5aJtEnZVXa2+jcsw0AJJirf5Ze+M42XLLRQryEFWb33sW RC8Xaxd5sDKPJ0WKM+Aixx+PBG27/sVQlykcaY59Nw7drly7yMBQBR0A6ClHsyIUREhM D0D6iR1hhoX/2/Y7nDlXwXgtbGU6GZkj9TnxMp7z9bxKfOfutTl2Pv14lYhSe1vDGbAh XIHfntDst+4W4UCUSb0j1g1iSG4HhMXYW7ts0r1uofeLMgqz6DtHBEeIreUsLsduBMiY v6ToX/A+Ge9EM4MbMrIeJ5+Xcp4TMbRR946HxVoCqw05vXxRn6T9cZ0YvP7bUcvCEZ3k 5pQg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z23-v6si1203301pfh.266.2018.06.19.20.06.54; Tue, 19 Jun 2018 20:07:08 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754588AbeFTDFu (ORCPT + 99 others); Tue, 19 Jun 2018 23:05:50 -0400 Received: from mga05.intel.com ([192.55.52.43]:1910 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754030AbeFTDDk (ORCPT ); Tue, 19 Jun 2018 23:03:40 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 19 Jun 2018 20:03:41 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,245,1526367600"; d="scan'208";a="64681732" Received: from sai-dev-mach.sc.intel.com ([143.183.140.145]) by fmsmga004.fm.intel.com with ESMTP; 19 Jun 2018 20:03:40 -0700 From: Sai Praneeth Prakhya To: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Sai Praneeth , Lee Chun-Yi , Borislav Petkov , Tony Luck , Dave Hansen , Bhupesh Sharma , Ricardo Neri , Peter Zijlstra , Ravi Shankar , Matt Fleming , Ard Biesheuvel Subject: [PATCH V3] x86/efi: Free allocated memory if remap fails Date: Tue, 19 Jun 2018 20:03:26 -0700 Message-Id: <1529463806-4046-1-git-send-email-sai.praneeth.prakhya@intel.com> X-Mailer: git-send-email 2.7.4 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Sai Praneeth efi_memmap_alloc(), as the name suggests, allocates memory for a new efi memory map. It's referenced from couple of places, namely, efi_arch_mem_reserve() and efi_free_boot_services(). These callers, after allocating memory, remap it for further use. As usual, a routine check is performed to confirm successful remap. If the remap fails, ideally, the allocated memory should be freed but presently we just return without freeing it up. Hence, fix this bug by introducing efi_memmap_free() which frees memory allocated by efi_memmap_alloc(). As efi_memmap_alloc() allocates memory depending on whether mm_init() has already been invoked or not, introduce a new argument called "late" that lets us know which type of allocation was performed by efi_memmap_alloc(). Later, this is used by efi_memmap_free() to invoke the appropriate method to free the allocated memory. The other main purpose "late" argument serves is to make sure that efi_memmap_alloc() and efi_memmap_free() are always binded properly i.e. there could be a scenario in which efi_memmap_alloc() is used before slab_is_available() and efi_memmap_free() could be used after slab_is_available(). Without "late", this could break because allocation would have been done using memblock_alloc() while freeing will be done using __free_pages(). Since these API's could easily be misused make it explicit, so that the caller has to pass "late" argument to efi_memmap_alloc() and later use the same for efi_memmap_free(). Also, efi_fake_memmap() references efi_memmap_alloc() but it frees memory correctly using memblock_free(), but replace it with efi_memmap_free() to maintain consistency, as in, allocate memory with efi_memmap_alloc() and free memory with efi_memmap_free(). It's a fact that memremap() and early_memremap() might never fail and this code might never get a chance to run but to maintain good kernel programming semantics, we might need this patch. Signed-off-by: Sai Praneeth Prakhya Cc: Lee Chun-Yi Cc: Borislav Petkov Cc: Tony Luck Cc: Dave Hansen Cc: Bhupesh Sharma Cc: Ricardo Neri Cc: Peter Zijlstra Cc: Ravi Shankar Cc: Matt Fleming Cc: Ard Biesheuvel --- Changes from V2 to V3: ---------------------- 1. Add a new argument "late" to efi_memmap_alloc(), so that efi_memmap_alloc() could communicate the type of allocation performed. 2. Re-introduce efi_memmap_free() (from V1) but with an extra argument "late", to know the type of allocation performed by efi_memmap_alloc(). Changes from V1 to V2: ---------------------- 1. Fix the bug of freeing memory map that was just installed by correctly calling free_pages(). 2. Call memblock_free() and __free_pages() directly from the appropriate places instead of efi_memmap_free(). Note: Patch based on Linus's mainline tree V4.18-rc1 arch/x86/platform/efi/quirks.c | 16 ++++++++++++---- drivers/firmware/efi/fake_mem.c | 5 +++-- drivers/firmware/efi/memmap.c | 38 ++++++++++++++++++++++++++++++++++++-- include/linux/efi.h | 3 ++- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 36c1f8b9f7e0..ef5698a3af7a 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -248,6 +248,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) efi_memory_desc_t md; int num_entries; void *new; + bool late; if (efi_mem_desc_lookup(addr, &md)) { pr_err("Failed to lookup EFI memory descriptor for %pa\n", &addr); @@ -276,7 +277,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) new_size = efi.memmap.desc_size * num_entries; - new_phys = efi_memmap_alloc(num_entries); + new_phys = efi_memmap_alloc(num_entries, &late); if (!new_phys) { pr_err("Could not allocate boot services memmap\n"); return; @@ -285,6 +286,7 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) new = early_memremap(new_phys, new_size); if (!new) { pr_err("Failed to map new boot services memmap\n"); + efi_memmap_free(new_phys, num_entries, late); return; } @@ -375,6 +377,7 @@ void __init efi_free_boot_services(void) efi_memory_desc_t *md; int num_entries = 0; void *new, *new_md; + bool late; for_each_efi_memory_desc(md) { unsigned long long start = md->phys_addr; @@ -420,7 +423,7 @@ void __init efi_free_boot_services(void) return; new_size = efi.memmap.desc_size * num_entries; - new_phys = efi_memmap_alloc(num_entries); + new_phys = efi_memmap_alloc(num_entries, &late); if (!new_phys) { pr_err("Failed to allocate new EFI memmap\n"); return; @@ -429,7 +432,7 @@ void __init efi_free_boot_services(void) new = memremap(new_phys, new_size, MEMREMAP_WB); if (!new) { pr_err("Failed to map new EFI memmap\n"); - return; + goto free_mem; } /* @@ -452,8 +455,13 @@ void __init efi_free_boot_services(void) if (efi_memmap_install(new_phys, num_entries)) { pr_err("Could not install new EFI memmap\n"); - return; + goto free_mem; } + + return; + +free_mem: + efi_memmap_free(new_phys, num_entries, late); } /* diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c index 6c7d60c239b5..52cb2b7fc2f7 100644 --- a/drivers/firmware/efi/fake_mem.c +++ b/drivers/firmware/efi/fake_mem.c @@ -57,6 +57,7 @@ void __init efi_fake_memmap(void) phys_addr_t new_memmap_phy; void *new_memmap; int i; + bool late; if (!nr_fake_mem) return; @@ -71,7 +72,7 @@ void __init efi_fake_memmap(void) } /* allocate memory for new EFI memmap */ - new_memmap_phy = efi_memmap_alloc(new_nr_map); + new_memmap_phy = efi_memmap_alloc(new_nr_map, &late); if (!new_memmap_phy) return; @@ -79,7 +80,7 @@ void __init efi_fake_memmap(void) new_memmap = early_memremap(new_memmap_phy, efi.memmap.desc_size * new_nr_map); if (!new_memmap) { - memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map); + efi_memmap_free(new_memmap_phy, new_nr_map, late); return; } diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 5fc70520e04c..678e85704054 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -32,6 +32,7 @@ static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) /** * efi_memmap_alloc - Allocate memory for the EFI memory map * @num_entries: Number of entries in the allocated map. + * @late: Type of allocation performed (late or early?). * * Depending on whether mm_init() has already been invoked or not, * either memblock or "normal" page allocation is used. @@ -39,17 +40,50 @@ static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) * Returns the physical address of the allocated memory map on * success, zero on failure. */ -phys_addr_t __init efi_memmap_alloc(unsigned int num_entries) +phys_addr_t __init efi_memmap_alloc(unsigned int num_entries, bool *late) { unsigned long size = num_entries * efi.memmap.desc_size; - if (slab_is_available()) + if (!late) + return 0; + + *late = slab_is_available(); + + if (*late) return __efi_memmap_alloc_late(size); return __efi_memmap_alloc_early(size); } /** + * efi_memmap_free - Free memory allocated by efi_memmap_alloc() + * @mem: Physical address allocated by efi_memmap_alloc(). + * @num_entries: Number of entries in the allocated map. + * @late: What type of allocation did efi_memmap_alloc() perform? + * + * Use this function _only_ in conjunction with efi_memmap_alloc(). + * efi_memmap_alloc() allocates memory depending on whether mm_init() + * has already been invoked or not. It uses either memblock or "normal" + * page allocation. Since the allocation is done in two different ways, + * similarly, we free it in two different ways. + * + */ +void __init efi_memmap_free(phys_addr_t mem, unsigned int num_entries, bool late) +{ + unsigned long size = num_entries * efi.memmap.desc_size; + unsigned int order = get_order(size); + phys_addr_t end = mem + size - 1; + + if (late) { + __free_pages(pfn_to_page(PHYS_PFN(mem)), order); + return; + } + + if (memblock_free(mem, size)) + pr_err("Failed to free mem from %pa to %pa\n", &mem, &end); +} + +/** * __efi_memmap_init - Common code for mapping the EFI memory map * @data: EFI memory map data * @late: Use early or late mapping function? diff --git a/include/linux/efi.h b/include/linux/efi.h index 56add823f190..f7f98d9a76f2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1007,7 +1007,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, #endif extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); -extern phys_addr_t __init efi_memmap_alloc(unsigned int num_entries); +extern phys_addr_t __init efi_memmap_alloc(unsigned int num_entries, bool *late); extern int __init efi_memmap_init_early(struct efi_memory_map_data *data); extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size); extern void __init efi_memmap_unmap(void); @@ -1016,6 +1016,7 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range); extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, struct efi_mem_range *mem); +void __init efi_memmap_free(phys_addr_t mem, unsigned int num_entries, bool late); extern int efi_config_init(efi_config_table_type_t *arch_tables); #ifdef CONFIG_EFI_ESRT -- 2.7.4