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