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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <string.h> 27 #include <stdlib.h> 28 29 #include "PayloadReader.h" 30 31 #define ITER_CONT_BYTE_LEN 4 32 #define IS_ITERATED(pathDef) \ 33 (pathDef->def->iterationType != FRU_NOT_ITERATED) 34 35 // functions to place bit data properly. 36 static fru_errno_t 37 writeBits(uint64_t bitData, size_t bitLength, 38 uint8_t *data, size_t dataLength, size_t bitOffset) 39 { 40 if ((bitLength > 64) && 41 (bitOffset > 64) && 42 (dataLength > 8) && 43 (bitOffset > (dataLength * 8))) 44 return (FRU_FAILURE); 45 // move the bit data into place 46 bitData = (bitData << (64-bitLength)); 47 bitData = (bitData >> bitOffset); 48 49 // create a mask to clear the old data. 50 uint64_t mask = 0; 51 for (size_t i = 0; i < bitLength; i++) { 52 mask = ((mask << 1) + 1); 53 } 54 mask = (mask << (64-bitLength)); 55 mask = (mask >> bitOffset); 56 mask = (mask ^ 0xFFFFFFFFFFFFFFFFULL); 57 58 // get the data out of the byte array. 59 uint64_t rd = 0; 60 memcpy((void *)&rd, (void *)data, dataLength); 61 62 // clear the old data 63 rd = (rd & mask); 64 // put in the new data. 65 rd = (rd | bitData); 66 67 // write the data back to the buffer. 68 memcpy((void *)data, (void *)&rd, dataLength); 69 return (FRU_SUCCESS); 70 } 71 72 static fru_errno_t 73 readBits(size_t bitLength, uint8_t *data, 74 size_t dataLength, int bitOffset, uint64_t *ret) 75 { 76 if ((bitLength > 64) || 77 (bitLength < 0) || 78 (bitOffset > 64) || 79 (dataLength > 8) || 80 (bitOffset > (dataLength * 8))) 81 return (FRU_FAILURE); 82 // get the data out of the byte array. 83 uint64_t rc = 0; 84 memcpy((void *)&rc, (void *)data, dataLength); 85 86 rc = (rc << bitOffset); 87 rc = (rc >> (64 - bitLength)); 88 *ret = rc; 89 return (FRU_SUCCESS); 90 } 91 92 // =========================================================================== 93 // caller is to be sure elemDef is contained by recDef. 94 int 95 PayloadReader::getOffsetIntoRecord(fru_regdef_t *recDef, 96 fru_regdef_t *elemDef) 97 { 98 int rc = 0; 99 for (int i = 0; i < recDef->enumCount; i++) { 100 if (strcmp(recDef->enumTable[i].text, elemDef->name) == 0) 101 return (rc); 102 const fru_regdef_t *tmpDef = fru_reg_lookup_def_by_name( 103 (char *)recDef->enumTable[i].text); 104 rc += tmpDef->payloadLen; 105 } 106 return(0); 107 } 108 109 // =========================================================================== 110 // return -1 on error. 111 int 112 PayloadReader::calcOffset(int iterType, 113 uint8_t head, uint8_t tail, 114 uint8_t iterThere, uint8_t iterPoss, 115 size_t length, int index, 116 fru_errno_t *err) 117 { 118 *err = FRU_SUCCESS; 119 switch (iterType) { 120 case FRU_FIFO: 121 case FRU_Linear: 122 { 123 if (index == PathDef::lastIteration) 124 return (length * tail); 125 return (length * index); 126 break; 127 } 128 case FRU_Circular: 129 case FRU_LIFO: 130 { 131 if (index == PathDef::lastIteration) { 132 if (iterType == FRU_LIFO) 133 return (length * head); 134 return (length * tail); 135 } 136 137 // For reading they are oposite. 138 if (iterType == FRU_Circular) { 139 return (length * ((head + index) % iterPoss)); 140 } else { 141 int abs = tail - index; 142 if (abs < 0) 143 // abs is negative here 144 abs = iterPoss + abs; 145 return (length * abs); 146 } 147 break; 148 } 149 } 150 *err = FRU_FAILURE; 151 return (-1); 152 } 153 154 // =========================================================================== 155 // return -1 on error. 156 int 157 PayloadReader::getIterationOffset(uint8_t *iter, int iterLen, 158 PathDef *path, int *rcIterThere, 159 fru_errno_t *err, 160 int onlyFindingIterThereFlag) 161 { 162 int rc = 0; 163 164 // read the iteration control bytes first because we may ONLY need 165 // them. 166 uint8_t head = iter[0]; 167 uint8_t tail = iter[1]; 168 uint8_t iterThere = iter[2]; 169 uint8_t iterPoss = iter[3]; 170 171 // the '+' symbol on anything is an error here 172 if (path->iterIndex == PathDef::addIteration) { 173 *err = FRU_INVALPATH; 174 return (-1); 175 } 176 177 // check assumptions for next calls. 178 if (iterPoss != path->def->iterationCount) { 179 *err = FRU_DATACORRUPT; 180 return (-1); 181 } 182 183 if (onlyFindingIterThereFlag == ITER_THERE_ONLY) { 184 if (rcIterThere != NULL) { 185 *rcIterThere = iterThere; 186 } 187 *err = FRU_SUCCESS; 188 return (ITER_CONT_BYTE_LEN); 189 } 190 191 if ((path->iterIndex != PathDef::addIteration) && 192 (path->iterIndex != PathDef::lastIteration) && 193 (path->iterIndex >= iterThere)) { 194 *err = FRU_DATANOTFOUND; 195 return (-1); 196 } 197 198 // don't forget to skip the iteration control bytes!!! 199 int length = ((path->def->payloadLen - ITER_CONT_BYTE_LEN) 200 /path->def->iterationCount); 201 202 rc = calcOffset(path->def->iterationType, 203 head, tail, iterThere, iterPoss, 204 length, path->iterIndex, err); 205 if (rc == -1) { 206 // error set by calcOffset 207 return (-1); 208 } 209 210 *err = FRU_SUCCESS; 211 return (ITER_CONT_BYTE_LEN + rc); 212 } 213 214 // =========================================================================== 215 // Iff onlyFindingIterThereFlag is set data is ignored and dataLen will be set 216 // to the number of iterations which are actually in the seeprom. 217 fru_errno_t 218 PayloadReader::readRecurse(PathDef *path, 219 uint8_t *cur, size_t curLen, 220 void **data, size_t *dataLen, 221 int onlyFindingIterThereFlag) 222 { 223 fru_errno_t rc = FRU_SUCCESS; 224 size_t calc_data_len = 0; 225 226 if (path->next == NULL) { 227 228 // alway go ahead and do the iterated thing. If we are not a 229 // field then the onlyFindingIterThereFlag should be set. 230 // Check this afterward. 231 int offset = 0; 232 int iterThere = 0; 233 // zzz altering the length things again... 234 if (IS_ITERATED(path)) { 235 // we are iterated. 236 calc_data_len = (path->def->payloadLen 237 -ITER_CONT_BYTE_LEN)/ 238 path->def->iterationCount; 239 // zzz still have to figure out the bit offsets for bit iterations... 240 offset = getIterationOffset(cur, curLen, path, 241 &iterThere, &rc, 242 onlyFindingIterThereFlag); 243 if (offset == -1) 244 return (rc); 245 246 // done 247 if (onlyFindingIterThereFlag) { 248 *dataLen = iterThere; 249 return (FRU_SUCCESS); 250 } 251 } else { 252 // done but this thing was not an iteration!!! 253 if (onlyFindingIterThereFlag) { 254 return (FRU_INVALPATH); 255 } 256 257 calc_data_len = path->def->payloadLen; 258 offset = 0; 259 } 260 // end zzz 261 262 // now make sure we have a field. 263 if (path->def->dataType == FDTYPE_Record) { 264 return (FRU_NOTFIELD); 265 } 266 267 // allocate and copy. 268 if (path->def->dataType == FDTYPE_Binary) { 269 uint64_t *eData = (uint64_t *)malloc(sizeof (*eData)); 270 if (eData == NULL) { 271 return (FRU_FAILURE); 272 } 273 274 int bitLength = path->def->dataLength; 275 // iterated bit field adjust acordingly. 276 if (IS_ITERATED(path)) { 277 bitLength = (bitLength-(ITER_CONT_BYTE_LEN*8))/ 278 path->def->iterationCount; 279 } 280 281 rc = readBits(bitLength, &(cur[offset]), 282 calc_data_len, 0, eData); 283 if (rc != FRU_SUCCESS) { 284 free(eData); 285 return (rc); 286 } 287 *data = (void *)eData; 288 *dataLen = sizeof (*eData); 289 } else if (path->def->dataType == FDTYPE_Enumeration) { 290 unsigned char *eData 291 = (unsigned char *)malloc(sizeof (uint64_t)); 292 if (eData == NULL) { 293 return (FRU_FAILURE); 294 } 295 /* copy the correct number of bytes to eData */ 296 memset(eData, 0x00, sizeof (uint64_t)); 297 memcpy(&(eData[(sizeof (uint64_t) - (calc_data_len))]), 298 &(cur[offset]), 299 (calc_data_len)); 300 *data = (void*)eData; 301 *dataLen = sizeof (uint64_t); 302 } else { 303 void *rc_data = malloc(calc_data_len); 304 if (rc_data == NULL) { 305 return (FRU_FAILURE); 306 } 307 memcpy(rc_data, &(cur[offset]), calc_data_len); 308 *data = rc_data; 309 *dataLen = calc_data_len; 310 } 311 312 return (FRU_SUCCESS); 313 } 314 315 // At this point we know the entry is some sort of record. 316 317 int newOffset = 0, newLength = 0; 318 if (IS_ITERATED(path)) { 319 320 // zzz still have to figure out the bit offsets for bit iterations... 321 newOffset = getIterationOffset(cur, curLen, 322 path, NULL, &rc, NORMAL_READ); 323 if (newOffset == -1) 324 return (rc); 325 } 326 327 newOffset += getOffsetIntoRecord(path->def, path->next->def); 328 newLength = path->next->def->payloadLen; 329 330 return (readRecurse(path->next, &(cur[newOffset]), newLength, 331 data, dataLen, onlyFindingIterThereFlag)); 332 } 333 334 // =========================================================================== 335 // will send the data back in (data,dataLen) 336 fru_errno_t 337 PayloadReader::readData(PathDef *path, Ancestor *curDef, 338 int instWICur, 339 uint8_t *payload, size_t payloadLen, 340 void **data, size_t *dataLen) 341 { 342 int offset = curDef->getInstOffset(instWICur); 343 return (readRecurse(path, &(payload[offset]), payloadLen-offset, 344 data, dataLen, NORMAL_READ)); 345 } 346 347 // =========================================================================== 348 fru_errno_t 349 PayloadReader::findIterThere(PathDef *path, Ancestor *curDef, 350 int instWICur, 351 uint8_t *payload, size_t payloadLen, 352 int *numThere) 353 { 354 int offset = curDef->getInstOffset(instWICur); 355 size_t tmp_num = 0; 356 fru_errno_t err = readRecurse(path, &(payload[offset]), 357 payloadLen-offset, NULL, &tmp_num, ITER_THERE_ONLY); 358 359 if (err == FRU_SUCCESS) { 360 int tmp_num_there = (int)tmp_num; 361 if (tmp_num_there != tmp_num) { 362 return (FRU_FAILURE); 363 } 364 *numThere = tmp_num_there; 365 } 366 return (err); 367 } 368 369 static fru_errno_t 370 update_iter_cont_bytes(PathDef *path, uint8_t *cur, size_t curLen) 371 { 372 // update the iteration control information 373 uint8_t *head = &(cur[0]); 374 uint8_t *tail = &(cur[1]); 375 uint8_t *numThere = &(cur[2]); 376 // This never changes. 377 uint8_t numPoss = cur[3]; 378 379 if (numPoss != path->def->iterationCount) { 380 return (FRU_DATACORRUPT); 381 } 382 383 // Remember that when the iteration is added the head and the tail both 384 // equal 0 (ie point to 0). So if we are empty when we are updating 385 // then we don't have to alter the head or tail values. We simply add 386 // one to the numThere. 387 if (*numThere != 0) { 388 switch (path->def->iterationType) { 389 case FRU_Linear: 390 // this will flag an error when Linear can't 391 // hold anymore. 392 if ((*tail + 1) == numPoss) 393 return (FRU_ITERFULL); 394 /* Fall through */ 395 case FRU_FIFO: 396 // if we are not at the end move the tail. 397 if (*tail != (numPoss-1)) 398 *tail = *tail+1; 399 break; 400 401 case FRU_Circular: 402 case FRU_LIFO: 403 // this is the same except LIFO is read 404 // BACKWARDS 405 406 // move the tail. 407 *tail = *tail + 1; 408 // if the tail hits the end wrap around. 409 if (*tail == numPoss) 410 *tail = 0; 411 // if tail catches head move the head. 412 if (*tail == *head) { 413 // if head hits the end wrap around. 414 if (++(*head) == numPoss) 415 *head = 0; 416 } 417 break; 418 } 419 } 420 if ((*numThere) < numPoss) { 421 // add one IFF we are not full 422 *numThere = *numThere + 1; 423 } 424 425 return (FRU_SUCCESS); 426 } 427 428 // =========================================================================== 429 fru_errno_t 430 PayloadReader::updateRecurse(PathDef *path, 431 uint8_t *cur, size_t curLen, 432 void *data, size_t dataLen) 433 { 434 fru_errno_t rc = FRU_SUCCESS; 435 436 if (path->next == NULL) { 437 438 // Delay checking for Records until after this which will 439 // allow for [+] notation for Iterated Records. 440 // if this is updating an iteration AND we are adding one... 441 if (IS_ITERATED(path) && 442 (path->iterIndex == PathDef::addIteration)) { 443 return (update_iter_cont_bytes(path, cur, curLen)); 444 } 445 446 if (path->def->dataType == FDTYPE_Record) { 447 return (FRU_NOTFIELD); 448 } 449 450 int offset = 0; 451 int calcLen = 0; 452 int dummy = 0; 453 // zzz altering the length things again... 454 if (IS_ITERATED(path)) { 455 // we are iterated. 456 calcLen = (path->def->payloadLen-ITER_CONT_BYTE_LEN)/ 457 path->def->iterationCount; 458 // zzz still have to figure out the bit offsets 459 offset = getIterationOffset(cur, curLen, 460 path, &dummy, &rc, NORMAL_READ); 461 if (offset == -1) 462 return (rc); 463 } else { 464 calcLen = path->def->payloadLen; 465 offset = 0; 466 } 467 // end zzz 468 469 // once again convert enums for the user again. 470 if (path->def->dataType == FDTYPE_Binary) { 471 int bitLength = path->def->dataLength; 472 // iterated bit field adjust acordingly. 473 if (path->def->iterationType != FRU_NOT_ITERATED) { 474 bitLength = (bitLength - 32)/ 475 path->def->iterationCount; 476 } 477 478 rc = writeBits (*(uint64_t *)data, bitLength, 479 &(cur[offset]), calcLen, 0); 480 if (rc != FRU_SUCCESS) 481 return (rc); 482 } else if (path->def->dataType == FDTYPE_Enumeration) { 483 unsigned char *tmp = (unsigned char *)data; 484 memcpy(&(cur[offset]), 485 &(tmp[(sizeof (uint64_t) - (calcLen))]), 486 calcLen); 487 } else { 488 // copy into and return. 489 memcpy(&(cur[offset]), data, dataLen); 490 } 491 492 return (FRU_SUCCESS); 493 } 494 495 int newOffset = 0, newLength = 0; 496 int dummy = 0; 497 if (path->def->iterationType != FRU_NOT_ITERATED) { 498 499 // zzz still have to figure out the bit offsets 500 newOffset = getIterationOffset(cur, curLen, path, 501 &dummy, &rc, NORMAL_READ); 502 if (newOffset == -1) 503 return (rc); 504 } 505 newOffset += getOffsetIntoRecord(path->def, path->next->def); 506 newLength = path->next->def->payloadLen; 507 508 return (updateRecurse(path->next, &(cur[newOffset]), newLength, 509 data, dataLen)); 510 } 511 512 // =========================================================================== 513 // will update the data in payload which can then be written back. 514 fru_errno_t 515 PayloadReader::updateData(PathDef *path, Ancestor *ancestorDef, 516 int instWICur, 517 uint8_t *payload, size_t payloadLen, 518 void *data, size_t dataLen) 519 { 520 // verify the user data first before doing any major work. 521 int calcLen = 0; 522 PathDef *prev = path; 523 PathDef *cur = path; 524 while (cur != NULL) { 525 prev = cur; 526 cur = cur->next; 527 } 528 529 // unless we are updateing with [+] symbol 530 // (which means we don't have any data length at all.) 531 if (prev->iterIndex != PathDef::addIteration) { 532 if (IS_ITERATED(prev)) { 533 calcLen = (prev->def->payloadLen-ITER_CONT_BYTE_LEN)/ 534 prev->def->iterationCount; 535 } else { 536 calcLen = prev->def->payloadLen; 537 } 538 // the sizeof the data for Binary or Enumeration MUST 539 // be uint64_t 540 if ((prev->def->dataType == FDTYPE_Enumeration) || 541 (prev->def->dataType == FDTYPE_Binary)) { 542 if (dataLen != sizeof (uint64_t)) 543 return (FRU_INVALDATASIZE); 544 // all others must be shorter than the space available. 545 } else { 546 if (dataLen > calcLen) 547 return (FRU_INVALDATASIZE); 548 } 549 } 550 551 int offset = ancestorDef->getInstOffset(instWICur); 552 return (updateRecurse(path, &(payload[offset]), payloadLen-offset, 553 data, dataLen)); 554 } 555