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