Skip to content

Commit 905b9e4

Browse files
nathanlynchmpe
authored andcommitted
powerpc/pseries/papr-sysparm: Expose character device to user space
Until now the papr_sysparm APIs have been kernel-internal. But user space needs access to PAPR system parameters too. The only method available to user space today to get or set system parameters is using sys_rtas() and /dev/mem to pass RTAS-addressable buffers between user space and firmware. This is incompatible with lockdown and should be deprecated. So provide an alternative ABI to user space in the form of a /dev/papr-sysparm character device with just two ioctl commands (get and set). The data payloads involved are small enough to fit in the ioctl argument buffer, making the code relatively simple. Exposing the system parameters through sysfs has been considered but it would be too awkward: * The kernel currently does not have to contain an exhaustive list of defined system parameters. This is a convenient property to maintain because we don't have to update the kernel whenever a new parameter is added to PAPR. Exporting a named attribute in sysfs for each parameter would negate this. * Some system parameters are text-based and some are not. * Retrieval of at least one system parameter requires input data, which a simple read-oriented interface can't support. Signed-off-by: Nathan Lynch <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://msgid.link/20231212-papr-sys_rtas-vs-lockdown-v6-11-e9eafd0c8c6c@linux.ibm.com
1 parent 35aae18 commit 905b9e4

File tree

4 files changed

+227
-8
lines changed

4 files changed

+227
-8
lines changed

Documentation/userspace-api/ioctl/ioctl-number.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ Code Seq# Include File Comments
351351
352352
0xB2 00 arch/powerpc/include/uapi/asm/papr-vpd.h powerpc/pseries VPD API
353353
<mailto:linuxppc-dev>
354+
0xB2 01-02 arch/powerpc/include/uapi/asm/papr-sysparm.h powerpc/pseries system parameter API
355+
<mailto:linuxppc-dev>
354356
0xB3 00 linux/mmc/ioctl.h
355357
0xB4 00-0F linux/gpio.h <mailto:[email protected]>
356358
0xB5 00-0F uapi/linux/rpmsg.h <mailto:[email protected]>

arch/powerpc/include/asm/papr-sysparm.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
#ifndef _ASM_POWERPC_PAPR_SYSPARM_H
33
#define _ASM_POWERPC_PAPR_SYSPARM_H
44

5+
#include <uapi/asm/papr-sysparm.h>
6+
57
typedef struct {
6-
const u32 token;
8+
u32 token;
79
} papr_sysparm_t;
810

911
#define mk_papr_sysparm(x_) ((papr_sysparm_t){ .token = x_, })
@@ -20,11 +22,14 @@ typedef struct {
2022
#define PAPR_SYSPARM_TLB_BLOCK_INVALIDATE_ATTRS mk_papr_sysparm(50)
2123
#define PAPR_SYSPARM_LPAR_NAME mk_papr_sysparm(55)
2224

23-
enum {
24-
PAPR_SYSPARM_MAX_INPUT = 1024,
25-
PAPR_SYSPARM_MAX_OUTPUT = 4000,
26-
};
27-
25+
/**
26+
* struct papr_sysparm_buf - RTAS work area layout for system parameter functions.
27+
*
28+
* This is the memory layout of the buffers passed to/from
29+
* ibm,get-system-parameter and ibm,set-system-parameter. It is
30+
* distinct from the papr_sysparm_io_block structure that is passed
31+
* between user space and the kernel.
32+
*/
2833
struct papr_sysparm_buf {
2934
__be16 len;
3035
char val[PAPR_SYSPARM_MAX_OUTPUT];
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+
#ifndef _UAPI_PAPR_SYSPARM_H_
3+
#define _UAPI_PAPR_SYSPARM_H_
4+
5+
#include <linux/types.h>
6+
#include <asm/ioctl.h>
7+
#include <asm/papr-miscdev.h>
8+
9+
enum {
10+
PAPR_SYSPARM_MAX_INPUT = 1024,
11+
PAPR_SYSPARM_MAX_OUTPUT = 4000,
12+
};
13+
14+
struct papr_sysparm_io_block {
15+
__u32 parameter;
16+
__u16 length;
17+
char data[PAPR_SYSPARM_MAX_OUTPUT];
18+
};
19+
20+
/**
21+
* PAPR_SYSPARM_IOC_GET - Retrieve the value of a PAPR system parameter.
22+
*
23+
* Uses _IOWR because of one corner case: Retrieving the value of the
24+
* "OS Service Entitlement Status" parameter (60) requires the caller
25+
* to supply input data (a date string) in the buffer passed to
26+
* firmware. So the @length and @data of the incoming
27+
* papr_sysparm_io_block are always used to initialize the work area
28+
* supplied to ibm,get-system-parameter. No other parameters are known
29+
* to parameterize the result this way, and callers are encouraged
30+
* (but not required) to zero-initialize @length and @data in the
31+
* common case.
32+
*
33+
* On error the contents of the ioblock are indeterminate.
34+
*
35+
* Return:
36+
* 0: Success; @length is the length of valid data in @data, not to exceed @PAPR_SYSPARM_MAX_OUTPUT.
37+
* -EIO: Platform error. (-1)
38+
* -EINVAL: Incorrect data length or format. (-9999)
39+
* -EPERM: The calling partition is not allowed to access this parameter. (-9002)
40+
* -EOPNOTSUPP: Parameter not supported on this platform (-3)
41+
*/
42+
#define PAPR_SYSPARM_IOC_GET _IOWR(PAPR_MISCDEV_IOC_ID, 1, struct papr_sysparm_io_block)
43+
44+
/**
45+
* PAPR_SYSPARM_IOC_SET - Update the value of a PAPR system parameter.
46+
*
47+
* The contents of the ioblock are unchanged regardless of success.
48+
*
49+
* Return:
50+
* 0: Success; the parameter has been updated.
51+
* -EIO: Platform error. (-1)
52+
* -EINVAL: Incorrect data length or format. (-9999)
53+
* -EPERM: The calling partition is not allowed to access this parameter. (-9002)
54+
* -EOPNOTSUPP: Parameter not supported on this platform (-3)
55+
*/
56+
#define PAPR_SYSPARM_IOC_SET _IOW(PAPR_MISCDEV_IOC_ID, 2, struct papr_sysparm_io_block)
57+
58+
#endif /* _UAPI_PAPR_SYSPARM_H_ */

arch/powerpc/platforms/pseries/papr-sysparm.c

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@
22

33
#define pr_fmt(fmt) "papr-sysparm: " fmt
44

5+
#include <linux/anon_inodes.h>
56
#include <linux/bug.h>
7+
#include <linux/file.h>
8+
#include <linux/fs.h>
69
#include <linux/init.h>
710
#include <linux/kernel.h>
11+
#include <linux/miscdevice.h>
812
#include <linux/printk.h>
913
#include <linux/slab.h>
10-
#include <asm/rtas.h>
14+
#include <linux/uaccess.h>
15+
#include <asm/machdep.h>
1116
#include <asm/papr-sysparm.h>
1217
#include <asm/rtas-work-area.h>
18+
#include <asm/rtas.h>
1319

1420
struct papr_sysparm_buf *papr_sysparm_buf_alloc(void)
1521
{
@@ -87,7 +93,6 @@ static bool papr_sysparm_buf_can_submit(const struct papr_sysparm_buf *buf)
8793
*
8894
* Return: 0 on success, -errno otherwise. @buf is unmodified on error.
8995
*/
90-
9196
int papr_sysparm_get(papr_sysparm_t param, struct papr_sysparm_buf *buf)
9297
{
9398
const s32 token = rtas_function_token(RTAS_FN_IBM_GET_SYSTEM_PARAMETER);
@@ -196,3 +201,152 @@ int papr_sysparm_set(papr_sysparm_t param, const struct papr_sysparm_buf *buf)
196201

197202
return ret;
198203
}
204+
205+
static struct papr_sysparm_buf *
206+
papr_sysparm_buf_from_user(const struct papr_sysparm_io_block __user *user_iob)
207+
{
208+
struct papr_sysparm_buf *kern_spbuf;
209+
long err;
210+
u16 len;
211+
212+
/*
213+
* The length of valid data that userspace claims to be in
214+
* user_iob->data[].
215+
*/
216+
if (get_user(len, &user_iob->length))
217+
return ERR_PTR(-EFAULT);
218+
219+
static_assert(sizeof(user_iob->data) >= PAPR_SYSPARM_MAX_INPUT);
220+
static_assert(sizeof(kern_spbuf->val) >= PAPR_SYSPARM_MAX_INPUT);
221+
222+
if (len > PAPR_SYSPARM_MAX_INPUT)
223+
return ERR_PTR(-EINVAL);
224+
225+
kern_spbuf = papr_sysparm_buf_alloc();
226+
if (!kern_spbuf)
227+
return ERR_PTR(-ENOMEM);
228+
229+
papr_sysparm_buf_set_length(kern_spbuf, len);
230+
231+
if (len > 0 && copy_from_user(kern_spbuf->val, user_iob->data, len)) {
232+
err = -EFAULT;
233+
goto free_sysparm_buf;
234+
}
235+
236+
return kern_spbuf;
237+
238+
free_sysparm_buf:
239+
papr_sysparm_buf_free(kern_spbuf);
240+
return ERR_PTR(err);
241+
}
242+
243+
static int papr_sysparm_buf_to_user(const struct papr_sysparm_buf *kern_spbuf,
244+
struct papr_sysparm_io_block __user *user_iob)
245+
{
246+
u16 len_out = papr_sysparm_buf_get_length(kern_spbuf);
247+
248+
if (put_user(len_out, &user_iob->length))
249+
return -EFAULT;
250+
251+
static_assert(sizeof(user_iob->data) >= PAPR_SYSPARM_MAX_OUTPUT);
252+
static_assert(sizeof(kern_spbuf->val) >= PAPR_SYSPARM_MAX_OUTPUT);
253+
254+
if (copy_to_user(user_iob->data, kern_spbuf->val, PAPR_SYSPARM_MAX_OUTPUT))
255+
return -EFAULT;
256+
257+
return 0;
258+
}
259+
260+
static long papr_sysparm_ioctl_get(struct papr_sysparm_io_block __user *user_iob)
261+
{
262+
struct papr_sysparm_buf *kern_spbuf;
263+
papr_sysparm_t param;
264+
long ret;
265+
266+
if (get_user(param.token, &user_iob->parameter))
267+
return -EFAULT;
268+
269+
kern_spbuf = papr_sysparm_buf_from_user(user_iob);
270+
if (IS_ERR(kern_spbuf))
271+
return PTR_ERR(kern_spbuf);
272+
273+
ret = papr_sysparm_get(param, kern_spbuf);
274+
if (ret)
275+
goto free_sysparm_buf;
276+
277+
ret = papr_sysparm_buf_to_user(kern_spbuf, user_iob);
278+
if (ret)
279+
goto free_sysparm_buf;
280+
281+
ret = 0;
282+
283+
free_sysparm_buf:
284+
papr_sysparm_buf_free(kern_spbuf);
285+
return ret;
286+
}
287+
288+
289+
static long papr_sysparm_ioctl_set(struct papr_sysparm_io_block __user *user_iob)
290+
{
291+
struct papr_sysparm_buf *kern_spbuf;
292+
papr_sysparm_t param;
293+
long ret;
294+
295+
if (get_user(param.token, &user_iob->parameter))
296+
return -EFAULT;
297+
298+
kern_spbuf = papr_sysparm_buf_from_user(user_iob);
299+
if (IS_ERR(kern_spbuf))
300+
return PTR_ERR(kern_spbuf);
301+
302+
ret = papr_sysparm_set(param, kern_spbuf);
303+
if (ret)
304+
goto free_sysparm_buf;
305+
306+
ret = 0;
307+
308+
free_sysparm_buf:
309+
papr_sysparm_buf_free(kern_spbuf);
310+
return ret;
311+
}
312+
313+
static long papr_sysparm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
314+
{
315+
void __user *argp = (__force void __user *)arg;
316+
long ret;
317+
318+
switch (ioctl) {
319+
case PAPR_SYSPARM_IOC_GET:
320+
ret = papr_sysparm_ioctl_get(argp);
321+
break;
322+
case PAPR_SYSPARM_IOC_SET:
323+
if (filp->f_mode & FMODE_WRITE)
324+
ret = papr_sysparm_ioctl_set(argp);
325+
else
326+
ret = -EBADF;
327+
break;
328+
default:
329+
ret = -ENOIOCTLCMD;
330+
break;
331+
}
332+
return ret;
333+
}
334+
335+
static const struct file_operations papr_sysparm_ops = {
336+
.unlocked_ioctl = papr_sysparm_ioctl,
337+
};
338+
339+
static struct miscdevice papr_sysparm_dev = {
340+
.minor = MISC_DYNAMIC_MINOR,
341+
.name = "papr-sysparm",
342+
.fops = &papr_sysparm_ops,
343+
};
344+
345+
static __init int papr_sysparm_init(void)
346+
{
347+
if (!rtas_function_implemented(RTAS_FN_IBM_GET_SYSTEM_PARAMETER))
348+
return -ENODEV;
349+
350+
return misc_register(&papr_sysparm_dev);
351+
}
352+
machine_device_initcall(pseries, papr_sysparm_init);

0 commit comments

Comments
 (0)