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  * Test a few different bits of the status code string generation and see that
18  * we get something expected. Note, we try to avoid using the existing constants
19  * that we have for the sct / sc when testing the corresponding entries so
20  * someone can more so copy and paste entries from the spec and be less tempted
21  * to copy data from the implementation.
22  */
23 
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <err.h>
27 #include <stdio.h>
28 #include <strings.h>
29 #include <sys/sysmacros.h>
30 #include <libnvme.h>
31 
32 typedef struct {
33 	uint32_t st_code;
34 	const char *st_str;
35 } sct_test_t;
36 
37 static const sct_test_t sct_tests[] = { {
38 	.st_code = 0,
39 	.st_str = "generic command status",
40 }, {
41 	.st_code = 7,
42 	.st_str = "vendor specific"
43 }, {
44 	.st_code = 0x23,
45 	.st_str = "unknown status type"
46 }, {
47 	.st_code = 0x169,
48 	.st_str = "unknown status type"
49 } };
50 
51 typedef struct {
52 	nvme_csi_t sc_csi;
53 	uint32_t sc_sct;
54 	uint32_t sc_sc;
55 	const char *sc_str;
56 } sc_test_t;
57 
58 static const sc_test_t sc_tests[] = { {
59 	.sc_csi = NVME_CSI_NVM,
60 	.sc_sct = NVME_CQE_SCT_GENERIC,
61 	.sc_sc = 0x0,
62 	.sc_str = "successful completion"
63 }, {
64 	.sc_csi = NVME_CSI_NVM,
65 	.sc_sct = NVME_CQE_SCT_GENERIC,
66 	.sc_sc = 0x2,
67 	.sc_str = "invalid field in command"
68 }, {
69 	.sc_csi = NVME_CSI_NVM,
70 	.sc_sct = NVME_CQE_SCT_GENERIC,
71 	.sc_sc = 0xb,
72 	.sc_str = "invalid namespace or format"
73 }, {
74 	/*
75 	 * This is a purposefully bad CSI, but the CSI shouldn't matter for this
76 	 * code.
77 	 */
78 	.sc_csi = 0xff,
79 	.sc_sct = NVME_CQE_SCT_GENERIC,
80 	.sc_sc = 0xb,
81 	.sc_str = "invalid namespace or format"
82 }, {
83 	.sc_csi = NVME_CSI_NVM,
84 	.sc_sct = NVME_CQE_SCT_GENERIC,
85 	.sc_sc = 0x7f,
86 	.sc_str = "unknown status code",
87 }, {
88 	.sc_csi = NVME_CSI_NVM,
89 	.sc_sct = NVME_CQE_SCT_GENERIC,
90 	.sc_sc = 0x80,
91 	.sc_str = "lba out of range"
92 }, {
93 	.sc_csi = NVME_CSI_NVM,
94 	.sc_sct = NVME_CQE_SCT_GENERIC,
95 	.sc_sc = 0x82,
96 	.sc_str = "namespace not ready"
97 }, {
98 	.sc_csi = NVME_CSI_NVM,
99 	.sc_sct = NVME_CQE_SCT_GENERIC,
100 	.sc_sc = 0xbf,
101 	.sc_str = "unknown command set specific general status code"
102 
103 }, {
104 	.sc_csi = NVME_CSI_NVM,
105 	.sc_sct = NVME_CQE_SCT_GENERIC,
106 	.sc_sc = 0xff,
107 	.sc_str = "generic vendor specific status code"
108 }, {
109 	.sc_csi = NVME_CSI_NVM,
110 	.sc_sct = NVME_CQE_SCT_SPECIFIC,
111 	.sc_sc = 0x6,
112 	.sc_str = "invalid firmware slot"
113 }, {
114 	.sc_csi = NVME_CSI_NVM,
115 	.sc_sct = NVME_CQE_SCT_SPECIFIC,
116 	.sc_sc = 0x9,
117 	.sc_str = "invalid log page"
118 }, {
119 	.sc_csi = NVME_CSI_NVM,
120 	.sc_sct = NVME_CQE_SCT_SPECIFIC,
121 	.sc_sc = 0x6f,
122 	.sc_str = "unknown generic command status code"
123 }, {
124 	.sc_csi = NVME_CSI_NVM,
125 	.sc_sct = NVME_CQE_SCT_SPECIFIC,
126 	.sc_sc = 0x80,
127 	.sc_str = "conflicting attributes"
128 }, {
129 	.sc_csi = NVME_CSI_NVM,
130 	.sc_sct = NVME_CQE_SCT_SPECIFIC,
131 	.sc_sc = 0xbf,
132 	.sc_str = "unknown command specific, I/O command set specific status "
133 	    "code",
134 }, {
135 	.sc_csi = NVME_CSI_NVM,
136 	.sc_sct = NVME_CQE_SCT_SPECIFIC,
137 	.sc_sc = 0xff,
138 	.sc_str = "command specific vendor specific status code"
139 }, {
140 	.sc_csi = NVME_CSI_NVM,
141 	.sc_sct = NVME_CQE_SCT_INTEGRITY,
142 	.sc_sc = 0x80,
143 	.sc_str = "write fault"
144 }, {
145 	.sc_csi = 0x23,
146 	.sc_sct = NVME_CQE_SCT_INTEGRITY,
147 	.sc_sc = 0x80,
148 	.sc_str = "write fault"
149 }, {
150 	.sc_csi = NVME_CSI_NVM,
151 	.sc_sct = NVME_CQE_SCT_INTEGRITY,
152 	.sc_sc = 0x0,
153 	.sc_str = "unknown media and data integrity status code"
154 }, {
155 	.sc_csi = NVME_CSI_NVM,
156 	.sc_sct = NVME_CQE_SCT_INTEGRITY,
157 	.sc_sc = 0xff,
158 	.sc_str = "vendor specific media and data integrity status code"
159 } };
160 
161 static bool
162 sct_test_one(const sct_test_t *test)
163 {
164 	const char *str = nvme_scttostr(NULL, test->st_code);
165 	if (strcmp(str, test->st_str) != 0) {
166 		warnx("TEST FAILED: sct 0x%x was translated to string %s, "
167 		    "not %s", test->st_code, str, test->st_str);
168 		return (false);
169 	}
170 
171 	(void) printf("TEST PASSED: sct 0x%x successfully translated\n",
172 	    test->st_code);
173 	return (true);
174 }
175 
176 static bool
177 sc_test_one(const sc_test_t *test)
178 {
179 	const char *str = nvme_sctostr(NULL, test->sc_csi, test->sc_sct,
180 	    test->sc_sc);
181 	if (strcmp(str, test->sc_str) != 0) {
182 		warnx("TEST FAILED: csi/sct/sc 0x%x/0x%x/0x%x was translated "
183 		    "to string %s, not %s", test->sc_csi, test->sc_sct,
184 		    test->sc_sc, str, test->sc_str);
185 		return (false);
186 	}
187 
188 	(void) printf("TEST PASSED: csi/sct/sc 0x%x/0x%x/0x%x successfully "
189 	    "translated\n", test->sc_csi, test->sc_sct, test->sc_sc);
190 
191 	return (true);
192 }
193 
194 int
195 main(void)
196 {
197 	int ret = EXIT_SUCCESS;
198 
199 	for (size_t i = 0; i < ARRAY_SIZE(sct_tests); i++) {
200 		if (!sct_test_one(&sct_tests[i])) {
201 			ret = EXIT_FAILURE;
202 		}
203 	}
204 
205 	for (size_t i = 0; i < ARRAY_SIZE(sc_tests); i++) {
206 		if (!sc_test_one(&sc_tests[i])) {
207 			ret = EXIT_FAILURE;
208 		}
209 	}
210 
211 	return (ret);
212 }
213