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
writeBits(uint64_t bitData,size_t bitLength,uint8_t * data,size_t dataLength,size_t bitOffset)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
readBits(size_t bitLength,uint8_t * data,size_t dataLength,int bitOffset,uint64_t * ret)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
getOffsetIntoRecord(fru_regdef_t * recDef,fru_regdef_t * elemDef)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
calcOffset(int iterType,uint8_t head,uint8_t tail,uint8_t iterThere,uint8_t iterPoss,size_t length,int index,fru_errno_t * err)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
getIterationOffset(uint8_t * iter,int iterLen,PathDef * path,int * rcIterThere,fru_errno_t * err,int onlyFindingIterThereFlag)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
readRecurse(PathDef * path,uint8_t * cur,size_t curLen,void ** data,size_t * dataLen,int onlyFindingIterThereFlag)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
readData(PathDef * path,Ancestor * curDef,int instWICur,uint8_t * payload,size_t payloadLen,void ** data,size_t * dataLen)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
findIterThere(PathDef * path,Ancestor * curDef,int instWICur,uint8_t * payload,size_t payloadLen,int * numThere)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
update_iter_cont_bytes(PathDef * path,uint8_t * cur,size_t curLen)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
updateRecurse(PathDef * path,uint8_t * cur,size_t curLen,void * data,size_t dataLen)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
updateData(PathDef * path,Ancestor * ancestorDef,int instWICur,uint8_t * payload,size_t payloadLen,void * data,size_t dataLen)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