Skip to content

Commit 720f2d0

Browse files
committed
char: broadcom: vcio: Create addtional vcio nodes for fine grained acccess
vcio has a number of uses, including clocks and power, board info, gencmd, display, vpu execution, provisioning, crypto, ab booting. Currently we have to give a user access to all or none of these. Provide some additional nodes which can only access a subset of these commands. That allows, say, gencmd access from a user or group, without exposing more dangerous commands. The initial extra nodes are for gencmd, provisioning, ab and crypto. Signed-off-by: Dom Cobley <popcornmix@gmail.com>
1 parent 6b979ce commit 720f2d0

1 file changed

Lines changed: 177 additions & 17 deletions

File tree

drivers/char/broadcom/vcio.c

Lines changed: 177 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,127 @@
3030
#define IOCTL_MBOX_PROPERTY32 _IOWR(VCIO_IOC_MAGIC, 0, compat_uptr_t)
3131
#endif
3232

33+
/* Tags permitted via /dev/vcio_provisioning. */
34+
static const u32 vcio_provisioning_tags[] = {
35+
0x00038021, /* SET_CUSTOMER_OTP */
36+
0x00038024, /* SET_USER_OTP */
37+
0x00038082, /* SET_CUSTOMER_ETHER_MAC_OTP */
38+
0x00038083, /* SET_CUSTOMER_WIFI_MAC_OTP */
39+
0x00038084, /* SET_CUSTOMER_BT_MAC_OTP */
40+
0x00030085, /* TST_MAC_OTP */
41+
0x00030086, /* SET_CUSTOMER_OTP_LOCK */
42+
};
43+
44+
/* Tags permitted via /dev/vcio_ab. */
45+
static const u32 vcio_ab_tags[] = {
46+
0x00030048, /* SET_NOTIFY_REBOOT */
47+
0x00030057, /* SET_GLOBAL_RESET */
48+
0x00030064, /* GET_REBOOT_FLAGS */
49+
0x00038064, /* SET_REBOOT_FLAGS */
50+
0x0003008b, /* GET_REBOOT_ORDER */
51+
0x0003808b, /* SET_REBOOT_ORDER */
52+
0x0003008c, /* GET_REBOOT_ARG */
53+
0x0003808c, /* SET_REBOOT_ARG */
54+
0x0003008d, /* GET_BOOT_COUNT */
55+
0x0003808d, /* SET_BOOT_COUNT */
56+
};
57+
58+
/* Tags permitted via /dev/vcio_gencmd. */
59+
static const u32 vcio_gencmd_tags[] = {
60+
0x00030080, /* GET_GENCMD_RESULT */
61+
};
62+
63+
/* Tags permitted via /dev/vcio_crypto. */
64+
static const u32 vcio_crypto_tags[] = {
65+
0x0003008e, /* GET_CRYPTO_LAST_ERROR */
66+
0x0003008f, /* GET_CRYPTO_NUM_OTP_KEYS */
67+
0x00030090, /* GET_CRYPTO_KEY_STATUS */
68+
0x00038090, /* SET_CRYPTO_KEY_STATUS */
69+
0x00030091, /* GET_CRYPTO_ECDSA_SIGN */
70+
0x00030092, /* GET_CRYPTO_HMAC_SHA256 */
71+
0x00030093, /* GET_CRYPTO_PUBLIC_KEY */
72+
0x00030094, /* GET_CRYPTO_PRIVATE_KEY */
73+
0x00030095, /* GET_CRYPTO_GEN_ECDSA */
74+
};
75+
76+
struct vcio_group {
77+
const char *name;
78+
const u32 *tags;
79+
size_t num_tags;
80+
};
81+
82+
#define VCIO_GROUP(suffix) { \
83+
.name = "vcio_" #suffix, \
84+
.tags = vcio_ ## suffix ## _tags, \
85+
.num_tags = ARRAY_SIZE(vcio_ ## suffix ## _tags), \
86+
}
87+
88+
static const struct vcio_group vcio_groups[] = {
89+
VCIO_GROUP(provisioning),
90+
VCIO_GROUP(ab),
91+
VCIO_GROUP(gencmd),
92+
VCIO_GROUP(crypto),
93+
};
94+
95+
struct vcio_dev {
96+
struct miscdevice misc_dev;
97+
struct rpi_firmware *fw;
98+
const u32 *allowed_tags;
99+
size_t num_allowed_tags;
100+
};
101+
33102
struct vcio_data {
34103
struct rpi_firmware *fw;
35-
struct miscdevice misc_dev;
104+
struct vcio_dev vcio;
105+
struct vcio_dev groups[ARRAY_SIZE(vcio_groups)];
36106
};
37107

38-
static int vcio_user_property_list(struct vcio_data *vcio, void *user)
108+
static bool vcio_tag_allowed(const struct vcio_dev *vdev, u32 tag)
109+
{
110+
size_t i;
111+
112+
if (!vdev->allowed_tags)
113+
return true;
114+
115+
for (i = 0; i < vdev->num_allowed_tags; i++)
116+
if (vdev->allowed_tags[i] == tag)
117+
return true;
118+
119+
return false;
120+
}
121+
122+
static int vcio_check_tags(const struct vcio_dev *vdev, const u32 *buf,
123+
size_t size)
124+
{
125+
size_t offset = 8; /* skip total size and status words */
126+
127+
if (!vdev->allowed_tags)
128+
return 0;
129+
130+
while (offset + 12 <= size) {
131+
u32 tag = buf[offset / 4];
132+
u32 val_size;
133+
134+
if (tag == 0)
135+
return 0;
136+
137+
if (!vcio_tag_allowed(vdev, tag)) {
138+
pr_warn_ratelimited("%s: tag 0x%08x not permitted\n",
139+
vdev->misc_dev.name, tag);
140+
return -EPERM;
141+
}
142+
143+
val_size = buf[offset / 4 + 1];
144+
offset += 12 + ALIGN(val_size, 4);
145+
146+
if (offset < 12 || offset > size)
147+
return -EINVAL;
148+
}
149+
150+
return 0;
151+
}
152+
153+
static int vcio_user_property_list(struct vcio_dev *vdev, void *user)
39154
{
40155
u32 *buf, size;
41156
int ret;
@@ -44,6 +159,9 @@ static int vcio_user_property_list(struct vcio_data *vcio, void *user)
44159
if (copy_from_user(&size, user, sizeof(size)))
45160
return -EFAULT;
46161

162+
if (size < 12)
163+
return -EINVAL;
164+
47165
buf = kmalloc(size, GFP_KERNEL);
48166
if (!buf)
49167
return -ENOMEM;
@@ -53,8 +171,14 @@ static int vcio_user_property_list(struct vcio_data *vcio, void *user)
53171
return -EFAULT;
54172
}
55173

174+
ret = vcio_check_tags(vdev, buf, size);
175+
if (ret) {
176+
kfree(buf);
177+
return ret;
178+
}
179+
56180
/* Strip off protocol encapsulation */
57-
ret = rpi_firmware_property_list(vcio->fw, &buf[2], size - 12);
181+
ret = rpi_firmware_property_list(vdev->fw, &buf[2], size - 12);
58182
if (ret) {
59183
kfree(buf);
60184
return ret;
@@ -86,12 +210,12 @@ static int vcio_device_release(struct inode *inode, struct file *file)
86210
static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num,
87211
unsigned long ioctl_param)
88212
{
89-
struct vcio_data *vcio = container_of(file->private_data,
90-
struct vcio_data, misc_dev);
213+
struct vcio_dev *vdev = container_of(file->private_data,
214+
struct vcio_dev, misc_dev);
91215

92216
switch (ioctl_num) {
93217
case IOCTL_MBOX_PROPERTY:
94-
return vcio_user_property_list(vcio, (void *)ioctl_param);
218+
return vcio_user_property_list(vdev, (void *)ioctl_param);
95219
default:
96220
pr_err("unknown ioctl: %x\n", ioctl_num);
97221
return -EINVAL;
@@ -102,12 +226,12 @@ static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num,
102226
static long vcio_device_compat_ioctl(struct file *file, unsigned int ioctl_num,
103227
unsigned long ioctl_param)
104228
{
105-
struct vcio_data *vcio = container_of(file->private_data,
106-
struct vcio_data, misc_dev);
229+
struct vcio_dev *vdev = container_of(file->private_data,
230+
struct vcio_dev, misc_dev);
107231

108232
switch (ioctl_num) {
109233
case IOCTL_MBOX_PROPERTY32:
110-
return vcio_user_property_list(vcio, compat_ptr(ioctl_param));
234+
return vcio_user_property_list(vdev, compat_ptr(ioctl_param));
111235
default:
112236
pr_err("unknown ioctl: %x\n", ioctl_num);
113237
return -EINVAL;
@@ -124,13 +248,30 @@ const struct file_operations vcio_fops = {
124248
.release = vcio_device_release,
125249
};
126250

251+
static int vcio_register(struct device *dev, struct vcio_dev *vdev,
252+
struct rpi_firmware *fw, const char *name,
253+
const u32 *allowed_tags, size_t num_allowed_tags)
254+
{
255+
vdev->fw = fw;
256+
vdev->allowed_tags = allowed_tags;
257+
vdev->num_allowed_tags = num_allowed_tags;
258+
vdev->misc_dev.fops = &vcio_fops;
259+
vdev->misc_dev.minor = MISC_DYNAMIC_MINOR;
260+
vdev->misc_dev.name = name;
261+
vdev->misc_dev.parent = dev;
262+
263+
return misc_register(&vdev->misc_dev);
264+
}
265+
127266
static int vcio_probe(struct platform_device *pdev)
128267
{
129268
struct device *dev = &pdev->dev;
130269
struct device_node *np = dev->of_node;
131270
struct device_node *fw_node;
132271
struct rpi_firmware *fw;
133272
struct vcio_data *vcio;
273+
size_t i;
274+
int ret;
134275

135276
fw_node = of_get_parent(np);
136277
if (!fw_node) {
@@ -143,24 +284,43 @@ static int vcio_probe(struct platform_device *pdev)
143284
if (!fw)
144285
return -EPROBE_DEFER;
145286

146-
vcio = devm_kzalloc(dev, sizeof(struct vcio_data), GFP_KERNEL);
287+
vcio = devm_kzalloc(dev, sizeof(*vcio), GFP_KERNEL);
147288
if (!vcio)
148289
return -ENOMEM;
149290

150291
vcio->fw = fw;
151-
vcio->misc_dev.fops = &vcio_fops;
152-
vcio->misc_dev.minor = MISC_DYNAMIC_MINOR;
153-
vcio->misc_dev.name = "vcio";
154-
vcio->misc_dev.parent = dev;
292+
platform_set_drvdata(pdev, vcio);
293+
294+
ret = vcio_register(dev, &vcio->vcio, fw, "vcio", NULL, 0);
295+
if (ret)
296+
return ret;
155297

156-
return misc_register(&vcio->misc_dev);
298+
for (i = 0; i < ARRAY_SIZE(vcio_groups); i++) {
299+
const struct vcio_group *g = &vcio_groups[i];
300+
301+
ret = vcio_register(dev, &vcio->groups[i], fw, g->name,
302+
g->tags, g->num_tags);
303+
if (ret)
304+
goto err_groups;
305+
}
306+
307+
return 0;
308+
309+
err_groups:
310+
while (i--)
311+
misc_deregister(&vcio->groups[i].misc_dev);
312+
misc_deregister(&vcio->vcio.misc_dev);
313+
return ret;
157314
}
158315

159316
static void vcio_remove(struct platform_device *pdev)
160317
{
161-
struct device *dev = &pdev->dev;
318+
struct vcio_data *vcio = platform_get_drvdata(pdev);
319+
size_t i = ARRAY_SIZE(vcio_groups);
162320

163-
misc_deregister(dev_get_drvdata(dev));
321+
while (i--)
322+
misc_deregister(&vcio->groups[i].misc_dev);
323+
misc_deregister(&vcio->vcio.misc_dev);
164324
}
165325

166326
static const struct of_device_id vcio_ids[] = {

0 commit comments

Comments
 (0)