xref: /freebsd/tests/sys/ses/destructive.c (revision 2b833162)
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  * $FreeBSD$
26  */
27 
28 /* Tests that alter an enclosure's state */
29 
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 
33 #include <atf-c.h>
34 #include <fcntl.h>
35 #include <glob.h>
36 #include <regex.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 
41 #include <cam/scsi/scsi_enc.h>
42 
43 #include "common.h"
44 
45 // Run a test function on just one ses device
46 static void
47 for_one_ses_dev(ses_cb cb)
48 {
49 	glob_t g;
50 	int fd, r;
51 
52 	g.gl_pathc = 0;
53 	g.gl_pathv = NULL;
54 	g.gl_offs = 0;
55 
56 	r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g);
57 	ATF_REQUIRE_EQ(r, 0);
58 	if (g.gl_matchc == 0)
59 		return;
60 
61 	fd = open(g.gl_pathv[0], O_RDWR);
62 	ATF_REQUIRE(fd >= 0);
63 	cb(g.gl_pathv[0], fd);
64 	close(fd);
65 
66 	globfree(&g);
67 }
68 
69 static bool
70 do_setelmstat(const char *devname __unused, int fd)
71 {
72 	encioc_element_t *map;
73 	unsigned elm_idx;
74 	unsigned nobj;
75 	int r;
76 	elm_type_t last_elm_type = -1;
77 
78 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
79 	ATF_REQUIRE_EQ(r, 0);
80 
81 	map = calloc(nobj, sizeof(encioc_element_t));
82 	ATF_REQUIRE(map != NULL);
83 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
84 
85 	/* Set the IDENT bit for every disk slot */
86 	for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
87 		encioc_elm_status_t elmstat;
88 		struct ses_ctrl_dev_slot *cslot;
89 
90 		if (last_elm_type != map[elm_idx].elm_type) {
91 			/* skip overall elements */
92 			last_elm_type = map[elm_idx].elm_type;
93 			continue;
94 		}
95 		elmstat.elm_idx = elm_idx;
96 		if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
97 		    map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
98 		{
99 			r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
100 			ATF_REQUIRE_EQ(r, 0);
101 			ses_status_to_ctrl(map[elm_idx].elm_type,
102 				&elmstat.cstat[0]);
103 
104 			cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
105 
106 			ses_ctrl_common_set_select(&cslot->common, 1);
107 			ses_ctrl_dev_slot_set_rqst_ident(cslot, 1);
108 			r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
109 			ATF_REQUIRE_EQ(r, 0);
110 		}
111 	}
112 
113 	/* Check the IDENT bit for every disk slot */
114 	last_elm_type = -1;
115 	for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
116 		encioc_elm_status_t elmstat;
117 		struct ses_status_dev_slot *sslot =
118 			(struct ses_status_dev_slot*)&elmstat.cstat[0];
119 
120 		if (last_elm_type != map[elm_idx].elm_type) {
121 			/* skip overall elements */
122 			last_elm_type = map[elm_idx].elm_type;
123 			continue;
124 		}
125 		elmstat.elm_idx = elm_idx;
126 		if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
127 		    map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
128 		{
129 			int i;
130 
131 			for (i = 0; i < 10; i++) {
132 				r = ioctl(fd, ENCIOC_GETELMSTAT,
133 				    (caddr_t)&elmstat);
134 				ATF_REQUIRE_EQ(r, 0);
135 				if (0 == ses_status_dev_slot_get_ident(sslot)) {
136 					/* Needs more time to take effect */
137 					usleep(100000);
138 				}
139 			}
140 			ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0);
141 
142 		}
143 	}
144 
145 	free(map);
146 	return (true);
147 }
148 
149 /*
150  * sg_ses doesn't provide "dump and restore" functionality.  The closest is to
151  * dump status page 2, then manually edit the file to set every individual
152  * element select bit, then load the entire file.  But that is much too hard.
153  * Instead, we'll just clear every ident bit.
154  */
155 static bool
156 do_setelmstat_cleanup(const char *devname __unused, int fd __unused)
157 {
158 	encioc_element_t *map;
159 	unsigned elm_idx;
160 	unsigned nobj;
161 	int r;
162 	elm_type_t last_elm_type = -1;
163 
164 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
165 	ATF_REQUIRE_EQ(r, 0);
166 
167 	map = calloc(nobj, sizeof(encioc_element_t));
168 	ATF_REQUIRE(map != NULL);
169 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
170 	ATF_REQUIRE_EQ(r, 0);
171 
172 	/* Clear the IDENT bit for every disk slot */
173 	for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
174 		encioc_elm_status_t elmstat;
175 		struct ses_ctrl_dev_slot *cslot;
176 
177 		if (last_elm_type != map[elm_idx].elm_type) {
178 			/* skip overall elements */
179 			last_elm_type = map[elm_idx].elm_type;
180 			continue;
181 		}
182 		elmstat.elm_idx = elm_idx;
183 		if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
184 		    map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
185 		{
186 			r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
187 			ATF_REQUIRE_EQ(r, 0);
188 			ses_status_to_ctrl(map[elm_idx].elm_type,
189 			    &elmstat.cstat[0]);
190 
191 			cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
192 
193 			ses_ctrl_common_set_select(&cslot->common, 1);
194 			ses_ctrl_dev_slot_set_rqst_ident(cslot, 0);
195 			r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
196 			ATF_REQUIRE_EQ(r, 0);
197 		}
198 	}
199 
200 	return(true);
201 }
202 
203 
204 ATF_TC_WITH_CLEANUP(setelmstat);
205 ATF_TC_HEAD(setelmstat, tc)
206 {
207 	atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT");
208 	atf_tc_set_md_var(tc, "require.user", "root");
209 }
210 ATF_TC_BODY(setelmstat, tc)
211 {
212 	if (!has_ses())
213 		atf_tc_skip("No ses devices found");
214 
215 	for_one_ses_dev(do_setelmstat);
216 }
217 ATF_TC_CLEANUP(setelmstat, tc)
218 {
219 	if (!has_ses())
220 		return;
221 
222 	for_one_ses_dev(do_setelmstat_cleanup);
223 }
224 
225 
226 static bool
227 do_setencstat(const char *devname __unused, int fd)
228 {
229 	unsigned char encstat;
230 	int r, i;
231 	bool worked = false;
232 
233 	/*
234 	 * SES provides no way to read the current setting of the enclosure
235 	 * control page common status bits.  So we'll blindly set CRIT.
236 	 */
237 	encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT;
238 	r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
239 	ATF_REQUIRE_EQ(r, 0);
240 
241 	/* Check that the status has changed */
242 	for (i = 0; i < 10; i++) {
243 		r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat);
244 		ATF_REQUIRE_EQ(r, 0);
245 		if (encstat & SES_CTRL_PAGE_CRIT_MASK) {
246 			worked = true;
247 			break;
248 		}
249 		usleep(100000);
250 	}
251 	if (!worked) {
252 		/* Some enclosures don't support setting the enclosure status */
253 		return (false);
254 	} else
255 		return (true);
256 }
257 
258 static bool
259 do_setencstat_cleanup(const char *devname __unused, int fd)
260 {
261 	unsigned char encstat;
262 
263 	/*
264 	 * SES provides no way to read the current setting of the enclosure
265 	 * control page common status bits.  So we don't know what they were
266 	 * set to before the test.  We'll blindly clear all bits.
267 	 */
268 	encstat = 0;
269 	ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
270 	return (true);
271 }
272 
273 ATF_TC_WITH_CLEANUP(setencstat);
274 ATF_TC_HEAD(setencstat, tc)
275 {
276 	atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT");
277 	atf_tc_set_md_var(tc, "require.user", "root");
278 }
279 ATF_TC_BODY(setencstat, tc)
280 {
281 	if (!has_ses())
282 		atf_tc_skip("No ses devices found");
283 
284 	for_each_ses_dev(do_setencstat, O_RDWR);
285 }
286 ATF_TC_CLEANUP(setencstat, tc)
287 {
288 	for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
289 }
290 
291 ATF_TP_ADD_TCS(tp)
292 {
293 
294 	/*
295 	 * Untested ioctls:
296 	 *
297 	 * * ENCIOC_INIT because SES doesn't need it and I don't have any
298 	 *   SAF-TE devices.
299 	 *
300 	 * * ENCIOC_SETSTRING because it's seriously unsafe!  It's normally
301 	 *   used for stuff like firmware updates
302 	 */
303 	ATF_TP_ADD_TC(tp, setelmstat);
304 	ATF_TP_ADD_TC(tp, setencstat);
305 
306 	return (atf_no_error());
307 }
308