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: Luis Chamberlain <mcgrof@kernel.org>
Reviewed-by: Tianfei zhang <tianfei.zhang@intel.com>
Tested-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
Signed-off-by: Russ Weight <russell.h.weight@intel.com>
Link: https://lore.kernel.org/r/20220421212204.36052-5-russell.h.weight@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Russ Weight 2022-04-21 14:22:00 -07:00 committed by Greg Kroah-Hartman
parent e0c11a8b98
commit 97730bbb24
12 changed files with 595 additions and 12 deletions

View file

@ -17,6 +17,64 @@ struct firmware {
void *priv;
};
/**
* enum fw_upload_err - firmware upload error codes
* @FW_UPLOAD_ERR_NONE: returned to indicate success
* @FW_UPLOAD_ERR_HW_ERROR: error signalled by hardware, see kernel log
* @FW_UPLOAD_ERR_TIMEOUT: SW timed out on handshake with HW/firmware
* @FW_UPLOAD_ERR_CANCELED: upload was cancelled by the user
* @FW_UPLOAD_ERR_BUSY: there is an upload operation already in progress
* @FW_UPLOAD_ERR_INVALID_SIZE: invalid firmware image size
* @FW_UPLOAD_ERR_RW_ERROR: read or write to HW failed, see kernel log
* @FW_UPLOAD_ERR_WEAROUT: FLASH device is approaching wear-out, wait & retry
* @FW_UPLOAD_ERR_MAX: Maximum error code marker
*/
enum fw_upload_err {
FW_UPLOAD_ERR_NONE,
FW_UPLOAD_ERR_HW_ERROR,
FW_UPLOAD_ERR_TIMEOUT,
FW_UPLOAD_ERR_CANCELED,
FW_UPLOAD_ERR_BUSY,
FW_UPLOAD_ERR_INVALID_SIZE,
FW_UPLOAD_ERR_RW_ERROR,
FW_UPLOAD_ERR_WEAROUT,
FW_UPLOAD_ERR_MAX
};
struct fw_upload {
void *dd_handle; /* reference to parent driver */
void *priv; /* firmware loader private fields */
};
/**
* struct fw_upload_ops - device specific operations to support firmware upload
* @prepare: Required: Prepare secure update
* @write: Required: The write() op receives the remaining
* size to be written and must return the actual
* size written or a negative error code. The write()
* op will be called repeatedly until all data is
* written.
* @poll_complete: Required: Check for the completion of the
* HW authentication/programming process.
* @cancel: Required: Request cancellation of update. This op
* is called from the context of a different kernel
* thread, so race conditions need to be considered.
* @cleanup: Optional: Complements the prepare()
* function and is called at the completion
* of the update, on success or failure, if the
* prepare function succeeded.
*/
struct fw_upload_ops {
enum fw_upload_err (*prepare)(struct fw_upload *fw_upload,
const u8 *data, u32 size);
enum fw_upload_err (*write)(struct fw_upload *fw_upload,
const u8 *data, u32 offset,
u32 size, u32 *written);
enum fw_upload_err (*poll_complete)(struct fw_upload *fw_upload);
void (*cancel)(struct fw_upload *fw_upload);
void (*cleanup)(struct fw_upload *fw_upload);
};
struct module;
struct device;
@ -112,6 +170,30 @@ static inline int request_partial_firmware_into_buf
#endif
#ifdef CONFIG_FW_UPLOAD
struct fw_upload *
firmware_upload_register(struct module *module, struct device *parent,
const char *name, const struct fw_upload_ops *ops,
void *dd_handle);
void firmware_upload_unregister(struct fw_upload *fw_upload);
#else
static inline struct fw_upload *
firmware_upload_register(struct module *module, struct device *parent,
const char *name, const struct fw_upload_ops *ops,
void *dd_handle)
{
return ERR_PTR(-EINVAL);
}
static inline void firmware_upload_unregister(struct fw_upload *fw_upload)
{
}
#endif
int firmware_request_cache(struct device *device, const char *name);
#endif