1 /*-
2 * Copyright (C) 2021 Axcient, Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 /* Tests that alter an enclosure's state */
27
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
30
31 #include <atf-c.h>
32 #include <fcntl.h>
33 #include <glob.h>
34 #include <regex.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38
39 #include <cam/scsi/scsi_enc.h>
40
41 #include "common.h"
42
43 // Run a test function on just one ses device
44 static void
for_one_ses_dev(ses_cb cb)45 for_one_ses_dev(ses_cb cb)
46 {
47 glob_t g;
48 int fd, r;
49
50 g.gl_pathc = 0;
51 g.gl_pathv = NULL;
52 g.gl_offs = 0;
53
54 r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g);
55 ATF_REQUIRE_EQ(r, 0);
56 if (g.gl_matchc == 0)
57 return;
58
59 fd = open(g.gl_pathv[0], O_RDWR);
60 ATF_REQUIRE(fd >= 0);
61 cb(g.gl_pathv[0], fd);
62 close(fd);
63
64 globfree(&g);
65 }
66
67 static bool
do_setelmstat(const char * devname __unused,int fd)68 do_setelmstat(const char *devname __unused, int fd)
69 {
70 encioc_element_t *map;
71 unsigned elm_idx;
72 unsigned nobj;
73 int r;
74 elm_type_t last_elm_type = -1;
75
76 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
77 ATF_REQUIRE_EQ(r, 0);
78
79 map = calloc(nobj, sizeof(encioc_element_t));
80 ATF_REQUIRE(map != NULL);
81 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
82
83 /* Set the IDENT bit for every disk slot */
84 for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
85 encioc_elm_status_t elmstat;
86 struct ses_ctrl_dev_slot *cslot;
87
88 if (last_elm_type != map[elm_idx].elm_type) {
89 /* skip overall elements */
90 last_elm_type = map[elm_idx].elm_type;
91 continue;
92 }
93 elmstat.elm_idx = elm_idx;
94 if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
95 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
96 {
97 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
98 ATF_REQUIRE_EQ(r, 0);
99 ses_status_to_ctrl(map[elm_idx].elm_type,
100 &elmstat.cstat[0]);
101
102 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
103
104 ses_ctrl_common_set_select(&cslot->common, 1);
105 ses_ctrl_dev_slot_set_rqst_ident(cslot, 1);
106 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
107 ATF_REQUIRE_EQ(r, 0);
108 }
109 }
110
111 /* Check the IDENT bit for every disk slot */
112 last_elm_type = -1;
113 for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
114 encioc_elm_status_t elmstat;
115 struct ses_status_dev_slot *sslot =
116 (struct ses_status_dev_slot*)&elmstat.cstat[0];
117
118 if (last_elm_type != map[elm_idx].elm_type) {
119 /* skip overall elements */
120 last_elm_type = map[elm_idx].elm_type;
121 continue;
122 }
123 elmstat.elm_idx = elm_idx;
124 if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
125 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
126 {
127 int i;
128
129 for (i = 0; i < 10; i++) {
130 r = ioctl(fd, ENCIOC_GETELMSTAT,
131 (caddr_t)&elmstat);
132 ATF_REQUIRE_EQ(r, 0);
133 if (0 == ses_status_dev_slot_get_ident(sslot)) {
134 /* Needs more time to take effect */
135 usleep(100000);
136 }
137 }
138 ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0);
139
140 }
141 }
142
143 free(map);
144 return (true);
145 }
146
147 /*
148 * sg_ses doesn't provide "dump and restore" functionality. The closest is to
149 * dump status page 2, then manually edit the file to set every individual
150 * element select bit, then load the entire file. But that is much too hard.
151 * Instead, we'll just clear every ident bit.
152 */
153 static bool
do_setelmstat_cleanup(const char * devname __unused,int fd __unused)154 do_setelmstat_cleanup(const char *devname __unused, int fd __unused)
155 {
156 encioc_element_t *map;
157 unsigned elm_idx;
158 unsigned nobj;
159 int r;
160 elm_type_t last_elm_type = -1;
161
162 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
163 ATF_REQUIRE_EQ(r, 0);
164
165 map = calloc(nobj, sizeof(encioc_element_t));
166 ATF_REQUIRE(map != NULL);
167 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
168 ATF_REQUIRE_EQ(r, 0);
169
170 /* Clear the IDENT bit for every disk slot */
171 for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
172 encioc_elm_status_t elmstat;
173 struct ses_ctrl_dev_slot *cslot;
174
175 if (last_elm_type != map[elm_idx].elm_type) {
176 /* skip overall elements */
177 last_elm_type = map[elm_idx].elm_type;
178 continue;
179 }
180 elmstat.elm_idx = elm_idx;
181 if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
182 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
183 {
184 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
185 ATF_REQUIRE_EQ(r, 0);
186 ses_status_to_ctrl(map[elm_idx].elm_type,
187 &elmstat.cstat[0]);
188
189 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
190
191 ses_ctrl_common_set_select(&cslot->common, 1);
192 ses_ctrl_dev_slot_set_rqst_ident(cslot, 0);
193 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
194 ATF_REQUIRE_EQ(r, 0);
195 }
196 }
197
198 return(true);
199 }
200
201
202 ATF_TC_WITH_CLEANUP(setelmstat);
ATF_TC_HEAD(setelmstat,tc)203 ATF_TC_HEAD(setelmstat, tc)
204 {
205 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT");
206 atf_tc_set_md_var(tc, "require.user", "root");
207 }
ATF_TC_BODY(setelmstat,tc)208 ATF_TC_BODY(setelmstat, tc)
209 {
210 if (!has_ses())
211 atf_tc_skip("No ses devices found");
212
213 for_one_ses_dev(do_setelmstat);
214 }
ATF_TC_CLEANUP(setelmstat,tc)215 ATF_TC_CLEANUP(setelmstat, tc)
216 {
217 if (!has_ses())
218 return;
219
220 for_one_ses_dev(do_setelmstat_cleanup);
221 }
222
223
224 static bool
do_setencstat(const char * devname __unused,int fd)225 do_setencstat(const char *devname __unused, int fd)
226 {
227 unsigned char encstat;
228 int r, i;
229 bool worked = false;
230
231 /*
232 * SES provides no way to read the current setting of the enclosure
233 * control page common status bits. So we'll blindly set CRIT.
234 */
235 encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT;
236 r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
237 ATF_REQUIRE_EQ(r, 0);
238
239 /* Check that the status has changed */
240 for (i = 0; i < 10; i++) {
241 r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat);
242 ATF_REQUIRE_EQ(r, 0);
243 if (encstat & SES_CTRL_PAGE_CRIT_MASK) {
244 worked = true;
245 break;
246 }
247 usleep(100000);
248 }
249 if (!worked) {
250 /* Some enclosures don't support setting the enclosure status */
251 return (false);
252 } else
253 return (true);
254 }
255
256 static bool
do_setencstat_cleanup(const char * devname __unused,int fd)257 do_setencstat_cleanup(const char *devname __unused, int fd)
258 {
259 unsigned char encstat;
260
261 /*
262 * SES provides no way to read the current setting of the enclosure
263 * control page common status bits. So we don't know what they were
264 * set to before the test. We'll blindly clear all bits.
265 */
266 encstat = 0;
267 ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
268 return (true);
269 }
270
271 ATF_TC_WITH_CLEANUP(setencstat);
ATF_TC_HEAD(setencstat,tc)272 ATF_TC_HEAD(setencstat, tc)
273 {
274 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT");
275 atf_tc_set_md_var(tc, "require.user", "root");
276 }
ATF_TC_BODY(setencstat,tc)277 ATF_TC_BODY(setencstat, tc)
278 {
279 if (!has_ses())
280 atf_tc_skip("No ses devices found");
281
282 for_each_ses_dev(do_setencstat, O_RDWR);
283 }
ATF_TC_CLEANUP(setencstat,tc)284 ATF_TC_CLEANUP(setencstat, tc)
285 {
286 for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
287 }
288
ATF_TP_ADD_TCS(tp)289 ATF_TP_ADD_TCS(tp)
290 {
291
292 /*
293 * Untested ioctls:
294 *
295 * * ENCIOC_INIT because SES doesn't need it and I don't have any
296 * SAF-TE devices.
297 *
298 * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally
299 * used for stuff like firmware updates
300 */
301 ATF_TP_ADD_TC(tp, setelmstat);
302 ATF_TP_ADD_TC(tp, setencstat);
303
304 return (atf_no_error());
305 }
306