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:
parent
97730bbb24
commit
536fd8184b
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue