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.
38static fru_errno_t
39writeBits(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
74static fru_errno_t
75readBits(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.
96int
97PayloadReader::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.
113int
114PayloadReader::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.
158int
159PayloadReader::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.
219fru_errno_t
220PayloadReader::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)
338fru_errno_t
339PayloadReader::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// ===========================================================================
350fru_errno_t
351PayloadReader::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
371static fru_errno_t
372update_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// ===========================================================================
431fru_errno_t
432PayloadReader::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.
516fru_errno_t
517PayloadReader::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