From patchwork Tue Dec 13 10:25:10 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 87823 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp2123769qgi; Tue, 13 Dec 2016 02:25:22 -0800 (PST) X-Received: by 10.84.171.1 with SMTP id k1mr198306116plb.169.1481624721979; Tue, 13 Dec 2016 02:25:21 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id o32si44789525pld.152.2016.12.13.02.25.21; Tue, 13 Dec 2016 02:25:21 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-efi-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-efi-owner@vger.kernel.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753080AbcLMKZV (ORCPT + 2 others); Tue, 13 Dec 2016 05:25:21 -0500 Received: from mail-wj0-f169.google.com ([209.85.210.169]:35952 "EHLO mail-wj0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752937AbcLMKZU (ORCPT ); Tue, 13 Dec 2016 05:25:20 -0500 Received: by mail-wj0-f169.google.com with SMTP id tk12so96239314wjb.3 for ; Tue, 13 Dec 2016 02:25:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=BB5pqpsV5brRUGvaQsymIipjQdnn/chPUVKBiVhyycQ=; b=JDEOY3Qnfv/saBXfxlhxLzGAfaWWMtmt0R5N9s2BUKd5xfvNcvTbG/xGZCwNxBBNfa T2ujn+QT78qxwPBqW/XfBoOBAhaXX2gTwAqrs2U6NDDrtTuHgAbxSPg3hdo8zymDqdmD YnPOHiU0hQSzKz5ORSmFY78B1d6Bp0aR7deC4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=BB5pqpsV5brRUGvaQsymIipjQdnn/chPUVKBiVhyycQ=; b=BKgSATAALEuvCjQRt8PO4rXuA2xcvS1060SOxY98u+3UeI4f/IreDWxFJ1tBdlFGre SqpOouaYdrEaXZJU9uuXYGK93BIzI8S9K4AV1FgYmODydPWUtbRqnvXoczKuA+ZfNXxf DEnYd036B++RWSQjnVqOEwrVx7R+cO0uJKvaPnmpSHZVSu5JKKWEclscfkiSxzoweEd7 OS0P3WJT9rvCqPAu82R/69xblauJY8S7h3caR7z3OfUjsz7wSdxI/fWtYC4+wp/lc5TC EsdzlvxROYx4SpgQn6QP7Q3je4M30FGrQzEhD+30hrGNtlaSmarMqa8gtersLcKSRc4k +USQ== X-Gm-Message-State: AKaTC00hN1yMCxMLy7uB7hLMBHE4kLsNzNfbt2YsL81m3HwmQBJxOXvxwbW79t1WDfXnfcZr X-Received: by 10.194.174.39 with SMTP id bp7mr82277498wjc.5.1481624718179; Tue, 13 Dec 2016 02:25:18 -0800 (PST) Received: from localhost.localdomain ([105.150.173.252]) by smtp.gmail.com with ESMTPSA id f134sm1878607wmf.19.2016.12.13.02.25.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 13 Dec 2016 02:25:15 -0800 (PST) From: Ard Biesheuvel To: linux-efi@vger.kernel.org, matt@codeblueprint.co.uk, pjones@redhat.com Cc: Ard Biesheuvel Subject: [PATCH v3] efi: prune invalid memory map entries Date: Tue, 13 Dec 2016 10:25:10 +0000 Message-Id: <1481624710-20892-1-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.7.4 Sender: linux-efi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-efi@vger.kernel.org From: Peter Jones Some machines, such as the Lenovo ThinkPad W541 with firmware GNET80WW (2.28), include memory map entries with phys_addr=0x0 and num_pages=0. Currently the log output for this case (with efi=debug) looks like: [ 0.000000] efi: mem45: [Reserved | | | | | | | | | | | | ] range=[0x0000000000000000-0xffffffffffffffff] (0MB) This is clearly wrong, and also not as informative as it could be. This patch changes it so that if we find obviously invalid memory map entries, we print an error and those entries. It also detects the display of the address range calculation overflow, so the new output is: [ 0.000000] efi: [Firmware Bug]: Invalid EFI memory map entries: [ 0.000000] efi: mem45: [Reserved | | | | | | | | | | | | ] range=[0x0000000000000000-0x0000000000000000] (invalid) It also detects memory map sizes that would overflow the physical address, for example phys_addr=0xfffffffffffff000 and num_pages=0x0200000000000001, and prints: [ 0.000000] efi: [Firmware Bug]: Invalid EFI memory map entries: [ 0.000000] efi: mem45: [Reserved | | | | | | | | | | | | ] range=[phys_addr=0xfffffffffffff000-0x20ffffffffffffffff] (invalid) It then removes these entries from the memory map. Cc: Matt Fleming Signed-off-by: Peter Jones [ardb: refactor for clarity with no functional changes, avoid PAGE_SHIFT] Signed-off-by: Ard Biesheuvel --- I took the liberty of refactoring the code because I found it difficult to understand. No functional changes are intended, although I couldn't figure out if the memcpy() in the original code is (a) correct, and (b) attempts to copy multiple entries at once. Also, pr_warn sounds more appropriate for complaining about broken firmware, but perhaps this is archite-cultural thing as well. arch/x86/platform/efi/efi.c | 70 ++++++++++++++++++++ include/linux/efi.h | 1 + 2 files changed, 71 insertions(+) -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index bf99aa7005eb..0a1550b82beb 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -210,6 +210,74 @@ int __init efi_memblock_x86_reserve_range(void) return 0; } +#define OVERFLOW_ADDR_SHIFT (64 - EFI_PAGE_SHIFT) +#define OVERFLOW_ADDR_MASK (U64_MAX << OVERFLOW_ADDR_SHIFT) +#define U64_HIGH_BIT (~(U64_MAX >> 1)) + +static bool __init efi_memmap_entry_valid(const efi_memory_desc_t *md, int i) +{ + static __initdata bool once = true; + u64 end = (md->num_pages << EFI_PAGE_SHIFT) + md->phys_addr - 1; + u64 end_hi = 0; + char buf[64]; + + if (md->num_pages == 0) { + end = 0; + } else if (md->num_pages > EFI_PAGES_MAX || + EFI_PAGES_MAX - md->num_pages < + (md->phys_addr >> EFI_PAGE_SHIFT)) { + end_hi = (md->num_pages & OVERFLOW_ADDR_MASK) + >> OVERFLOW_ADDR_SHIFT; + + if ((md->phys_addr & U64_HIGH_BIT) && !(end & U64_HIGH_BIT)) + end_hi += 1; + } else { + return true; + } + + if (once) { + pr_warn(FW_BUG "Invalid EFI memory map entries:\n"); + once = false; + } + + if (end_hi) { + pr_warn("mem%02u: %s range=[0x%016llx-0x%llx%016llx] (invalid)\n", + i, efi_md_typeattr_format(buf, sizeof(buf), md), + md->phys_addr, end_hi, end); + } else { + pr_warn("mem%02u: %s range=[0x%016llx-0x%016llx] (invalid)\n", + i, efi_md_typeattr_format(buf, sizeof(buf), md), + md->phys_addr, end); + } + return false; +} + +static void __init efi_clean_memmap(void) +{ + efi_memory_desc_t *out = efi.memmap.map; + const efi_memory_desc_t *in = out; + const efi_memory_desc_t *end = efi.memmap.map_end; + int i, n_removal; + + for (i = n_removal = 0; in < end; i++) { + if (efi_memmap_entry_valid(in, i)) { + if (out != in) + memcpy(out, in, efi.memmap.desc_size); + out = (void *)out + efi.memmap.desc_size; + } else { + n_removal++; + } + in = (void *)in + efi.memmap.desc_size; + } + + if (n_removal > 0) { + u64 size = efi.memmap.nr_map - n_removal; + + pr_warn("Removing %d invalid memory map entries.\n", n_removal); + efi_memmap_install(efi.memmap.phys_map, size); + } +} + void __init efi_print_memmap(void) { efi_memory_desc_t *md; @@ -472,6 +540,8 @@ void __init efi_init(void) } } + efi_clean_memmap(); + if (efi_enabled(EFI_DBG)) efi_print_memmap(); } diff --git a/include/linux/efi.h b/include/linux/efi.h index 2d089487d2da..fda79cdf9f10 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -103,6 +103,7 @@ typedef struct { #define EFI_PAGE_SHIFT 12 #define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) +#define EFI_PAGES_MAX (U64_MAX >> EFI_PAGE_SHIFT) typedef struct { u32 type;