Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/bus/fsl-mc/fsl-mc-uapi.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Management Complex (MC) userspace support
4
*
5
* Copyright 2021 NXP
6
*
7
*/
8
9
#include <linux/slab.h>
10
#include <linux/fs.h>
11
#include <linux/uaccess.h>
12
#include <linux/miscdevice.h>
13
14
#include "fsl-mc-private.h"
15
16
struct uapi_priv_data {
17
struct fsl_mc_uapi *uapi;
18
struct fsl_mc_io *mc_io;
19
};
20
21
struct fsl_mc_cmd_desc {
22
u16 cmdid_value;
23
u16 cmdid_mask;
24
int size;
25
bool token;
26
int flags;
27
};
28
29
#define FSL_MC_CHECK_MODULE_ID BIT(0)
30
#define FSL_MC_CAP_NET_ADMIN_NEEDED BIT(1)
31
32
enum fsl_mc_cmd_index {
33
DPDBG_DUMP = 0,
34
DPDBG_SET,
35
DPRC_GET_CONTAINER_ID,
36
DPRC_CREATE_CONT,
37
DPRC_DESTROY_CONT,
38
DPRC_ASSIGN,
39
DPRC_UNASSIGN,
40
DPRC_GET_OBJ_COUNT,
41
DPRC_GET_OBJ,
42
DPRC_GET_RES_COUNT,
43
DPRC_GET_RES_IDS,
44
DPRC_SET_OBJ_LABEL,
45
DPRC_SET_LOCKED,
46
DPRC_CONNECT,
47
DPRC_DISCONNECT,
48
DPRC_GET_POOL,
49
DPRC_GET_POOL_COUNT,
50
DPRC_GET_CONNECTION,
51
DPRC_GET_MEM,
52
DPCI_GET_LINK_STATE,
53
DPCI_GET_PEER_ATTR,
54
DPAIOP_GET_SL_VERSION,
55
DPAIOP_GET_STATE,
56
DPMNG_GET_VERSION,
57
DPSECI_GET_TX_QUEUE,
58
DPMAC_GET_COUNTER,
59
DPMAC_GET_MAC_ADDR,
60
DPNI_SET_PRIM_MAC,
61
DPNI_GET_PRIM_MAC,
62
DPNI_GET_STATISTICS,
63
DPNI_GET_LINK_STATE,
64
DPNI_GET_MAX_FRAME_LENGTH,
65
DPSW_GET_TAILDROP,
66
DPSW_SET_TAILDROP,
67
DPSW_IF_GET_COUNTER,
68
DPSW_IF_GET_MAX_FRAME_LENGTH,
69
DPDMUX_GET_COUNTER,
70
DPDMUX_IF_GET_MAX_FRAME_LENGTH,
71
GET_ATTR,
72
GET_IRQ_MASK,
73
GET_IRQ_STATUS,
74
CLOSE,
75
OPEN,
76
GET_API_VERSION,
77
DESTROY,
78
CREATE,
79
};
80
81
static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
82
[DPDBG_DUMP] = {
83
.cmdid_value = 0x1300,
84
.cmdid_mask = 0xFFF0,
85
.token = true,
86
.size = 28,
87
},
88
[DPDBG_SET] = {
89
.cmdid_value = 0x1400,
90
.cmdid_mask = 0xFFF0,
91
.token = true,
92
.size = 28,
93
},
94
[DPRC_GET_CONTAINER_ID] = {
95
.cmdid_value = 0x8300,
96
.cmdid_mask = 0xFFF0,
97
.token = false,
98
.size = 8,
99
},
100
[DPRC_CREATE_CONT] = {
101
.cmdid_value = 0x1510,
102
.cmdid_mask = 0xFFF0,
103
.token = true,
104
.size = 40,
105
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
106
},
107
[DPRC_DESTROY_CONT] = {
108
.cmdid_value = 0x1520,
109
.cmdid_mask = 0xFFF0,
110
.token = true,
111
.size = 12,
112
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
113
},
114
[DPRC_ASSIGN] = {
115
.cmdid_value = 0x1570,
116
.cmdid_mask = 0xFFF0,
117
.token = true,
118
.size = 40,
119
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
120
},
121
[DPRC_UNASSIGN] = {
122
.cmdid_value = 0x1580,
123
.cmdid_mask = 0xFFF0,
124
.token = true,
125
.size = 40,
126
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
127
},
128
[DPRC_GET_OBJ_COUNT] = {
129
.cmdid_value = 0x1590,
130
.cmdid_mask = 0xFFF0,
131
.token = true,
132
.size = 16,
133
},
134
[DPRC_GET_OBJ] = {
135
.cmdid_value = 0x15A0,
136
.cmdid_mask = 0xFFF0,
137
.token = true,
138
.size = 12,
139
},
140
[DPRC_GET_RES_COUNT] = {
141
.cmdid_value = 0x15B0,
142
.cmdid_mask = 0xFFF0,
143
.token = true,
144
.size = 32,
145
},
146
[DPRC_GET_RES_IDS] = {
147
.cmdid_value = 0x15C0,
148
.cmdid_mask = 0xFFF0,
149
.token = true,
150
.size = 40,
151
},
152
[DPRC_SET_OBJ_LABEL] = {
153
.cmdid_value = 0x1610,
154
.cmdid_mask = 0xFFF0,
155
.token = true,
156
.size = 48,
157
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
158
},
159
[DPRC_SET_LOCKED] = {
160
.cmdid_value = 0x16B0,
161
.cmdid_mask = 0xFFF0,
162
.token = true,
163
.size = 16,
164
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
165
},
166
[DPRC_CONNECT] = {
167
.cmdid_value = 0x1670,
168
.cmdid_mask = 0xFFF0,
169
.token = true,
170
.size = 56,
171
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
172
},
173
[DPRC_DISCONNECT] = {
174
.cmdid_value = 0x1680,
175
.cmdid_mask = 0xFFF0,
176
.token = true,
177
.size = 32,
178
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
179
},
180
[DPRC_GET_POOL] = {
181
.cmdid_value = 0x1690,
182
.cmdid_mask = 0xFFF0,
183
.token = true,
184
.size = 12,
185
},
186
[DPRC_GET_POOL_COUNT] = {
187
.cmdid_value = 0x16A0,
188
.cmdid_mask = 0xFFF0,
189
.token = true,
190
.size = 8,
191
},
192
[DPRC_GET_CONNECTION] = {
193
.cmdid_value = 0x16C0,
194
.cmdid_mask = 0xFFF0,
195
.token = true,
196
.size = 32,
197
},
198
[DPRC_GET_MEM] = {
199
.cmdid_value = 0x16D0,
200
.cmdid_mask = 0xFFF0,
201
.token = true,
202
.size = 12,
203
},
204
205
[DPCI_GET_LINK_STATE] = {
206
.cmdid_value = 0x0E10,
207
.cmdid_mask = 0xFFF0,
208
.token = true,
209
.size = 8,
210
},
211
[DPCI_GET_PEER_ATTR] = {
212
.cmdid_value = 0x0E20,
213
.cmdid_mask = 0xFFF0,
214
.token = true,
215
.size = 8,
216
},
217
[DPAIOP_GET_SL_VERSION] = {
218
.cmdid_value = 0x2820,
219
.cmdid_mask = 0xFFF0,
220
.token = true,
221
.size = 8,
222
},
223
[DPAIOP_GET_STATE] = {
224
.cmdid_value = 0x2830,
225
.cmdid_mask = 0xFFF0,
226
.token = true,
227
.size = 8,
228
},
229
[DPMNG_GET_VERSION] = {
230
.cmdid_value = 0x8310,
231
.cmdid_mask = 0xFFF0,
232
.token = false,
233
.size = 8,
234
},
235
[DPSECI_GET_TX_QUEUE] = {
236
.cmdid_value = 0x1970,
237
.cmdid_mask = 0xFFF0,
238
.token = true,
239
.size = 14,
240
},
241
[DPMAC_GET_COUNTER] = {
242
.cmdid_value = 0x0c40,
243
.cmdid_mask = 0xFFF0,
244
.token = true,
245
.size = 9,
246
},
247
[DPMAC_GET_MAC_ADDR] = {
248
.cmdid_value = 0x0c50,
249
.cmdid_mask = 0xFFF0,
250
.token = true,
251
.size = 8,
252
},
253
[DPNI_SET_PRIM_MAC] = {
254
.cmdid_value = 0x2240,
255
.cmdid_mask = 0xFFF0,
256
.token = true,
257
.size = 16,
258
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
259
},
260
[DPNI_GET_PRIM_MAC] = {
261
.cmdid_value = 0x2250,
262
.cmdid_mask = 0xFFF0,
263
.token = true,
264
.size = 8,
265
},
266
[DPNI_GET_STATISTICS] = {
267
.cmdid_value = 0x25D0,
268
.cmdid_mask = 0xFFF0,
269
.token = true,
270
.size = 10,
271
},
272
[DPNI_GET_LINK_STATE] = {
273
.cmdid_value = 0x2150,
274
.cmdid_mask = 0xFFF0,
275
.token = true,
276
.size = 8,
277
},
278
[DPNI_GET_MAX_FRAME_LENGTH] = {
279
.cmdid_value = 0x2170,
280
.cmdid_mask = 0xFFF0,
281
.token = true,
282
.size = 8,
283
},
284
[DPSW_GET_TAILDROP] = {
285
.cmdid_value = 0x0A90,
286
.cmdid_mask = 0xFFF0,
287
.token = true,
288
.size = 14,
289
},
290
[DPSW_SET_TAILDROP] = {
291
.cmdid_value = 0x0A80,
292
.cmdid_mask = 0xFFF0,
293
.token = true,
294
.size = 24,
295
.flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
296
},
297
[DPSW_IF_GET_COUNTER] = {
298
.cmdid_value = 0x0340,
299
.cmdid_mask = 0xFFF0,
300
.token = true,
301
.size = 11,
302
},
303
[DPSW_IF_GET_MAX_FRAME_LENGTH] = {
304
.cmdid_value = 0x0450,
305
.cmdid_mask = 0xFFF0,
306
.token = true,
307
.size = 10,
308
},
309
[DPDMUX_GET_COUNTER] = {
310
.cmdid_value = 0x0b20,
311
.cmdid_mask = 0xFFF0,
312
.token = true,
313
.size = 11,
314
},
315
[DPDMUX_IF_GET_MAX_FRAME_LENGTH] = {
316
.cmdid_value = 0x0a20,
317
.cmdid_mask = 0xFFF0,
318
.token = true,
319
.size = 10,
320
},
321
[GET_ATTR] = {
322
.cmdid_value = 0x0040,
323
.cmdid_mask = 0xFFF0,
324
.token = true,
325
.size = 8,
326
},
327
[GET_IRQ_MASK] = {
328
.cmdid_value = 0x0150,
329
.cmdid_mask = 0xFFF0,
330
.token = true,
331
.size = 13,
332
},
333
[GET_IRQ_STATUS] = {
334
.cmdid_value = 0x0160,
335
.cmdid_mask = 0xFFF0,
336
.token = true,
337
.size = 13,
338
},
339
[CLOSE] = {
340
.cmdid_value = 0x8000,
341
.cmdid_mask = 0xFFF0,
342
.token = true,
343
.size = 8,
344
},
345
346
/* Common commands amongst all types of objects. Must be checked last. */
347
[OPEN] = {
348
.cmdid_value = 0x8000,
349
.cmdid_mask = 0xFC00,
350
.token = false,
351
.size = 12,
352
.flags = FSL_MC_CHECK_MODULE_ID,
353
},
354
[GET_API_VERSION] = {
355
.cmdid_value = 0xA000,
356
.cmdid_mask = 0xFC00,
357
.token = false,
358
.size = 8,
359
.flags = FSL_MC_CHECK_MODULE_ID,
360
},
361
[DESTROY] = {
362
.cmdid_value = 0x9800,
363
.cmdid_mask = 0xFC00,
364
.token = true,
365
.size = 12,
366
.flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
367
},
368
[CREATE] = {
369
.cmdid_value = 0x9000,
370
.cmdid_mask = 0xFC00,
371
.token = true,
372
.size = 64,
373
.flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
374
},
375
};
376
377
#define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds)
378
379
#define FSL_MC_MAX_MODULE_ID 0x10
380
381
static int fsl_mc_command_check(struct fsl_mc_device *mc_dev,
382
struct fsl_mc_command *mc_cmd)
383
{
384
struct fsl_mc_cmd_desc *desc = NULL;
385
int mc_cmd_max_size, i;
386
bool token_provided;
387
u16 cmdid, module_id;
388
char *mc_cmd_end;
389
char sum = 0;
390
391
/* Check if this is an accepted MC command */
392
cmdid = mc_cmd_hdr_read_cmdid(mc_cmd);
393
for (i = 0; i < FSL_MC_NUM_ACCEPTED_CMDS; i++) {
394
desc = &fsl_mc_accepted_cmds[i];
395
if ((cmdid & desc->cmdid_mask) == desc->cmdid_value)
396
break;
397
}
398
if (i == FSL_MC_NUM_ACCEPTED_CMDS) {
399
dev_err(&mc_dev->dev, "MC command 0x%04x: cmdid not accepted\n", cmdid);
400
return -EACCES;
401
}
402
403
/* Check if the size of the command is honored. Anything beyond the
404
* last valid byte of the command should be zeroed.
405
*/
406
mc_cmd_max_size = sizeof(*mc_cmd);
407
mc_cmd_end = ((char *)mc_cmd) + desc->size;
408
for (i = desc->size; i < mc_cmd_max_size; i++)
409
sum |= *mc_cmd_end++;
410
if (sum) {
411
dev_err(&mc_dev->dev, "MC command 0x%04x: garbage beyond max size of %d bytes!\n",
412
cmdid, desc->size);
413
return -EACCES;
414
}
415
416
/* Some MC commands request a token to be passed so that object
417
* identification is possible. Check if the token passed in the command
418
* is as expected.
419
*/
420
token_provided = mc_cmd_hdr_read_token(mc_cmd) ? true : false;
421
if (token_provided != desc->token) {
422
dev_err(&mc_dev->dev, "MC command 0x%04x: token 0x%04x is invalid!\n",
423
cmdid, mc_cmd_hdr_read_token(mc_cmd));
424
return -EACCES;
425
}
426
427
/* If needed, check if the module ID passed is valid */
428
if (desc->flags & FSL_MC_CHECK_MODULE_ID) {
429
/* The module ID is represented by bits [4:9] from the cmdid */
430
module_id = (cmdid & GENMASK(9, 4)) >> 4;
431
if (module_id == 0 || module_id > FSL_MC_MAX_MODULE_ID) {
432
dev_err(&mc_dev->dev, "MC command 0x%04x: unknown module ID 0x%x\n",
433
cmdid, module_id);
434
return -EACCES;
435
}
436
}
437
438
/* Some commands alter how hardware resources are managed. For these
439
* commands, check for CAP_NET_ADMIN.
440
*/
441
if (desc->flags & FSL_MC_CAP_NET_ADMIN_NEEDED) {
442
if (!capable(CAP_NET_ADMIN)) {
443
dev_err(&mc_dev->dev, "MC command 0x%04x: needs CAP_NET_ADMIN!\n",
444
cmdid);
445
return -EPERM;
446
}
447
}
448
449
return 0;
450
}
451
452
static int fsl_mc_uapi_send_command(struct fsl_mc_device *mc_dev, unsigned long arg,
453
struct fsl_mc_io *mc_io)
454
{
455
struct fsl_mc_command mc_cmd;
456
int error;
457
458
error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd));
459
if (error)
460
return -EFAULT;
461
462
error = fsl_mc_command_check(mc_dev, &mc_cmd);
463
if (error)
464
return error;
465
466
error = mc_send_command(mc_io, &mc_cmd);
467
if (error)
468
return error;
469
470
error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd));
471
if (error)
472
return -EFAULT;
473
474
return 0;
475
}
476
477
static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep)
478
{
479
struct fsl_mc_device *root_mc_device;
480
struct uapi_priv_data *priv_data;
481
struct fsl_mc_io *dynamic_mc_io;
482
struct fsl_mc_uapi *mc_uapi;
483
struct fsl_mc_bus *mc_bus;
484
int error;
485
486
priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
487
if (!priv_data)
488
return -ENOMEM;
489
490
mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc);
491
mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc);
492
root_mc_device = &mc_bus->mc_dev;
493
494
mutex_lock(&mc_uapi->mutex);
495
496
if (!mc_uapi->local_instance_in_use) {
497
priv_data->mc_io = mc_uapi->static_mc_io;
498
mc_uapi->local_instance_in_use = 1;
499
} else {
500
error = fsl_mc_portal_allocate(root_mc_device, 0,
501
&dynamic_mc_io);
502
if (error) {
503
dev_dbg(&root_mc_device->dev,
504
"Could not allocate MC portal\n");
505
goto error_portal_allocate;
506
}
507
508
priv_data->mc_io = dynamic_mc_io;
509
}
510
priv_data->uapi = mc_uapi;
511
filep->private_data = priv_data;
512
513
mutex_unlock(&mc_uapi->mutex);
514
515
return 0;
516
517
error_portal_allocate:
518
mutex_unlock(&mc_uapi->mutex);
519
kfree(priv_data);
520
521
return error;
522
}
523
524
static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep)
525
{
526
struct uapi_priv_data *priv_data;
527
struct fsl_mc_uapi *mc_uapi;
528
struct fsl_mc_io *mc_io;
529
530
priv_data = filep->private_data;
531
mc_uapi = priv_data->uapi;
532
mc_io = priv_data->mc_io;
533
534
mutex_lock(&mc_uapi->mutex);
535
536
if (mc_io == mc_uapi->static_mc_io)
537
mc_uapi->local_instance_in_use = 0;
538
else
539
fsl_mc_portal_free(mc_io);
540
541
kfree(filep->private_data);
542
filep->private_data = NULL;
543
544
mutex_unlock(&mc_uapi->mutex);
545
546
return 0;
547
}
548
549
static long fsl_mc_uapi_dev_ioctl(struct file *file,
550
unsigned int cmd,
551
unsigned long arg)
552
{
553
struct uapi_priv_data *priv_data = file->private_data;
554
struct fsl_mc_device *root_mc_device;
555
struct fsl_mc_bus *mc_bus;
556
int error;
557
558
mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc);
559
root_mc_device = &mc_bus->mc_dev;
560
561
switch (cmd) {
562
case FSL_MC_SEND_MC_COMMAND:
563
error = fsl_mc_uapi_send_command(root_mc_device, arg, priv_data->mc_io);
564
break;
565
default:
566
dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n");
567
error = -EINVAL;
568
}
569
570
return error;
571
}
572
573
static const struct file_operations fsl_mc_uapi_dev_fops = {
574
.owner = THIS_MODULE,
575
.open = fsl_mc_uapi_dev_open,
576
.release = fsl_mc_uapi_dev_release,
577
.unlocked_ioctl = fsl_mc_uapi_dev_ioctl,
578
};
579
580
int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus)
581
{
582
struct fsl_mc_device *mc_dev = &mc_bus->mc_dev;
583
struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc;
584
int error;
585
586
mc_uapi->misc.minor = MISC_DYNAMIC_MINOR;
587
mc_uapi->misc.name = dev_name(&mc_dev->dev);
588
mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops;
589
590
error = misc_register(&mc_uapi->misc);
591
if (error)
592
return error;
593
594
mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io;
595
596
mutex_init(&mc_uapi->mutex);
597
598
return 0;
599
}
600
601
void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)
602
{
603
misc_deregister(&mc_bus->uapi_misc.misc);
604
}
605
606