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__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
50struct config_type_validator;
51typedef int (validate_func)(const struct config_type_validator *,
52   const nvlist_t *, const char *name);
53typedef int (default_validate_t)(const struct config_type_validator *,
54   const nvlist_t *);
55
56static validate_func pci_iov_schema_validate_bool;
57static validate_func pci_iov_schema_validate_string;
58static validate_func pci_iov_schema_validate_uint;
59static validate_func pci_iov_schema_validate_unicast_mac;
60
61static default_validate_t pci_iov_validate_bool_default;
62static default_validate_t pci_iov_validate_string_default;
63static default_validate_t pci_iov_validate_uint_default;
64static default_validate_t pci_iov_validate_unicast_mac_default;
65
66struct config_type_validator {
67	const char *type_name;
68	validate_func *validate;
69	default_validate_t *default_validate;
70	uintmax_t limit;
71};
72
73static 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
115static const struct config_type_validator *
116pci_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
130static void
131pci_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
141static void
142pci_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
155void
156pci_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
175void
176pci_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
195static void
196pci_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
215void
216pci_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
223void
224pci_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
231void
232pci_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
239void
240pci_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
247void
248pci_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
267static int
268pci_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
277static int
278pci_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
287static int
288pci_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
304static int
305pci_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
326static void
327pci_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
352static int
353pci_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
362static int
363pci_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
372static int
373pci_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
387static int
388pci_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
406static int
407pci_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
442static int
443pci_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 */
472static int
473pci_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 */
496static int
497pci_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
513static int
514pci_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. */
540static int
541pci_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
557int
558pci_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 */
578static int
579pci_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
603static int
604pci_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 */
623static int
624pci_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
648static int
649pci_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
699out:
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
708static int
709pci_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 */
730static int
731pci_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 */
754static int
755pci_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 */
801static int
802pci_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
829int
830pci_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 */
853uint16_t
854pci_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. */
864nvlist_t *
865pci_iov_schema_alloc_node(void)
866{
867
868	return (nvlist_create(NV_FLAG_IGNORE_CASE));
869}
870