1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 /* 17 * This file contains shared pieces of the NVMe field validation logic and has 18 * shared pieces that are used between different parts. 19 */ 20 21 #include "nvme_common.h" 22 23 #include <sys/sysmacros.h> 24 #ifdef _KERNEL 25 #include <sys/sunddi.h> 26 #include <sys/stdint.h> 27 #else 28 #include <stdio.h> 29 #include <inttypes.h> 30 #endif 31 32 bool 33 nvme_field_atleast(const nvme_valid_ctrl_data_t *data, 34 const nvme_version_t *targ) 35 { 36 return (nvme_vers_atleast(data->vcd_vers, targ)); 37 } 38 39 /* 40 * Note, we rely on external logic to determine if the broadcast nsid is valid. 41 * We always accept it. 42 */ 43 bool 44 nvme_field_valid_nsid(const nvme_field_info_t *field, 45 const nvme_valid_ctrl_data_t *data, uint64_t nsid, char *msg, size_t msglen) 46 { 47 if ((nsid != 0 && nsid <= data->vcd_id->id_nn) || 48 nsid == NVME_NSID_BCAST) { 49 return (true); 50 } 51 52 (void) snprintf(msg, msglen, "namespace id %" PRIu64 "is outside the " 53 "valid range [0x%x, 0x%x], the broadcast nsid (0x%x) may be valid", 54 nsid, NVME_NSID_MIN, NVME_NSID_BCAST, data->vcd_id->id_nn); 55 return (false); 56 } 57 58 bool 59 nvme_field_range_check(const nvme_field_info_t *field, uint64_t min, 60 uint64_t max, char *msg, size_t msglen, uint64_t value) 61 { 62 if (value >= min && value <= max) { 63 return (true); 64 } 65 66 (void) snprintf(msg, msglen, "field %s (%s) value 0x%" 67 PRIx64 " is outside the valid range: [0x%" PRIx64 ", 0x%" PRIx64 68 "]", field->nlfi_human, field->nlfi_spec, value, min, max); 69 return (false); 70 } 71 72 /* 73 * This is a general validation function for fields that are part of a command. 74 * It will check if the field is supported by the controller and if so, that its 75 * value is within the expected range. On error, an optional message will be 76 * written that explains the error. This is intended to be shared between 77 * userland and the kernel. The kernel should pass NULL/0 for msg/msglen because 78 * there is no message translation capability in the kernel. 79 */ 80 nvme_field_error_t 81 nvme_field_validate(const nvme_field_info_t *field, 82 const nvme_valid_ctrl_data_t *data, uint64_t value, char *msg, 83 size_t msglen) 84 { 85 ASSERT3P(field->nlfi_vers, !=, NULL); 86 87 if (msglen > 0) 88 *msg = '\0'; 89 90 if (!nvme_field_atleast(data, field->nlfi_vers)) { 91 (void) snprintf(msg, msglen, "field %s (%s) requires " 92 "version %u.%u, but device is at %u.%u", field->nlfi_human, 93 field->nlfi_spec, data->vcd_vers->v_major, 94 data->vcd_vers->v_minor, field->nlfi_vers->v_major, 95 field->nlfi_vers->v_minor); 96 return (NVME_FIELD_ERR_UNSUP_VERSION); 97 } 98 99 if (field->nlfi_sup != NULL && !field->nlfi_sup(field, data, msg, 100 msglen)) { 101 (void) snprintf(msg, msglen, "field %s (%s) is not " 102 "supported by the controller", field->nlfi_human, 103 field->nlfi_spec); 104 return (NVME_FIELD_ERR_UNSUP_FIELD); 105 } 106 107 if (field->nlfi_valid != NULL) { 108 if (!field->nlfi_valid(field, data, value, msg, msglen)) { 109 if (msglen > 0 && *msg == '\0') { 110 (void) snprintf(msg, msglen, 111 "field %s (%s) value 0x%" PRIx64 112 " is invalid", 113 field->nlfi_human, 114 field->nlfi_spec, value); 115 } 116 return (NVME_FIELD_ERR_BAD_VALUE); 117 } 118 } else if (!nvme_field_range_check(field, 0, field->nlfi_max_size, msg, 119 msglen, value)) { 120 return (NVME_FIELD_ERR_BAD_VALUE); 121 } 122 123 return (NVME_FIELD_ERR_OK); 124 } 125