#!/usr/bin/python # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # $Source: src/build/debug/simics-debug-framework.py $ # # OpenPOWER HostBoot Project # # Contributors Listed Below - COPYRIGHT 2011,2018 # [+] Google Inc. # [+] 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 simics-debug-framework.py # @brief Simics/Python implementation of the common debug framework. # # This is the Python side of the simics implementation of the debug framework. # It operates by opening a Perl script as a subprocess using stdin/stdout to # the subprocess as an IPC pipe. # # The Python script will handle the bridging from framework primatives, such # as 'readData', to Simics interfaces. The script will also search for all # existing debug modules and automatically instantiate simics commands of the # form 'hb-Tool' so they can be used. # # If the users are expecting another tool name, such as 'hb-printk' instead of # 'hb-Printk', or are expecting nicer parameter passing, such as # 'hb-trace COMP1,COMP2' instead of 'hb-Trace "components=COMP1,COMP2"', then a # manual wrapper command should be implemented in 'hb-simdebug.py'. import os import subprocess import re import random # @class DebugFrameworkIPCMessage # @brief Wrapper class for constructing a properly formed IPC message for the # Python-Perl bridge. # # The class provides a Pickle-like API (dumps / loads). # # Messages are of the format: # [ "type", "data-in-ascii-encoded-hex" ] # Example: # The message... # [ "display", "48656c6c6f20576f726c642e0a" ] # means 'display "Hello World.\n"' # class DebugFrameworkIPCMessage: msgtype = "unknown" msg = "" def __init__(self, msgtype = "unknown", msg = ""): self.msgtype = msgtype self.msg = msg def dumps(self): return ("[ \"" + self.msgtype + "\", \"" + self.msg.encode("hex") + "\" ]\n") def loads(self,string): pattern = re.compile("\[ \"([^\"]+)\", \"([0-9a-f]*)\" ]") match = pattern.search(string) if match is None: print "error: empty message >%s< received from perl"%(string) print " Check for print's in your perl script!!!" else: self.msgtype = match.group(1) self.msg = match.group(2).decode("hex") # @class DebugFrameworkProcess # @brief Provides a wrapper to the 'subprocess' interface and IPC bridge. # # This class also provides the handling functions for various bridge message # types into the appropriate simics interface. # class DebugFrameworkProcess: process = ""; # subprocess object. tool = ""; # string - tool module name. toolOptions = ""; # string - tool options outputToString = None; # mode - String output instead of STDOUT. imgPath = None; # Image dir path override. result = ""; # Result string for Usage-mode. outputFile = None; # Output file for results in addition to STDOUT def __init__(self, tool = "Printk", toolOptions = "", outputToString = None, usage = None, imgPath = None, outputFile = None): # Assign instance 'imgPath' variable. self.imgPath = imgPath if imgPath else (os.environ['HB_TOOLPATH']+"/"); # Determine sub-process arguments. process_args = [self.imgPath+"simics-debug-framework.pl"]; if (usage): # Pass --usage if Usage mode selected. process_args = process_args + [ "--usage" ]; outputToString = True; # Spawn sub-process self.process = subprocess.Popen(process_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # Update instance variables. self.tool = tool; self.toolOptions = toolOptions; self.outputToString = outputToString; self.outputFile = open(outputFile, 'w') if outputFile else None; # Read a message from the process pipe. def recvMsg(self): msg = DebugFrameworkIPCMessage() line = self.process.stdout.readline() if len(line) != 0: msg.loads(line) return (msg.msgtype, msg.msg) else: return ("", "") # Send a message into the process pipe. def sendMsg(self,msgtype,msg): msg = DebugFrameworkIPCMessage(msgtype, msg) self.process.stdin.write(msg.dumps()) # End sub-process by closing its pipe. def endProcess(self): self.process.stdin.close() # Display string (or save to result in Usage mode). def display(self,data): if (self.outputToString): self.result += data else: print data, if self.outputFile: print >>self.outputFile,data, # Read data from memory. # This message has data of the format "0dADDRESS,0dSIZE". def read_data(self,data): pattern = re.compile("([0-9]+),([0-9]+)") match = pattern.search(data) addr = int(match.group(1)) size = int(match.group(2)) data = "".join(map(chr, conf.system_cmp0.phys_mem.memory[[addr , addr+size-1]])) self.sendMsg("data-response", data) # Write data to memory. # This message has data of the format "0dADDR,0dSIZE,hDATA". def write_data(self,data): pattern = re.compile("([0-9]+),([0-9]+),([0-9A-Fa-f]+)") match = pattern.search(data) addr = int(match.group(1)) size = int(match.group(2)) data = map(ord, match.group(3).decode("hex")); conf.system_cmp0.phys_mem.memory[[addr, addr+size-1]] = data; # Read data from PNOR. # This message has data of the format "0dADDRESS,0dSIZE". def read_pnor(self,data): pattern = re.compile("([0-9]+),([0-9]+)") match = pattern.search(data) addr = int(match.group(1)) size = int(match.group(2)) data = "".join(map(chr, conf.fpga0.sfc_master_mem.memory[[addr , addr+size-1]])) self.sendMsg("data-response", data) # Clock forward the model. # This message had data of the format "0dCYCLES". def execute_instrs(self,data): pattern = re.compile("([0-9]+)") match = pattern.search(data) cycles = int(match.group(1)) ## @todo mww SIM_continue is busted, try this... if (not SIM_simics_is_running()): ## SIM_continue(cycles) syscmd = "run-cycles %d"%(cycles) ## print ">> %s"%(syscmd) ( rc, out ) = quiet_run_command( syscmd, output_modes.regular ) if ( rc ): print "simics ERROR running %s: %d "%( syscmd, rc ) def ready_for_instr(self,data): self.sendMsg("data-response", "0" if SIM_simics_is_running() else "1") # Get tool module name. def get_tool(self,data): self.sendMsg("data-response", self.tool) # Get tool options. def get_tool_options(self,data): self.sendMsg("data-response", self.toolOptions) # Get image path. def get_img_path(self,data): self.sendMsg("data-response", self.imgPath) # Read data from xscom address. # This message has data of the format "0dADDRESS,0dSIZE". def read_xscom(self,data): pattern = re.compile("([0-9]+),([0-9]+)") match = pattern.search(data) addr = int(match.group(1)) size = int(match.group(2)) ## read the register using xscom reg addresses runStr = "(system_cmp0.phys_mem).read 0x%x 0x%x"%(addr, size) ( result, out ) = quiet_run_command( runStr, output_modes.regular ) ## DEBUG print ">> %s: "%(runStr) + "0x%16.16x"%(result) + " : " + out self.sendMsg("data-response", "%16.16x"%(result) ) # Write data to xscom address.. # This message has data of the format "0dADDR,0dSIZE,hDATA". def write_xscom(self,data): pattern = re.compile("([0-9]+),([0-9]+),([0-9]+)") match = pattern.search(data) addr = int(match.group(1)) size = int(match.group(2)) data = int(match.group(3) ) runStr = "(system_cmp0.phys_mem).write 0x%x 0x%x 0x%x"%(addr, data, size) ( result, out ) = quiet_run_command( runStr, output_modes.regular ) ## DEBUG print ">> %s : "%(runStr) + " 0x%16.16x"%(result) + " : " + out if ( result ): print "simics ERROR running %s: %d "%( syscmd, result ) # Read HRMOR from processors. # This message has no input data def get_hrmor(self,data): hrmor = getHRMOR() self.sendMsg("data-response", "%d"%(hrmor) ) # @fn run_hb_debug_framework # @brief Wrapper function to execute a tool module. # # @param tool - Tool module to execute. # @param toolOpts - String containing tool options. # @param usage - Usage mode or Execute mode. # @param imgPath - Image path override. def run_hb_debug_framework(tool = "Printk", toolOpts = "", outputToString = None, usage = None, imgPath = None, outputFile = None): # Create debug sub-process. fp = DebugFrameworkProcess(tool,toolOpts,outputToString, usage,imgPath,outputFile) # Read / handle messages until there are no more. msg = fp.recvMsg() while msg[0] != "": operations = { "display" : DebugFrameworkProcess.display, "read-data" : DebugFrameworkProcess.read_data, "write-data" : DebugFrameworkProcess.write_data, "read-pnor" : DebugFrameworkProcess.read_pnor, "execute-instrs" : DebugFrameworkProcess.execute_instrs, "ready-for-instr" : DebugFrameworkProcess.ready_for_instr, "get-tool" : DebugFrameworkProcess.get_tool, "get-tool-options" : DebugFrameworkProcess.get_tool_options, "get-img-path" : DebugFrameworkProcess.get_img_path, "write-scom" : DebugFrameworkProcess.write_xscom, "read-scom" : DebugFrameworkProcess.read_xscom, "get-hrmor" : DebugFrameworkProcess.get_hrmor, "exit" : DebugFrameworkProcess.endProcess, } operations[msg[0]](fp,msg[1]) msg = fp.recvMsg() # If in Usage mode, return result string. if (usage or outputToString): return fp.result return None # @fn register_hb_debug_framework_tools # @brief Create a simics command wrapper for each debug tool module. def register_hb_debug_framework_tools(): # Find all modules from within Hostboot subdirectory. files = os.listdir(os.environ['HB_TOOLPATH']+"/Hostboot") # Filter out any prefixed with '_' (utility module) or a '.' (hidden file). pattern = re.compile("[^\._]"); files = [f for f in files if pattern.match(f)] # Filter out modules written for vbu only pattern = re.compile("AutoIpl|ContTrace"); files = [f for f in files if not pattern.match(f)] # Remove the .pm extension from the tool modules. files = [re.sub("\.pm","",f) for f in files]; # Create an entry for each module. for tool in files: # Get usage information for each module, fix text to HTML-like. usage = run_hb_debug_framework(tool, usage = 1) usage = re.sub("<","<", usage); usage = re.sub(">",">", usage); usage = re.sub("\t"," ",usage) usage = "
"+usage+"" # Create command hook. new_command("hb-" + tool, (lambda toolname: lambda options: run_hb_debug_framework(toolname, options, outputFile="hb-debug-"+toolname+".output")) (tool), args = [arg(str_t, "options", "?", "")], alias = "hb-debug-" + tool, type = ["hostboot-commands"], short = "Runs the debug framework for tool " + tool, doc = usage) print "Hostboot Debug Framework: Registered tool:", "hb-" + tool # Do a quick file write test to make sure we can write files and set a # simics variable to let us know if we need to avoid file writes. SIM_run_command("$fileSystemOk=1") try: f = open('hbTracTEST', 'w') f.write("\n") f.close() os.system("rm -rf hbTracTEST") except: SIM_run_command("$fileSystemOk=0") # Return a number/address built from the input list elements. Each element # in the input is a string representation of a byte-sized hex number, for # example '0x2b' or '0x0' or '0xa'. This does no endian conversion, thus # the input needs to be big endian. The length of the input list can be # any size, usually 2, 4, or 8. def hexDumpToNumber(hexlist): strNumber="" for i in range(len(hexlist)): # take away 0x for this byte hexlist[i] = hexlist[i][2:] # zero-fill leading zeroes to make a 2-char string hexlist[i] = hexlist[i].zfill(2) # concatenate onto addr strNumber += hexlist[i] return int(strNumber,16) # Fetch the current HRMOR value. def getHRMOR(): # Note: will default to using the currently selected cpu result = SIM_get_object(simenv.hb_cpu).hrmor return result # Read simics memory and return a list of strings such as ['0x0','0x2b','0x8'] # representing the data read from simics. The list returned may be handed # to hexDumpToNumber() to turn the list into a number. def dumpSimicsMemory(address,bytecount): address = address + getHRMOR() hexlist = map(hex, conf.system_cmp0.phys_mem.memory[[address,address+bytecount-1]]) return hexlist # Read the 64-bit big endian at the address given, return it as a number. def readLongLong(address): hexlist = dumpSimicsMemory(address,8) return hexDumpToNumber(hexlist) def readLong(address): hexlist = dumpSimicsMemory(address,4) return hexDumpToNumber(hexlist) def writeLong(address,datvalue): address = address + getHRMOR() conf.system_cmp0.phys_mem.memory[[address,address+3]] = [0,0,0,datvalue] return # Write simics memory. address is an integer. # data is a list of byte-sized integers. def writeSimicsMemory(address,data): address = address + getHRMOR() size = len(data) conf.system_cmp0.phys_mem.memory[[address, address+size-1]] = data; # Convert an integer to a byte list