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