xref: /illumos-gate/usr/src/test/bhyve-tests/tests/viona/link_params.c (revision 616c76953410e11aab663c510c9ba143e545bd4b)
1 
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * http://www.illumos.org/license/CDDL.
11  */
12 
13 /*
14  * Copyright 2024 Oxide Computer Company
15  */
16 
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <fcntl.h>
21 #include <libgen.h>
22 #include <strings.h>
23 #include <libnvpair.h>
24 #include <sys/sysmacros.h>
25 
26 #include <sys/vmm.h>
27 #include <sys/viona_io.h>
28 #include <vmmapi.h>
29 
30 #include "common.h"
31 #include "in_guest.h"
32 #include "viona_suite.h"
33 
34 #define	PARAM_BUF_SZ	VIONA_MAX_PARAM_NVLIST_SZ
35 
36 const char *expected_params[] = {
37 	"tx_copy_data",
38 	"tx_header_pad"
39 };
40 
41 static void
42 print_errors(vioc_set_params_t *vsp)
43 {
44 	if (vsp->vsp_error_sz == 0) {
45 		return;
46 	}
47 
48 	nvlist_t *nverr = NULL;
49 	if (nvlist_unpack(vsp->vsp_error, vsp->vsp_error_sz, &nverr, 0) != 0) {
50 		return;
51 	}
52 
53 	(void) fprintf(stderr, "vioc_set_params errors:\n");
54 	nvlist_print(stderr, nverr);
55 
56 	nvlist_free(nverr);
57 }
58 
59 static void
60 test_set_param_errors(int vfd)
61 {
62 	vioc_set_params_t set_param = {
63 		.vsp_param_sz = VIONA_MAX_PARAM_NVLIST_SZ + 1,
64 	};
65 
66 	if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) == 0) {
67 		test_fail_msg("SET_PARAMS should fail for too-big size");
68 	}
69 
70 	char bogus_nvlist[256];
71 	arc4random_buf(bogus_nvlist, sizeof (bogus_nvlist));
72 	set_param.vsp_param = bogus_nvlist;
73 	set_param.vsp_param_sz = sizeof (bogus_nvlist);
74 	if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) == 0) {
75 		test_fail_msg("SET_PARAMS should fail invalid nvlist");
76 	}
77 
78 	/*
79 	 * Assemble parameters which should be rejected:
80 	 * - One of the wrong nvpair data type
81 	 * - A tx_header_pad outside the valid range
82 	 * - A wholly unrecognized field
83 	 */
84 	nvlist_t *nvl = fnvlist_alloc();
85 	fnvlist_add_uint32(nvl, "tx_copy_data", 0);
86 	fnvlist_add_uint16(nvl, "tx_header_pad", UINT16_MAX);
87 	fnvlist_add_boolean_value(nvl, "widdly_scuds", false);
88 
89 	uint8_t errbuf[512];
90 	set_param.vsp_param = fnvlist_pack(nvl, &set_param.vsp_param_sz);
91 	set_param.vsp_error = errbuf;
92 	set_param.vsp_error_sz = sizeof (errbuf);
93 	if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) == 0) {
94 		test_fail_msg("SET_PARAMS should fail on invalid params");
95 	}
96 	nvlist_free(nvl);
97 	free(set_param.vsp_param);
98 
99 	nvlist_t *error_nvl =
100 	    fnvlist_unpack(set_param.vsp_error, set_param.vsp_error_sz);
101 	const char *err_params[] = {
102 		"tx_copy_data",
103 		"tx_header_pad",
104 		"widdly_scuds"
105 	};
106 	for (uint_t i = 0; i < ARRAY_SIZE(err_params); i++) {
107 		const char *name = err_params[i];
108 
109 		if (!nvlist_exists(error_nvl, name)) {
110 			print_errors(&set_param);
111 			test_fail_msg("missing SET_PARAMS error for field %s\n",
112 			    name);
113 		}
114 	}
115 	nvlist_free(error_nvl);
116 }
117 
118 int
119 main(int argc, char *argv[])
120 {
121 	const char *suite_name = basename(argv[0]);
122 	struct vmctx *ctx;
123 
124 	ctx = test_initialize_plain(suite_name);
125 	if (ctx == NULL) {
126 		test_fail_errno(errno, "could not open test VM");
127 	}
128 
129 	int vfd = open_viona();
130 	if (vfd < 0) {
131 		test_fail_errno(errno, "could not open viona device");
132 	}
133 
134 	/*
135 	 * Getting default parameters should work before the viona device is
136 	 * associated with a link and vmm
137 	 */
138 
139 	void *param_buf = malloc(PARAM_BUF_SZ);
140 	if (param_buf == NULL) {
141 		test_fail_errno(errno, "could not allocate param buffer");
142 	}
143 	vioc_get_params_t get_param = {
144 		.vgp_param = param_buf,
145 		.vgp_param_sz = PARAM_BUF_SZ,
146 	};
147 	if (ioctl(vfd, VNA_IOC_DEFAULT_PARAMS, &get_param) != 0) {
148 		test_fail_errno(errno, "ioctl(VNA_IOC_DEFAULT_PARAMS) failed");
149 	}
150 
151 	nvlist_t *params = NULL;
152 	if (nvlist_unpack(param_buf, get_param.vgp_param_sz, &params, 0) != 0) {
153 		test_fail_errno(errno, "nvlist_unpack() failed");
154 	}
155 
156 	/* Are all the presented default parameters ones we expect? */
157 	nvpair_t *nvp = NULL;
158 	while ((nvp = nvlist_next_nvpair(params, nvp)) != NULL) {
159 		bool found = false;
160 		const char *pname = nvpair_name(nvp);
161 
162 		for (uint_t i = 0; i < ARRAY_SIZE(expected_params); i++) {
163 			if (strcmp(pname, expected_params[i]) == 0) {
164 				found = true;
165 				break;
166 			}
167 		}
168 		if (!found) {
169 			test_fail_msg("unexpected parameter %s", pname);
170 		}
171 	}
172 
173 	datalink_id_t dlid;
174 	dladm_status_t dls = query_dlid(VIONA_TEST_IFACE_NAME, &dlid);
175 	if (dls != DLADM_STATUS_OK) {
176 		char errbuf[DLADM_STRSIZE];
177 
178 		test_fail_msg("could not query datalink id for %s: %s",
179 		    VIONA_TEST_IFACE_NAME, dladm_status2str(dls, errbuf));
180 	}
181 
182 	vioc_create_t create_ioc = {
183 		.c_linkid = dlid,
184 		.c_vmfd = vm_get_device_fd(ctx),
185 	};
186 	if (ioctl(vfd, VNA_IOC_CREATE, &create_ioc) != 0) {
187 		test_fail_errno(errno, "failed to create link on viona device");
188 	}
189 
190 	/*
191 	 * Based on the parameters we got from the defaults, build a new set of
192 	 * parameters to set on the link which are slighly different.
193 	 */
194 	nvlist_t *new_params = fnvlist_alloc();
195 	fnvlist_add_boolean_value(new_params, "tx_copy_data",
196 	    !fnvlist_lookup_boolean_value(params, "tx_copy_data"));
197 	fnvlist_add_uint16(new_params, "tx_header_pad",
198 	    fnvlist_lookup_uint16(params, "tx_header_pad") + 32);
199 
200 	uint8_t errbuf[256];
201 	vioc_set_params_t set_param = {
202 		.vsp_error = errbuf,
203 		.vsp_error_sz = sizeof (errbuf)
204 	};
205 	if (nvlist_pack(new_params, (char **)&set_param.vsp_param,
206 	    &set_param.vsp_param_sz, NV_ENCODE_NATIVE, 0) != 0) {
207 		test_fail_errno(errno, "nvlist_pack() failed");
208 	}
209 	nvlist_free(params);
210 	nvlist_free(new_params);
211 
212 	if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) != 0) {
213 		print_errors(&set_param);
214 		test_fail_errno(errno, "ioctl(VNA_IOC_SET_PARAMS) failed");
215 	}
216 	free(set_param.vsp_param);
217 
218 	test_set_param_errors(vfd);
219 
220 	test_pass();
221 	return (EXIT_SUCCESS);
222 }
223