/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: import/chips/p9/xip/p9_xip_tool.C $                           */
/*                                                                        */
/* OpenPOWER HCODE Project                                                */
/*                                                                        */
/* COPYRIGHT 2015,2017                                                    */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/// \file p9_xip_tool.c
/// \brief P9 XIP image search and edit tool
///

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>
#include <fcntl.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string>

#undef P9_XIP_TOOL_VERBOSE

#include "p9_xip_image.h"
#ifndef __PPE__ // Needed on ppe side to avoid TOR API
#include "p9_tor.H"
#include "p9_scan_compression.H"
namespace P9_RID
{
#include "p9_ringId.H"
};
namespace CEN_RID
{
#include "cen_ringId.H"
}
#endif
#include "p9_infrastruct_help.H"
#include "p9_dd_container.h"

#define LINE_SIZE_MAX  1024     // Max size of a single snprintf dump.
#define RING_BUF_SIZE_MAX  1000000

// Listing mode IDs:
//
enum LISTING_MODE_ID
{
    LMID_TABLE,
    LMID_SHORT,
    LMID_NORMAL,
    LMID_LONG,
    LMID_RAW
};

const char* g_usage =
    "Usage: p9_xip_tool <image> [-i<flag> ...] normalize\n"
    "       p9_xip_tool <image> [-i<flag> ...] get <item>\n"
    "       p9_xip_tool <image> [-i<flag> ...] getv <item> <index>\n"
    "       p9_xip_tool <image> [-i<flag> ...] set <item> <value> [ <item1> <value1> ... ]\n"
    "       p9_xip_tool <image> [-i<flag> ...] setv <item> <index> <value> [ <item1> <index1> <value1> ... ]\n"
    "       p9_xip_tool <image> [-i<flag> ...] report [<regex>]\n"
    "       p9_xip_tool <image> [-i<flag> ...] attrdump <attr dump file>\n"
    "       p9_xip_tool <image> [-i<flag> ...] append <section> <file> [ <ddSupport> ]\n"
    "       p9_xip_tool <image> [-i<flag> ...] extract <section> <file> [ <ddLevel> ]\n"
    "       p9_xip_tool <image> [-i<flag> ...] delete <section> [ <section1> ... <sectionN> ]\n"
    "       p9_xip_tool <image> [-i<flag> ...] dis <section>\n"
    "       p9_xip_tool <image> [-i<flag> ...] dissect <section={.rings,.overlays,.overrides,(none)}> [ table,short,normal(default),long,raw ]\n"
    "       p9_xip_tool <image> [-i<flag> ...] check-sbe-ring-section <dd level> <maximum size>\n"
    "\n"
    "This simple application uses the P9-XIP image APIs to normalize, search\n"
    "update and edit P9-XIP images. This program encapsulates several commands\n"
    "in a common command framework which requires an image to operate on, a\n"
    "command name, and command arguments that vary by command.  Commands that\n"
    "modify the image always rewrite the image in-place in the filesystem;\n"
    "however the original image is only modified if the command has completed\n"
    "without error.\n"
    "\n"
    "The program operates on a P9-XIP format binary image, which must be\n"
    "normalized - unless the tool is being called to normalize the image in the\n"
    "first place using the 'normalize' command. The tool also validates the\n"
    "image prior to operating on the image.\n"
    "\n"
    "The 'get' command retrieves a scalar value from the image and prints its\n"
    "representation on stdout (followed by a newline).  Scalar integer values\n"
    "and image addresses are printed as hex numbers (0x...).  Strings are printed\n"
    "verbatim.\n"
    "\n"
    "The 'getv' command retrieves a vector element from the image and prints its\n"
    "representation on stdout (followed by a newline).  Integer values\n"
    "and image addresses are printed as hex numbers (0x...).  Vectors of strings\n"
    "are not supported.\n"
    "\n"
    "The 'set' command allows setting integer and string values in the image.\n"
    "New integer values can be specified in decimal or hex (0x...).  Strings are\n"
    "taken verbatim from the command line.  Note that new string values will be\n"
    "silently truncated to the length of the current string if the new value is\n"
    "longer than the current string. Updating address values is currently not\n"
    "supported.  Any number of item/value pairs can be specified with a single\n"
    "'set' command.\n"
    "\n"
    "The 'setv' command is provided to set individual vector elements of\n"
    "integral arrays.\n"
    "\n"
    "The 'report' command prints a report including a dump of the header and\n"
    "section table, a listing of the types and values of all items that appear\n"
    "in the TOC. The TOC listing includes the\n"
    "sequence number of the entry in the TOC, the item name, the item type and\n"
    "the item value.\n"
    "\n"
    "The 'attrdump' command prints a listing of the names, types and values\n"
    "of all attribute items that appear in the TOC and their value from \n"
    "the attribute dump file\n"
    "\n"
    "The 'append' command either creates or extends the section named by the\n"
    "section argument, by appending the contents of the named file verbatim.\n"
    "Currently the section must either be the final (highest address) section of\n"
    "the image, or must be empty, in which case the append command creates the\n"
    "section as the final section of the image. The 'append' command writes the\n"
    "relocatable image address where the input file was loaded to stdout.  The\n"
    "last argument, ddSupport, indicates if the section being added has ddLevel\n"
    "metadata support (=1) or not (=0). If this arg is omitted, it's assumed\n"
    "to be false (=0).\n"
    "\n"
    "The 'extract' command extracts a section from the binary image.  The last\n"
    "argument, ddLevel, indicates [in hex] the DD level to be extracted.  If\n"
    "the section doesn't have DD level support, a message is returned stating\n"
    "that and to reissue the command w/o the ddLevel arg.  If the section does\n"
    "have DD support but the specified ddLevel cannot be found, a message is\n"
    "returned stating that and no section is returned.  If the arg is omitted,\n"
    "the entire XIP section is returned."
    "\n"
    "The 'delete' command deletes 0 or more sections, starting with <section0>.\n"
    "Each section to be deleted must either be the final (highest address)\n"
    "section of the image at the time it is deleted, or must be empty. The\n"
    "'delete' command writes the size of the final modified image to stdout.\n"
    "\n"
    "The 'dissect' command summarizes the content of a ring section. It\n"
    "accepts two different types of images:  1) The regular XIP image and 2)\n"
    "a stand-alone ring section image (that has the TOR header). Wrt an XIP\n"
    "image, the ring section named by the ring section argument is summarized.\n"
    "Wrt a stand-alone ring section image, the ring section based on the TOR\n"
    "magic word in the image, is summarized. (Hint. To find out what type of\n"
    "image file you're supplying, run \"p9_xip_tool.exe <image file> report\"\n"
    "on it first.) Note that the first sub-argument must be PASSED for an XIP\n"
    "image but must be OMITTED for a stand-alone image. For either image, the\n"
    "second sub-argument is optional and offers the following listing choices:\n"
    "   table:           Tabular overview. No dump of ring block.\n"
    "   short:           Key header info. No dump of ring block.\n"
    "   normal(default): All header info. No dump of ring block.\n"
    "   long:            All header info and binary dump of ring block.\n"
    "   raw:             All header info and binary+raw dump of ring block.\n"
    "Note that if the second sub-argument is omitted, a 'normal' listing of\n"
    "the ring section is chosen.\n"
    "\n"
    "-i<flag>:\n"
    "\t-ifs  Causes the validation step to ignore image size check against the\n"
    "\tfile size.\n"
    "\t-iv   Causes all validation checking to be ignored.\n"
    ;

P9_XIP_ERROR_STRINGS(g_errorStrings);
P9_XIP_TYPE_STRINGS(g_typeStrings);
P9_XIP_TYPE_ABBREVS(g_typeAbbrevs);

P9_XIP_SECTION_NAMES_HW(g_sectionNamesHw);
P9_XIP_SECTION_NAMES_SGPE(g_sectionNamesSgpe);
P9_XIP_SECTION_NAMES_RESTORE(g_sectionNamesRestore);
P9_XIP_SECTION_NAMES_CME(g_sectionNamesCme);
P9_XIP_SECTION_NAMES_PGPE(g_sectionNamesPgpe);
P9_XIP_SECTION_NAMES_IOPPE(g_sectionNamesIoppe);
P9_XIP_SECTION_NAMES_FPPE(g_sectionNamesFppe);
P9_XIP_SECTION_NAMES_SBE(g_sectionNamesSbe);

#define ERRBUF_SIZE 60

typedef struct
{
    int index;
    int regex;
    regex_t preg;
} ReportControl;

off_t g_imageSize;


// Determine name of section given by its index in section table

static inline const char* get_sectionName(uint64_t magic, int index)
{
    switch (magic)
    {
        case P9_XIP_MAGIC_SEEPROM:
            return P9_XIP_SECTION_NAME(g_sectionNamesSbe, index);

        case P9_XIP_MAGIC_CENTAUR:
        case P9_XIP_MAGIC_HW:
            return P9_XIP_SECTION_NAME(g_sectionNamesHw, index);

        case P9_XIP_MAGIC_SGPE:
            return P9_XIP_SECTION_NAME(g_sectionNamesSgpe, index);

        case P9_XIP_MAGIC_RESTORE:
            return P9_XIP_SECTION_NAME(g_sectionNamesRestore, index);

        case P9_XIP_MAGIC_CME:
            return P9_XIP_SECTION_NAME(g_sectionNamesCme, index);

        case P9_XIP_MAGIC_PGPE:
            return P9_XIP_SECTION_NAME(g_sectionNamesPgpe, index);

        case P9_XIP_MAGIC_IOPPE:
            return P9_XIP_SECTION_NAME(g_sectionNamesIoppe, index);

        case P9_XIP_MAGIC_FPPE:
            return P9_XIP_SECTION_NAME(g_sectionNamesFppe, index);
    }

    return "";
}

// Determine index of section given by its name in section table

static inline int get_sectionId(uint64_t i_magic, const char* i_sectionName)
{
    int i;

    for (i = 0; i < P9_XIP_SECTIONS; i++)
        if (strcmp(i_sectionName, get_sectionName(i_magic, i)) == 0)
        {
            return i;
        }

    return -1;
}

// Normalize a P9-XIP image.  We normalize a copy of the image first so that
// the original image will be available for debugging in case the
// normalization fails, then validate and copy the normalized image back to
// the mmap()-ed file.

static int
normalize(void* io_image, const int i_argc, const char** i_argv, uint32_t i_maskIgnores)
{
    int rc;
    void* copy;

    do
    {

        // The 'normalize' command takes no arguments

        if (i_argc != 0)
        {
            fprintf(stderr, g_usage);
            exit(1);
        }

        copy = malloc(g_imageSize);

        if (copy == 0)
        {
            perror("malloc() failed : ");
            exit(1);
        }

        memcpy(copy, io_image, g_imageSize);

        rc = p9_xip_normalize(copy);

        if (rc)
        {
            break;
        }

        if ( !(i_maskIgnores & P9_XIP_IGNORE_ALL) )
        {
            rc = p9_xip_validate2(copy, g_imageSize, i_maskIgnores);
        }

        if (rc)
        {
            break;
        }

        memcpy(io_image, copy, g_imageSize);

    }
    while (0);

    return rc;
}


// Print a line of attribute report, listing the symbol, type and current
// value.
static int
attrListing(const P9XipItem* i_item, const char* prefix)
{
    int rc = 0;
    uint64_t data = 0;
    uint32_t i;
    char name[42];

    if (i_item->iv_address == 0)
    {
        //TOC item not present in fixed section
        return rc;
    }

    for (i = 0; i < i_item->iv_elements; i++)
    {
        rc = p9_xip_get_item(i_item, &data, i);

        if (rc)
        {
            return rc;
        }

        if (i_item->iv_elements > 1)
        {
            snprintf(name, sizeof(name), "%s[%d]", i_item->iv_id, i);
        }
        else
        {
            strncpy(name, i_item->iv_id, sizeof(name));
        }

        printf("%s%-42s | %s | ", prefix, name,
               P9_XIP_TYPE_STRING(g_typeAbbrevs, i_item->iv_type));

        switch (i_item->iv_type)
        {
            case P9_XIP_UINT8:
                printf("0x%02x", (uint8_t)data);
                break;

            case P9_XIP_UINT16:
                printf("0x%04x", (uint16_t)data);
                break;

            case P9_XIP_UINT32:
                printf("0x%08x", (uint32_t)data);
                break;

            case P9_XIP_UINT64:
                printf("0x%016lx", data);
                break;

            case P9_XIP_INT8:
                printf("0x%02x", (uint8_t)data);
                break;

            case P9_XIP_INT16:
                printf("0x%04x", (uint16_t)data);
                break;

            case P9_XIP_INT32:
                printf("0x%08x", (uint32_t)data);
                break;

            case P9_XIP_INT64:
                printf("0x%016lx", data);
                break;

            case P9_XIP_STRING:
                printf("%s", (char*)(i_item->iv_imageData));
                break;

            case P9_XIP_ADDRESS:
                printf("0x%04x:0x%08x",
                       (uint16_t)((data >> 32) & 0xffff),
                       (uint32_t)(data & 0xffffffff));
                break;

            default:
                printf("unknown type\n");
                rc = P9_XIP_BUG;
                break;
        }

        printf("\n");
    }

    return rc;
}


// Print a line of a report, listing the index, symbol, type and current
// value.

static int
tocListing(void* i_image,
           const P9XipItem* i_item,
           void* arg)
{
    P9XipItem item;
    int rc = 0;
    ReportControl* control = (ReportControl*)arg;
    char prefix[10];

    do
    {
        if (control->regex)
        {
            if (regexec(&(control->preg), i_item->iv_id, 0, 0, 0))
            {
                break;
            }
        }

        rc = p9_xip_find(i_image, i_item->iv_id, &item);

        if (!rc)
        {
            snprintf(prefix, sizeof(prefix), "0x%04x | ", control->index);
            attrListing(&item, prefix);
        }
    }
    while (0);

    control->index += 1;
    return rc;
}


/// Function: resolve_image_section_type
///
/// Brief: Resolves the type of the input image section, e.g. XIP image.
///
/// \param[in]  i_image             A pointer to the image section.
///
/// \param[out] o_imageSectionType  The resolved image section type.
///
static void
resolve_image_section_type( const void* i_image,
                            image_section_type_t& o_imageSectionType )
{

    if (be64toh(((P9XipHeader*)i_image)->iv_magic) >> 32 == P9_XIP_MAGIC)
    {
        o_imageSectionType = IST_XIP;
    }

#ifndef __PPE__
    else if (be32toh(((TorHeader_t*)i_image)->magic) >> 8 == TOR_MAGIC)
    {
        o_imageSectionType = IST_TOR;
    }
    else if (be32toh(((DdContHeader_t*)i_image)->iv_magic) == DD_CONTAINER_MAGIC)
    {
        o_imageSectionType = IST_DDCO;
    }

#endif
    else
    {
        o_imageSectionType = IST_UNDEFINED;
    }

    return;
}


// Dump the image header, including the section table

static void
dumpHeader(void* i_image, image_section_type_t i_imageSectionType)
{
    int i;
    P9XipHeader header;
    P9XipSection* section;
#ifndef __PPE__
    TorHeader_t* torHeader = (TorHeader_t*)i_image;
    DdContHeader_t* ddContHeader = (DdContHeader_t*)i_image;
#endif
    char magicString[9];

    switch (i_imageSectionType)
    {

        case IST_XIP:

            // Dump header information. Since the TOC may not exist we need to get
            // the information from the header explicitly.

            p9_xip_translate_header(&header, (P9XipHeader*)i_image);

            memcpy(magicString, (char*)(&(((P9XipHeader*)i_image)->iv_magic)), 8);
            magicString[8] = 0;

            printf("Image section type : 0x%02x \"XIP image\"\n", i_imageSectionType);
            printf("Magic number       : 0x%016lx \"%s\"\n",
                   header.iv_magic, magicString);
            printf("Header version     : 0x%02x\n", header.iv_headerVersion);
            printf("Link Address       : 0x%016lx\n", header.iv_linkAddress);
            printf("L1 Loader Address  : 0x%08x\n", (uint32_t)header.iv_L1LoaderAddr);
            printf("L2 Loader Address  : 0x%08x\n", (uint32_t)header.iv_L2LoaderAddr);
            printf("Kernel Address     : 0x%08x\n", (uint32_t)header.iv_kernelAddr);
            printf("Image size         : 0x%08x (%d)\n",
                   header.iv_imageSize, header.iv_imageSize);
            printf("Normalized         : %s\n", header.iv_normalized ? "Yes" : "No");
            printf("TOC Sorted         : %s\n", header.iv_tocSorted ? "Yes" : "No");
            printf("Build Date         : %02d/%02d/%04d\n",
                   (header.iv_buildDate / 100) % 100,
                   header.iv_buildDate % 100,
                   header.iv_buildDate / 10000);
            printf("Build Time         : %02d:%02d\n",
                   header.iv_buildTime / 100,
                   header.iv_buildTime % 100);
            printf("Build User         : %s\n", header.iv_buildUser);
            printf("Build Host         : %s\n", header.iv_buildHost);
            printf("Build Tag          : %s\n", header.iv_buildTag);
            printf("\n");

            printf("Section Table      :\n\n");
            printf("    Name            Align   DD   Start        End          Size\n");
            printf("\n");

            for (i = 0; i < P9_XIP_SECTIONS; i++)
            {
                section = &(header.iv_section[i]);
                printf("   %-16s %d       %d    0x%08x   ",
                       get_sectionName(header.iv_magic, i),
                       section->iv_alignment,
                       section->iv_ddSupport,
                       section->iv_offset);

                if (section->iv_size == 0)
                {
                    printf("          ");
                }
                else
                {
                    printf("0x%08x", section->iv_offset + section->iv_size - 1);
                }

                printf("   0x%08x (%d)\n", section->iv_size, section->iv_size);
            }

            break;

#ifndef __PPE__

        case IST_TOR:

            memcpy(magicString, (char*) & (torHeader->magic), 4);
            magicString[4] = 0;

            printf("Image section type : 0x%02x \"TOR ring section\"\n", i_imageSectionType);
            printf("Magic number       : 0x%08x \"%s\"\n",
                   be32toh(torHeader->magic), magicString);
            printf("Header version     : 0x%02x\n", torHeader->version);
            printf("Chip type          : 0x%02x \"%s\"\n",
                   torHeader->chipType, CHIP_TYPE_LIST[torHeader->chipType].name);
            printf("DD level           : 0x%02x\n", torHeader->ddLevel);
            printf("Number DD levels   : %d\n", torHeader->numDdLevels);
            printf("Image size         : 0x%08x (%d)\n",
                   be32toh(torHeader->size), be32toh(torHeader->size));

            break;

        case IST_DDCO:

            memcpy(magicString, (char*) & (ddContHeader->iv_magic), 4);
            magicString[4] = 0;

            printf("Image section type : 0x%02x \"DDCO image section\"\n", i_imageSectionType);
            printf("Magic number       : 0x%08x \"%s\"\n",
                   be32toh(ddContHeader->iv_magic), magicString);
            printf("Number DD levels   : %d\n", ddContHeader->iv_num);

            break;
#endif

        default:

            fprintf(stderr,
                    "\nERROR: In dumpHeader(): Invalid image section type (=%d)\n",
                    i_imageSectionType);
            uint64_t l_magic = *((uint64_t*)i_image);
            fprintf(stderr, "Here's the first 8 bytes of the image section (LE): 0x%016lx ",
                    be64toh(l_magic));
            fprintf(stderr, "\"%c%c%c%c%c%c%c%c\"\n",
                    *(((uint8_t*)&l_magic) + 0),
                    *(((uint8_t*)&l_magic) + 1),
                    *(((uint8_t*)&l_magic) + 2),
                    *(((uint8_t*)&l_magic) + 3),
                    *(((uint8_t*)&l_magic) + 4),
                    *(((uint8_t*)&l_magic) + 5),
                    *(((uint8_t*)&l_magic) + 6),
                    *(((uint8_t*)&l_magic) + 7));
            fprintf(stderr,
                    "If you're seeing \"TOR\" or \"DDCO\" in the first 8 bytes, you're probably using the PPE version of p9_xip_tool. Use the EKB version instead!\n");
            exit(EXIT_FAILURE);

    }

    return;
}


// Print a report

static int
report(void*                      io_image,
       const int                  i_argc,
       const char**               i_argv,
       const image_section_type_t i_imageSectionType)
{
    int rc = INFRASTRUCT_RC_SUCCESS;
    ReportControl control;
    char errbuf[ERRBUF_SIZE];

    do
    {

        if (i_imageSectionType == IST_XIP)
        {

            // Basic syntax check : [<regexp>]

            if (i_argc > 1)
            {
                fprintf(stderr, g_usage);
                exit(1);
            }

            // Compile a regular expression if supplied

            if (i_argc == 1)
            {
                rc = regcomp(&(control.preg), i_argv[0], REG_NOSUB);

                if (rc)
                {
                    regerror(rc, &(control.preg), errbuf, ERRBUF_SIZE);
                    fprintf(stderr, "Error from regcomp() : %s\n", errbuf);
                    exit(1);
                }

                control.regex = 1;
            }
            else
            {
                control.regex = 0;

                dumpHeader(io_image, IST_XIP);
                printf("\nTOC Report\n\n");
            }

            // Map the TOC with the mapReport() function

            control.index = 0;
            rc = p9_xip_map_toc(io_image, tocListing, (void*)(&control));

            if (rc)
            {
                break;
            }
        }
        else
        {
            dumpHeader(io_image, i_imageSectionType);
            printf("\n");
        }

    }
    while (0);

    return rc;
}

//Print attributes from dump image
static int
reportAttr(void* io_image, size_t i_imageSize, void* io_dump)
{
    int rc = 0;
    P9XipToc* imageToc = NULL;
    P9XipItem item = {0};
    size_t entries = 0;

    //check for seeprom image validity
    rc = p9_xip_validate(io_image, i_imageSize);

    if (rc)
    {
        return rc;
    }

    //get toc listing from seeprom image
    rc = p9_xip_get_toc(io_image, &imageToc, &entries, 0, 0);

    if (rc)
    {
        return rc;
    }

    //loop through each toc listing and print its value from pibmem dump
    for (; entries--; imageToc++)
    {
        rc = p9_xip_decode_toc_dump(io_image, io_dump, imageToc, &item);

        if (!rc)
        {
            //helper function to print the attributes
            attrListing(&item, "");
        }
    }

    return rc;
}

// Set a scalar or vector element values in the image.  The 'i_setv' argument
// indicates set/setv (0/1).

static int
set(void* io_image, const int i_argc, const char** i_argv, int i_setv)
{
    int rc = P9_XIP_BUG, arg, base, clause_args, index_val;
    P9XipItem item;
    unsigned long long newValue;
    const char* key, *index, *value;
    char* endptr;

    do
    {

        // Basic syntax check: <item> <value> [ <item1> <value1> ... ]
        // Basic syntax check: <item> <index> <value> [ <item1> <index1> <value1> ... ]

        clause_args = (i_setv ? 3 : 2);

        if ((i_argc % clause_args) != 0)
        {
            fprintf(stderr, g_usage);
            exit(1);
        }

        for (arg = 0; arg < i_argc; arg += clause_args)
        {

            key = i_argv[arg];

            if (i_setv)
            {
                index = i_argv[arg + 1];
                index_val = strtol(index, 0, 0);
                value = i_argv[arg + 2];
            }
            else
            {
                index = "";
                index_val = 0;
                value = i_argv[arg + 1];
            }

            // Search for the item to see what type of data it expects, then
            // case split on the type.

            rc = p9_xip_find(io_image, key, &item);

            if (rc)
            {
                break;
            }

            if (index_val < 0)
            {
                fprintf(stderr,
                        "Illegal negative vector index %s for %s\n",
                        index, key);
                exit(1);
            }
            else if ((item.iv_elements != 0) &&
                     (index_val >= item.iv_elements))
            {
                fprintf(stderr,
                        "Index %s out-of-bounds for %s (%d elements)\n",
                        index, key, item.iv_elements);
                exit(1);
            }

            switch (item.iv_type)
            {
                case P9_XIP_UINT8:
                case P9_XIP_UINT16:
                case P9_XIP_UINT32:
                case P9_XIP_UINT64:

                    // We need to do a bit of preprocessing on the string to
                    // determine its format and set the base for strtoull(),
                    // otherwise strtoull() will be confused by leading zeros
                    // e.g. in time strings generated by `date +%H%M`, and try to
                    // process the string as octal.

                    if ((strlen(value) >= 2) && (value[0] == '0') &&
                        ((value[1] == 'x') || (value[1] == 'X')))
                    {
                        base = 16;
                    }
                    else
                    {
                        base = 10;
                    }

                    errno = 0;
                    newValue = strtoull(value, &endptr, base);

                    if ((errno != 0) || (endptr != (value + strlen(value))))
                    {
                        fprintf(stderr,
                                "Error parsing putative integer value : %s\n",
                                value);
                        exit(1);
                    }

                    switch (item.iv_type)
                    {

                        case P9_XIP_UINT8:
                            if ((uint8_t)newValue != newValue)
                            {
                                fprintf(stderr,
                                        "Value 0x%016llx too large for 8-bit type\n",
                                        newValue);
                                exit(1);
                            }

                            break;

                        case P9_XIP_UINT16:
                            if ((uint16_t)newValue != newValue)
                            {
                                fprintf(stderr,
                                        "Value 0x%016llx too large for 16-bit type\n",
                                        newValue);
                                exit(1);
                            }

                            break;

                        case P9_XIP_UINT32:
                            if ((uint32_t)newValue != newValue)
                            {
                                fprintf(stderr,
                                        "Value 0x%016llx too large for 32-bit type\n",
                                        newValue);
                                exit(1);
                            }

                            break;

                        case P9_XIP_UINT64:
                            break;

                        default:
                            break;
                    }

                    rc = p9_xip_set_element(io_image, key, index_val, newValue);

                    if (rc)
                    {
                        rc = P9_XIP_BUG;
                    }

                    break;

                case P9_XIP_STRING:

                    if (i_setv)
                    {
                        fprintf(stderr, "Can't use 'setv' for string data %s\n",
                                key);
                        exit(1);
                    }

                    rc = p9_xip_set_string(io_image, key, (char*)value);

                    if (rc)
                    {
                        rc = P9_XIP_BUG;
                    }

                    break;

                case P9_XIP_INT8:
                case P9_XIP_INT16:
                case P9_XIP_INT32:
                case P9_XIP_INT64:
                    fprintf(stderr,
                            "Item %s has int type %s, "
                            "which is not supported for '%s'.\n",
                            i_argv[arg],
                            P9_XIP_TYPE_STRING(g_typeStrings, item.iv_type),
                            (i_setv ? "setv" : "set"));
                    exit(1);
                    break;

                default:
                    fprintf(stderr,
                            "Item %s has type %s, "
                            "which is not supported for '%s'.\n",
                            i_argv[arg],
                            P9_XIP_TYPE_STRING(g_typeStrings, item.iv_type),
                            (i_setv ? "setv" : "set"));
                    exit(1);
                    break;
            }

            if (rc)
            {
                break;
            }

        }
    }
    while (0);

    //if good rc, we need to msync the mmaped file to push contents to
    //the actual file. Per man page this is required although some
    //file systems (notably AFS) don't seem to require (GSA does)
    if(!rc)
    {
        uint8_t i = 0;

        do
        {
            rc = msync(io_image, g_imageSize , MS_SYNC);

            if(rc)
            {
                i++;
                fprintf(stderr,
                        "msync failed with errno %d\n", errno);
            }
        }
        while(rc && i < 5);

        if(rc)
        {
            exit(3);
        }
    }

    return rc;
}


// Get a value from the image, and return on stdout.  The 'i_getv' argument
// indicates get/getv (0/1)

static int
get(void* i_image, const int i_argc, const char** i_argv, int i_getv)
{
    int rc, nargs, index_val;
    P9XipItem item;
    const char* key, *index;
    uint64_t data;
    char* s;

    do
    {

        // Basic syntax check: <item>
        // Basic syntax check: <item> <index>

        nargs = (i_getv ? 2 : 1);

        if (i_argc != nargs)
        {
            fprintf(stderr, g_usage);
            exit(1);
        }

        key = i_argv[0];

        if (i_getv)
        {
            index = i_argv[1];
            index_val = strtol(index, 0, 0);
        }
        else
        {
            index = "";
            index_val = 0;
        }

        // Search for the item to determine its type, then case split on the
        // type.

        rc = p9_xip_find(i_image, key, &item);

        if (rc)
        {
            break;
        }

        if (index_val < 0)
        {
            fprintf(stderr,
                    "Illegal negative vector index %s for %s\n",
                    index, key);
            exit(1);
        }
        else if ((item.iv_elements != 0) &&
                 (index_val >= item.iv_elements))
        {
            fprintf(stderr, "Index %s out-of-bounds for %s (%d elements)\n",
                    index, key, item.iv_elements);
            exit(1);
        }

        switch (item.iv_type)
        {

            case P9_XIP_UINT8:
            case P9_XIP_UINT16:
            case P9_XIP_UINT32:
            case P9_XIP_UINT64:
                rc = p9_xip_get_element(i_image, key, index_val, &data);

                if (rc)
                {
                    rc = P9_XIP_BUG;
                    break;
                }

                switch (item.iv_type)
                {
                    case P9_XIP_UINT8:
                        printf("0x%02x\n", (uint8_t)data);
                        break;

                    case P9_XIP_UINT16:
                        printf("0x%04x\n", (uint16_t)data);
                        break;

                    case P9_XIP_UINT32:
                        printf("0x%08x\n", (uint32_t)data);
                        break;

                    case P9_XIP_UINT64:
                        printf("0x%016lx\n", data);
                        break;

                    default:
                        break;
                }

                break;

            case P9_XIP_ADDRESS:
                if (i_getv)
                {
                    fprintf(stderr, "Can't use 'getv' for address data : %s\n",
                            key);
                    exit(1);
                }

                rc = p9_xip_get_scalar(i_image, key, &data);

                if (rc)
                {
                    rc = P9_XIP_BUG;
                    break;
                }

                printf("0x%012lx\n", data);
                break;

            case P9_XIP_STRING:
                if (i_getv)
                {
                    fprintf(stderr, "Can't use 'getv' for string data : %s\n",
                            key);
                    exit(1);
                }

                rc = p9_xip_get_string(i_image, key, &s);

                if (rc)
                {
                    rc = P9_XIP_BUG;
                    break;
                }

                printf("%s\n", s);
                break;

            default:
                fprintf(stderr, "%s%d : Bug, unexpected type %d\n",
                        __FILE__, __LINE__, item.iv_type);
                exit(1);
                break;
        }
    }
    while (0);

    return rc;
}


// Append a file to section
static int
append(const char* i_imageFile, const int i_imageFd, void* io_image,
       int i_argc, const char** i_argv)
{
    uint8_t i_ddSupport = 0;
    int fileFd, newImageFd, sectionId, rc;
    struct stat buf;
    const char* section;
    const char* file;
    void* appendImage;
    void* newImage;
    uint32_t size, newSize, sectionOffset;
    uint64_t homerAddress;
    P9XipHeader header;

    do
    {

        // Basic syntax check: <section> <file>

        if (i_argc != 2 && i_argc != 3)
        {
            fprintf(stderr, g_usage);
            exit(1);
        }

        section = i_argv[0];
        file = i_argv[1];

        if (i_argc == 3)
        {
            if (strcmp(i_argv[2], "1") == 0)
            {
                i_ddSupport = 1;
            }
            else if (strcmp(i_argv[2], "0") == 0)
            {
                i_ddSupport = 0;
            }
            else
            {
                fprintf(stderr, g_usage);
                exit(1);
            }
        }

        p9_xip_translate_header(&header, (P9XipHeader*)io_image);

        // Translate the section name to a section Id
        sectionId = get_sectionId(header.iv_magic, section);

        if (sectionId < 0)
        {
            fprintf(stderr, "Unrecognized section name : '%s;\n", section);
            exit(1);
        }

        // Open and mmap the file to be appended

        fileFd = open(file, O_RDONLY);

        if (fileFd < 0)
        {
            perror("open() of the file to be appended failed : ");
            exit(1);
        }

        rc = fstat(fileFd, &buf);

        if (rc)
        {
            perror("fstat() of the file to be appended failed : ");
            exit(1);
        }

        appendImage = mmap(0, buf.st_size, PROT_READ, MAP_SHARED, fileFd, 0);

        if (appendImage == MAP_FAILED)
        {
            perror("mmap() of the file to be appended failed : ");
            exit(1);
        }


        // malloc() a buffer for the new image, adding space for alignment

        rc = p9_xip_image_size(io_image, &size);

        if (rc)
        {
            break;
        }

        newSize = size + buf.st_size + P9_XIP_MAX_SECTION_ALIGNMENT;

        newImage = malloc(newSize);

        if (newImage == 0)
        {
            fprintf(stderr, "Can't malloc() a buffer for the new image\n");
            exit(1);
        }


        // Copy the image.  At this point the original image file must be
        // closed.

        memcpy(newImage, io_image, size);

        rc = close(i_imageFd);

        if (rc)
        {
            perror("close() of the original image file failed : ");
            exit(1);
        }


        // Do the append and print the image address where the data was loaded.
        // We will not fail for unaligned addresses, as we have no knowledge
        // of whether or why the user wants the final image address.

        rc = p9_xip_append(newImage,
                           sectionId,
                           appendImage,
                           buf.st_size,
                           newSize,
                           &sectionOffset,
                           i_ddSupport);

        if (rc)
        {
            break;
        }

        rc = p9_xip_section2image(newImage, sectionId, sectionOffset,
                                  &homerAddress);

        if (rc && (rc != P9_XIP_ALIGNMENT_ERROR))
        {
            break;
        }

        printf("0x%016lx\n", homerAddress);


        // Now write the new image back to the filesystem

        newImageFd = open(i_imageFile, O_WRONLY | O_TRUNC);

        if (newImageFd < 0)
        {
            perror("re-open() of image file failed : ");
            exit(1);
        }

        rc = p9_xip_image_size(newImage, &size);

        if (rc)
        {
            break;
        }

        rc = write(newImageFd, newImage, size);

        if ((rc < 0) || ((uint32_t)rc != size))
        {
            perror("write() of modified image failed : ");
            exit(1);
        }

        rc = close(newImageFd);

        if (rc)
        {
            perror("close() of modified image failed : ");
            exit(1);
        }
    }
    while (0);

    return rc;
}

// Extract section from an image incl a DD-specific sub-section within an XIP section.
static int
extract(const char* i_imageFile, const int i_imageFd, void* io_image,
        int i_argc, const char** i_argv)
{
    int rc = 0;
    const char* i_sectionName;  //Direct copy of input arg, thus i_
    const char* i_fileName;     //Same
    std::string i_ddLevelStr;   //Same
    uint8_t ddLevel = P9_XIP_UNDEFINED_DDLEVEL;
    bool bDdSuppExpected = false;
    int fileFd, sectionId;
    void* newImage;
    P9XipHeader header;
    P9XipSection* xSection;   // XIP section of i_section
    P9XipSection  xDdSection; // Extracted XIP (self) or Dd section of i_section

    do
    {

        if (i_argc != 2 && i_argc != 3)
        {
            fprintf(stderr, g_usage);
            exit(1);
        }

        i_sectionName = i_argv[0];
        i_fileName = i_argv[1];

        p9_xip_translate_header(&header, (P9XipHeader*)io_image);
        sectionId = get_sectionId(header.iv_magic, i_sectionName);

        if (sectionId < 0)
        {
            fprintf(stderr, "\nUnrecognized section name : '%s;\n", i_sectionName);
            exit(1);
        }

        xSection = &(header.iv_section[sectionId]);

        printf("\nInput parms to the \"extract\" command:\n"\
               "  Section:      %s\n"\
               "  Output file:  %s\n",
               i_sectionName, i_fileName);

        ddLevel = P9_XIP_UNDEFINED_DDLEVEL;
        bDdSuppExpected = false;

        if (i_argc == 3)
        {
            i_ddLevelStr = i_argv[2];

            bDdSuppExpected = true;

            printf("  DD level (input): %s\n",
                   i_ddLevelStr.c_str());
        }

        if (bDdSuppExpected)
        {
            if (xSection->iv_ddSupport)
            {
                if (i_ddLevelStr.size() != 2 && i_ddLevelStr.size() != 4)
                {
                    fprintf(stderr, "\nThe specified ddLevel \"%s\" has an unsupported format.\n",
                            i_ddLevelStr.c_str());
                    fprintf(stderr, "Specify ddLevel in hex format, e.g. \"0x10\" or \"10\"\n\n");
                    exit(1);
                }

                ddLevel = strtol(i_ddLevelStr.c_str(), NULL, 16);

                printf("  DD level (hex converted): 0x%x\n",
                       ddLevel);
            }
            else
            {
                fprintf(stderr, "\nThe section \"%s\" has no DD level support.\n", i_sectionName);
                fprintf(stderr, "To extract the entire section, omit the \"ddLevel\" arg.\n\n");
                exit(1);
            }
        }

        printf("\nThe specified XIP section has the following attributes:\n"\
               "  Offset:       0x%08x\n"\
               "  Size (size):  0x%08x (%d)\n"\
               "  DD support:   %d\n",
               xSection->iv_offset, xSection->iv_size, xSection->iv_size, xSection->iv_ddSupport);

        if (ddLevel == 0)
        {
            ddLevel = P9_XIP_UNDEFINED_DDLEVEL;
            // Even though this may seem like we should just exit here, we'll leave it up
            // to xip_get_section what to do in this case. Who knows, maybe it'll eventually
            // return a list of supported DD levels.
        }

#ifdef __PPE__
        rc = p9_xip_get_section( io_image, sectionId, &xDdSection);
#else
        rc = p9_xip_get_section( io_image, sectionId, &xDdSection, ddLevel);
#endif

        switch (rc)
        {
            case 0:
                break;

            case P9_XIP_NO_DDLEVEL_SUPPORT:
                fprintf(stderr, "\nThere is no support for DD level extraction yet.\n");
                fprintf(stderr, "To extract the entire section, omit the \"ddLevel\" arg.\n\n");
                exit(1);

            case P9_XIP_DDLEVEL_NOT_FOUND:
                fprintf(stderr, "\nA sub-section w/the specified ddLevel (=0x%x) was not found.\n", ddLevel);
                fprintf(stderr, "To extract the entire section, omit the \"ddLevel\" arg.\n\n");
                exit(1);

            default:
                fprintf(stderr, "\np9_xip_get_section() failed w/rc = %d\n\n", rc);
                exit(1);
        }

        newImage = malloc(xSection->iv_size);

        if (newImage == 0)
        {
            fprintf(stderr, "\nCan't malloc() a buffer for the new image\n");
            exit(1);
        }

        memcpy( newImage, (void*)((uint64_t)io_image + xDdSection.iv_offset), xDdSection.iv_size);

        fileFd = open(i_fileName, O_CREAT | O_WRONLY | O_TRUNC, 0755);

        if (fileFd < 0)
        {
            perror("open() of the fixed section : ");
            exit(1);
        }

        rc = write(fileFd, newImage, xDdSection.iv_size);

        if ((rc < 0) || ((uint32_t)rc != xDdSection.iv_size))
        {
            perror("write() of fixed section : ");
            exit(1);
        }

        rc = close(fileFd);

        if (rc)
        {
            perror("close() of fixed section : ");
            exit(1);
        }

    }
    while (0);

    return rc;

}


// Delete 0 or more sections in order.

static int
deleteSection(const char* i_imageFile, const int i_imageFd, void* io_image,
              int i_argc, const char** i_argv)
{
    int newImageFd, sectionId, rc, argc;
    const char* section;
    const char** argv;
    void* newImage;
    void* tempImage;
    uint32_t size;
    P9XipHeader header;

    do
    {

        // malloc() a buffer for the new image

        rc = p9_xip_image_size(io_image, &size);

        if (rc)
        {
            break;
        }

        newImage = malloc(size);

        if (newImage == 0)
        {
            fprintf(stderr, "Can't malloc() a buffer for the new image\n");
            exit(1);
        }


        // Copy the image. At this point the original image file must be
        // closed.

        memcpy(newImage, io_image, size);

        rc = close(i_imageFd);

        if (rc)
        {
            perror("close() of the original image file failed : ");
            exit(1);
        }

        // Create a temporary place holder for image

        tempImage = malloc(size);

        if (tempImage == 0)
        {
            fprintf(stderr, "Can't malloc() a buffer for the temporary image\n");
            exit(1);
        }


        p9_xip_translate_header(&header, (P9XipHeader*)io_image);

        // Delete the sections in argument order

        for (argc = i_argc, argv = i_argv; argc != 0; argc--, argv++)
        {

            // Translate the section name to a section Id

            section = *argv;

            sectionId = get_sectionId(header.iv_magic, section);

            if (sectionId < 0)
            {
                fprintf(stderr, "Unrecognized section name : '%s;\n", section);
                exit(1);
            }

            // Delete the section

            rc = p9_xip_delete_section(newImage, tempImage, size, sectionId);

            if (rc)
            {
                break;
            }
        }

        if (rc)
        {
            break;
        }

        // Print the final size of the new image

        rc = p9_xip_image_size(newImage, &size);

        if (rc)
        {
            break;
        }

        printf("%u\n", size);

        // Now write the new image back to the filesystem

        newImageFd = open(i_imageFile, O_WRONLY | O_TRUNC);

        if (newImageFd < 0)
        {
            perror("re-open() of image file failed : ");
            exit(1);
        }

        rc = write(newImageFd, newImage, size);

        if ((rc < 0) || ((uint32_t)rc != size))
        {
            perror("write() of modified image failed : ");
            exit(1);
        }

        rc = close(newImageFd);

        if (rc)
        {
            perror("close() of modified image failed : ");
            exit(1);
        }
    }
    while (0);

    return rc;
}


// 'TEST' is an undocumented command provided to test the APIs.  It searches
// and modifies a copy of the image but puts the image back together as it
// was, then verifies that the the original image and the copy are identical.

#define BOMB_IF(test)                                   \
    if (test) {                                         \
        fprintf(stderr, "%s:%d : Error in TEST\n",      \
                __FILE__, __LINE__);                    \
        exit(1);                                        \
    }

#define BOMB_IF_RC                                              \
    if (rc) {                                                   \
        fprintf(stderr, "%s:%d : Error in TEST, rc = %s\n",     \
                __FILE__, __LINE__,                             \
                P9_XIP_ERROR_STRING(g_errorStrings, rc));      \
        exit(1);                                                \
    }


static int
TEST(void* io_image, const int i_argc, const char** i_argv)
{
    int rc;
    uint64_t linkAddress, entryPoint, data, data1, magicKey, L1_LoaderAddr[2];
    char* key, *revision, *revdup, *longString, *shortString;
    void* originalImage;
    uint32_t imageSize;
    P9XipItem item;
    P9XipHeader header;
    P9XipSection section;
    //ProcSbeFixed* fixed;
    uint32_t tocSize;

    do
    {
        rc = p9_xip_image_size(io_image, &imageSize);
        BOMB_IF_RC;
        originalImage = malloc(imageSize);
        BOMB_IF(originalImage == 0);
        memcpy(originalImage, io_image, imageSize);

        rc = p9_xip_get_scalar(io_image, "toc_sorted", &data);
        BOMB_IF_RC;
        BOMB_IF(data != 1);

        rc = p9_xip_get_scalar(io_image, "image_size", &data);
        BOMB_IF_RC;
        BOMB_IF(data != (uint64_t)g_imageSize);

        rc = p9_xip_get_scalar(io_image, "magic", &magicKey);
        BOMB_IF_RC;

        switch (magicKey)
        {
            case P9_XIP_MAGIC_BASE:
                key = (char*)"proc_p9_fabricinit_revision";
                rc = p9_xip_get_string(io_image, key, &revision);
                BOMB_IF_RC;
                BOMB_IF(strncmp(revision, "1.", 2) != 0);
                break;

            case P9_XIP_MAGIC_SEEPROM:
                key = (char*)"";
                // Can't do this test here as the TOC has been stripped
                break;

            case P9_XIP_MAGIC_CENTAUR:
                key = (char*)"cen_p9_initf_revision";
                rc = p9_xip_get_string(io_image, key, &revision);
                BOMB_IF_RC;
                BOMB_IF(strncmp(revision, "1.", 2) != 0);
                break;

            default:
                BOMB_IF(1);
                break;
        }

        rc = p9_xip_get_scalar(io_image, "link_address", &linkAddress);
        BOMB_IF_RC;

        if (magicKey != P9_XIP_MAGIC_SEEPROM)
        {
            rc = p9_xip_get_scalar(io_image, "entry_point", &entryPoint);
            BOMB_IF_RC;
        }

        rc = p9_xip_get_scalar(io_image, "L1_LoaderAddr", &data);
        BOMB_IF_RC;
        BOMB_IF((magicKey != P9_XIP_MAGIC_SEEPROM) && (entryPoint != (linkAddress + data)));

        rc =
            p9_xip_set_scalar(io_image, "toc_sorted", 0) ||
            p9_xip_set_scalar(io_image, "image_size", 0);
        BOMB_IF_RC;

        data = 0;
        data += (rc = p9_xip_get_scalar(io_image, "toc_sorted", &data), data);
        BOMB_IF_RC;
        data += (rc = p9_xip_get_scalar(io_image, "image_size", &data), data);
        BOMB_IF_RC;
        BOMB_IF(data != 0);

        // Write back keys found during read check.

        rc =
            p9_xip_set_scalar(io_image, "toc_sorted", 1) ||
            p9_xip_set_scalar(io_image, "image_size", g_imageSize);
        BOMB_IF_RC;

        // We'll rewrite the revision keyword with a long string and a short
        // string, and verify that rewriting is being done correctly.  In the
        // end we copy the original revision string back in, which is safe
        // because the memory allocation for strings does not change when they
        // are modified.

        revdup = strdup(revision);
        longString = (char*)"A very long string";
        shortString = (char*)"?";

        if (magicKey != P9_XIP_MAGIC_SEEPROM)
        {
            rc =
                p9_xip_set_string(io_image, key, longString) ||
                p9_xip_get_string(io_image, key, &revision);
            BOMB_IF_RC;
            BOMB_IF((strlen(revision) != strlen(revdup)) ||
                    (strncmp(revision, longString, strlen(revdup)) != 0));

            rc =
                p9_xip_set_string(io_image, key, shortString) ||
                p9_xip_get_string(io_image, key, &revision);
            BOMB_IF_RC;
            BOMB_IF(strcmp(revision, shortString) != 0);

            memcpy(revision, revdup, strlen(revdup) + 1);
        }

        // Use p9_xip_[read,write]_uint64 to modify the image and restore it
        // to its original form.

        rc = p9_xip_find(io_image, "L1_LoaderAddr", &item);
        BOMB_IF_RC;
        rc = p9_xip_get_scalar(io_image, "L1_LoaderAddr", &(L1_LoaderAddr[0]));
        BOMB_IF_RC;

        rc = p9_xip_read_uint64(io_image, item.iv_address, &(L1_LoaderAddr[1]));
        BOMB_IF_RC;
        BOMB_IF(L1_LoaderAddr[0] != L1_LoaderAddr[1]);

        rc = p9_xip_write_uint64(io_image, item.iv_address,
                                 0xdeadbeefdeadc0deull);
        BOMB_IF_RC;
        rc = p9_xip_read_uint64(io_image, item.iv_address, &(L1_LoaderAddr[1]));
        BOMB_IF_RC;
        BOMB_IF(L1_LoaderAddr[1] != 0xdeadbeefdeadc0deull);

        rc = p9_xip_write_uint64(io_image, item.iv_address, L1_LoaderAddr[0]);
        BOMB_IF_RC;

        // Try p9_xip_get_section against the translated header

        p9_xip_translate_header(&header, (P9XipHeader*)io_image);
        rc = p9_xip_get_section(io_image, P9_XIP_SECTION_TOC, &section);
        BOMB_IF_RC;
        BOMB_IF((section.iv_size !=
                 header.iv_section[P9_XIP_SECTION_TOC].iv_size));


        // Make sure the .fixed section access compiles and seems to
        // work. Modify an entry via the .fixed and verify it with normal TOC
        // access.

        if (magicKey == P9_XIP_MAGIC_SEEPROM)
        {

            BOMB_IF(0 != 0);

            exit(1);

            rc = p9_xip_get_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                                   &data);
            BOMB_IF_RC;
            //fixed =
            //(ProcSbeFixed*)((unsigned long)io_image + P9_XIP_FIXED_OFFSET);
            //fixed->proc_p9_ex_dpll_initf_control = 0xdeadbeefdeadc0deull;
            rc = p9_xip_get_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                                   &data1);
            BOMB_IF_RC;
#ifdef _BIG_ENDIAN
            BOMB_IF(data1 != 0xdeadbeefdeadc0deull);
#else
            BOMB_IF(data1 != 0xdec0addeefbeaddeull);
#endif
            rc = p9_xip_set_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                                   data);
            BOMB_IF_RC;
        }

        // Temporarily "delete" the .toc section and try to get/set via the
        // mini-TOC for .fixed, and make sure that we can't get things that
        // are not in the mini-toc.

        tocSize =
            ((P9XipHeader*)io_image)->iv_section[P9_XIP_SECTION_TOC].iv_size;

        ((P9XipHeader*)io_image)->iv_section[P9_XIP_SECTION_TOC].iv_size =
            0;

        rc = p9_xip_get_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                               &data);
        rc = p9_xip_set_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                               0xdeadbeef);
        rc = p9_xip_get_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                               &data1);
        BOMB_IF(data1 != 0xdeadbeef);
        rc = p9_xip_set_scalar(io_image, "proc_p9_ex_dpll_initf_control",
                               data);
        BOMB_IF_RC;

        BOMB_IF(p9_xip_find(io_image, "proc_p9_ex_dpll_initf", 0) !=
                P9_XIP_ITEM_NOT_FOUND);

        ((P9XipHeader*)io_image)->iv_section[P9_XIP_SECTION_TOC].iv_size =
            tocSize;

        if (magicKey != P9_XIP_MAGIC_SEEPROM)
        {
            BOMB_IF(p9_xip_find(io_image, "proc_p9_ex_dpll_initf", 0) != 0);
        }


#ifdef DEBUG_P9_XIP_IMAGE
        printf("\nYou will see an expected warning below "
               "about P9_XIP_WOULD_OVERFLOW\n"
               "It means the TEST is working (not failing)\n\n");
#endif

        // Finally compare against the original

        BOMB_IF(memcmp(io_image, originalImage, imageSize));

    }
    while (0);

    return rc;
}


#ifndef __PPE__  // Needed on the ppe side to avoid TOR API

//@FIXME: This should be improved. Probably defined somewhere else.
#define CHIPLET_ID_MAX            (uint8_t)0x37

/// Function:  dissectRingSectionTor()
///
/// Brief:  Dissects and summarizes content of a ring section.
///
/// \param[in] i_ringSection    A pointer to a TOR compliant ring section.
///
/// \param[in] i_listingModeId  The listing mode: {table, short, normal(default), long}.
///
/// Assumptions:
/// - Dissection only works with .rings section. It does not work with .overrides
///   until we get TOR magic (RTC157744).
///
static
int dissectRingSectionTor( void*       i_ringSection,
                           uint8_t     i_listingModeId )
{
    int         rc = 0;
    uint32_t    i;
    RingId_t    numRingIds = 0;
    uint32_t    torMagic = 0xffffffff; // Undefined value
    ChipType_t  chipType = 0xff; // Undefined value
    uint32_t    numDdLevels = 0; // Undefined value
    uint8_t     iDdLevel, ddLevel = 0xff; // Undefined value
    PpeType_t   ppeType;
    RingId_t    ringId;
    RingType_t  ringType;
    RingVariant_t ringVariant;
    uint8_t     instanceId;
    void*       ringBlockPtr;
    uint32_t    ringBlockSize;
    char        ringName[32];
    uint32_t    ringSeqNo  = 0; // Ring sequence number
    CompressedScanData* rs4;
    CompressedScanData* rs4Stump;
    CompressedScanData* rs4Cmsk;
    CompressedScanData* rs4Print;
    uint8_t* data;
    uint8_t* care;
    uint32_t bits;
    int rs4rc;
    uint16_t ringSize;
    double comprRate;
    uint8_t cmskRingIteration = 0;
    char ringSuffix = ' ';
    uint8_t* buf = (uint8_t*)i_ringSection;
    TorHeader_t* torHeader = (TorHeader_t*)i_ringSection;
    int offset = sizeof(TorHeader_t);
    TorDdBlock_t* torDdBlock;

    //
    // Get TOR magic and DD level info from TOR header field
    //
    torMagic    = be32toh(torHeader->magic);
    chipType    = torHeader->chipType;
    ddLevel     = torHeader->ddLevel;
    numDdLevels = torHeader->numDdLevels;

    //
    // Make some ChipType specific data translations
    //
    switch (chipType)
    {
        case CT_P9N:
            numRingIds = P9_RID::NUM_RING_IDS;
            break;

        case CT_P9C:
            numRingIds = P9_RID::NUM_RING_IDS;
            break;

        case CT_CEN:
            numRingIds = CEN_RID::NUM_RING_IDS;
            break;

        default:
            fprintf(stderr, "ERROR: Invalid chipType(=%d)\n", chipType);
            exit(1);
    }

    fprintf(stdout, "---------------------------------\n");
    fprintf(stdout, "*      TOR header summary       *\n");
    fprintf(stdout, "---------------------------------\n");
    dumpHeader(i_ringSection, IST_TOR);
    fprintf(stdout, "---------------------------------\n");
    fprintf(stdout, "\n\n");

    if (i_listingModeId != LMID_TABLE)
    {
        fprintf( stdout, "-----------------------------\n"
                 "*       Ring summary        *\n");
    }

    //
    // Allocate large buffer to hold max length ring block.
    //
    ringBlockSize = RING_BUF_SIZE_MAX;
    ringBlockPtr = malloc(ringBlockSize);


    if (i_listingModeId == LMID_TABLE)
    {
        fprintf(stdout, "\n   #      DD     PPE     Var    Inst       Bits  Compr  Name\n");
    }

    //----------------
    // DD level loop.
    for (iDdLevel = 0; iDdLevel < numDdLevels; iDdLevel++)
    {
        if (torMagic == TOR_MAGIC_HW)
        {
            torDdBlock = (TorDdBlock_t*)&buf[offset];
            offset += sizeof(TorDdBlock_t);
            ddLevel = torDdBlock->ddLevel;
        }

        //----------------
        // PPE type loop.
        // - SBE, CME, SGPE
        for (ppeType = 0; ppeType < NUM_PPE_TYPES; ppeType++)
        {

            if ((torMagic == TOR_MAGIC_SGPE && ppeType != PT_SGPE) ||
                (torMagic == TOR_MAGIC_CME  && ppeType != PT_CME)  ||
                (torMagic == TOR_MAGIC_SBE  && ppeType != PT_SBE)  ||
                (torMagic == TOR_MAGIC_OVRD && ppeType != PT_SBE)  ||
                (torMagic == TOR_MAGIC_CEN  && ppeType != PT_SBE))
            {
                continue;
            }

            //--------------------
            // Ring variant loop.
            // - Base, cache, risk, override, overlay
            for (ringVariant = 0; ringVariant <= OVERRIDE; ringVariant++)
            {
                if ((torMagic != TOR_MAGIC_OVRD && ringVariant == OVERRIDE) ||
                    (torMagic == TOR_MAGIC_OVRD && ringVariant != OVERRIDE) ||
                    (torMagic == TOR_MAGIC_CEN  && ringVariant == CC))
                {
                    continue;
                }

                //----------------------
                // Unique ring ID loop.
                for (ringId = 0; ringId < numRingIds; ringId++)
                {

                    ringType = -1;

                    //---------------------------
                    // Chiplet instance ID loop.
                    // - Only loop once if ringId is a common ring.
                    for (instanceId = 0; instanceId <= CHIPLET_ID_MAX && ringType != COMMON_RING; instanceId++)
                    {
#ifdef P9_XIP_TOOL_VERBOSE
                        fprintf( stderr, "Processing:  "
                                 "DD=0x%02x  "
                                 "PPE=%s "
                                 "Variant=%s "
                                 "RingID=%d  "
                                 "InstanceID=0x%02x\n",
                                 ddLevel, ppeTypeName[ppeType], ringVariantName[ringVariant], ringId, instanceId);
#endif

                        ringBlockSize = RING_BUF_SIZE_MAX;
                        rc = tor_access_ring( i_ringSection,
                                              ringId,
                                              ddLevel,
                                              ppeType,
                                              ringType,            // IO parm
                                              ringVariant,
                                              instanceId,          // IO parm
                                              GET_SINGLE_RING,
                                              &ringBlockPtr,       // IO parm
                                              ringBlockSize,       // IO parm
                                              ringName,
                                              0 );

                        // Gather ring details and print it.
                        //
                        if (rc == TOR_RING_FOUND)
                        {
                            rs4 = (CompressedScanData*)ringBlockPtr;

                            // Sanity check RS4 container's ringId matches the requested.
                            RingId_t l_ringId = be16toh(rs4->iv_ringId);

                            if ( l_ringId != ringId )
                            {
                                fprintf(stderr, "tor_access_ring() was successful and found a ring. But "
                                        "RS4 header's iv_ringId(=0x%x) differs from requested ringId(=0x%x).\n",
                                        l_ringId, ringId);
                                exit(1);
                            }


                            // Check ring block size.
                            ringSize = be16toh(rs4->iv_size);

                            if ( ringSize != ringBlockSize || ringSize == 0 )
                            {
                                fprintf(stderr, "tor_access_ring() was successful and found a ring. But "
                                        "RS4 header's iv_size(=0x%04x) is either zero or doesn't match "
                                        "size of ring buffer (ringBlockSize=0x%04x).\n",
                                        ringSize, ringBlockSize);
                                exit(1);
                            }

                            ringSeqNo++;

                            // This do-while loop is for cmsk support to display
                            // both rs4 stump ring and rs4 cmsk ring else this
                            // loop would run only once
                            cmskRingIteration = 0;

                            do
                            {
                                // decompress ring to obtain ring length and to verify compressed string
                                // check for cmsk ring
                                if (rs4_is_cmsk(rs4))
                                {
                                    if (!cmskRingIteration)
                                    {
                                        // Extract Stump & Cmsk rings from large rs4 ring
                                        // decompress each to get ring length and display
                                        // accordingly
                                        rc = rs4_extract_cmsk(rs4, &rs4Stump, &rs4Cmsk);

                                        if (rc)
                                        {
                                            fprintf(stdout, "CMSK extract error %i)", rc);
                                        }

                                        cmskRingIteration++;
                                        ringSuffix = 's';
                                        rs4Print = rs4Stump;    //For 'raw' & 'long' display
                                        ringBlockSize = be16toh(rs4Stump->iv_size);
                                        rs4rc = rs4_decompress(&data, &care, &bits, rs4Stump);
                                    }
                                    else
                                    {
                                        cmskRingIteration--;
                                        ringSuffix = 'c';
                                        rs4Print = rs4Cmsk;    //For 'raw' & 'long' display
                                        ringBlockSize = be16toh(rs4Cmsk->iv_size);
                                        rs4rc = rs4_decompress(&data, &care, &bits, rs4Cmsk);
                                    }
                                }
                                else
                                {
                                    ringSuffix = ' ';
                                    rs4Print = rs4;
                                    rs4rc = rs4_decompress(&data, &care, &bits, rs4);
                                }

                                comprRate = (double)ringSize / (double)bits * 100.0;

                                // tabular ring list if "table".
                                if (i_listingModeId == LMID_TABLE)
                                {
                                    fprintf(stdout,
                                            "%4i%c    "
                                            "0x%02x    "
                                            "%4s    "
                                            "%4s    "
                                            "0x%02x    "
                                            "%7d "
                                            "%6.2f  "
                                            "%s   ",
                                            ringSeqNo, ringSuffix, ddLevel, ppeTypeName[ppeType],
                                            ringVariantName[ringVariant], instanceId,
                                            bits, comprRate, ringName);

                                    if (rs4rc != SCAN_COMPRESSION_OK)
                                    {
                                        fprintf(stdout, "Decompression error %i)", rs4rc);
                                    }

                                    fprintf(stdout, "\n");
                                }

                                // Summarize a few key characteristics of the ring block if "short".
                                if (i_listingModeId == LMID_SHORT)
                                {
                                    fprintf( stdout,
                                             "-----------------------------\n"
                                             "%i.%c\n"
                                             "ddLevel = 0x%02x\n"
                                             "ppeType = %s\n"
                                             "ringName = %s\n"
                                             "ringVariant = %s\n"
                                             "instanceId = 0x%02x\n",
                                             ringSeqNo, ringSuffix, ddLevel, ppeTypeName[ppeType], ringName,
                                             ringVariantName[ringVariant], instanceId );
                                }

                                // Summarize all characteristics of the ring block if "normal", "long" or "raw"
                                if (i_listingModeId == LMID_NORMAL ||
                                    i_listingModeId == LMID_LONG ||
                                    i_listingModeId == LMID_RAW)
                                {
                                    fprintf( stdout,
                                             "-----------------------------\n"
                                             "%i.%c\n"
                                             "ddLevel = 0x%02x\n"
                                             "ppeType = %s\n"
                                             "ringId = %u\n"
                                             "ringName = %s\n"
                                             "ringVariant = %s\n"
                                             "instanceId = 0x%02x\n"
                                             "ringBlockSize = 0x%08x\n"
                                             "raw bit length = %d\n"
                                             "compression [%%] = %6.2f\n",
                                             ringSeqNo, ringSuffix, ddLevel, ppeTypeName[ppeType], ringId, ringName,
                                             ringVariantName[ringVariant], instanceId,
                                             ringBlockSize, bits, comprRate);
                                }

                                // Dump ring block if "long" or "raw"
                                if (i_listingModeId == LMID_LONG ||
                                    i_listingModeId == LMID_RAW)
                                {
                                    fprintf(stdout, "Binary ring block dump (LE format):\n");

                                    // Below code is used to display the stumped and
                                    // cmsk raw|long ring block.
                                    // @FIXME: Tried to display the same by assigning
                                    // rs4Stump and rs4Cmsk to ringBlockPtr but having issue
                                    // with ringBlockPtr being void pointer.
                                    if (rs4_is_cmsk(rs4))
                                    {
                                        for (i = 0; i < ringBlockSize / 8; i++)
                                        {
                                            fprintf( stdout,
                                                     "%04x: %04x %04x %04x %04x\n",
                                                     i * 8,
                                                     (uint16_t)( htobe64(*((uint64_t*)rs4Print + i)) >> 48),
                                                     (uint16_t)( htobe64(*((uint64_t*)rs4Print + i)) >> 32),
                                                     (uint16_t)( htobe64(*((uint64_t*)rs4Print + i)) >> 16),
                                                     (uint16_t)( htobe64(*((uint64_t*)rs4Print + i))) );
                                        }
                                    }
                                    else
                                    {
                                        for (i = 0; i < ringBlockSize / 8; i++)
                                        {
                                            fprintf( stdout,
                                                     "%04x: %04x %04x %04x %04x\n",
                                                     i * 8,
                                                     (uint16_t)( htobe64(*((uint64_t*)ringBlockPtr + i)) >> 48),
                                                     (uint16_t)( htobe64(*((uint64_t*)ringBlockPtr + i)) >> 32),
                                                     (uint16_t)( htobe64(*((uint64_t*)ringBlockPtr + i)) >> 16),
                                                     (uint16_t)( htobe64(*((uint64_t*)ringBlockPtr + i))) );
                                        }
                                    }
                                }

                                // Below we dump the raw decompressed ring content in the exact same
                                //   format that it appears as in EKB's ifCompiler generated raw ring
                                //   files, i.e. *.bin.srd (DATA) and *.bin.srd.bitsModified (CARE).
                                if (i_listingModeId == LMID_RAW)
                                {
                                    fprintf( stdout, "\nRaw decompressed DATA nibbles:\n");
                                    print_raw_ring( data, bits);

                                    fprintf( stdout, "\nRaw decompressed CARE nibbles:\n");
                                    print_raw_ring( care, bits);

                                    fprintf( stdout, "\n");
                                }

                                free(data);
                                free(care);

                            }
                            while (cmskRingIteration);
                        }
                        else if (rc == TOR_RING_NOT_FOUND      ||
                                 rc == TOR_INVALID_INSTANCE_ID ||
                                 rc == TOR_INVALID_CHIPLET     ||
                                 rc == TOR_INVALID_VARIANT     ||
                                 rc == TOR_AMBIGUOUS_API_PARMS ||
                                 rc == TOR_INVALID_RING_ID)
                        {
#ifdef P9_XIP_TOOL_VERBOSE
                            fprintf(stderr, "tor_access_ring() returned error code rc=%d\n", rc);
#endif
                        }
                        else
                        {
                            fprintf(stderr, "tor_access_ring() returned error code rc=%d\n", rc);
                            {
                                exit(1);
                            }
                        }

                    }  // End of for(instanceId)

                }  // End of for(ringVariant)

            }  // End of for(iRingId)

        }  // End of for(ppeType)

    }  // End of for(iDdLevel)

    if (i_listingModeId != LMID_TABLE)
    {
        fprintf(stdout, "-----------------------------\n");
    }

    return 0;
}



/// Function:  dissectRingSection()
///
/// Brief:  Processes XIP tool input parms and prepares parameters to be passed
///         to dissectRingSectionTor which does the actual dissection and
///         summarizing of the ring section.
///
/// \param[in] i_image A pointer to a P9-XIP image or TOR ringSection in host memory.
///
/// \param[in] i_argc  Additional number of arguments beyond "dissect" keyword.
///
/// \param[in] i_argv  Additional arguments beyond "dissect" keyword.
///
/// \param[in] i_imageSectionType  Image section type, e.g. IST_XIP or IST_TOR.
///
/// Assumptions:
///
static int
dissectRingSection(void*                      i_image,
                   int                        i_argc,
                   const char**               i_argv,
                   const image_section_type_t i_imageSectionType)
{
    int             rc = 0;
    const char*     sectionName;
    const char*     listingModeName = NULL;
    uint8_t         sectionId, listingModeId;
    P9XipHeader     hostHeader;
    P9XipSection    hostSection;
    void*           ringSectionPtr;
    image_section_type_t l_imageSectionType = IST_UNDEFINED;


    //
    // Treat input image section according to its type.
    //

    switch(i_imageSectionType)
    {

        case IST_XIP:

            if (i_argc == 1)
            {
                sectionName = i_argv[0];
            }
            else if (i_argc == 2)
            {
                sectionName = i_argv[0];
                listingModeName = i_argv[1];
            }
            else
            {
                fprintf(stderr,
                        "\nERROR:  The number of sub arguments (=%d) is too few or too many for the 'dissect' command for an XIP image\n",
                        i_argc);
                exit(EXIT_FAILURE);
            }

            p9_xip_translate_header(&hostHeader, (P9XipHeader*)i_image);

            // Determine P9-XIP ring section ID from the section name, e.g.
            //         .rings    =>  P9_XIP_SECTION_HW_RINGS
            if (strcmp(sectionName, ".rings") == 0)
            {
                if (hostHeader.iv_magic == P9_XIP_MAGIC_SEEPROM)
                {
                    sectionId = P9_XIP_SECTION_SBE_RINGS;
                }
                else if (hostHeader.iv_magic == P9_XIP_MAGIC_HW ||
                         hostHeader.iv_magic == P9_XIP_MAGIC_CENTAUR)
                {
                    sectionId = P9_XIP_SECTION_HW_RINGS;
                }
                else
                {
                    fprintf(stderr,
                            "\nERROR: .rings is not a valid section for image w/magic=0x%016lx\n",
                            hostHeader.iv_magic);
                    exit(EXIT_FAILURE);
                }
            }
            else if (strcmp(sectionName, ".overrides") == 0)
            {
                if (hostHeader.iv_magic == P9_XIP_MAGIC_SEEPROM)
                {
                    sectionId = P9_XIP_SECTION_SBE_OVERRIDES;
                }
                else
                {
                    fprintf(stderr,
                            "\nERROR: .overrides is not a valid section for image w/magic=0x%016lx\n",
                            hostHeader.iv_magic);
                    exit(EXIT_FAILURE);
                }
            }
            else if (strcmp(sectionName, ".overlays") == 0)
            {
                if (hostHeader.iv_magic == P9_XIP_MAGIC_SEEPROM)
                {
                    sectionId = P9_XIP_SECTION_SBE_OVERLAYS;
                }
                else
                {
                    fprintf(stderr,
                            "\nERROR: .overlays is not a valid section for image w/magic=0x%016lx\n",
                            hostHeader.iv_magic);
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                fprintf(stderr,
                        "\nERROR : %s is an invalid ring section name.\n", sectionName);
                fprintf(stderr, "Valid ring <section> names for the 'dissect' function are:\n");
                fprintf(stderr, "\t.rings\n");
                fprintf(stderr, "\t.overrides\n");
                fprintf(stderr, "\t.overlays\n");
                fprintf(stderr, "\n");
                exit(EXIT_FAILURE);
            }

            // Get ring section.
            //
            rc = p9_xip_get_section( i_image, sectionId, &hostSection);

            if (rc)
            {
                fprintf( stderr, "p9_xip_get_section() failed : %s\n", P9_XIP_ERROR_STRING(g_errorStrings, rc));
                return P9_XIP_DISASSEMBLER_ERROR;
            }

            if (hostSection.iv_offset == 0)
            {
                fprintf( stdout, "Ring section (w/ID=%d) is empty. Nothing to do. Quitting.\n", sectionId);
                exit(EXIT_FAILURE);
            }

            ringSectionPtr = (void*)((uintptr_t)i_image + hostSection.iv_offset);

            resolve_image_section_type( ringSectionPtr, l_imageSectionType);

            if (l_imageSectionType != IST_TOR)
            {
                fprintf( stderr,
                         "\nERROR:  The XIP section's ring section type (=%d) is not a TOR ring section type (=%d). Possibly, you may be attempting to dissect one of the other XIP ring section types, such as DDCO, but which have no dissect support yet.\n",
                         l_imageSectionType, IST_TOR);
                exit(EXIT_FAILURE);
            }

            break;

        case IST_TOR:

            if (i_argc == 1)
            {
                listingModeName = i_argv[0];
            }
            else
            {
                fprintf(stderr,
                        "\nERROR:  The number of sub arguments (=%d) is too few or too many for the 'dissect' command for a TOR section\n",
                        i_argc);
                exit(EXIT_FAILURE);
            }

            ringSectionPtr = i_image;

            break;

        case IST_DDCO:

            fprintf(stderr,
                    "\nERROR:  No support for DDCO image section type yet.\n");
            exit(EXIT_FAILURE);

        default:

            fprintf(stderr,
                    "\nERROR : In dissectRingSection(): Invalid image section type (=0x%02x)\n",
                    i_imageSectionType);

            uint64_t l_magic = *((uint64_t*)i_image);
            fprintf(stderr, "Here's the first 8 bytes of the image section (LE): 0x%016lx ",
                    be64toh(l_magic));
            fprintf(stderr, "\"%c%c%c%c%c%c%c%c\"\n",
                    *(((uint8_t*)&l_magic) + 0),
                    *(((uint8_t*)&l_magic) + 1),
                    *(((uint8_t*)&l_magic) + 2),
                    *(((uint8_t*)&l_magic) + 3),
                    *(((uint8_t*)&l_magic) + 4),
                    *(((uint8_t*)&l_magic) + 5),
                    *(((uint8_t*)&l_magic) + 6),
                    *(((uint8_t*)&l_magic) + 7));
            fprintf(stderr,
                    "If you're seeing \"TOR\" or \"DDCO\" in the first 8 bytes, you're probably using the PPE version of p9_xip_tool. Use the EKB version instead!\n");
            exit(EXIT_FAILURE);
    }

    // Determine mode of listing.
    //
    if ( listingModeName == NULL )
    {
        listingModeId = LMID_NORMAL;
    }
    else if (strcmp(listingModeName, "table") == 0)
    {
        listingModeId = LMID_TABLE;
    }
    else if (strcmp(listingModeName, "short") == 0)
    {
        listingModeId = LMID_SHORT;
    }
    else if (strcmp(listingModeName, "normal") == 0)
    {
        listingModeId = LMID_NORMAL;
    }
    else if (strcmp(listingModeName, "long") == 0)
    {
        listingModeId = LMID_LONG;
    }
    else if (strcmp(listingModeName, "raw") == 0)
    {
        listingModeId = LMID_RAW;
    }
    else
    {
        fprintf(stderr,
                "\nERROR : %s is an invalid listing mode name.\n", listingModeName);
        fprintf(stderr, "Valid listing mode names the 'dissect' function are:\n");
        fprintf(stderr, "\ttable\n");
        fprintf(stderr, "\tshort\n");
        fprintf(stderr, "\tnormal (default if omitted)\n");
        fprintf(stderr, "\tlong\n");
        fprintf(stderr, "\traw\n");
        fprintf(stderr, "\n");
        exit(EXIT_FAILURE);
    }

    rc = dissectRingSectionTor(ringSectionPtr, listingModeId);

    return rc;

}

#endif



/// Function:  openAndMap()
///
/// Brief:  Opens and mmaps the file.
///
static void
openAndMap(const char* i_fileName, int i_writable, int* o_fd, void** o_image, const uint32_t i_maskIgnores)
{
    int rc, openMode, mmapProt, mmapShared;
    struct stat buf;

    if (i_writable)
    {
        openMode = O_RDWR;
        mmapProt = PROT_READ | PROT_WRITE;
        mmapShared = MAP_SHARED;
    }
    else
    {
        openMode = O_RDONLY;
        mmapProt = PROT_READ;
        mmapShared = MAP_PRIVATE;
    }

    *o_fd = open(i_fileName, openMode);

    if (*o_fd < 0)
    {
        perror("open() of the image failed : ");
        fprintf(stderr, "ERROR: open() failed for filename=%s failed : ", i_fileName);
        exit(1);
    }

    rc = fstat(*o_fd, &buf);

    if (rc)
    {
        perror("fstat() of the image failed : ");
        exit(1);
    }

    g_imageSize = buf.st_size;

    *o_image = mmap(0, g_imageSize, mmapProt, mmapShared, *o_fd, 0);

    if (*o_image == MAP_FAILED)
    {
        perror("mmap() of the image failed : ");
        exit(1);
    }

    if ( !(i_maskIgnores & P9_XIP_IGNORE_ALL) &&
         (be64toh(((P9XipHeader*)o_image)->iv_magic) >> 32 == P9_XIP_MAGIC) )
    {
        rc = p9_xip_validate2(*o_image, g_imageSize, i_maskIgnores);

        if (rc)
        {
            fprintf(stderr, "p9_xip_validate2() failed : %s\n",
                    P9_XIP_ERROR_STRING(g_errorStrings, rc));
            exit(1);
        }
    }

}


static inline void
openAndMapWritable(const char* i_imageFile, int* o_fd, void** o_image, const uint32_t i_maskIgnores)
{
    openAndMap(i_imageFile, 1, o_fd, o_image, i_maskIgnores);
}


static inline void
openAndMapReadOnly(const char* i_imageFile, int* o_fd, void** o_image, const uint32_t i_maskIgnores)
{
    openAndMap(i_imageFile, 0, o_fd, o_image, i_maskIgnores);
}

///
/// @brief Checks .rings section created for SBE image to
///        see if it is less than the passed in size.
///
/// @param[in]  i_hwImage - pointer to an unsigned hw image.
/// @param[in]  i_ddLevel - DD level .rings section to check
/// @param[in]  i_maxSize - Maximum allowable section size
///
/// @return IMGBUILD_SUCCESS if section is less than i_maxSize
///
static
int check_sbe_ring_section_size( void* i_hwImage,
                                 uint32_t i_ddLevel,
                                 uint32_t i_maxSize )
{
    int rc = 0;
#ifndef __PPE__ // Needed on ppe side to avoid TOR API 

    P9XipSection l_ringsSection;

    RingType_t l_ringType = ALLRING;

    uint8_t  unused_parm  = 0;
    void**   l_blockPtr   = NULL;
    uint32_t l_blockSize  = 0;

    // Get the full .rings section from the HW image
    rc = p9_xip_get_section(i_hwImage, P9_XIP_SECTION_HW_RINGS, &l_ringsSection);

    if (!rc)
    {
        // verify the .rings section is populated
        if (l_ringsSection.iv_size == 0)
        {
            fprintf(stderr, "Ring section size in HW image is zero.\n");
            rc = P9_XIP_DATA_NOT_PRESENT;
            return rc;
        }

        // Make a pointer to the start of the rings section
        void* ringsSection = (uint8_t*)i_hwImage + l_ringsSection.iv_offset;

        do
        {
            // Call the tor function will a null block pointer to get the
            // section size
            rc = tor_get_block_of_rings( ringsSection,
                                         i_ddLevel,
                                         PT_SBE,
                                         l_ringType,
                                         BASE,
                                         unused_parm,
                                         l_blockPtr,
                                         l_blockSize);

            if(rc)
            {
                fprintf(stderr, "error calling tor API rc = %d\n", rc);
                break;
            }

            if( l_blockSize == 0 )
            {
                fprintf(stderr, "No rings for dd_level %#02x found\n", i_ddLevel);
                break;
            }

            fprintf(stderr, " SBE .ring section size for DD level %#02x ", i_ddLevel);

            // return failure if the block size would exceed the maximum allowed size
            if( l_blockSize > i_maxSize )
            {
                fprintf(stderr, "is %d bytes, which exceeds maximum size limit of %i\n", l_blockSize, i_maxSize);
                rc = P9_XIP_SBE_DD_SIZE_ERR;
            }
            else
            {
                fprintf(stderr, "is %d bytes - OK\n", l_blockSize);
            }

        }
        while(0);
    }

#endif
    return rc;
}

// Parse and execute a pre-tokenized command

static void
command(const char* i_imageFile, const int i_argc, const char** i_argv, const uint32_t i_maskIgnores)
{
    int rc = INFRASTRUCT_RC_SUCCESS;
    void* image;
    void* attrDump;
    int fd;
    image_section_type_t l_imageSectionType = IST_UNDEFINED;

    // First, determine image section type, IST, of the supplied input image.
    //
    openAndMapReadOnly(i_imageFile, &fd, &image, i_maskIgnores);
    resolve_image_section_type(image, l_imageSectionType);
    close(fd);

    // Second, determine the command action and act accordingly.
    //
    if ( strcmp(i_argv[0], "normalize") == 0 &&
         l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = normalize(image, i_argc - 1, &(i_argv[1]), i_maskIgnores);

    }
    else if ( strcmp(i_argv[0], "set") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = set(image, i_argc - 1, &(i_argv[1]), 0);

    }
    else if ( strcmp(i_argv[0], "setv") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = set(image, i_argc - 1, &(i_argv[1]), 1);

    }
    else if ( strcmp(i_argv[0], "get") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapReadOnly(i_imageFile, &fd, &image, i_maskIgnores);
        rc = get(image, i_argc - 1, &(i_argv[1]), 0);

    }
    else if ( strcmp(i_argv[0], "getv") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapReadOnly(i_imageFile, &fd, &image, i_maskIgnores);
        rc = get(image, i_argc - 1, &(i_argv[1]), 1);

    }
    else if (strcmp(i_argv[0], "report") == 0)
    {

        openAndMapReadOnly(i_imageFile, &fd, &image, i_maskIgnores);
        rc = report(image, i_argc - 1, &(i_argv[1]), l_imageSectionType);

    }
    else if ( strcmp(i_argv[0], "attrdump") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapReadOnly(i_imageFile, &fd, &image, i_maskIgnores);
        //capture the g_imageSize size for validation, since the next
        //openAndMapReadOnly will overwrite it
        size_t imageSize = g_imageSize;
        //first argument after command is dump file
        openAndMapReadOnly(i_argv[1], &fd, &attrDump, P9_XIP_IGNORE_ALL);
        rc = reportAttr(image, imageSize, attrDump);

    }
    else if ( strcmp(i_argv[0], "append") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = append(i_imageFile, fd, image, i_argc - 1, &(i_argv[1]));

    }
    else if ( strcmp(i_argv[0], "extract") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = extract(i_imageFile, fd, image, i_argc - 1, &(i_argv[1]));

    }
    else if ( strcmp(i_argv[0], "delete") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = deleteSection(i_imageFile, fd, image, i_argc - 1,
                           &(i_argv[1]));

    }
    else if (strcmp(i_argv[0], "dissect") == 0)
    {

        openAndMapReadOnly(i_imageFile, &fd, &image, i_maskIgnores);
#ifndef __PPE__ // Needed on ppe side to avoid TOR API
        rc = dissectRingSection(image, i_argc - 1, &(i_argv[1]), l_imageSectionType);
#else
        fprintf(stderr, "\n");
        fprintf(stderr, "---------------------------------------------\n");
        fprintf(stderr, "  dissect feature not supported in PPE repo  \n");
        fprintf(stderr, "  => Use EKB version of p9_xip_tool          \n");
        fprintf(stderr, "---------------------------------------------\n\n");
        exit(EXIT_FAILURE);
#endif

    }
    else if ( strcmp(i_argv[0], "check-sbe-ring-section") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        if(i_argc != 3)
        {
            fprintf(stderr, g_usage);
            exit(EXIT_FAILURE);
        }

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);

        // grab the size and dd level from the cmdline
        const char* i_ddLevel = i_argv[1];

        uint8_t l_ddLevel = strtol(i_ddLevel, 0, 0);

        uint32_t l_maxSize = strtol(i_argv[2], 0, 10);

        // validate that the dd level specific .rings section generated for the
        // sbe image will not exceed the given size.
        rc = check_sbe_ring_section_size(image, l_ddLevel, l_maxSize) ;

    }
    else if ( strcmp(i_argv[0], "TEST") == 0 &&
              l_imageSectionType == IST_XIP )
    {

        openAndMapWritable(i_imageFile, &fd, &image, i_maskIgnores);
        rc = TEST(image, i_argc - 1, &(i_argv[1]));

    }
    else
    {

        fprintf(stderr, g_usage);
        exit(EXIT_FAILURE);

    }

    if (rc)
    {
        fprintf(stderr,
                "\nERROR: In command(): Command failed : %s\n",
                P9_XIP_ERROR_STRING(g_errorStrings, rc));
        exit(EXIT_FAILURE);
    }
}


// Open, map and validate the image, then parse and execute the command.  The
// image is memory-mapped read/write, i.e, it may be modified in-place.
// Commands that modify the size of the image will close and recreate the
// file.

int
main(int argc, const char** argv)
{
    uint8_t   argcMin, idxArgvFlagsStart;
    uint8_t   numFlags = 0, idxArgv, bMoreFlags;
    uint32_t  maskIgnores = 0;

    argcMin = 3;
    idxArgvFlagsStart = argcMin - 1; // -i flags must start after image file name.

    numFlags = 0;
    bMoreFlags = 1;

    do
    {
        idxArgv = idxArgvFlagsStart + numFlags;

        if (idxArgv <= (argc - 1))
        {
            if (strncmp(argv[idxArgv], "-i", 1) == 0)
            {
                numFlags++;
                bMoreFlags = 1;

                if (strncmp(argv[idxArgv], "-ifs", 4) == 0)
                {
                    maskIgnores = maskIgnores | P9_XIP_IGNORE_FILE_SIZE;
                }
                else if (strncmp(argv[idxArgv], "-iv", 3) == 0)
                {
                    maskIgnores = maskIgnores | P9_XIP_IGNORE_ALL;
                }
                else
                {
                    fprintf(stderr, g_usage);
                    fprintf(stderr, "\n");
                    fprintf(stderr, "argv[%i]=%s is an unsupported flag.", idxArgv, argv[idxArgv]);
                    fprintf(stderr, "See top of above help menu for supported flags.\n");
                    exit(1);
                }
            }
            else
            {
                bMoreFlags = 0;
            }
        }
        else
        {
            bMoreFlags = 0;
            break;
        }
    }
    while (bMoreFlags);

    if ((argc < (argcMin + numFlags)) ||
        (strncmp(argv[1], "-h", 2) == 0) ||
        (strncmp(argv[1], "--h", 3) == 0) )
    {
        fprintf(stderr, g_usage);
        exit(1);
    }

    command(argv[1], argc - idxArgv, &(argv[idxArgv]), maskIgnores);

    return 0;
}
