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