diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/libpore/sbe_xip_image.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/libpore/sbe_xip_image.c')
-rw-r--r-- | roms/skiboot/libpore/sbe_xip_image.c | 2572 |
1 files changed, 2572 insertions, 0 deletions
diff --git a/roms/skiboot/libpore/sbe_xip_image.c b/roms/skiboot/libpore/sbe_xip_image.c new file mode 100644 index 000000000..2b05a0b90 --- /dev/null +++ b/roms/skiboot/libpore/sbe_xip_image.c @@ -0,0 +1,2572 @@ +/* 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 */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,2015 */ +/* [+] International Business Machines 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. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: sbe_xip_image.c,v 1.31 2015/07/29 23:40:06 cmolsen 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 +// *! *** *** +//----------------------------------------------------------------------------- +// *! 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, §ion); + 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 = NULL; + + 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 = NULL; + int rc; + + rc = xipGetSectionPointer(io_image, i_section, §ion); + 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 = NULL; + int rc; + + rc = xipGetSectionPointer(io_image, i_section, §ion); + 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, §ion); + 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, §ion); + 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, §ion); + 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 = NULL; + + 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, §ion); + 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, §ion); + 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, §ion); + 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; + uint32_t initialSize = 0; + + do { + restoreOnError = 0; + + rc = xipQuickCheck(io_image, 1); + if (rc) break; + + rc = sbe_xip_get_section(io_image, i_sectionId, §ion); + 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, §ion) != 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, §ion); + 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); + o_dest->iv_ptsVersion = xipRevLe64(i_src->iv_ptsVersion); + + for (i = 0; i < 4; 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; +} |