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