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 bool 40 nvme_field_supported_nsid(const nvme_field_info_t *field, 41 const nvme_valid_ctrl_data_t *data, char *msg, size_t msglen) 42 { 43 if (data->vcd_id->id_oacs.oa_nsmgmt != 0) { 44 return (true); 45 } 46 47 (void) snprintf(msg, msglen, "controller does not support field %s " 48 "(%s): missing namespace support in Optional Admin Command Support " 49 "(OACS)", field->nlfi_human, field->nlfi_spec); 50 return (false); 51 } 52 53 /* 54 * Note, we rely on external logic to determine if the broadcast nsid is valid. 55 * We always accept it. 56 */ 57 bool 58 nvme_field_valid_nsid(const nvme_field_info_t *field, 59 const nvme_valid_ctrl_data_t *data, uint64_t nsid, char *msg, size_t msglen) 60 { 61 if ((nsid != 0 && nsid <= data->vcd_id->id_nn) || 62 nsid == NVME_NSID_BCAST) { 63 return (true); 64 } 65 66 (void) snprintf(msg, msglen, "namespace id %" PRIu64 "is outside the " 67 "valid range [0x%x, 0x%x], the broadcast nsid (0x%x) may be valid", 68 nsid, NVME_NSID_MIN, NVME_NSID_BCAST, data->vcd_id->id_nn); 69 return (false); 70 } 71 72 bool 73 nvme_field_range_check(const nvme_field_info_t *field, uint64_t min, 74 uint64_t max, char *msg, size_t msglen, uint64_t value) 75 { 76 if (value >= min && value <= max) { 77 return (true); 78 } 79 80 (void) snprintf(msg, msglen, "field %s (%s) value 0x%" 81 PRIx64 " is outside the valid range: [0x%" PRIx64 ", 0x%" PRIx64 82 "]", field->nlfi_human, field->nlfi_spec, value, min, max); 83 return (false); 84 } 85 86 /* 87 * This is a general validation function for fields that are part of a command. 88 * It will check if the field is supported by the controller and if so, that its 89 * value is within the expected range. On error, an optional message will be 90 * written that explains the error. This is intended to be shared between 91 * userland and the kernel. The kernel should pass NULL/0 for msg/msglen because 92 * there is no message translation capability in the kernel. 93 */ 94 nvme_field_error_t 95 nvme_field_validate(const nvme_field_info_t *field, 96 const nvme_valid_ctrl_data_t *data, uint64_t value, char *msg, 97 size_t msglen) 98 { 99 ASSERT3P(field->nlfi_vers, !=, NULL); 100 101 if (!nvme_field_atleast(data, field->nlfi_vers)) { 102 (void) snprintf(msg, msglen, "field %s (%s) requires " 103 "version %u.%u, but device is at %u.%u", field->nlfi_human, 104 field->nlfi_spec, data->vcd_vers->v_major, 105 data->vcd_vers->v_minor, field->nlfi_vers->v_major, 106 field->nlfi_vers->v_minor); 107 return (NVME_FIELD_ERR_UNSUP_VERSION); 108 } 109 110 if (field->nlfi_sup != NULL && !field->nlfi_sup(field, data, msg, 111 msglen)) { 112 (void) snprintf(msg, msglen, "field %s (%s) is not " 113 "supported by the controller", field->nlfi_human, 114 field->nlfi_spec); 115 return (NVME_FIELD_ERR_UNSUP_FIELD); 116 } 117 118 if (field->nlfi_valid != NULL) { 119 if (!field->nlfi_valid(field, data, value, msg, msglen)) { 120 (void) snprintf(msg, msglen, "field %s (%s) " 121 "value 0x%" PRIx64 " is invalid", field->nlfi_human, 122 field->nlfi_spec, value); 123 return (NVME_FIELD_ERR_BAD_VALUE); 124 } 125 } else if (!nvme_field_range_check(field, 0, field->nlfi_max_size, msg, 126 msglen, value)) { 127 return (NVME_FIELD_ERR_BAD_VALUE); 128 } 129 130 return (NVME_FIELD_ERR_OK); 131 } 132