/* 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 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; }