From patchwork Wed Jan 15 16:35:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 208272 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 04702C32771 for ; Wed, 15 Jan 2020 16:36:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BA00A2465A for ; Wed, 15 Jan 2020 16:36:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="CSjTlGm4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729092AbgAOQgS (ORCPT ); Wed, 15 Jan 2020 11:36:18 -0500 Received: from us-smtp-delivery-1.mimecast.com ([207.211.31.120]:42405 "EHLO us-smtp-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729074AbgAOQgS (ORCPT ); Wed, 15 Jan 2020 11:36:18 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579106176; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=lb0NDxMEx0ddgTbHUh0qQbJhrjsgTsDPRcVNGdh2q24=; b=CSjTlGm4QCRPHvWkei3DbeNI0HL775jk1H2iTd32oJYwZm03snmcTyR3rnojBn9nlNLiba Qh9sw01fk+QglL++r3MCCaI5oXUyZNQV3jgiMDCJqqYM0xQySTojpX2A3LMk+4NKid+nvd aXihJn/IEwtmDCysSUNEIl2nd/eIsaE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-189-qaQDJskDPCOS_wPla5GI2g-1; Wed, 15 Jan 2020 11:36:15 -0500 X-MC-Unique: qaQDJskDPCOS_wPla5GI2g-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 51C88100550E; Wed, 15 Jan 2020 16:36:12 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-188.ams2.redhat.com [10.36.116.188]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6318C5DA76; Wed, 15 Jan 2020 16:36:08 +0000 (UTC) From: Hans de Goede To: Ard Biesheuvel , Darren Hart , Andy Shevchenko , Luis Chamberlain , Greg Kroah-Hartman , "Rafael J . Wysocki" , Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H . Peter Anvin" , Jonathan Corbet , Dmitry Torokhov Cc: Hans de Goede , Peter Jones , Dave Olsthoorn , x86@kernel.org, platform-driver-x86@vger.kernel.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-input@vger.kernel.org, Ard Biesheuvel Subject: [PATCH v12 02/10] efi: Add embedded peripheral firmware support Date: Wed, 15 Jan 2020 17:35:46 +0100 Message-Id: <20200115163554.101315-3-hdegoede@redhat.com> In-Reply-To: <20200115163554.101315-1-hdegoede@redhat.com> References: <20200115163554.101315-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org 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 makes the found firmware available through the new efi_get_embedded_fw() function. Support for loading these firmwares through the standard firmware loading mechanism is added in a follow-up commit in this patch-series. 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 --- Changes in v12: - Use memcmp instead of type-punned pointer casting to compare the prefix - Make efi_get_embedded_fw() return a const u8 * pointer to the efi_embedded_fw copy of the firmware instead of doing a vmemdup and and returning that Changes in v9: - At least touchscreen_dmi.c uses the same dmi_table for its own private data and the fw_desc structs, putting the fw_desc struct first in the data driver_data points to so that the dmi_table can be shared with efi_check_for_embedded_firmwares(). But not all entries there have embedded-fw so in some cases the fw_desc is empty (zero-ed out). This can lead to a possible crash because fw_desc->length now is less then 8, so if the segment size is close enough to a multiple of the page_size, then the memcmp to check the prefix my segfault. Crashing the machine. v9 checks for and skips these empty fw_desc entries avoiding this. - Rename found_fw_list to efi_embedded_fw_list and export it for use by lib/test_firmware.c Changes in v8: - Properly deal with the (theoretical?) case of an EFI segment being smaller then the fw we are looking for - Log a warning when efi_get_embedded_fw get called while we did not (yet) check for embedded firmwares Changes in v7: - Split drivers/firmware/efi and drivers/base/firmware_loader changes into 2 patches - Use new, standalone, lib/crypto/sha256.c code Changes in v6: - Rework code to remove casts from if (prefix == mem) comparison - Use SHA256 hashes instead of crc32 sums - Add new READING_FIRMWARE_EFI_EMBEDDED read_file_id and use it - Call security_kernel_read_file(NULL, READING_FIRMWARE_EFI_EMBEDDED) to check if this is allowed before looking at EFI embedded fw - Document why we are not using the UEFI PI Firmware Volume protocol Changes in v5: - Rename the EFI_BOOT_SERVICES flag to EFI_PRESERVE_BS_REGIONS Changes in v4: - Drop note in docs about EFI_FIRMWARE_VOLUME_PROTOCOL, it is not part of UEFI proper, so the EFI maintainers don't want us referring people to it - Use new EFI_BOOT_SERVICES flag - Put the new fw_get_efi_embedded_fw() function in its own fallback_efi.c file which only gets built when EFI_EMBEDDED_FIRMWARE is selected - Define an empty stub for fw_get_efi_embedded_fw() in fallback.h hwen EFI_EMBEDDED_FIRMWARE is not selected, to avoid the need for #ifdefs in firmware_loader/main.c - Properly call security_kernel_post_read_file() on the firmware returned by efi_get_embedded_fw() to make sure that we are allowed to use it Changes in v3: - Fix the docs using "efi-embedded-fw" as property name instead of "efi-embedded-firmware" Changes in v2: - Rebased on driver-core/driver-core-next - Add documentation describing the EFI embedded firmware mechanism to: Documentation/driver-api/firmware/request_firmware.rst - Add a new EFI_EMBEDDED_FIRMWARE Kconfig bool and only build the embedded fw support if this is set. This is an invisible option which should be selected by drivers which need this - Remove the efi_embedded_fw_desc and dmi_system_id-s for known devices from the efi-embedded-fw code, instead drivers using this are expected to export a dmi_system_id array, with each entries' driver_data pointing to a efi_embedded_fw_desc struct and register this with the efi-embedded-fw code - Use kmemdup to make a copy instead of efi_mem_reserve()-ing the firmware, this avoids us messing with the EFI memmap and avoids the need to make changes to efi_mem_desc_lookup() - Make the firmware-loader code only fallback to efi_get_embedded_fw() if the passed in device has the "efi-embedded-firmware" device-property bool set - Skip usermodehelper fallback when "efi-embedded-firmware" device-property is set --- arch/x86/platform/efi/efi.c | 1 + drivers/firmware/efi/Kconfig | 5 + drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/embedded-firmware.c | 147 +++++++++++++++++++++++ include/linux/efi.h | 6 + include/linux/efi_embedded_fw.h | 41 +++++++ 6 files changed, 201 insertions(+) create mode 100644 drivers/firmware/efi/embedded-firmware.c create mode 100644 include/linux/efi_embedded_fw.h diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 51ee2e2a18d6..cc10a925d9e7 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -944,6 +944,7 @@ static void __init __efi_enter_virtual_mode(void) goto err; } + efi_check_for_embedded_firmwares(); efi_free_boot_services(); /* diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index ecc83e2f032c..613828d3f106 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -239,6 +239,11 @@ config EFI_DISABLE_PCI_DMA endmenu +config EFI_EMBEDDED_FIRMWARE + bool + depends on EFI + select CRYPTO_LIB_SHA256 + config UEFI_CPER bool diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 554d795270d9..256d6121b2b3 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o +obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o fake_map-y += fake_mem.o fake_map-$(CONFIG_X86) += x86_fake_mem.o diff --git a/drivers/firmware/efi/embedded-firmware.c b/drivers/firmware/efi/embedded-firmware.c new file mode 100644 index 000000000000..1bc9cdae2eed --- /dev/null +++ b/drivers/firmware/efi/embedded-firmware.c @@ -0,0 +1,147 @@ +// 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 +#include + +/* Exported for use by lib/test_firmware.c only */ +LIST_HEAD(efi_embedded_fw_list); +EXPORT_SYMBOL_GPL(efi_embedded_fw_list); + +static bool checked_for_fw; + +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 sha256_state sctx; + struct efi_embedded_fw *fw; + u8 sha256[32]; + u64 i, size; + u8 *map; + + size = md->num_pages << EFI_PAGE_SHIFT; + map = memremap(md->phys_addr, size, MEMREMAP_WB); + if (!map) { + pr_err("Error mapping EFI mem at %#llx\n", md->phys_addr); + return -ENOMEM; + } + + for (i = 0; (i + desc->length) <= size; i += 8) { + if (memcmp(map + i, desc->prefix, EFI_EMBEDDED_FW_PREFIX_LEN)) + continue; + + sha256_init(&sctx); + sha256_update(&sctx, map + i, desc->length); + sha256_final(&sctx, sha256); + if (memcmp(sha256, desc->sha256, 32) == 0) + break; + } + if ((i + desc->length) > size) { + memunmap(map); + return -ENOENT; + } + + pr_info("Found EFI embedded fw '%s'\n", desc->name); + + fw = kmalloc(sizeof(*fw), GFP_KERNEL); + if (!fw) { + memunmap(map); + return -ENOMEM; + } + + fw->data = kmemdup(map + i, desc->length, GFP_KERNEL); + memunmap(map); + if (!fw->data) { + kfree(fw); + return -ENOMEM; + } + + fw->name = desc->name; + fw->length = desc->length; + list_add(&fw->list, &efi_embedded_fw_list); + + return 0; +} + +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; + + /* + * In some drivers the struct driver_data contains may contain + * other driver specific data after the fw_desc struct; and + * the fw_desc struct itself may be empty, skip these. + */ + if (!fw_desc->name) + continue; + + 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; + } + } + + checked_for_fw = true; +} + +int efi_get_embedded_fw(const char *name, const u8 **data, size_t *size) +{ + struct efi_embedded_fw *iter, *fw = NULL; + + if (!checked_for_fw) { + pr_warn("Warning %s called while we did not check for embedded fw\n", + __func__); + return -ENOENT; + } + + list_for_each_entry(iter, &efi_embedded_fw_list, list) { + if (strcmp(name, iter->name) == 0) { + fw = iter; + break; + } + } + + if (!fw) + return -ENOENT; + + *data = fw->data; + *size = fw->length; + + return 0; +} +EXPORT_SYMBOL_GPL(efi_get_embedded_fw); diff --git a/include/linux/efi.h b/include/linux/efi.h index a6e1a2d8511e..23392b88bcc0 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1554,6 +1554,12 @@ static inline void efi_enable_reset_attack_mitigation(void) { } #endif +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE +void efi_check_for_embedded_firmwares(void); +#else +static inline void efi_check_for_embedded_firmwares(void) { } +#endif + efi_status_t efi_random_get_seed(void); void efi_retrieve_tpm2_eventlog(void); diff --git a/include/linux/efi_embedded_fw.h b/include/linux/efi_embedded_fw.h new file mode 100644 index 000000000000..3d066c6370c6 --- /dev/null +++ b/include/linux/efi_embedded_fw.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_EFI_EMBEDDED_FW_H +#define _LINUX_EFI_EMBEDDED_FW_H + +#include +#include + +#define EFI_EMBEDDED_FW_PREFIX_LEN 8 + +/* + * This struct and efi_embedded_fw_list are private to the efi-embedded fw + * implementation they are in this header for use by lib/test_firmware.c only! + */ +struct efi_embedded_fw { + struct list_head list; + const char *name; + const u8 *data; + size_t length; +}; + +extern struct list_head efi_embedded_fw_list; + +/** + * 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 + * @sha256: SHA256 of the firmware + */ +struct efi_embedded_fw_desc { + const char *name; + u8 prefix[EFI_EMBEDDED_FW_PREFIX_LEN]; + u32 length; + u8 sha256[32]; +}; + +int efi_get_embedded_fw(const char *name, const u8 **dat, size_t *sz); + +#endif From patchwork Wed Jan 15 16:35:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 208271 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 57621C32771 for ; Wed, 15 Jan 2020 16:36:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 195EA2187F for ; Wed, 15 Jan 2020 16:36:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="faNgKDHx" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729127AbgAOQg1 (ORCPT ); Wed, 15 Jan 2020 11:36:27 -0500 Received: from us-smtp-delivery-1.mimecast.com ([205.139.110.120]:50380 "EHLO us-smtp-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729126AbgAOQg1 (ORCPT ); Wed, 15 Jan 2020 11:36:27 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579106186; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rA2MP2khUKTgP5Dut80fSoZxMx9KkXu8lup+EUK8Wew=; b=faNgKDHxf2zVIGAIWXCrmgzpUACB+GV7/OAvaW1HuT8Ebhm1vfXsaqMNu7+qXmAyhaEjco 5g4TpOshnaYaZbzMWHkLSv0tNUbHOvxRYSjXzBJhHc++RzGkZUq1KtbKnTxJwzSSxGZHHJ Hhxhoji+eeOfT9/THT7ScZfy/SQsRck= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-58-lx9Uxkz4Mw6eiTq-273i7Q-1; Wed, 15 Jan 2020 11:36:24 -0500 X-MC-Unique: lx9Uxkz4Mw6eiTq-273i7Q-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 364D7800EBF; Wed, 15 Jan 2020 16:36:22 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-188.ams2.redhat.com [10.36.116.188]) by smtp.corp.redhat.com (Postfix) with ESMTP id 767625DA76; Wed, 15 Jan 2020 16:36:17 +0000 (UTC) From: Hans de Goede To: Ard Biesheuvel , Darren Hart , Andy Shevchenko , Luis Chamberlain , Greg Kroah-Hartman , "Rafael J . Wysocki" , Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H . Peter Anvin" , Jonathan Corbet , Dmitry Torokhov Cc: Hans de Goede , Peter Jones , Dave Olsthoorn , x86@kernel.org, platform-driver-x86@vger.kernel.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-input@vger.kernel.org Subject: [PATCH v12 04/10] firmware: Add new platform fallback mechanism and firmware_request_platform() Date: Wed, 15 Jan 2020 17:35:48 +0100 Message-Id: <20200115163554.101315-5-hdegoede@redhat.com> In-Reply-To: <20200115163554.101315-1-hdegoede@redhat.com> References: <20200115163554.101315-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org In some cases the platform's main firmware (e.g. the UEFI fw) may contain an embedded copy of device 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 a new platform fallback mechanism to the firmware loader which will try to lookup a device fw copy embedded in the platform's main firmware if direct filesystem lookup fails. Drivers which need such embedded fw copies can enable this fallback mechanism by using the new firmware_request_platform() function. Note that for now this is only supported on EFI platforms and even on these platforms firmware_fallback_platform() only works if CONFIG_EFI_EMBEDDED_FIRMWARE is enabled (this gets selected by drivers which need this), in all other cases firmware_fallback_platform() simply always returns -ENOENT. Reported-by: Dave Olsthoorn Suggested-by: Peter Jones Acked-by: Luis Chamberlain Signed-off-by: Hans de Goede --- Changes in v12: - Adjust firmware_fallback_platform() for efi_get_embedded_fw() now returning a const u8 * pointer. This means we now need to do the vmemdup (vmalloc + memcpy) ourselves. This is cleaner because the matching vfree is also part of the firmware loader code. Changes in v9: - Add static inline wrapper for firmware_request_platform() to firmware.h, for when CONFIG_FW_LOADER is not set Changes in v8: - Only build fallback_platform.c if CONFIG_EFI_EMBEDDED_FIRMWARE is defined, otherwise make firmware_fallback_platform() a static inline stub - Add documentation to Documentation/driver-api/firmware/fallback-mechanisms.rst on how the boot_service_code? files exported by EFI debugfs can be used to check if there is an embedded firmware and to get the embedded firmware length and sha256sum Changes in v7: - Split drivers/firmware/efi and drivers/base/firmware_loader changes into 2 patches - Address kdoc comments from Randy Dunlap - Add new FW_OPT_FALLBACK_PLATFORM flag and firmware_request_platform() _request_firmware() wrapper, as requested by Luis R. Rodriguez - Stop using "efi-embedded-firmware" device-property, now that drivers need to use the new firmware_request_platform() to enable fallback to a device fw copy embedded in the platform's main firmware, we no longer need a property on the device to trigger this behavior - Use security_kernel_load_data instead of calling security_kernel_read_file with a NULL file pointer argument - Move the docs to Documentation/driver-api/firmware/fallback-mechanisms.rst - Document the new firmware_request_platform() function in Documentation/driver-api/firmware/request_firmware.rst Changes in v6: - Rework code to remove casts from if (prefix == mem) comparison - Use SHA256 hashes instead of crc32 sums - Add new READING_FIRMWARE_EFI_EMBEDDED read_file_id and use it - Call security_kernel_read_file(NULL, READING_FIRMWARE_EFI_EMBEDDED) to check if this is allowed before looking at EFI embedded fw - Document why we are not using the UEFI PI Firmware Volume protocol Changes in v5: - Rename the EFI_BOOT_SERVICES flag to EFI_PRESERVE_BS_REGIONS Changes in v4: - Drop note in docs about EFI_FIRMWARE_VOLUME_PROTOCOL, it is not part of UEFI proper, so the EFI maintainers don't want us referring people to it - Use new EFI_BOOT_SERVICES flag - Put the new fw_get_efi_embedded_fw() function in its own fallback_efi.c file which only gets built when EFI_EMBEDDED_FIRMWARE is selected - Define an empty stub for fw_get_efi_embedded_fw() in fallback.h hwen EFI_EMBEDDED_FIRMWARE is not selected, to avoid the need for #ifdefs in firmware_loader/main.c - Properly call security_kernel_post_read_file() on the firmware returned by efi_get_embedded_fw() to make sure that we are allowed to use it Changes in v3: - Fix the docs using "efi-embedded-fw" as property name instead of "efi-embedded-firmware" Changes in v2: - Rebased on driver-core/driver-core-next - Add documentation describing the EFI embedded firmware mechanism to: Documentation/driver-api/firmware/request_firmware.rst - Add a new EFI_EMBEDDED_FIRMWARE Kconfig bool and only build the embedded fw support if this is set. This is an invisible option which should be selected by drivers which need this - Remove the efi_embedded_fw_desc and dmi_system_id-s for known devices from the efi-embedded-fw code, instead drivers using this are expected to export a dmi_system_id array, with each entries' driver_data pointing to a efi_embedded_fw_desc struct and register this with the efi-embedded-fw code - Use kmemdup to make a copy instead of efi_mem_reserve()-ing the firmware, this avoids us messing with the EFI memmap and avoids the need to make changes to efi_mem_desc_lookup() - Make the firmware-loader code only fallback to efi_get_embedded_fw() if the passed in device has the "efi-embedded-firmware" device-property bool set - Skip usermodehelper fallback when "efi-embedded-firmware" device-property is set --- .../firmware/fallback-mechanisms.rst | 103 ++++++++++++++++++ .../driver-api/firmware/lookup-order.rst | 2 + .../driver-api/firmware/request_firmware.rst | 5 + drivers/base/firmware_loader/Makefile | 1 + drivers/base/firmware_loader/fallback.h | 10 ++ .../base/firmware_loader/fallback_platform.c | 36 ++++++ drivers/base/firmware_loader/firmware.h | 4 + drivers/base/firmware_loader/main.c | 27 +++++ include/linux/firmware.h | 9 ++ include/linux/fs.h | 1 + 10 files changed, 198 insertions(+) create mode 100644 drivers/base/firmware_loader/fallback_platform.c diff --git a/Documentation/driver-api/firmware/fallback-mechanisms.rst b/Documentation/driver-api/firmware/fallback-mechanisms.rst index 8b041d0ab426..036383dad6d6 100644 --- a/Documentation/driver-api/firmware/fallback-mechanisms.rst +++ b/Documentation/driver-api/firmware/fallback-mechanisms.rst @@ -202,3 +202,106 @@ the following file: If you echo 0 into it means MAX_JIFFY_OFFSET will be used. The data type for the timeout is an int. + +EFI embedded firmware fallback mechanism +======================================== + +On some devices the system's EFI code / ROM may contain an embedded copy +of firmware for some of the system's integrated peripheral devices and +the peripheral's Linux device-driver needs to access this firmware. + +Device drivers which need such firmware can use the +firmware_request_platform() function for this, note that this is a +separate fallback mechanism from the other fallback mechanisms and +this does not use the sysfs interface. + +A device driver which needs this can describe the firmware it needs +using an efi_embedded_fw_desc struct: + +.. kernel-doc:: include/linux/efi_embedded_fw.h + :functions: efi_embedded_fw_desc + +The EFI embedded-fw code works by scanning all EFI_BOOT_SERVICES_CODE memory +segments for an eight byte sequence matching prefix; if the prefix is found it +then does a sha256 over length bytes and if that matches makes a copy of length +bytes and adds that to its list with found firmwares. + +To avoid doing this somewhat expensive scan on all systems, dmi matching is +used. Drivers are expected to export a dmi_system_id array, with each entries' +driver_data pointing to an efi_embedded_fw_desc. + +To register this array with the efi-embedded-fw code, a driver needs to: + +1. Always be builtin to the kernel or store the dmi_system_id array in a + separate object file which always gets builtin. + +2. Add an extern declaration for the dmi_system_id array to + include/linux/efi_embedded_fw.h. + +3. Add the dmi_system_id array to the embedded_fw_table in + drivers/firmware/efi/embedded-firmware.c wrapped in a #ifdef testing that + the driver is being builtin. + +4. Add "select EFI_EMBEDDED_FIRMWARE if EFI_STUB" to its Kconfig entry. + +The firmware_request_platform() function will always first try to load firmware +with the specified name directly from the disk, so the EFI embedded-fw can +always be overridden by placing a file under /lib/firmware. + +Note that: + +1. The code scanning for EFI embedded-firmware runs near the end + of start_kernel(), just before calling rest_init(). For normal drivers and + subsystems using subsys_initcall() to register themselves this does not + matter. This means that code running earlier cannot use EFI + embedded-firmware. + +2. At the moment the EFI embedded-fw code assumes that firmwares always start at + an offset which is a multiple of 8 bytes, if this is not true for your case + send in a patch to fix this. + +3. At the moment the EFI embedded-fw code only works on x86 because other archs + free EFI_BOOT_SERVICES_CODE before the EFI embedded-fw code gets a chance to + scan it. + +4. The current brute-force scanning of EFI_BOOT_SERVICES_CODE is an ad-hoc + brute-force solution. There has been discussion to use the UEFI Platform + Initialization (PI) spec's Firmware Volume protocol. This has been rejected + because the FV Protocol relies on *internal* interfaces of the PI spec, and: + 1. The PI spec does not define peripheral firmware at all + 2. The internal interfaces of the PI spec do not guarantee any backward + compatibility. Any implementation details in FV may be subject to change, + and may vary system to system. Supporting the FV Protocol would be + difficult as it is purposely ambiguous. + +Example how to check for and extract embedded firmware +------------------------------------------------------ + +To check for, for example Silead touchscreen controller embedded firmware, +do the following: + +1. Boot the system with efi=debug on the kernel commandline + +2. cp /sys/kernel/debug/efi/boot_services_code? to your home dir + +3. Open the boot_services_code? files in a hex-editor, search for the + magic prefix for Silead firmware: F0 00 00 00 02 00 00 00, this gives you + the beginning address of the firmware inside the boot_services_code? file. + +4. The firmware has a specific pattern, it starts with a 8 byte page-address, + typically F0 00 00 00 02 00 00 00 for the first page followed by 32-bit + word-address + 32-bit value pairs. With the word-address incrementing 4 + bytes (1 word) for each pair until a page is complete. A complete page is + followed by a new page-address, followed by more word + value pairs. This + leads to a very distinct pattern. Scroll down until this pattern stops, + this gives you the end of the firmware inside the boot_services_code? file. + +5. "dd if=boot_services_code? of=firmware bs=1 skip= count=" + will extract the firmware for you. Inspect the firmware file in a + hexeditor to make sure you got the dd parameters correct. + +6. Copy it to /lib/firmware under the expected name to test it. + +7. If the extracted firmware works, you can use the found info to fill an + efi_embedded_fw_desc struct to describe it, run "sha256sum firmware" + to get the sha256sum to put in the sha256 field. diff --git a/Documentation/driver-api/firmware/lookup-order.rst b/Documentation/driver-api/firmware/lookup-order.rst index 88c81739683c..6064672a782e 100644 --- a/Documentation/driver-api/firmware/lookup-order.rst +++ b/Documentation/driver-api/firmware/lookup-order.rst @@ -12,6 +12,8 @@ a driver issues a firmware API call. return it immediately * The ''Direct filesystem lookup'' is performed next, if found we return it immediately +* The ''Platform firmware fallback'' is performed next, but only when + firmware_request_platform() is used, if found we return it immediately * If no firmware has been found and the fallback mechanism was enabled the sysfs interface is created. After this either a kobject uevent is issued or the custom firmware loading is relied upon for firmware diff --git a/Documentation/driver-api/firmware/request_firmware.rst b/Documentation/driver-api/firmware/request_firmware.rst index f62bdcbfed5b..cd076462d235 100644 --- a/Documentation/driver-api/firmware/request_firmware.rst +++ b/Documentation/driver-api/firmware/request_firmware.rst @@ -25,6 +25,11 @@ firmware_request_nowarn .. kernel-doc:: drivers/base/firmware_loader/main.c :functions: firmware_request_nowarn +firmware_request_platform +------------------------- +.. kernel-doc:: drivers/base/firmware_loader/main.c + :functions: firmware_request_platform + request_firmware_direct ----------------------- .. kernel-doc:: drivers/base/firmware_loader/main.c diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile index 0b2dfa6259c9..e87843408fe6 100644 --- a/drivers/base/firmware_loader/Makefile +++ b/drivers/base/firmware_loader/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_FW_LOADER_USER_HELPER) += fallback_table.o obj-$(CONFIG_FW_LOADER) += firmware_class.o firmware_class-objs := main.o firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o +firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o obj-y += builtin/ diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h index 21063503e4ea..06f4577733a8 100644 --- a/drivers/base/firmware_loader/fallback.h +++ b/drivers/base/firmware_loader/fallback.h @@ -66,4 +66,14 @@ static inline void unregister_sysfs_loader(void) } #endif /* CONFIG_FW_LOADER_USER_HELPER */ +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE +int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags); +#else +static inline int firmware_fallback_platform(struct fw_priv *fw_priv, + enum fw_opt opt_flags) +{ + return -ENOENT; +} +#endif + #endif /* __FIRMWARE_FALLBACK_H */ diff --git a/drivers/base/firmware_loader/fallback_platform.c b/drivers/base/firmware_loader/fallback_platform.c new file mode 100644 index 000000000000..c88c745590fe --- /dev/null +++ b/drivers/base/firmware_loader/fallback_platform.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include "fallback.h" +#include "firmware.h" + +int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags) +{ + const u8 *data; + size_t size; + int rc; + + if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM)) + return -ENOENT; + + rc = security_kernel_load_data(LOADING_FIRMWARE_EFI_EMBEDDED); + if (rc) + return rc; + + rc = efi_get_embedded_fw(fw_priv->fw_name, &data, &size); + if (rc) + return rc; /* rc == -ENOENT when the fw was not found */ + + fw_priv->data = vmalloc(size); + if (!fw_priv->data) + return -ENOMEM; + + memcpy(fw_priv->data, data, size); + fw_priv->size = size; + fw_state_done(fw_priv); + return 0; +} diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h index 8656e5239a80..25836a6afc9f 100644 --- a/drivers/base/firmware_loader/firmware.h +++ b/drivers/base/firmware_loader/firmware.h @@ -29,6 +29,9 @@ * firmware caching mechanism. * @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes * precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER. + * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in + * the platform's main firmware. If both this fallback and the sysfs + * fallback are enabled, then this fallback will be tried first. */ enum fw_opt { FW_OPT_UEVENT = BIT(0), @@ -37,6 +40,7 @@ enum fw_opt { FW_OPT_NO_WARN = BIT(3), FW_OPT_NOCACHE = BIT(4), FW_OPT_NOFALLBACK_SYSFS = BIT(5), + FW_OPT_FALLBACK_PLATFORM = BIT(6), }; enum fw_status { diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 57133a9dad09..cfdbf0010545 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -776,6 +776,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name, fw_decompress_xz); #endif + if (ret == -ENOENT) + ret = firmware_fallback_platform(fw->priv, opt_flags); + if (ret) { if (!(opt_flags & FW_OPT_NO_WARN)) dev_warn(device, @@ -883,6 +886,30 @@ int request_firmware_direct(const struct firmware **firmware_p, } EXPORT_SYMBOL_GPL(request_firmware_direct); +/** + * firmware_request_platform() - request firmware with platform-fw fallback + * @firmware: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * + * This function is similar in behaviour to request_firmware, except that if + * direct filesystem lookup fails, it will fallback to looking for a copy of the + * requested firmware embedded in the platform's main (e.g. UEFI) firmware. + **/ +int firmware_request_platform(const struct firmware **firmware, + const char *name, struct device *device) +{ + int ret; + + /* Need to pin this module until return */ + __module_get(THIS_MODULE); + ret = _request_firmware(firmware, name, device, NULL, 0, + FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(firmware_request_platform); + /** * firmware_request_cache() - cache firmware for suspend so resume can use it * @name: name of firmware file diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 2dd566c91d44..4bbd0afd91b7 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -44,6 +44,8 @@ int request_firmware(const struct firmware **fw, const char *name, struct device *device); int firmware_request_nowarn(const struct firmware **fw, const char *name, struct device *device); +int firmware_request_platform(const struct firmware **fw, const char *name, + struct device *device); int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, @@ -69,6 +71,13 @@ static inline int firmware_request_nowarn(const struct firmware **fw, return -EINVAL; } +static inline int firmware_request_platform(const struct firmware **fw, + const char *name, + struct device *device) +{ + return -EINVAL; +} + static inline int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, diff --git a/include/linux/fs.h b/include/linux/fs.h index 98e0349adb52..4b4f11a80a2c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2964,6 +2964,7 @@ extern int do_pipe_flags(int *, int); id(UNKNOWN, unknown) \ id(FIRMWARE, firmware) \ id(FIRMWARE_PREALLOC_BUFFER, firmware) \ + id(FIRMWARE_EFI_EMBEDDED, firmware) \ id(MODULE, kernel-module) \ id(KEXEC_IMAGE, kexec-image) \ id(KEXEC_INITRAMFS, kexec-initramfs) \ From patchwork Wed Jan 15 16:35:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 208268 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4C943C32771 for ; Wed, 15 Jan 2020 16:36:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 22A292467C for ; Wed, 15 Jan 2020 16:36:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="QlRVr+j+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729144AbgAOQge (ORCPT ); Wed, 15 Jan 2020 11:36:34 -0500 Received: from us-smtp-delivery-1.mimecast.com ([205.139.110.120]:21154 "EHLO us-smtp-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729125AbgAOQge (ORCPT ); Wed, 15 Jan 2020 11:36:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579106193; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=D2+KBQ3DRpskHHzDy8jC2pctG+R82LGlRTav9U6g4p0=; b=QlRVr+j+zuC8LArcIN9gk59g1/vYL3W/K1ENCukEdIragiDT/Ex6HFpftXqioY/rAdlNTF 5vZBzWM9ST9oSXvYtOEvx4MdBmrxmwcvakZmoOhASzEuJRLcYOYtb5RMGmSxH4bMC9QmQr zUohlmW3TRD2QtNaMk74mMQWHlTCKxk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-124-AX241fG7Pxeuh35YOBMYiA-1; Wed, 15 Jan 2020 11:36:28 -0500 X-MC-Unique: AX241fG7Pxeuh35YOBMYiA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 263821800D48; Wed, 15 Jan 2020 16:36:26 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-188.ams2.redhat.com [10.36.116.188]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7AE515DA76; Wed, 15 Jan 2020 16:36:22 +0000 (UTC) From: Hans de Goede To: Ard Biesheuvel , Darren Hart , Andy Shevchenko , Luis Chamberlain , Greg Kroah-Hartman , "Rafael J . Wysocki" , Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H . Peter Anvin" , Jonathan Corbet , Dmitry Torokhov Cc: Hans de Goede , Peter Jones , Dave Olsthoorn , x86@kernel.org, platform-driver-x86@vger.kernel.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-input@vger.kernel.org Subject: [PATCH v12 05/10] test_firmware: add support for firmware_request_platform Date: Wed, 15 Jan 2020 17:35:49 +0100 Message-Id: <20200115163554.101315-6-hdegoede@redhat.com> In-Reply-To: <20200115163554.101315-1-hdegoede@redhat.com> References: <20200115163554.101315-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Add support for testing firmware_request_platform through a new trigger_request_platform trigger. Signed-off-by: Hans de Goede --- Changes in v12: - Use local / private firmware variable for the test instead of the global test_firmware variable and drop the mutex lock + unlock needed for accessing the global test_firmware variable Changes in v11: - Drop a few empty lines which were accidentally introduced Changes in v10: - New patch in v10 of this patch-set --- lib/test_firmware.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 251213c872b5..0c7fbcf07ac5 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -24,6 +24,7 @@ #include #include #include +#include #define TEST_FIRMWARE_NAME "test-firmware.bin" #define TEST_FIRMWARE_NUM_REQS 4 @@ -507,6 +508,57 @@ static ssize_t trigger_request_store(struct device *dev, } static DEVICE_ATTR_WO(trigger_request); +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE +static ssize_t trigger_request_platform_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + static const u8 test_data[] = { + 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04, + 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08, + 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40, + 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80 + }; + struct efi_embedded_fw efi_embedded_fw; + const struct firmware *firmware = NULL; + char *name; + int rc; + + name = kstrndup(buf, count, GFP_KERNEL); + if (!name) + return -ENOSPC; + + pr_info("inserting test platform fw '%s'\n", name); + efi_embedded_fw.name = name; + efi_embedded_fw.data = (void *)test_data; + efi_embedded_fw.length = sizeof(test_data); + list_add(&efi_embedded_fw.list, &efi_embedded_fw_list); + + pr_info("loading '%s'\n", name); + rc = firmware_request_platform(&firmware, name, dev); + if (rc) { + pr_info("load of '%s' failed: %d\n", name, rc); + goto out; + } + if (firmware->size != sizeof(test_data) || + memcmp(firmware->data, test_data, sizeof(test_data)) != 0) { + pr_info("firmware contents mismatch for '%s'\n", name); + rc = -EINVAL; + goto out; + } + pr_info("loaded: %zu\n", firmware->size); + rc = count; + +out: + release_firmware(firmware); + list_del(&efi_embedded_fw.list); + kfree(name); + + return rc; +} +static DEVICE_ATTR_WO(trigger_request_platform); +#endif + static DECLARE_COMPLETION(async_fw_done); static void trigger_async_request_cb(const struct firmware *fw, void *context) @@ -903,6 +955,9 @@ static struct attribute *test_dev_attrs[] = { TEST_FW_DEV_ATTR(trigger_request), TEST_FW_DEV_ATTR(trigger_async_request), TEST_FW_DEV_ATTR(trigger_custom_fallback), +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE + TEST_FW_DEV_ATTR(trigger_request_platform), +#endif /* These use the config and can use the test_result */ TEST_FW_DEV_ATTR(trigger_batched_requests), From patchwork Wed Jan 15 16:35:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 208270 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EA04AC32771 for ; Wed, 15 Jan 2020 16:36:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C2E612467C for ; Wed, 15 Jan 2020 16:36:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="alC2gcx/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728998AbgAOQgk (ORCPT ); Wed, 15 Jan 2020 11:36:40 -0500 Received: from us-smtp-delivery-1.mimecast.com ([205.139.110.120]:36466 "EHLO us-smtp-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729191AbgAOQgi (ORCPT ); Wed, 15 Jan 2020 11:36:38 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579106197; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VqDSi00IIiR4MKfM9kfHuXfjy0CMBtCyRpTjZC9pBlY=; b=alC2gcx/j76hp0+Uuc63wqUt2qCfIzzgRY93aR7KbkjG3mx1XbhWM9nQeMP47IzXJN8j2q Xa8m2RFcWRwZhtvxFvVOoEQA6UL4oGBTjUp2XGFb/XuObIlS2X8RdaGjh1PxOY5kM3J+h5 DhycVlCh1kdln+7X9qqOWZkRf8vAe7w= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-176-21qKNekrPvucHTbRYTvVvw-1; Wed, 15 Jan 2020 11:36:36 -0500 X-MC-Unique: 21qKNekrPvucHTbRYTvVvw-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id E33F414E1; Wed, 15 Jan 2020 16:36:33 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-188.ams2.redhat.com [10.36.116.188]) by smtp.corp.redhat.com (Postfix) with ESMTP id 539B75DA76; Wed, 15 Jan 2020 16:36:30 +0000 (UTC) From: Hans de Goede To: Ard Biesheuvel , Darren Hart , Andy Shevchenko , Luis Chamberlain , Greg Kroah-Hartman , "Rafael J . Wysocki" , Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H . Peter Anvin" , Jonathan Corbet , Dmitry Torokhov Cc: Hans de Goede , Peter Jones , Dave Olsthoorn , x86@kernel.org, platform-driver-x86@vger.kernel.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-input@vger.kernel.org Subject: [PATCH v12 07/10] Input: silead - Switch to firmware_request_platform for retreiving the fw Date: Wed, 15 Jan 2020 17:35:51 +0100 Message-Id: <20200115163554.101315-8-hdegoede@redhat.com> In-Reply-To: <20200115163554.101315-1-hdegoede@redhat.com> References: <20200115163554.101315-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Unfortunately sofar we have been unable to get permission to redistribute Silead touchscreen firmwares in linux-firmware. This means that people need to find and install the firmware themselves before the touchscreen will work Some UEFI/x86 tablets with a Silead touchscreen have a copy of the fw embedded in their UEFI boot-services code. This commit makes the silead driver use the new firmware_request_platform function, which will fallback to looking for such an embedded copy when direct filesystem lookup fails. This will make the touchscreen work OOTB on devices where there is a fw copy embedded in the UEFI code. Acked-by: Dmitry Torokhov Signed-off-by: Hans de Goede --- drivers/input/touchscreen/silead.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index ad8b6a2bfd36..8fa2f3b7cfd8 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -288,7 +288,7 @@ static int silead_ts_load_fw(struct i2c_client *client) dev_dbg(dev, "Firmware file name: %s", data->fw_name); - error = request_firmware(&fw, data->fw_name, dev); + error = firmware_request_platform(&fw, data->fw_name, dev); if (error) { dev_err(dev, "Firmware request error %d\n", error); return error; From patchwork Wed Jan 15 16:35:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 208269 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3C86C33CB3 for ; Wed, 15 Jan 2020 16:36:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9BF472467C for ; Wed, 15 Jan 2020 16:36:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="jEqrEcWm" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729252AbgAOQgr (ORCPT ); Wed, 15 Jan 2020 11:36:47 -0500 Received: from us-smtp-1.mimecast.com ([207.211.31.81]:46914 "EHLO us-smtp-delivery-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729248AbgAOQgr (ORCPT ); Wed, 15 Jan 2020 11:36:47 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579106205; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bXVEKi78iq6ADfrfhBiGszXy/96dOsu++UIbMbCrm7w=; b=jEqrEcWm8Nnpyd4EX1wXccTaBOV5Q1LXnrSORy6KxhTEcKpI2vOdWvRrwm1Ns7/QOgxlnF ItX2TVdjai7IPawedNDn3D11orhK/6toRRBnO2R7RI9daCE+TcR9jp8qqq+opzObevvOUv pjX4E0avOCqCk2bAN1D7gwBed7OzxMs= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-93-9X45UpGDMXqeBpAmdgiTEA-1; Wed, 15 Jan 2020 11:36:44 -0500 X-MC-Unique: 9X45UpGDMXqeBpAmdgiTEA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id EB59B18557C0; Wed, 15 Jan 2020 16:36:41 +0000 (UTC) Received: from shalem.localdomain.com (ovpn-116-188.ams2.redhat.com [10.36.116.188]) by smtp.corp.redhat.com (Postfix) with ESMTP id 12A425DA76; Wed, 15 Jan 2020 16:36:37 +0000 (UTC) From: Hans de Goede To: Ard Biesheuvel , Darren Hart , Andy Shevchenko , Luis Chamberlain , Greg Kroah-Hartman , "Rafael J . Wysocki" , Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H . Peter Anvin" , Jonathan Corbet , Dmitry Torokhov Cc: Hans de Goede , Peter Jones , Dave Olsthoorn , x86@kernel.org, platform-driver-x86@vger.kernel.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-input@vger.kernel.org, Andy Shevchenko , Ard Biesheuvel Subject: [PATCH v12 09/10] platform/x86: touchscreen_dmi: Add EFI embedded firmware info support Date: Wed, 15 Jan 2020 17:35:53 +0100 Message-Id: <20200115163554.101315-10-hdegoede@redhat.com> In-Reply-To: <20200115163554.101315-1-hdegoede@redhat.com> References: <20200115163554.101315-1-hdegoede@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Sofar we have been unable to get permission from the vendors to put the firmware for touchscreens listed in touchscreen_dmi in linux-firmware. Some of the tablets with such a touchscreen have a touchscreen driver, and thus a copy of the firmware, as part of their EFI code. This commit adds the necessary info for the new EFI embedded-firmware code to extract these firmwares, making the touchscreen work OOTB without the user needing to manually add the firmware. Acked-by: Andy Shevchenko Acked-by: Ard Biesheuvel Signed-off-by: Hans de Goede --- Changes in v7: - Remove adding of PROPERTY_ENTRY_BOOL("efi-embedded-firmware"), to touchscreen props, as this is no longer necessary Changes in v6: - Switch from crc sums to SHA256 hashes for the firmware hashes --- drivers/firmware/efi/embedded-firmware.c | 3 ++ drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/touchscreen_dmi.c | 41 +++++++++++++++++++++++- include/linux/efi_embedded_fw.h | 2 ++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/embedded-firmware.c b/drivers/firmware/efi/embedded-firmware.c index 1bc9cdae2eed..a1b199de9006 100644 --- a/drivers/firmware/efi/embedded-firmware.c +++ b/drivers/firmware/efi/embedded-firmware.c @@ -21,6 +21,9 @@ EXPORT_SYMBOL_GPL(efi_embedded_fw_list); static bool checked_for_fw; static const struct dmi_system_id * const embedded_fw_table[] = { +#ifdef CONFIG_TOUCHSCREEN_DMI + touchscreen_dmi_table, +#endif NULL }; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 27d5b40fb717..a65f4ffb289a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1253,6 +1253,7 @@ config INTEL_TURBO_MAX_3 config TOUCHSCREEN_DMI bool "DMI based touchscreen configuration info" depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD + select EFI_EMBEDDED_FIRMWARE if EFI ---help--- Certain ACPI based tablets with e.g. Silead or Chipone touchscreens do not have enough data in ACPI tables for the touchscreen driver to diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 72205771d03d..4449e4c0b26b 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -11,12 +11,15 @@ #include #include #include +#include #include #include #include #include struct ts_dmi_data { + /* The EFI embedded-fw code expects this to be the first member! */ + struct efi_embedded_fw_desc embedded_fw; const char *acpi_name; const struct property_entry *properties; }; @@ -64,6 +67,15 @@ static const struct property_entry chuwi_hi8_pro_props[] = { }; static const struct ts_dmi_data chuwi_hi8_pro_data = { + .embedded_fw = { + .name = "silead/gsl3680-chuwi-hi8-pro.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 39864, + .sha256 = { 0xc0, 0x88, 0xc5, 0xef, 0xd1, 0x70, 0x77, 0x59, + 0x4e, 0xe9, 0xc4, 0xd8, 0x2e, 0xcd, 0xbf, 0x95, + 0x32, 0xd9, 0x03, 0x28, 0x0d, 0x48, 0x9f, 0x92, + 0x35, 0x37, 0xf6, 0x8b, 0x2a, 0xe4, 0x73, 0xff }, + }, .acpi_name = "MSSL1680:00", .properties = chuwi_hi8_pro_props, }; @@ -181,6 +193,15 @@ static const struct property_entry cube_iwork8_air_props[] = { }; static const struct ts_dmi_data cube_iwork8_air_data = { + .embedded_fw = { + .name = "silead/gsl3670-cube-iwork8-air.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 38808, + .sha256 = { 0xff, 0x62, 0x2d, 0xd1, 0x8a, 0x78, 0x04, 0x7b, + 0x33, 0x06, 0xb0, 0x4f, 0x7f, 0x02, 0x08, 0x9c, + 0x96, 0xd4, 0x9f, 0x04, 0xe1, 0x47, 0x25, 0x25, + 0x60, 0x77, 0x41, 0x33, 0xeb, 0x12, 0x82, 0xfc }, + }, .acpi_name = "MSSL1680:00", .properties = cube_iwork8_air_props, }; @@ -390,6 +411,15 @@ static const struct property_entry onda_v80_plus_v3_props[] = { }; static const struct ts_dmi_data onda_v80_plus_v3_data = { + .embedded_fw = { + .name = "silead/gsl3676-onda-v80-plus-v3.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 37224, + .sha256 = { 0x8f, 0xbd, 0x8f, 0x0c, 0x6b, 0xba, 0x5b, 0xf5, + 0xa3, 0xc7, 0xa3, 0xc0, 0x4f, 0xcd, 0xdf, 0x32, + 0xcc, 0xe4, 0x70, 0xd6, 0x46, 0x9c, 0xd7, 0xa7, + 0x4b, 0x82, 0x3f, 0xab, 0xc7, 0x90, 0xea, 0x23 }, + }, .acpi_name = "MSSL1680:00", .properties = onda_v80_plus_v3_props, }; @@ -456,6 +486,15 @@ static const struct property_entry pipo_w2s_props[] = { }; static const struct ts_dmi_data pipo_w2s_data = { + .embedded_fw = { + .name = "silead/gsl1680-pipo-w2s.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 39072, + .sha256 = { 0xd0, 0x58, 0xc4, 0x7d, 0x55, 0x2d, 0x62, 0x18, + 0xd1, 0x6a, 0x71, 0x73, 0x0b, 0x3f, 0xbe, 0x60, + 0xbb, 0x45, 0x8c, 0x52, 0x27, 0xb7, 0x18, 0xf4, + 0x31, 0x00, 0x6a, 0x49, 0x76, 0xd8, 0x7c, 0xd3 }, + }, .acpi_name = "MSSL1680:00", .properties = pipo_w2s_props, }; @@ -642,7 +681,7 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = { }; /* NOTE: Please keep this table sorted alphabetically */ -static const struct dmi_system_id touchscreen_dmi_table[] = { +const struct dmi_system_id touchscreen_dmi_table[] = { { /* Chuwi Hi8 */ .driver_data = (void *)&chuwi_hi8_data, diff --git a/include/linux/efi_embedded_fw.h b/include/linux/efi_embedded_fw.h index 3d066c6370c6..57eac5241303 100644 --- a/include/linux/efi_embedded_fw.h +++ b/include/linux/efi_embedded_fw.h @@ -36,6 +36,8 @@ struct efi_embedded_fw_desc { u8 sha256[32]; }; +extern const struct dmi_system_id touchscreen_dmi_table[]; + int efi_get_embedded_fw(const char *name, const u8 **dat, size_t *sz); #endif