Commit 97730bbb authored by Russ Weight's avatar Russ Weight Committed by Greg Kroah-Hartman
Browse files

firmware_loader: Add firmware-upload support



Extend the firmware subsystem to support a persistent sysfs interface that
userspace may use to initiate a firmware update. For example, FPGA based
PCIe cards load firmware and FPGA images from local FLASH when the card
boots. The images in FLASH may be updated with new images provided by the
user at his/her convenience.

A device driver may call firmware_upload_register() to expose persistent
"loading" and "data" sysfs files. These files are used in the same way as
the fallback sysfs "loading" and "data" files. When 0 is written to
"loading" to complete the write of firmware data, the data is transferred
to the lower-level driver using pre-registered call-back functions. The
data transfer is done in the context of a kernel worker thread.

Reviewed-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
Reviewed-by: default avatarTianfei zhang <tianfei.zhang@intel.com>
Tested-by: default avatarMatthew Gerlach <matthew.gerlach@linux.intel.com>
Signed-off-by: default avatarRuss Weight <russell.h.weight@intel.com>
Link: https://lore.kernel.org/r/20220421212204.36052-5-russell.h.weight@intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e0c11a8b
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
What: 		/sys/class/firmware/.../data
Date:		July 2022
KernelVersion:	5.19
Contact:	Russ Weight <russell.h.weight@intel.com>
Description:	The data sysfs file is used for firmware-fallback and for
		firmware uploads. Cat a firmware image to this sysfs file
		after you echo 1 to the loading sysfs file. When the firmware
		image write is complete, echo 0 to the loading sysfs file. This
		sequence will signal the completion of the firmware write and
		signal the lower-level driver that the firmware data is
		available.

What: 		/sys/class/firmware/.../loading
Date:		July 2022
KernelVersion:	5.19
Contact:	Russ Weight <russell.h.weight@intel.com>
Description:	The loading sysfs file is used for both firmware-fallback and
		for firmware uploads. Echo 1 onto the loading file to indicate
		you are writing a firmware file to the data sysfs node. Echo
		-1 onto this file to abort the data write or echo 0 onto this
		file to indicate that the write is complete. For firmware
		uploads, the zero value also triggers the transfer of the
		firmware data to the lower-level device driver.

What: 		/sys/class/firmware/.../timeout
Date:		July 2022
KernelVersion:	5.19
Contact:	Russ Weight <russell.h.weight@intel.com>
Description:	This file supports the timeout mechanism for firmware
		fallback.  This file has no affect on firmware uploads. For
		more information on timeouts please see the documentation
		for firmware fallback.
+107 −0
Original line number Diff line number Diff line
.. SPDX-License-Identifier: GPL-2.0

===================
Firmware Upload API
===================

A device driver that registers with the firmware loader will expose
persistent sysfs nodes to enable users to initiate firmware updates for
that device.  It is the responsibility of the device driver and/or the
device itself to perform any validation on the data received. Firmware
upload uses the same *loading* and *data* sysfs files described in the
documentation for firmware fallback.

Register for firmware upload
============================

A device driver registers for firmware upload by calling
firmware_upload_register(). Among the parameter list is a name to
identify the device under /sys/class/firmware. A user may initiate a
firmware upload by echoing a 1 to the *loading* sysfs file for the target
device. Next, the user writes the firmware image to the *data* sysfs
file. After writing the firmware data, the user echos 0 to the *loading*
sysfs file to signal completion. Echoing 0 to *loading* also triggers the
transfer of the firmware to the lower-lever device driver in the context
of a kernel worker thread.

To use the firmware upload API, write a driver that implements a set of
ops.  The probe function calls firmware_upload_register() and the remove
function calls firmware_upload_unregister() such as::

	static const struct fw_upload_ops m10bmc_ops = {
		.prepare = m10bmc_sec_prepare,
		.write = m10bmc_sec_write,
		.poll_complete = m10bmc_sec_poll_complete,
		.cancel = m10bmc_sec_cancel,
		.cleanup = m10bmc_sec_cleanup,
	};

	static int m10bmc_sec_probe(struct platform_device *pdev)
	{
		const char *fw_name, *truncate;
		struct m10bmc_sec *sec;
		struct fw_upload *fwl;
		unsigned int len;

		sec = devm_kzalloc(&pdev->dev, sizeof(*sec), GFP_KERNEL);
		if (!sec)
			return -ENOMEM;

		sec->dev = &pdev->dev;
		sec->m10bmc = dev_get_drvdata(pdev->dev.parent);
		dev_set_drvdata(&pdev->dev, sec);

		fw_name = dev_name(sec->dev);
		truncate = strstr(fw_name, ".auto");
		len = (truncate) ? truncate - fw_name : strlen(fw_name);
		sec->fw_name = kmemdup_nul(fw_name, len, GFP_KERNEL);

		fwl = firmware_upload_register(sec->dev, sec->fw_name, &m10bmc_ops, sec);
		if (IS_ERR(fwl)) {
			dev_err(sec->dev, "Firmware Upload driver failed to start\n");
			kfree(sec->fw_name);
			return PTR_ERR(fwl);
		}

		sec->fwl = fwl;
		return 0;
	}

	static int m10bmc_sec_remove(struct platform_device *pdev)
	{
		struct m10bmc_sec *sec = dev_get_drvdata(&pdev->dev);

		firmware_upload_unregister(sec->fwl);
		kfree(sec->fw_name);
		return 0;
	}

firmware_upload_register
------------------------
.. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.c
   :identifiers: firmware_upload_register

firmware_upload_unregister
--------------------------
.. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.c
   :identifiers: firmware_upload_unregister

Firmware Upload Ops
-------------------
.. kernel-doc:: include/linux/firmware.h
   :identifiers: fw_upload_ops

Firmware Upload Progress Codes
------------------------------
The following progress codes are used internally by the firmware loader:

.. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.h
   :identifiers: fw_upload_prog

Firmware Upload Error Codes
---------------------------
The following error codes may be returned by the driver ops in case of
failure:

.. kernel-doc:: include/linux/firmware.h
   :identifiers: fw_upload_err
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ Linux Firmware API
   core
   efi/index
   request_firmware
   fw_upload
   other_interfaces

.. only::  subproject and html
+14 −0
Original line number Diff line number Diff line
@@ -202,5 +202,19 @@ config FW_CACHE

	  If unsure, say Y.

config FW_UPLOAD
	bool "Enable users to initiate firmware updates using sysfs"
	select FW_LOADER_SYSFS
	select FW_LOADER_PAGED_BUF
	help
	  Enabling this option will allow device drivers to expose a persistent
	  sysfs interface that allows firmware updates to be initiated from
	  userspace. For example, FPGA based PCIe cards load firmware and FPGA
	  images from local FLASH when the card boots. The images in FLASH may
	  be updated with new images provided by the user. Enable this device
	  to support cards that rely on user-initiated updates for firmware files.

	  If unsure, say N.

endif # FW_LOADER
endmenu
+1 −0
Original line number Diff line number Diff line
@@ -7,5 +7,6 @@ firmware_class-objs := main.o
firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o
firmware_class-$(CONFIG_FW_LOADER_SYSFS) += sysfs.o
firmware_class-$(CONFIG_FW_UPLOAD) += sysfs_upload.o

obj-y += builtin/
Loading