Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp3117736imm; Sun, 13 May 2018 04:43:38 -0700 (PDT) X-Google-Smtp-Source: AB8JxZr6w3JRTdrSW0T0LU9Py+Op1s8D6fXUxof0AKzx14Xa/kwDIm5rj1U700J86GmTAsRfculg X-Received: by 2002:a62:48d1:: with SMTP id q78-v6mr6362971pfi.70.1526211818578; Sun, 13 May 2018 04:43:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526211818; cv=none; d=google.com; s=arc-20160816; b=H1e4XXWC0Furb+zFgADDZf4HO9taR7lqsY6j5hpawEDb5l0qed3tbM0NS2FdJ5NHXq ex3GDDqWwMYXjcLtTXcN04I+kGR5Gxc+V65rb1vW/APQa3NItOUr8L5kAUU4zOgPYhgu z9hKSAlTzgkif+r5zxFtkiFYE5zUWhXPNCLBR5xkDPS48jHlGwehiUuz4kw7gekSM//m TtHL/CLlrEHR1kq/qDV9H0/MHU3C14t8+Y60SNYUDE5EIgaXEikm9AucYD70Io4nb6fs O4pyrq+oH52eVXBCX6K7nwDV0EQ++idKdxA4QAj9y+pjPRo/gFpMUfNOqZt2Q/nt5hMn LaeQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=HDlO4bqLb+U2BSYT976bgs0oIBesXRx1N7J4/HTHgcU=; b=Tp/6WsLV2opApgdplqVW1GSisP+ged4MMxNuTZmhqan0qezTa2QQ5erfVSuxS33SHw 5TtOcjkomZdHNdSAMo4zCmaVWT5J+OxlIHWoXQ3tXb7Q4G27ILGCkRdke2uXWMJ3Fns4 OW+rHedy1N3GA6XfulkWvUsF2VYG/HLm1/rROMXsEi7PaKQsdDjZOYMUOCFdowdeyC1i XoDV9yVzxyz3a7/fyj4DmdVOU5xW9cY/FsGEqg/Ejj/9LtHXQ6GGsmz0xRCGGyEiOz7/ WSoU/GG6R1hp8vKn2hdMX2P0QlTHEjQrM+ujTEVkQPOAu1SSWYMEQa4m/kRsBlQEBEbf s0rw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Kgjnul21; 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=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id o1-v6si5739161pgp.273.2018.05.13.04.43.23; Sun, 13 May 2018 04:43:38 -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; dkim=pass header.i=@linaro.org header.s=google header.b=Kgjnul21; 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=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751338AbeEMLnM (ORCPT + 99 others); Sun, 13 May 2018 07:43:12 -0400 Received: from mail-io0-f194.google.com ([209.85.223.194]:46019 "EHLO mail-io0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750949AbeEMLnK (ORCPT ); Sun, 13 May 2018 07:43:10 -0400 Received: by mail-io0-f194.google.com with SMTP id c9-v6so11993584iob.12 for ; Sun, 13 May 2018 04:43:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=HDlO4bqLb+U2BSYT976bgs0oIBesXRx1N7J4/HTHgcU=; b=Kgjnul2149bwfMDzKjUVv9J7C+fCiucDyFRo2IPap/3hNEHWObxDn/6LGGk6hb9zZ9 K5yPbye+vcSPLb30ZEtkYMI/AixSEfbm1TzasvK5CzNqtT7C/N5atv7x6Civ6CJv1Zwp 8mDI3s2qfmntEXQ5E23fm07PwvEtashDd0wDA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=HDlO4bqLb+U2BSYT976bgs0oIBesXRx1N7J4/HTHgcU=; b=iVkbxWqE4y9N6anc18yK4T3RN+MaPfW6KKMPbpJefCfpmN00XbvDTqQh537C4TLVmu 50W0amZleb3oCSeGvNCffclY9QLndmrdtsHj1L2VEyFX4ffONn/M+Lci9faczzhgDAjw E/6mSf7DJiT4OtJhGSGSpYz3fPqyAG8wtKV4ZDGfn0flvG5kjA68mTJdlJoJgRuHmfme 91WRbjrEmRBMYQO4ehX+c8VnY7iojOyBS8tD/VLJmFXPNn9pZkEdK5qqwo1PDrFzE5qG Ywbzq/LaBsbbILkJ9b6fcrigkinlL52IgfShUVrXO0yRJ5Tn2jwo4EEjAHeX3frF9Gdg tdyg== X-Gm-Message-State: ALKqPwf1A/1Mqjl3D7qpoIrRm6an1howFhGK7QbRRRxNr8iNaTkYa2Yo rKW0TRflw5L8UuG6F2SMd3hVfLmioSiwpoatqdlG7Q== X-Received: by 2002:a6b:978e:: with SMTP id z136-v6mr6569659iod.60.1526211789273; Sun, 13 May 2018 04:43:09 -0700 (PDT) MIME-Version: 1.0 Received: by 10.107.187.134 with HTTP; Sun, 13 May 2018 04:43:08 -0700 (PDT) In-Reply-To: <7bc779ac-864c-cf43-dda7-bd9705f76be6@redhat.com> References: <20180429093558.5411-1-hdegoede@redhat.com> <20180429093558.5411-3-hdegoede@redhat.com> <7bc779ac-864c-cf43-dda7-bd9705f76be6@redhat.com> From: Ard Biesheuvel Date: Sun, 13 May 2018 13:43:08 +0200 Message-ID: Subject: Re: [PATCH v5 2/5] efi: Add embedded peripheral firmware support To: Hans de Goede Cc: "Luis R . Rodriguez" , Greg Kroah-Hartman , Thomas Gleixner , Ingo Molnar , "H . Peter Anvin" , Peter Jones , Dave Olsthoorn , Will Deacon , Andy Lutomirski , Matt Fleming , David Howells , Mimi Zohar , Josh Triplett , Dmitry Torokhov , Martin Fuzzey , Kalle Valo , Arend Van Spriel , Linus Torvalds , Nicolas Broeking , Bjorn Andersson , Torsten Duwe , Kees Cook , "the arch/x86 maintainers" , linux-efi@vger.kernel.org, Linux Kernel Mailing List Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 13 May 2018 at 13:03, Hans de Goede wrote: > Hi, > > > On 05/04/2018 06:56 AM, Ard Biesheuvel wrote: >> >> Hi Hans, >> >> One comment below, which I missed in review before. >> >> On 29 April 2018 at 11:35, Hans de Goede wrote: >>> >>> Just like with PCI options ROMs, which we save in the setup_efi_pci* >>> functions from arch/x86/boot/compressed/eboot.c, the EFI code / ROM >>> itself >>> sometimes may contain data which is useful/necessary for peripheral >>> drivers >>> to have access to. >>> >>> Specifically the EFI code may contain an embedded copy of firmware which >>> needs to be (re)loaded into the peripheral. Normally such firmware would >>> be >>> part of linux-firmware, but in some cases this is not feasible, for 2 >>> reasons: >>> >>> 1) The firmware is customized for a specific use-case of the chipset / >>> use >>> with a specific hardware model, so we cannot have a single firmware file >>> for the chipset. E.g. touchscreen controller firmwares are compiled >>> specifically for the hardware model they are used with, as they are >>> calibrated for a specific model digitizer. >>> >>> 2) Despite repeated attempts we have failed to get permission to >>> redistribute the firmware. This is especially a problem with customized >>> firmwares, these get created by the chip vendor for a specific ODM and >>> the >>> copyright may partially belong with the ODM, so the chip vendor cannot >>> give a blanket permission to distribute these. >>> >>> This commit adds support for finding peripheral firmware embedded in the >>> EFI code and making this available to peripheral drivers through the >>> standard firmware loading mechanism. >>> >>> Note we check the EFI_BOOT_SERVICES_CODE for embedded firmware near the >>> end >>> of start_kernel(), just before calling rest_init(), this is on purpose >>> because the typical EFI_BOOT_SERVICES_CODE memory-segment is too large >>> for >>> early_memremap(), so the check must be done after mm_init(). This relies >>> on EFI_BOOT_SERVICES_CODE not being free-ed until >>> efi_free_boot_services() >>> is called, which means that this will only work on x86 for now. >>> >>> Reported-by: Dave Olsthoorn >>> Suggested-by: Peter Jones >>> Acked-by: Ard Biesheuvel >>> Signed-off-by: Hans de Goede >>> --- >> >> [...] >>> >>> diff --git a/drivers/firmware/efi/embedded-firmware.c >>> b/drivers/firmware/efi/embedded-firmware.c >>> new file mode 100644 >>> index 000000000000..22a0f598b53d >>> --- /dev/null >>> +++ b/drivers/firmware/efi/embedded-firmware.c >>> @@ -0,0 +1,149 @@ >>> +// SPDX-License-Identifier: GPL-2.0 >>> +/* >>> + * Support for extracting embedded firmware for peripherals from EFI >>> code, >>> + * >>> + * Copyright (c) 2018 Hans de Goede >>> + */ >>> + >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +struct embedded_fw { >>> + struct list_head list; >>> + const char *name; >>> + void *data; >>> + size_t length; >>> +}; >>> + >>> +static LIST_HEAD(found_fw_list); >>> + >>> +static const struct dmi_system_id * const embedded_fw_table[] = { >>> + NULL >>> +}; >>> + >>> +/* >>> + * Note the efi_check_for_embedded_firmwares() code currently makes the >>> + * following 2 assumptions. This may needs to be revisited if embedded >>> firmware >>> + * is found where this is not true: >>> + * 1) The firmware is only found in EFI_BOOT_SERVICES_CODE memory >>> segments >>> + * 2) The firmware always starts at an offset which is a multiple of 8 >>> bytes >>> + */ >>> +static int __init efi_check_md_for_embedded_firmware( >>> + efi_memory_desc_t *md, const struct efi_embedded_fw_desc *desc) >>> +{ >>> + struct embedded_fw *fw; >>> + u64 i, size; >>> + u32 crc; >>> + u8 *mem; >>> + >>> + size = md->num_pages << EFI_PAGE_SHIFT; >>> + mem = memremap(md->phys_addr, size, MEMREMAP_WB); >>> + if (!mem) { >>> + pr_err("Error mapping EFI mem at %#llx\n", >>> md->phys_addr); >>> + return -ENOMEM; >>> + } >>> + >>> + size -= desc->length; >>> + for (i = 0; i < size; i += 8) { >>> + if (*((u64 *)(mem + i)) != *((u64 *)desc->prefix)) >>> + continue; >>> + >> >> >> Please use the proper APIs here to cast u8* to u64*, i.e., either use >> get_unaligned64() or use memcmp() > > > But we know the memory addresses are 64 bit aligned, so using > get_unaligned64 seems wrong, and I'm not sure if the compiler is > smart enough to optimize a memcmp to the single 64 bit integer comparison > we want done here. > Fair enough. The memory regions are indeed guaranteed to be 4k aligned. So in that case, please make mem a u64* and cast the other way where needed. >> >>> + /* Seed with ~0, invert to match crc32 userspace utility >>> */ >>> + crc = ~crc32(~0, mem + i, desc->length); >>> + if (crc == desc->crc) >>> + break; >>> + } >>> + >>> + memunmap(mem); >>> + >>> + if (i >= size) >>> + return -ENOENT; >>> + >>> + pr_info("Found EFI embedded fw '%s' crc %08x\n", desc->name, >>> desc->crc); >>> + >>> + fw = kmalloc(sizeof(*fw), GFP_KERNEL); >>> + if (!fw) >>> + return -ENOMEM; >>> + >>> + mem = memremap(md->phys_addr + i, desc->length, MEMREMAP_WB); >>> + if (!mem) { >>> + pr_err("Error mapping embedded firmware\n"); >>> + goto error_free_fw; >>> + } >>> + fw->data = kmemdup(mem, desc->length, GFP_KERNEL); >>> + memunmap(mem); >>> + if (!fw->data) >>> + goto error_free_fw; >>> + >>> + fw->name = desc->name; >>> + fw->length = desc->length; >>> + list_add(&fw->list, &found_fw_list); >>> + >>> + return 0; >>> + >>> +error_free_fw: >>> + kfree(fw); >>> + return -ENOMEM; >>> +} >>> + >>> +void __init efi_check_for_embedded_firmwares(void) >>> +{ >>> + const struct efi_embedded_fw_desc *fw_desc; >>> + const struct dmi_system_id *dmi_id; >>> + efi_memory_desc_t *md; >>> + int i, r; >>> + >>> + for (i = 0; embedded_fw_table[i]; i++) { >>> + dmi_id = dmi_first_match(embedded_fw_table[i]); >>> + if (!dmi_id) >>> + continue; >>> + >>> + fw_desc = dmi_id->driver_data; >>> + for_each_efi_memory_desc(md) { >>> + if (md->type != EFI_BOOT_SERVICES_CODE) >>> + continue; >>> + >>> + r = efi_check_md_for_embedded_firmware(md, >>> fw_desc); >>> + if (r == 0) >>> + break; >>> + } >>> + } >>> +} >>> + >>> +int efi_get_embedded_fw(const char *name, void **data, size_t *size, >>> + size_t msize) >>> +{ >>> + struct embedded_fw *iter, *fw = NULL; >>> + void *buf = *data; >>> + >>> + list_for_each_entry(iter, &found_fw_list, list) { >>> + if (strcmp(name, iter->name) == 0) { >>> + fw = iter; >>> + break; >>> + } >>> + } >>> + >>> + if (!fw) >>> + return -ENOENT; >>> + >>> + if (msize && msize < fw->length) >>> + return -EFBIG; >>> + >>> + if (!buf) { >>> + buf = vmalloc(fw->length); >>> + if (!buf) >>> + return -ENOMEM; >>> + } >>> + >>> + memcpy(buf, fw->data, fw->length); >>> + *size = fw->length; >>> + *data = buf; >>> + >>> + return 0; >>> +} >>> +EXPORT_SYMBOL_GPL(efi_get_embedded_fw); >>> diff --git a/include/linux/efi.h b/include/linux/efi.h >>> index 791088360c1e..23e8a9c26ce2 100644 >>> --- a/include/linux/efi.h >>> +++ b/include/linux/efi.h >>> @@ -1575,6 +1575,12 @@ static inline void >>> efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg) { >>> } >>> #endif >>> >>> +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE >>> +void efi_check_for_embedded_firmwares(void); >>> +#else >>> +static inline void efi_check_for_embedded_firmwares(void) { } >>> +#endif >>> + >>> void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table); >>> >>> /* >>> diff --git a/include/linux/efi_embedded_fw.h >>> b/include/linux/efi_embedded_fw.h >>> new file mode 100644 >>> index 000000000000..0f7d4df3f57a >>> --- /dev/null >>> +++ b/include/linux/efi_embedded_fw.h >>> @@ -0,0 +1,25 @@ >>> +/* SPDX-License-Identifier: GPL-2.0 */ >>> +#ifndef _LINUX_EFI_EMBEDDED_FW_H >>> +#define _LINUX_EFI_EMBEDDED_FW_H >>> + >>> +#include >>> + >>> +/** >>> + * struct efi_embedded_fw_desc - This struct is used by the EFI >>> embedded-fw >>> + * code to search for embedded firmwares. >>> + * >>> + * @name: Name to register the firmware with if found >>> + * @prefix: First 8 bytes of the firmware >>> + * @length: Length of the firmware in bytes including prefix >>> + * @crc: Inverted little endian Ethernet style CRC32, with 0xffffffff >>> seed >>> + */ >>> +struct efi_embedded_fw_desc { >>> + const char *name; >>> + u8 prefix[8]; >>> + u32 length; >>> + u32 crc; >>> +}; >>> + >>> +int efi_get_embedded_fw(const char *name, void **dat, size_t *sz, size_t >>> msize); >>> + >>> +#endif >>> diff --git a/init/main.c b/init/main.c >>> index b795aa341a3a..ab29775b35db 100644 >>> --- a/init/main.c >>> +++ b/init/main.c >>> @@ -729,6 +729,9 @@ asmlinkage __visible void __init start_kernel(void) >>> arch_post_acpi_subsys_init(); >>> sfi_init_late(); >>> >>> + if (efi_enabled(EFI_PRESERVE_BS_REGIONS)) >>> + efi_check_for_embedded_firmwares(); >>> + >>> if (efi_enabled(EFI_RUNTIME_SERVICES)) { >>> efi_free_boot_services(); >>> } >>> -- >>> 2.17.0 >>> >