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
|
signal the lower-level driver that the firmware data is
|
||||||
available.
|
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
|
What: /sys/class/firmware/.../loading
|
||||||
Date: July 2022
|
Date: July 2022
|
||||||
KernelVersion: 5.19
|
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
|
uploads, the zero value also triggers the transfer of the
|
||||||
firmware data to the lower-level device driver.
|
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
|
What: /sys/class/firmware/.../timeout
|
||||||
Date: July 2022
|
Date: July 2022
|
||||||
KernelVersion: 5.19
|
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
|
that device. It is the responsibility of the device driver and/or the
|
||||||
device itself to perform any validation on the data received. Firmware
|
device itself to perform any validation on the data received. Firmware
|
||||||
upload uses the same *loading* and *data* sysfs files described in the
|
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
|
Register for firmware upload
|
||||||
============================
|
============================
|
||||||
|
@ -93,7 +94,9 @@ Firmware Upload Ops
|
||||||
|
|
||||||
Firmware Upload Progress Codes
|
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
|
.. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.h
|
||||||
:identifiers: fw_upload_prog
|
:identifiers: fw_upload_prog
|
||||||
|
@ -105,3 +108,19 @@ failure:
|
||||||
|
|
||||||
.. kernel-doc:: include/linux/firmware.h
|
.. kernel-doc:: include/linux/firmware.h
|
||||||
:identifiers: fw_upload_err
|
: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[] = {
|
static struct attribute *fw_dev_attrs[] = {
|
||||||
&dev_attr_loading.attr,
|
&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
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -382,6 +388,9 @@ static struct bin_attribute *fw_dev_bin_attrs[] = {
|
||||||
static const struct attribute_group fw_dev_attr_group = {
|
static const struct attribute_group fw_dev_attr_group = {
|
||||||
.attrs = fw_dev_attrs,
|
.attrs = fw_dev_attrs,
|
||||||
.bin_attrs = fw_dev_bin_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[] = {
|
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.
|
* 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,
|
static void fw_upload_update_progress(struct fw_upload_priv *fwlp,
|
||||||
enum fw_upload_prog new_progress)
|
enum fw_upload_prog new_progress)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,6 +37,11 @@ struct fw_upload_priv {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_FW_UPLOAD
|
#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);
|
int fw_upload_start(struct fw_sysfs *fw_sysfs);
|
||||||
umode_t fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n);
|
umode_t fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n);
|
||||||
#else
|
#else
|
||||||
|
|
Loading…
Reference in New Issue