218 lines
5.3 KiB
C
218 lines
5.3 KiB
C
/* Copyright 2013-2014 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "hdif.h"
|
|
#include <stack.h>
|
|
|
|
const void *HDIF_get_idata(const struct HDIF_common_hdr *hdif, unsigned int di,
|
|
unsigned int *size)
|
|
{
|
|
const struct HDIF_common_hdr *hdr = hdif;
|
|
const struct HDIF_idata_ptr *iptr;
|
|
|
|
if (be16_to_cpu(hdr->d1f0) != 0xd1f0) {
|
|
prerror("HDIF: Bad header format !\n");
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
if (di >= be16_to_cpu(hdr->idptr_count)) {
|
|
prlog(PR_DEBUG, "HDIF: idata %d out of range for %.6s!\n",
|
|
di, hdr->id);
|
|
return NULL;
|
|
}
|
|
|
|
iptr = (void *)hdif + be32_to_cpu(hdr->idptr_off)
|
|
+ di * sizeof(struct HDIF_idata_ptr);
|
|
|
|
if (size)
|
|
*size = be32_to_cpu(iptr->size);
|
|
|
|
return (void *)hdif + be32_to_cpu(iptr->offset);
|
|
}
|
|
|
|
const void *HDIF_get_iarray_item(const struct HDIF_common_hdr *hdif,
|
|
unsigned int di, unsigned int ai,
|
|
unsigned int *size)
|
|
{
|
|
const struct HDIF_array_hdr *ahdr;
|
|
unsigned int asize;
|
|
const void *arr;
|
|
|
|
arr = HDIF_get_idata(hdif, di, &asize);
|
|
if (!arr)
|
|
return NULL;
|
|
|
|
if (asize < sizeof(struct HDIF_array_hdr)) {
|
|
prerror("HDIF: idata block too small for array !\n");
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
ahdr = arr;
|
|
|
|
if (ai >= be32_to_cpu(ahdr->ecnt)) {
|
|
prerror("HDIF: idata array index out of range !\n");
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
if (size)
|
|
*size = be32_to_cpu(ahdr->eactsz);
|
|
|
|
return arr + be32_to_cpu(ahdr->offset) + ai * be32_to_cpu(ahdr->esize);
|
|
}
|
|
|
|
int HDIF_get_iarray_size(const struct HDIF_common_hdr *hdif, unsigned int di)
|
|
{
|
|
const struct HDIF_array_hdr *ahdr;
|
|
unsigned int asize;
|
|
const void *arr;
|
|
|
|
arr = HDIF_get_idata(hdif, di, &asize);
|
|
if (!arr)
|
|
return -1;
|
|
|
|
if (asize < sizeof(struct HDIF_array_hdr)) {
|
|
prerror("HDIF: idata block too small for array !\n");
|
|
backtrace();
|
|
return -1;
|
|
}
|
|
|
|
ahdr = arr;
|
|
return be32_to_cpu(ahdr->ecnt);
|
|
}
|
|
|
|
/*
|
|
* Returns NULL and sets *items to zero when:
|
|
*
|
|
* a) Array extends beyond bounds (hard error)
|
|
* b) The array is empty (soft error)
|
|
* c) The item size is zero (soft error)
|
|
* d) The array is missing (soft error)
|
|
*
|
|
* b, c) are bugs in the input data so they generate backtraces.
|
|
*
|
|
* If you care about the soft error cases, retrive the array header manually
|
|
* with HDIF_get_idata().
|
|
*/
|
|
const struct HDIF_array_hdr *HDIF_get_iarray(const struct HDIF_common_hdr *hdif,
|
|
unsigned int di, unsigned int *items)
|
|
{
|
|
const struct HDIF_array_hdr *arr;
|
|
unsigned int req_size, size, elements;
|
|
unsigned int actual_sz, alloc_sz, offset;
|
|
|
|
arr = HDIF_get_idata(hdif, di, &size);
|
|
|
|
if(items)
|
|
*items = 0;
|
|
|
|
if (!arr || !size)
|
|
return NULL;
|
|
|
|
/* base size of an Idata array header */
|
|
offset = be32_to_cpu(arr->offset);
|
|
actual_sz = be32_to_cpu(arr->eactsz);
|
|
alloc_sz = be32_to_cpu(arr->esize);
|
|
elements = be32_to_cpu(arr->ecnt);
|
|
|
|
/* actual size should always be smaller than allocated */
|
|
if (alloc_sz < actual_sz) {
|
|
prerror("HDIF %.6s iarray %u has actsz (%u) < alloc_sz (%u)\n)",
|
|
hdif->id, di, actual_sz, alloc_sz);
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
req_size = elements * alloc_sz + offset;
|
|
if (req_size > size) {
|
|
prerror("HDIF: %.6s iarray %u requires %#x bytes, but only %#x are allocated!\n",
|
|
hdif->id, di, req_size, size);
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
if (!elements || !actual_sz)
|
|
return NULL;
|
|
|
|
if (items)
|
|
*items = elements;
|
|
|
|
return arr;
|
|
}
|
|
|
|
const void *HDIF_iarray_item(const struct HDIF_array_hdr *ahdr,
|
|
unsigned int index)
|
|
{
|
|
if (!ahdr || index >= be32_to_cpu(ahdr->ecnt))
|
|
return NULL;
|
|
|
|
return (const void * )ahdr + be32_to_cpu(ahdr->offset) +
|
|
index * be32_to_cpu(ahdr->esize);
|
|
}
|
|
|
|
struct HDIF_child_ptr *
|
|
HDIF_child_arr(const struct HDIF_common_hdr *hdif, unsigned int idx)
|
|
{
|
|
struct HDIF_child_ptr *children;
|
|
|
|
children = (void *)hdif + be32_to_cpu(hdif->child_off);
|
|
|
|
if (idx >= be16_to_cpu(hdif->child_count)) {
|
|
prerror("HDIF: child array idx out of range!\n");
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
return &children[idx];
|
|
}
|
|
|
|
struct HDIF_common_hdr *HDIF_child(const struct HDIF_common_hdr *hdif,
|
|
const struct HDIF_child_ptr *child,
|
|
unsigned int idx,
|
|
const char *eyecatcher)
|
|
{
|
|
void *base = (void *)hdif;
|
|
struct HDIF_common_hdr *ret;
|
|
long child_off;
|
|
|
|
/* child must be in hdif's child array */
|
|
child_off = (void *)child - (base + be32_to_cpu(hdif->child_off));
|
|
assert(child_off % sizeof(struct HDIF_child_ptr) == 0);
|
|
assert(child_off / sizeof(struct HDIF_child_ptr)
|
|
< be16_to_cpu(hdif->child_count));
|
|
|
|
assert(idx < be32_to_cpu(child->count));
|
|
|
|
if (be32_to_cpu(child->size) < sizeof(struct HDIF_common_hdr)) {
|
|
prerror("HDIF: %s child #%i too small: %u\n",
|
|
eyecatcher, idx, be32_to_cpu(child->size));
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
ret = base + be32_to_cpu(child->offset)
|
|
+ be32_to_cpu(child->size) * idx;
|
|
if (!HDIF_check(ret, eyecatcher)) {
|
|
prerror("HDIF: #%i bad type (wanted %6s, got %6s)\n",
|
|
idx, eyecatcher, ret->id);
|
|
backtrace();
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|