xref: /freebsd/tests/sys/ses/destructive.c (revision 2e3f49888ec8851bafb22011533217487764fdb0)
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
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
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
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);
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 }
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 }
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
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
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);
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 }
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 }
284 ATF_TC_CLEANUP(setencstat, tc)
285 {
286 	for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
287 }
288 
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