OpenSolaris_b135/lib/libfru/libfru/PayloadReader.cc

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <string.h>
#include <stdlib.h>

#include "PayloadReader.h"

#define	ITER_CONT_BYTE_LEN 4
#define	IS_ITERATED(pathDef) \
(pathDef->def->iterationType != FRU_NOT_ITERATED)

// functions to place bit data properly.
static fru_errno_t
writeBits(uint64_t bitData, size_t bitLength,
	uint8_t *data, size_t dataLength, size_t bitOffset)
{
	if ((bitLength > 64) &&
		(bitOffset > 64) &&
		(dataLength > 8) &&
		(bitOffset > (dataLength * 8)))
		return (FRU_FAILURE);
	// move the bit data into place
	bitData = (bitData << (64-bitLength));
	bitData = (bitData >> bitOffset);

	// create a mask to clear the old data.
	uint64_t mask = 0;
	for (size_t i = 0; i < bitLength; i++) {
		mask = ((mask << 1) + 1);
	}
	mask = (mask << (64-bitLength));
	mask = (mask >> bitOffset);
	mask = (mask ^ 0xFFFFFFFFFFFFFFFFULL);

	// get the data out of the byte array.
	uint64_t rd = 0;
	memcpy((void *)&rd, (void *)data, dataLength);

	// clear the old data
	rd = (rd & mask);
	// put in the new data.
	rd = (rd | bitData);

	// write the data back to the buffer.
	memcpy((void *)data, (void *)&rd, dataLength);
	return (FRU_SUCCESS);
}

static fru_errno_t
readBits(size_t bitLength, uint8_t *data,
	size_t dataLength, int bitOffset, uint64_t *ret)
{
	if ((bitLength > 64) ||
		(bitLength < 0) ||
		(bitOffset > 64) ||
		(dataLength > 8) ||
		(bitOffset > (dataLength * 8)))
		return (FRU_FAILURE);
	// get the data out of the byte array.
	uint64_t rc = 0;
	memcpy((void *)&rc, (void *)data, dataLength);

	rc = (rc << bitOffset);
	rc = (rc >> (64 - bitLength));
	*ret = rc;
	return (FRU_SUCCESS);
}

// ===========================================================================
// caller is to be sure elemDef is contained by recDef.
int
PayloadReader::getOffsetIntoRecord(fru_regdef_t *recDef,
				fru_regdef_t *elemDef)
{
	int rc = 0;
	for (int i = 0; i < recDef->enumCount; i++) {
		if (strcmp(recDef->enumTable[i].text, elemDef->name) == 0)
			return (rc);
		const fru_regdef_t *tmpDef = fru_reg_lookup_def_by_name(
					(char *)recDef->enumTable[i].text);
		rc += tmpDef->payloadLen;
	}
	return(0);
}

// ===========================================================================
// return -1 on error.
int
PayloadReader::calcOffset(int iterType,
			uint8_t head, uint8_t tail,
			uint8_t iterThere, uint8_t iterPoss,
			size_t length, int index,
			fru_errno_t *err)
{
	*err = FRU_SUCCESS;
	switch (iterType) {
		case FRU_FIFO:
		case FRU_Linear:
		{
			if (index == PathDef::lastIteration)
				return (length * tail);
			return (length * index);
		break;
		}
		case FRU_Circular:
		case FRU_LIFO:
		{
			if (index == PathDef::lastIteration) {
				if (iterType == FRU_LIFO)
					return (length * head);
				return (length * tail);
			}

			// For reading they are oposite.
			if (iterType == FRU_Circular) {
				return (length * ((head + index) % iterPoss));
			} else {
				int abs = tail - index;
				if (abs < 0)
					// abs is negative here
					abs = iterPoss + abs;
				return (length * abs);
			}
		break;
		}
	}
	*err = FRU_FAILURE;
	return (-1);
}

// ===========================================================================
// return -1 on error.
int
PayloadReader::getIterationOffset(uint8_t *iter, int iterLen,
				PathDef *path, int *rcIterThere,
				fru_errno_t *err,
				int onlyFindingIterThereFlag)
{
	int rc = 0;

	// read the iteration control bytes first because we may ONLY need
	// them.
	uint8_t head = iter[0];
	uint8_t tail = iter[1];
	uint8_t iterThere = iter[2];
	uint8_t iterPoss = iter[3];

	// the '+' symbol on anything is an error here
	if (path->iterIndex == PathDef::addIteration) {
		*err = FRU_INVALPATH;
		return (-1);
	}

	// check assumptions for next calls.
	if (iterPoss != path->def->iterationCount) {
		*err = FRU_DATACORRUPT;
		return (-1);
	}

	if (onlyFindingIterThereFlag == ITER_THERE_ONLY) {
		if (rcIterThere != NULL) {
			*rcIterThere = iterThere;
		}
		*err = FRU_SUCCESS;
		return (ITER_CONT_BYTE_LEN);
	}

	if ((path->iterIndex != PathDef::addIteration) &&
		(path->iterIndex != PathDef::lastIteration) &&
		(path->iterIndex >= iterThere)) {
		*err = FRU_DATANOTFOUND;
		return (-1);
	}

	// don't forget to skip the iteration control bytes!!!
	int length = ((path->def->payloadLen - ITER_CONT_BYTE_LEN)
			/path->def->iterationCount);

	rc = calcOffset(path->def->iterationType,
			head, tail, iterThere, iterPoss,
			length, path->iterIndex, err);
	if (rc == -1) {
		// error set by calcOffset
		return (-1);
	}

	*err = FRU_SUCCESS;
	return (ITER_CONT_BYTE_LEN + rc);
}

// ===========================================================================
// Iff onlyFindingIterThereFlag is set data is ignored and dataLen will be set
// to the number of iterations which are actually in the seeprom.
fru_errno_t
PayloadReader::readRecurse(PathDef *path,
			uint8_t *cur, size_t curLen,
			void **data, size_t *dataLen,
			int onlyFindingIterThereFlag)
{
	fru_errno_t rc = FRU_SUCCESS;
	size_t calc_data_len = 0;

	if (path->next == NULL) {

		// alway go ahead and do the iterated thing.  If we are not a
		// field then the onlyFindingIterThereFlag should be set.
		// Check this afterward.
		int offset = 0;
		int iterThere = 0;
		// zzz altering the length things again...
		if (IS_ITERATED(path)) {
			// we are iterated.
			calc_data_len = (path->def->payloadLen
						-ITER_CONT_BYTE_LEN)/
					path->def->iterationCount;
// zzz still have to figure out the bit offsets for bit iterations...
			offset = getIterationOffset(cur, curLen, path,
				&iterThere, &rc,
				onlyFindingIterThereFlag);
			if (offset == -1)
				return (rc);

			// done
			if (onlyFindingIterThereFlag) {
				*dataLen = iterThere;
				return (FRU_SUCCESS);
			}
		} else {
			// done but this thing was not an iteration!!!
			if (onlyFindingIterThereFlag) {
				return (FRU_INVALPATH);
			}

			calc_data_len = path->def->payloadLen;
			offset = 0;
		}
		// end zzz

		// now make sure we have a field.
		if (path->def->dataType == FDTYPE_Record) {
			return (FRU_NOTFIELD);
		}

		// allocate and copy.
		if (path->def->dataType == FDTYPE_Binary) {
			uint64_t *eData = (uint64_t *)malloc(sizeof (*eData));
			if (eData == NULL) {
				return (FRU_FAILURE);
			}

			int bitLength = path->def->dataLength;
			// iterated bit field adjust acordingly.
			if (IS_ITERATED(path)) {
				bitLength = (bitLength-(ITER_CONT_BYTE_LEN*8))/
					path->def->iterationCount;
			}

			rc = readBits(bitLength, &(cur[offset]),
					calc_data_len, 0, eData);
			if (rc != FRU_SUCCESS) {
				free(eData);
				return (rc);
			}
			*data = (void *)eData;
			*dataLen = sizeof (*eData);
		} else if (path->def->dataType == FDTYPE_Enumeration) {
			unsigned char *eData
				= (unsigned char *)malloc(sizeof (uint64_t));
			if (eData == NULL) {
				return (FRU_FAILURE);
			}
			/* copy the correct number of bytes to eData */
			memset(eData, 0x00, sizeof (uint64_t));
			memcpy(&(eData[(sizeof (uint64_t) - (calc_data_len))]),
				&(cur[offset]),
				(calc_data_len));
			*data = (void*)eData;
			*dataLen = sizeof (uint64_t);
		} else {
			void *rc_data = malloc(calc_data_len);
			if (rc_data == NULL) {
				return (FRU_FAILURE);
			}
			memcpy(rc_data, &(cur[offset]), calc_data_len);
			*data = rc_data;
			*dataLen = calc_data_len;
		}

		return (FRU_SUCCESS);
	}

	// At this point we know the entry is some sort of record.

	int newOffset = 0, newLength = 0;
	if (IS_ITERATED(path)) {

// zzz still have to figure out the bit offsets for bit iterations...
		newOffset = getIterationOffset(cur, curLen,
				path, NULL, &rc, NORMAL_READ);
		if (newOffset == -1)
			return (rc);
	}

	newOffset += getOffsetIntoRecord(path->def, path->next->def);
	newLength = path->next->def->payloadLen;

	return (readRecurse(path->next, &(cur[newOffset]), newLength,
		data, dataLen, onlyFindingIterThereFlag));
}

// ===========================================================================
// will send the data back in (data,dataLen)
fru_errno_t
PayloadReader::readData(PathDef *path, Ancestor *curDef,
			int instWICur,
			uint8_t *payload, size_t payloadLen,
			void **data, size_t *dataLen)
{
	int offset = curDef->getInstOffset(instWICur);
	return (readRecurse(path, &(payload[offset]), payloadLen-offset,
		data, dataLen, NORMAL_READ));
}

// ===========================================================================
fru_errno_t
PayloadReader::findIterThere(PathDef *path, Ancestor *curDef,
				int instWICur,
				uint8_t *payload, size_t payloadLen,
				int *numThere)
{
	int offset = curDef->getInstOffset(instWICur);
	size_t tmp_num = 0;
	fru_errno_t err = readRecurse(path, &(payload[offset]),
		payloadLen-offset, NULL, &tmp_num, ITER_THERE_ONLY);

	if (err == FRU_SUCCESS) {
		int tmp_num_there = (int)tmp_num;
		if (tmp_num_there != tmp_num) {
			return (FRU_FAILURE);
		}
		*numThere = tmp_num_there;
	}
	return (err);
}

static fru_errno_t
update_iter_cont_bytes(PathDef *path, uint8_t *cur, size_t curLen)
{
	// update the iteration control information
	uint8_t *head = &(cur[0]);
	uint8_t *tail = &(cur[1]);
	uint8_t *numThere = &(cur[2]);
	// This never changes.
	uint8_t numPoss = cur[3];

	if (numPoss != path->def->iterationCount) {
		return (FRU_DATACORRUPT);
	}

	// Remember that when the iteration is added the head and the tail both
	// equal 0 (ie point to 0).  So if we are empty when we are updating
	// then we don't have to alter the head or tail values.  We simply add
	// one to the numThere.
	if (*numThere != 0) {
		switch (path->def->iterationType) {
			case FRU_Linear:
				// this will flag an error when Linear can't
				// hold anymore.
				if ((*tail + 1) == numPoss)
					return (FRU_ITERFULL);
			/* Fall through */
			case FRU_FIFO:
				// if we are not at the end move the tail.
				if (*tail != (numPoss-1))
					*tail = *tail+1;
			break;

			case FRU_Circular:
			case FRU_LIFO:
				// this is the same except LIFO is read
				// BACKWARDS

				// move the tail.
				*tail = *tail + 1;
				// if the tail hits the end wrap around.
				if (*tail == numPoss)
					*tail = 0;
				// if tail catches head move the head.
				if (*tail == *head) {
					// if head hits the end wrap around.
					if (++(*head) == numPoss)
						*head = 0;
				}
			break;
		}
	}
	if ((*numThere) < numPoss) {
		// add one IFF we are not full
		*numThere = *numThere + 1;
	}

	return (FRU_SUCCESS);
}

// ===========================================================================
fru_errno_t
PayloadReader::updateRecurse(PathDef *path,
				uint8_t *cur, size_t curLen,
				void *data, size_t dataLen)
{
	fru_errno_t rc = FRU_SUCCESS;

	if (path->next == NULL) {

		// Delay checking for Records until after this which will
		// allow for [+] notation for Iterated Records.
		// if this is updating an iteration AND we are adding one...
		if (IS_ITERATED(path) &&
			(path->iterIndex == PathDef::addIteration)) {
			return (update_iter_cont_bytes(path, cur, curLen));
		}

		if (path->def->dataType == FDTYPE_Record) {
			return (FRU_NOTFIELD);
		}

		int offset = 0;
		int calcLen = 0;
		int dummy = 0;
		// zzz altering the length things again...
		if (IS_ITERATED(path)) {
			// we are iterated.
			calcLen = (path->def->payloadLen-ITER_CONT_BYTE_LEN)/
				path->def->iterationCount;
// zzz still have to figure out the bit offsets
			offset = getIterationOffset(cur, curLen,
				path, &dummy, &rc, NORMAL_READ);
			if (offset == -1)
				return (rc);
		} else {
			calcLen = path->def->payloadLen;
			offset = 0;
		}
		// end zzz

		// once again convert enums for the user again.
		if (path->def->dataType == FDTYPE_Binary) {
			int bitLength = path->def->dataLength;
			// iterated bit field adjust acordingly.
			if (path->def->iterationType != FRU_NOT_ITERATED) {
				bitLength = (bitLength - 32)/
					path->def->iterationCount;
			}

			rc = writeBits (*(uint64_t *)data, bitLength,
					&(cur[offset]), calcLen, 0);
			if (rc != FRU_SUCCESS)
				return (rc);
		} else if (path->def->dataType == FDTYPE_Enumeration) {
			unsigned char *tmp = (unsigned char *)data;
			memcpy(&(cur[offset]),
				&(tmp[(sizeof (uint64_t) - (calcLen))]),
				calcLen);
		} else {
			// copy into and return.
			memcpy(&(cur[offset]), data, dataLen);
		}

		return (FRU_SUCCESS);
	}

	int newOffset = 0, newLength = 0;
	int dummy = 0;
	if (path->def->iterationType != FRU_NOT_ITERATED) {

// zzz still have to figure out the bit offsets
		newOffset = getIterationOffset(cur, curLen, path,
					&dummy, &rc, NORMAL_READ);
		if (newOffset == -1)
			return (rc);
	}
	newOffset += getOffsetIntoRecord(path->def, path->next->def);
	newLength = path->next->def->payloadLen;

	return (updateRecurse(path->next, &(cur[newOffset]), newLength,
		data, dataLen));
}

// ===========================================================================
// will update the data in payload which can then be written back.
fru_errno_t
PayloadReader::updateData(PathDef *path, Ancestor *ancestorDef,
			int instWICur,
			uint8_t *payload, size_t payloadLen,
			void *data, size_t dataLen)
{
	// verify the user data first before doing any major work.
	int calcLen = 0;
	PathDef *prev = path;
	PathDef *cur = path;
	while (cur != NULL) {
		prev = cur;
		cur = cur->next;
	}

	// unless we are updateing with [+] symbol
	// (which means we don't have any data length at all.)
	if (prev->iterIndex != PathDef::addIteration) {
		if (IS_ITERATED(prev)) {
			calcLen = (prev->def->payloadLen-ITER_CONT_BYTE_LEN)/
				prev->def->iterationCount;
		} else {
				calcLen = prev->def->payloadLen;
		}
		// the sizeof the data for Binary or Enumeration MUST
		// be uint64_t
		if ((prev->def->dataType == FDTYPE_Enumeration) ||
			(prev->def->dataType == FDTYPE_Binary)) {
			if (dataLen != sizeof (uint64_t))
				return (FRU_INVALDATASIZE);
		// all others must be shorter than the space available.
		} else {
			if (dataLen > calcLen)
				return (FRU_INVALDATASIZE);
		}
	}

	int offset = ancestorDef->getInstOffset(instWICur);
	return (updateRecurse(path, &(payload[offset]), payloadLen-offset,
		data, dataLen));
}