2 * Copyright (C) 2021 Axcient, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
28 /* Tests that alter an enclosure's state */
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
41 #include <cam/scsi/scsi_enc.h>
45 // Run a test function on just one ses device
47 for_one_ses_dev(ses_cb cb)
56 r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g);
61 fd = open(g.gl_pathv[0], O_RDWR);
63 cb(g.gl_pathv[0], fd);
69 static bool do_setelmstat(const char *devname __unused, int fd) {
70 encioc_element_t *map;
74 elm_type_t last_elm_type = -1;
76 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
79 map = calloc(nobj, sizeof(encioc_element_t));
80 ATF_REQUIRE(map != NULL);
81 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
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;
88 if (last_elm_type != map[elm_idx].elm_type) {
89 /* skip overall elements */
90 last_elm_type = map[elm_idx].elm_type;
93 elmstat.elm_idx = elm_idx;
94 if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
95 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
97 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
100 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
102 ses_ctrl_common_set_select(&cslot->common, 1);
103 ses_ctrl_dev_slot_set_rqst_ident(cslot, 1);
104 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
105 ATF_REQUIRE_EQ(r, 0);
109 /* Check the IDENT bit for every disk slot */
111 for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
112 encioc_elm_status_t elmstat;
113 struct ses_status_dev_slot *sslot;
115 if (last_elm_type != map[elm_idx].elm_type) {
116 /* skip overall elements */
117 last_elm_type = map[elm_idx].elm_type;
120 elmstat.elm_idx = elm_idx;
121 if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
122 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
126 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
127 ATF_REQUIRE_EQ(r, 0);
129 sslot = (struct ses_status_dev_slot*)&elmstat.cstat[0];
131 for (i = 0; i < 10; i++) {
132 r = ioctl(fd, ENCIOC_GETELMSTAT,
134 ATF_REQUIRE_EQ(r, 0);
135 if (0 == ses_status_dev_slot_get_ident(sslot)) {
136 /* Needs more time to take effect */
140 ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0);
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.
156 do_setelmstat_cleanup(const char *devname __unused, int fd __unused) {
157 encioc_element_t *map;
161 elm_type_t last_elm_type = -1;
163 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
164 ATF_REQUIRE_EQ(r, 0);
166 map = calloc(nobj, sizeof(encioc_element_t));
167 ATF_REQUIRE(map != NULL);
168 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
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;
175 if (last_elm_type != map[elm_idx].elm_type) {
176 /* skip overall elements */
177 last_elm_type = map[elm_idx].elm_type;
180 elmstat.elm_idx = elm_idx;
181 if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
182 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
184 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
186 ses_ctrl_common_set_select(&cslot->common, 1);
187 ses_ctrl_dev_slot_set_rqst_ident(cslot, 0);
188 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
196 ATF_TC_WITH_CLEANUP(setelmstat);
197 ATF_TC_HEAD(setelmstat, tc)
199 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT");
200 atf_tc_set_md_var(tc, "require.user", "root");
202 ATF_TC_BODY(setelmstat, tc)
205 atf_tc_skip("No ses devices found");
207 for_one_ses_dev(do_setelmstat);
209 ATF_TC_CLEANUP(setelmstat, tc)
214 for_one_ses_dev(do_setelmstat_cleanup);
218 static bool do_setencstat(const char *devname __unused, int fd) {
219 unsigned char encstat;
224 * SES provides no way to read the current setting of the enclosure
225 * control page common status bits. So we'll blindly set CRIT.
227 encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT;
228 r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
229 ATF_REQUIRE_EQ(r, 0);
231 /* Check that the status has changed */
232 for (i = 0; i < 10; i++) {
233 r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat);
234 ATF_REQUIRE_EQ(r, 0);
235 if (encstat & SES_CTRL_PAGE_CRIT_MASK) {
242 /* Some enclosures don't support setting the enclosure status */
248 static bool do_setencstat_cleanup(const char *devname __unused, int fd) {
249 unsigned char encstat;
252 * SES provides no way to read the current setting of the enclosure
253 * control page common status bits. So we don't know what they were
254 * set to before the test. We'll blindly clear all bits.
257 ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
261 ATF_TC_WITH_CLEANUP(setencstat);
262 ATF_TC_HEAD(setencstat, tc)
264 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT");
265 atf_tc_set_md_var(tc, "require.user", "root");
267 ATF_TC_BODY(setencstat, tc)
270 atf_tc_skip("No ses devices found");
272 for_each_ses_dev(do_setencstat, O_RDWR);
274 ATF_TC_CLEANUP(setencstat, tc)
276 for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
285 * * ENCIOC_INIT because SES doesn't need it and I don't have any
288 * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally
289 * used for stuff like firmware updates
291 ATF_TP_ADD_TC(tp, setelmstat);
292 ATF_TP_ADD_TC(tp, setencstat);
293 // TODO ENCIOC_SETELMSTAT
295 return (atf_no_error());