blob: 3f32d653aed2e01cc9e48d0c57d33d2f9ef96c43 [file] [log] [blame]
/* IBM_PROLOG_BEGIN_TAG */
/* This is an automatically generated prolog. */
/* */
/* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/sbe_xip_image.c $ */
/* */
/* OpenPOWER HostBoot Project */
/* */
/* COPYRIGHT International Business Machines Corp. 2012,2014 */
/* */
/* 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. */
/* */
/* IBM_PROLOG_END_TAG */
// $Id: sbe_xip_image.c,v 1.28 2013/12/11 00:12:41 bcbrock Exp $
// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ipl/sbe/sbe_xip_image.c,v $
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2011
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------
// *! OWNER NAME: Bishop Brock Email: bcbrock@us.ibm.com
//------------------------------------------------------------------------------
/// \file sbe_xip_image.c
/// \brief APIs for validating, normalizing, searching and manipulating
/// SBE-XIP images.
///
/// The background, APIs and implementation details are documented in the
/// document "SBE-XIP Binary format" currently available at this link:
///
/// - https://mcdoc.boeblingen.de.ibm.com/out/out.ViewDocument.php?documentid=2678
///
/// \bug The sbe_xip_validate() API should be carefully reviewed to ensure
/// that validating even a corrupt image can not lead to a segfault, i.e., to
/// ensure that no memory outside of the putative bounds of the image is ever
/// referenced during validation.
#ifndef PLIC_MODULE
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#endif // PLIC_MODULE
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sbe_xip_image.h"
////////////////////////////////////////////////////////////////////////////
// Local Functions
////////////////////////////////////////////////////////////////////////////
// PHYP has their own way of implementing the <string.h> functions. PHYP also
// does not allow static functions or data, so all of the XIP_STATIC functions
// defined here are global to PHYP.
#ifdef PPC_HYP
#ifdef PLIC_MODULE
#define strcpy(dest, src) hvstrcpy(dest, src)
#define strlen(s) hvstrlen(s)
#define strcmp(s1, s2) hvstrcmp(s1, s2)
#endif //PLIC_MODULE
#define XIP_STATIC
#else // PPC_HYP
#define XIP_STATIC static
#endif // PPC_HYP
#ifdef DEBUG_SBE_XIP_IMAGE
// Debugging support, normally disabled. All of the formatted I/O you see in
// the code is effectively under this switch.
#ifdef __FAPI
#include "fapi.H"
#define fprintf(stream, ...) FAPI_ERR(__VA_ARGS__)
#define printf(...) FAPI_INF(__VA_ARGS__)
#define TRACE_NEWLINE ""
#else // __FAPI
#include <stdio.h>
#define TRACE_NEWLINE "\n"
#endif // __FAPI
// Portable formatting of uint64_t. The ISO C99 standard requires
// __STDC_FORMAT_MACROS to be defined in order for PRIx64 etc. to be defined.
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#define F0x016llx "0x%016" PRIx64
#define F0x012llx "0x%012" PRIx64
XIP_STATIC SBE_XIP_ERROR_STRINGS(sbe_xip_error_strings);
#define TRACE_ERROR(x) \
({ \
fprintf(stderr, "%s:%d : Returning error code %d : %s" TRACE_NEWLINE, \
__FILE__, __LINE__, (x), \
SBE_XIP_ERROR_STRING(sbe_xip_error_strings, (x))); \
(x); \
})
#define TRACE_ERRORX(x, ...) \
({ \
TRACE_ERROR(x); \
fprintf(stderr, ##__VA_ARGS__); \
(x); \
})
// Uncomment these if required for debugging, otherwise we get warnings from
// GCC as they are not otherwise used.
#if 0
XIP_STATIC uint32_t xipRevLe32(const uint32_t i_x);
XIP_STATIC SBE_XIP_TYPE_STRINGS(type_strings);
XIP_STATIC void
dumpToc(int index, SbeXipToc* toc)
{
printf("TOC entry %d @ %p\n"
" iv_id = 0x%08x\n"
" iv_data = 0x%08x\n"
" iv_type = %s\n"
" iv_section = 0x%02x\n"
" iv_elements = %d\n",
index, toc,
xipRevLe32(toc->iv_id),
xipRevLe32(toc->iv_data),
SBE_XIP_TYPE_STRING(type_strings, toc->iv_type),
toc->iv_section,
toc->iv_elements);
}
#endif
#if 0
XIP_STATIC void
dumpItem(SbeXipItem* item)
{
printf("SbeXipItem @ %p\n"
" iv_toc = %p\n"
" iv_address = " F0x016llx "\n"
" iv_imageData = %p\n"
" iv_id = %s\n"
" iv_type = %s\n"
" iv_elements = %d\n",
item,
item->iv_toc,
item->iv_address,
item->iv_imageData,
item->iv_id,
SBE_XIP_TYPE_STRING(type_strings, item->iv_type),
item->iv_elements);
dumpToc(-1, item->iv_toc);
}
#endif /* 0 */
XIP_STATIC void
dumpSectionTable(const void* i_image)
{
int i, rc;
SbeXipSection section;
printf("Section table dump of image @ %p\n"
" Entry Offset Size\n"
"-------------------------------\n",
i_image);
for (i = 0; i < SBE_XIP_SECTIONS; i++) {
rc = sbe_xip_get_section(i_image, i, &section);
if (rc) {
printf(">>> dumpSectionTable got error at entry %d : %s\n",
i, SBE_XIP_ERROR_STRING(sbe_xip_error_strings, rc));
break;
}
printf("%7d 0x%08x 0x%08x\n",
i, section.iv_offset, section.iv_size);
}
}
#else
#define TRACE_ERROR(x) (x)
#define TRACE_ERRORX(x, ...) (x)
#define dumpToc(...)
#define dumpItem(...)
#define dumpSectionTable(...)
#endif
// Note: For maximum flexibility we provide private versions of
// endian-conversion routines rather than counting on a system-specific header
// to provide these.
/// Byte-reverse a 16-bit integer if on a little-endian machine
XIP_STATIC uint16_t
xipRevLe16(const uint16_t i_x)
{
uint16_t rx;
#ifndef _BIG_ENDIAN
uint8_t *pix = (uint8_t*)(&i_x);
uint8_t *prx = (uint8_t*)(&rx);
prx[0] = pix[1];
prx[1] = pix[0];
#else
rx = i_x;
#endif
return rx;
}
/// Byte-reverse a 32-bit integer if on a little-endian machine
XIP_STATIC uint32_t
xipRevLe32(const uint32_t i_x)
{
uint32_t rx;
#ifndef _BIG_ENDIAN
uint8_t *pix = (uint8_t*)(&i_x);
uint8_t *prx = (uint8_t*)(&rx);
prx[0] = pix[3];
prx[1] = pix[2];
prx[2] = pix[1];
prx[3] = pix[0];
#else
rx = i_x;
#endif
return rx;
}
/// Byte-reverse a 64-bit integer if on a little-endian machine
XIP_STATIC uint64_t
xipRevLe64(const uint64_t i_x)
{
uint64_t rx;
#ifndef _BIG_ENDIAN
uint8_t *pix = (uint8_t*)(&i_x);
uint8_t *prx = (uint8_t*)(&rx);
prx[0] = pix[7];
prx[1] = pix[6];
prx[2] = pix[5];
prx[3] = pix[4];
prx[4] = pix[3];
prx[5] = pix[2];
prx[6] = pix[1];
prx[7] = pix[0];
#else
rx = i_x;
#endif
return rx;
}
/// What is the image link address?
XIP_STATIC uint64_t
xipLinkAddress(const void* i_image)
{
return xipRevLe64(((SbeXipHeader*)i_image)->iv_linkAddress);
}
/// What is the image size?
XIP_STATIC uint32_t
xipImageSize(const void* i_image)
{
return xipRevLe32(((SbeXipHeader*)i_image)->iv_imageSize);
}
/// Set the image size
XIP_STATIC void
xipSetImageSize(void* io_image, const size_t i_size)
{
((SbeXipHeader*)io_image)->iv_imageSize = xipRevLe32(i_size);
}
/// Re-establish the required final alignment
XIP_STATIC void
xipFinalAlignment(void* io_image)
{
uint32_t size;
size = xipImageSize(io_image);
if ((size % SBE_XIP_FINAL_ALIGNMENT) != 0) {
xipSetImageSize(io_image,
size + (SBE_XIP_FINAL_ALIGNMENT -
(size % SBE_XIP_FINAL_ALIGNMENT)));
}
}
/// Compute a host address from an image address and offset
XIP_STATIC void*
xipHostAddressFromOffset(const void* i_image, const uint32_t offset)
{
return (void*)((unsigned long)i_image + offset);
}
/// Convert a PORE address to a host address
XIP_STATIC void*
xipPore2Host(const void* i_image, const uint64_t i_poreAddress)
{
return xipHostAddressFromOffset(i_image,
i_poreAddress - xipLinkAddress(i_image));
}
XIP_STATIC int
xipValidatePoreAddress(const void* i_image,
const uint64_t i_poreAddress,
const uint32_t size)
{
int rc;
if ((i_poreAddress < xipLinkAddress(i_image)) ||
(i_poreAddress > (xipLinkAddress(i_image) +
xipImageSize(i_image) -
size))) {
rc = TRACE_ERRORX(SBE_XIP_INVALID_ARGUMENT,
"The PORE address " F0x012llx
" is outside the bounds "
"of the image ("
F0x012llx ":" F0x012llx
") for %u-byte access.\n",
i_poreAddress,
xipLinkAddress(i_image),
xipLinkAddress(i_image) + xipImageSize(i_image) - 1,
size);
} else {
rc = 0;
}
return rc;
}
/// Get the magic number from the image
XIP_STATIC uint64_t
xipMagic(const void* i_image)
{
return xipRevLe64(((SbeXipHeader*)i_image)->iv_magic);
}
/// Get the header version from the image
XIP_STATIC uint8_t
xipHeaderVersion(const void* i_image)
{
return ((SbeXipHeader*)i_image)->iv_headerVersion;
}
/// Has the image been normalized?
XIP_STATIC uint8_t
xipNormalized(const void* i_image)
{
return ((SbeXipHeader*)i_image)->iv_normalized;
}
/// Has the image TOC been sorted?
XIP_STATIC uint8_t
xipSorted(const void* i_image)
{
return ((SbeXipHeader*)i_image)->iv_tocSorted;
}
/// A quick check that the image exists, has the correct magic and header
/// version, and optionally is normalized.
XIP_STATIC int
xipQuickCheck(const void* i_image, const int i_normalizationRequired)
{
int rc;
do {
rc = 0;
if (i_image == 0) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"Image pointer is NULL (0)\n");
break;
}
if ((xipMagic(i_image) >> 32) != SBE_XIP_MAGIC) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"Magic number mismatch; Found "
"" F0x016llx ", expected 0x%08x........\n",
xipMagic(i_image), SBE_XIP_MAGIC);
break;
}
if ((xipHeaderVersion(i_image)) != SBE_XIP_HEADER_VERSION) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"Header version mismatch; Expecting %d, "
"found %d\n",
SBE_XIP_HEADER_VERSION,
xipHeaderVersion(i_image));
break;
}
if (i_normalizationRequired && !xipNormalized(i_image)) {
rc = TRACE_ERRORX(SBE_XIP_NOT_NORMALIZED,
"Image not normalized\n");
break;
}
} while(0);
return rc;
}
/// Convert a 32-bit relocatable offset to a full PORE 48-bit address
XIP_STATIC uint64_t
xipFullAddress(const void* i_image, uint32_t offset)
{
return (xipLinkAddress(i_image) & 0x0000ffff00000000ull) + offset;
}
/// Translate a section table entry
XIP_STATIC void
xipTranslateSection(SbeXipSection* o_dest, const SbeXipSection* i_src)
{
#ifndef _BIG_ENDIAN
#if SBE_XIP_HEADER_VERSION != 8
#error This code assumes the SBE-XIP header version 8 layout
#endif
o_dest->iv_offset = xipRevLe32(i_src->iv_offset);
o_dest->iv_size = xipRevLe32(i_src->iv_size);
o_dest->iv_alignment = i_src->iv_alignment;
o_dest->iv_reserved8[0] = 0;
o_dest->iv_reserved8[1] = 0;
o_dest->iv_reserved8[2] = 0;
#else
if (o_dest != i_src) {
*o_dest = *i_src;
}
#endif /* _BIG_ENDIAN */
}
/// Translate a TOC entry
XIP_STATIC void
xipTranslateToc(SbeXipToc* o_dest, SbeXipToc* i_src)
{
#ifndef _BIG_ENDIAN
#if SBE_XIP_HEADER_VERSION != 8
#error This code assumes the SBE-XIP header version 8 layout
#endif
o_dest->iv_id = xipRevLe32(i_src->iv_id);
o_dest->iv_data = xipRevLe32(i_src->iv_data);
o_dest->iv_type = i_src->iv_type;
o_dest->iv_section = i_src->iv_section;
o_dest->iv_elements = i_src->iv_elements;
o_dest->iv_pad = 0;
#else
if (o_dest != i_src) {
*o_dest = *i_src;
}
#endif /* _BIG_ENDIAN */
}
/// Find the final (highest-address) section of the image
XIP_STATIC int
xipFinalSection(const void* i_image, int* o_sectionId)
{
int i, rc, found;
uint32_t offset;
SbeXipHeader hostHeader;
sbe_xip_translate_header(&hostHeader, (SbeXipHeader*)i_image);
found = 0;
offset = 0;
*o_sectionId = 0; /* Make GCC -O3 happy */
for (i = 0; i < SBE_XIP_SECTIONS; i++) {
if ((hostHeader.iv_section[i].iv_size != 0) &&
(hostHeader.iv_section[i].iv_offset >= offset)) {
*o_sectionId = i;
offset = hostHeader.iv_section[i].iv_offset;
found = 1;
}
}
if (!found) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR, "The image is empty\n");
} else {
rc = 0;
}
return rc;
}
/// Return a pointer to an image-format section table entry
XIP_STATIC int
xipGetSectionPointer(const void* i_image,
const int i_sectionId,
SbeXipSection** o_imageSection)
{
int rc;
if ((i_sectionId < 0) || (i_sectionId >= SBE_XIP_SECTIONS)) {
rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT);
} else {
*o_imageSection =
&(((SbeXipHeader*)i_image)->iv_section[i_sectionId]);
rc = 0;
}
return rc;
}
/// Restore a section table entry from host format to image format.
XIP_STATIC int
xipPutSection(const void* i_image,
const int i_sectionId,
SbeXipSection* i_hostSection)
{
int rc;
SbeXipSection *imageSection;
rc = xipGetSectionPointer(i_image, i_sectionId, &imageSection);
if (!rc) {
xipTranslateSection(imageSection, i_hostSection);
}
return rc;
}
/// Set the offset of a section
XIP_STATIC int
xipSetSectionOffset(void* io_image, const int i_section,
const uint32_t i_offset)
{
SbeXipSection* section;
int rc;
rc = xipGetSectionPointer(io_image, i_section, &section);
if (!rc) {
section->iv_offset = xipRevLe32(i_offset);
}
return rc;
}
/// Set the size of a section
XIP_STATIC int
xipSetSectionSize(void* io_image, const int i_section, const uint32_t i_size)
{
SbeXipSection* section;
int rc;
rc = xipGetSectionPointer(io_image, i_section, &section);
if (!rc) {
section->iv_size = xipRevLe32(i_size);
}
return rc;
}
/// Translate a PORE address in the image to a section and offset
// We first check to be sure that the PORE address is contained in the image,
// using the full 48-bit form. Then we scan the section table to see which
// section contains the address - if none then the image is corrupted. We can
// (must) use the 32-bit offset form of the address here.
XIP_STATIC int
xipPore2Section(const void* i_image,
const uint64_t i_poreAddress,
int* o_section,
uint32_t* o_offset)
{
int rc, sectionId;
SbeXipSection section;
uint32_t addressOffset;
do {
rc = 0;
if ((i_poreAddress < xipLinkAddress(i_image)) ||
(i_poreAddress >
(xipLinkAddress(i_image) + xipImageSize(i_image)))) {
rc = TRACE_ERRORX(SBE_XIP_INVALID_ARGUMENT,
"pore2section: The i_poreAddress argument "
"(" F0x016llx ")\nis outside the bounds of the "
"image (" F0x016llx ":" F0x016llx ")\n",
i_poreAddress,
xipLinkAddress(i_image),
xipLinkAddress(i_image) + xipImageSize(i_image));
break;
}
addressOffset = (i_poreAddress - xipLinkAddress(i_image)) & 0xffffffff;
for (sectionId = 0; sectionId < SBE_XIP_SECTIONS; sectionId++) {
rc = sbe_xip_get_section(i_image, sectionId, &section);
if (rc) {
rc = TRACE_ERROR(SBE_XIP_BUG); /* Can't happen */
break;
}
if ((section.iv_size != 0) &&
(addressOffset >= section.iv_offset) &&
(addressOffset < (section.iv_offset + section.iv_size))) {
break;
}
}
if (rc) break;
if (sectionId == SBE_XIP_SECTIONS) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"Error processing PORE address " F0x016llx ". "
"The address is not mapped in any section.\n"
"A section table dump appears below\n",
i_poreAddress);
dumpSectionTable(i_image);
break;
}
*o_section = sectionId;
*o_offset = addressOffset - section.iv_offset;
} while(0);
return rc;
}
/// Get the information required to search the TOC.
///
/// All return values are optional.
XIP_STATIC int
xipGetToc(void* i_image,
SbeXipToc** o_toc,
size_t* o_entries,
int* o_sorted,
char** o_strings)
{
int rc;
SbeXipSection tocSection, stringsSection;
do {
rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_TOC, &tocSection);
if (rc) break;
rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_STRINGS,
&stringsSection);
if (rc) break;
if (o_toc) {
*o_toc = (SbeXipToc*)((uint8_t*)i_image + tocSection.iv_offset);
}
if (o_entries) {
*o_entries = tocSection.iv_size / sizeof(SbeXipToc);
}
if (o_sorted) {
*o_sorted = xipSorted(i_image);
}
if (o_strings) {
*o_strings = (char*)i_image + stringsSection.iv_offset;
}
} while (0);
return rc;
}
/// Compare two normalized TOC entries for sorting.
XIP_STATIC int
xipCompareToc(const SbeXipToc* i_a, const SbeXipToc* i_b,
const char* i_strings)
{
return strcmp(i_strings + xipRevLe32(i_a->iv_id),
i_strings + xipRevLe32(i_b->iv_id));
}
/// Iterative quicksort of the TOC
// Note: The stack requirement is limited to 256 bytes + minor local storage.
XIP_STATIC void
xipQuickSort(SbeXipToc* io_toc, int i_left, int i_right,
const char* i_strings)
{
int i, j, left, right, sp;
SbeXipToc pivot, temp;
uint32_t stack[64];
sp = 0;
stack[sp++] = i_left;
stack[sp++] = i_right;
while (sp) {
right = stack[--sp];
left = stack[--sp];
i = left;
j = right;
pivot = io_toc[(i + j) / 2];
while (i <= j) {
while (xipCompareToc(&(io_toc[i]), &pivot, i_strings) < 0) {
i++;
}
while (xipCompareToc(&(io_toc[j]), &pivot, i_strings) > 0) {
j--;
}
if (i <= j) {
temp = io_toc[i];
io_toc[i] = io_toc[j];
io_toc[j] = temp;
i++;
j--;
}
}
if (left < j) {
stack[sp++] = left;
stack[sp++] = j;
}
if (i < right) {
stack[sp++] = i;
stack[sp++] = right;
}
}
}
/// TOC linear search
XIP_STATIC int
xipLinearSearch(void* i_image, const char* i_id, SbeXipToc** o_entry)
{
int rc;
SbeXipToc *imageToc, hostToc;
size_t entries;
char* strings;
*o_entry = 0;
rc = xipGetToc(i_image, &imageToc, &entries, 0, &strings);
if (!rc) {
for (; entries; entries--, imageToc++) {
xipTranslateToc(&hostToc, imageToc);
if (strcmp(i_id, strings + hostToc.iv_id) == 0) {
break;
}
}
if (entries) {
*o_entry = imageToc;
rc = 0;
} else {
*o_entry = 0;
rc = TRACE_ERROR(SBE_XIP_ITEM_NOT_FOUND);
}
}
return rc;
}
/// A classic binary search of a (presumed) sorted array
XIP_STATIC int
xipBinarySearch(void* i_image, const char* i_id, SbeXipToc** o_entry)
{
int rc;
SbeXipToc *imageToc;
size_t entries;
char* strings;
int sorted, left, right, next, sort;
do {
*o_entry = 0;
rc = xipGetToc(i_image, &imageToc, &entries, &sorted, &strings);
if (rc) break;
if (!sorted) {
rc = TRACE_ERROR(SBE_XIP_BUG);
break;
}
left = 0;
right = entries - 1;
while (left <= right) {
next = (left + right) / 2;
sort = strcmp(i_id, strings + xipRevLe32(imageToc[next].iv_id));
if (sort == 0) {
*o_entry = &(imageToc[next]);
break;
} else if (sort < 0) {
right = next - 1;
} else {
left = next + 1;
}
}
if (*o_entry == 0) {
rc = TRACE_ERROR(SBE_XIP_ITEM_NOT_FOUND);
break;
}
} while (0);
return rc;
}
/// Validate a TOC entry as a mapping function
///
/// The TOC is validated by searching for the entry, which will uncover
/// duplicate entries or problems with sorting/searching.
XIP_STATIC int
xipValidateTocEntry(void* io_image, const SbeXipItem* i_item, void* io_arg)
{
int rc;
SbeXipItem found;
(void)io_arg;
do {
rc = sbe_xip_find(io_image, i_item->iv_id, &found);
if (rc) {
rc = TRACE_ERRORX(rc, "TOC entry for %s not found\n",
i_item->iv_id);
} else if (found.iv_toc != i_item->iv_toc) {
rc = TRACE_ERRORX(SBE_XIP_TOC_ERROR,
"Duplicate TOC entry for '%s'\n", i_item->iv_id);
}
break;
} while (0);
return rc;
}
// This is the FNV-1a hash, used for hashing symbol names in the .fixed
// section into 32-bit hashes for the mini-TOC.
// According to the authors:
// "FNV hash algorithms and source code have been released into the public
// domain. The authors of the FNV algorithmm look deliberate steps to disclose
// the algorhtm (sic) in a public forum soon after it was invented. More than
// a year passed after this public disclosure and the authors deliberately took
// no steps to patent the FNV algorithm. Therefore it is safe to say that the
// FNV authors have no patent claims on the FNV algorithm as published."
#define FNV_OFFSET_BASIS 2166136261u
#define FNV_PRIME32 16777619u
static uint32_t
xipHash32(const char* s)
{
uint32_t hash;
hash = FNV_OFFSET_BASIS;
while (*s) {
hash ^= *s++;
hash *= FNV_PRIME32;
}
return hash;
}
// Normalize a TOC entry
// Normalize the TOC entry by converting relocatable pointers into 32-bit
// offsets from the beginning of the section containing the data. All
// addresses in the TOC are actually 32-bit offsets in the address space named
// in bits 16:31 of the link address of the image.
XIP_STATIC int
xipNormalizeToc(void* io_image, SbeXipToc *io_imageToc,
SbeXipHashedToc** io_fixedTocEntry,
size_t* io_fixedEntriesRemaining)
{
SbeXipToc hostToc;
int idSection, dataSection;
uint32_t idOffset, dataOffset;
char* hostString;
int rc;
do {
// Translate the TOC entry to host format. Then locate the
// sections/offsets of the Id string (which must be in .strings) and
// the data.
xipTranslateToc(&hostToc, io_imageToc);
hostString =
(char*)xipPore2Host(io_image,
xipFullAddress(io_image, hostToc.iv_id));
rc = xipPore2Section(io_image,
xipFullAddress(io_image, hostToc.iv_id),
&idSection,
&idOffset);
if (rc) break;
if (idSection != SBE_XIP_SECTION_STRINGS) {
rc = TRACE_ERROR(SBE_XIP_IMAGE_ERROR);
break;
}
rc = xipPore2Section(io_image,
xipFullAddress(io_image, hostToc.iv_data),
&dataSection,
&dataOffset);
if (rc) break;
// Now replace the Id and data pointers with their offsets, and update
// the data section in the TOC entry.
hostToc.iv_id = idOffset;
hostToc.iv_data = dataOffset;
hostToc.iv_section = dataSection;
// If this TOC entry is from .fixed, create a new record in .fixed_toc
if (hostToc.iv_section == SBE_XIP_SECTION_FIXED) {
if (*io_fixedEntriesRemaining == 0) {
rc = TRACE_ERRORX(SBE_XIP_TOC_ERROR,
"Too many TOC entries for .fixed\n");
break;
}
if (hostToc.iv_data != (uint16_t)hostToc.iv_data) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"The .fixed section is too big to index\n");
break;
}
(*io_fixedTocEntry)->iv_hash = xipRevLe32(xipHash32(hostString));
(*io_fixedTocEntry)->iv_offset = xipRevLe16(hostToc.iv_data);
(*io_fixedTocEntry)->iv_type = hostToc.iv_type;
(*io_fixedTocEntry)->iv_elements = hostToc.iv_elements;
(*io_fixedTocEntry)++;
(*io_fixedEntriesRemaining)--;
}
// Finally update the TOC entry
xipTranslateToc(io_imageToc, &hostToc);
} while (0);
return rc;
}
// Check for hash collisions in the .fixed mini-TOC. Note that endianness is
// not an issue here, as we're comparing for equality.
XIP_STATIC int
xipHashCollision(SbeXipHashedToc* i_fixedToc, size_t i_entries)
{
int rc;
size_t i, j;
rc = 0;
for (i = 0; i < i_entries; i++) {
for (j = i + 1; j < i_entries; j++) {
if (i_fixedToc[i].iv_hash == i_fixedToc[j].iv_hash) {
rc = TRACE_ERRORX(SBE_XIP_HASH_COLLISION,
"Hash collision at index %d\n",
i);
break;
}
}
if (rc) break;
}
return rc;
}
/// Decode a normalized image-format TOC entry into a host-format SbeXipItem
/// structure
XIP_STATIC int
xipDecodeToc(void* i_image,
SbeXipToc* i_imageToc,
SbeXipItem* o_item)
{
int rc;
SbeXipToc hostToc;
SbeXipSection dataSection, stringsSection;
do {
if (!xipNormalized(i_image)) {
rc = TRACE_ERROR(SBE_XIP_NOT_NORMALIZED);
break;
}
// Translate the TOC entry and set the TOC pointer, data type and
// number of elements in the outgoing structure. The Id string is
// always located in the TOC_STRINGS section.
xipTranslateToc(&hostToc, i_imageToc);
o_item->iv_toc = i_imageToc;
o_item->iv_type = hostToc.iv_type;
o_item->iv_elements = hostToc.iv_elements;
rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_STRINGS,
&stringsSection);
if (rc) break;
o_item->iv_id =
(char*)i_image + stringsSection.iv_offset + hostToc.iv_id;
// The data (or text address) are addressed by relative offsets from
// the beginning of their section. The TOC entry may remain in the TOC
// even though the section has been removed from the image, so this
// case needs to be covered.
rc = sbe_xip_get_section(i_image, hostToc.iv_section, &dataSection);
if (rc) break;
if (dataSection.iv_size == 0) {
rc = TRACE_ERROR(SBE_XIP_DATA_NOT_PRESENT);
break;
}
o_item->iv_imageData =
(void*)((uint8_t*)i_image +
dataSection.iv_offset + hostToc.iv_data);
o_item->iv_address =
xipLinkAddress(i_image) + dataSection.iv_offset + hostToc.iv_data;
o_item->iv_partial = 0;
} while (0);
return rc;
}
/// Sort the TOC
XIP_STATIC int
xipSortToc(void* io_image)
{
int rc;
SbeXipToc *hostToc;
size_t entries;
char* strings;
do {
rc = xipQuickCheck(io_image, 1);
if (rc) break;
if (xipSorted(io_image)) break;
rc = xipGetToc(io_image, &hostToc, &entries, 0, &strings);
if (rc) break;
xipQuickSort(hostToc, 0, entries - 1, strings);
((SbeXipHeader*)io_image)->iv_tocSorted = 1;
} while (0);
return rc;
}
// Pad the image with 0 to a given power-of-2 alignment. The image size is
// modified to reflect the pad, but the caller must modify the section size to
// reflect the pad.
XIP_STATIC int
xipPadImage(void* io_image, uint32_t i_allocation,
uint32_t i_align, uint32_t* pad)
{
int rc;
do {
rc = 0;
if ((i_align == 0) || ((i_align & (i_align - 1)) != 0)) {
rc = TRACE_ERRORX(SBE_XIP_INVALID_ARGUMENT,
"Alignment specification (%u) "
"not a power-of-2\n",
i_align);
break;
}
*pad = xipImageSize(io_image) % i_align;
if (*pad != 0) {
*pad = i_align - *pad;
if ((xipImageSize(io_image) + *pad) > i_allocation) {
rc = TRACE_ERROR(SBE_XIP_WOULD_OVERFLOW);
break;
}
memset((void*)((unsigned long)io_image + xipImageSize(io_image)),
0, *pad);
xipSetImageSize(io_image, xipImageSize(io_image) + *pad);
}
} while (0);
return rc;
}
// Get the .fixed_toc section
XIP_STATIC int
xipGetFixedToc(void* io_image,
SbeXipHashedToc** o_imageToc,
size_t* o_entries)
{
int rc;
SbeXipSection section;
rc = sbe_xip_get_section(io_image, SBE_XIP_SECTION_FIXED_TOC, &section);
if (!rc) {
*o_imageToc =
(SbeXipHashedToc*)((unsigned long)io_image + section.iv_offset);
*o_entries = section.iv_size / sizeof(SbeXipHashedToc);
}
return rc;
}
// Search for an item in the fixed TOC, and populate a partial TOC entry if
// requested. This table is small and unsorted so a linear search is
// adequate. The TOC structures are also small so all byte-reversal is done
// 'by hand' rather than with a translate-type API.
XIP_STATIC int
xipFixedFind(void* i_image, const char* i_id, SbeXipItem* o_item)
{
int rc;
SbeXipHashedToc* toc;
size_t entries;
uint32_t hash;
SbeXipSection fixedSection;
uint32_t offset;
do {
rc = xipGetFixedToc(i_image, &toc, &entries);
if (rc) break;
for (hash = xipRevLe32(xipHash32(i_id)); entries != 0; entries--, toc++) {
if (toc->iv_hash == hash) break;
}
if (entries == 0) {
rc = SBE_XIP_ITEM_NOT_FOUND;
break;
} else {
rc = 0;
}
// The caller may have requested a lookup only (o_item == 0), in which
// case we're done. Otherwise we create a partial SbeXipItem and
// populate the non-0 fields analogously to the xipDecodeToc()
// routine. The data resides in the .fixed section in this case.
if (o_item == 0) break;
o_item->iv_partial = 1;
o_item->iv_toc = 0;
o_item->iv_id = 0;
o_item->iv_type = toc->iv_type;
o_item->iv_elements = toc->iv_elements;
rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_FIXED, &fixedSection);
if (rc) break;
if (fixedSection.iv_size == 0) {
rc = TRACE_ERROR(SBE_XIP_DATA_NOT_PRESENT);
break;
}
offset = fixedSection.iv_offset + xipRevLe16(toc->iv_offset);
o_item->iv_imageData = (void*)((uint8_t*)i_image + offset);
o_item->iv_address = xipLinkAddress(i_image) + offset;
} while (0);
return rc;
}
// Search for an item in the special built-in TOC of header fields, and
// populate a partial TOC entry if requested.
//
// This facility was added to allow header data to be searched by name even
// when the TOC has been stripped. This API will only be used in the case of a
// stripped TOC since the header fields are also indexed in the main TOC.
//
// The table is allocated on the stack in order to make this code concurrently
// patchable in PHYP (although PHYP applications will never use this code).
// The table is small and unsorted so a linear search is adequate, and the
// stack requirememts are small.
XIP_STATIC int
xipHeaderFind(void* i_image, const char* i_id, SbeXipItem* o_item)
{
int rc;
unsigned i;
uint32_t offset;
SbeXipSection headerSection;
#define HEADER_TOC(id, field, type) \
{#id, offsetof(SbeXipHeader, field), type}
struct HeaderToc {
const char* iv_id;
uint16_t iv_offset;
uint8_t iv_type;
} toc[] = {
HEADER_TOC(magic, iv_magic, SBE_XIP_UINT64),
HEADER_TOC(entry_offset, iv_entryOffset, SBE_XIP_UINT64),
HEADER_TOC(link_address, iv_linkAddress, SBE_XIP_UINT64),
HEADER_TOC(image_size, iv_imageSize, SBE_XIP_UINT32),
HEADER_TOC(build_date, iv_buildDate, SBE_XIP_UINT32),
HEADER_TOC(build_time, iv_buildTime, SBE_XIP_UINT32),
HEADER_TOC(header_version, iv_headerVersion, SBE_XIP_UINT8),
HEADER_TOC(toc_normalized, iv_normalized, SBE_XIP_UINT8),
HEADER_TOC(toc_sorted, iv_tocSorted, SBE_XIP_UINT8),
HEADER_TOC(build_user, iv_buildUser, SBE_XIP_STRING),
HEADER_TOC(build_host, iv_buildHost, SBE_XIP_STRING),
};
do {
rc = SBE_XIP_ITEM_NOT_FOUND;
for (i = 0; i < (sizeof(toc) / sizeof(struct HeaderToc)); i++) {
if (strcmp(i_id, toc[i].iv_id) == 0) {
rc = 0;
break;
}
}
if (rc) break;
// The caller may have requested a lookup only (o_item == 0), in which
// case we're done. Otherwise we create a partial SbeXipItem and
// populate the non-0 fields analogously to the xipDecodeToc()
// routine. The data resides in the .fixed section in this case.
if (o_item == 0) break;
o_item->iv_partial = 1;
o_item->iv_toc = 0;
o_item->iv_id = 0;
o_item->iv_type = toc[i].iv_type;
o_item->iv_elements = 1; /* True for now... */
rc = sbe_xip_get_section(i_image, SBE_XIP_SECTION_HEADER,
&headerSection);
if (rc) break;
if (headerSection.iv_size == 0) {
rc = TRACE_ERROR(SBE_XIP_DATA_NOT_PRESENT);
break;
}
offset = headerSection.iv_offset + toc[i].iv_offset;
o_item->iv_imageData = (void*)((uint8_t*)i_image + offset);
o_item->iv_address = xipLinkAddress(i_image) + offset;
} while (0);
return rc;
}
////////////////////////////////////////////////////////////////////////////
// Published API
////////////////////////////////////////////////////////////////////////////
int
sbe_xip_validate(void* i_image, const uint32_t i_size)
{
SbeXipHeader hostHeader;
int rc = 0, i;
uint32_t linkAddress, imageSize, extent, offset, size;
uint8_t alignment;
sbe_xip_translate_header(&hostHeader, (SbeXipHeader*)i_image);
do {
// Validate C/Assembler constraints.
if (sizeof(SbeXipSection) != SIZE_OF_SBE_XIP_SECTION) {
rc = TRACE_ERRORX(SBE_XIP_BUG,
"C/Assembler size mismatch(%d/%d) "
"for SbeXipSection\n",
sizeof(SbeXipSection), SIZE_OF_SBE_XIP_SECTION);
break;
}
if (sizeof(SbeXipToc) != SIZE_OF_SBE_XIP_TOC) {
rc = TRACE_ERRORX(SBE_XIP_BUG,
"C/Assembler size mismatch(%d/%d) "
"for SbeXipToc\n",
sizeof(SbeXipToc), SIZE_OF_SBE_XIP_TOC);
break;
}
if (sizeof(SbeXipHashedToc) != SIZE_OF_SBE_XIP_HASHED_TOC) {
rc = TRACE_ERRORX(SBE_XIP_BUG,
"C/Assembler size mismatch(%d/%d) "
"for SbeXipHashedToc\n",
sizeof(SbeXipHashedToc),
SIZE_OF_SBE_XIP_HASHED_TOC);
break;
}
// Validate the image pointer and magic number
rc = xipQuickCheck(i_image, 0);
if (rc) break;
// Validate the image size
linkAddress = hostHeader.iv_linkAddress;
imageSize = hostHeader.iv_imageSize;
extent = linkAddress + imageSize;
if (imageSize < sizeof(SbeXipHeader)) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"sbe_xip_validate(%p, %u) : "
"The image size recorded in the image "
"(%u) is smaller than the header size.\n",
i_image, i_size, imageSize);
break;
}
if (imageSize != i_size) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"sbe_xip_validate(%p, %u) : "
"The image size recorded in the image "
"(%u) does not match the i_size parameter.\n",
i_image, i_size, imageSize);
break;
}
if (extent <= linkAddress) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"sbe_xip_validate(%p, %u) : "
"Given the link address (%u) and the "
"image size, the image wraps the address space\n",
i_image, i_size, linkAddress);
break;
}
if ((imageSize % SBE_XIP_FINAL_ALIGNMENT) != 0) {
rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR,
"sbe_xip_validate(%p, %u) : "
"The image size (%u) is not a multiple of %u\n",
i_image, i_size, imageSize,
SBE_XIP_FINAL_ALIGNMENT);
break;
}
// Validate that all sections appear to be within the image
// bounds, and are aligned correctly.
for (i = 0; i < SBE_XIP_SECTIONS; i++) {
offset = hostHeader.iv_section[i].iv_offset;
size = hostHeader.iv_section[i].iv_size;
alignment = hostHeader.iv_section[i].iv_alignment;
if ((offset > imageSize) ||
((offset + size) > imageSize) ||
((offset + size) < offset)) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"Section %d does not appear to be within "
"the bounds of the image\n"
"offset = %u, size = %u, image size = %u\n",
i, offset, size, imageSize);
break;
}
if ((offset % alignment) != 0) {
rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR,
"Section %d requires %d-byte initial "
"alignment but the section offset is %u\n",
i, alignment, offset);
break;
}
}
if (rc) break;
// If the TOC exists and the image is normalized, validate each TOC
// entry.
size = hostHeader.iv_section[SBE_XIP_SECTION_TOC].iv_size;
if (size != 0) {
if (xipNormalized(i_image)) {
rc = sbe_xip_map_toc(i_image, xipValidateTocEntry, 0);
if (rc) break;
}
}
} while (0);
return rc;
}
int
sbe_xip_validate2(void* i_image, const uint32_t i_size, const uint32_t i_maskIgnores)
{
SbeXipHeader hostHeader;
int rc = 0, i;
uint32_t linkAddress, imageSize, extent, offset, size;
uint8_t alignment;
sbe_xip_translate_header(&hostHeader, (SbeXipHeader*)i_image);
do {
// Validate C/Assembler constraints.
if (sizeof(SbeXipSection) != SIZE_OF_SBE_XIP_SECTION) {
rc = TRACE_ERRORX(SBE_XIP_BUG,
"C/Assembler size mismatch(%d/%d) "
"for SbeXipSection\n",
sizeof(SbeXipSection), SIZE_OF_SBE_XIP_SECTION);
break;
}
if (sizeof(SbeXipToc) != SIZE_OF_SBE_XIP_TOC) {
rc = TRACE_ERRORX(SBE_XIP_BUG,
"C/Assembler size mismatch(%d/%d) "
"for SbeXipToc\n",
sizeof(SbeXipToc), SIZE_OF_SBE_XIP_TOC);
break;
}
if (sizeof(SbeXipHashedToc) != SIZE_OF_SBE_XIP_HASHED_TOC) {
rc = TRACE_ERRORX(SBE_XIP_BUG,
"C/Assembler size mismatch(%d/%d) "
"for SbeXipHashedToc\n",
sizeof(SbeXipHashedToc),
SIZE_OF_SBE_XIP_HASHED_TOC);
break;
}
// Validate the image pointer and magic number
rc = xipQuickCheck(i_image, 0);
if (rc) break;
// Validate the image size
linkAddress = hostHeader.iv_linkAddress;
imageSize = hostHeader.iv_imageSize;
extent = linkAddress + imageSize;
if (imageSize < sizeof(SbeXipHeader)) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"sbe_xip_validate2(%p, %u) : "
"The image size recorded in the image "
"(%u) is smaller than the header size.\n",
i_image, i_size, imageSize);
break;
}
if (imageSize != i_size && !(i_maskIgnores & SBE_XIP_IGNORE_FILE_SIZE)) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"sbe_xip_validate2(%p, %u) : "
"The image size recorded in the image "
"(%u) does not match the i_size parameter.\n",
i_image, i_size, imageSize);
break;
}
if (extent <= linkAddress) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"sbe_xip_validate2(%p, %u) : "
"Given the link address (%u) and the "
"image size, the image wraps the address space\n",
i_image, i_size, linkAddress);
break;
}
if ((imageSize % SBE_XIP_FINAL_ALIGNMENT) != 0) {
rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR,
"sbe_xip_validate2(%p, %u) : "
"The image size (%u) is not a multiple of %u\n",
i_image, i_size, imageSize,
SBE_XIP_FINAL_ALIGNMENT);
break;
}
// Validate that all sections appear to be within the image
// bounds, and are aligned correctly.
for (i = 0; i < SBE_XIP_SECTIONS; i++) {
offset = hostHeader.iv_section[i].iv_offset;
size = hostHeader.iv_section[i].iv_size;
alignment = hostHeader.iv_section[i].iv_alignment;
if ((offset > imageSize) ||
((offset + size) > imageSize) ||
((offset + size) < offset)) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"Section %d does not appear to be within "
"the bounds of the image\n"
"offset = %u, size = %u, image size = %u\n",
i, offset, size, imageSize);
break;
}
if ((offset % alignment) != 0) {
rc = TRACE_ERRORX(SBE_XIP_ALIGNMENT_ERROR,
"Section %d requires %d-byte initial "
"alignment but the section offset is %u\n",
i, alignment, offset);
break;
}
}
if (rc) break;
// If the TOC exists and the image is normalized, validate each TOC
// entry.
size = hostHeader.iv_section[SBE_XIP_SECTION_TOC].iv_size;
if (size != 0) {
if (xipNormalized(i_image)) {
rc = sbe_xip_map_toc(i_image, xipValidateTocEntry, 0);
if (rc) break;
}
}
} while (0);
return rc;
}
// Normalization:
//
// 1. Normalize the TOC, unless the image is already normalized. The image
// must be marked as normalized before sorting.
//
// 2. Sort the TOC.
//
// 3. Clear the section offsets of any empty sections to make the section
// table reports less confusing.
//
// 4. Clear normalization status on any failure.
int
sbe_xip_normalize(void* io_image)
{
int rc, i;
SbeXipSection section;
SbeXipToc* imageToc;
SbeXipHashedToc* fixedImageToc = NULL;
SbeXipHashedToc* fixedTocEntry = NULL;
size_t tocEntries = 0;
size_t fixedTocEntries = 0;
size_t fixedEntriesRemaining = 0;
do {
rc = xipQuickCheck(io_image, 0);
if (rc) break;
if (!xipNormalized(io_image)) {
rc = xipGetToc(io_image, &imageToc, &tocEntries, 0, 0);
if (rc) break;
rc = xipGetFixedToc(io_image, &fixedImageToc, &fixedTocEntries);
if (rc) break;
fixedTocEntry = fixedImageToc;
fixedEntriesRemaining = fixedTocEntries;
for (; tocEntries--; imageToc++) {
rc = xipNormalizeToc(io_image, imageToc,
&fixedTocEntry, &fixedEntriesRemaining);
if (rc) break;
}
if (rc) break;
if (fixedEntriesRemaining != 0) {
rc = TRACE_ERRORX(SBE_XIP_TOC_ERROR,
"Not enough TOC entries for .fixed");
break;
}
rc = xipHashCollision(fixedImageToc, fixedTocEntries);
if (rc) break;
((SbeXipHeader*)io_image)->iv_normalized = 1;
}
rc = xipSortToc(io_image);
if (rc) break;
for (i = 0; i < SBE_XIP_SECTIONS; i++) {
rc = sbe_xip_get_section(io_image, i, &section);
if (rc) break;
if (section.iv_size == 0) {
xipSetSectionOffset(io_image, i, 0);
}
}
if (rc) break;
} while(0);
((SbeXipHeader*)io_image)->iv_normalized = (rc == 0);
return rc;
}
int
sbe_xip_image_size(void* io_image, uint32_t* o_size)
{
int rc;
rc = xipQuickCheck(io_image, 0);
if (!rc) {
*o_size = xipImageSize(io_image);
}
return rc;
}
int
sbe_xip_get_section(const void* i_image,
const int i_sectionId,
SbeXipSection* o_hostSection)
{
int rc;
SbeXipSection *imageSection;
rc = xipGetSectionPointer(i_image, i_sectionId, &imageSection);
if (!rc) {
xipTranslateSection(o_hostSection, imageSection);
}
return rc;
}
// If the 'big' TOC is not present, search the mini-TOCs that only index the
// .fixed and .header sections.
int
sbe_xip_find(void* i_image,
const char* i_id,
SbeXipItem* o_item)
{
int rc;
SbeXipToc* toc;
SbeXipItem item, *pitem;
SbeXipSection* tocSection;
do {
rc = xipQuickCheck(i_image, 1);
if (rc) break;
rc = xipGetSectionPointer(i_image, SBE_XIP_SECTION_TOC, &tocSection);
if (rc) break;
if (tocSection->iv_size == 0) {
rc = xipFixedFind(i_image, i_id, o_item);
if (rc) {
rc = xipHeaderFind(i_image, i_id, o_item);
}
break;
}
if (xipSorted(i_image)) {
rc = xipBinarySearch(i_image, i_id, &toc);
} else {
rc = xipLinearSearch(i_image, i_id, &toc);
}
if (rc) break;
if (o_item) {
pitem = o_item;
} else {
pitem = &item;
}
rc = xipDecodeToc(i_image, toc, pitem);
if (rc) break;
} while (0);
return rc;
}
int
sbe_xip_map_halt(void* io_image,
int (*i_fn)(void* io_image,
const uint64_t i_poreAddress,
const char* i_rcString,
void* io_arg),
void* io_arg)
{
int rc;
SbeXipSection haltSection;
SbeXipHalt *halt;
uint32_t size;
uint32_t actualSize;
do {
rc = xipQuickCheck(io_image, 0);
if (rc) break;
rc = sbe_xip_get_section(io_image, SBE_XIP_SECTION_HALT, &haltSection);
if (rc) break;
halt = (SbeXipHalt*)((unsigned long)io_image + haltSection.iv_offset);
size = haltSection.iv_size;
while (size) {
rc = i_fn(io_image,
xipRevLe64(halt->iv_address),
halt->iv_string,
io_arg);
if (rc) break;
// The SbeXipHalt structure claims a 4-character string. The
// computation below computes the actual record size based on the
// actual length of the string, including the 0-byte termination.
actualSize = 8 + (((strlen(halt->iv_string) + 4) / 4) * 4);
if (size < actualSize) {
rc = TRACE_ERRORX(SBE_XIP_IMAGE_ERROR,
"The .halt section is improperly formed\n");
break;
}
size -= actualSize;
halt = (SbeXipHalt*)((unsigned long)halt + actualSize);
};
if (rc) break;
} while (0);
return rc;
}
typedef struct {
uint64_t iv_address;
const char* iv_string;
} GetHaltStruct;
XIP_STATIC int
xipGetHaltMap(void* io_image,
const uint64_t i_poreAddress,
const char* i_rcString,
void* io_arg)
{
int rc;
GetHaltStruct* s = (GetHaltStruct*)io_arg;
(void)io_image;
if (i_poreAddress == s->iv_address) {
s->iv_string = i_rcString;
rc = -1;
} else {
rc = 0;
}
return rc;
}
int
sbe_xip_get_halt(void* io_image,
const uint64_t i_poreAddress,
const char** o_rcString)
{
int rc;
GetHaltStruct s;
s.iv_address = i_poreAddress;
do {
rc = xipQuickCheck(io_image, 0);
if (rc) break;
rc = sbe_xip_map_halt(io_image, xipGetHaltMap, &s);
if (rc == 0) {
rc = TRACE_ERRORX(SBE_XIP_ITEM_NOT_FOUND,
"sbe_xip_get_halt: No HALT code is associated "
"with address " F0x012llx "\n", i_poreAddress);
} else if (rc < 0) {
*o_rcString = s.iv_string;
rc = 0;
}
} while (0);
return rc;
}
int
sbe_xip_get_scalar(void *i_image, const char* i_id, uint64_t* o_data)
{
int rc;
SbeXipItem item;
rc = sbe_xip_find(i_image, i_id, &item);
if (!rc) {
switch (item.iv_type) {
case SBE_XIP_UINT8:
*o_data = *((uint8_t*)(item.iv_imageData));
break;
case SBE_XIP_UINT32:
*o_data = xipRevLe32(*((uint32_t*)(item.iv_imageData)));
break;
case SBE_XIP_UINT64:
*o_data = xipRevLe64(*((uint64_t*)(item.iv_imageData)));
break;
case SBE_XIP_ADDRESS:
*o_data = item.iv_address;
break;
default:
rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR);
break;
}
}
return rc;
}
int
sbe_xip_get_element(void *i_image,
const char* i_id,
const uint32_t i_index,
uint64_t* o_data)
{
int rc;
SbeXipItem item;
do {
rc = sbe_xip_find(i_image, i_id, &item);
if (rc) break;
if ((item.iv_elements != 0) && (i_index >= item.iv_elements)) {
rc = TRACE_ERROR(SBE_XIP_BOUNDS_ERROR);
break;
}
switch (item.iv_type) {
case SBE_XIP_UINT8:
*o_data = ((uint8_t*)(item.iv_imageData))[i_index];
break;
case SBE_XIP_UINT32:
*o_data = xipRevLe32(((uint32_t*)(item.iv_imageData))[i_index]);
break;
case SBE_XIP_UINT64:
*o_data = xipRevLe64(((uint64_t*)(item.iv_imageData))[i_index]);
break;
default:
rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR);
break;
}
if (rc) break;
} while (0);
return rc;
}
int
sbe_xip_get_string(void *i_image, const char* i_id, char** o_data)
{
int rc;
SbeXipItem item;
rc = sbe_xip_find(i_image, i_id, &item);
if (!rc) {
switch (item.iv_type) {
case SBE_XIP_STRING:
*o_data = (char*)(item.iv_imageData);
break;
default:
rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR);
break;
}
}
return rc;
}
int
sbe_xip_read_uint64(const void *i_image,
const uint64_t i_poreAddress,
uint64_t* o_data)
{
int rc;
do {
rc = xipQuickCheck(i_image, 0);
if (rc) break;
rc = xipValidatePoreAddress(i_image, i_poreAddress, 8);
if (rc) break;
if (i_poreAddress % 8) {
rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR);
break;
}
*o_data =
xipRevLe64(*((uint64_t*)xipPore2Host(i_image, i_poreAddress)));
} while(0);
return rc;
}
int
sbe_xip_set_scalar(void* io_image, const char* i_id, const uint64_t i_data)
{
int rc;
SbeXipItem item;
rc = sbe_xip_find(io_image, i_id, &item);
if (!rc) {
switch(item.iv_type) {
case SBE_XIP_UINT8:
*((uint8_t*)(item.iv_imageData)) = (uint8_t)i_data;
break;
case SBE_XIP_UINT32:
*((uint32_t*)(item.iv_imageData)) = xipRevLe32((uint32_t)i_data);
break;
case SBE_XIP_UINT64:
*((uint64_t*)(item.iv_imageData)) = xipRevLe64((uint64_t)i_data);
break;
default:
rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR);
break;
}
}
return rc;
}
int
sbe_xip_set_element(void *i_image,
const char* i_id,
const uint32_t i_index,
const uint64_t i_data)
{
int rc;
SbeXipItem item;
do {
rc = sbe_xip_find(i_image, i_id, &item);
if (rc) break;
if ((item.iv_elements != 0) && (i_index >= item.iv_elements)) {
rc = TRACE_ERROR(SBE_XIP_BOUNDS_ERROR);
break;
}
switch (item.iv_type) {
case SBE_XIP_UINT8:
((uint8_t*)(item.iv_imageData))[i_index] = (uint8_t)i_data;
break;
case SBE_XIP_UINT32:
((uint32_t*)(item.iv_imageData))[i_index] =
xipRevLe32((uint32_t)i_data);
break;
case SBE_XIP_UINT64:
((uint64_t*)(item.iv_imageData))[i_index] =
xipRevLe64((uint64_t)i_data);
break;
default:
rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR);
break;
}
if (rc) break;
} while (0);
return rc;
}
int
sbe_xip_set_string(void *i_image, const char* i_id, const char* i_data)
{
int rc;
SbeXipItem item;
char* dest;
rc = sbe_xip_find(i_image, i_id, &item);
if (!rc) {
switch (item.iv_type) {
case SBE_XIP_STRING:
dest = (char*)(item.iv_imageData);
if (strlen(dest) < strlen(i_data)) {
memcpy(dest, i_data, strlen(dest));
} else {
strcpy(dest, i_data);
}
break;
default:
rc = TRACE_ERROR(SBE_XIP_TYPE_ERROR);
break;
}
}
return rc;
}
int
sbe_xip_write_uint64(void *io_image,
const uint64_t i_poreAddress,
const uint64_t i_data)
{
int rc;
do {
rc = xipQuickCheck(io_image, 0);
if (rc) break;
rc = xipValidatePoreAddress(io_image, i_poreAddress, 8);
if (rc) break;
if (i_poreAddress % 8) {
rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR);
break;
}
*((uint64_t*)xipPore2Host(io_image, i_poreAddress)) =
xipRevLe64(i_data);
} while(0);
return rc;
}
int
sbe_xip_delete_section(void* io_image, const int i_sectionId)
{
int rc, final;
SbeXipSection section;
do {
rc = xipQuickCheck(io_image, 1);
if (rc) break;
rc = sbe_xip_get_section(io_image, i_sectionId, &section);
if (rc) break;
// Deleting an empty section is a NOP. Otherwise the section must be
// the final section of the image. Update the sizes and re-establish
// the final image alignment.
if (section.iv_size == 0) break;
rc = xipFinalSection(io_image, &final);
if (rc) break;
if (final != i_sectionId) {
rc = TRACE_ERRORX(SBE_XIP_SECTION_ERROR,
"Attempt to delete non-final section %d\n",
i_sectionId);
break;
}
xipSetSectionOffset(io_image, i_sectionId, 0);
xipSetSectionSize(io_image, i_sectionId, 0);
// For cleanliness we also remove any alignment padding that had been
// appended between the now-last section and the deleted section, then
// re-establish the final alignment. The assumption is that all images
// always have the correct final alignment, so there is no way this
// could overflow a designated buffer space since the image size is
// the same or has been reduced.
rc = xipFinalSection(io_image, &final);
if (rc) break;
rc = sbe_xip_get_section(io_image, final, &section);
if (rc) break;
xipSetImageSize(io_image, section.iv_offset + section.iv_size);
xipFinalAlignment(io_image);
} while (0);
return rc;
}
#ifndef PPC_HYP
// This API is not needed by PHYP procedures, and is elided since PHYP does
// not support malloc().
int
sbe_xip_duplicate_section(const void* i_image,
const int i_sectionId,
void** o_duplicate,
uint32_t* o_size)
{
SbeXipSection section;
int rc;
*o_duplicate = 0;
do {
rc = xipQuickCheck(i_image, 0);
if (rc) break;
rc = sbe_xip_get_section(i_image, i_sectionId, &section);
if (rc) break;
if (section.iv_size == 0) {
rc = TRACE_ERRORX(SBE_XIP_SECTION_ERROR,
"Attempt to duplicate empty section %d\n",
i_sectionId);
break;
}
*o_duplicate = malloc(section.iv_size);
*o_size = section.iv_size;
if (*o_duplicate == 0) {
rc = TRACE_ERROR(SBE_XIP_NO_MEMORY);
break;
}
memcpy(*o_duplicate,
xipHostAddressFromOffset(i_image, section.iv_offset),
section.iv_size);
} while (0);
if (rc) {
free(*o_duplicate);
*o_duplicate = 0;
*o_size = 0;
}
return rc;
}
#endif // PPC_HYP
// The append must be done in such a way that if the append fails, the image
// is not modified. This behavior is required by applications that
// speculatively append until the allocation fails, but still require the
// final image to be valid. To accomplish this the initial image size and
// section statistics are captured at entry, and restored in the event of an
// error.
int
sbe_xip_append(void* io_image,
const int i_sectionId,
const void* i_data,
const uint32_t i_size,
const uint32_t i_allocation,
uint32_t* o_sectionOffset)
{
SbeXipSection section, initialSection;
int rc, final, restoreOnError;
void* hostAddress;
uint32_t pad, initialSize;
do {
restoreOnError = 0;
rc = xipQuickCheck(io_image, 1);
if (rc) break;
rc = sbe_xip_get_section(io_image, i_sectionId, &section);
if (rc) break;
if (i_size == 0) break;
initialSection = section;
initialSize = xipImageSize(io_image);
restoreOnError = 1;
if (section.iv_size == 0) {
// The section is empty, and now becomes the final section. Pad
// the image to the specified section alignment. Note that the
// size of the previously final section does not change.
rc = xipPadImage(io_image, i_allocation, section.iv_alignment,
&pad);
if (rc) break;
section.iv_offset = xipImageSize(io_image);
} else {
// Otherwise, the section must be the final section in order to
// continue. Remove any padding from the image.
rc = xipFinalSection(io_image, &final);
if (rc) break;
if (final != i_sectionId) {
rc = TRACE_ERRORX(SBE_XIP_SECTION_ERROR,
"Attempt to append to non-final section "
"%d\n", i_sectionId);
break;
}
xipSetImageSize(io_image, section.iv_offset + section.iv_size);
}
// Make sure the allocated space won't overflow. Set the return
// parameter o_sectionOffset and copy the new data into the image (or
// simply clear the space).
if ((xipImageSize(io_image) + i_size) > i_allocation) {
rc = TRACE_ERROR(SBE_XIP_WOULD_OVERFLOW);
break;
}
if (o_sectionOffset != 0) {
*o_sectionOffset = section.iv_size;
}
hostAddress =
xipHostAddressFromOffset(io_image, xipImageSize(io_image));
if (i_data == 0) {
memset(hostAddress, 0, i_size);
} else {
memcpy(hostAddress, i_data, i_size);
}
// Update the image size and section table. Note that the final
// alignment may push out of the allocation.
xipSetImageSize(io_image, xipImageSize(io_image) + i_size);
xipFinalAlignment(io_image);
if (xipImageSize(io_image) > i_allocation) {
rc = TRACE_ERROR(SBE_XIP_WOULD_OVERFLOW);
break;
}
section.iv_size += i_size;
if (xipPutSection(io_image, i_sectionId, &section) != 0) {
rc = TRACE_ERROR(SBE_XIP_BUG); /* Can't happen */
break;
}
// Special case
if (i_sectionId == SBE_XIP_SECTION_TOC) {
((SbeXipHeader*)io_image)->iv_tocSorted = 0;
}
} while (0);
if (rc && restoreOnError) {
if (xipPutSection(io_image, i_sectionId, &initialSection) != 0) {
rc = TRACE_ERROR(SBE_XIP_BUG); /* Can't happen */
}
xipSetImageSize(io_image, initialSize);
}
return rc;
}
int
sbe_xip_section2pore(const void* i_image,
const int i_sectionId,
const uint32_t i_offset,
uint64_t* o_poreAddress)
{
int rc;
SbeXipSection section;
do {
rc = xipQuickCheck(i_image, 0);
if (rc) break;
rc = sbe_xip_get_section(i_image, i_sectionId, &section);
if (rc) break;
if (section.iv_size == 0) {
rc = TRACE_ERROR(SBE_XIP_SECTION_ERROR);
break;
}
if (i_offset > (section.iv_offset + section.iv_size)) {
rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT);
break;
}
*o_poreAddress = xipLinkAddress(i_image) + section.iv_offset + i_offset;
if (*o_poreAddress % 4) {
rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR);
break;
}
} while(0);
return rc;
}
int
sbe_xip_pore2section(const void* i_image,
const uint64_t i_poreAddress,
int* i_section,
uint32_t* i_offset)
{
int rc;
do {
rc = xipQuickCheck(i_image, 0);
if (rc) break;
rc = xipPore2Section(i_image, i_poreAddress, i_section, i_offset);
} while(0);
return rc;
}
int
sbe_xip_pore2host(const void* i_image,
const uint64_t i_poreAddress,
void** o_hostAddress)
{
int rc;
do {
rc = xipQuickCheck(i_image, 0);
if (rc) break;
if ((i_poreAddress < xipLinkAddress(i_image)) ||
(i_poreAddress >
(xipLinkAddress(i_image) + xipImageSize(i_image)))) {
rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT);
break;
}
*o_hostAddress =
xipHostAddressFromOffset(i_image,
i_poreAddress - xipLinkAddress(i_image));
} while(0);
return rc;
}
int
sbe_xip_host2pore(const void* i_image,
void* i_hostAddress,
uint64_t* o_poreAddress)
{
int rc;
do {
rc = xipQuickCheck(i_image, 0);
if (rc) break;
if ((i_hostAddress < i_image) ||
(i_hostAddress >
xipHostAddressFromOffset(i_image, xipImageSize(i_image)))) {
rc = TRACE_ERROR(SBE_XIP_INVALID_ARGUMENT);
break;
}
*o_poreAddress = xipLinkAddress(i_image) +
((unsigned long)i_hostAddress - (unsigned long)i_image);
if (*o_poreAddress % 4) {
rc = TRACE_ERROR(SBE_XIP_ALIGNMENT_ERROR);
break;
}
} while(0);
return rc;
}
void
sbe_xip_translate_header(SbeXipHeader* o_dest, const SbeXipHeader* i_src)
{
#ifndef _BIG_ENDIAN
int i;
SbeXipSection* destSection;
const SbeXipSection* srcSection;
#if SBE_XIP_HEADER_VERSION != 8
#error This code assumes the SBE-XIP header version 8 layout
#endif
o_dest->iv_magic = xipRevLe64(i_src->iv_magic);
o_dest->iv_entryOffset = xipRevLe64(i_src->iv_entryOffset);
o_dest->iv_linkAddress = xipRevLe64(i_src->iv_linkAddress);
for (i = 0; i < 5; i++) {
o_dest->iv_reserved64[i] = 0;
}
for (i = 0, destSection = o_dest->iv_section,
srcSection = i_src->iv_section;
i < SBE_XIP_SECTIONS;
i++, destSection++, srcSection++) {
xipTranslateSection(destSection, srcSection);
}
o_dest->iv_imageSize = xipRevLe32(i_src->iv_imageSize);
o_dest->iv_buildDate = xipRevLe32(i_src->iv_buildDate);
o_dest->iv_buildTime = xipRevLe32(i_src->iv_buildTime);
for (i = 0; i < 5; i++) {
o_dest->iv_reserved32[i] = 0;
}
o_dest->iv_headerVersion = i_src->iv_headerVersion;
o_dest->iv_normalized = i_src->iv_normalized;
o_dest->iv_tocSorted = i_src->iv_tocSorted;
for (i = 0; i < 3; i++) {
o_dest->iv_reserved8[i] = 0;
}
memcpy(o_dest->iv_buildUser, i_src->iv_buildUser,
sizeof(i_src->iv_buildUser));
memcpy(o_dest->iv_buildHost, i_src->iv_buildHost,
sizeof(i_src->iv_buildHost));
memcpy(o_dest->iv_reservedChar, i_src->iv_reservedChar,
sizeof(i_src->iv_reservedChar));
#else
if (o_dest != i_src) {
*o_dest = *i_src;
}
#endif /* _BIG_ENDIAN */
}
int
sbe_xip_map_toc(void* io_image,
int (*i_fn)(void* io_image,
const SbeXipItem* i_item,
void* io_arg),
void* io_arg)
{
int rc;
SbeXipToc *imageToc;
SbeXipItem item;
size_t entries;
do {
rc = xipQuickCheck(io_image, 0);
if (rc) break;
rc = xipGetToc(io_image, &imageToc, &entries, 0, 0);
if (rc) break;
for (; entries--; imageToc++) {
rc = xipDecodeToc(io_image, imageToc, &item);
if (rc) break;
rc = i_fn(io_image, &item, io_arg);
if (rc) break;
}
} while(0);
return rc;
}