1/*-
2 * Copyright (c) 2013-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: releng/11.0/usr.sbin/iovctl/iovctl.c 296865 2016-03-14 17:41:17Z rstone $");
29
30#include <sys/param.h>
31#include <sys/iov.h>
32#include <sys/dnv.h>
33#include <sys/nv.h>
34
35#include <err.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <regex.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "iovctl.h"
45
46static void	config_action(const char *filename, int dryrun);
47static void	delete_action(const char *device, int dryrun);
48static void	print_schema(const char *device);
49
50/*
51 * Fetch the config schema from the kernel via ioctl.  This function has to
52 * call the ioctl twice: the first returns the amount of memory that we need
53 * to allocate for the schema, and the second actually fetches the schema.
54 */
55static nvlist_t *
56get_schema(int fd)
57{
58	struct pci_iov_schema arg;
59	nvlist_t *schema;
60	int error;
61
62	/* Do the ioctl() once to fetch the size of the schema. */
63	arg.schema = NULL;
64	arg.len = 0;
65	arg.error = 0;
66	error = ioctl(fd, IOV_GET_SCHEMA, &arg);
67	if (error != 0)
68		err(1, "Could not fetch size of config schema");
69
70	arg.schema = malloc(arg.len);
71	if (arg.schema == NULL)
72		err(1, "Could not allocate %zu bytes for schema",
73		    arg.len);
74
75	/* Now do the ioctl() for real to get the schema. */
76	error = ioctl(fd, IOV_GET_SCHEMA, &arg);
77	if (error != 0 || arg.error != 0) {
78		if (arg.error != 0)
79			errno = arg.error;
80		err(1, "Could not fetch config schema");
81	}
82
83	schema = nvlist_unpack(arg.schema, arg.len, NV_FLAG_IGNORE_CASE);
84	if (schema == NULL)
85		err(1, "Could not unpack schema");
86
87	free(arg.schema);
88	return (schema);
89}
90
91/*
92 * Call the ioctl that activates SR-IOV and creates the VFs.
93 */
94static void
95config_iov(int fd, const char *dev_name, const nvlist_t *config, int dryrun)
96{
97	struct pci_iov_arg arg;
98	int error;
99
100	arg.config = nvlist_pack(config, &arg.len);
101	if (arg.config == NULL)
102		err(1, "Could not pack configuration");
103
104	if (dryrun) {
105		printf("Would enable SR-IOV on device '%s'.\n", dev_name);
106		printf(
107		    "The following configuration parameters would be used:\n");
108		nvlist_fdump(config, stdout);
109		printf(
110		"The configuration parameters consume %zu bytes when packed.\n",
111		    arg.len);
112	} else {
113		error = ioctl(fd, IOV_CONFIG, &arg);
114		if (error != 0)
115			err(1, "Failed to configure SR-IOV");
116	}
117
118	free(arg.config);
119}
120
121static int
122open_device(const char *dev_name)
123{
124	char *dev;
125	int fd;
126	size_t copied, size;
127	long path_max;
128
129	path_max = pathconf("/dev", _PC_PATH_MAX);
130	if (path_max < 0)
131		err(1, "Could not get maximum path length");
132
133	size = path_max;
134	dev = malloc(size);
135	if (dev == NULL)
136		err(1, "Could not allocate memory for device path");
137
138	if (dev_name[0] == '/')
139		copied = strlcpy(dev, dev_name, size);
140	else
141		copied = snprintf(dev, size, "/dev/iov/%s", dev_name);
142
143	/* >= to account for null terminator. */
144	if (copied >= size)
145		errx(1, "Provided file name too long");
146
147	fd = open(dev, O_RDWR);
148	if (fd < 0)
149		err(1, "Could not open device '%s'", dev);
150
151	free(dev);
152	return (fd);
153}
154
155static void
156usage(void)
157{
158
159	warnx("Usage: iovctl -C -f <config file> [-n]");
160	warnx("       iovctl -D [-d <PF device> | -f <config file>] [-n]");
161	warnx("       iovctl -S [-d <PF device> | -f <config file>]");
162	exit(1);
163
164}
165
166enum main_action {
167	NONE,
168	CONFIG,
169	DELETE,
170	PRINT_SCHEMA,
171};
172
173int
174main(int argc, char **argv)
175{
176	char *device;
177	const char *filename;
178	int ch, dryrun;
179	enum main_action action;
180
181	device = NULL;
182	filename = NULL;
183	dryrun = 0;
184	action = NONE;
185
186	while ((ch = getopt(argc, argv, "Cd:Df:nS")) != -1) {
187		switch (ch) {
188		case 'C':
189			if (action != NONE) {
190				warnx(
191				   "Only one of -C, -D or -S may be specified");
192				usage();
193			}
194			action = CONFIG;
195			break;
196		case 'd':
197			device = strdup(optarg);
198			break;
199		case 'D':
200			if (action != NONE) {
201				warnx(
202				   "Only one of -C, -D or -S may be specified");
203				usage();
204			}
205			action = DELETE;
206			break;
207		case 'f':
208			filename = optarg;
209			break;
210		case 'n':
211			dryrun = 1;
212			break;
213		case 'S':
214			if (action != NONE) {
215				warnx(
216				   "Only one of -C, -D or -S may be specified");
217				usage();
218			}
219			action = PRINT_SCHEMA;
220			break;
221		case '?':
222			warnx("Unrecognized argument '-%c'\n", optopt);
223			usage();
224			break;
225		}
226	}
227
228	if (device != NULL && filename != NULL) {
229		warnx("Only one of the -d and -f flags may be specified");
230		usage();
231	}
232
233	if (device == NULL && filename == NULL) {
234		warnx("Either the -d or -f flag must be specified");
235		usage();
236	}
237
238	switch (action) {
239	case CONFIG:
240		if (filename == NULL) {
241			warnx("-d flag cannot be used with the -C flag");
242			usage();
243		}
244		config_action(filename, dryrun);
245		break;
246	case DELETE:
247		if (device == NULL)
248			device = find_device(filename);
249		delete_action(device, dryrun);
250		free(device);
251		break;
252	case PRINT_SCHEMA:
253		if (dryrun) {
254			warnx("-n flag cannot be used with the -S flag");
255			usage();
256		}
257		if (device == NULL)
258			device = find_device(filename);
259		print_schema(device);
260		free(device);
261		break;
262	default:
263		usage();
264		break;
265	}
266
267	exit(0);
268}
269
270static void
271config_action(const char *filename, int dryrun)
272{
273	char *dev;
274	nvlist_t *schema, *config;
275	int fd;
276
277	dev = find_device(filename);
278	fd = open(dev, O_RDWR);
279	if (fd < 0)
280		err(1, "Could not open device '%s'", dev);
281
282	schema = get_schema(fd);
283	config = parse_config_file(filename, schema);
284	if (config == NULL)
285		errx(1, "Could not parse config");
286
287	config_iov(fd, dev, config, dryrun);
288
289	nvlist_destroy(config);
290	nvlist_destroy(schema);
291	free(dev);
292	close(fd);
293}
294
295static void
296delete_action(const char *dev_name, int dryrun)
297{
298	int fd, error;
299
300	fd = open_device(dev_name);
301
302	if (dryrun)
303		printf("Would attempt to delete all VF children of '%s'\n",
304		    dev_name);
305	else {
306		error = ioctl(fd, IOV_DELETE);
307		if (error != 0)
308			err(1, "Failed to delete VFs");
309	}
310
311	close(fd);
312}
313
314static void
315print_default_value(const nvlist_t *parameter, const char *type)
316{
317	const uint8_t *mac;
318	size_t size;
319
320	if (strcasecmp(type, "bool") == 0)
321		printf(" (default = %s)",
322		    nvlist_get_bool(parameter, DEFAULT_SCHEMA_NAME) ? "true" :
323		    "false");
324	else if (strcasecmp(type, "string") == 0)
325		printf(" (default = %s)",
326		    nvlist_get_string(parameter, DEFAULT_SCHEMA_NAME));
327	else if (strcasecmp(type, "uint8_t") == 0)
328		printf(" (default = %ju)",
329		    (uintmax_t)nvlist_get_number(parameter,
330		    DEFAULT_SCHEMA_NAME));
331	else if (strcasecmp(type, "uint16_t") == 0)
332		printf(" (default = %ju)",
333		    (uintmax_t)nvlist_get_number(parameter,
334		    DEFAULT_SCHEMA_NAME));
335	else if (strcasecmp(type, "uint32_t") == 0)
336		printf(" (default = %ju)",
337		    (uintmax_t)nvlist_get_number(parameter,
338		    DEFAULT_SCHEMA_NAME));
339	else if (strcasecmp(type, "uint64_t") == 0)
340		printf(" (default = %ju)",
341		    (uintmax_t)nvlist_get_number(parameter,
342		    DEFAULT_SCHEMA_NAME));
343	else if (strcasecmp(type, "unicast-mac") == 0) {
344		mac = nvlist_get_binary(parameter, DEFAULT_SCHEMA_NAME, &size);
345		printf(" (default = %02x:%02x:%02x:%02x:%02x:%02x)", mac[0],
346		    mac[1], mac[2], mac[3], mac[4], mac[5]);
347	} else
348		errx(1, "Unexpected type in schema: '%s'", type);
349}
350
351static void
352print_subsystem_schema(const nvlist_t * subsystem_schema)
353{
354	const char *name, *type;
355	const nvlist_t *parameter;
356	void *it;
357	int nvtype;
358
359	it = NULL;
360	while ((name = nvlist_next(subsystem_schema, &nvtype, &it)) != NULL) {
361		parameter = nvlist_get_nvlist(subsystem_schema, name);
362		type = nvlist_get_string(parameter, TYPE_SCHEMA_NAME);
363
364		printf("\t%s : %s", name, type);
365		if (dnvlist_get_bool(parameter, REQUIRED_SCHEMA_NAME, false))
366			printf(" (required)");
367		else if (nvlist_exists(parameter, DEFAULT_SCHEMA_NAME))
368			print_default_value(parameter, type);
369		else
370			printf(" (optional)");
371		printf("\n");
372	}
373}
374
375static void
376print_schema(const char *dev_name)
377{
378	nvlist_t *schema;
379	const nvlist_t *iov_schema, *driver_schema, *pf_schema, *vf_schema;
380	int fd;
381
382	fd = open_device(dev_name);
383	schema = get_schema(fd);
384
385	pf_schema = nvlist_get_nvlist(schema, PF_CONFIG_NAME);
386	iov_schema = nvlist_get_nvlist(pf_schema, IOV_CONFIG_NAME);
387	driver_schema = nvlist_get_nvlist(pf_schema, DRIVER_CONFIG_NAME);
388	printf(
389"The following configuration parameters may be configured on the PF:\n");
390	print_subsystem_schema(iov_schema);
391	print_subsystem_schema(driver_schema);
392
393	vf_schema = nvlist_get_nvlist(schema, VF_SCHEMA_NAME);
394	iov_schema = nvlist_get_nvlist(vf_schema, IOV_CONFIG_NAME);
395	driver_schema = nvlist_get_nvlist(vf_schema, DRIVER_CONFIG_NAME);
396	printf(
397"\nThe following configuration parameters may be configured on a VF:\n");
398	print_subsystem_schema(iov_schema);
399	print_subsystem_schema(driver_schema);
400
401	nvlist_destroy(schema);
402	close(fd);
403}
404