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