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 do_setelmstat(const char *devname __unused, int fd) { 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 100 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0]; 101 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); 106 } 107 } 108 109 /* Check the IDENT bit for every disk slot */ 110 last_elm_type = -1; 111 for (elm_idx = 0; elm_idx < nobj; elm_idx++) { 112 encioc_elm_status_t elmstat; 113 struct ses_status_dev_slot *sslot; 114 115 if (last_elm_type != map[elm_idx].elm_type) { 116 /* skip overall elements */ 117 last_elm_type = map[elm_idx].elm_type; 118 continue; 119 } 120 elmstat.elm_idx = elm_idx; 121 if (map[elm_idx].elm_type == ELMTYP_DEVICE || 122 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV) 123 { 124 int i; 125 126 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat); 127 ATF_REQUIRE_EQ(r, 0); 128 129 sslot = (struct ses_status_dev_slot*)&elmstat.cstat[0]; 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 encioc_element_t *map; 158 unsigned elm_idx; 159 unsigned nobj; 160 int r; 161 elm_type_t last_elm_type = -1; 162 163 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj); 164 ATF_REQUIRE_EQ(r, 0); 165 166 map = calloc(nobj, sizeof(encioc_element_t)); 167 ATF_REQUIRE(map != NULL); 168 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map); 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 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0]; 185 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); 189 } 190 } 191 192 return(true); 193 } 194 195 196 ATF_TC_WITH_CLEANUP(setelmstat); 197 ATF_TC_HEAD(setelmstat, tc) 198 { 199 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT"); 200 atf_tc_set_md_var(tc, "require.user", "root"); 201 } 202 ATF_TC_BODY(setelmstat, tc) 203 { 204 if (!has_ses()) 205 atf_tc_skip("No ses devices found"); 206 207 for_one_ses_dev(do_setelmstat); 208 } 209 ATF_TC_CLEANUP(setelmstat, tc) 210 { 211 if (!has_ses()) 212 return; 213 214 for_one_ses_dev(do_setelmstat_cleanup); 215 } 216 217 218 static bool do_setencstat(const char *devname __unused, int fd) { 219 unsigned char encstat; 220 int r, i; 221 bool worked = false; 222 223 /* 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. 226 */ 227 encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT; 228 r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat); 229 ATF_REQUIRE_EQ(r, 0); 230 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) { 236 worked = true; 237 break; 238 } 239 usleep(100000); 240 } 241 if (!worked) { 242 /* Some enclosures don't support setting the enclosure status */ 243 return (false); 244 } else 245 return (true); 246 } 247 248 static bool do_setencstat_cleanup(const char *devname __unused, int fd) { 249 unsigned char encstat; 250 251 /* 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. 255 */ 256 encstat = 0; 257 ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat); 258 return (true); 259 } 260 261 ATF_TC_WITH_CLEANUP(setencstat); 262 ATF_TC_HEAD(setencstat, tc) 263 { 264 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT"); 265 atf_tc_set_md_var(tc, "require.user", "root"); 266 } 267 ATF_TC_BODY(setencstat, tc) 268 { 269 if (!has_ses()) 270 atf_tc_skip("No ses devices found"); 271 272 for_each_ses_dev(do_setencstat, O_RDWR); 273 } 274 ATF_TC_CLEANUP(setencstat, tc) 275 { 276 for_each_ses_dev(do_setencstat_cleanup, O_RDWR); 277 } 278 279 ATF_TP_ADD_TCS(tp) 280 { 281 282 /* 283 * Untested ioctls: 284 * 285 * * ENCIOC_INIT because SES doesn't need it and I don't have any 286 * SAF-TE devices. 287 * 288 * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally 289 * used for stuff like firmware updates 290 */ 291 ATF_TP_ADD_TC(tp, setelmstat); 292 ATF_TP_ADD_TC(tp, setencstat); 293 // TODO ENCIOC_SETELMSTAT 294 295 return (atf_no_error()); 296 } 297