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