Received: by 10.192.165.148 with SMTP id m20csp133283imm; Thu, 3 May 2018 16:30:00 -0700 (PDT) X-Google-Smtp-Source: AB8JxZoqnnlBR8P20h/IIyCLRTnCOnNz0Dn85nce29meir+/hhifnNVc+j6VRDKKQQ+iOUFmErMS X-Received: by 2002:a17:902:380c:: with SMTP id l12-v6mr13927590plc.19.1525390199999; Thu, 03 May 2018 16:29:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1525390199; cv=none; d=google.com; s=arc-20160816; b=aSZ5I8Jm0tqUXac1/jQXnNBsr7WDgPkAhjUgsAeTzfR82i3gHbFm6SQj7X+EVcaEuI ciDhqqkE+pIRHbGsN8eQoJ6Vw2C7GbplX+ZEmA6nzadTqBzzjpXF8d2UzzaHjwnOHCSW DTOWOCXeFUXjoZM77jjaAhQ+/f7PdX6TkvMs6M7rGuG1TQOnINZhdMSf5PO6SwFlNEG0 /JUDdLm9cI6U2FnQF88LqUTmfW7CYRmUle7sqv4ta9T3Xgpi1lFL/IaIiCosp/N53Etc zcpH2O7haV8vqeLJ1zNa3+KPO5llLfX3FLkL/o2IKIwSX5zYXcjJ/M4jykJQ/6Nk/WFE oA/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:user-agent:in-reply-to :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:arc-authentication-results; bh=jumBzbzTyfX3VhuP/8vfxOJqfpBls4FsWzK+kgpZU70=; b=cmZgNC36hMfTjGC79L4sw4AhD2RysWRDviKg1fq7mHujJJ91JAMffYKpnkbYJJmOJ9 49/AZD+TeOoyZ+DpU3kndL/IwFnJ+b6LC/3BLxHbrTkPO+JPSNastBxhE9LJlP39lqnB +nbhZ8hjYgrDtWfBQyEWmsNLfULnpToqlAfwNuovuydtXpTF0CDrh8H07SiI3GbT8gC+ ti+ma+uto4ef/MyGKuTlao2EISGXyWMVcIY2P1/7QI57UFvaIfg3+phMUspjAYb6rogx Df76k96Ll0+A8C8QB72RKgMj006TEDbkuwO/08uHAF+IKWAP/6rfLWMNooD477+PyhtC noEg== 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=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id h91-v6si5583104pld.559.2018.05.03.16.29.45; Thu, 03 May 2018 16:29:59 -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=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751195AbeECX32 (ORCPT + 99 others); Thu, 3 May 2018 19:29:28 -0400 Received: from mx2.suse.de ([195.135.220.15]:59265 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750911AbeECX30 (ORCPT ); Thu, 3 May 2018 19:29:26 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 8BE94AF5E; Thu, 3 May 2018 23:29:24 +0000 (UTC) Date: Thu, 3 May 2018 23:29:21 +0000 From: "Luis R. Rodriguez" To: Hans de Goede Cc: Ard Biesheuvel , "Luis R . Rodriguez" , Greg Kroah-Hartman , Thomas Gleixner , Ingo Molnar , "H . Peter Anvin" , Peter Jones , Dave Olsthoorn , Will Deacon , andresx7@gmail.com, Andy Lutomirski , Matt Fleming , David Howells , Mimi Zohar , Josh Triplett , dmitry.torokhov@gmail.com, mfuzzey@parkeon.com, Kalle Valo , Arend Van Spriel , Linus Torvalds , nbroeking@me.com, bjorn.andersson@linaro.org, Torsten Duwe , Kees Cook , x86@kernel.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH v5 2/5] efi: Add embedded peripheral firmware support Message-ID: <20180503232920.GF27853@wotan.suse.de> References: <20180429093558.5411-1-hdegoede@redhat.com> <20180429093558.5411-3-hdegoede@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20180429093558.5411-3-hdegoede@redhat.com> User-Agent: Mutt/1.6.0 (2016-04-01) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Please Cc andresx7@gmail.com on future patches. On Sun, Apr 29, 2018 at 11:35:55AM +0200, 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. Interesting. Mimi you may want to look into that for appraisal gap coverage. > 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: This and other reasons have been scattered through mailing lists, and documentation, our Kconfig does not reflect which of the reasons are currently addressed as how. Our goal for your commit log would be to document the precise reason you can't use any of the existing interfaces exposed. Let's see if some of the reasons you state cannot be used with the existing APIs. > 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. In such cases the firmware may be stashed in a separate partition of a custom device and firmwared could be used with a uevent and the fallback mechanism, enabling userspace to do whatever it needs. However in your case the firmware has been found on EFI. > 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. Indeed, we don't have a good way to deal with this now except the above noted firmwared use case, and that still ponies up the redistribution problem up to a system integrator somehow. But also -- your solution is super custom to only two drivers, does not follow a standard of any sort either, and I fear of setting any wrong precedents which will be hard to later not support. Just look at the fallback mechanism and how hairy it got, I've put some love into it recently but it was a pain to even comprehend. Once we add this interface we are going to have to support it for a while so I want to be sure we get the architecture right. Specially if were going to enable *other* vendors to start using such interface. Is your goal to enable or encourage other vendors in similar predicament to use the same strategy? There is nothing wrong in setting de-facto standards for Linux, we do this all the time, but if we are going to do it I want to be sure we document this well and provide proper justifications in the commit log and documentation. > This commit adds support for finding peripheral firmware embedded in the > EFI code Please specify this is through a custom scheme.... as it reads now it seems to read this is a sort of standard. > diff --git a/Documentation/driver-api/firmware/request_firmware.rst b/Documentation/driver-api/firmware/request_firmware.rst > index c8bddbdcfd10..560dfed76e38 100644 > --- a/Documentation/driver-api/firmware/request_firmware.rst > +++ b/Documentation/driver-api/firmware/request_firmware.rst > @@ -73,3 +73,69 @@ If something went wrong firmware_request() returns non-zero and fw_entry > is set to NULL. Once your driver is done with processing the firmware it > can call call firmware_release(fw_entry) to release the firmware image > and any related resource. > + > +EFI embedded firmware support > +============================= This is a new fallback mechanism, please see: Documentation/driver-api/firmware/fallback-mechanisms.rst Refer to the section "Types of fallback mechanisms", augument the list there and then move the section "Firmware sysfs loading facility" to a new file, and then add a new file for your own. > + > +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. You in no way indicate this is a just an invented scheme, a custom solution and nothing standard. I realize Ard criticized that the EFI Firmware Volume Protocol is not part of the UEFI spec -- however it is a bit more widely used right? Why can't Linux support it instead? Could your new scheme grow to support the EFI Firmware Volume Protocol rather easily if someone wants to wrap up their sleeves? If so what likely would change? Ensure that the documentation answers the question then, when would folks use the EFI Firmware Volume Protocol? Why can't Linux support it? Why and would use this alternative scheme? Granted, we seem to have reverse engineered it, but we can certainly improve upon it, and if we're going to make a new de-facto standard for Linux best we cover our bases for our justifications. > + > +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 crc32 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, Well we should also only enable this for systems that enable these drivers *and* enable this kernel feature. Ie, if the drivers are enabled but the feature is disabled nothing new should happen. I'd like this feature disabled by default for now. > 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 request_firmware() 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/firmare. > + > +To make request_firmware() fallback to trying This sounds funny, best to just start off describing that this is just a new fallback mechanism. > EFI embedded firmwares after this, > +the driver must set a boolean "efi-embedded-firmware" device-property on the > +device before passing it to request_firmware() Mention that this is for struct platform_device's and what you'd use is PROPERTY_ENTRY_BOOL(). Provide an example. Anyway, I'm in no way a fan of this being the only requirement. It makes no sense to call into EFI or check a device property if the driver didn't even mean it to happen. As I have been asking for a while now, please also add a new firmware_request_efi() which if called would enable a new internal firmware_loader flag FW_OPT_EFI which you'd kdoc'ify and add to drivers/base/firmware_loader/firmware.h. Then only if *this* internal flag is set would we call fw_get_efi_embedded_fw() (I'm also asking you to rename this to firmware_fallback_efi() below). > . Note that this disables the > +usual usermodehelper fallback, so you may want to only set this on systems > +which match your dmi_system_id array. You'll need both the device property and for the driver to use this new firmware_request_efi() call. > + > +Once the device-property is set, the driver can use the regular > +request_firmware() function to get the firmware, using the name filled in > +in the efi_embedded_fw_desc. And nuke this. > + > +Note that: > + > +1. The code scanning for EFI embbedded-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 > + embbedded-firmware. > + > +2. ATM 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. ATM 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. > diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile > index a97eeb0be1d8..365a040995d3 100644 > --- a/drivers/base/firmware_loader/Makefile > +++ b/drivers/base/firmware_loader/Makefile > @@ -5,3 +5,4 @@ obj-y := 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_efi.o Please add a CONFIG_FW_LOADER_FALLBACK_EFI which will be properly documented on a Kconfig which then if selected would select CONFIG_EFI_EMBEDDED_FIRMWARE which enables the efi_check_for_embedded_firmwares(). Note you'd be expanding CONFIG_FW_LOADER_FALLBACK_EFI with the new call too. > diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h > index 8cfaa3299bb7..92f462415d25 100644 > --- a/drivers/base/firmware_loader/fallback.h > +++ b/drivers/base/firmware_loader/fallback.h > @@ -66,4 +66,16 @@ static inline void unregister_sysfs_loader(void) > } > #endif /* CONFIG_FW_LOADER_USER_HELPER */ > > +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE > +int fw_get_efi_embedded_fw(struct device *dev, struct fw_priv *fw_priv, > + enum fw_opt *opt_flags, int ret); > +#else > +static inline int fw_get_efi_embedded_fw(struct device *dev, > + struct fw_priv *fw_priv, > + enum fw_opt *opt_flags, int ret) > +{ > + return ret; > +} > +#endif /* CONFIG_EFI_EMBEDDED_FIRMWARE */ > + > #endif /* __FIRMWARE_FALLBACK_H */ > diff --git a/drivers/base/firmware_loader/fallback_efi.c b/drivers/base/firmware_loader/fallback_efi.c > new file mode 100644 > index 000000000000..82ba82f48a79 > --- /dev/null > +++ b/drivers/base/firmware_loader/fallback_efi.c > @@ -0,0 +1,51 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include > +#include > +#include > +#include > + > +#include "fallback.h" > +#include "firmware.h" > + > +int fw_get_efi_embedded_fw(struct device *dev, struct fw_priv *fw_priv, > + enum fw_opt *opt_flags, int ret) > +{ Please rename to firmware_fallback_efi() and kdocify it above. I'll soon post a patch which does the same for firmware_fallback_sysfs(). Andres's pending patch renames fw_sysfs_fallback() to firmware_sysfs_fallback() but in retrospect it should be firmware_fallback_sysfs() -- I'll fix this. > + enum kernel_read_file_id id = READING_FIRMWARE; > + size_t size, max = INT_MAX; > + int rc; > + > + if (!dev) > + return ret; > + > + if (!device_property_read_bool(dev, "efi-embedded-firmware")) > + return ret; > + > + *opt_flags |= FW_OPT_NO_WARN | FW_OPT_NOCACHE | FW_OPT_NOFALLBACK; > + > + /* Already populated data member means we're loading into a buffer */ > + if (fw_priv->data) { > + id = READING_FIRMWARE_PREALLOC_BUFFER; > + max = fw_priv->allocated_size; > + } > + > + rc = efi_get_embedded_fw(fw_priv->fw_name, &fw_priv->data, &size, max); > + if (rc) { > + dev_warn(dev, "Firmware %s not in EFI\n", fw_priv->fw_name); > + return ret; > + } > + > + rc = security_kernel_post_read_file(NULL, fw_priv->data, size, id); > + if (rc) { > + if (id != READING_FIRMWARE_PREALLOC_BUFFER) { > + vfree(fw_priv->data); > + fw_priv->data = NULL; > + } > + return rc; > + } > + > + dev_dbg(dev, "using efi-embedded fw %s\n", fw_priv->fw_name); > + fw_priv->size = size; > + fw_state_done(fw_priv); > + return 0; > +} > diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c > index f009566acd35..23c5392eb59e 100644 > --- a/drivers/base/firmware_loader/main.c > +++ b/drivers/base/firmware_loader/main.c > @@ -576,6 +576,8 @@ _firmware_request(const struct firmware **firmware_p, const char *name, > goto out; > > ret = fw_get_filesystem_firmware(device, fw->priv); > + if (ret) > + ret = fw_get_efi_embedded_fw(device, fw->priv, &opt_flags, ret); This is going to get complex. If you can come up with a nicer scheme for dealing with fallback that'd be appreciated. If you this is the most legible you can come up with I understand though, I can have a look later. Luis