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
print_errors(vioc_set_params_t * vsp)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
test_set_param_errors(int vfd)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
main(int argc,char * argv[])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, ¶ms, 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