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