Skip to content

Commit 6985d92

Browse files
popcornmixpelwell
authored andcommitted
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 f0d6f35 commit 6985d92

1 file changed

Lines changed: 188 additions & 17 deletions

File tree

drivers/char/broadcom/vcio.c

Lines changed: 188 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,138 @@
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+
0x00040011, /* GET_RSTS */
57+
0x00030096, /* GET_EEPROM_PACKET */
58+
0x00038096, /* SET_EEPROM_PACKET */
59+
0x00030097, /* GET_EEPROM_UPDATE_STATUS */
60+
0x00038097, /* SET_EEPROM_UPDATE_STATUS */
61+
0x00030098, /* GET_EEPROM_PARTITION */
62+
0x00038098, /* SET_EEPROM_PARTITION */
63+
0x00030099, /* GET_EEPROM_AB_PARAMS */
64+
0x00038099, /* SET_EEPROM_AB_PARAMS */
65+
};
66+
67+
/* Tags permitted via /dev/vcio_gencmd. */
68+
static const u32 vcio_gencmd_tags[] = {
69+
0x00030080, /* GET_GENCMD_RESULT */
70+
};
71+
72+
/* Tags permitted via /dev/vcio_crypto. */
73+
static const u32 vcio_crypto_tags[] = {
74+
0x0003008e, /* GET_CRYPTO_LAST_ERROR */
75+
0x0003008f, /* GET_CRYPTO_NUM_OTP_KEYS */
76+
0x00030090, /* GET_CRYPTO_KEY_STATUS */
77+
0x00038090, /* SET_CRYPTO_KEY_STATUS */
78+
0x00030091, /* GET_CRYPTO_ECDSA_SIGN */
79+
0x00030092, /* GET_CRYPTO_HMAC_SHA256 */
80+
0x00030093, /* GET_CRYPTO_PUBLIC_KEY */
81+
0x00030094, /* GET_CRYPTO_PRIVATE_KEY */
82+
0x00030095, /* GET_CRYPTO_GEN_ECDSA */
83+
};
84+
85+
struct vcio_group {
86+
const char *name;
87+
const u32 *tags;
88+
size_t num_tags;
89+
};
90+
91+
#define VCIO_GROUP(suffix) { \
92+
.name = "vcio_" #suffix, \
93+
.tags = vcio_ ## suffix ## _tags, \
94+
.num_tags = ARRAY_SIZE(vcio_ ## suffix ## _tags), \
95+
}
96+
97+
static const struct vcio_group vcio_groups[] = {
98+
VCIO_GROUP(provisioning),
99+
VCIO_GROUP(ab),
100+
VCIO_GROUP(gencmd),
101+
VCIO_GROUP(crypto),
102+
};
103+
104+
struct vcio_dev {
105+
struct miscdevice misc_dev;
106+
struct rpi_firmware *fw;
107+
const u32 *allowed_tags;
108+
size_t num_allowed_tags;
109+
};
110+
33111
struct vcio_data {
34112
struct rpi_firmware *fw;
35-
struct miscdevice misc_dev;
113+
struct vcio_dev vcio;
114+
struct vcio_dev groups[ARRAY_SIZE(vcio_groups)];
36115
};
37116

38-
static int vcio_user_property_list(struct vcio_data *vcio, void *user)
117+
static bool vcio_tag_allowed(const struct vcio_dev *vdev, u32 tag)
118+
{
119+
size_t i;
120+
121+
if (!vdev->allowed_tags)
122+
return true;
123+
124+
for (i = 0; i < vdev->num_allowed_tags; i++)
125+
if (vdev->allowed_tags[i] == tag)
126+
return true;
127+
128+
return false;
129+
}
130+
131+
static int vcio_check_tags(const struct vcio_dev *vdev, const u32 *buf,
132+
size_t size)
133+
{
134+
size_t offset = 8; /* skip total size and status words */
135+
136+
if (!vdev->allowed_tags)
137+
return 0;
138+
139+
while (offset + 12 <= size) {
140+
u32 tag = buf[offset / 4];
141+
u32 val_size;
142+
143+
if (tag == 0)
144+
return 0;
145+
146+
if (!vcio_tag_allowed(vdev, tag)) {
147+
pr_warn_ratelimited("%s: tag 0x%08x not permitted\n",
148+
vdev->misc_dev.name, tag);
149+
return -EPERM;
150+
}
151+
152+
val_size = buf[offset / 4 + 1];
153+
if (val_size > size - offset - 12)
154+
return -EINVAL;
155+
offset += 12 + ALIGN(val_size, 4);
156+
157+
if (offset > size)
158+
return -EINVAL;
159+
}
160+
161+
return 0;
162+
}
163+
164+
static int vcio_user_property_list(struct vcio_dev *vdev, void *user)
39165
{
40166
u32 *buf, size;
41167
int ret;
@@ -44,6 +170,9 @@ static int vcio_user_property_list(struct vcio_data *vcio, void *user)
44170
if (copy_from_user(&size, user, sizeof(size)))
45171
return -EFAULT;
46172

173+
if (size < 12)
174+
return -EINVAL;
175+
47176
buf = kmalloc(size, GFP_KERNEL);
48177
if (!buf)
49178
return -ENOMEM;
@@ -53,8 +182,14 @@ static int vcio_user_property_list(struct vcio_data *vcio, void *user)
53182
return -EFAULT;
54183
}
55184

185+
ret = vcio_check_tags(vdev, buf, size);
186+
if (ret) {
187+
kfree(buf);
188+
return ret;
189+
}
190+
56191
/* Strip off protocol encapsulation */
57-
ret = rpi_firmware_property_list(vcio->fw, &buf[2], size - 12);
192+
ret = rpi_firmware_property_list(vdev->fw, &buf[2], size - 12);
58193
if (ret) {
59194
kfree(buf);
60195
return ret;
@@ -86,12 +221,12 @@ static int vcio_device_release(struct inode *inode, struct file *file)
86221
static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num,
87222
unsigned long ioctl_param)
88223
{
89-
struct vcio_data *vcio = container_of(file->private_data,
90-
struct vcio_data, misc_dev);
224+
struct vcio_dev *vdev = container_of(file->private_data,
225+
struct vcio_dev, misc_dev);
91226

92227
switch (ioctl_num) {
93228
case IOCTL_MBOX_PROPERTY:
94-
return vcio_user_property_list(vcio, (void *)ioctl_param);
229+
return vcio_user_property_list(vdev, (void *)ioctl_param);
95230
default:
96231
pr_err("unknown ioctl: %x\n", ioctl_num);
97232
return -EINVAL;
@@ -102,12 +237,12 @@ static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num,
102237
static long vcio_device_compat_ioctl(struct file *file, unsigned int ioctl_num,
103238
unsigned long ioctl_param)
104239
{
105-
struct vcio_data *vcio = container_of(file->private_data,
106-
struct vcio_data, misc_dev);
240+
struct vcio_dev *vdev = container_of(file->private_data,
241+
struct vcio_dev, misc_dev);
107242

108243
switch (ioctl_num) {
109244
case IOCTL_MBOX_PROPERTY32:
110-
return vcio_user_property_list(vcio, compat_ptr(ioctl_param));
245+
return vcio_user_property_list(vdev, compat_ptr(ioctl_param));
111246
default:
112247
pr_err("unknown ioctl: %x\n", ioctl_num);
113248
return -EINVAL;
@@ -124,13 +259,30 @@ const struct file_operations vcio_fops = {
124259
.release = vcio_device_release,
125260
};
126261

262+
static int vcio_register(struct device *dev, struct vcio_dev *vdev,
263+
struct rpi_firmware *fw, const char *name,
264+
const u32 *allowed_tags, size_t num_allowed_tags)
265+
{
266+
vdev->fw = fw;
267+
vdev->allowed_tags = allowed_tags;
268+
vdev->num_allowed_tags = num_allowed_tags;
269+
vdev->misc_dev.fops = &vcio_fops;
270+
vdev->misc_dev.minor = MISC_DYNAMIC_MINOR;
271+
vdev->misc_dev.name = name;
272+
vdev->misc_dev.parent = dev;
273+
274+
return misc_register(&vdev->misc_dev);
275+
}
276+
127277
static int vcio_probe(struct platform_device *pdev)
128278
{
129279
struct device *dev = &pdev->dev;
130280
struct device_node *np = dev->of_node;
131281
struct device_node *fw_node;
132282
struct rpi_firmware *fw;
133283
struct vcio_data *vcio;
284+
size_t i;
285+
int ret;
134286

135287
fw_node = of_get_parent(np);
136288
if (!fw_node) {
@@ -143,24 +295,43 @@ static int vcio_probe(struct platform_device *pdev)
143295
if (!fw)
144296
return -EPROBE_DEFER;
145297

146-
vcio = devm_kzalloc(dev, sizeof(struct vcio_data), GFP_KERNEL);
298+
vcio = devm_kzalloc(dev, sizeof(*vcio), GFP_KERNEL);
147299
if (!vcio)
148300
return -ENOMEM;
149301

150302
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;
303+
platform_set_drvdata(pdev, vcio);
304+
305+
ret = vcio_register(dev, &vcio->vcio, fw, "vcio", NULL, 0);
306+
if (ret)
307+
return ret;
155308

156-
return misc_register(&vcio->misc_dev);
309+
for (i = 0; i < ARRAY_SIZE(vcio_groups); i++) {
310+
const struct vcio_group *g = &vcio_groups[i];
311+
312+
ret = vcio_register(dev, &vcio->groups[i], fw, g->name,
313+
g->tags, g->num_tags);
314+
if (ret)
315+
goto err_groups;
316+
}
317+
318+
return 0;
319+
320+
err_groups:
321+
while (i--)
322+
misc_deregister(&vcio->groups[i].misc_dev);
323+
misc_deregister(&vcio->vcio.misc_dev);
324+
return ret;
157325
}
158326

159327
static void vcio_remove(struct platform_device *pdev)
160328
{
161-
struct device *dev = &pdev->dev;
329+
struct vcio_data *vcio = platform_get_drvdata(pdev);
330+
size_t i = ARRAY_SIZE(vcio_groups);
162331

163-
misc_deregister(dev_get_drvdata(dev));
332+
while (i--)
333+
misc_deregister(&vcio->groups[i].misc_dev);
334+
misc_deregister(&vcio->vcio.misc_dev);
164335
}
165336

166337
static const struct of_device_id vcio_ids[] = {

0 commit comments

Comments
 (0)