[PATCH RFC 05/22] powerpc/rtas: let procfs copy in writes

From: Christian Brauner

Date: Mon Nov 02 2026 - 10:41:51 EST


Implement ->proc_write_buf() for the files in /proc/powerpc/rtas and
/proc/ppc64/rtas so procfs copies in what is written before it pins the
entry, and parse what was written from that copy.

firmware_flash, firmware_update and validate_flash take an image in 4k
pieces and relied on writes coming up short. They keep doing that with
PROC_ENTRY_SHORT_WRITES. Writes to the other files that are larger than
PAGE_SIZE - 1 bytes fail with -EINVAL now.

validate_flash only keeps the first 4k of the image and used to swallow
whatever was written beyond that in one go. It takes at most 4k per
write now, like for the part it keeps, so a writer has to loop there
like it has to for the image itself.

Signed-off-by: Christian Brauner (Amutable) <brauner@xxxxxxxxxx>
---
arch/powerpc/kernel/rtas-proc.c | 40 ++++++++++++++++++----------------------
arch/powerpc/kernel/rtas_flash.c | 38 +++++++++++++++++---------------------
2 files changed, 35 insertions(+), 43 deletions(-)

diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index ebf416840b3c..9598866f11cb 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -138,45 +138,45 @@ static unsigned long rtas_tone_volume = 0;
static int ppc_rtas_sensors_show(struct seq_file *m, void *v);
static int ppc_rtas_clock_show(struct seq_file *m, void *v);
static ssize_t ppc_rtas_clock_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos);
+ char *buf, size_t count, loff_t *ppos);
static int ppc_rtas_progress_show(struct seq_file *m, void *v);
static ssize_t ppc_rtas_progress_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos);
+ char *buf, size_t count, loff_t *ppos);
static int ppc_rtas_poweron_show(struct seq_file *m, void *v);
static ssize_t ppc_rtas_poweron_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos);
+ char *buf, size_t count, loff_t *ppos);

static ssize_t ppc_rtas_tone_freq_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos);
+ char *buf, size_t count, loff_t *ppos);
static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v);
static ssize_t ppc_rtas_tone_volume_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos);
+ char *buf, size_t count, loff_t *ppos);
static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v);
static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v);

static const struct proc_ops ppc_rtas_poweron_proc_ops = {
.proc_show = ppc_rtas_poweron_show,
- .proc_write = ppc_rtas_poweron_write,
+ .proc_write_buf = ppc_rtas_poweron_write,
};

static const struct proc_ops ppc_rtas_progress_proc_ops = {
.proc_show = ppc_rtas_progress_show,
- .proc_write = ppc_rtas_progress_write,
+ .proc_write_buf = ppc_rtas_progress_write,
};

static const struct proc_ops ppc_rtas_clock_proc_ops = {
.proc_show = ppc_rtas_clock_show,
- .proc_write = ppc_rtas_clock_write,
+ .proc_write_buf = ppc_rtas_clock_write,
};

static const struct proc_ops ppc_rtas_tone_freq_proc_ops = {
.proc_show = ppc_rtas_tone_freq_show,
- .proc_write = ppc_rtas_tone_freq_write,
+ .proc_write_buf = ppc_rtas_tone_freq_write,
};

static const struct proc_ops ppc_rtas_tone_volume_proc_ops = {
.proc_show = ppc_rtas_tone_volume_show,
- .proc_write = ppc_rtas_tone_volume_write,
+ .proc_write_buf = ppc_rtas_tone_volume_write,
};

static int ppc_rtas_find_all_sensors(void);
@@ -216,16 +216,14 @@ static int __init proc_rtas_init(void)

__initcall(proc_rtas_init);

-static int parse_number(const char __user *p, size_t count, u64 *val)
+static int parse_number(const char *p, size_t count, u64 *val)
{
char buf[40];

if (count > 39)
return -EINVAL;

- if (copy_from_user(buf, p, count))
- return -EFAULT;
-
+ memcpy(buf, p, count);
buf[count] = 0;

return kstrtoull(buf, 10, val);
@@ -235,7 +233,7 @@ static int parse_number(const char __user *p, size_t count, u64 *val)
/* POWER-ON-TIME */
/* ****************************************************************** */
static ssize_t ppc_rtas_poweron_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+ char *buf, size_t count, loff_t *ppos)
{
struct rtc_time tm;
time64_t nowtime;
@@ -269,15 +267,13 @@ static int ppc_rtas_poweron_show(struct seq_file *m, void *v)
/* PROGRESS */
/* ****************************************************************** */
static ssize_t ppc_rtas_progress_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+ char *buf, size_t count, loff_t *ppos)
{
unsigned long hex;

if (count >= MAX_LINELENGTH)
count = MAX_LINELENGTH -1;
- if (copy_from_user(progress_led, buf, count)) { /* save the string */
- return -EFAULT;
- }
+ memcpy(progress_led, buf, count); /* save the string */
progress_led[count] = 0;

/* Lets see if the user passed hexdigits */
@@ -301,7 +297,7 @@ static int ppc_rtas_progress_show(struct seq_file *m, void *v)
/* CLOCK */
/* ****************************************************************** */
static ssize_t ppc_rtas_clock_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+ char *buf, size_t count, loff_t *ppos)
{
struct rtc_time tm;
time64_t nowtime;
@@ -660,7 +656,7 @@ static void get_location_code(struct seq_file *m, struct individual_sensor *s,
/* INDICATORS - Tone Frequency */
/* ****************************************************************** */
static ssize_t ppc_rtas_tone_freq_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+ char *buf, size_t count, loff_t *ppos)
{
u64 freq;
int error = parse_number(buf, count, &freq);
@@ -685,7 +681,7 @@ static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v)
/* INDICATORS - Tone Volume */
/* ****************************************************************** */
static ssize_t ppc_rtas_tone_volume_write(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos)
+ char *buf, size_t count, loff_t *ppos)
{
u64 volume;
int error = parse_number(buf, count, &volume);
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index f1cd105f7426..77a4627f2507 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -307,7 +307,7 @@ static ssize_t rtas_flash_read_num(struct file *file, char __user *buf,
* count is. If the system is low on memory it will be just as well
* that we fail....
*/
-static ssize_t rtas_flash_write(struct file *file, const char __user *buffer,
+static ssize_t rtas_flash_write(struct file *file, char *buffer,
size_t count, loff_t *off)
{
struct rtas_update_flash_t *const uf = &rtas_update_flash_data;
@@ -343,16 +343,11 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer,
next_free = 0;
}

- if (count > RTAS_BLK_SIZE)
- count = RTAS_BLK_SIZE;
p = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL);
if (!p)
return -ENOMEM;

- if(copy_from_user(p, buffer, count)) {
- kmem_cache_free(flash_block_cache, p);
- return -EFAULT;
- }
+ memcpy(p, buffer, count);
fl->blocks[next_free].data = p;
fl->blocks[next_free].length = count;
fl->num_blocks++;
@@ -390,7 +385,7 @@ static ssize_t manage_flash_read(struct file *file, char __user *buf,
return simple_read_from_buffer(buf, count, ppos, msg, msglen);
}

-static ssize_t manage_flash_write(struct file *file, const char __user *buf,
+static ssize_t manage_flash_write(struct file *file, char *buf,
size_t count, loff_t *off)
{
struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data;
@@ -401,9 +396,8 @@ static ssize_t manage_flash_write(struct file *file, const char __user *buf,

if ((args_buf->status == MANAGE_AUTH) || (count == 0))
return count;
-
- if (get_user(cmd, buf))
- return -EFAULT;
+
+ cmd = buf[0];

if (cmd == '0')
op = RTAS_REJECT_TMP_IMG;
@@ -471,7 +465,7 @@ static ssize_t validate_flash_read(struct file *file, char __user *buf,
return simple_read_from_buffer(buf, count, ppos, msg, msglen);
}

-static ssize_t validate_flash_write(struct file *file, const char __user *buf,
+static ssize_t validate_flash_write(struct file *file, char *buf,
size_t count, loff_t *off)
{
struct rtas_validate_flash_t *const args_buf =
@@ -494,11 +488,7 @@ static ssize_t validate_flash_write(struct file *file, const char __user *buf,
args_buf->status = VALIDATE_INCOMPLETE;
}

- if (!access_ok(buf, count))
- return -EFAULT;
-
- if (copy_from_user(args_buf->buf + *off, buf, count))
- return -EFAULT;
+ memcpy(args_buf->buf + *off, buf, count);

*off += count;
return count;
@@ -632,8 +622,10 @@ static const struct rtas_flash_file rtas_flash_files[] = {
.filename = "powerpc/rtas/" FIRMWARE_FLASH_NAME,
.handle = RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
.status = &rtas_update_flash_data.status,
+ .ops.proc_flags = PROC_ENTRY_SHORT_WRITES,
.ops.proc_read = rtas_flash_read_msg,
- .ops.proc_write = rtas_flash_write,
+ .ops.proc_write_buf = rtas_flash_write,
+ .ops.proc_write_size = RTAS_BLK_SIZE,
.ops.proc_release = rtas_flash_release,
.ops.proc_lseek = default_llseek,
},
@@ -641,8 +633,10 @@ static const struct rtas_flash_file rtas_flash_files[] = {
.filename = "powerpc/rtas/" FIRMWARE_UPDATE_NAME,
.handle = RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
.status = &rtas_update_flash_data.status,
+ .ops.proc_flags = PROC_ENTRY_SHORT_WRITES,
.ops.proc_read = rtas_flash_read_num,
- .ops.proc_write = rtas_flash_write,
+ .ops.proc_write_buf = rtas_flash_write,
+ .ops.proc_write_size = RTAS_BLK_SIZE,
.ops.proc_release = rtas_flash_release,
.ops.proc_lseek = default_llseek,
},
@@ -650,8 +644,10 @@ static const struct rtas_flash_file rtas_flash_files[] = {
.filename = "powerpc/rtas/" VALIDATE_FLASH_NAME,
.handle = RTAS_FN_IBM_VALIDATE_FLASH_IMAGE,
.status = &rtas_validate_flash_data.status,
+ .ops.proc_flags = PROC_ENTRY_SHORT_WRITES,
.ops.proc_read = validate_flash_read,
- .ops.proc_write = validate_flash_write,
+ .ops.proc_write_buf = validate_flash_write,
+ .ops.proc_write_size = VALIDATE_BUF_SIZE,
.ops.proc_release = validate_flash_release,
.ops.proc_lseek = default_llseek,
},
@@ -660,7 +656,7 @@ static const struct rtas_flash_file rtas_flash_files[] = {
.handle = RTAS_FN_IBM_MANAGE_FLASH_IMAGE,
.status = &rtas_manage_flash_data.status,
.ops.proc_read = manage_flash_read,
- .ops.proc_write = manage_flash_write,
+ .ops.proc_write_buf = manage_flash_write,
.ops.proc_lseek = default_llseek,
}
};

--
2.53.0