Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp3200753imm; Sun, 13 May 2018 06:28:08 -0700 (PDT) X-Google-Smtp-Source: AB8JxZrjeJKpTaxXOZcI75hFBHSKXE8jCC69kd+US3W3rFZtCEfo2EecO7DuhdQDjDQK+3EsGY54 X-Received: by 2002:a17:902:bc4a:: with SMTP id t10-v6mr5981594plz.343.1526218088457; Sun, 13 May 2018 06:28:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526218088; cv=none; d=google.com; s=arc-20160816; b=oCXQmDTY9bNME4agL9qIpAY18JEuc/OscHIy6OL7kVtjr2glaV4qPNfyQca085he36 6T1hTbrWjjFXIO6NtWuK2zmqa7yVT8EyUwR+V0ufHpRDN5kE9D3jMZTmZfbdWdhp1hqa kr3ygAo966qxqmWgTcMW6xA7NcKTZsK8OM9X3Tu7+kVtwQ386rnv6Jb3MKYMlFmiu9cQ xqMVw6tUotNl6R5xBAl1z55GnUyoRwIIiwG4klBOMgPdjV8pPK4AVOEwltUI0uu+gYW/ BCGNixhoepYvjyN8RuAt0gbwXj+ev81wY6LY1SP0h8MJ43KsrwZEJgJUAzxVYQbUaGtl 2O1Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:cc:to:subject:arc-authentication-results; bh=Puu0w9z1PzSLCZUICUW+b2jcfK1tmn3cJoMWslil49o=; b=uccFZ3zihW2Uva7pnMndgM8rPTYnAbQrxIw0J5MteJVE4C6aQwSfb3XrBdfgS84eZ1 mQ32/ibv3x0ZS964MwkBDeZ0PXABbuqNleuG20F+Bha6uB6bsnP6mW8aQjo5r7lI9gG1 OFm7djcuMLwztNmsOxH8PIVr0rcEnF3+cVSeLmWwzQ22DwDSvsS5kJILOKuaizy+He0R 7Xum7kYKLS+soZKqUpDwJOlJI85xCdCNJPHDbsd4EWvwKwHozqJPgBlcF5UxyZmA1Fcw o/wQv5plS8a9WwatmpQ8DnlLH8A8oqkBwUCiM9APQLIokk1XdvgjmCVNzLa3bM5I3HSt CyNA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s30-v6si5823026pgo.638.2018.05.13.06.27.52; Sun, 13 May 2018 06:28:08 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751596AbeEMN03 (ORCPT + 99 others); Sun, 13 May 2018 09:26:29 -0400 Received: from mail-wr0-f196.google.com ([209.85.128.196]:45592 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751313AbeEMN01 (ORCPT ); Sun, 13 May 2018 09:26:27 -0400 Received: by mail-wr0-f196.google.com with SMTP id p5-v6so9549915wre.12 for ; Sun, 13 May 2018 06:26:27 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=Puu0w9z1PzSLCZUICUW+b2jcfK1tmn3cJoMWslil49o=; b=c9eemFH/kUUBra9u9593wPlk1MJs5IIx3EupaHD8kWkwiBJryg+6gTQcF4Es7sn4Z3 2aZDHowMg51q5+prpSWDHs3Dq1dbv30E4uEsdin+l+oukDJ1ApxMa2hGh4IyJSeRTQT8 2llc1yGO6NBKie4f/Y8eWOMX7RAFVkA7rZksXICHhcxk32/8V8XrFUrJVAISXGXUNZf6 lVslcSd9zBaf/K9GyGFAGbMQvQB9hGN6xxQIXimMMANOI6kL7lWguov/DIwfrcWayQ4B KdisN8LBSPhApBOecJXaMvR2pL8F4O3Z3K0B0eoCxGon8hCS4xIee0zE9jWVaVnmLXLX K9sw== X-Gm-Message-State: ALKqPwep4LfC5CXxNyTNU/IL0ufYX62tx+1jlNOqEnwLYRP379G62osP rDCKBBMZEoyst6dqLSPThcMPVyNAJyE= X-Received: by 2002:adf:bd01:: with SMTP id j1-v6mr4603054wrh.69.1526217986226; Sun, 13 May 2018 06:26:26 -0700 (PDT) Received: from localhost.localdomain ([109.144.219.116]) by smtp.gmail.com with ESMTPSA id n23-v6sm9178027wra.39.2018.05.13.06.26.22 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 13 May 2018 06:26:25 -0700 (PDT) Subject: Re: [PATCH v5 2/5] efi: Add embedded peripheral firmware support To: Ard Biesheuvel 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 References: <20180429093558.5411-1-hdegoede@redhat.com> <20180429093558.5411-3-hdegoede@redhat.com> <7bc779ac-864c-cf43-dda7-bd9705f76be6@redhat.com> From: Hans de Goede Message-ID: Date: Sun, 13 May 2018 14:26:21 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.7.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi, On 05/13/2018 12:43 PM, Ard Biesheuvel wrote: > 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. Ok, I've reworked the code to get rid of the compares in the if condition. Regards, Hans > >>> >>>> + /* 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 >>>> >>