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