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
nvme_vuc_field_valid_opc(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t opcode,char * msg,size_t msglen)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
nvme_vuc_field_valid_nsid(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t nsid,char * msg,size_t msglen)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
nvme_vuc_field_valid_ndt(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t len,char * msg,size_t msglen)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
nvme_vuc_field_valid_to(const nvme_field_info_t * field,const nvme_valid_ctrl_data_t * data,uint64_t to,char * msg,size_t msglen)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