diff options
-rw-r--r-- | src/import/chips/p9/xip/p9_xip_image.c | 3079 | ||||
-rw-r--r-- | src/import/chips/p9/xip/p9_xip_image.h | 1745 |
2 files changed, 4824 insertions, 0 deletions
diff --git a/src/import/chips/p9/xip/p9_xip_image.c b/src/import/chips/p9/xip/p9_xip_image.c new file mode 100644 index 000000000..d96c8ae07 --- /dev/null +++ b/src/import/chips/p9/xip/p9_xip_image.c @@ -0,0 +1,3079 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/xip/p9_xip_image.c $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// \file p9_xip_image.c +/// \brief APIs for validating, normalizing, searching and manipulating +/// P9-XIP images. +/// +/// The background, APIs and implementation details are documented in the +/// document "P9-XIP Binary format" currently available at this link: +/// +/// - https://mcdoc.boeblingen.de.ibm.com/out/out.ViewDocument.php?documentid=2678 +/// +/// \bug The p9_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 "p9_xip_image.h" + + +//////////////////////////////////////////////////////////////////////////// +// Local Functions +//////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG_P9_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 P9_XIP_ERROR_STRINGS(p9_xip_error_strings); + +#define TRACE_ERROR(x) \ + ({ \ + fprintf(stderr, "%s:%d : Returning error code %d : %s" TRACE_NEWLINE, \ + __FILE__, __LINE__, (x), \ + P9_XIP_ERROR_STRING(p9_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 P9_XIP_TYPE_STRINGS(type_strings); + +XIP_STATIC void +dumpToc(int index, P9XipToc* 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), + P9_XIP_TYPE_STRING(type_strings, toc->iv_type), + toc->iv_section, + toc->iv_elements); +} + +#endif + +#if 0 + +XIP_STATIC void +dumpItem(P9XipItem* item) +{ + printf("P9XipItem @ %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, + P9_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; + P9XipSection section; + + printf("Section table dump of image @ %p\n" + " Entry Offset Size\n" + "-------------------------------\n", + i_image); + + for (i = 0; i < P9_XIP_SECTIONS; i++) + { + rc = p9_xip_get_section(i_image, i, §ion); + + if (rc) + { + printf(">>> dumpSectionTable got error at entry %d : %s\n", + i, P9_XIP_ERROR_STRING(p9_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(((P9XipHeader*)i_image)->iv_linkAddress); +} + + +/// What is the image size? + +XIP_STATIC uint32_t +xipImageSize(const void* i_image) +{ + return xipRevLe32(((P9XipHeader*)i_image)->iv_imageSize); +} + + +/// Set the image size + +XIP_STATIC void +xipSetImageSize(void* io_image, const size_t i_size) +{ + ((P9XipHeader*)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 % P9_XIP_FINAL_ALIGNMENT) != 0) + { + xipSetImageSize(io_image, + size + (P9_XIP_FINAL_ALIGNMENT - + (size % P9_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 IMAGE address to a host address + +XIP_STATIC void* +xipImage2Host(const void* i_image, const uint64_t i_imageAddress) +{ + return xipHostAddressFromOffset(i_image, + i_imageAddress - xipLinkAddress(i_image)); +} + + +XIP_STATIC int +xipValidateImageAddress(const void* i_image, + const uint64_t i_imageAddress, + const uint32_t size) +{ + int rc; + + if ((i_imageAddress < xipLinkAddress(i_image)) || + (i_imageAddress > (xipLinkAddress(i_image) + + xipImageSize(i_image) - + size))) + { + rc = TRACE_ERRORX(P9_XIP_INVALID_ARGUMENT, + "The IMAGE address " F0x012llx + " is outside the bounds " + "of the image (" + F0x012llx ":" F0x012llx + ") for %u-byte access.\n", + i_imageAddress, + 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(((P9XipHeader*)i_image)->iv_magic); +} + + +/// Get the header version from the image + +XIP_STATIC uint8_t +xipHeaderVersion(const void* i_image) +{ + return ((P9XipHeader*)i_image)->iv_headerVersion; +} + + +/// Has the image been normalized? + +XIP_STATIC uint8_t +xipNormalized(const void* i_image) +{ + return ((P9XipHeader*)i_image)->iv_normalized; +} + + +/// Has the image TOC been sorted? + +XIP_STATIC uint8_t +xipSorted(const void* i_image) +{ + return ((P9XipHeader*)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(P9_XIP_IMAGE_ERROR, + "Image pointer is NULL (0)\n"); + break; + } + + if ((xipMagic(i_image) >> 32) != P9_XIP_MAGIC) + { + rc = TRACE_ERRORX(P9_XIP_IMAGE_ERROR, + "Magic number mismatch; Found " + "" F0x016llx ", expected 0x%08x........\n", + xipMagic(i_image), P9_XIP_MAGIC); + break; + } + + if ((xipHeaderVersion(i_image)) != P9_XIP_HEADER_VERSION) + { + rc = TRACE_ERRORX(P9_XIP_IMAGE_ERROR, + "Header version mismatch; Expecting %d, " + "found %d\n", + P9_XIP_HEADER_VERSION, + xipHeaderVersion(i_image)); + break; + } + + if (i_normalizationRequired && !xipNormalized(i_image)) + { + rc = TRACE_ERRORX(P9_XIP_NOT_NORMALIZED, + "Image not normalized\n"); + break; + } + } + while(0); + + return rc; +} + + +/// Convert a 32-bit relocatable offset to a full IMAGE 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(P9XipSection* o_dest, const P9XipSection* i_src) +{ +#ifndef _BIG_ENDIAN + +#if P9_XIP_HEADER_VERSION != 8 +#error This code assumes the P9-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(P9XipToc* o_dest, P9XipToc* i_src) +{ +#ifndef _BIG_ENDIAN + +#if P9_XIP_HEADER_VERSION != 8 +#error This code assumes the P9-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; + P9XipHeader hostHeader; + + p9_xip_translate_header(&hostHeader, (P9XipHeader*)i_image); + + found = 0; + offset = 0; + *o_sectionId = 0; /* Make GCC -O3 happy */ + + for (i = 0; i < P9_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(P9_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, + P9XipSection** o_imageSection) +{ + int rc; + + if ((i_sectionId < 0) || (i_sectionId >= P9_XIP_SECTIONS)) + { + rc = TRACE_ERROR(P9_XIP_INVALID_ARGUMENT); + } + else + { + *o_imageSection = + &(((P9XipHeader*)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, + P9XipSection* i_hostSection) +{ + int rc; + P9XipSection* 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) +{ + P9XipSection* section; + 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) +{ + P9XipSection* section; + int rc; + + rc = xipGetSectionPointer(io_image, i_section, §ion); + + if (!rc) + { + section->iv_size = xipRevLe32(i_size); + } + + return rc; +} + + +/// Translate a IMAGE address in the image to a section and offset + +// We first check to be sure that the IMAGE 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 +xipImage2Section(const void* i_image, + const uint64_t i_imageAddress, + int* o_section, + uint32_t* o_offset) +{ + int rc, sectionId; + P9XipSection section; + uint32_t addressOffset; + + do + { + rc = 0; + + if ((i_imageAddress < xipLinkAddress(i_image)) || + (i_imageAddress > + (xipLinkAddress(i_image) + xipImageSize(i_image)))) + { + rc = TRACE_ERRORX(P9_XIP_INVALID_ARGUMENT, + "image2section: The i_imageAddress argument " + "(" F0x016llx ")\nis outside the bounds of the " + "image (" F0x016llx ":" F0x016llx ")\n", + i_imageAddress, + xipLinkAddress(i_image), + xipLinkAddress(i_image) + xipImageSize(i_image)); + break; + } + + addressOffset = (i_imageAddress - xipLinkAddress(i_image)) & 0xffffffff; + + for (sectionId = 0; sectionId < P9_XIP_SECTIONS; sectionId++) + { + rc = p9_xip_get_section(i_image, sectionId, §ion); + + if (rc) + { + rc = TRACE_ERROR(P9_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 == P9_XIP_SECTIONS) + { + rc = TRACE_ERRORX(P9_XIP_IMAGE_ERROR, + "Error processing IMAGE address " F0x016llx ". " + "The address is not mapped in any section.\n" + "A section table dump appears below\n", + i_imageAddress); + 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, + P9XipToc** o_toc, + size_t* o_entries, + int* o_sorted, + char** o_strings) +{ + int rc; + P9XipSection tocSection, stringsSection; + + do + { + rc = p9_xip_get_section(i_image, P9_XIP_SECTION_TOC, &tocSection); + + if (rc) + { + break; + } + + rc = p9_xip_get_section(i_image, P9_XIP_SECTION_STRINGS, + &stringsSection); + + if (rc) + { + break; + } + + if (o_toc) + { + *o_toc = (P9XipToc*)((uint8_t*)i_image + tocSection.iv_offset); + } + + if (o_entries) + { + *o_entries = tocSection.iv_size / sizeof(P9XipToc); + } + + 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 P9XipToc* i_a, const P9XipToc* 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(P9XipToc* io_toc, int i_left, int i_right, + const char* i_strings) +{ + int i, j, left, right, sp; + P9XipToc 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, P9XipToc** o_entry) +{ + int rc; + P9XipToc* 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(P9_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, P9XipToc** o_entry) +{ + int rc; + P9XipToc* 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(P9_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(P9_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 P9XipItem* i_item, void* io_arg) +{ + int rc; + P9XipItem found; + + do + { + rc = p9_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(P9_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 deliberatly 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 + +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, P9XipToc* io_imageToc, + P9XipHashedToc** io_fixedTocEntry, + size_t* io_fixedEntriesRemaining) +{ + P9XipToc 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*)xipImage2Host(io_image, + xipFullAddress(io_image, hostToc.iv_id)); + + rc = xipImage2Section(io_image, + xipFullAddress(io_image, hostToc.iv_id), + &idSection, + &idOffset); + + if (rc) + { + break; + } + + if (idSection != P9_XIP_SECTION_STRINGS) + { + rc = TRACE_ERROR(P9_XIP_IMAGE_ERROR); + break; + } + + rc = xipImage2Section(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 == P9_XIP_SECTION_FIXED) + { + + if (*io_fixedEntriesRemaining == 0) + { + rc = TRACE_ERRORX(P9_XIP_TOC_ERROR, + "Too many TOC entries for .fixed\n"); + break; + } + + if (hostToc.iv_data != (uint16_t)hostToc.iv_data) + { + rc = TRACE_ERRORX(P9_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(P9XipHashedToc* 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(P9_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 P9XipItem +/// structure + +XIP_STATIC int +xipDecodeToc(void* i_image, + P9XipToc* i_imageToc, + P9XipItem* o_item) +{ + int rc; + P9XipToc hostToc; + P9XipSection dataSection, stringsSection; + + do + { + if (!xipNormalized(i_image)) + { + rc = TRACE_ERROR(P9_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; + + p9_xip_get_section(i_image, P9_XIP_SECTION_STRINGS, &stringsSection); + 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 = p9_xip_get_section(i_image, hostToc.iv_section, &dataSection); + + if (rc) + { + break; + } + + if (dataSection.iv_size == 0) + { + rc = TRACE_ERROR(P9_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; + P9XipToc* 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); + + ((P9XipHeader*)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(P9_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(P9_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, + P9XipHashedToc** o_imageToc, + size_t* o_entries) +{ + int rc; + P9XipSection section; + + rc = p9_xip_get_section(io_image, P9_XIP_SECTION_FIXED_TOC, §ion); + + if (!rc) + { + + *o_imageToc = + (P9XipHashedToc*)((unsigned long)io_image + section.iv_offset); + + *o_entries = section.iv_size / sizeof(P9XipHashedToc); + } + + 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, P9XipItem* o_item) +{ + int rc; + P9XipHashedToc* toc; + size_t entries; + uint32_t hash; + P9XipSection 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 = P9_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 P9XipItem 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 = p9_xip_get_section(i_image, P9_XIP_SECTION_FIXED, &fixedSection); + + if (rc) + { + break; + } + + if (fixedSection.iv_size == 0) + { + rc = TRACE_ERROR(P9_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, P9XipItem* o_item) +{ + int rc; + unsigned i; + uint32_t offset; + P9XipSection headerSection; + +#define HEADER_TOC(id, field, type) \ + {#id, offsetof(P9XipHeader, field), type} + + struct HeaderToc + { + + const char* iv_id; + uint16_t iv_offset; + uint8_t iv_type; + + } toc[] = + { + + HEADER_TOC(magic, iv_magic, P9_XIP_UINT64), + HEADER_TOC(entry_offset, iv_entryOffset, P9_XIP_UINT64), + HEADER_TOC(link_address, iv_linkAddress, P9_XIP_UINT64), + + HEADER_TOC(image_size, iv_imageSize, P9_XIP_UINT32), + HEADER_TOC(build_date, iv_buildDate, P9_XIP_UINT32), + HEADER_TOC(build_time, iv_buildTime, P9_XIP_UINT32), + + HEADER_TOC(header_version, iv_headerVersion, P9_XIP_UINT8), + HEADER_TOC(toc_normalized, iv_normalized, P9_XIP_UINT8), + HEADER_TOC(toc_sorted, iv_tocSorted, P9_XIP_UINT8), + + HEADER_TOC(build_user, iv_buildUser, P9_XIP_STRING), + HEADER_TOC(build_host, iv_buildHost, P9_XIP_STRING), + + }; + + do + { + + rc = P9_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 P9XipItem 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 = p9_xip_get_section(i_image, P9_XIP_SECTION_HEADER, + &headerSection); + + if (rc) + { + break; + } + + if (headerSection.iv_size == 0) + { + rc = TRACE_ERROR(P9_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 +p9_xip_validate(void* i_image, const uint32_t i_size) +{ + P9XipHeader hostHeader; + int rc = 0, i; + uint32_t linkAddress, imageSize, extent, offset, size; + uint8_t alignment; + + p9_xip_translate_header(&hostHeader, (P9XipHeader*)i_image); + + do + { + + // Validate C/Assembler constraints. + + if (sizeof(P9XipSection) != SIZE_OF_P9_XIP_SECTION) + { + rc = TRACE_ERRORX(P9_XIP_BUG, + "C/Assembler size mismatch(%d/%d) " + "for P9XipSection\n", + sizeof(P9XipSection), SIZE_OF_P9_XIP_SECTION); + break; + } + + if (sizeof(P9XipToc) != SIZE_OF_P9_XIP_TOC) + { + rc = TRACE_ERRORX(P9_XIP_BUG, + "C/Assembler size mismatch(%d/%d) " + "for P9XipToc\n", + sizeof(P9XipToc), SIZE_OF_P9_XIP_TOC); + break; + } + + if (sizeof(P9XipHashedToc) != SIZE_OF_P9_XIP_HASHED_TOC) + { + rc = TRACE_ERRORX(P9_XIP_BUG, + "C/Assembler size mismatch(%d/%d) " + "for P9XipHashedToc\n", + sizeof(P9XipHashedToc), + SIZE_OF_P9_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(P9XipHeader)) + { + rc = TRACE_ERRORX(P9_XIP_IMAGE_ERROR, + "p9_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(P9_XIP_IMAGE_ERROR, + "p9_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(P9_XIP_IMAGE_ERROR, + "p9_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 % P9_XIP_FINAL_ALIGNMENT) != 0) + { + rc = TRACE_ERRORX(P9_XIP_ALIGNMENT_ERROR, + "p9_xip_validate(%p, %u) : " + "The image size (%u) is not a multiple of %u\n", + i_image, i_size, imageSize, + P9_XIP_FINAL_ALIGNMENT); + break; + } + + // Validate that all sections appear to be within the image + // bounds, and are aligned correctly. + + for (i = 0; i < P9_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(P9_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(P9_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[P9_XIP_SECTION_TOC].iv_size; + + if (size != 0) + { + if (xipNormalized(i_image)) + { + rc = p9_xip_map_toc(i_image, xipValidateTocEntry, 0); + + if (rc) + { + break; + } + } + } + } + while (0); + + return rc; +} + + +int +p9_xip_validate2(void* i_image, const uint32_t i_size, const uint32_t i_maskIgnores) +{ + P9XipHeader hostHeader; + int rc = 0, i; + uint32_t linkAddress, imageSize, extent, offset, size; + uint8_t alignment; + + p9_xip_translate_header(&hostHeader, (P9XipHeader*)i_image); + + do + { + + // Validate C/Assembler constraints. + + if (sizeof(P9XipSection) != SIZE_OF_P9_XIP_SECTION) + { + rc = TRACE_ERRORX(P9_XIP_BUG, + "C/Assembler size mismatch(%d/%d) " + "for P9XipSection\n", + sizeof(P9XipSection), SIZE_OF_P9_XIP_SECTION); + break; + } + + if (sizeof(P9XipToc) != SIZE_OF_P9_XIP_TOC) + { + rc = TRACE_ERRORX(P9_XIP_BUG, + "C/Assembler size mismatch(%d/%d) " + "for P9XipToc\n", + sizeof(P9XipToc), SIZE_OF_P9_XIP_TOC); + break; + } + + if (sizeof(P9XipHashedToc) != SIZE_OF_P9_XIP_HASHED_TOC) + { + rc = TRACE_ERRORX(P9_XIP_BUG, + "C/Assembler size mismatch(%d/%d) " + "for P9XipHashedToc\n", + sizeof(P9XipHashedToc), + SIZE_OF_P9_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(P9XipHeader)) + { + rc = TRACE_ERRORX(P9_XIP_IMAGE_ERROR, + "p9_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 & P9_XIP_IGNORE_FILE_SIZE)) + { + rc = TRACE_ERRORX(P9_XIP_IMAGE_ERROR, + "p9_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(P9_XIP_IMAGE_ERROR, + "p9_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 % P9_XIP_FINAL_ALIGNMENT) != 0) + { + rc = TRACE_ERRORX(P9_XIP_ALIGNMENT_ERROR, + "p9_xip_validate2(%p, %u) : " + "The image size (%u) is not a multiple of %u\n", + i_image, i_size, imageSize, + P9_XIP_FINAL_ALIGNMENT); + break; + } + + // Validate that all sections appear to be within the image + // bounds, and are aligned correctly. + + for (i = 0; i < P9_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(P9_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(P9_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[P9_XIP_SECTION_TOC].iv_size; + + if (size != 0) + { + if (xipNormalized(i_image)) + { + rc = p9_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 +p9_xip_normalize(void* io_image) +{ + int rc, i; + P9XipSection section; + P9XipToc* imageToc; + P9XipHashedToc* fixedImageToc; + P9XipHashedToc* fixedTocEntry; + size_t tocEntries, fixedTocEntries, fixedEntriesRemaining; + + 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(P9_XIP_TOC_ERROR, + "Not enough TOC entries for .fixed"); + break; + } + + rc = xipHashCollision(fixedImageToc, fixedTocEntries); + + if (rc) + { + break; + } + + ((P9XipHeader*)io_image)->iv_normalized = 1; + } + + rc = xipSortToc(io_image); + + if (rc) + { + break; + } + + for (i = 0; i < P9_XIP_SECTIONS; i++) + { + rc = p9_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); + + ((P9XipHeader*)io_image)->iv_normalized = (rc == 0); + + return rc; +} + + +int +p9_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 +p9_xip_get_section(const void* i_image, + const int i_sectionId, + P9XipSection* o_hostSection) +{ + int rc; + P9XipSection* 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 +p9_xip_find(void* i_image, + const char* i_id, + P9XipItem* o_item) +{ + int rc; + P9XipToc* toc; + P9XipItem item, *pitem; + P9XipSection* tocSection; + + do + { + rc = xipQuickCheck(i_image, 1); + + if (rc) + { + break; + } + + rc = xipGetSectionPointer(i_image, P9_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 +p9_xip_get_scalar(void* i_image, const char* i_id, uint64_t* o_data) +{ + int rc; + P9XipItem item; + + rc = p9_xip_find(i_image, i_id, &item); + + if (!rc) + { + switch (item.iv_type) + { + case P9_XIP_UINT8: + *o_data = *((uint8_t*)(item.iv_imageData)); + break; + + case P9_XIP_UINT16: + *o_data = xipRevLe16(*((uint16_t*)(item.iv_imageData))); + break; + + case P9_XIP_UINT32: + *o_data = xipRevLe32(*((uint32_t*)(item.iv_imageData))); + break; + + case P9_XIP_UINT64: + *o_data = xipRevLe64(*((uint64_t*)(item.iv_imageData))); + break; + + case P9_XIP_INT8: + *o_data = *((int8_t*)(item.iv_imageData)); + break; + + case P9_XIP_INT16: + *o_data = xipRevLe16(*((int16_t*)(item.iv_imageData))); + break; + + case P9_XIP_INT32: + *o_data = xipRevLe32(*((int32_t*)(item.iv_imageData))); + break; + + case P9_XIP_INT64: + *o_data = xipRevLe64(*((int64_t*)(item.iv_imageData))); + break; + + case P9_XIP_ADDRESS: + *o_data = item.iv_address; + break; + + default: + rc = TRACE_ERROR(P9_XIP_TYPE_ERROR); + break; + } + } + + return rc; +} + + +int +p9_xip_get_element(void* i_image, + const char* i_id, + const uint32_t i_index, + uint64_t* o_data) +{ + int rc; + P9XipItem item; + + do + { + rc = p9_xip_find(i_image, i_id, &item); + + if (rc) + { + break; + } + + if ((item.iv_elements != 0) && (i_index >= item.iv_elements)) + { + rc = TRACE_ERROR(P9_XIP_BOUNDS_ERROR); + break; + } + + switch (item.iv_type) + { + case P9_XIP_UINT8: + *o_data = ((uint8_t*)(item.iv_imageData))[i_index]; + break; + + case P9_XIP_UINT16: + *o_data = xipRevLe16(((uint16_t*)(item.iv_imageData))[i_index]); + break; + + case P9_XIP_UINT32: + *o_data = xipRevLe32(((uint32_t*)(item.iv_imageData))[i_index]); + break; + + case P9_XIP_UINT64: + *o_data = xipRevLe64(((uint64_t*)(item.iv_imageData))[i_index]); + break; + + case P9_XIP_INT8: + *o_data = ((int8_t*)(item.iv_imageData))[i_index]; + break; + + case P9_XIP_INT16: + *o_data = xipRevLe16(((int16_t*)(item.iv_imageData))[i_index]); + break; + + case P9_XIP_INT32: + *o_data = xipRevLe32(((int32_t*)(item.iv_imageData))[i_index]); + break; + + case P9_XIP_INT64: + *o_data = xipRevLe64(((int64_t*)(item.iv_imageData))[i_index]); + break; + + default: + rc = TRACE_ERROR(P9_XIP_TYPE_ERROR); + break; + } + + if (rc) + { + break; + } + + } + while (0); + + return rc; +} + + +int +p9_xip_get_string(void* i_image, const char* i_id, char** o_data) +{ + int rc; + P9XipItem item; + + rc = p9_xip_find(i_image, i_id, &item); + + if (!rc) + { + switch (item.iv_type) + { + case P9_XIP_STRING: + *o_data = (char*)(item.iv_imageData); + break; + + default: + rc = TRACE_ERROR(P9_XIP_TYPE_ERROR); + break; + } + } + + return rc; +} + + +int +p9_xip_read_uint64(const void* i_image, + const uint64_t i_imageAddress, + uint64_t* o_data) +{ + int rc; + + do + { + rc = xipQuickCheck(i_image, 0); + + if (rc) + { + break; + } + + rc = xipValidateImageAddress(i_image, i_imageAddress, 8); + + if (rc) + { + break; + } + + if (i_imageAddress % 8) + { + rc = TRACE_ERROR(P9_XIP_ALIGNMENT_ERROR); + break; + } + + *o_data = + xipRevLe64(*((uint64_t*)xipImage2Host(i_image, i_imageAddress))); + + } + while(0); + + return rc; +} + + +int +p9_xip_set_scalar(void* io_image, const char* i_id, const uint64_t i_data) +{ + int rc; + P9XipItem item; + + rc = p9_xip_find(io_image, i_id, &item); + + if (!rc) + { + switch(item.iv_type) + { + case P9_XIP_UINT8: + *((uint8_t*)(item.iv_imageData)) = (uint8_t)i_data; + break; + + case P9_XIP_UINT16: + *((uint16_t*)(item.iv_imageData)) = xipRevLe16((uint16_t)i_data); + break; + + case P9_XIP_UINT32: + *((uint32_t*)(item.iv_imageData)) = xipRevLe32((uint32_t)i_data); + break; + + case P9_XIP_UINT64: + *((uint64_t*)(item.iv_imageData)) = xipRevLe64((uint64_t)i_data); + break; + + case P9_XIP_INT8: + *((int8_t*)(item.iv_imageData)) = (int8_t)i_data; + break; + + case P9_XIP_INT16: + *((int16_t*)(item.iv_imageData)) = xipRevLe16((int16_t)i_data); + break; + + case P9_XIP_INT32: + *((int32_t*)(item.iv_imageData)) = xipRevLe32((int32_t)i_data); + break; + + case P9_XIP_INT64: + *((int64_t*)(item.iv_imageData)) = xipRevLe64((int64_t)i_data); + break; + + default: + rc = TRACE_ERROR(P9_XIP_TYPE_ERROR); + break; + } + } + + return rc; +} + + +int +p9_xip_set_element(void* i_image, + const char* i_id, + const uint32_t i_index, + const uint64_t i_data) +{ + int rc; + P9XipItem item; + + do + { + rc = p9_xip_find(i_image, i_id, &item); + + if (rc) + { + break; + } + + if ((item.iv_elements != 0) && (i_index >= item.iv_elements)) + { + rc = TRACE_ERROR(P9_XIP_BOUNDS_ERROR); + break; + } + + switch (item.iv_type) + { + case P9_XIP_UINT8: + ((uint8_t*)(item.iv_imageData))[i_index] = (uint8_t)i_data; + break; + + case P9_XIP_UINT16: + ((uint16_t*)(item.iv_imageData))[i_index] = + xipRevLe16((uint16_t)i_data); + break; + + case P9_XIP_UINT32: + ((uint32_t*)(item.iv_imageData))[i_index] = + xipRevLe32((uint32_t)i_data); + break; + + case P9_XIP_UINT64: + ((uint64_t*)(item.iv_imageData))[i_index] = + xipRevLe64((uint64_t)i_data); + break; + + case P9_XIP_INT8: + ((int8_t*)(item.iv_imageData))[i_index] = (int8_t)i_data; + break; + + case P9_XIP_INT16: + ((int16_t*)(item.iv_imageData))[i_index] = + xipRevLe16((uint16_t)i_data); + break; + + case P9_XIP_INT32: + ((int32_t*)(item.iv_imageData))[i_index] = + xipRevLe32((uint32_t)i_data); + break; + + case P9_XIP_INT64: + ((int64_t*)(item.iv_imageData))[i_index] = + xipRevLe64((uint64_t)i_data); + break; + + default: + rc = TRACE_ERROR(P9_XIP_TYPE_ERROR); + break; + } + + if (rc) + { + break; + } + + } + while (0); + + return rc; +} + + +int +p9_xip_set_string(void* i_image, const char* i_id, const char* i_data) +{ + int rc; + P9XipItem item; + char* dest; + + rc = p9_xip_find(i_image, i_id, &item); + + if (!rc) + { + switch (item.iv_type) + { + case P9_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(P9_XIP_TYPE_ERROR); + break; + } + } + + return rc; +} + + +int +p9_xip_write_uint64(void* io_image, + const uint64_t i_imageAddress, + const uint64_t i_data) +{ + int rc; + + do + { + rc = xipQuickCheck(io_image, 0); + + if (rc) + { + break; + } + + rc = xipValidateImageAddress(io_image, i_imageAddress, 8); + + if (rc) + { + break; + } + + if (i_imageAddress % 8) + { + rc = TRACE_ERROR(P9_XIP_ALIGNMENT_ERROR); + break; + } + + *((uint64_t*)xipImage2Host(io_image, i_imageAddress)) = + xipRevLe64(i_data); + + } + while(0); + + return rc; +} + + +int +p9_xip_delete_section(void* io_image, const int i_sectionId) +{ + int rc, final; + P9XipSection section; + + do + { + rc = xipQuickCheck(io_image, 1); + + if (rc) + { + break; + } + + rc = p9_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(P9_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 = p9_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 +p9_xip_duplicate_section(const void* i_image, + const int i_sectionId, + void** o_duplicate, + uint32_t* o_size) +{ + P9XipSection section; + int rc; + + *o_duplicate = 0; + + do + { + rc = xipQuickCheck(i_image, 0); + + if (rc) + { + break; + } + + rc = p9_xip_get_section(i_image, i_sectionId, §ion); + + if (rc) + { + break; + } + + if (section.iv_size == 0) + { + rc = TRACE_ERRORX(P9_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(P9_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 +p9_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) +{ + P9XipSection section, initialSection; + int rc, final, restoreOnError; + void* hostAddress; + uint32_t pad, initialSize; + + do + { + restoreOnError = 0; + + rc = xipQuickCheck(io_image, 1); + + if (rc) + { + break; + } + + rc = p9_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(P9_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(P9_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(P9_XIP_WOULD_OVERFLOW); + break; + } + + section.iv_size += i_size; + + if (xipPutSection(io_image, i_sectionId, §ion) != 0) + { + rc = TRACE_ERROR(P9_XIP_BUG); /* Can't happen */ + break; + } + + + // Special case + + if (i_sectionId == P9_XIP_SECTION_TOC) + { + ((P9XipHeader*)io_image)->iv_tocSorted = 0; + } + + } + while (0); + + if (rc && restoreOnError) + { + if (xipPutSection(io_image, i_sectionId, &initialSection) != 0) + { + rc = TRACE_ERROR(P9_XIP_BUG); /* Can't happen */ + } + + xipSetImageSize(io_image, initialSize); + } + + return rc; +} + + +int +p9_xip_section2image(const void* i_image, + const int i_sectionId, + const uint32_t i_offset, + uint64_t* o_imageAddress) +{ + int rc; + P9XipSection section; + + do + { + rc = xipQuickCheck(i_image, 0); + + if (rc) + { + break; + } + + rc = p9_xip_get_section(i_image, i_sectionId, §ion); + + if (rc) + { + break; + } + + if (section.iv_size == 0) + { + rc = TRACE_ERROR(P9_XIP_SECTION_ERROR); + break; + } + + if (i_offset > (section.iv_offset + section.iv_size)) + { + rc = TRACE_ERROR(P9_XIP_INVALID_ARGUMENT); + break; + } + + *o_imageAddress = xipLinkAddress(i_image) + section.iv_offset + i_offset; + + if (*o_imageAddress % 4) + { + rc = TRACE_ERROR(P9_XIP_ALIGNMENT_ERROR); + break; + } + + } + while(0); + + return rc; +} + + +int +p9_xip_image2section(const void* i_image, + const uint64_t i_imageAddress, + int* i_section, + uint32_t* i_offset) +{ + int rc; + + do + { + rc = xipQuickCheck(i_image, 0); + + if (rc) + { + break; + } + + rc = xipImage2Section(i_image, i_imageAddress, i_section, i_offset); + + } + while(0); + + return rc; +} + + +int +p9_xip_image2host(const void* i_image, + const uint64_t i_imageAddress, + void** o_hostAddress) +{ + int rc; + + do + { + rc = xipQuickCheck(i_image, 0); + + if (rc) + { + break; + } + + if ((i_imageAddress < xipLinkAddress(i_image)) || + (i_imageAddress > + (xipLinkAddress(i_image) + xipImageSize(i_image)))) + { + rc = TRACE_ERROR(P9_XIP_INVALID_ARGUMENT); + break; + } + + *o_hostAddress = + xipHostAddressFromOffset(i_image, + i_imageAddress - xipLinkAddress(i_image)); + } + while(0); + + return rc; +} + + +int +p9_xip_host2image(const void* i_image, + void* i_hostAddress, + uint64_t* o_imageAddress) +{ + 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(P9_XIP_INVALID_ARGUMENT); + break; + } + + *o_imageAddress = xipLinkAddress(i_image) + + ((unsigned long)i_hostAddress - (unsigned long)i_image); + + if (*o_imageAddress % 4) + { + rc = TRACE_ERROR(P9_XIP_ALIGNMENT_ERROR); + break; + } + } + while(0); + + return rc; +} + + +void +p9_xip_translate_header(P9XipHeader* o_dest, const P9XipHeader* i_src) +{ +#ifndef _BIG_ENDIAN + int i; + P9XipSection* destSection; + const P9XipSection* srcSection; + +#if P9_XIP_HEADER_VERSION != 8 +#error This code assumes the P9-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 < P9_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 +p9_xip_map_toc(void* io_image, + int (*i_fn)(void* io_image, + const P9XipItem* i_item, + void* io_arg), + void* io_arg) +{ + int rc; + P9XipToc* imageToc; + P9XipItem 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; +} diff --git a/src/import/chips/p9/xip/p9_xip_image.h b/src/import/chips/p9/xip/p9_xip_image.h new file mode 100644 index 000000000..1d65b220a --- /dev/null +++ b/src/import/chips/p9/xip/p9_xip_image.h @@ -0,0 +1,1745 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/xip/p9_xip_image.h $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// \file p9_xip_image.h +/// \brief definition of structs in sections +/// +/// Contains struct ProcSbeFixed which contains functions, rings and +/// attributes whose pointers are stored in the fixed and fixed_toc section +/// Everything related to creating and manipulating P9-XIP binary images + +#ifndef __P9_XIP_IMAGE_H +#define __P9_XIP_IMAGE_H + +#include "fapi_sbe_common.H" + +/// Current version (fields, layout, sections) of the P9_XIP header +/// +/// If any changes are made to this file or to p9_xip_header.H, please update +/// the header version and follow-up on all of the error messages. + +#define P9_XIP_HEADER_VERSION 8 + +/// \defgroup p9_xip_magic_numbers P9-XIP magic numbers +/// +/// An P9-XIP magic number is a 64-bit constant. The 4 high-order bytes +/// contain the ASCII characters "XIP " and identify the image as a P9-XIP +/// image, while the 4 low-order bytes identify the type of the image. +/// +/// @{ + +#define P9_XIP_MAGIC 0x58495020 // "XIP " +#define P9_BASE_MAGIC ULL(0x5849502042415345) // "XIP BASE" +#define P9_SEEPROM_MAGIC ULL(0x584950205345504d) // "XIP SEPM" +#define P9_CENTAUR_MAGIC ULL(0x58495020434e5452) // "XIP CNTR" + +/// @} + + +/// \defgroup p9_xip_sections P9-XIP Image Section Indexes +/// +/// These constants define the order that the P9XipSection structures appear +/// in the header, which is not necessarily the order the sections appear in +/// the binary image. Given that P9-XIP image contents are tightly +/// controlled, we use this simple indexing scheme for the allowed sections +/// rather than a more general approach, e.g., allowing arbitrary sections +/// identified by their names. +/// +/// @{ + +// -*- DO NOT REORDER OR EDIT THIS SET OF CONSTANTS WITHOUT ALSO EDITING -*- +// -*- THE ASSEMBLER LAYOUT IN p9_xip_header.H. -*- + +#define P9_XIP_SECTION_HEADER 0 +#define P9_XIP_SECTION_FIXED 1 +#define P9_XIP_SECTION_FIXED_TOC 2 +#define P9_XIP_SECTION_LOADER_TEXT 3 +#define P9_XIP_SECTION_LOADER_DATA 4 +#define P9_XIP_SECTION_TEXT 5 +#define P9_XIP_SECTION_DATA 6 +#define P9_XIP_SECTION_TOC 7 +#define P9_XIP_SECTION_STRINGS 8 +#define P9_XIP_SECTION_BASE 9 +#define P9_XIP_SECTION_BASELOADER 10 +#define P9_XIP_SECTION_OVERLAYS 11 +#define P9_XIP_SECTION_RINGS 12 +#define P9_XIP_SECTION_HBBL 13 + +#define P9_XIP_SECTIONS 14 + +/// @} + + +/// \defgroup p9_xip_validate() ignore masks. +/// +/// These defines, when matched in p9_xip_validate(), cause the validation +/// to skip the check of the corresponding property. The purpose is to more +/// effectively debug images that may be damaged and which have excess info +/// before or after the image. The latter will be the case when dumping the +/// image as a memory block without knowing where the image starts and ends. +/// +/// @{ + +#define P9_XIP_IGNORE_FILE_SIZE (uint32_t)0x00000001 +#define P9_XIP_IGNORE_ALL (uint32_t)0x80000000 + +/// @} + + +#ifndef __ASSEMBLER__ + +/// Applications can expand this macro to create an array of section names. +#define P9_XIP_SECTION_NAMES(var) \ + const char* var[] = { \ + ".header", \ + ".fixed", \ + ".fixed_toc", \ + ".loader_text", \ + ".loader_data", \ + ".text", \ + ".data", \ + ".toc", \ + ".strings", \ + ".base", \ + ".baseloader", \ + ".overlays", \ + ".rings", \ + ".hbbl", \ + } + +/// Applications can use this macro to safely index the array of section +/// names. +#define P9_XIP_SECTION_NAME(var, n) \ + ((((n) < 0) || ((n) > (int)(sizeof(var) / sizeof(char*)))) ? \ + "Bug : Invalid P9-XIP section name" : var[n]) + + +#endif /* __ASSEMBLER__ */ + + +/// Maximum section alignment for P9-XIP sections +#define P9_XIP_MAX_SECTION_ALIGNMENT 128 + +/// \defgroup p9_xip_toc_types P9-XIP Table of Contents data types +/// +/// These are the data types stored in the \a iv_type field of the P9XipToc +/// objects. These must be defined as manifest constants because they are +/// required to be recognized as manifest constants in C (as opposed to C++) +/// code. +/// +/// NB: The 0x0 code is purposefully left undefined to catch bugs. +/// +/// @{ + +/// Data is a single unsigned byte +#define P9_XIP_UINT8 0x01 + +/// Data is a 16-bit unsigned integer +#define P9_XIP_UINT16 0x02 + +/// Data is a 32-bit unsigned integer +#define P9_XIP_UINT32 0x03 + +/// Data is a 64-bit unsigned integer +#define P9_XIP_UINT64 0x04 + +/// Data is a single signed byte +#define P9_XIP_INT8 0x05 + +/// Data is a 16-bit signed integer +#define P9_XIP_INT16 0x06 + +/// Data is a 32-bit signed integer +#define P9_XIP_INT32 0x07 + +/// Data is a 64-bit signed integer +#define P9_XIP_INT64 0x08 + +/// Data is a 0-byte terminated ASCII string +#define P9_XIP_STRING 0x09 + +/// Data is an address +#define P9_XIP_ADDRESS 0x0A + +/// The maximum type number +#define P9_XIP_MAX_TYPE_INDEX 0x0A + +/// Applications can expand this macro to get access to string forms of the +/// P9-XIP data types if desired. +#define P9_XIP_TYPE_STRINGS(var) \ + const char* var[] = { \ + "Illegal 0 Code", \ + "P9_XIP_UINT8", \ + "P9_XIP_UINT16", \ + "P9_XIP_UINT32", \ + "P9_XIP_UINT64", \ + "P9_XIP_INT8", \ + "P9_XIP_INT16", \ + "P9_XIP_INT32", \ + "P9_XIP_INT64", \ + "P9_XIP_STRING", \ + "P9_XIP_ADDRESS", \ + } + +/// Applications can expand this macro to get access to abbreviated string +/// forms of the P9-XIP data types if desired. +#define P9_XIP_TYPE_ABBREVS(var) \ + const char* var[] = { \ + "Illegal 0 Code", \ + "u8 ", \ + "u16", \ + "u32", \ + "u64", \ + "i8 ", \ + "i16", \ + "i32", \ + "i64", \ + "str", \ + "adr", \ + } + +/// Applications can use this macro to safely index either array of P9-XIP +/// type strings. +#define P9_XIP_TYPE_STRING(var, n) \ + (((n) > (sizeof(var) / sizeof(char*))) ? \ + "Invalid P9-XIP type specification" : var[n]) + +/// @} + + +/// Final alignment constraint for P9-XIP images. +/// +/// images are required to be multiples of 8 bytes in length, to +/// gaurantee that the something will be able to complete any 8-byte load/store. +#define P9_XIP_FINAL_ALIGNMENT 8 + + +//////////////////////////////////////////////////////////////////////////// +// C Definitions +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* So __cplusplus doesn't mess w/auto-indent */ +#endif + +/// P9-XIP Section information +/// +/// This structure defines the data layout of section table entries in the +/// P9-XIP image header. + +// -*- DO NOT REORDER OR EDIT THIS STRUCTURE DEFINITION WITHOUT ALSO -*- +// -*- EDITING THE ASSEMBLER LAYOUT IN p9_xip_header.H -*- + +typedef struct +{ + + /// The offset (in bytes) of the section from the beginning of the image + /// + /// In normalized images the section offset will always be 0 if the + /// section size is also 0. + uint32_t iv_offset; + + /// The size of the section in bytes, exclusive of alignment padding + /// + /// This is the size of the program-significant data in the section, + /// exclusive of any alignment padding or reserved or extra space. The + /// alignment padding (reserved space) is not represented explicitly, but + /// is only implied by the offset of any subsequent non-empty section, or + /// in the case of the final section in the image, the image size. + /// + /// Regardless of the \a iv_offset, if the \a iv_size of a section is 0 it + /// should be considered "not present" in the image. In normalized images + /// the section offset will always be 0 if the section size is also 0. + uint32_t iv_size; + + /// The required initial alignment for the section offset + /// + /// The image and the applications using P9-XIP images have strict + /// alignment/padding requirements. The image does not handle any type of + /// unaligned instruction or data fetches. Some sections and subsections + /// must also be POWER cache-line aligned. The \a iv_alignment applies to + /// the first byte of the section. image images are also required to be + /// multiples of 8 bytes in length, to gaurantee that the something will be + /// able to complete any 8-byte load/store. These constraints are checked + /// by p9_xip_validate() and enforced by p9_xip_append(). The alignment + /// constraints may force a section to be padded, which may create "holes" + /// in the image as explained in the comments for the \a iv_size field. + /// + /// Note that alignment constraints are always checked relative to the + /// first byte of the image for in-memory images, not relative to the host + /// address. Alignment specifications are required to be a power-of-2. + uint8_t iv_alignment; + + /// Reserved structure alignment padding; Pad to 12 bytes + uint8_t iv_reserved8[3]; + +} P9XipSection; + +/// The P9XipSection structure is created by assembler code and is expected +/// to have the same size in C code. This constraint is checked in +/// p9_xip_validate(). +#define SIZE_OF_P9_XIP_SECTION 12 + + +/// P9-XIP binary image header +/// +/// This header occupies the initial bytes of a P9-XIP binary image. +/// The header contents are documented here, however the structure is actually +/// defined in the file p9_xip_header.S, and these two definitions must be +/// kept consistent. +/// +/// The header is a fixed-format representation of the most critical +/// information about the image. The large majority of information about the +/// image and its contents are available through the searchable table of +/// contents. image code itself normally accesses the data directly through +/// global symbols. +/// +/// The header only contains information 1) required by OTPROM code (e.g., the +/// entry point); 2) required by search and updating APIs (e.g., the +/// locations and sizes of all of the sections.); a few pieces of critical +/// meta-data (e.g., information about the image build process). +/// +/// Any entries that are accessed by image code are required to be 64 bits, and +/// will appear at the beginning of the header. +/// +/// The header also contains bytewise offsets and sizes of all of the sections +/// that are assembled to complete the image. The offsets are relative to the +/// start of the image (where the header is loaded). The sizes include any +/// padding inserted by the link editor to guarantee section alignment. +/// +/// Every field of the header is also accesssible through the searchable table +/// of contents as documented in p9_xip_header.S. + +// -*- DO NOT REORDER OR EDIT THIS STRUCTURE DEFINITION WITHOUT ALSO -*- +// -*- EDITING THE ASSEMBLER LAYOUT IN p9_xip_header.S, AND WITHOUT -*- +// -*- UPDATING THE p9_xip_translate_header() API IN p9_xip_image.c. -*- + +typedef struct +{ + + ////////////////////////////////////////////////////////////////////// + // Identification - 8-byte aligned; 8 entries + ////////////////////////////////////////////////////////////////////// + + /// Contains P9_XIP_MAGIC to identify a P9-XIP image + uint64_t iv_magic; + + /// The offset of the P9-XIP entry point from the start of the image + uint64_t iv_entryOffset; + + /// The base address used to link the image, as a full relocatable image + /// address + uint64_t iv_linkAddress; + + /// The entry address of base loader + uint64_t iv_entryAddressSBE; + + /// Reserved for future expansion + uint64_t iv_reserved64[4]; + + ////////////////////////////////////////////////////////////////////// + // Section Table - 4-byte aligned; 16 entries + ////////////////////////////////////////////////////////////////////// + + P9XipSection iv_section[P9_XIP_SECTIONS]; + + ////////////////////////////////////////////////////////////////////// + // Other information - 4-byte aligned; 8 entries + ////////////////////////////////////////////////////////////////////// + + /// The size of the image (including padding) in bytes + uint32_t iv_imageSize; + + /// Build date generated by `date +%Y%m%d`, e.g., 20110630 + uint32_t iv_buildDate; + + /// Build time generated by `date +%H%M`, e.g., 0756 + uint32_t iv_buildTime; + + /// Reserved for future expansion + uint32_t iv_reserved32[5]; + + ////////////////////////////////////////////////////////////////////// + // Other Information - 1-byte aligned; 8 entries + ////////////////////////////////////////////////////////////////////// + + /// Header format version number + uint8_t iv_headerVersion; + + /// Indicates whether the image has been normalized (0/1) + uint8_t iv_normalized; + + /// Indicates whether the TOC has been sorted to speed searching (0/1) + uint8_t iv_tocSorted; + + /// Reserved for future expansion + uint8_t iv_reserved8[5]; + + ////////////////////////////////////////////////////////////////////// + // Strings; 64 characters allocated + ////////////////////////////////////////////////////////////////////// + + /// Build user, generated by `id -un` + char iv_buildUser[16]; + + /// Build host, generated by `hostname` + char iv_buildHost[24]; + + /// Reserved for future expansion + char iv_reservedChar[24]; + +} P9XipHeader; + + + +/// A C-structure form of the P9-XIP Table of Contents (TOC) entries +/// +/// The .toc section consists entirely of an array of these structures. +/// TOC entries are never accessed by image code. +/// +/// These structures store indexing information for global data required to be +/// manipulated by external tools. The actual data is usually allocated in a +/// data section and manipulated by the SBE code using global or local symbol +/// names. Each TOC entry contains a pointer to a keyword string naming the +/// data, the address of the data (or the data itself), the data type, +/// meta-information about the data, and for vectors the vector size. + +// -*- DO NOT REORDER OR EDIT THIS STRUCTURE DEFINITION WITHOUT ALSO -*- +// -*- EDITING THE ASSEMBLER MACROS (BELOW) THAT CREATE THE TABLE OF -*- +// -*- CONTENTS ENTRIES. -*- + +typedef struct +{ + + /// A pointer to a 0-byte terminated ASCII string identifying the data. + /// + /// When allocated by the .xip_toc macro this is a pointer to the string + /// form of the symbol name for the global or local symbol associated with + /// the data which is allocated in the .strings section. This pointer is + /// not aligned. + /// + /// When the image is normalized this pointer is replaced by the offset of + /// the string in the .strings section. + uint32_t iv_id; + + /// A 32-bit pointer locating the data + /// + /// This field is initially populated by the link editor. For scalar, + /// vector and string types this is the final relocated address of the + /// first byte of the data. For address types, this is the relocated + /// address. When the image is normalized, these addresses are converted + /// into the equivalent offsets from the beginning of the section holding + /// the data. + uint32_t iv_data; + + /// The type of the data; See \ref p9_xip_toc_types. + uint8_t iv_type; + + /// The section containing the data; See \ref p9_xip_sections. + uint8_t iv_section; + + /// The number of elements for vector types, otherwise 1 for scalar types + /// and addresses. + /// + /// Vectors are naturally limited in size, e.g. to the number of cores, + /// chips in a node, DD-levels etc. If \a iv_elements is 0 then no bounds + /// checking is done on get/set accesses of the data. + uint8_t iv_elements; + + /// Structure alignment padding; Pad to 12 bytes + uint8_t iv_pad; + +} P9XipToc; + +/// The P9XipToc structure is created by assembler code and is expected +/// to have the same size in C code. This constraint is checked in +/// p9_xip_validate(). +#define SIZE_OF_P9_XIP_TOC 12 + + +/// A C-structure form of hashed P9-XIP Table of Contents (TOC) entries +/// +/// This structure was introduced in order to allow a small TOC for the .fixed +/// section to support minimum-sized SEEPROM images in which the global TOC +/// and all strings have been stripped out. In this structure the index +/// string has been replaced by a 32-bit hash, and there is no longer a record +/// of the original data name other then the hash. The section of the data is +/// assumed to be .fixed, with a maximum 16-bit offset. +/// +/// These structures are created when entries are made in the .fixed section. +/// They are created empty, then filled in during image normalization. +/// +/// This structure allows the p9_xip_get*() and p9_xip_set*() APIs to work +/// even on highly-stripped SEEPROM images. + +typedef struct +{ + + /// A 32-bit hash (FNV-1a) of the Id string. + uint32_t iv_hash; + + /// The offset in bytes from the start of the (implied) section of the data + uint16_t iv_offset; + + /// The type of the data; See \ref p9_xip_toc_types. + uint8_t iv_type; + + /// The number of elements for vector types, otherwise 1 for scalar types + /// and addresses. + /// + /// Vectors are naturally limited in size, e.g. to the number of cores, + /// chips in a node, DD-levels etc. If \a iv_elements is 0 then no bounds + /// checking is done on get/set accesses of the data. + uint8_t iv_elements; + +} P9XipHashedToc; + +/// The P9XipHashedToc structure is created by assembler code and is expected +/// to have the same size in C code. This constraint is checked in +/// p9_xip_validate(). +#define SIZE_OF_P9_XIP_HASHED_TOC 8 + + +/// A decoded TOC entry for use by applications +/// +/// This structure is a decoded form of a normalized TOC entry, filled in by +/// the p9_xip_decode_toc() and p9_xip_find() APIs. This structure is +/// always returned with data elements in host-endian format. +/// +/// In the event that the TOC has been removed from the image, this structure +/// will also be returned by p9_xip_find() with information populated from +/// the .fixed_toc section if possible. In this case the field \a iv_partial +/// will be set and only the fields \a iv_address, \a iv_imageData, \a iv_type +/// and \a iv_elements will be populated (all other fields will be set to 0). +/// +/// \note Only special-purpose applications will ever need to use this +/// structure given that the higher-level APIs p9_xip_get_*() and +/// p9_xip_set_*() are provided and should be used if possible, especially +/// given that the information may be truncated as described above. + +typedef struct +{ + + /// A pointer to the associated TOC entry as it exists in the image + /// + /// If \a iv_partial is set this field is returned as 0. + P9XipToc* iv_toc; + + /// The full relocatable image address + /// + /// All relocatable addresses are computed from the \a iv_linkAddress + /// stored in the header. For scalar and string data, this is the + /// relocatable address of the data. For address-only entries, this is + /// the indexed address itself. + uint64_t iv_address; + + /// A host pointer to the first byte of text or data within the image + /// + /// For scalar or string types this is a host pointer to the first byte of + /// the data. For code pointers (addresses) this is host pointer to the + /// first byte of code. Note that any use of this field requires the + /// caller to handle conversion of the data to host endian-ness if + /// required. Only 8-bit and string data can be used directly on all + /// hosts. + void* iv_imageData; + + /// The item name + /// + /// This is a pointer in host memory to a string that names the TOC entry + /// requested. This field is set to a pointer to the ID string of the TOC + /// entry inside the image. If \a iv_partial is set this field is returned + /// as 0. + char* iv_id; + + /// The data type, one of the P9_XIP_* constants + uint8_t iv_type; + + /// The number of elements in a vector + /// + /// This field is set from the TOC entry when the TOC entry is + /// decoded. This value is stored as 1 for scalar declarations, and may be + /// set to 0 for vectors with large or undeclared sizes. Otherwise it is + /// used to bounds check indexed accesses. + uint8_t iv_elements; + + /// Is this record only partially populated? + /// + /// This field is set to 0 normally, and only set to 1 if a lookup is made + /// in an image that only has the fixed TOC and the requested Id hashes to + /// the fixed TOC. + uint8_t iv_partial; + +} P9XipItem; + + +/// Validate a P9-XIP image +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. +/// +/// \param[in] i_size The putative size of the image +/// +/// \param[in] i_maskIgnores Array of ignore bits representing which properties +/// should not be checked for in p9_xip_validate2(). +/// +/// This API should be called first by all applications that manipulate +/// P9-XIP images in host memory. The magic number is validated, and +/// the image is checked for consistency of the section table and table of +/// contents. The \a iv_imageSize field of the header must also match the +/// provided \a i_size parameter. Validation does not modify the image. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_validate(void* i_image, const uint32_t i_size); + +int +p9_xip_validate2(void* i_image, const uint32_t i_size, const uint32_t i_maskIgnores); + + +/// Normalize the P9-XIP image +/// +/// \param[in] io_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. +/// +/// P9-XIP images must be normalized before any other APIs are allowed to +/// operate on the image. Since normalization modifies the image, an explicit +/// call to normalize the image is required. Briefly, normalization modifies +/// the TOC entries created by the final link to simplify search, updates, +/// modification and relocation of the image. Normalization is explained in +/// the written documentation of the P9-XIP binary format. Normalization does +/// not modify the size of the image. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_normalize(void* io_image); + + +/// Return the size of a P9-XIP image from the image header +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. +/// +/// \param[out] o_size A pointer to a variable returned as the size of the +/// image in bytes, as recorded in the image header. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_image_size(void* i_image, uint32_t* o_size); + + +/// Locate a section table entry and translate into host format +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. +/// +/// \param[in] i_sectionId Identifies the section to be queried. See \ref +/// p9_xip_sections. +/// +/// \param[out] o_hostSection Updated to contain the section table entry +/// translated to host byte order. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_get_section(const void* i_image, + const int i_sectionId, + P9XipSection* o_hostSection); + + +/// Endian translation of a P9XipHeader object +/// +/// \param[out] o_hostHeader The destination object. +/// +/// \param[in] i_imageHeader The source object. +/// +/// Translation of a P9XipHeader includes translation of all data members +/// including traslation of the embedded section table. This translation +/// works even if \a o_src == \a o_dest, i.e., in the destructive case. +void +p9_xip_translate_header(P9XipHeader* o_hostHeader, + const P9XipHeader* i_imageHeader); + + +/// Get scalar data from a P9-XIP image +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item +/// requested. +/// +/// \param[out] o_data A pointer to an 8-byte integer to receive the scalar +/// data. Assuming the item is located this variable is assigned by the call. +/// In the event of an error the final state of \a o_data is not specified. +/// +/// This API searches the P9-XIP Table of Contents (TOC) for the item named +/// \a i_id, assigning \a o_data from the image if the item is found and is a +/// scalar value. Scalar values include 8- 32- and 64-bit integers and image +/// addresses. Image data smaller than 64 bits are extracted as unsigned +/// types, and it is the caller's responsibility to cast or convert the +/// returned data as appropriate. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_get_scalar(void* i_image, const char* i_id, uint64_t* o_data); + + +/// Get an integral element from a vector held in a P9-XIP image +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item +/// requested. +/// +/// \param[in] i_index The index of the vector element to return. +/// +/// \param[out] o_data A pointer to an 8-byte integer to receive the +/// data. Assuming the item is located this variable is assigned by the call. +/// In the event of an error the final state of \a o_data is not specified. +/// +/// This API searches the P9-XIP Table of Contents (TOC) for the \a i_index +/// element of the item named \a i_id, assigning \a o_data from the image if +/// the item is found, is a vector of an integral type, and the \a i_index is +/// in bounds. Vector elements smaller than 64 bits are extracted as unsigned +/// types, and it is the caller's responsibility to cast or convert the +/// returned data as appropriate. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_get_element(void* i_image, + const char* i_id, + const uint32_t i_index, + uint64_t* o_data); + + +/// Get string data from a P9-XIP image +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item +/// requested. +/// +/// \param[out] o_data A pointer to a character pointer. Assuming the +/// item is located this variable is assigned by the call to point to the +/// string as it exists in the \a i_image. In the event of an error the final +/// state of \a o_data is not specified. +/// +/// This API searches the P9-XIP Table of Contents (TOC) for the item named +/// \a i_id, assigning \a o_data if the item is found and is a string. It is +/// the caller's responsibility to copy the string from the \a i_image memory +/// space if necessary. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_get_string(void* i_image, const char* i_id, char** o_data); + + +/// Directly read 64-bit data from the image based on a image address +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. +/// +/// \param[in] i_imageAddress A relocatable IMAGE address contained in the +/// image, presumably of an 8-byte data area. The \a i_imageAddress is +/// required to be 8-byte aligned, otherwise the P9_XIP_ALIGNMENT_ERROR code +/// is returned. +/// +/// \param[out] o_data The 64 bit data in host format that was found at \a +/// i_imageAddress. +/// +/// This API is provided for applications that need to manipulate P9-XIP +/// images in terms of their relocatable IMAGE addresses. The API checks that +/// the \a i_imageAddress is properly aligned and contained in the image, then +/// reads the contents of \a i_imageAddress into \a o_data, performing +/// image-to-host endianess conversion if required. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_read_uint64(const void* i_image, + const uint64_t i_imageAddress, + uint64_t* o_data); + + +/// Set scalar data in a P9-XIP image +/// +/// \param[in,out] io_image A pointer to a P9-XIP image in host memory. +/// The image is assumed to be consistent with the information contained in +/// the header regarding the presence of and sizes of all sections. The image +/// is also required to have been normalized. +/// +/// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item +/// to be modified. +/// +/// \param[in] i_data The new scalar data. +/// +/// This API searches the P9-XIP Table of Contents (TOC) for the item named +/// by \a i_id, updating the image from \a i_data if the item is found, has +/// a scalar type and can be modified. For this API the scalar types include +/// 8- 32- and 64-bit integers. Although IMAGE addresses are considered a +/// scalar type for p9_xip_get_scalar(), IMAGE addresses can not be modified +/// by this API. The caller is responsible for ensuring that the \a i_data is +/// of the correct size for the underlying data element in the image. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_set_scalar(void* io_image, const char* i_id, const uint64_t i_data); + + +/// Set an integral element in a vector held in a P9-XIP image +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item +/// to be updated. +/// +/// \param[in] i_index The index of the vector element to update. +/// +/// \param[out] i_data The new vector element. +/// +/// This API searches the P9-XIP Table of Contents (TOC) for the \a i_index +/// element of the item named \a i_id, update the image from \a i_data if the +/// item is found, is a vector of an integral type, and the \a i_index is in +/// bounds. The caller is responsible for ensuring that the \a i_data is of +/// the correct size for the underlying data element in the image. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_set_element(void* i_image, + const char* i_id, + const uint32_t i_index, + const uint64_t i_data); + + +/// Set string data in a P9-XIP image +/// +/// \param[in,out] io_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_id A pointer to a 0-terminated ASCII string naming the item +/// to be modified. +/// +/// \param[in] i_data A pointer to the new string data. +/// +/// This API searches the P9-XIP Table of Contents (TOC) for the item named +/// \a i_id, which must be a string variable. If found, then the string data +/// in the image is overwritten with \a i_data. Strings are held 0-terminated +/// in the image, and the P9-XIP format does not maintain a record of the +/// amount of memory allocated for an individual string. If a string is +/// overwritten by a shorter string then the 'excess' storage is effectively +/// lost. If the length of \a i_data is longer that the current strlen() of +/// the string data then \a i_data is silently truncated to the first +/// strlen(old_string) characters. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_set_string(void* io_image, const char* i_id, const char* i_data); + + +/// Directly write 64-bit data into the image based on a IMAGE address +/// +/// \param[in, out] io_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. +/// +/// \param[in] i_imageAddress A relocatable IMAGE address contained in the +/// image, presumably of an 8-byte data area. The \a i_imageAddress is +/// required to be 8-byte aligned, otherwise the P9_XIP_ALIGNMENT_ERROR code +/// is returned. +/// +/// \param[in] i_data The 64 bit data in host format to be written to \a +/// i_imageAddress. +/// +/// This API is provided for applications that need to manipulate P9-XIP +/// images in terms of their relocatable IMAGE addresses. The API checks that +/// the \a i_imageAddress is properly aligned and contained in the image, then +/// updates the contents of \a i_imageAddress with \a i_data, performing +/// host-to-image endianess conversion if required. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_write_uint64(void* io_image, + const uint64_t i_imageAddress, + const uint64_t i_data); + + +/// Map over a P9-XIP image Table of Contents +/// +/// \param[in,out] io_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_fn A pointer to a function to call on each TOC entry. The +/// function has the prototype: +/// +/// \code +/// int (*i_fn)(void* io_image, +/// const P9XipItem* i_item, +/// void* io_arg) +/// \endcode +/// +/// \param[in,out] io_arg The private argument of \a i_fn. +/// +/// This API iterates over each entry of the TOC, calling \a i_fn with +/// pointers to the image, a P9XipItem* pointer, and a private argument. The +/// iteration terminates either when all TOC entries have been mapped, or \a +/// i_fn returns a non-zero code. +/// +/// \retval 0 Success; All TOC entries were mapped, including the case that +/// the .toc section is empty. +/// +/// \retval non-0 May be either one of the P9-XIP image error codes (see \ref +/// p9_xip_image_errors), or a non-zero code from \a i_fn. Since the standard +/// P9_XIP return codes are > 0, application-defined codes should be < 0. +int +p9_xip_map_toc(void* io_image, + int (*i_fn)(void* io_image, + const P9XipItem* i_item, + void* io_arg), + void* io_arg); + + +/// Find a P9-XIP TOC entry +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_id A 0-byte terminated ASCII string naming the item to be +/// searched for. +/// +/// \param[out] o_item If the search is successful, then the object +/// pointed to by \a o_item is filled in with the decoded form of the +/// TOC entry for \a i_id. If the API returns a non-0 error code then the +/// final state of the storage at \a o_item is undefined. This parameter may +/// be suppied as 0, in which case p9_xip_find() serves as a simple predicate +/// on whether an item is indexded in the TOC. +/// +/// This API searches the TOC of a normalized P9-XIP image for the item named +/// \a i_id, and if found, fills in the structure pointed to by \a +/// o_item with a decoded form of the TOC entry. If the item is not found, +/// the following two return codes may be considered non-error codes: +/// +/// - P9_XIP_ITEM_NOT_FOUND : No TOC record for \a i_id was found. +/// +/// - P9_XIP_DATA_NOT_PRESENT : The item appears in the TOC, however the +/// section containing the data is no longer present in the image. +/// +/// If the TOC section has been deleted from the image, then the search is +/// restricted to the abbreviated TOC that indexes data in the .fixed section. +/// In this case the \a o_item structure is marked with a 1 in the \a +/// iv_partial field since the abbreviated TOC can not populate the entire +/// P9XipItem structure. +/// +/// \note This API should typically only be used as a predicate, not as a way +/// to access the image via the returned P9XipItem structure. To obtain data +/// from the image or update data in the image use the p9_xip_get_*() and +/// p9_xip_set_*() APIs respectively. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_find(void* i_image, + const char* i_id, + P9XipItem* o_item); + + + +/// Delete a section from a P9-XIP image in host memory +/// +/// \param[in,out] io_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_sectionId Identifies the section to be deleted. See \ref +/// p9_xip_sections. +/// +/// This API effectively deletes a section from a P9-XIP image held in host +/// memory. Unless the requested section \a i_section is already empty, only +/// the final (highest address offset) section of the image may be deleted. +/// Deleting the final section of the image means that the section size is set +/// to 0, and the size of the image recorded in the header is reduced by the +/// section size. Any alignment padding of the now-last section is also +/// removed. +/// +/// \note This API does not check for or warn if other sections in the image +/// reference the deleted section. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_delete_section(void* io_image, const int i_sectionId); + + +#ifndef PPC_HYP + +/// Duplicate a section from a P9-XIP image in host memory +/// +/// \param[in,out] i_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. +/// +/// \param[in] i_sectionId Identifies the section to be duplicated. See \ref +/// p9_xip_sections. +/// +/// \param[out] o_duplicate At exit, points to the newly allocated and +/// initialized duplicate of the given section. The caller is responsible for +/// free()-ing this memory when no longer required. +/// +/// \param[out] o_size At exit, contains the size (in bytes) of the duplicated +/// section. +/// +/// This API creates a bytewise duplicate of a non-empty section into newly +/// malloc()-ed memory. At exit \a o_duplicate points to the duplicate, and \a +/// o_size is set the the size of the duplicated section. The caller is +/// responsible for free()-ing the memory when no longer required. The +/// pointer at \a o_duplicate is set to NULL (0) and the \a o_size is set to 0 +/// in the event of any failure. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_duplicate_section(const void* i_image, + const int i_sectionId, + void** o_duplicate, + uint32_t* o_size); + +#endif // PPC_HYP + + +/// Append binary data to a P9-XIP image held in host memory +/// +/// \param[in,out] io_image A pointer to a P9-XIP image in host memory. The +/// image is assumed to be consistent with the information contained in the +/// header regarding the presence of and sizes of all sections. The image is +/// also required to have been normalized. +/// +/// \param[in] i_sectionId Identifies the section to contain the new data. +/// +/// \param[in] i_data A pointer to the data to be appended to the image. If +/// this pointer is NULL (0), then the effect is as if \a i_data were a +/// pointer to an \a i_size array of 0 bytes. +/// +/// \param[in] i_size The size of the data to be appended in bytes. If \a +/// i_data is 0, then this is the number of bytes to clear. +/// +/// \param[in] i_allocation The size of the memory region containing the +/// image, measured from the first byte of the image. The call will fail if +/// appending the new data plus any alignment padding would overflow the +/// allocated memory. +/// +/// \param[out] o_sectionOffset If non-0 at entry, then the API updates the +/// location pointed to by \a o_sectionOffset with the offset of the first +/// byte of the appended data within the indicated section. This return value +/// is invalid in the event of a non-0 return code. +/// +/// This API copies data from \a i_data to the end of the indicated \a +/// i_section. The section \a i_section must either be empty, or must be the +/// final (highest address) section in the image. If the section is initially +/// empty and \a i_size is non-0 then the section is created at the end of the +/// image. The size of \a i_section and the size of the image are always +/// adjusted to reflect the newly added data. This is a simple binary copy +/// without any interpretation (e.g., endian-translation) of the copied data. +/// The caller is responsible for insuring that the host memory area +/// containing the P9-XIP image is large enough to hold the newly appended +/// data without causing addressing errors or buffer overrun errors. +/// +/// The final parameter \a o_sectionOffset is optional, and may be passed as +/// NULL (0) if the application does not require the information. This return +/// value is provided to simplify typical use cases of this API: +/// +/// - A scan program is appended to the image, or a run-time data area is +/// allocated and cleared at the end of the image. +/// +/// - Pointer variables in the image are updated with IMAGE addresses obtained +/// via p9_xip_section2image(), or +/// other procedure code initializes a newly allocated and cleared data area +/// via host addresses obtained from p9_xip_section2host(). +/// +/// Regarding alignment, note that the P9-XIP format requires that sections +/// maintain an initial alignment that varies by section, and the API will +/// enforce these alignment constraints for all sections created by the API. +/// All alignment is relative to the first byte of the image (\a io_image) - +/// \e not to the current in-memory address of the image. By specification +/// P9-XIP images must be loaded at a 4K alignment in order for IMAGE hardware +/// relocation to work, however the APIs don't require this 4K alignment for +/// in-memory manipulation of images. Images to be executed on ImageVe will +/// normally require at least 8-byte final aligment in order to guarantee that +/// the ImageVe can execute an 8-byte fetch or load/store of the final +/// doubleword. +/// +/// \note If the TOC section is modified then the image is marked as having an +/// unsorted TOC. +/// +/// \note If the call fails for any reason (other than a bug in the API +/// itself) then the \a io_image data is returned unmodified. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_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); + + +/// Convert a P9-XIP section offset to a relocatable IMAGE address +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory +/// +/// \param[in] i_sectionId A valid P9-XIP section identifier; The section +/// must be non-empty. +/// +/// \param[in] i_offset An offset (in bytes) within the section. At least one +/// byte at \a i_offset must be currently allocated in the section. +/// +/// \param[in] o_imageAddress The equivalent relocatable IMAGE address is +/// returned via this pointer. Since valid IMAGE addresses are always either +/// 4-byte (code) or 8-byte (data) aligned, this API checks the aligment of +/// the translated address and returns P9_XIP_ALIGNMENT_ERROR if the IMAGE +/// address is not at least 4-byte aligned. Note that the translated address +/// is still returned even if incorrectly aligned. +/// +/// This API is typically used to translate section offsets returned from +/// p9_xip_append() into relocatable IMAGE addresses. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_section2image(const void* i_image, + const int i_sectionId, + const uint32_t i_offset, + uint64_t* o_imageAddress); + + +/// Convert a P9-XIP relocatable image address to a host memory address +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. +/// +/// \param[in] i_imageAddress A relocatable image address putatively addressing +/// relocatable memory contained in the image. +/// +/// \param[out] o_hostAddress The API updates the location pointed to by \a +/// o_hostAddress with the host address of the memory addressed by \a +/// i_imageAddress. In the event of an error (non-0 return code) the final +/// content of \a o_hostAddress is undefined. +/// +/// This API is typically used to translate relocatable image addresses stored +/// in the P9-XIP image into the equivalent host address of the in-memory +/// image, allowing host-code to manipulate arbitrary data structures in the +/// image. If the \a i_imageAddress does not refer to memory within the image +/// (as determined by the link address and image size) then the +/// P9_XIP_INVALID_ARGUMENT error code is returned. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_image2host(const void* i_image, + const uint64_t i_imageAddress, + void** o_hostAddress); + + +/// Convert a P9-XIP relocatable image address to section Id and offset +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory. +/// +/// \param[in] i_imageAddress A relocatable image address putatively addressing +/// relocatable memory contained in the image. +/// +/// \param[out] o_section The API updates the location pointed to by \a +/// o_section with the section Id of the memory addressed by \a +/// i_imageAddress. In the event of an error (non-0 return code) the final +/// content of \a o_section is undefined. +/// +/// \param[out] o_offset The API updates the location pointed to by \a +/// o_offset with the byte offset of the memory addressed by \a i_imageAddress +/// within \a o_section. In the event of an error (non-0 return code) the +/// final content of \a o_offset is undefined. +/// +/// This API is typically used to translate relocatable image addresses stored +/// in the P9-XIP image into the equivalent section + offset form, allowing +/// host-code to manipulate arbitrary data structures in the image. If the \a +/// i_imageAddress does not refer to memory within the image (as determined by +/// the link address and image size) then the P9_XIP_INVALID_ARGUMENT error +/// code is returned. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_image2section(const void* i_image, + const uint64_t i_imageAddress, + int* o_section, + uint32_t* o_offset); + + +/// Convert an in-memory P9-XIP host address to a relocatable image address +/// +/// \param[in] i_image A pointer to a P9-XIP image in host memory +/// +/// \param[in] i_hostAddress A host address addressing data within the image. +/// +/// \param[out] o_imageAddress The API updates the location pointed to by \a +/// o_imageAddress with the equivelent relocatable image address of the memory +/// addressed by i_hostAddress. Since valid image addresses are always either +/// 4-byte (code) or 8-byte (data) aligned, this API checks the aligment of +/// the translated address and returns P9_XIP_ALIGNMENT_ERROR if the image +/// address is not at least 4-byte aligned. Note that the translated address +/// is still returned evn if incorrectly aligned. +/// +/// This API is provided as a convenient way to convert host memory addresses +/// for an in-memory P9-XIP image into image addresses correctly relocated for +/// the image, for example to update pointer variables in the image. If the +/// \a i_hostAddress does not refer to memory within the image (as determined +/// by the image address and image size) then the P9_XIP_INVALID_ARGUMENT +/// error code is returned. +/// +/// \retval 0 Success +/// +/// \retval non-0 See \ref p9_xip_image_errors +int +p9_xip_host2image(const void* i_image, + void* i_hostAddress, + uint64_t* o_imageAddress); + + + +// 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 + #define XIP_STATIC + +#endif // PPC_HYP + +// 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); + +/// Byte-reverse a 32-bit integer if on a little-endian machine +XIP_STATIC uint32_t +xipRevLe32(const uint32_t i_x); + + +/// Byte-reverse a 64-bit integer if on a little-endian machine +XIP_STATIC uint64_t +xipRevLe64(const uint64_t i_x); + +/// \defgroup p9_xip_image_errors Error codes from P9-XIP image APIs +/// +/// @{ + +/// A putative P9-XIP image does not have the correct magic number, or +/// contains some other major inconsistency. +#define P9_XIP_IMAGE_ERROR 1 + +/// The TOC may be missing, partially present or may have an alignment problem. +#define P9_XIP_TOC_ERROR 2 + +/// A named item was not found in the P9-XIP TOC, or a putative HALT address +/// is not associated with a halt code in .halt. +#define P9_XIP_ITEM_NOT_FOUND 3 + +/// A named item appears in the P9-XIP TOC, but the data is not present in +/// the image. This error can occur if sections have been deleted from the +/// image. +#define P9_XIP_DATA_NOT_PRESENT 4 + +/// A named item appears in the P9-XIP TOC, but the data can not be +/// modified. This error will occur if an attempt is made to modify an +/// address-only entry. +#define P9_XIP_CANT_MODIFY 5 + +/// A direct or implied argument is invalid, e.g. an illegal data type or +/// section identifier, or an address not contained within the image. +#define P9_XIP_INVALID_ARGUMENT 6 + +/// A data type mismatch or an illegal type was specified or implied for an +/// operation. +#define P9_XIP_TYPE_ERROR 7 + +/// A bug in a P9-XIP image API +#define P9_XIP_BUG 8 + +/// The image must first be normalized with p9_xip_normalize(). +#define P9_XIP_NOT_NORMALIZED 9 + +/// Attempt to delete a non-empty section that is not the final section of the +/// image, or an attempt to append data to a non-empty section that is not the +/// final section of the image, or an attempt to operate on an empty section +/// for those APIs that prohibit this. +#define P9_XIP_SECTION_ERROR 10 + +/// An address translation API returned a image address that was not at least +/// 4-byte aligned, or alignment violations were observed by +/// p9_xip_validate() or p9_xip_append(). +#define P9_XIP_ALIGNMENT_ERROR 11 + +/// An API that performs dynamic memory allocation was unable to allocate +/// memory. +#define P9_XIP_NO_MEMORY 12 + +/// Attempt to get or set a vector element with an index that is outside of +/// the declared bounds of the vector. +#define P9_XIP_BOUNDS_ERROR 13 + +/// Attempt to grow the image past its defined memory allocation +#define P9_XIP_WOULD_OVERFLOW 14 + +/// Error associated with the disassembler occured. +#define P9_XIP_DISASSEMBLER_ERROR 15 + +/// hash collision creating the .fixed_toc section +#define P9_XIP_HASH_COLLISION 16 + +/// Applications can expand this macro to declare an array of string forms of +/// the error codes if desired. +#define P9_XIP_ERROR_STRINGS(var) \ + const char* var[] = { \ + "Success", \ + "P9_XIP_IMAGE_ERROR", \ + "P9_XIP_TOC_ERROR", \ + "P9_XIP_ITEM_NOT_FOUND", \ + "P9_XIP_DATA_NOT_PRESENT", \ + "P9_XIP_CANT_MODIFY", \ + "P9_XIP_INVALID_ARGUMENT", \ + "P9_XIP_TYPE_ERROR", \ + "P9_XIP_BUG", \ + "P9_XIP_NOT_NORMALIZED", \ + "P9_XIP_SECTION_ERROR", \ + "P9_XIP_ALIGNMENT_ERROR", \ + "P9_XIP_NO_MEMORY", \ + "P9_XIP_BOUNDS_ERROR", \ + "P9_XIP_WOULD_OVERFLOW", \ + "P9_XIP_DISASSEMBLER_ERROR", \ + "P9_XIP_HASH_COLLISION", \ + } + +/// Applications can use this macro to safely index the array of error +/// strings. +#define P9_XIP_ERROR_STRING(var, n) \ + ((((n) < 0) || ((n) > (int)(sizeof(var) / sizeof(char*)))) ? \ + "Bug : Invalid P9-XIP error code" : var[n]) + +/// @} + +/// Disassembler error codes. +#define DIS_IMAGE_ERROR 1 +#define DIS_MEMORY_ERROR 2 +#define DIS_DISASM_ERROR 3 +#define DIS_RING_NAME_ADDR_MATCH_SUCCESS 4 +#define DIS_RING_NAME_ADDR_MATCH_FAILURE 5 +#define DIS_TOO_MANY_DISASM_WARNINGS 6 +#define DIS_DISASM_TROUBLES 7 + +#define DIS_ERROR_STRINGS(var) \ + const char* var[] = { \ + "Success", \ + "DIS_IMAGE_ERROR", \ + "DIS_MEMORY_ERROR", \ + "DIS_DISASM_ERROR", \ + "DIS_RING_NAME_ADDR_MATCH_SUCCESS", \ + "DIS_RING_NAME_ADDR_MATCH_FAILURE", \ + "DIS_TOO_MANY_DISASM_WARNINGS", \ + "DIS_DISASM_TROUBLES", \ + } + +#define DIS_ERROR_STRING(var, n) \ + ((((n) < 0) || ((n) > (int)(sizeof(var) / sizeof(char*)))) ? \ + "Bug : Invalid DIS error code" : var[n]) + +#if 0 +{ + /* So __cplusplus doesn't mess w/auto-indent */ +#endif +#ifdef __cplusplus +} +#endif + +#endif // __ASSEMBLER__ + + +//////////////////////////////////////////////////////////////////////////// +// Assembler Definitions +//////////////////////////////////////////////////////////////////////////// + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + +/// Create an XIP TOC entry +/// +/// \param[in] index The string form of the \a index symbol is created and +/// linked from the TOC entry to allow external search procedures to locate +/// the \a address. +/// +/// \param[in] type One of the P9_XIP_* type constants; See \ref +/// p9_xip_toc_types. +/// +/// \param[in] address The address of the idexed code or data; This wlll +/// typically be a symbol. +/// +/// \param[in] elements <Optional> For vector types, number of elements in the +/// vector, which is limited to an 8-bit unsigned integer. This parameter +/// defaults to 1 which indicates a scalar type. Declaring a vector with 0 +/// elements disables bounds checking on vector accesses, and can be used if +/// very large or indeterminate sized vectors are required. The TOC format +/// does not support vectors of strings or addresses. +/// +/// The \c .xip_toc macro creates a XIP Table of Contents (TOC) structure in +/// the \c .toc section, as specified by the parameters. This macro is +/// typically not used directly in assembly code. Instead programmers should +/// use .xip_quad, .xip_quada, .xip_quadia, .xip_address, .xip_string or +/// .xip_cvs_revision. + + .macro .xip_toc, index:req, type:req, address:req, elements=1 + + .if (((\type) < 1) || ((\type) > P9_XIP_MAX_TYPE_INDEX)) + .error ".xip_toc : Illegal type index" + .endif + + // First push into the .strings section to lay down the + // string form of the index name under a local label. + + .pushsection .strings +7667862: + .asciz "\index" + .popsection + + // Now the 12-byte TOC entry is created. Push into the .toc section + // and lay down the first 4 bytes which are always a pointer to the + // string just declared. The next 4 bytes are the address of the data + // (or the address itself in the case of address types). The final 4 + // bytes are the type, section (always 0 prior to normalization), + // number of elements, and a padding byte. + + .pushsection .toc + + .long 7667862b, (\address) + .byte (\type), 0, (\elements), 0 + + .popsection + + .endm + + +/// Allocate and initialize 64-bit global scalar or vector data and create the +/// TOC entry. +/// +/// \param[in] symbol The name of the scalar or vector; this name is also used +/// as the TOC index of the data. +/// +/// \param[in] init The initial value of (each element of) the data. +/// This is a 64-bit integer; To allocate address pointers use .xip_quada. +/// +/// \param[in] elements The number of 64-bit elements in the data structure, +/// defaulting to 1, with a maximum value of 255. +/// +/// \param[in] section The section where the data will be allocated, +/// default depends on the memory space + + .macro .xip_quad, symbol:req, init:req, elements=1, section + + ..xip_quad_helper .quad, \symbol, (\init), (\elements), \section + + .endm + + +/// Allocate and initialize 64-bit global scalar or vector data containing a +/// relocatable address in and create the TOC entry. +/// +/// \param[in] symbol The name of the scalar or vector; this name is also used +/// as the TOC index of the data. +/// +/// \param[in] init The initial value of (each element of) the data. This +/// will typically be a symbolic address. If the intention is to define an +/// address that will always be filled in later by image manipulation tools, +/// then use the .xip_quad macro with a 0 initial value. +/// +/// \param[in] elements The number of 64-bit elements in the data structure, +/// defaulting to 1, with a maximum value of 255. +/// +/// \param[in] section The section where the data will be allocated, +/// default depends on the memory space + + .macro .xip_quada, symbol:req, offset:req, elements=1, section + + ..xip_quad_helper .quada, \symbol, (\offset), (\elements), \section + + .endm + + +/// Helper for .xip_quad and .xip_quada + + .macro ..xip_quad_helper, directive, symbol, init, elements, section + + .if (((\elements) < 1) || ((\elements) > 255)) + .error "The number of vector elements must be in the range 1..255" + .endif + + ..xip_pushsection \section + .balign 8 + + .global \symbol +\symbol\(): + .rept (\elements) + \directive (\init) + .endr + + .popsection + + .xip_toc \symbol, P9_XIP_UINT64, \symbol, (\elements) + + .endm + + +/// Allocate and initialize 64-bit global scalar or vector data containing +/// full 64-bit addresses and create a TOC entry +/// +/// \param[in] symbol The name of the scalar or vector; this name is also used +/// as the TOC index of the data. +/// +/// \param[in] space A valid image memory space descriptor +/// +/// \param[in] offset A 32-bit relocatable offset +/// +/// \param[in] elements The number of 64-bit elements in the data structure, +/// defaulting to 1, with a maximum value of 255. +/// +/// \param[in] section The section where the data will be allocated, +/// default depends on the memory space + + .macro .xip_quadia, symbol:req, space:req, offset:req, \ + elements=1, section + + .if (((\elements) < 1) || ((\elements) > 255)) + .error "The number of vector elements must be in the range 1..255" + .endif + + ..xip_pushsection \section + .balign 8 + + .global \symbol +\symbol\(): + .rept (\elements) + .quadia (\space), (\offset) + .endr + + .popsection + + .xip_toc \symbol, P9_XIP_UINT64, \symbol, (\elements) + + .endm + +/// Default push into .ipl_data unless in an OCI space, then .data + + .macro ..xip_pushsection, section + + .ifnb \section + .pushsection \section + .else + .if (_PGAS_DEFAULT_SPACE == PORE_SPACE_OCI) + .pushsection .data + .else + .pushsection .ipl_data + .endif + .endif + + .balign 8 + + .endm + +/// Allocate and initialize a string in .strings +/// +/// \param[in] index The string will be stored in the TOC using this index +/// symbol. +/// +/// \param[in] string The string to be allocated in .strings. String space is +/// fixed once allocated. Strings designed to be overwritten by external tools +/// should be allocated to be as long as eventually needed (e.g., by a string +/// of blanks.) + + .macro .xip_string, index:req, string:req + + .pushsection .strings +7874647: + .asciz "\string" + .popsection + + .xip_toc \index, P9_XIP_STRING, 7874647b + + .endm + + +/// Allocate and initialize a CVS Revison string in .strings +/// +/// \param[in] index The string will be stored in the TOC using this index +/// symbol. +/// +/// \param[in] string A CVS revision string to be allocated in .strings. CVS +/// revision strings are formatted by stripping out and only storing the +/// actual revision number : +/// +/// \code +/// "$Revision <n>.<m> $" -> "<n>.<m>" +/// \endcode + + + .macro .xip_cvs_revision, index:req, string:req + + .pushsection .strings +7874647: + ..cvs_revision_string "\string" + .popsection + + .xip_toc \index, P9_XIP_STRING, 7874647b + + .endm + + +/// Shorthand to create a TOC entry for an address +/// +/// \param[in] index The symbol will be indexed as this name +/// +/// \param[in] symbol <Optional> The symbol to index; by default the same as +/// the index. + + .macro .xip_address, index:req, symbol + + .ifb \symbol + .xip_toc \index, P9_XIP_ADDRESS, \index + .else + .xip_toc \index, P9_XIP_ADDRESS, \symbol + .endif + + .endm + + +/// Edit and allocate a CVS revision string +/// +/// CVS revision strings are formatted by stripping out and only storing the +/// actual revision number : +/// \code +/// "$Revision <n>.<m> $" -> "<n>.<m>" +/// \endcode + + .macro ..cvs_revision_string, rev:req + .irpc c, \rev + .ifnc "\c", "$" + .ifnc "\c", "R" + .ifnc "\c", "e" + .ifnc "\c", "v" + .ifnc "\c", "i" + .ifnc "\c", "s" + .ifnc "\c", "i" + .ifnc "\c", "o" + .ifnc "\c", "n" + .ifnc "\c", ":" + .ifnc "\c", " " + .ascii "\c" + .endif + .endif + .endif + .endif + .endif + .endif + .endif + .endif + .endif + .endif + .endif + .endr + .byte 0 + .endm + +// *INDENT-ON* +#endif // __ASSEMBLER__ + +#endif // __P9_XIP_TOC_H |