Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Russ Weight and committed by
Greg Kroah-Hartman
536fd818 97730bbb

+201 -2
+45
Documentation/ABI/testing/sysfs-class-firmware
··· 10 10 signal the lower-level driver that the firmware data is 11 11 available. 12 12 13 + What: /sys/class/firmware/.../cancel 14 + Date: July 2022 15 + KernelVersion: 5.19 16 + Contact: Russ Weight <russell.h.weight@intel.com> 17 + Description: Write-only. For firmware uploads, write a "1" to this file to 18 + request that the transfer of firmware data to the lower-level 19 + device be canceled. This request will be rejected (EBUSY) if 20 + the update cannot be canceled (e.g. a FLASH write is in 21 + progress) or (ENODEV) if there is no firmware update in progress. 22 + 23 + What: /sys/class/firmware/.../error 24 + Date: July 2022 25 + KernelVersion: 5.19 26 + Contact: Russ Weight <russell.h.weight@intel.com> 27 + Description: Read-only. Returns a string describing a failed firmware 28 + upload. This string will be in the form of <STATUS>:<ERROR>, 29 + where <STATUS> will be one of the status strings described 30 + for the status sysfs file and <ERROR> will be one of the 31 + following: "hw-error", "timeout", "user-abort", "device-busy", 32 + "invalid-file-size", "read-write-error", "flash-wearout". The 33 + error sysfs file is only meaningful when the current firmware 34 + upload status is "idle". If this file is read while a firmware 35 + transfer is in progress, then the read will fail with EBUSY. 36 + 13 37 What: /sys/class/firmware/.../loading 14 38 Date: July 2022 15 39 KernelVersion: 5.19 ··· 45 21 file to indicate that the write is complete. For firmware 46 22 uploads, the zero value also triggers the transfer of the 47 23 firmware data to the lower-level device driver. 24 + 25 + What: /sys/class/firmware/.../remaining_size 26 + Date: July 2022 27 + KernelVersion: 5.19 28 + Contact: Russ Weight <russell.h.weight@intel.com> 29 + Description: Read-only. For firmware upload, this file contains the size 30 + of the firmware data that remains to be transferred to the 31 + lower-level device driver. The size value is initialized to 32 + the full size of the firmware image that was previously 33 + written to the data sysfs file. This value is periodically 34 + updated during the "transferring" phase of the firmware 35 + upload. 36 + Format: "%u". 37 + 38 + What: /sys/class/firmware/.../status 39 + Date: July 2022 40 + KernelVersion: 5.19 41 + Contact: Russ Weight <russell.h.weight@intel.com> 42 + Description: Read-only. Returns a string describing the current status of 43 + a firmware upload. The string will be one of the following: 44 + idle, "receiving", "preparing", "transferring", "programming". 48 45 49 46 What: /sys/class/firmware/.../timeout 50 47 Date: July 2022
+21 -2
Documentation/driver-api/firmware/fw_upload.rst
··· 9 9 that device. It is the responsibility of the device driver and/or the 10 10 device itself to perform any validation on the data received. Firmware 11 11 upload uses the same *loading* and *data* sysfs files described in the 12 - documentation for firmware fallback. 12 + documentation for firmware fallback. It also adds additional sysfs files 13 + to provide status on the transfer of the firmware image to the device. 13 14 14 15 Register for firmware upload 15 16 ============================ ··· 94 93 95 94 Firmware Upload Progress Codes 96 95 ------------------------------ 97 - The following progress codes are used internally by the firmware loader: 96 + The following progress codes are used internally by the firmware loader. 97 + Corresponding strings are reported through the status sysfs node that 98 + is described below and are documented in the ABI documentation. 98 99 99 100 .. kernel-doc:: drivers/base/firmware_loader/sysfs_upload.h 100 101 :identifiers: fw_upload_prog ··· 108 105 109 106 .. kernel-doc:: include/linux/firmware.h 110 107 :identifiers: fw_upload_err 108 + 109 + Sysfs Attributes 110 + ================ 111 + 112 + In addition to the *loading* and *data* sysfs files, there are additional 113 + sysfs files to monitor the status of the data transfer to the target 114 + device and to determine the final pass/fail status of the transfer. 115 + Depending on the device and the size of the firmware image, a firmware 116 + update could take milliseconds or minutes. 117 + 118 + The additional sysfs files are: 119 + 120 + * status - provides an indication of the progress of a firmware update 121 + * error - provides error information for a failed firmware update 122 + * remaining_size - tracks the data transfer portion of an update 123 + * cancel - echo 1 to this file to cancel the update
+9
drivers/base/firmware_loader/sysfs.c
··· 371 371 372 372 static struct attribute *fw_dev_attrs[] = { 373 373 &dev_attr_loading.attr, 374 + #ifdef CONFIG_FW_UPLOAD 375 + &dev_attr_cancel.attr, 376 + &dev_attr_status.attr, 377 + &dev_attr_error.attr, 378 + &dev_attr_remaining_size.attr, 379 + #endif 374 380 NULL 375 381 }; 376 382 ··· 388 382 static const struct attribute_group fw_dev_attr_group = { 389 383 .attrs = fw_dev_attrs, 390 384 .bin_attrs = fw_dev_bin_attrs, 385 + #ifdef CONFIG_FW_UPLOAD 386 + .is_visible = fw_upload_is_visible, 387 + #endif 391 388 }; 392 389 393 390 static const struct attribute_group *fw_dev_attr_groups[] = {
+121
drivers/base/firmware_loader/sysfs_upload.c
··· 11 11 * Support for user-space to initiate a firmware upload to a device. 12 12 */ 13 13 14 + static const char * const fw_upload_prog_str[] = { 15 + [FW_UPLOAD_PROG_IDLE] = "idle", 16 + [FW_UPLOAD_PROG_RECEIVING] = "receiving", 17 + [FW_UPLOAD_PROG_PREPARING] = "preparing", 18 + [FW_UPLOAD_PROG_TRANSFERRING] = "transferring", 19 + [FW_UPLOAD_PROG_PROGRAMMING] = "programming" 20 + }; 21 + 22 + static const char * const fw_upload_err_str[] = { 23 + [FW_UPLOAD_ERR_NONE] = "none", 24 + [FW_UPLOAD_ERR_HW_ERROR] = "hw-error", 25 + [FW_UPLOAD_ERR_TIMEOUT] = "timeout", 26 + [FW_UPLOAD_ERR_CANCELED] = "user-abort", 27 + [FW_UPLOAD_ERR_BUSY] = "device-busy", 28 + [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size", 29 + [FW_UPLOAD_ERR_RW_ERROR] = "read-write-error", 30 + [FW_UPLOAD_ERR_WEAROUT] = "flash-wearout", 31 + }; 32 + 33 + static const char *fw_upload_progress(struct device *dev, 34 + enum fw_upload_prog prog) 35 + { 36 + const char *status = "unknown-status"; 37 + 38 + if (prog < FW_UPLOAD_PROG_MAX) 39 + status = fw_upload_prog_str[prog]; 40 + else 41 + dev_err(dev, "Invalid status during secure update: %d\n", prog); 42 + 43 + return status; 44 + } 45 + 46 + static const char *fw_upload_error(struct device *dev, 47 + enum fw_upload_err err_code) 48 + { 49 + const char *error = "unknown-error"; 50 + 51 + if (err_code < FW_UPLOAD_ERR_MAX) 52 + error = fw_upload_err_str[err_code]; 53 + else 54 + dev_err(dev, "Invalid error code during secure update: %d\n", 55 + err_code); 56 + 57 + return error; 58 + } 59 + 60 + static ssize_t 61 + status_show(struct device *dev, struct device_attribute *attr, char *buf) 62 + { 63 + struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv; 64 + 65 + return sysfs_emit(buf, "%s\n", fw_upload_progress(dev, fwlp->progress)); 66 + } 67 + DEVICE_ATTR_RO(status); 68 + 69 + static ssize_t 70 + error_show(struct device *dev, struct device_attribute *attr, char *buf) 71 + { 72 + struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv; 73 + int ret; 74 + 75 + mutex_lock(&fwlp->lock); 76 + 77 + if (fwlp->progress != FW_UPLOAD_PROG_IDLE) 78 + ret = -EBUSY; 79 + else if (!fwlp->err_code) 80 + ret = 0; 81 + else 82 + ret = sysfs_emit(buf, "%s:%s\n", 83 + fw_upload_progress(dev, fwlp->err_progress), 84 + fw_upload_error(dev, fwlp->err_code)); 85 + 86 + mutex_unlock(&fwlp->lock); 87 + 88 + return ret; 89 + } 90 + DEVICE_ATTR_RO(error); 91 + 92 + static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, 93 + const char *buf, size_t count) 94 + { 95 + struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv; 96 + int ret = count; 97 + bool cancel; 98 + 99 + if (kstrtobool(buf, &cancel) || !cancel) 100 + return -EINVAL; 101 + 102 + mutex_lock(&fwlp->lock); 103 + if (fwlp->progress == FW_UPLOAD_PROG_IDLE) 104 + ret = -ENODEV; 105 + 106 + fwlp->ops->cancel(fwlp->fw_upload); 107 + mutex_unlock(&fwlp->lock); 108 + 109 + return ret; 110 + } 111 + DEVICE_ATTR_WO(cancel); 112 + 113 + static ssize_t remaining_size_show(struct device *dev, 114 + struct device_attribute *attr, char *buf) 115 + { 116 + struct fw_upload_priv *fwlp = to_fw_sysfs(dev)->fw_upload_priv; 117 + 118 + return sysfs_emit(buf, "%u\n", fwlp->remaining_size); 119 + } 120 + DEVICE_ATTR_RO(remaining_size); 121 + 122 + umode_t 123 + fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n) 124 + { 125 + static struct fw_sysfs *fw_sysfs; 126 + 127 + fw_sysfs = to_fw_sysfs(kobj_to_dev(kobj)); 128 + 129 + if (fw_sysfs->fw_upload_priv || attr == &dev_attr_loading.attr) 130 + return attr->mode; 131 + 132 + return 0; 133 + } 134 + 14 135 static void fw_upload_update_progress(struct fw_upload_priv *fwlp, 15 136 enum fw_upload_prog new_progress) 16 137 {
+5
drivers/base/firmware_loader/sysfs_upload.h
··· 37 37 }; 38 38 39 39 #ifdef CONFIG_FW_UPLOAD 40 + extern struct device_attribute dev_attr_status; 41 + extern struct device_attribute dev_attr_error; 42 + extern struct device_attribute dev_attr_cancel; 43 + extern struct device_attribute dev_attr_remaining_size; 44 + 40 45 int fw_upload_start(struct fw_sysfs *fw_sysfs); 41 46 umode_t fw_upload_is_visible(struct kobject *kobj, struct attribute *attr, int n); 42 47 #else