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