Skip to content

Commit 840cfb7

Browse files
committed
fwctl: FWCTL_RPC to execute a Remote Procedure Call to device firmware
Add the FWCTL_RPC ioctl which allows a request/response RPC call to device firmware. Drivers implementing this call must follow the security guidelines under Documentation/userspace-api/fwctl.rst The core code provides some memory management helpers to get the messages copied from and back to userspace. The driver is responsible for allocating the output message memory and delivering the message to the device. Link: https://patch.msgid.link/r/[email protected] Reviewed-by: Jonathan Cameron <[email protected]> Reviewed-by: Dave Jiang <[email protected]> Reviewed-by: Shannon Nelson <[email protected]> Tested-by: Dave Jiang <[email protected]> Tested-by: Shannon Nelson <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 8eea4e7 commit 840cfb7

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

drivers/fwctl/main.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
#include <linux/container_of.h>
99
#include <linux/fs.h>
1010
#include <linux/module.h>
11+
#include <linux/sizes.h>
1112
#include <linux/slab.h>
1213

1314
#include <uapi/fwctl/fwctl.h>
1415

1516
enum {
1617
FWCTL_MAX_DEVICES = 4096,
18+
MAX_RPC_LEN = SZ_2M,
1719
};
1820
static_assert(FWCTL_MAX_DEVICES < (1U << MINORBITS));
1921

2022
static dev_t fwctl_dev;
2123
static DEFINE_IDA(fwctl_ida);
24+
static unsigned long fwctl_tainted;
2225

2326
struct fwctl_ucmd {
2427
struct fwctl_uctx *uctx;
@@ -80,9 +83,65 @@ static int fwctl_cmd_info(struct fwctl_ucmd *ucmd)
8083
return ucmd_respond(ucmd, sizeof(*cmd));
8184
}
8285

86+
static int fwctl_cmd_rpc(struct fwctl_ucmd *ucmd)
87+
{
88+
struct fwctl_device *fwctl = ucmd->uctx->fwctl;
89+
struct fwctl_rpc *cmd = ucmd->cmd;
90+
size_t out_len;
91+
92+
if (cmd->in_len > MAX_RPC_LEN || cmd->out_len > MAX_RPC_LEN)
93+
return -EMSGSIZE;
94+
95+
switch (cmd->scope) {
96+
case FWCTL_RPC_CONFIGURATION:
97+
case FWCTL_RPC_DEBUG_READ_ONLY:
98+
break;
99+
100+
case FWCTL_RPC_DEBUG_WRITE_FULL:
101+
if (!capable(CAP_SYS_RAWIO))
102+
return -EPERM;
103+
fallthrough;
104+
case FWCTL_RPC_DEBUG_WRITE:
105+
if (!test_and_set_bit(0, &fwctl_tainted)) {
106+
dev_warn(
107+
&fwctl->dev,
108+
"%s(%d): has requested full access to the physical device device",
109+
current->comm, task_pid_nr(current));
110+
add_taint(TAINT_FWCTL, LOCKDEP_STILL_OK);
111+
}
112+
break;
113+
default:
114+
return -EOPNOTSUPP;
115+
}
116+
117+
void *inbuf __free(kvfree) = kvzalloc(cmd->in_len, GFP_KERNEL_ACCOUNT);
118+
if (!inbuf)
119+
return -ENOMEM;
120+
if (copy_from_user(inbuf, u64_to_user_ptr(cmd->in), cmd->in_len))
121+
return -EFAULT;
122+
123+
out_len = cmd->out_len;
124+
void *outbuf __free(kvfree) = fwctl->ops->fw_rpc(
125+
ucmd->uctx, cmd->scope, inbuf, cmd->in_len, &out_len);
126+
if (IS_ERR(outbuf))
127+
return PTR_ERR(outbuf);
128+
if (outbuf == inbuf) {
129+
/* The driver can re-use inbuf as outbuf */
130+
inbuf = NULL;
131+
}
132+
133+
if (copy_to_user(u64_to_user_ptr(cmd->out), outbuf,
134+
min(cmd->out_len, out_len)))
135+
return -EFAULT;
136+
137+
cmd->out_len = out_len;
138+
return ucmd_respond(ucmd, sizeof(*cmd));
139+
}
140+
83141
/* On stack memory for the ioctl structs */
84142
union fwctl_ucmd_buffer {
85143
struct fwctl_info info;
144+
struct fwctl_rpc rpc;
86145
};
87146

88147
struct fwctl_ioctl_op {
@@ -103,6 +162,7 @@ struct fwctl_ioctl_op {
103162
}
104163
static const struct fwctl_ioctl_op fwctl_ioctl_ops[] = {
105164
IOCTL_OP(FWCTL_INFO, fwctl_cmd_info, struct fwctl_info, out_device_data),
165+
IOCTL_OP(FWCTL_RPC, fwctl_cmd_rpc, struct fwctl_rpc, out),
106166
};
107167

108168
static long fwctl_fops_ioctl(struct file *filp, unsigned int cmd,

include/linux/fwctl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ struct fwctl_ops {
4747
* ignore length on input, the core code will handle everything.
4848
*/
4949
void *(*info)(struct fwctl_uctx *uctx, size_t *length);
50+
/**
51+
* @fw_rpc: Implement FWCTL_RPC. Deliver rpc_in/in_len to the FW and
52+
* return the response and set out_len. rpc_in can be returned as the
53+
* response pointer. Otherwise the returned pointer is freed with
54+
* kvfree().
55+
*/
56+
void *(*fw_rpc)(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
57+
void *rpc_in, size_t in_len, size_t *out_len);
5058
};
5159

5260
/**

include/uapi/fwctl/fwctl.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
enum {
3838
FWCTL_CMD_BASE = 0,
3939
FWCTL_CMD_INFO = 0,
40+
FWCTL_CMD_RPC = 1,
4041
};
4142

4243
enum fwctl_device_type {
@@ -66,4 +67,72 @@ struct fwctl_info {
6667
};
6768
#define FWCTL_INFO _IO(FWCTL_TYPE, FWCTL_CMD_INFO)
6869

70+
/**
71+
* enum fwctl_rpc_scope - Scope of access for the RPC
72+
*
73+
* Refer to fwctl.rst for a more detailed discussion of these scopes.
74+
*/
75+
enum fwctl_rpc_scope {
76+
/**
77+
* @FWCTL_RPC_CONFIGURATION: Device configuration access scope
78+
*
79+
* Read/write access to device configuration. When configuration
80+
* is written to the device it remains in a fully supported state.
81+
*/
82+
FWCTL_RPC_CONFIGURATION = 0,
83+
/**
84+
* @FWCTL_RPC_DEBUG_READ_ONLY: Read only access to debug information
85+
*
86+
* Readable debug information. Debug information is compatible with
87+
* kernel lockdown, and does not disclose any sensitive information. For
88+
* instance exposing any encryption secrets from this information is
89+
* forbidden.
90+
*/
91+
FWCTL_RPC_DEBUG_READ_ONLY = 1,
92+
/**
93+
* @FWCTL_RPC_DEBUG_WRITE: Writable access to lockdown compatible debug information
94+
*
95+
* Allows write access to data in the device which may leave a fully
96+
* supported state. This is intended to permit intensive and possibly
97+
* invasive debugging. This scope will taint the kernel.
98+
*/
99+
FWCTL_RPC_DEBUG_WRITE = 2,
100+
/**
101+
* @FWCTL_RPC_DEBUG_WRITE_FULL: Write access to all debug information
102+
*
103+
* Allows read/write access to everything. Requires CAP_SYS_RAW_IO, so
104+
* it is not required to follow lockdown principals. If in doubt
105+
* debugging should be placed in this scope. This scope will taint the
106+
* kernel.
107+
*/
108+
FWCTL_RPC_DEBUG_WRITE_FULL = 3,
109+
};
110+
111+
/**
112+
* struct fwctl_rpc - ioctl(FWCTL_RPC)
113+
* @size: sizeof(struct fwctl_rpc)
114+
* @scope: One of enum fwctl_rpc_scope, required scope for the RPC
115+
* @in_len: Length of the in memory
116+
* @out_len: Length of the out memory
117+
* @in: Request message in device specific format
118+
* @out: Response message in device specific format
119+
*
120+
* Deliver a Remote Procedure Call to the device FW and return the response. The
121+
* call's parameters and return are marshaled into linear buffers of memory. Any
122+
* errno indicates that delivery of the RPC to the device failed. Return status
123+
* originating in the device during a successful delivery must be encoded into
124+
* out.
125+
*
126+
* The format of the buffers matches the out_device_type from FWCTL_INFO.
127+
*/
128+
struct fwctl_rpc {
129+
__u32 size;
130+
__u32 scope;
131+
__u32 in_len;
132+
__u32 out_len;
133+
__aligned_u64 in;
134+
__aligned_u64 out;
135+
};
136+
#define FWCTL_RPC _IO(FWCTL_TYPE, FWCTL_CMD_RPC)
137+
69138
#endif

0 commit comments

Comments
 (0)