1 /*-
2 * Copyright (c) 2014-2015 Sandvine Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/param.h>
28 #include <sys/conf.h>
29 #include <sys/ctype.h>
30 #include <sys/kernel.h>
31 #include <sys/systm.h>
32 #include <sys/iov.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
35 #include <sys/queue.h>
36
37 #include <machine/stdarg.h>
38
39 #include <sys/dnv.h>
40 #include <sys/nv.h>
41 #include <sys/iov_schema.h>
42
43 #include <net/ethernet.h>
44
45 #include <dev/pci/schema_private.h>
46
47 struct config_type_validator;
48 typedef int (validate_func)(const struct config_type_validator *,
49 const nvlist_t *, const char *name);
50 typedef int (default_validate_t)(const struct config_type_validator *,
51 const nvlist_t *);
52
53 static validate_func pci_iov_schema_validate_bool;
54 static validate_func pci_iov_schema_validate_string;
55 static validate_func pci_iov_schema_validate_uint;
56 static validate_func pci_iov_schema_validate_unicast_mac;
57
58 static default_validate_t pci_iov_validate_bool_default;
59 static default_validate_t pci_iov_validate_string_default;
60 static default_validate_t pci_iov_validate_uint_default;
61 static default_validate_t pci_iov_validate_unicast_mac_default;
62
63 struct config_type_validator {
64 const char *type_name;
65 validate_func *validate;
66 default_validate_t *default_validate;
67 uintmax_t limit;
68 };
69
70 static struct config_type_validator pci_iov_schema_validators[] = {
71 {
72 .type_name = "bool",
73 .validate = pci_iov_schema_validate_bool,
74 .default_validate = pci_iov_validate_bool_default
75 },
76 {
77 .type_name = "string",
78 .validate = pci_iov_schema_validate_string,
79 .default_validate = pci_iov_validate_string_default
80 },
81 {
82 .type_name = "uint8_t",
83 .validate = pci_iov_schema_validate_uint,
84 .default_validate = pci_iov_validate_uint_default,
85 .limit = UINT8_MAX
86 },
87 {
88 .type_name = "uint16_t",
89 .validate = pci_iov_schema_validate_uint,
90 .default_validate = pci_iov_validate_uint_default,
91 .limit = UINT16_MAX
92 },
93 {
94 .type_name = "uint32_t",
95 .validate = pci_iov_schema_validate_uint,
96 .default_validate = pci_iov_validate_uint_default,
97 .limit = UINT32_MAX
98 },
99 {
100 .type_name = "uint64_t",
101 .validate = pci_iov_schema_validate_uint,
102 .default_validate = pci_iov_validate_uint_default,
103 .limit = UINT64_MAX
104 },
105 {
106 .type_name = "unicast-mac",
107 .validate = pci_iov_schema_validate_unicast_mac,
108 .default_validate = pci_iov_validate_unicast_mac_default,
109 },
110 };
111
112 static const struct config_type_validator *
pci_iov_schema_find_validator(const char * type)113 pci_iov_schema_find_validator(const char *type)
114 {
115 struct config_type_validator *validator;
116 int i;
117
118 for (i = 0; i < nitems(pci_iov_schema_validators); i++) {
119 validator = &pci_iov_schema_validators[i];
120 if (strcmp(type, validator->type_name) == 0)
121 return (validator);
122 }
123
124 return (NULL);
125 }
126
127 static void
pci_iov_schema_add_type(nvlist_t * entry,const char * type)128 pci_iov_schema_add_type(nvlist_t *entry, const char *type)
129 {
130
131 if (pci_iov_schema_find_validator(type) == NULL) {
132 nvlist_set_error(entry, EINVAL);
133 return;
134 }
135 nvlist_add_string(entry, "type", type);
136 }
137
138 static void
pci_iov_schema_add_required(nvlist_t * entry,uint32_t flags)139 pci_iov_schema_add_required(nvlist_t *entry, uint32_t flags)
140 {
141
142 if (flags & IOV_SCHEMA_REQUIRED) {
143 if (flags & IOV_SCHEMA_HASDEFAULT) {
144 nvlist_set_error(entry, EINVAL);
145 return;
146 }
147
148 nvlist_add_bool(entry, "required", 1);
149 }
150 }
151
152 void
pci_iov_schema_add_bool(nvlist_t * schema,const char * name,uint32_t flags,int defaultVal)153 pci_iov_schema_add_bool(nvlist_t *schema, const char *name, uint32_t flags,
154 int defaultVal)
155 {
156 nvlist_t *entry;
157
158 entry = nvlist_create(NV_FLAG_IGNORE_CASE);
159 if (entry == NULL) {
160 nvlist_set_error(schema, ENOMEM);
161 return;
162 }
163
164 pci_iov_schema_add_type(entry, "bool");
165 if (flags & IOV_SCHEMA_HASDEFAULT)
166 nvlist_add_bool(entry, "default", defaultVal);
167 pci_iov_schema_add_required(entry, flags);
168
169 nvlist_move_nvlist(schema, name, entry);
170 }
171
172 void
pci_iov_schema_add_string(nvlist_t * schema,const char * name,uint32_t flags,const char * defaultVal)173 pci_iov_schema_add_string(nvlist_t *schema, const char *name, uint32_t flags,
174 const char *defaultVal)
175 {
176 nvlist_t *entry;
177
178 entry = nvlist_create(NV_FLAG_IGNORE_CASE);
179 if (entry == NULL) {
180 nvlist_set_error(schema, ENOMEM);
181 return;
182 }
183
184 pci_iov_schema_add_type(entry, "string");
185 if (flags & IOV_SCHEMA_HASDEFAULT)
186 nvlist_add_string(entry, "default", defaultVal);
187 pci_iov_schema_add_required(entry, flags);
188
189 nvlist_move_nvlist(schema, name, entry);
190 }
191
192 static void
pci_iov_schema_int(nvlist_t * schema,const char * name,const char * type,uint32_t flags,uint64_t defaultVal)193 pci_iov_schema_int(nvlist_t *schema, const char *name, const char *type,
194 uint32_t flags, uint64_t defaultVal)
195 {
196 nvlist_t *entry;
197
198 entry = nvlist_create(NV_FLAG_IGNORE_CASE);
199 if (entry == NULL) {
200 nvlist_set_error(schema, ENOMEM);
201 return;
202 }
203
204 pci_iov_schema_add_type(entry, type);
205 if (flags & IOV_SCHEMA_HASDEFAULT)
206 nvlist_add_number(entry, "default", defaultVal);
207 pci_iov_schema_add_required(entry, flags);
208
209 nvlist_move_nvlist(schema, name, entry);
210 }
211
212 void
pci_iov_schema_add_uint8(nvlist_t * schema,const char * name,uint32_t flags,uint8_t defaultVal)213 pci_iov_schema_add_uint8(nvlist_t *schema, const char *name, uint32_t flags,
214 uint8_t defaultVal)
215 {
216
217 pci_iov_schema_int(schema, name, "uint8_t", flags, defaultVal);
218 }
219
220 void
pci_iov_schema_add_uint16(nvlist_t * schema,const char * name,uint32_t flags,uint16_t defaultVal)221 pci_iov_schema_add_uint16(nvlist_t *schema, const char *name, uint32_t flags,
222 uint16_t defaultVal)
223 {
224
225 pci_iov_schema_int(schema, name, "uint16_t", flags, defaultVal);
226 }
227
228 void
pci_iov_schema_add_uint32(nvlist_t * schema,const char * name,uint32_t flags,uint32_t defaultVal)229 pci_iov_schema_add_uint32(nvlist_t *schema, const char *name, uint32_t flags,
230 uint32_t defaultVal)
231 {
232
233 pci_iov_schema_int(schema, name, "uint32_t", flags, defaultVal);
234 }
235
236 void
pci_iov_schema_add_uint64(nvlist_t * schema,const char * name,uint32_t flags,uint64_t defaultVal)237 pci_iov_schema_add_uint64(nvlist_t *schema, const char *name, uint32_t flags,
238 uint64_t defaultVal)
239 {
240
241 pci_iov_schema_int(schema, name, "uint64_t", flags, defaultVal);
242 }
243
244 void
pci_iov_schema_add_unicast_mac(nvlist_t * schema,const char * name,uint32_t flags,const uint8_t * defaultVal)245 pci_iov_schema_add_unicast_mac(nvlist_t *schema, const char *name,
246 uint32_t flags, const uint8_t * defaultVal)
247 {
248 nvlist_t *entry;
249
250 entry = nvlist_create(NV_FLAG_IGNORE_CASE);
251 if (entry == NULL) {
252 nvlist_set_error(schema, ENOMEM);
253 return;
254 }
255
256 pci_iov_schema_add_type(entry, "unicast-mac");
257 if (flags & IOV_SCHEMA_HASDEFAULT)
258 nvlist_add_binary(entry, "default", defaultVal, ETHER_ADDR_LEN);
259 pci_iov_schema_add_required(entry, flags);
260
261 nvlist_move_nvlist(schema, name, entry);
262 }
263
264 static int
pci_iov_schema_validate_bool(const struct config_type_validator * validator,const nvlist_t * config,const char * name)265 pci_iov_schema_validate_bool(const struct config_type_validator * validator,
266 const nvlist_t *config, const char *name)
267 {
268
269 if (!nvlist_exists_bool(config, name))
270 return (EINVAL);
271 return (0);
272 }
273
274 static int
pci_iov_schema_validate_string(const struct config_type_validator * validator,const nvlist_t * config,const char * name)275 pci_iov_schema_validate_string(const struct config_type_validator * validator,
276 const nvlist_t *config, const char *name)
277 {
278
279 if (!nvlist_exists_string(config, name))
280 return (EINVAL);
281 return (0);
282 }
283
284 static int
pci_iov_schema_validate_uint(const struct config_type_validator * validator,const nvlist_t * config,const char * name)285 pci_iov_schema_validate_uint(const struct config_type_validator * validator,
286 const nvlist_t *config, const char *name)
287 {
288 uint64_t value;
289
290 if (!nvlist_exists_number(config, name))
291 return (EINVAL);
292
293 value = nvlist_get_number(config, name);
294
295 if (value > validator->limit)
296 return (EINVAL);
297
298 return (0);
299 }
300
301 static int
pci_iov_schema_validate_unicast_mac(const struct config_type_validator * validator,const nvlist_t * config,const char * name)302 pci_iov_schema_validate_unicast_mac(
303 const struct config_type_validator * validator,
304 const nvlist_t *config, const char *name)
305 {
306 const uint8_t *mac;
307 size_t size;
308
309 if (!nvlist_exists_binary(config, name))
310 return (EINVAL);
311
312 mac = nvlist_get_binary(config, name, &size);
313
314 if (size != ETHER_ADDR_LEN)
315 return (EINVAL);
316
317 if (ETHER_IS_MULTICAST(mac))
318 return (EINVAL);
319
320 return (0);
321 }
322
323 static void
pci_iov_config_add_default(const nvlist_t * param_schema,const char * name,nvlist_t * config)324 pci_iov_config_add_default(const nvlist_t *param_schema, const char *name,
325 nvlist_t *config)
326 {
327 const void *binary;
328 size_t len;
329
330 if (nvlist_exists_binary(param_schema, "default")) {
331 binary = nvlist_get_binary(param_schema, "default", &len);
332 nvlist_add_binary(config, name, binary, len);
333 } else if (nvlist_exists_bool(param_schema, "default"))
334 nvlist_add_bool(config, name,
335 nvlist_get_bool(param_schema, "default"));
336 else if (nvlist_exists_number(param_schema, "default"))
337 nvlist_add_number(config, name,
338 nvlist_get_number(param_schema, "default"));
339 else if (nvlist_exists_nvlist(param_schema, "default"))
340 nvlist_add_nvlist(config, name,
341 nvlist_get_nvlist(param_schema, "default"));
342 else if (nvlist_exists_string(param_schema, "default"))
343 nvlist_add_string(config, name,
344 nvlist_get_string(param_schema, "default"));
345 else
346 panic("Unexpected nvlist type");
347 }
348
349 static int
pci_iov_validate_bool_default(const struct config_type_validator * validator,const nvlist_t * param)350 pci_iov_validate_bool_default(const struct config_type_validator * validator,
351 const nvlist_t *param)
352 {
353
354 if (!nvlist_exists_bool(param, DEFAULT_SCHEMA_NAME))
355 return (EINVAL);
356 return (0);
357 }
358
359 static int
pci_iov_validate_string_default(const struct config_type_validator * validator,const nvlist_t * param)360 pci_iov_validate_string_default(const struct config_type_validator * validator,
361 const nvlist_t *param)
362 {
363
364 if (!nvlist_exists_string(param, DEFAULT_SCHEMA_NAME))
365 return (EINVAL);
366 return (0);
367 }
368
369 static int
pci_iov_validate_uint_default(const struct config_type_validator * validator,const nvlist_t * param)370 pci_iov_validate_uint_default(const struct config_type_validator * validator,
371 const nvlist_t *param)
372 {
373 uint64_t defaultVal;
374
375 if (!nvlist_exists_number(param, DEFAULT_SCHEMA_NAME))
376 return (EINVAL);
377
378 defaultVal = nvlist_get_number(param, DEFAULT_SCHEMA_NAME);
379 if (defaultVal > validator->limit)
380 return (EINVAL);
381 return (0);
382 }
383
384 static int
pci_iov_validate_unicast_mac_default(const struct config_type_validator * validator,const nvlist_t * param)385 pci_iov_validate_unicast_mac_default(
386 const struct config_type_validator * validator, const nvlist_t *param)
387 {
388 const uint8_t *mac;
389 size_t size;
390
391 if (!nvlist_exists_binary(param, DEFAULT_SCHEMA_NAME))
392 return (EINVAL);
393
394 mac = nvlist_get_binary(param, DEFAULT_SCHEMA_NAME, &size);
395 if (size != ETHER_ADDR_LEN)
396 return (EINVAL);
397
398 if (ETHER_IS_MULTICAST(mac))
399 return (EINVAL);
400 return (0);
401 }
402
403 static int
pci_iov_validate_param_schema(const nvlist_t * schema)404 pci_iov_validate_param_schema(const nvlist_t *schema)
405 {
406 const struct config_type_validator *validator;
407 const char *type;
408 int error;
409
410 /* All parameters must define a type. */
411 if (!nvlist_exists_string(schema, TYPE_SCHEMA_NAME))
412 return (EINVAL);
413 type = nvlist_get_string(schema, TYPE_SCHEMA_NAME);
414
415 validator = pci_iov_schema_find_validator(type);
416 if (validator == NULL)
417 return (EINVAL);
418
419 /* Validate that the default value conforms to the type. */
420 if (nvlist_exists(schema, DEFAULT_SCHEMA_NAME)) {
421 error = validator->default_validate(validator, schema);
422 if (error != 0)
423 return (error);
424
425 /* Required and Default are mutually exclusive. */
426 if (nvlist_exists(schema, REQUIRED_SCHEMA_NAME))
427 return (EINVAL);
428 }
429
430 /* The "Required" field must be a bool. */
431 if (nvlist_exists(schema, REQUIRED_SCHEMA_NAME)) {
432 if (!nvlist_exists_bool(schema, REQUIRED_SCHEMA_NAME))
433 return (EINVAL);
434 }
435
436 return (0);
437 }
438
439 static int
pci_iov_validate_subsystem_schema(const nvlist_t * dev_schema,const char * name)440 pci_iov_validate_subsystem_schema(const nvlist_t *dev_schema, const char *name)
441 {
442 const nvlist_t *sub_schema, *param_schema;
443 const char *param_name;
444 void *it;
445 int type, error;
446
447 if (!nvlist_exists_nvlist(dev_schema, name))
448 return (EINVAL);
449 sub_schema = nvlist_get_nvlist(dev_schema, name);
450
451 it = NULL;
452 while ((param_name = nvlist_next(sub_schema, &type, &it)) != NULL) {
453 if (type != NV_TYPE_NVLIST)
454 return (EINVAL);
455 param_schema = nvlist_get_nvlist(sub_schema, param_name);
456
457 error = pci_iov_validate_param_schema(param_schema);
458 if (error != 0)
459 return (error);
460 }
461
462 return (0);
463 }
464
465 /*
466 * Validate that the driver schema does not define any configuration parameters
467 * whose names collide with configuration parameters defined in the iov schema.
468 */
469 static int
pci_iov_validate_param_collisions(const nvlist_t * dev_schema)470 pci_iov_validate_param_collisions(const nvlist_t *dev_schema)
471 {
472 const nvlist_t *iov_schema, *driver_schema;
473 const char *name;
474 void *it;
475 int type;
476
477 driver_schema = nvlist_get_nvlist(dev_schema, DRIVER_CONFIG_NAME);
478 iov_schema = nvlist_get_nvlist(dev_schema, IOV_CONFIG_NAME);
479
480 it = NULL;
481 while ((name = nvlist_next(driver_schema, &type, &it)) != NULL) {
482 if (nvlist_exists(iov_schema, name))
483 return (EINVAL);
484 }
485
486 return (0);
487 }
488
489 /*
490 * Validate that we only have IOV and DRIVER subsystems beneath the given
491 * device schema node.
492 */
493 static int
pci_iov_validate_schema_subsystems(const nvlist_t * dev_schema)494 pci_iov_validate_schema_subsystems(const nvlist_t *dev_schema)
495 {
496 const char *name;
497 void *it;
498 int type;
499
500 it = NULL;
501 while ((name = nvlist_next(dev_schema, &type, &it)) != NULL) {
502 if (strcmp(name, IOV_CONFIG_NAME) != 0 &&
503 strcmp(name, DRIVER_CONFIG_NAME) != 0)
504 return (EINVAL);
505 }
506
507 return (0);
508 }
509
510 static int
pci_iov_validate_device_schema(const nvlist_t * schema,const char * name)511 pci_iov_validate_device_schema(const nvlist_t *schema, const char *name)
512 {
513 const nvlist_t *dev_schema;
514 int error;
515
516 if (!nvlist_exists_nvlist(schema, name))
517 return (EINVAL);
518 dev_schema = nvlist_get_nvlist(schema, name);
519
520 error = pci_iov_validate_subsystem_schema(dev_schema, IOV_CONFIG_NAME);
521 if (error != 0)
522 return (error);
523
524 error = pci_iov_validate_subsystem_schema(dev_schema,
525 DRIVER_CONFIG_NAME);
526 if (error != 0)
527 return (error);
528
529 error = pci_iov_validate_param_collisions(dev_schema);
530 if (error != 0)
531 return (error);
532
533 return (pci_iov_validate_schema_subsystems(dev_schema));
534 }
535
536 /* Validate that we only have PF and VF devices beneath the top-level schema. */
537 static int
pci_iov_validate_schema_devices(const nvlist_t * dev_schema)538 pci_iov_validate_schema_devices(const nvlist_t *dev_schema)
539 {
540 const char *name;
541 void *it;
542 int type;
543
544 it = NULL;
545 while ((name = nvlist_next(dev_schema, &type, &it)) != NULL) {
546 if (strcmp(name, PF_CONFIG_NAME) != 0 &&
547 strcmp(name, VF_SCHEMA_NAME) != 0)
548 return (EINVAL);
549 }
550
551 return (0);
552 }
553
554 int
pci_iov_validate_schema(const nvlist_t * schema)555 pci_iov_validate_schema(const nvlist_t *schema)
556 {
557 int error;
558
559 error = pci_iov_validate_device_schema(schema, PF_CONFIG_NAME);
560 if (error != 0)
561 return (error);
562
563 error = pci_iov_validate_device_schema(schema, VF_SCHEMA_NAME);
564 if (error != 0)
565 return (error);
566
567 return (pci_iov_validate_schema_devices(schema));
568 }
569
570 /*
571 * Validate that all required parameters from the schema are specified in the
572 * config. If any parameter with a default value is not specified in the
573 * config, add it to config.
574 */
575 static int
pci_iov_schema_validate_required(const nvlist_t * schema,nvlist_t * config)576 pci_iov_schema_validate_required(const nvlist_t *schema, nvlist_t *config)
577 {
578 const nvlist_t *param_schema;
579 const char *name;
580 void *cookie;
581 int type;
582
583 cookie = NULL;
584 while ((name = nvlist_next(schema, &type, &cookie)) != NULL) {
585 param_schema = nvlist_get_nvlist(schema, name);
586
587 if (dnvlist_get_bool(param_schema, "required", 0)) {
588 if (!nvlist_exists(config, name))
589 return (EINVAL);
590 }
591
592 if (nvlist_exists(param_schema, "default") &&
593 !nvlist_exists(config, name))
594 pci_iov_config_add_default(param_schema, name, config);
595 }
596
597 return (nvlist_error(config));
598 }
599
600 static int
pci_iov_schema_validate_param(const nvlist_t * schema_param,const char * name,const nvlist_t * config)601 pci_iov_schema_validate_param(const nvlist_t *schema_param, const char *name,
602 const nvlist_t *config)
603 {
604 const struct config_type_validator *validator;
605 const char *type;
606
607 type = nvlist_get_string(schema_param, "type");
608 validator = pci_iov_schema_find_validator(type);
609
610 KASSERT(validator != NULL,
611 ("Schema was not validated: Unknown type %s", type));
612
613 return (validator->validate(validator, config, name));
614 }
615
616 /*
617 * Validate that all parameters in config are defined in the schema. Also
618 * validate that the type of the parameter matches the type in the schema.
619 */
620 static int
pci_iov_schema_validate_types(const nvlist_t * schema,const nvlist_t * config)621 pci_iov_schema_validate_types(const nvlist_t *schema, const nvlist_t *config)
622 {
623 const nvlist_t *schema_param;
624 void *cookie;
625 const char *name;
626 int type, error;
627
628 cookie = NULL;
629 while ((name = nvlist_next(config, &type, &cookie)) != NULL) {
630 if (!nvlist_exists_nvlist(schema, name))
631 return (EINVAL);
632
633 schema_param = nvlist_get_nvlist(schema, name);
634
635 error = pci_iov_schema_validate_param(schema_param, name,
636 config);
637
638 if (error != 0)
639 return (error);
640 }
641
642 return (0);
643 }
644
645 static int
pci_iov_schema_validate_device(const nvlist_t * schema,nvlist_t * config,const char * schema_device,const char * config_device)646 pci_iov_schema_validate_device(const nvlist_t *schema, nvlist_t *config,
647 const char *schema_device, const char *config_device)
648 {
649 const nvlist_t *device_schema, *iov_schema, *driver_schema;
650 nvlist_t *device_config, *iov_config, *driver_config;
651 int error;
652
653 device_config = NULL;
654 iov_config = NULL;
655 driver_config = NULL;
656
657 device_schema = nvlist_get_nvlist(schema, schema_device);
658 iov_schema = nvlist_get_nvlist(device_schema, IOV_CONFIG_NAME);
659 driver_schema = nvlist_get_nvlist(device_schema, DRIVER_CONFIG_NAME);
660
661 device_config = dnvlist_take_nvlist(config, config_device, NULL);
662 if (device_config == NULL) {
663 error = EINVAL;
664 goto out;
665 }
666
667 iov_config = dnvlist_take_nvlist(device_config, IOV_CONFIG_NAME, NULL);
668 if (iov_config == NULL) {
669 error = EINVAL;
670 goto out;
671 }
672
673 driver_config = dnvlist_take_nvlist(device_config, DRIVER_CONFIG_NAME,
674 NULL);
675 if (driver_config == NULL) {
676 error = EINVAL;
677 goto out;
678 }
679
680 error = pci_iov_schema_validate_required(iov_schema, iov_config);
681 if (error != 0)
682 goto out;
683
684 error = pci_iov_schema_validate_required(driver_schema, driver_config);
685 if (error != 0)
686 goto out;
687
688 error = pci_iov_schema_validate_types(iov_schema, iov_config);
689 if (error != 0)
690 goto out;
691
692 error = pci_iov_schema_validate_types(driver_schema, driver_config);
693 if (error != 0)
694 goto out;
695
696 out:
697 /* Note that these functions handle NULL pointers safely. */
698 nvlist_move_nvlist(device_config, IOV_CONFIG_NAME, iov_config);
699 nvlist_move_nvlist(device_config, DRIVER_CONFIG_NAME, driver_config);
700 nvlist_move_nvlist(config, config_device, device_config);
701
702 return (error);
703 }
704
705 static int
pci_iov_schema_validate_vfs(const nvlist_t * schema,nvlist_t * config,uint16_t num_vfs)706 pci_iov_schema_validate_vfs(const nvlist_t *schema, nvlist_t *config,
707 uint16_t num_vfs)
708 {
709 char device[VF_MAX_NAME];
710 int i, error;
711
712 for (i = 0; i < num_vfs; i++) {
713 snprintf(device, sizeof(device), VF_PREFIX"%d", i);
714
715 error = pci_iov_schema_validate_device(schema, config,
716 VF_SCHEMA_NAME, device);
717 if (error != 0)
718 return (error);
719 }
720
721 return (0);
722 }
723
724 /*
725 * Validate that the device node only has IOV and DRIVER subnodes.
726 */
727 static int
pci_iov_schema_validate_device_subsystems(const nvlist_t * config)728 pci_iov_schema_validate_device_subsystems(const nvlist_t *config)
729 {
730 void *cookie;
731 const char *name;
732 int type;
733
734 cookie = NULL;
735 while ((name = nvlist_next(config, &type, &cookie)) != NULL) {
736 if (strcasecmp(name, IOV_CONFIG_NAME) == 0)
737 continue;
738 else if (strcasecmp(name, DRIVER_CONFIG_NAME) == 0)
739 continue;
740
741 return (EINVAL);
742 }
743
744 return (0);
745 }
746
747 /*
748 * Validate that the string is a valid device node name. It must either be "PF"
749 * or "VF-n", where n is an integer in the range [0, num_vfs).
750 */
751 static int
pci_iov_schema_validate_dev_name(const char * name,uint16_t num_vfs)752 pci_iov_schema_validate_dev_name(const char *name, uint16_t num_vfs)
753 {
754 const char *number_start;
755 char *endp;
756 u_long vf_num;
757
758 if (strcasecmp(PF_CONFIG_NAME, name) == 0)
759 return (0);
760
761 /* Ensure that we start with "VF-" */
762 if (strncasecmp(name, VF_PREFIX, VF_PREFIX_LEN) != 0)
763 return (EINVAL);
764
765 number_start = name + VF_PREFIX_LEN;
766
767 /* Filter out name == "VF-" (no number) */
768 if (number_start[0] == '\0')
769 return (EINVAL);
770
771 /* Disallow leading whitespace or +/- */
772 if (!isdigit(number_start[0]))
773 return (EINVAL);
774
775 vf_num = strtoul(number_start, &endp, 10);
776 if (*endp != '\0')
777 return (EINVAL);
778
779 /* Disallow leading zeros on VF-[1-9][0-9]* */
780 if (vf_num != 0 && number_start[0] == '0')
781 return (EINVAL);
782
783 /* Disallow leading zeros on VF-0 */
784 if (vf_num == 0 && number_start[1] != '\0')
785 return (EINVAL);
786
787 if (vf_num >= num_vfs)
788 return (EINVAL);
789
790 return (0);
791 }
792
793 /*
794 * Validate that there are no device nodes in config other than the ones for
795 * the PF and the VFs. This includes validating that all config nodes of the
796 * form VF-n specify a VF number that is < num_vfs.
797 */
798 static int
pci_iov_schema_validate_device_names(const nvlist_t * config,uint16_t num_vfs)799 pci_iov_schema_validate_device_names(const nvlist_t *config, uint16_t num_vfs)
800 {
801 const nvlist_t *device;
802 void *cookie;
803 const char *name;
804 int type, error;
805
806 cookie = NULL;
807 while ((name = nvlist_next(config, &type, &cookie)) != NULL) {
808 error = pci_iov_schema_validate_dev_name(name, num_vfs);
809 if (error != 0)
810 return (error);
811
812 /*
813 * Note that as this is a valid PF/VF node, we know that
814 * pci_iov_schema_validate_device() has already checked that
815 * the PF/VF node is an nvlist.
816 */
817 device = nvlist_get_nvlist(config, name);
818 error = pci_iov_schema_validate_device_subsystems(device);
819 if (error != 0)
820 return (error);
821 }
822
823 return (0);
824 }
825
826 int
pci_iov_schema_validate_config(const nvlist_t * schema,nvlist_t * config)827 pci_iov_schema_validate_config(const nvlist_t *schema, nvlist_t *config)
828 {
829 int error;
830 uint16_t num_vfs;
831
832 error = pci_iov_schema_validate_device(schema, config, PF_CONFIG_NAME,
833 PF_CONFIG_NAME);
834 if (error != 0)
835 return (error);
836
837 num_vfs = pci_iov_config_get_num_vfs(config);
838
839 error = pci_iov_schema_validate_vfs(schema, config, num_vfs);
840 if (error != 0)
841 return (error);
842
843 return (pci_iov_schema_validate_device_names(config, num_vfs));
844 }
845
846 /*
847 * Return value of the num_vfs parameter. config must have already been
848 * validated, which guarantees that the parameter exists.
849 */
850 uint16_t
pci_iov_config_get_num_vfs(const nvlist_t * config)851 pci_iov_config_get_num_vfs(const nvlist_t *config)
852 {
853 const nvlist_t *pf, *iov;
854
855 pf = nvlist_get_nvlist(config, PF_CONFIG_NAME);
856 iov = nvlist_get_nvlist(pf, IOV_CONFIG_NAME);
857 return (nvlist_get_number(iov, "num_vfs"));
858 }
859
860 /* Allocate a new empty schema node. */
861 nvlist_t *
pci_iov_schema_alloc_node(void)862 pci_iov_schema_alloc_node(void)
863 {
864
865 return (nvlist_create(NV_FLAG_IGNORE_CASE));
866 }
867