firmware_loader: Add sysfs nodes to monitor fw_upload

Add additional sysfs nodes to monitor the transfer of firmware upload data
to the target device:

cancel: Write 1 to cancel the data transfer
error: Display error status for a failed firmware upload
remaining_size: Display the remaining amount of data to be transferred
status: Display the progress of the firmware upload

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-6-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:01 -07:00 committed by Greg Kroah-Hartman
parent 97730bbb24
commit 536fd8184b
5 changed files with 201 additions and 2 deletions

View File

@ -10,6 +10,30 @@ Description: The data sysfs file is used for firmware-fallback and for
signal the lower-level driver that the firmware data is
available.
What: /sys/class/firmware/.../cancel
Date: July 2022
KernelVersion: 5.19
Contact: Russ Weight <russell.h.weight@intel.com>
Description: Write-only. For firmware uploads, write a "1" to this file to
request that the transfer of firmware data to the lower-level
device be canceled. This request will be rejected (EBUSY) if
the update cannot be canceled (e.g. a FLASH write is in
progress) or (ENODEV) if there is no firmware update in progress.
What: /sys/class/firmware/.../error
Date: July 2022
KernelVersion: 5.19
Contact: Russ Weight <russell.h.weight@intel.com>
Description: Read-only. Returns a string describing a failed firmware
upload. This string will be in the form of <STATUS>:<ERROR>,
where <STATUS> will be one of the status strings described
for the status sysfs file and <ERROR> will be one of the
following: "hw-error", "timeout", "user-abort", "device-busy",
"invalid-file-size", "read-write-error", "flash-wearout". The
error sysfs file is only meaningful when the current firmware
upload status is "idle". If this file is read while a firmware
transfer is in progress, then the read will fail with EBUSY.
What: /sys/class/firmware/.../loading
Date: July 2022
KernelVersion: 5.19
@ -22,6 +46,27 @@ Description: The loading sysfs file is used for both firmware-fallback and
uploads, the zero value also triggers the transfer of the
firmware data to the lower-level device driver.
What: /sys/class/firmware/.../remaining_size
Date: July 2022
KernelVersion: 5.19
Contact: Russ Weight <russell.h.weight@intel.com>
Description: Read-only. For firmware upload, this file contains the size
of the firmware data that remains to be transferred to the
lower-level device driver. The size value is initialized to
the full size of the firmware image that was previously
written to the data sysfs file. This value is periodically
updated during the "transferring" phase of the firmware
upload.
Format: "%u".
What: /sys/class/firmware/.../status
Date: July 2022
KernelVersion: 5.19
Contact: Russ Weight <russell.h.weight@intel.com>
Description: Read-only. Returns a string describing the current status of
a firmware upload. The string will be one of the following:
idle, "receiving", "preparing", "transferring", "programming".
What: /sys/class/firmware/.../timeout
Date: July 2022
KernelVersion: 5.19

View File

@ -9,7 +9,8 @@ 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.
documentation for firmware fallback. It also adds additional sysfs files
to provide status on the transfer of the firmware image to the device.
Register for firmware upload
============================
@ -93,7 +94,9 @@ Firmware Upload Ops
Firmware Upload Progress Codes
------------------------------
The following progress codes are used internally by the firmware loader:
The following progress codes are used internally by the firmware loader.
Corresponding strings are reported through the status sysfs node that
is described below and are documented in the ABI documentation.
.. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.h
:identifiers: fw_upload_prog
@ -105,3 +108,19 @@ failure:
.. kernel-doc:: include/linux/firmware.h
:identifiers: fw_upload_err
Sysfs Attributes
================
In addition to the *loading* and *data* sysfs files, there are additional
sysfs files to monitor the status of the data transfer to the target
device and to determine the final pass/fail status of the transfer.
Depending on the device and the size of the firmware image, a firmware
update could take milliseconds or minutes.
The additional sysfs files are:
* status - provides an indication of the progress of a firmware update
* error - provides error information for a failed firmware update
* remaining_size - tracks the data transfer portion of an update
* cancel - echo 1 to this file to cancel the update

View File

@ -371,6 +371,12 @@ static struct bin_attribute firmware_attr_data = {
static struct attribute *fw_dev_attrs[] = {
&dev_attr_loading.attr,
#ifdef CONFIG_FW_UPLOAD
&dev_attr_cancel.attr,
&dev_attr_status.attr,
&dev_attr_error.attr,
&dev_attr_remaining_size.attr,
#endif
NULL
};
@ -382,6 +388,9 @@ static struct bin_attribute *fw_dev_bin_attrs[] = {
static const struct attribute_group fw_dev_attr_group = {
.attrs = fw_dev_attrs,
.bin_attrs = fw_dev_bin_attrs,
#ifdef CONFIG_FW_UPLOAD
.is_visible = fw_upload_is_visible,
#endif
};
static const struct attribute_group *fw_dev_attr_groups[] = {

View File

@ -11,6 +11,127 @@
* Support for user-space to initiate a firmware upload to a device.
*/
static const char * const fw_upload_prog_str[] = {
[FW_UPLOAD_PROG_IDLE] = "idle",
[FW_UPLOAD_PROG_RECEIVING] = "receiving",
[FW_UPLOAD_PROG_PREPARING] = "preparing",
[FW_UPLOAD_PROG_TRANSFERRING] = "transferring",
[FW_UPLOAD_PROG_PROGRAMMING] = "programming"
};
static const char * const fw_upload_err_str[] = {
[FW_UPLOAD_ERR_NONE] = "none",
[FW_UPLOAD_ERR_HW_ERROR] = "hw-error",
[FW_UPLOAD_ERR_TIMEOUT] = "timeout",
[FW_UPLOAD_ERR_CANCELED] = "user-abort",
[FW_UPLOAD_ERR_BUSY] = "device-busy",
[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
[FW_UPLOAD_ERR_RW_ERROR] = "read-write-error",
[FW_UPLOAD_ERR_WEAROUT] = "flash-wearout",
};
static const char *fw_upload_progress(struct device *dev,
enum fw_upload_prog prog)
{
const char *status = "unknown-status";
if (prog < FW_UPLOAD_PROG_MAX)
status = fw_upload_prog_str[prog];
else
dev_err(dev, "Invalid status during secure update: %d\n", prog);
return status;
}
static const char *fw_upload_error(struct device *dev,
enum fw_upload_err err_code)
{
const char *error = "unknown-error";
if (err_code < FW_UPLOAD_ERR_MAX)
error = fw_upload_err_str[err_code];
else
dev_err(dev, "Invalid error code during secure update: %d\n",
err_code);
return error;
}
static ssize_t
status_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
return sysfs_emit(buf, "%s\n", fw_upload_progress(dev, fwlp->progress));
}
DEVICE_ATTR_RO(status);
static ssize_t
error_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
int ret;
mutex_lock(&fwlp->lock);
if (fwlp->progress != FW_UPLOAD_PROG_IDLE)
ret = -EBUSY;
else if (!fwlp->err_code)
ret = 0;
else
ret = sysfs_emit(buf, "%s:%s\n",
fw_upload_progress(dev, fwlp->err_progress),
fw_upload_error(dev, fwlp->err_code));
mutex_unlock(&fwlp->lock);
return ret;
}
DEVICE_ATTR_RO(error);
static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
int ret = count;
bool cancel;
if (kstrtobool(buf, &cancel) || !cancel)
return -EINVAL;
mutex_lock(&fwlp->lock);
if (fwlp->progress == FW_UPLOAD_PROG_IDLE)
ret = -ENODEV;
fwlp->ops->cancel(fwlp->fw_upload);
mutex_unlock(&fwlp->lock);
return ret;
}
DEVICE_ATTR_WO(cancel);
static ssize_t remaining_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv;
return sysfs_emit(buf, "%u\n", fwlp->remaining_size);
}
DEVICE_ATTR_RO(remaining_size);
umode_t
fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n)
{
static struct fw_sysfs *fw_sysfs;
fw_sysfs = to_fw_sysfs(kobj_to_dev(kobj));
if (fw_sysfs->fw_upload_priv || attr == &dev_attr_loading.attr)
return attr->mode;
return 0;
}
static void fw_upload_update_progress(struct fw_upload_priv *fwlp,
enum fw_upload_prog new_progress)
{

View File

@ -37,6 +37,11 @@ struct fw_upload_priv {
};
#ifdef CONFIG_FW_UPLOAD
extern struct device_attribute dev_attr_status;
extern struct device_attribute dev_attr_error;
extern struct device_attribute dev_attr_cancel;
extern struct device_attribute dev_attr_remaining_size;
int fw_upload_start(struct fw_sysfs *fw_sysfs);
umode_t fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n);
#else