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 * Common field and validation pieces for NVMe Vendor Unique Admin and NVM 18 * commands. 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 /* 33 * Right now this mainly checks for a valid admin command set operation code. 34 * When we end up supporting commands that are meant to be submitted to NVM 35 * queues, we'll likely need to add a way to know what type of command set we're 36 * targeting. 37 */ 38 static bool 39 nvme_vuc_field_valid_opc(const nvme_field_info_t *field, 40 const nvme_valid_ctrl_data_t *data, uint64_t opcode, char *msg, 41 size_t msglen) 42 { 43 return (nvme_field_range_check(field, NVME_PASSTHRU_MIN_ADMIN_OPC, 44 NVME_PASSTHRU_MAX_ADMIN_OPC, msg, msglen, opcode)); 45 } 46 47 /* 48 * Unlike with log pages, identify commands, features, etc. we do not know how 49 * the namespace will be used. It may be zero, it may need to be the broadcast 50 * namespace, or it may target a device specific namespace. As such, we accept 51 * all of these, which isn't normally the case. 52 */ 53 static bool 54 nvme_vuc_field_valid_nsid(const nvme_field_info_t *field, 55 const nvme_valid_ctrl_data_t *data, uint64_t nsid, char *msg, size_t msglen) 56 { 57 if (nsid == 0) { 58 return (true); 59 } 60 61 return (nvme_field_valid_nsid(field, data, nsid, msg, msglen)); 62 } 63 64 /* 65 * A VUC data length is in dwords. It's our responsibility to make sure that 66 * this is properly aligned. Though zero is a valid value. 67 */ 68 static bool 69 nvme_vuc_field_valid_ndt(const nvme_field_info_t *field, 70 const nvme_valid_ctrl_data_t *data, uint64_t len, char *msg, size_t msglen) 71 { 72 uint64_t max = (uint64_t)UINT32_MAX << NVME_DWORD_SHIFT; 73 74 if ((len % NVME_DWORD_SIZE) != 0) { 75 (void) snprintf(msg, msglen, "%s (%s) value 0x%" PRIx64 " is " 76 "invalid: value must be %u-byte aligned", field->nlfi_human, 77 field->nlfi_spec, len, NVME_DWORD_SIZE); 78 return (false); 79 } 80 81 return (nvme_field_range_check(field, 0, max, msg, msglen, len)); 82 } 83 84 /* 85 * The maximum timeout is controlled by the kernel. The only real constraint 86 * right now is that it fit into the ioctl size and is non-zero. 87 */ 88 static bool 89 nvme_vuc_field_valid_to(const nvme_field_info_t *field, 90 const nvme_valid_ctrl_data_t *data, uint64_t to, char *msg, size_t msglen) 91 { 92 return (nvme_field_range_check(field, 1, UINT32_MAX, msg, msglen, to)); 93 } 94 95 const nvme_field_info_t nvme_vuc_fields[] = { 96 [NVME_VUC_REQ_FIELD_OPC] = { 97 .nlfi_vers = &nvme_vers_1v0, 98 .nlfi_valid = nvme_vuc_field_valid_opc, 99 .nlfi_spec = "opc", 100 .nlfi_human = "opcode", 101 .nlfi_def_req = true, 102 .nlfi_def_allow = true 103 }, 104 [NVME_VUC_REQ_FIELD_NSID] = { 105 .nlfi_vers = &nvme_vers_1v0, 106 .nlfi_valid = nvme_vuc_field_valid_nsid, 107 .nlfi_spec = "nsid", 108 .nlfi_human = "namespace ID", 109 .nlfi_def_req = false, 110 .nlfi_def_allow = true 111 }, 112 [NVME_VUC_REQ_FIELD_CDW12] = { 113 .nlfi_vers = &nvme_vers_1v0, 114 .nlfi_max_size = UINT32_MAX, 115 .nlfi_spec = "cdw12", 116 .nlfi_human = "command dword 12", 117 .nlfi_def_req = false, 118 .nlfi_def_allow = true 119 }, 120 [NVME_VUC_REQ_FIELD_CDW13] = { 121 .nlfi_vers = &nvme_vers_1v0, 122 .nlfi_max_size = UINT32_MAX, 123 .nlfi_spec = "cdw13", 124 .nlfi_human = "command dword 13", 125 .nlfi_def_req = false, 126 .nlfi_def_allow = true 127 }, 128 [NVME_VUC_REQ_FIELD_CDW14] = { 129 .nlfi_vers = &nvme_vers_1v0, 130 .nlfi_max_size = UINT32_MAX, 131 .nlfi_spec = "cdw14", 132 .nlfi_human = "command dword 14", 133 .nlfi_def_req = false, 134 .nlfi_def_allow = true 135 }, 136 [NVME_VUC_REQ_FIELD_CDW15] = { 137 .nlfi_vers = &nvme_vers_1v0, 138 .nlfi_max_size = UINT32_MAX, 139 .nlfi_spec = "cdw15", 140 .nlfi_human = "command dword 15", 141 .nlfi_def_req = false, 142 .nlfi_def_allow = true 143 }, 144 [NVME_VUC_REQ_FIELD_NDT] = { 145 .nlfi_vers = &nvme_vers_1v0, 146 .nlfi_valid = nvme_vuc_field_valid_ndt, 147 .nlfi_spec = "ndt", 148 .nlfi_human = "number of dwords in data transfer", 149 .nlfi_def_req = false, 150 .nlfi_def_allow = true 151 }, 152 [NVME_VUC_REQ_FIELD_TO] = { 153 .nlfi_vers = &nvme_vers_1v0, 154 .nlfi_valid = nvme_vuc_field_valid_to, 155 .nlfi_spec = "to", 156 .nlfi_human = "timeout", 157 .nlfi_def_req = true, 158 .nlfi_def_allow = true 159 } 160 }; 161 162 size_t nvme_vuc_nfields = ARRAY_SIZE(nvme_vuc_fields); 163