xref: /freebsd/sys/dev/pci/pci_iov_schema.c (revision db33c6f3ae9d1231087710068ee4ea5398aacca7)
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
923 pci_iov_schema_alloc_node(void)
924 {
925 
926 	return (nvlist_create(NV_FLAG_IGNORE_CASE));
927 }
928