xref: /illumos-gate/usr/src/lib/libfru/libfru/PayloadReader.cc (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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
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
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
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
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
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
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
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
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
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
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
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