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 * $FreeBSD$ 26 */ 27 28 /* Tests that alter an enclosure's state */ 29 30 #include <sys/types.h> 31 #include <sys/ioctl.h> 32 33 #include <atf-c.h> 34 #include <fcntl.h> 35 #include <glob.h> 36 #include <regex.h> 37 #include <stdint.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 41 #include <cam/scsi/scsi_enc.h> 42 43 #include "common.h" 44 45 // Run a test function on just one ses device 46 static void 47 for_one_ses_dev(ses_cb cb) 48 { 49 glob_t g; 50 int fd, r; 51 52 g.gl_pathc = 0; 53 g.gl_pathv = NULL; 54 g.gl_offs = 0; 55 56 r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g); 57 ATF_REQUIRE_EQ(r, 0); 58 if (g.gl_matchc == 0) 59 return; 60 61 fd = open(g.gl_pathv[0], O_RDWR); 62 ATF_REQUIRE(fd >= 0); 63 cb(g.gl_pathv[0], fd); 64 close(fd); 65 66 globfree(&g); 67 } 68 69 static bool 70 do_setelmstat(const char *devname __unused, int fd) 71 { 72 encioc_element_t *map; 73 unsigned elm_idx; 74 unsigned nobj; 75 int r; 76 elm_type_t last_elm_type = -1; 77 78 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj); 79 ATF_REQUIRE_EQ(r, 0); 80 81 map = calloc(nobj, sizeof(encioc_element_t)); 82 ATF_REQUIRE(map != NULL); 83 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map); 84 85 /* Set the IDENT bit for every disk slot */ 86 for (elm_idx = 0; elm_idx < nobj; elm_idx++) { 87 encioc_elm_status_t elmstat; 88 struct ses_ctrl_dev_slot *cslot; 89 90 if (last_elm_type != map[elm_idx].elm_type) { 91 /* skip overall elements */ 92 last_elm_type = map[elm_idx].elm_type; 93 continue; 94 } 95 elmstat.elm_idx = elm_idx; 96 if (map[elm_idx].elm_type == ELMTYP_DEVICE || 97 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV) 98 { 99 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat); 100 ATF_REQUIRE_EQ(r, 0); 101 ses_status_to_ctrl(map[elm_idx].elm_type, 102 &elmstat.cstat[0]); 103 104 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0]; 105 106 ses_ctrl_common_set_select(&cslot->common, 1); 107 ses_ctrl_dev_slot_set_rqst_ident(cslot, 1); 108 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat); 109 ATF_REQUIRE_EQ(r, 0); 110 } 111 } 112 113 /* Check the IDENT bit for every disk slot */ 114 last_elm_type = -1; 115 for (elm_idx = 0; elm_idx < nobj; elm_idx++) { 116 encioc_elm_status_t elmstat; 117 struct ses_status_dev_slot *sslot = 118 (struct ses_status_dev_slot*)&elmstat.cstat[0]; 119 120 if (last_elm_type != map[elm_idx].elm_type) { 121 /* skip overall elements */ 122 last_elm_type = map[elm_idx].elm_type; 123 continue; 124 } 125 elmstat.elm_idx = elm_idx; 126 if (map[elm_idx].elm_type == ELMTYP_DEVICE || 127 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV) 128 { 129 int i; 130 131 for (i = 0; i < 10; i++) { 132 r = ioctl(fd, ENCIOC_GETELMSTAT, 133 (caddr_t)&elmstat); 134 ATF_REQUIRE_EQ(r, 0); 135 if (0 == ses_status_dev_slot_get_ident(sslot)) { 136 /* Needs more time to take effect */ 137 usleep(100000); 138 } 139 } 140 ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0); 141 142 } 143 } 144 145 free(map); 146 return (true); 147 } 148 149 /* 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. 154 */ 155 static bool 156 do_setelmstat_cleanup(const char *devname __unused, int fd __unused) 157 { 158 encioc_element_t *map; 159 unsigned elm_idx; 160 unsigned nobj; 161 int r; 162 elm_type_t last_elm_type = -1; 163 164 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj); 165 ATF_REQUIRE_EQ(r, 0); 166 167 map = calloc(nobj, sizeof(encioc_element_t)); 168 ATF_REQUIRE(map != NULL); 169 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map); 170 ATF_REQUIRE_EQ(r, 0); 171 172 /* Clear the IDENT bit for every disk slot */ 173 for (elm_idx = 0; elm_idx < nobj; elm_idx++) { 174 encioc_elm_status_t elmstat; 175 struct ses_ctrl_dev_slot *cslot; 176 177 if (last_elm_type != map[elm_idx].elm_type) { 178 /* skip overall elements */ 179 last_elm_type = map[elm_idx].elm_type; 180 continue; 181 } 182 elmstat.elm_idx = elm_idx; 183 if (map[elm_idx].elm_type == ELMTYP_DEVICE || 184 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV) 185 { 186 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat); 187 ATF_REQUIRE_EQ(r, 0); 188 ses_status_to_ctrl(map[elm_idx].elm_type, 189 &elmstat.cstat[0]); 190 191 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0]; 192 193 ses_ctrl_common_set_select(&cslot->common, 1); 194 ses_ctrl_dev_slot_set_rqst_ident(cslot, 0); 195 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat); 196 ATF_REQUIRE_EQ(r, 0); 197 } 198 } 199 200 return(true); 201 } 202 203 204 ATF_TC_WITH_CLEANUP(setelmstat); 205 ATF_TC_HEAD(setelmstat, tc) 206 { 207 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT"); 208 atf_tc_set_md_var(tc, "require.user", "root"); 209 } 210 ATF_TC_BODY(setelmstat, tc) 211 { 212 if (!has_ses()) 213 atf_tc_skip("No ses devices found"); 214 215 for_one_ses_dev(do_setelmstat); 216 } 217 ATF_TC_CLEANUP(setelmstat, tc) 218 { 219 if (!has_ses()) 220 return; 221 222 for_one_ses_dev(do_setelmstat_cleanup); 223 } 224 225 226 static bool 227 do_setencstat(const char *devname __unused, int fd) 228 { 229 unsigned char encstat; 230 int r, i; 231 bool worked = false; 232 233 /* 234 * SES provides no way to read the current setting of the enclosure 235 * control page common status bits. So we'll blindly set CRIT. 236 */ 237 encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT; 238 r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat); 239 ATF_REQUIRE_EQ(r, 0); 240 241 /* Check that the status has changed */ 242 for (i = 0; i < 10; i++) { 243 r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat); 244 ATF_REQUIRE_EQ(r, 0); 245 if (encstat & SES_CTRL_PAGE_CRIT_MASK) { 246 worked = true; 247 break; 248 } 249 usleep(100000); 250 } 251 if (!worked) { 252 /* Some enclosures don't support setting the enclosure status */ 253 return (false); 254 } else 255 return (true); 256 } 257 258 static bool 259 do_setencstat_cleanup(const char *devname __unused, int fd) 260 { 261 unsigned char encstat; 262 263 /* 264 * SES provides no way to read the current setting of the enclosure 265 * control page common status bits. So we don't know what they were 266 * set to before the test. We'll blindly clear all bits. 267 */ 268 encstat = 0; 269 ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat); 270 return (true); 271 } 272 273 ATF_TC_WITH_CLEANUP(setencstat); 274 ATF_TC_HEAD(setencstat, tc) 275 { 276 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT"); 277 atf_tc_set_md_var(tc, "require.user", "root"); 278 } 279 ATF_TC_BODY(setencstat, tc) 280 { 281 if (!has_ses()) 282 atf_tc_skip("No ses devices found"); 283 284 for_each_ses_dev(do_setencstat, O_RDWR); 285 } 286 ATF_TC_CLEANUP(setencstat, tc) 287 { 288 for_each_ses_dev(do_setencstat_cleanup, O_RDWR); 289 } 290 291 ATF_TP_ADD_TCS(tp) 292 { 293 294 /* 295 * Untested ioctls: 296 * 297 * * ENCIOC_INIT because SES doesn't need it and I don't have any 298 * SAF-TE devices. 299 * 300 * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally 301 * used for stuff like firmware updates 302 */ 303 ATF_TP_ADD_TC(tp, setelmstat); 304 ATF_TP_ADD_TC(tp, setencstat); 305 306 return (atf_no_error()); 307 } 308