/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2024 Oxide Computer Company */ #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "in_guest.h" #include "viona_suite.h" #define PARAM_BUF_SZ VIONA_MAX_PARAM_NVLIST_SZ const char *expected_params[] = { "tx_copy_data", "tx_header_pad" }; static void print_errors(vioc_set_params_t *vsp) { if (vsp->vsp_error_sz == 0) { return; } nvlist_t *nverr = NULL; if (nvlist_unpack(vsp->vsp_error, vsp->vsp_error_sz, &nverr, 0) != 0) { return; } (void) fprintf(stderr, "vioc_set_params errors:\n"); nvlist_print(stderr, nverr); nvlist_free(nverr); } static void test_set_param_errors(int vfd) { vioc_set_params_t set_param = { .vsp_param_sz = VIONA_MAX_PARAM_NVLIST_SZ + 1, }; if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) == 0) { test_fail_msg("SET_PARAMS should fail for too-big size"); } char bogus_nvlist[256]; arc4random_buf(bogus_nvlist, sizeof (bogus_nvlist)); set_param.vsp_param = bogus_nvlist; set_param.vsp_param_sz = sizeof (bogus_nvlist); if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) == 0) { test_fail_msg("SET_PARAMS should fail invalid nvlist"); } /* * Assemble parameters which should be rejected: * - One of the wrong nvpair data type * - A tx_header_pad outside the valid range * - A wholly unrecognized field */ nvlist_t *nvl = fnvlist_alloc(); fnvlist_add_uint32(nvl, "tx_copy_data", 0); fnvlist_add_uint16(nvl, "tx_header_pad", UINT16_MAX); fnvlist_add_boolean_value(nvl, "widdly_scuds", false); uint8_t errbuf[512]; set_param.vsp_param = fnvlist_pack(nvl, &set_param.vsp_param_sz); set_param.vsp_error = errbuf; set_param.vsp_error_sz = sizeof (errbuf); if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) == 0) { test_fail_msg("SET_PARAMS should fail on invalid params"); } nvlist_free(nvl); free(set_param.vsp_param); nvlist_t *error_nvl = fnvlist_unpack(set_param.vsp_error, set_param.vsp_error_sz); const char *err_params[] = { "tx_copy_data", "tx_header_pad", "widdly_scuds" }; for (uint_t i = 0; i < ARRAY_SIZE(err_params); i++) { const char *name = err_params[i]; if (!nvlist_exists(error_nvl, name)) { print_errors(&set_param); test_fail_msg("missing SET_PARAMS error for field %s\n", name); } } nvlist_free(error_nvl); } int main(int argc, char *argv[]) { const char *suite_name = basename(argv[0]); struct vmctx *ctx; ctx = test_initialize_plain(suite_name); if (ctx == NULL) { test_fail_errno(errno, "could not open test VM"); } int vfd = open_viona(); if (vfd < 0) { test_fail_errno(errno, "could not open viona device"); } /* * Getting default parameters should work before the viona device is * associated with a link and vmm */ void *param_buf = malloc(PARAM_BUF_SZ); if (param_buf == NULL) { test_fail_errno(errno, "could not allocate param buffer"); } vioc_get_params_t get_param = { .vgp_param = param_buf, .vgp_param_sz = PARAM_BUF_SZ, }; if (ioctl(vfd, VNA_IOC_DEFAULT_PARAMS, &get_param) != 0) { test_fail_errno(errno, "ioctl(VNA_IOC_DEFAULT_PARAMS) failed"); } nvlist_t *params = NULL; if (nvlist_unpack(param_buf, get_param.vgp_param_sz, ¶ms, 0) != 0) { test_fail_errno(errno, "nvlist_unpack() failed"); } /* Are all the presented default parameters ones we expect? */ nvpair_t *nvp = NULL; while ((nvp = nvlist_next_nvpair(params, nvp)) != NULL) { bool found = false; const char *pname = nvpair_name(nvp); for (uint_t i = 0; i < ARRAY_SIZE(expected_params); i++) { if (strcmp(pname, expected_params[i]) == 0) { found = true; break; } } if (!found) { test_fail_msg("unexpected parameter %s", pname); } } datalink_id_t dlid; dladm_status_t dls = query_dlid(VIONA_TEST_IFACE_NAME, &dlid); if (dls != DLADM_STATUS_OK) { char errbuf[DLADM_STRSIZE]; test_fail_msg("could not query datalink id for %s: %s", VIONA_TEST_IFACE_NAME, dladm_status2str(dls, errbuf)); } vioc_create_t create_ioc = { .c_linkid = dlid, .c_vmfd = vm_get_device_fd(ctx), }; if (ioctl(vfd, VNA_IOC_CREATE, &create_ioc) != 0) { test_fail_errno(errno, "failed to create link on viona device"); } /* * Based on the parameters we got from the defaults, build a new set of * parameters to set on the link which are slighly different. */ nvlist_t *new_params = fnvlist_alloc(); fnvlist_add_boolean_value(new_params, "tx_copy_data", !fnvlist_lookup_boolean_value(params, "tx_copy_data")); fnvlist_add_uint16(new_params, "tx_header_pad", fnvlist_lookup_uint16(params, "tx_header_pad") + 32); uint8_t errbuf[256]; vioc_set_params_t set_param = { .vsp_error = errbuf, .vsp_error_sz = sizeof (errbuf) }; if (nvlist_pack(new_params, (char **)&set_param.vsp_param, &set_param.vsp_param_sz, NV_ENCODE_NATIVE, 0) != 0) { test_fail_errno(errno, "nvlist_pack() failed"); } nvlist_free(params); nvlist_free(new_params); if (ioctl(vfd, VNA_IOC_SET_PARAMS, &set_param) != 0) { print_errors(&set_param); test_fail_errno(errno, "ioctl(VNA_IOC_SET_PARAMS) failed"); } free(set_param.vsp_param); test_set_param_errors(vfd); test_pass(); return (EXIT_SUCCESS); }