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
44static void
45for_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
67static bool
68do_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 */
153static bool
154do_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
202ATF_TC_WITH_CLEANUP(setelmstat);
203ATF_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}
208ATF_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}
215ATF_TC_CLEANUP(setelmstat, tc)
216{
217	if (!has_ses())
218		return;
219
220	for_one_ses_dev(do_setelmstat_cleanup);
221}
222
223
224static bool
225do_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
256static bool
257do_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
271ATF_TC_WITH_CLEANUP(setencstat);
272ATF_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}
277ATF_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}
284ATF_TC_CLEANUP(setencstat, tc)
285{
286	for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
287}
288
289ATF_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