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