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