#!/usr/bin/env python # Created 03/20/15 by Jason Albert # Program to deconstruct a VPD image into xml template files # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # OpenPOWER HostBoot Project # # Contributors Listed Below - COPYRIGHT 2010,2014 # [+] 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 ############################################################ # Imports - Imports - Imports - Imports - Imports - Imports ############################################################ import os # Get the path the script resides in scriptPath = os.path.dirname(os.path.realpath(__file__)) import sys sys.path.insert(0,scriptPath + "/pymod"); import argparse import textwrap import out import xml.etree.ElementTree as ET import struct import re import binascii import string import operator def asciiAllowed(s): for c in s: if c not in string.printable: return False return True ############################################################ # Classes - Classes - Classes - Classes - Classes - Classes ############################################################ class RecordInfo: """Stores the info about each vpd record""" def __init__(self): # The name of the record self.recordName = None # The location of the Record Offset self.recordOffset = None # The location of the Record Length self.recordLength = None # The location of the ECC Offset self.eccOffset = None # The location of the ECC Length self.eccLength = None ############################################################ # Function - Functions - Functions - Functions - Functions ############################################################ # Function to write out the resultant tvpd xml file def writeTvpd(manifest, outputFile): tree = ET.ElementTree(manifest) tree.write(outputFile, encoding="utf-8", xml_declaration=True) return None ############################################################ # Main - Main - Main - Main - Main - Main - Main - Main ############################################################ rc = 0 ################################################ # Command line options # Create the argparser object # We disable auto help options here and add them manually below. This is we can get all the optional args in 1 group parser = argparse.ArgumentParser(description='Reverses a VPD image into XML template files', add_help=False, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=textwrap.dedent('''\ Examples: ./reverseVpd.py -v image.vpd -o /tmp ''')) # Create our group of required command line args reqgroup = parser.add_argument_group('Required Arguments') reqgroup.add_argument('-v', '--vpdfile', help='The valid vpd formatted input file', required=True) reqgroup.add_argument('-o', '--outpath', help='The output path for the files created by the tool', required=True) # Create our group of optional command line args optgroup = parser.add_argument_group('Optional Arguments') optgroup.add_argument('-h', '--help', action="help", help="Show this help message and exit") optgroup.add_argument('-d', '--debug', help="Enables debug printing",action="store_true") optgroup.add_argument('-r', '--create-records', help="Create tvpd files for each record in the vpd",action="store_true") # We've got everything we want loaded up, now look for it args = parser.parse_args() # Get the manifest file and get this party started clVpdFile = args.vpdfile # Look for output path clOutputPath = args.outpath # Make sure the path exists, we aren't going to create it if (os.path.exists(clOutputPath) != True): out.error("The given output path %s does not exist!" % clOutputPath) out.error("Please create the output directory and run again") exit(1) # Debug printing clDebug = args.debug # Create separate tvpd files for each record clCreateRecords = args.create_records ################################################ # Read in the VPD file and break it apart out.setIndent(0) out.msg("==== Stage 1: Parsing the VPD file") out.setIndent(2) # Create our output name from the input name vpdName = os.path.splitext(os.path.basename(clVpdFile))[0] # Open the vpdfile vpdContents = open(clVpdFile, mode='rb').read() # Jump right to where the VTOC should be and make sure it says VTOC offset = 61 if (vpdContents[offset:(offset+4)].decode() != "VTOC"): out.error("Did not find VTOC at the expected offset!") exit(1) offset+=4 # Skip the PT keyword and read the 1 byte length to loop over the VTOC contents and create our record list offset+=2 # PT skip tocLength = struct.unpack('= 0): if (keywordData[nzeroidx] != 0x00): break; nzeroidx-=1 # If we didn't get back to the start, shorten the data if (nzeroidx >= 0): keywordData = keywordData[0:(nzeroidx+1)] # Made it all the way to the start, must be all zero. Shorten it to just the first zero byte to put into the template else: keywordData = keywordData[0:1] # Now try to decode and figure out hex vs ascii try: keywordData.decode('ascii') except UnicodeDecodeError: asciiState = False else: if asciiAllowed(keywordData.decode('ascii')): asciiState = True else: asciiState = False # We know if its ascii or not, store away our data if (asciiState): ET.SubElement(keyword, "kwformat").text = "ascii" ET.SubElement(keyword, "kwdata").text = keywordData.decode() else: ET.SubElement(keyword, "kwformat").text = "hex" ET.SubElement(keyword, "kwdata").text = binascii.hexlify(keywordData).decode() out.setIndent(4) out.msg("Keyword: %s Type: %5s Length: %s" % (keywordName, ("ascii" if (asciiState) else "hex"), str(keywordLength))) # We should be done with all the keywords, which means it's pointing to the SR tag if (struct.unpack('