1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/stropts.h> 30 #include <sys/debug.h> 31 #include <sys/isa_defs.h> 32 #include <sys/dditypes.h> 33 #include <sys/ddi_impldefs.h> 34 #include "devid_impl.h" 35 36 static int devid_str_decode_id(char *devidstr, ddi_devid_t *devidp, 37 char **minor_namep, impl_devid_t *id); 38 39 40 /* 41 * Validate device id. 42 */ 43 int 44 #ifdef _KERNEL 45 ddi_devid_valid(ddi_devid_t devid) 46 #else /* !_KERNEL */ 47 devid_valid(ddi_devid_t devid) 48 #endif /* _KERNEL */ 49 { 50 impl_devid_t *id = (impl_devid_t *)devid; 51 ushort_t type; 52 53 DEVID_ASSERT(devid != NULL); 54 55 if (id->did_magic_hi != DEVID_MAGIC_MSB) 56 return (DEVID_RET_INVALID); 57 58 if (id->did_magic_lo != DEVID_MAGIC_LSB) 59 return (DEVID_RET_INVALID); 60 61 if (id->did_rev_hi != DEVID_REV_MSB) 62 return (DEVID_RET_INVALID); 63 64 if (id->did_rev_lo != DEVID_REV_LSB) 65 return (DEVID_RET_INVALID); 66 67 type = DEVID_GETTYPE(id); 68 if ((type == DEVID_NONE) || (type > DEVID_MAXTYPE)) 69 return (DEVID_RET_INVALID); 70 71 return (DEVID_RET_VALID); 72 } 73 74 /* 75 * Return the sizeof a device id. If called with NULL devid it returns 76 * the amount of space needed to determine the size. 77 */ 78 size_t 79 #ifdef _KERNEL 80 ddi_devid_sizeof(ddi_devid_t devid) 81 #else /* !_KERNEL */ 82 devid_sizeof(ddi_devid_t devid) 83 #endif /* _KERNEL */ 84 { 85 impl_devid_t *id = (impl_devid_t *)devid; 86 87 if (id == NULL) 88 return (sizeof (*id) - sizeof (id->did_id)); 89 90 DEVID_ASSERT(DEVID_FUNC(devid_valid)(devid) == DEVID_RET_VALID); 91 92 return (sizeof (*id) + DEVID_GETLEN(id) - sizeof (id->did_id)); 93 } 94 95 /* 96 * Compare two device id's. 97 * -1 - less than 98 * 0 - equal 99 * 1 - greater than 100 */ 101 int 102 #ifdef _KERNEL 103 ddi_devid_compare(ddi_devid_t id1, ddi_devid_t id2) 104 #else /* !_KERNEL */ 105 devid_compare(ddi_devid_t id1, ddi_devid_t id2) 106 #endif /* _KERNEL */ 107 { 108 int rval; 109 impl_devid_t *i_id1 = (impl_devid_t *)id1; 110 impl_devid_t *i_id2 = (impl_devid_t *)id2; 111 ushort_t i_id1_type; 112 ushort_t i_id2_type; 113 114 DEVID_ASSERT((id1 != NULL) && (id2 != NULL)); 115 DEVID_ASSERT(DEVID_FUNC(devid_valid)(id1) == DEVID_RET_VALID); 116 DEVID_ASSERT(DEVID_FUNC(devid_valid)(id2) == DEVID_RET_VALID); 117 118 /* magic and revision comparison */ 119 if ((rval = bcmp(id1, id2, 4)) != 0) { 120 return (rval); 121 } 122 123 /* get current devid types */ 124 i_id1_type = DEVID_GETTYPE(i_id1); 125 i_id2_type = DEVID_GETTYPE(i_id2); 126 127 /* 128 * Originaly all page83 devids used DEVID_SCSI3_WWN. 129 * To avoid a possible uniqueness issue each type of page83 130 * encoding supported is represented as a separate 131 * devid type. If comparing DEVID_SCSI3_WWN against 132 * one of the new page83 encodings we assume that no 133 * uniqueness issue exists (since we had apparently been 134 * running with the old DEVID_SCSI3_WWN encoding without 135 * a problem). 136 */ 137 if ((i_id1_type == DEVID_SCSI3_WWN) || 138 (i_id2_type == DEVID_SCSI3_WWN)) { 139 /* 140 * Atleast one devid is using old scsi 141 * encode algorithm. Force devid types 142 * to same scheme for comparison. 143 */ 144 if (IS_DEVID_SCSI3_VPD_TYPE(i_id1_type)) { 145 i_id1_type = DEVID_SCSI3_WWN; 146 } 147 if (IS_DEVID_SCSI3_VPD_TYPE(i_id2_type)) { 148 i_id2_type = DEVID_SCSI3_WWN; 149 } 150 } 151 152 /* type comparison */ 153 if (i_id1_type != i_id2_type) { 154 return ((i_id1_type < i_id2_type) ? -1 : 1); 155 } 156 157 /* length comparison */ 158 if (DEVID_GETLEN(i_id1) != DEVID_GETLEN(i_id2)) { 159 return (DEVID_GETLEN(i_id1) < DEVID_GETLEN(i_id2) ? -1 : 1); 160 } 161 162 /* id comparison */ 163 rval = bcmp(i_id1->did_id, i_id2->did_id, DEVID_GETLEN(i_id1)); 164 165 return (rval); 166 } 167 168 /* 169 * Free a Device Id 170 */ 171 void 172 #ifdef _KERNEL 173 ddi_devid_free(ddi_devid_t devid) 174 #else /* !_KERNEL */ 175 devid_free(ddi_devid_t devid) 176 #endif /* _KERNEL */ 177 { 178 DEVID_ASSERT(devid != NULL); 179 DEVID_FREE(devid, DEVID_FUNC(devid_sizeof)(devid)); 180 } 181 182 /* 183 * Encode a device id into a string. See ddi_impldefs.h for details. 184 */ 185 char * 186 #ifdef _KERNEL 187 ddi_devid_str_encode(ddi_devid_t devid, char *minor_name) 188 #else /* !_KERNEL */ 189 devid_str_encode(ddi_devid_t devid, char *minor_name) 190 #endif /* _KERNEL */ 191 { 192 impl_devid_t *id = (impl_devid_t *)devid; 193 size_t driver_len, devid_len, slen; 194 char *sbuf, *dsp, *dp, ta; 195 int i, n, ascii; 196 197 /* "id0" is the encoded representation of a NULL device id */ 198 if (devid == NULL) { 199 if ((sbuf = DEVID_MALLOC(4)) == NULL) 200 return (NULL); 201 *(sbuf+0) = DEVID_MAGIC_MSB; 202 *(sbuf+1) = DEVID_MAGIC_LSB; 203 *(sbuf+2) = '0'; 204 *(sbuf+3) = 0; 205 return (sbuf); 206 } 207 208 /* verify input */ 209 if (DEVID_FUNC(devid_valid)(devid) != DEVID_RET_VALID) 210 return (NULL); 211 212 /* scan the driver hint to see how long the hint is */ 213 for (driver_len = 0; driver_len < DEVID_HINT_SIZE; driver_len++) 214 if (id->did_driver[driver_len] == '\0') 215 break; 216 217 /* scan the contained did_id to see if it meets ascii requirements */ 218 devid_len = DEVID_GETLEN(id); 219 for (ascii = 1, i = 0; i < devid_len; i++) 220 if (!DEVID_IDBYTE_ISASCII(id->did_id[i])) { 221 ascii = 0; 222 break; 223 } 224 225 /* some types should always go hex even if they look ascii */ 226 if (DEVID_TYPE_BIN_FORCEHEX(id->did_type_lo)) 227 ascii = 0; 228 229 /* set the length of the resulting string */ 230 slen = 2 + 1; /* <magic><rev> "id1" */ 231 slen += 1 + driver_len + 1 + 1; /* ",<driver>@<type>" */ 232 slen += ascii ? devid_len : (devid_len * 2); /* did_id field */ 233 if (minor_name) { 234 slen += 1; /* '/' */ 235 slen += strlen(minor_name); /* len of minor_name */ 236 } 237 slen += 1; /* NULL */ 238 239 /* allocate string */ 240 if ((sbuf = DEVID_MALLOC(slen)) == NULL) 241 return (NULL); 242 243 /* perform encode of id to hex string */ 244 dsp = sbuf; 245 *dsp++ = id->did_magic_hi; 246 *dsp++ = id->did_magic_lo; 247 *dsp++ = DEVID_REV_BINTOASCII(id->did_rev_lo); 248 *dsp++ = ','; 249 for (i = 0; i < driver_len; i++) 250 *dsp++ = id->did_driver[i]; 251 *dsp++ = '@'; 252 ta = DEVID_TYPE_BINTOASCII(id->did_type_lo); 253 if (ascii) 254 ta = DEVID_TYPE_SETASCII(ta); 255 *dsp++ = ta; 256 for (i = 0, dp = &id->did_id[0]; i < devid_len; i++, dp++) { 257 if (ascii) { 258 if (*dp == ' ') 259 *dsp++ = '_'; 260 else if (*dp == 0x00) 261 *dsp++ = '~'; 262 else 263 *dsp++ = *dp; 264 } else { 265 n = ((*dp) >> 4) & 0xF; 266 *dsp++ = (n < 10) ? (n + '0') : (n + ('a' - 10)); 267 n = (*dp) & 0xF; 268 *dsp++ = (n < 10) ? (n + '0') : (n + ('a' - 10)); 269 } 270 } 271 272 if (minor_name) { 273 *dsp++ = '/'; 274 (void) strcpy(dsp, minor_name); 275 } else 276 *dsp++ = 0; 277 278 /* ensure that (strlen + 1) is correct length for free */ 279 DEVID_ASSERT((strlen(sbuf) + 1) == slen); 280 return (sbuf); 281 } 282 283 /* free the string returned by devid_str_encode */ 284 void 285 #ifdef _KERNEL 286 ddi_devid_str_free(char *devidstr) 287 #else /* !_KERNEL */ 288 devid_str_free(char *devidstr) 289 #endif /* _KERNEL */ 290 { 291 DEVID_FREE(devidstr, strlen(devidstr) + 1); 292 } 293 294 /* 295 * given the string representation of a device id returned by calling 296 * devid_str_encode (passed in as devidstr), return pointers to the 297 * broken out devid and minor_name as requested. Devidstr remains 298 * allocated and unmodified. The devid returned in *devidp should be freed by 299 * calling devid_free. The minor_name returned in minor_namep should 300 * be freed by calling devid_str_free(minor_namep). 301 * 302 * See ddi_impldefs.h for format details. 303 */ 304 int 305 #ifdef _KERNEL 306 ddi_devid_str_decode( 307 #else /* !_KERNEL */ 308 devid_str_decode( 309 #endif /* _KERNEL */ 310 char *devidstr, ddi_devid_t *devidp, char **minor_namep) 311 { 312 return (devid_str_decode_id(devidstr, devidp, minor_namep, NULL)); 313 } 314 315 /* implementation for (ddi_)devid_str_decode */ 316 static int 317 devid_str_decode_id(char *devidstr, ddi_devid_t *devidp, 318 char **minor_namep, impl_devid_t *id) 319 { 320 char *str, *msp, *dsp, *dp, ta; 321 int slen, devid_len, ascii, i, n, c, pre_alloc = FALSE; 322 unsigned short id_len, type; /* for hibyte/lobyte */ 323 324 if (devidp != NULL) 325 *devidp = NULL; 326 if (minor_namep != NULL) 327 *minor_namep = NULL; 328 if (id != NULL) 329 pre_alloc = TRUE; 330 331 if (devidstr == NULL) 332 return (DEVID_FAILURE); 333 334 /* the string must atleast contain the ascii two byte header */ 335 slen = strlen(devidstr); 336 if ((slen < 3) || (devidstr[0] != DEVID_MAGIC_MSB) || 337 (devidstr[1] != DEVID_MAGIC_LSB)) 338 return (DEVID_FAILURE); 339 340 /* "id0" is the encoded representation of a NULL device id */ 341 if ((devidstr[2] == '0') && (slen == 3)) 342 return (DEVID_SUCCESS); 343 344 /* "id1,@S0" is the shortest possible, reject if shorter */ 345 if (slen < 7) 346 return (DEVID_FAILURE); 347 348 /* find the optional minor name, start after ',' */ 349 if ((msp = strchr(&devidstr[4], '/')) != NULL) 350 msp++; 351 352 /* skip devid processing if we are not asked to return it */ 353 if (devidp) { 354 /* find the required '@' separator */ 355 if ((str = strchr(devidstr, '@')) == NULL) 356 return (DEVID_FAILURE); 357 str++; /* skip '@' */ 358 359 /* pick up <type> after the '@' and verify */ 360 ta = *str++; 361 ascii = DEVID_TYPE_ISASCII(ta); 362 type = DEVID_TYPE_ASCIITOBIN(ta); 363 if (type > DEVID_MAXTYPE) 364 return (DEVID_FAILURE); 365 366 /* determine length of id->did_id field */ 367 if (msp == NULL) 368 id_len = strlen(str); 369 else 370 id_len = msp - str - 1; 371 372 /* account for encoding: with hex, binary is half the size */ 373 if (!ascii) { 374 /* hex id field must be even length */ 375 if (id_len & 1) 376 return (DEVID_FAILURE); 377 id_len /= 2; 378 } 379 380 /* add in size of the binary devid header */ 381 devid_len = id_len + sizeof (*id) - sizeof (id->did_id); 382 383 /* 384 * Allocate space for devid if we are asked to decode it 385 * decode it and space wasn't pre-allocated. 386 */ 387 if (pre_alloc == FALSE) { 388 if ((id = (impl_devid_t *)DEVID_MALLOC( 389 devid_len)) == NULL) 390 return (DEVID_FAILURE); 391 } 392 393 /* decode header portion of the string into the binary devid */ 394 dsp = devidstr; 395 id->did_magic_hi = *dsp++; /* <magic> "id" */ 396 id->did_magic_lo = *dsp++; 397 id->did_rev_hi = 0; 398 id->did_rev_lo = 399 DEVID_REV_ASCIITOBIN(*dsp); /* <rev> "1" */ 400 dsp++; /* skip "1" */ 401 dsp++; /* skip "," */ 402 for (i = 0; i < DEVID_HINT_SIZE; i++) { /* <driver>@ */ 403 if (*dsp == '@') 404 break; 405 id->did_driver[i] = *dsp++; 406 } 407 for (; i < DEVID_HINT_SIZE; i++) 408 id->did_driver[i] = 0; 409 410 /* we must now be at the '@' */ 411 if (*dsp != '@') 412 goto efree; 413 414 /* set the type and length */ 415 DEVID_FORMTYPE(id, type); 416 DEVID_FORMLEN(id, id_len); 417 418 /* decode devid portion of string into the binary */ 419 for (i = 0, dsp = str, dp = &id->did_id[0]; 420 i < id_len; i++, dp++) { 421 if (ascii) { 422 if (*dsp == '_') 423 *dp = ' '; 424 else if (*dsp == '~') 425 *dp = 0x00; 426 else 427 *dp = *dsp; 428 dsp++; 429 } else { 430 c = *dsp++; 431 if (c >= '0' && c <= '9') 432 n = (c - '0') & 0xFF; 433 else if (c >= 'a' && c <= 'f') 434 n = (c - ('a' - 10)) & 0xFF; 435 else 436 goto efree; 437 n <<= 4; 438 c = *dsp++; 439 if (c >= '0' && c <= '9') 440 n |= (c - '0') & 0xFF; 441 else if (c >= 'a' && c <= 'f') 442 n |= (c - ('a' - 10)) & 0xFF; 443 else 444 goto efree; 445 *dp = n; 446 } 447 } 448 449 /* verify result */ 450 if (DEVID_FUNC(devid_valid)((ddi_devid_t)id) != DEVID_RET_VALID) 451 goto efree; 452 } 453 454 /* duplicate minor_name if we are asked to decode it */ 455 if (minor_namep && msp) { 456 if ((*minor_namep = DEVID_MALLOC(strlen(msp) + 1)) == NULL) 457 goto efree; 458 (void) strcpy(*minor_namep, msp); 459 } 460 461 /* return pointer to binary */ 462 if (devidp) 463 *devidp = (ddi_devid_t)id; 464 return (DEVID_SUCCESS); 465 466 efree: 467 if ((pre_alloc == FALSE) && (id)) 468 DEVID_FREE(id, devid_len); 469 return (DEVID_FAILURE); 470 } 471 472 473 /* 474 * Compare two device id's in string form 475 * -1 - id1 less than id2 476 * 0 - equal 477 * 1 - id1 greater than id2 478 */ 479 int 480 #ifdef _KERNEL 481 ddi_devid_str_compare(char *id1_str, char *id2_str) 482 #else /* !_KERNEL */ 483 devid_str_compare(char *id1_str, char *id2_str) 484 #endif /* _KERNEL */ 485 { 486 int rval = DEVID_FAILURE; 487 ddi_devid_t devid1; 488 ddi_devid_t devid2; 489 #ifdef _KERNEL 490 /* kernel use static protected by lock. */ 491 static kmutex_t id_lock; 492 static uchar_t id1[sizeof (impl_devid_t) + MAXPATHLEN]; 493 static uchar_t id2[sizeof (impl_devid_t) + MAXPATHLEN]; 494 #else /* !_KERNEL */ 495 /* userland place on stack, since malloc might fail */ 496 uchar_t id1[sizeof (impl_devid_t) + MAXPATHLEN]; 497 uchar_t id2[sizeof (impl_devid_t) + MAXPATHLEN]; 498 #endif /* _KERNEL */ 499 500 #ifdef _KERNEL 501 mutex_enter(&id_lock); 502 #endif /* _KERNEL */ 503 504 /* 505 * encode string form of devid 506 */ 507 if ((devid_str_decode_id(id1_str, &devid1, NULL, (impl_devid_t *)id1) == 508 DEVID_SUCCESS) && 509 (devid_str_decode_id(id2_str, &devid2, NULL, (impl_devid_t *)id2) == 510 DEVID_SUCCESS)) { 511 rval = DEVID_FUNC(devid_compare)(devid1, devid2); 512 } 513 514 #ifdef _KERNEL 515 mutex_exit(&id_lock); 516 #endif /* _KERNEL */ 517 518 return (rval); 519 } 520