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
47struct config_type_validator;
48typedef int (validate_func)(const struct config_type_validator *,
49   const nvlist_t *, const char *name);
50typedef int (default_validate_t)(const struct config_type_validator *,
51   const nvlist_t *);
52
53static validate_func pci_iov_schema_validate_bool;
54static validate_func pci_iov_schema_validate_string;
55static validate_func pci_iov_schema_validate_uint;
56static validate_func pci_iov_schema_validate_unicast_mac;
57
58static default_validate_t pci_iov_validate_bool_default;
59static default_validate_t pci_iov_validate_string_default;
60static default_validate_t pci_iov_validate_uint_default;
61static default_validate_t pci_iov_validate_unicast_mac_default;
62
63struct config_type_validator {
64	const char *type_name;
65	validate_func *validate;
66	default_validate_t *default_validate;
67	uintmax_t limit;
68};
69
70static 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
112static const struct config_type_validator *
113pci_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
127static void
128pci_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
138static void
139pci_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
152void
153pci_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
172void
173pci_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
192static void
193pci_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
212void
213pci_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
220void
221pci_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
228void
229pci_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
236void
237pci_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
244void
245pci_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
264static int
265pci_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
274static int
275pci_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
284static int
285pci_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
301static int
302pci_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
323static void
324pci_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
349static int
350pci_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
359static int
360pci_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
369static int
370pci_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
384static int
385pci_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
403static int
404pci_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
439static int
440pci_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 */
469static int
470pci_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 */
493static int
494pci_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
510static int
511pci_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. */
537static int
538pci_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
554int
555pci_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 */
575static int
576pci_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
600static int
601pci_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 */
620static int
621pci_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
645static int
646pci_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
696out:
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
705static int
706pci_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 */
727static int
728pci_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 */
751static int
752pci_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 */
798static int
799pci_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
826int
827pci_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 */
850uint16_t
851pci_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. */
861nvlist_t *
862pci_iov_schema_alloc_node(void)
863{
864
865	return (nvlist_create(NV_FLAG_IGNORE_CASE));
866}
867