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_NOSORT, NULL, &g); 57 ATF_REQUIRE_EQ(r, 0); 58 if (g.gl_pathc == 0) 59 atf_tc_skip("No ses devices found"); 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 struct ses_status_dev_slot *sslot; 88 89 if (last_elm_type != map[elm_idx].elm_type) { 90 /* skip overall elements */ 91 last_elm_type = map[elm_idx].elm_type; 92 continue; 93 } 94 elmstat.elm_idx = elm_idx; 95 if (map[elm_idx].elm_type == ELMTYP_DEVICE || 96 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV) 97 { 98 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat); 99 ATF_REQUIRE_EQ(r, 0); 100 101 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0]; 102 sslot = (struct ses_status_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 117 if (last_elm_type != map[elm_idx].elm_type) { 118 /* skip overall elements */ 119 last_elm_type = map[elm_idx].elm_type; 120 continue; 121 } 122 elmstat.elm_idx = elm_idx; 123 if (map[elm_idx].elm_type == ELMTYP_DEVICE || 124 map[elm_idx].elm_type == ELMTYP_ARRAY_DEV) 125 { 126 int i; 127 128 r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat); 129 ATF_REQUIRE_EQ(r, 0); 130 131 sslot = (struct ses_status_dev_slot*)&elmstat.cstat[0]; 132 133 for (i = 0; i < 10; i++) { 134 r = ioctl(fd, ENCIOC_GETELMSTAT, 135 (caddr_t)&elmstat); 136 ATF_REQUIRE_EQ(r, 0); 137 if (0 == ses_status_dev_slot_get_ident(sslot)) { 138 /* Needs more time to take effect */ 139 usleep(100000); 140 } 141 } 142 ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0); 143 144 } 145 } 146 147 free(map); 148 return (true); 149 } 150 151 /* 152 * sg_ses doesn't provide "dump and restore" functionality. The closest is to 153 * dump status page 2, then manually edit the file to set every individual 154 * element select bit, then load the entire file. But that is much too hard. 155 * Instead, we'll just clear every ident bit. 156 */ 157 static bool 158 do_setelmstat_cleanup(const char *devname __unused, int fd __unused) { 159 encioc_element_t *map; 160 unsigned elm_idx; 161 unsigned nobj; 162 int r; 163 elm_type_t last_elm_type = -1; 164 165 r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj); 166 ATF_REQUIRE_EQ(r, 0); 167 168 map = calloc(nobj, sizeof(encioc_element_t)); 169 ATF_REQUIRE(map != NULL); 170 r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map); 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 cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0]; 187 188 ses_ctrl_common_set_select(&cslot->common, 1); 189 ses_ctrl_dev_slot_set_rqst_ident(cslot, 0); 190 r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat); 191 } 192 } 193 194 return(true); 195 } 196 197 198 ATF_TC_WITH_CLEANUP(setelmstat); 199 ATF_TC_HEAD(setelmstat, tc) 200 { 201 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT"); 202 atf_tc_set_md_var(tc, "require.user", "root"); 203 } 204 ATF_TC_BODY(setelmstat, tc) 205 { 206 for_one_ses_dev(do_setelmstat); 207 } 208 ATF_TC_CLEANUP(setelmstat, tc) 209 { 210 for_one_ses_dev(do_setelmstat_cleanup); 211 } 212 213 214 static bool do_setencstat(const char *devname __unused, int fd) { 215 unsigned char encstat; 216 int r, i; 217 bool worked = false; 218 219 /* 220 * SES provides no way to read the current setting of the enclosure 221 * control page common status bits. So we'll blindly set CRIT. 222 */ 223 encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT; 224 r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat); 225 ATF_REQUIRE_EQ(r, 0); 226 227 /* Check that the status has changed */ 228 for (i = 0; i < 10; i++) { 229 r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat); 230 ATF_REQUIRE_EQ(r, 0); 231 if (encstat & SES_CTRL_PAGE_CRIT_MASK) { 232 worked = true; 233 break; 234 } 235 usleep(100000); 236 } 237 if (!worked) { 238 /* Some enclosures don't support setting the enclosure status */ 239 return (false); 240 } else 241 return (true); 242 } 243 244 static bool do_setencstat_cleanup(const char *devname __unused, int fd) { 245 unsigned char encstat; 246 247 /* 248 * SES provides no way to read the current setting of the enclosure 249 * control page common status bits. So we don't know what they were 250 * set to before the test. We'll blindly clear all bits. 251 */ 252 encstat = 0; 253 ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat); 254 return (true); 255 } 256 257 ATF_TC_WITH_CLEANUP(setencstat); 258 ATF_TC_HEAD(setencstat, tc) 259 { 260 atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT"); 261 atf_tc_set_md_var(tc, "require.user", "root"); 262 } 263 ATF_TC_BODY(setencstat, tc) 264 { 265 for_each_ses_dev(do_setencstat, O_RDWR); 266 } 267 ATF_TC_CLEANUP(setencstat, tc) 268 { 269 for_each_ses_dev(do_setencstat_cleanup, O_RDWR); 270 } 271 272 ATF_TP_ADD_TCS(tp) 273 { 274 275 /* 276 * Untested ioctls: 277 * 278 * * ENCIOC_INIT because SES doesn't need it and I don't have any 279 * SAF-TE devices. 280 * 281 * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally 282 * used for stuff like firmware updates 283 */ 284 ATF_TP_ADD_TC(tp, setelmstat); 285 ATF_TP_ADD_TC(tp, setencstat); 286 // TODO ENCIOC_SETELMSTAT 287 288 return (atf_no_error()); 289 } 290