#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)
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);
bitData = (bitData << (64-bitLength));
bitData = (bitData >> bitOffset);
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);
uint64_t rd = 0;
memcpy((void *)&rd, (void *)data, dataLength);
rd = (rd & mask);
rd = (rd | bitData);
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);
uint64_t rc = 0;
memcpy((void *)&rc, (void *)data, dataLength);
rc = (rc << bitOffset);
rc = (rc >> (64 - bitLength));
*ret = rc;
return (FRU_SUCCESS);
}
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);
}
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);
}
if (iterType == FRU_Circular) {
return (length * ((head + index) % iterPoss));
} else {
int abs = tail - index;
if (abs < 0)
abs = iterPoss + abs;
return (length * abs);
}
break;
}
}
*err = FRU_FAILURE;
return (-1);
}
int
PayloadReader::getIterationOffset(uint8_t *iter, int iterLen,
PathDef *path, int *rcIterThere,
fru_errno_t *err,
int onlyFindingIterThereFlag)
{
int rc = 0;
uint8_t head = iter[0];
uint8_t tail = iter[1];
uint8_t iterThere = iter[2];
uint8_t iterPoss = iter[3];
if (path->iterIndex == PathDef::addIteration) {
*err = FRU_INVALPATH;
return (-1);
}
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);
}
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) {
return (-1);
}
*err = FRU_SUCCESS;
return (ITER_CONT_BYTE_LEN + rc);
}
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) {
int offset = 0;
int iterThere = 0;
if (IS_ITERATED(path)) {
calc_data_len = (path->def->payloadLen
-ITER_CONT_BYTE_LEN)/
path->def->iterationCount;
offset = getIterationOffset(cur, curLen, path,
&iterThere, &rc,
onlyFindingIterThereFlag);
if (offset == -1)
return (rc);
if (onlyFindingIterThereFlag) {
*dataLen = iterThere;
return (FRU_SUCCESS);
}
} else {
if (onlyFindingIterThereFlag) {
return (FRU_INVALPATH);
}
calc_data_len = path->def->payloadLen;
offset = 0;
}
if (path->def->dataType == FDTYPE_Record) {
return (FRU_NOTFIELD);
}
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;
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);
}
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);
}
int newOffset = 0, newLength = 0;
if (IS_ITERATED(path)) {
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));
}
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)
{
uint8_t *head = &(cur[0]);
uint8_t *tail = &(cur[1]);
uint8_t *numThere = &(cur[2]);
uint8_t numPoss = cur[3];
if (numPoss != path->def->iterationCount) {
return (FRU_DATACORRUPT);
}
if (*numThere != 0) {
switch (path->def->iterationType) {
case FRU_Linear:
if ((*tail + 1) == numPoss)
return (FRU_ITERFULL);
case FRU_FIFO:
if (*tail != (numPoss-1))
*tail = *tail+1;
break;
case FRU_Circular:
case FRU_LIFO:
*tail = *tail + 1;
if (*tail == numPoss)
*tail = 0;
if (*tail == *head) {
if (++(*head) == numPoss)
*head = 0;
}
break;
}
}
if ((*numThere) < numPoss) {
*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) {
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;
if (IS_ITERATED(path)) {
calcLen = (path->def->payloadLen-ITER_CONT_BYTE_LEN)/
path->def->iterationCount;
offset = getIterationOffset(cur, curLen,
path, &dummy, &rc, NORMAL_READ);
if (offset == -1)
return (rc);
} else {
calcLen = path->def->payloadLen;
offset = 0;
}
if (path->def->dataType == FDTYPE_Binary) {
int bitLength = path->def->dataLength;
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 {
memcpy(&(cur[offset]), data, dataLen);
}
return (FRU_SUCCESS);
}
int newOffset = 0, newLength = 0;
int dummy = 0;
if (path->def->iterationType != FRU_NOT_ITERATED) {
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));
}
fru_errno_t
PayloadReader::updateData(PathDef *path, Ancestor *ancestorDef,
int instWICur,
uint8_t *payload, size_t payloadLen,
void *data, size_t dataLen)
{
int calcLen = 0;
PathDef *prev = path;
PathDef *cur = path;
while (cur != NULL) {
prev = cur;
cur = cur->next;
}
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;
}
if ((prev->def->dataType == FDTYPE_Enumeration) ||
(prev->def->dataType == FDTYPE_Binary)) {
if (dataLen != sizeof (uint64_t))
return (FRU_INVALDATASIZE);
} else {
if (dataLen > calcLen)
return (FRU_INVALDATASIZE);
}
}
int offset = ancestorDef->getInstOffset(instWICur);
return (updateRecurse(path, &(payload[offset]), payloadLen-offset,
data, dataLen));
}