From: Rosen Penev <rosenp@gmail.com>
To: "Jonathan Corbet" <corbet@lwn.net>,
"Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
"Stuart Hayes" <stuart.w.hayes@gmail.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
platform-driver-x86@vger.kernel.org
Subject: [PATCH 1/2] platform/x86: dell_rbu: Use the firmware upload API
Date: Wed, 11 Nov 2026 13:48:25 -0800 [thread overview]
Message-ID: <20261111214826.6577-2-rosenp@gmail.com> (raw)
In-Reply-To: <20261111214826.6577-1-rosenp@gmail.com>
dell_rbu receives the BIOS image through the legacy firmware sysfs
fallback: writing "init" to image_type calls request_firmware_nowait()
with FW_ACTION_NOUEVENT, and userspace feeds the image through
/sys/class/firmware/dell_rbu/{loading,data}. This is why DELL_RBU
selects FW_LOADER_USER_HELPER, an option most distributions disable.
On such kernels the driver cannot be enabled at all.
The firmware upload API (FW_UPLOAD) was added for this use case and
provides the same /sys/class/firmware/<name>/{loading,data} interface
without the user helper. Register a firmware upload device at module init
and move the work done in callbackfn_rbu() into the prepare() op,
which receives the whole image. The BIOS picks the image up from memory
on the next reboot, so write(), poll_complete() and cancel() have
nothing to do.
The upload entries now persist for the lifetime of the module, so
the "init" image_type workaround for recreating them and the
entry_created tracking are no longer needed. "init" is still accepted
and frees any downloaded image, as documented. Allocation or
packetizing failures are now reported via the error attribute instead
of being silently dropped.
The upload device is registered from module init and unregistered from
module exit, so pass a NULL module to firmware_upload_register():
otherwise the reference it takes would pin dell_rbu and make it
impossible to unload. firmware_upload_unregister() cancels and flushes
any in-flight upload before returning.
The userspace sequence documented in dell_rbu.rst (echo 1 > loading,
cp image > data, echo 0 > loading) is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
Documentation/admin-guide/dell_rbu.rst | 25 +++---
drivers/platform/x86/dell/Kconfig | 2 +-
drivers/platform/x86/dell/dell_rbu.c | 107 ++++++++++++++++---------
3 files changed, 81 insertions(+), 53 deletions(-)
diff --git a/Documentation/admin-guide/dell_rbu.rst b/Documentation/admin-guide/dell_rbu.rst
index 2196caf1b939..d33927b0d52d 100644
--- a/Documentation/admin-guide/dell_rbu.rst
+++ b/Documentation/admin-guide/dell_rbu.rst
@@ -47,6 +47,10 @@ The driver load creates the following directories under the /sys file system::
/sys/class/firmware/dell_rbu/loading
/sys/class/firmware/dell_rbu/data
+ /sys/class/firmware/dell_rbu/status
+ /sys/class/firmware/dell_rbu/error
+ /sys/class/firmware/dell_rbu/remaining_size
+ /sys/class/firmware/dell_rbu/cancel
/sys/devices/platform/dell_rbu/image_type
/sys/devices/platform/dell_rbu/data
/sys/devices/platform/dell_rbu/packet_size
@@ -95,26 +99,21 @@ Do the steps below to download the BIOS image.
2) cp bios_image.hdr /sys/class/firmware/dell_rbu/data
3) echo 0 > /sys/class/firmware/dell_rbu/loading
-The /sys/class/firmware/dell_rbu/ entries will remain till the following is
-done.
-
-::
+The /sys/class/firmware/dell_rbu/ entries are provided by the firmware upload
+framework (see Documentation/driver-api/firmware/fw_upload.rst) and remain
+for as long as the driver is loaded. Once step 3 is done,
+/sys/class/firmware/dell_rbu/status returns to "idle" and
+/sys/class/firmware/dell_rbu/error is empty if the image was accepted.
+A download in progress can be aborted with::
echo -1 > /sys/class/firmware/dell_rbu/loading
-Until this step is completed the driver cannot be unloaded.
-
Also echoing either mono, packet or init in to image_type will free up the
memory allocated by the driver.
-If a user by accident executes steps 1 and 3 above without executing step 2;
-it will make the /sys/class/firmware/dell_rbu/ entries disappear.
-
-The entries can be recreated by doing the following::
-
- echo init > /sys/devices/platform/dell_rbu/image_type
-
.. note:: echoing init in image_type does not change its original value.
+ It is kept for compatibility; the /sys/class/firmware/dell_rbu/ entries
+ no longer need to be recreated.
Also the driver provides /sys/devices/platform/dell_rbu/data readonly file to
read back the image downloaded.
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index f6acf1cc2a2f..1672ed04a36a 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -89,7 +89,7 @@ config DELL_RBU
default m
depends on X86
select FW_LOADER
- select FW_LOADER_USER_HELPER
+ select FW_UPLOAD
help
Say m if you want to have the option of updating the BIOS for your
DELL system. Note you need a Dell OpenManage or Dell Update package (DUP)
diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c
index 768b15f406d3..a121f59a215d 100644
--- a/drivers/platform/x86/dell/dell_rbu.c
+++ b/drivers/platform/x86/dell/dell_rbu.c
@@ -60,12 +60,11 @@ static struct _rbu_data {
unsigned long num_packets;
unsigned long packetsize;
unsigned long imagesize;
- int entry_created;
} rbu_data;
static char image_type[MAX_IMAGE_LENGTH + 1] = "mono";
module_param_string(image_type, image_type, sizeof (image_type), 0);
-MODULE_PARM_DESC(image_type, "BIOS image type. choose- mono or packet or init");
+MODULE_PARM_DESC(image_type, "BIOS image type. choose- mono or packet");
static unsigned long allocation_floor = 0x100000;
module_param(allocation_floor, ulong, 0644);
@@ -81,7 +80,7 @@ struct packet_data {
static struct list_head packet_data_list;
static struct platform_device *rbu_device;
-static int context;
+static struct fw_upload *rbu_fw_upload;
static void init_packet_head(void)
{
@@ -497,41 +496,72 @@ static ssize_t data_read(struct file *filp, struct kobject *kobj,
}
static const BIN_ATTR_RO(data, 0);
-static void callbackfn_rbu(const struct firmware *fw, void *context)
+/*
+ * The whole image is handed to prepare() by the firmware upload core, so
+ * all of the work happens here. The BIOS consumes the image from memory on
+ * the next reboot; there is nothing to stream to hardware afterwards.
+ */
+static enum fw_upload_err rbu_fw_prepare(struct fw_upload *fw_upload,
+ const u8 *data, u32 size)
{
- rbu_data.entry_created = 0;
-
- if (!fw)
- return;
-
- if (!fw->size)
- goto out;
+ enum fw_upload_err ret = FW_UPLOAD_ERR_NONE;
spin_lock(&rbu_data.lock);
if (!strcmp(image_type, "mono")) {
- if (!img_update_realloc(fw->size))
- memcpy(rbu_data.image_update_buffer,
- fw->data, fw->size);
+ if (img_update_realloc(size))
+ ret = FW_UPLOAD_ERR_RW_ERROR;
+ else
+ memcpy(rbu_data.image_update_buffer, data, size);
} else if (!strcmp(image_type, "packet")) {
/*
* we need to free previous packets if a
* new hunk of packets needs to be downloaded
*/
packet_empty_list();
- if (packetize_data(fw->data, fw->size))
+ if (packetize_data(data, size)) {
/* Incase something goes wrong when we are
* in middle of packetizing the data, we
* need to free up whatever packets might
* have been created before we quit.
*/
packet_empty_list();
- } else
+ ret = FW_UPLOAD_ERR_RW_ERROR;
+ }
+ } else {
pr_debug("invalid image type specified\n");
+ ret = FW_UPLOAD_ERR_FW_INVALID;
+ }
spin_unlock(&rbu_data.lock);
- out:
- release_firmware(fw);
+
+ return ret;
+}
+
+static enum fw_upload_err rbu_fw_write(struct fw_upload *fw_upload,
+ const u8 *data, u32 offset,
+ u32 size, u32 *written)
+{
+ /* Already consumed in rbu_fw_prepare() */
+ *written = size;
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static enum fw_upload_err rbu_fw_poll_complete(struct fw_upload *fw_upload)
+{
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void rbu_fw_cancel(struct fw_upload *fw_upload)
+{
+ /* Nothing in flight to abort; the image only lives in memory. */
}
+static const struct fw_upload_ops rbu_fw_ops = {
+ .prepare = rbu_fw_prepare,
+ .write = rbu_fw_write,
+ .poll_complete = rbu_fw_poll_complete,
+ .cancel = rbu_fw_cancel,
+};
+
static ssize_t image_type_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
@@ -547,7 +577,6 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj,
char *buffer, loff_t pos, size_t count)
{
int rc = count;
- int req_firm_rc = 0;
int i;
spin_lock(&rbu_data.lock);
/*
@@ -567,26 +596,10 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj,
strscpy(image_type, "packet");
else if (strstr(buffer, "init")) {
/*
- * If due to the user error the driver gets in a bad
- * state where even though it is loaded , the
- * /sys/class/firmware/dell_rbu entries are missing.
- * to cover this situation the user can recreate entries
- * by writing init to image_type.
+ * Historically this recreated the /sys/class/firmware/dell_rbu
+ * entries. They are now persistent, so "init" only frees
+ * any previously downloaded image, as before.
*/
- if (!rbu_data.entry_created) {
- spin_unlock(&rbu_data.lock);
- req_firm_rc = request_firmware_nowait(THIS_MODULE,
- FW_ACTION_NOUEVENT, "dell_rbu",
- &rbu_device->dev, GFP_KERNEL, &context,
- callbackfn_rbu);
- if (req_firm_rc) {
- pr_err("request_firmware_nowait failed %d\n", rc);
- rc = -EIO;
- } else
- rbu_data.entry_created = 1;
-
- spin_lock(&rbu_data.lock);
- }
} else {
pr_warn("image_type is invalid\n");
spin_unlock(&rbu_data.lock);
@@ -661,9 +674,24 @@ static int __init dcdrbu_init(void)
if (rc)
goto out_devreg;
- rbu_data.entry_created = 0;
+ /*
+ * Registered from module init and unregistered from module exit, so
+ * don't let the upload device pin this module: that would make it
+ * impossible to unload. firmware_upload_unregister() cancels and
+ * flushes any in-flight upload before returning.
+ */
+ rbu_fw_upload = firmware_upload_register(NULL, &rbu_device->dev,
+ "dell_rbu", &rbu_fw_ops, NULL);
+ if (IS_ERR(rbu_fw_upload)) {
+ rc = PTR_ERR(rbu_fw_upload);
+ pr_err("firmware_upload_register failed %d\n", rc);
+ goto out_group;
+ }
+
return 0;
+out_group:
+ sysfs_remove_group(&rbu_device->dev.kobj, &rbu_group);
out_devreg:
platform_device_unregister(rbu_device);
return rc;
@@ -671,6 +699,7 @@ static int __init dcdrbu_init(void)
static __exit void dcdrbu_exit(void)
{
+ firmware_upload_unregister(rbu_fw_upload);
spin_lock(&rbu_data.lock);
packet_empty_list();
img_update_free();
--
2.56.1
next prev parent reply other threads:[~2026-11-11 21:48 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 21:48 [PATCH 0/2] platform/x86: dell_rbu: make usable Rosen Penev
2026-11-11 21:48 ` Rosen Penev [this message]
2026-11-11 21:48 ` [PATCH 2/2] platform/x86: dell_rbu: Allocate large monolithic images contiguously Rosen Penev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111214826.6577-2-rosenp@gmail.com \
--to=rosenp@gmail.com \
--cc=corbet@lwn.net \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=platform-driver-x86@vger.kernel.org \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
--cc=stuart.w.hayes@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®