Received: by 10.192.165.148 with SMTP id m20csp386160imm; Thu, 3 May 2018 22:57:19 -0700 (PDT) X-Google-Smtp-Source: AB8JxZoU6sae9f2yulGZChpXBKbKtraO9pIp4zq5SLHjb1ehjF8t9QRQcVLJCPiPFK7WDQnH/AMF X-Received: by 2002:a65:468d:: with SMTP id h13-v6mr21325266pgr.166.1525413439692; Thu, 03 May 2018 22:57:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1525413439; cv=none; d=google.com; s=arc-20160816; b=W6IV1TH/we65EnkgrXuX8nZ372A4NjG3Bc+RR3enEhG3wVFiNnLlfyz3Ou93+1TvXk vCyw0OIT85UeSoP9mR7EkIZSgB6zXxvJTs4r9lPr9nso4E0dznkzkYjOJo51ocvyJ3vG YZntv3+NJJhez63e0prcwW9vroAUxZr6MhN2QXIdsGu8TvlXb+2W4xLxLNXbeurbscJ4 gW7IEeOdiqYXJC1lm7CuS7dJYhIvPRus+dhiUEN2WtytEn9/ptqpRfSQYfIKp38EN2k+ LSqgk4cSSxciyvg7exOCyvR/SVbSy4lwFn5heVF6mSrrhAsCF++NEYnsqPSYkjHKFPss AM3w== 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=itg1VFBUKn9/iR6vkAWhirUHO6bNRm40ohpUSY68PGI=; b=C4/QAdZydW12bF7t1mIy9bL19JaDZePlHUDHekDtCulCmu4Y+BMdmrnKjxA1LJnawK Tec2T8leflAwopn2jNOSzezICQ2+ju7nGuYFkyCfnGZbvrZRDKF2lCSquWSmVHDOrA/q cYM7yXP+0PQ2VDpGKO54kob2R3jeeeb9eQKius58+Q61iQFBrZIoS5ryiWH8i4Da7pM7 tS9HVqlJ7sm4zIBOijfryb/rrPZl1kn9jmphzcicL4/2lNcwl6sXl5PZ/PwL+aUKmdM1 0pjlVvhEwob8FeBNY9bd5F7JvQIv9Zo1PLoDgoZhfswh9kcPRSJ2JLX9anX6CnBGL/80 cucQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=PnqrEfcB; 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 h1-v6si11998910pgs.169.2018.05.03.22.57.05; Thu, 03 May 2018 22:57:19 -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=PnqrEfcB; 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 S1751341AbeEDF4x (ORCPT + 99 others); Fri, 4 May 2018 01:56:53 -0400 Received: from mail-it0-f66.google.com ([209.85.214.66]:51626 "EHLO mail-it0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751124AbeEDF4u (ORCPT ); Fri, 4 May 2018 01:56:50 -0400 Received: by mail-it0-f66.google.com with SMTP id n202-v6so2047075ita.1 for ; Thu, 03 May 2018 22:56:50 -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=itg1VFBUKn9/iR6vkAWhirUHO6bNRm40ohpUSY68PGI=; b=PnqrEfcBXr5CFrCFwQ+qoOr2eaANHi+XjxaY52e5vikJnl5dY3vhHIg1v0D3w/ST1G J9O2KJOVwgXz5IVZl4cg87Vkjpl1Xbi0QKDkr3x4GJKqva4Gw0JKEEYdbETb2qy9dkBN 2qJD8MPxdb524LKk0oDDYM/XvYGED9Y6FFaU0= 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=itg1VFBUKn9/iR6vkAWhirUHO6bNRm40ohpUSY68PGI=; b=CWZNTFMYniOz+qw7S/V6b5S5bHEZ/ze+ZE+Y/Xt6ChccCp8YF/Zv+DIRWA8467UsIX i4q7p/LSGZcvGNAbfQsePTYOQF4r7AvKYJTJUUFE2CGnD6NNLZ1k9HIzP8ejas7/jLG5 xlYlKcQoce02M0uMhGE2UkXZNDUqEnPltWT9lkz3ON4fRddE3vgdKZFG/XAH4CppxQN/ Oj0gdnWIxNl/tX92PZszkO0mUZiOD3ERS0Ti6PvkLI0xzo7VqJQCFQ+jhXDrhUeqpF7V wKfinzLXG/OYTzUQowE/wmKs76PPSA/fXQHGTtuoavWeRq+sXBLkf3S1+2K90QwuLaC7 3nXg== X-Gm-Message-State: ALQs6tBG9BMuXnHqUIFR667B0sWnX7ZXJR6YFsaYyKnhIaO3EWCoIkEX 4onC0ahfOWeRcPjUuA0Iqwx/tDJOKGgH/3MBHX9xxA== X-Received: by 2002:a24:57c3:: with SMTP id u186-v6mr19494309ita.42.1525413410040; Thu, 03 May 2018 22:56:50 -0700 (PDT) MIME-Version: 1.0 Received: by 10.107.187.134 with HTTP; Thu, 3 May 2018 22:56:49 -0700 (PDT) In-Reply-To: <20180429093558.5411-3-hdegoede@redhat.com> References: <20180429093558.5411-1-hdegoede@redhat.com> <20180429093558.5411-3-hdegoede@redhat.com> From: Ard Biesheuvel Date: Fri, 4 May 2018 07:56:49 +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 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() > + /* 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 >