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, ¶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