diff options
author | Patrick Williams <iawillia@us.ibm.com> | 2011-11-21 15:44:52 -0600 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2011-12-06 16:11:44 -0600 |
commit | 7799749ee2db86f4fb66c4a7a69fb9fb0b46334e (patch) | |
tree | f50027f990e1db0ea79fabb1b8a9afa1a172dfd1 /src/build/debug | |
parent | c761a76534988071d0988daa77b8c51526e2d9f4 (diff) | |
download | talos-hostboot-7799749ee2db86f4fb66c4a7a69fb9fb0b46334e.tar.gz talos-hostboot-7799749ee2db86f4fb66c4a7a69fb9fb0b46334e.zip |
Interactive debug tool.
- Modify debug fw to support writing data.
- Modify debug fw to support clocking model forward.
- Add simics environment support for both.
- Kernel support to start a task when directed.
- Write debug tool to modify kernel structure for debug.
Change-Id: Ic001dfd45f91392aefbc9d5096c5344018d5190e
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/518
Tested-by: Jenkins Server
Reviewed-by: Andrew J. Geissler <andrewg@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/build/debug')
-rw-r--r-- | src/build/debug/Hostboot/CallFunc.pm | 159 | ||||
-rwxr-xr-x | src/build/debug/Hostboot/_DebugFramework.pm | 149 | ||||
-rwxr-xr-x | src/build/debug/simics-debug-framework.pl | 37 | ||||
-rwxr-xr-x | src/build/debug/simics-debug-framework.py | 52 |
4 files changed, 382 insertions, 15 deletions
diff --git a/src/build/debug/Hostboot/CallFunc.pm b/src/build/debug/Hostboot/CallFunc.pm new file mode 100644 index 000000000..69cf0130b --- /dev/null +++ b/src/build/debug/Hostboot/CallFunc.pm @@ -0,0 +1,159 @@ +use strict; + +package Hostboot::CallFunc; +use Exporter; +our @EXPORT_OK = ('main'); + +use constant CALLFUNC_DEBUG_READY_OFFSET => 0; +use constant CALLFUNC_DEBUG_RUNNING_OFFSET => CALLFUNC_DEBUG_READY_OFFSET + 1; +use constant CALLFUNC_DEBUG_COMPLETE_OFFSET => CALLFUNC_DEBUG_READY_OFFSET + 2; +use constant CALLFUNC_DEBUG_ENTRY_OFFSET => CALLFUNC_DEBUG_READY_OFFSET + 8; +use constant CALLFUNC_DEBUG_RETVAL_OFFSET => CALLFUNC_DEBUG_ENTRY_OFFSET + 8; +use constant CALLFUNC_DEBUG_PARMS_OFFSET => CALLFUNC_DEBUG_RETVAL_OFFSET + 8; +use constant CALLFUNC_DEBUG_PARMS => 8; +sub main +{ + my ($packName,$args) = @_; + + # Parse 'debug' option. + my $debug = 0; + if (defined $args->{"debug"}) + { + $debug = 1; + } + + # Parse function name from options. + my $function = ""; + if (defined $args->{"function"}) + { + $function = $args->{"function"}; + } + elsif (defined $args->{"func"}) + { + $function = $args->{"func"}; + } + else + { + ::userDisplay "Must give a function to execute.\n"; + return; + } + + # Parse function arguments from options. + my @parms = (); + my $parms_string = ""; + if (defined $args->{"arguments"}) + { + $parms_string = $args->{"arguments"} + } + elsif (defined $args->{"args"}) + { + $parms_string = $args->{"args"}; + } + elsif (defined $args->{"parameters"}) + { + $parms_string = $args->{"parameters"}; + } + elsif (defined $args->{"parms"}) + { + $parms_string = $args->{"parms"}; + } + @parms = map eval, split /,/, $parms_string; + + # Parse 'force' argument. + my $force = 0; + if (defined $args->{"force"}) + { + $force = 1; + } + + + # Ensure environment is in the proper state for running instructions. + if (!::readyForInstructions()) + { + ::userDisplay "Cannot execute while unable to run instructions.\n"; + return; + } + + # Find symbol's TOC address. + my $toc = ::findSymbolTOCAddress($function); + if ($debug) + { + ::userDisplay("Symbol $function TOC at $toc.\n"); + } + + # Find interactive debug structure in the kernel. + my $address = (::findSymbolAddress("CpuManager::cv_interactive_debug"))[0]; + + if ((not defined $toc) || (not defined $address)) + { + ::userDisplay "Cannot find symbol to execute $function.\n"; + return; + } + + # Verify kernel isn't busy with an outstanding request. + if ((0 != ::read16 ($address + CALLFUNC_DEBUG_READY_OFFSET)) && + (0 == $force)) + { + ::userDisplay "Another command is still pending.\n"; + return; + } + + # Write entry point (function TOC) and parameters into debug structure. + ::write64(($address + CALLFUNC_DEBUG_ENTRY_OFFSET), $toc); + while(($#parms + 1) != CALLFUNC_DEBUG_PARMS) + { + push @parms, 0x0; + } + my $i = 0; + while ($i != CALLFUNC_DEBUG_PARMS) + { + if ($debug) + { + ::userDisplay "Param $i = ".$parms[$i]."\n"; + } + ::write64(($address + CALLFUNC_DEBUG_PARMS_OFFSET + 8*$i), $parms[$i]); + $i = $i + 1; + } + + # Set ready state. + ::write32(($address + CALLFUNC_DEBUG_READY_OFFSET), 0x01000000); + + # Clock forward until kernel marks 'complete' state. + my $i = 0; + while((0 != ::read16($address + CALLFUNC_DEBUG_READY_OFFSET)) && + (0 == ::read8($address + CALLFUNC_DEBUG_COMPLETE_OFFSET)) && + ($i < 50)) + { + ::executeInstrCycles(100000); + $i = $i + 1; + if ($debug) + { + ::userDisplay("Loop $i.\n"); + } + } + + # Verify successful execution ('complete' state set). + if (0 == ::read8($address + CALLFUNC_DEBUG_COMPLETE_OFFSET)) + { + ::userDisplay "Command failed to complete.\n"; + return; + } + + # Display return value. + ::userDisplay ::read64($address + CALLFUNC_DEBUG_RETVAL_OFFSET)."\n"; +} + +sub help +{ + ::userDisplay "Tool: CallFunc\n"; + ::userDisplay "\tInteractively execute a function.\n"; + ::userDisplay "\n Options:\n"; + ::userDisplay "\tfunction='function name' - Function to execute.\n"; + ::userDisplay "\targuments=arg0,arg1... - List of arguments to pass.\n"; + ::userDisplay "\tforce - Run command even if state doesn't not appear ". + "correct.\n"; + ::userDisplay "\n\tfunc can be used as a short-name for 'function'.\n"; + ::userDisplay "\targs, parameters, or parms can be used as a short-name "; + ::userDisplay "for arguments.\n"; +} + diff --git a/src/build/debug/Hostboot/_DebugFramework.pm b/src/build/debug/Hostboot/_DebugFramework.pm index a55794ebc..fd62127bf 100755 --- a/src/build/debug/Hostboot/_DebugFramework.pm +++ b/src/build/debug/Hostboot/_DebugFramework.pm @@ -44,13 +44,16 @@ use Exporter 'import'; our @EXPORT = ( 'callToolModule', 'callToolModuleHelp', 'parseToolOpts', 'determineImagePath', - 'findSymbolAddress', 'findSymbolByAddress', + 'findSymbolAddress', 'findSymbolTOCAddress', + 'findSymbolByAddress', 'findModuleByAddress', 'littleendian', - 'read64', 'read32', 'read16', 'read8', 'readStr' + 'read64', 'read32', 'read16', 'read8', 'readStr', + 'write64', 'write32', 'write16', 'write8' ); -our ($parsedSymbolFile, %symbolAddress, %addressSymbol, %symbolSize); +our ($parsedSymbolFile, %symbolAddress, %symbolTOC, + %addressSymbol, %symbolSize); our ($parsedModuleFile, %moduleAddress); our (%toolOpts); @@ -58,6 +61,7 @@ BEGIN { $parsedSymbolFile = 0; %symbolAddress = (); + %symbolTOC = (); %addressSymbol = (); %symbolSize = (); @@ -109,18 +113,50 @@ sub callToolModuleHelp # # Parses a space deliminated string of options for use by the tool. # +# Allows an option to contain spaces itself by wrapping the value in a +# single quote. +# # @param string - Tool option list. # -# Example: "foo bar=/abcd/efg" --> { "foo" => 1 , "bar" => "/abcd/efg" } +# Example: +# "foo bar=/abcd/efg" --> { "foo" => 1 , "bar" => "/abcd/efg" } +# "foo='bar nil'" --> { "foo" => "bar nil" } # sub parseToolOpts { my $toolOptions = shift; + my $partial = ""; foreach my $opt (split / /, $toolOptions) { + # Search for a single-quoted word in the option string. + if (($opt =~ m/'/) and (not ($opt =~ m/'.*'/))) + { + # If partial is not empty, this single-quote terminates the string. + if ($partial ne "") + { + $opt = $partial." ".$opt; + $partial = ""; + } + # Otherwise, append it to the partial string. + else + { + $partial = $opt; + next; + } + } + # Append a word to a partially completed string in progress. + elsif ($partial ne "") + { + $partial = $partial." ".$opt; + next; + } + + # At this point "opt" is either a free-standing argument set or a + # fully complete single-quote deliminated string. if ($opt =~ m/=/) { my ($name,$value) = split /=/, $opt; + $value =~ s/^'([^']*)'$/$1/; # Trim out the 's. $toolOpts{$name} = $value; } else @@ -185,11 +221,26 @@ sub parseSymbolFile my $addr = hex $2; my $tocAddr = hex $3; my $size = hex $4; + my $type = $1; - $symbolAddress{$name} = $addr; - $symbolSize{$name} = $size; $addressSymbol{$addr} = $name; $addressSymbol{$tocAddr} = $name; + + # Use only the first definition of a symbol. + # This is useful for constructors where we only want to call the + # 'in-charge' version of the constructor. + if (defined $symbolAddress{$name}) + { + next; + } + + $symbolAddress{$name} = $addr; + if ($type eq "F") + { + $symbolTOC{$name} = $tocAddr; + } + $symbolSize{$name} = $size; + } close(FILE); @@ -212,6 +263,21 @@ sub findSymbolAddress return ($symbolAddress{$name}, $symbolSize{$name} ); } +# @sub findSymbolTOCAddress +# Searches a syms file for the address of the TOC of a symbol. +# +# @param string - Symbol to search for. +# @return TOC address or 'not-defined'. +# +sub findSymbolTOCAddress +{ + my $name = shift; + + parseSymbolFile(); + + return $symbolTOC{$name}; +} + # @sub findSymbolByAddress # # Searchs a syms file for the symbol name at a particular address. @@ -393,4 +459,75 @@ sub readStr return $result; } +# @sub write64 +# +# Write a 64-bit unsigned integer to an address. +# +# @param Address to write to. +# @param Value to write +# +sub write64 +{ + my $addr = shift; + my $value = shift; + + $value = pack("Q", $value); + if (littleendian()) { $value = reverse($value); } + + my $result = ::writeData($addr, 8, $value); +} + +# @sub write32 +# +# Write a 32-bit unsigned integer to an address. +# +# @param Address to write to. +# @param Value to write +# +sub write32 +{ + my $addr = shift; + my $value = shift; + + $value = pack("L", $value); + if (littleendian()) { $value = reverse($value); } + + my $result = ::writeData($addr, 4, $value); +} + +# @sub write16 +# +# Write a 16-bit unsigned integer to an address. +# +# @param Address to write to. +# @param Value to write +# +sub write16 +{ + my $addr = shift; + my $value = shift; + + $value = pack("S", $value); + if (littleendian()) { $value = reverse($value); } + + my $result = ::writeData($addr, 2, $value); +} + +# @sub write8 +# +# Write a 8-bit unsigned integer to an address. +# +# @param Address to write to. +# @param Value to write +# +sub write8 +{ + my $addr = shift; + my $value = shift; + + $value = pack("C", $value); + + my $result = ::writeData($addr, 1, $value); +} + __END__ diff --git a/src/build/debug/simics-debug-framework.pl b/src/build/debug/simics-debug-framework.pl index a255439a9..90a7822a9 100755 --- a/src/build/debug/simics-debug-framework.pl +++ b/src/build/debug/simics-debug-framework.pl @@ -106,6 +106,43 @@ sub readData return ""; } +# @sub writeData +# @brief Send a 'write-data' type message to Python. +sub writeData +{ + my $addr = shift; + my $size = shift; + my $value = shift; + + my $value = unpack("H*", $value); + sendIPCMsg("write-data", "$addr,$size,$value"); + + return; +} + +# @sub executeInstrCycles +# @brief Send a 'execute-instrs' type message to Python. +sub executeInstrCycles +{ + my $cycles = shift; + sendIPCMsg("execute-instrs", "$cycles"); +} + +# @sub readyForInstructions +# @brief Send a 'ready-for-instr' type message to Python. +# @returns 0 - Not ready or 1 - Ready +sub readyForInstructions +{ + sendIPCMsg("ready-for-instr", ""); + + my ($type, $data) = recvIPCMsg(); + if ("1" eq $data) + { + return 1; + } + return 0; +} + # Image path global. my $imgPath = ""; sub getImgPath diff --git a/src/build/debug/simics-debug-framework.py b/src/build/debug/simics-debug-framework.py index 9cbfe8090..dcd05cee1 100755 --- a/src/build/debug/simics-debug-framework.py +++ b/src/build/debug/simics-debug-framework.py @@ -86,17 +86,19 @@ class DebugFrameworkProcess: process = ""; # subprocess object. tool = ""; # string - tool module name. toolOptions = ""; # string - tool options - usage = None; # mode - Usage output instead of Execution. + outputToString = None; # mode - String output instead of STDOUT. imgPath = "./"; # 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 = "", - usage = None, imgPath = "./",outputFile = None): + outputToString = None, usage = None, + imgPath = "./",outputFile = None): # Determine sub-process arguments. process_args = ["./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, @@ -104,7 +106,7 @@ class DebugFrameworkProcess: # Update instance variables. self.tool = tool; self.toolOptions = toolOptions; - self.usage = usage; + self.outputToString = outputToString; self.imgPath = imgPath; self.outputFile = open(outputFile, 'w') if outputFile else None; @@ -129,7 +131,7 @@ class DebugFrameworkProcess: # Display string (or save to result in Usage mode). def display(self,data): - if (self.usage): + if (self.outputToString): self.result += data else: print data, @@ -137,10 +139,10 @@ class DebugFrameworkProcess: print >>self.outputFile,data, # Read data from memory. - # This message has data of the format "0xADDRESS,0xSIZE". + # 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); + match = pattern.search(data) addr = int(match.group(1)) size = int(match.group(2)) @@ -148,6 +150,32 @@ class DebugFrameworkProcess: data = "".join(map(chr, conf.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.phys_mem.memory[[addr, addr+size-1]] = 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)) + + if (not SIM_simics_is_running()): + SIM_continue(cycles) + + 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) @@ -168,15 +196,20 @@ class DebugFrameworkProcess: # @param usage - Usage mode or Execute mode. # @param imgPath - Image path override. def run_hb_debug_framework(tool = "Printk", toolOpts = "", - usage = None, imgPath = "./", outputFile = None): + outputToString = None, usage = None, + imgPath = "./", outputFile = None): # Create debug sub-process. - fp = DebugFrameworkProcess(tool,toolOpts,usage,imgPath,outputFile) + 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, + "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, @@ -186,10 +219,11 @@ def run_hb_debug_framework(tool = "Printk", toolOpts = "", msg = fp.recvMsg() # If in Usage mode, return result string. - if (usage): + 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(): |