#
# This file contains an 'Intel Peripheral Driver' and is
# licensed for Intel CPUs and chipsets under the terms of your
# license agreement with Intel or your vendor.  This file may
# be modified by the user, subject to additional terms of the
# license agreement
#
# ATTENTION: The most scripts are written in python language which is supported
#            by GDB 7.0 offical. So you need use GDB later than 7.0.
#
# Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>
#
#   This software and associated documentation (if any) is furnished
#   under a license and may only be used or copied in accordance
#   with the terms of the license. Except as permitted by such
#   license, no part of this software or documentation may be
#   reproduced, stored in a retrieval system, or transmitted in any
#   form or by any means without the express written consent of
#   Intel Corporation.

################################################################################
# Configure GDB
################################################################################
set remotetimeout 20
set disassemble-next-line auto
set step-mode on
set disassembly-flavor intel
set height 0
set prompt (udb) 

define hook-stop
  stop-handler
end

################################################################################
# Python Scripts
################################################################################
python
import sys
import os
import re
import gdb

SCRIPT_BANNER_BEGIN = "##############################################################\n# This gdb configuration file contains settings and scripts\n# for debugging UDK firmware."
SUPPORT_PENDING_BREAKPOINTS = "# Setting pending breakpoints is supported."
NOT_SUPPORT_PENDING_BREAKPOINTS = "# WARNING: Setting pending breakpoints is NOT supported!\n# Additional commands for source level debugging will be added!"
GDBSERVER_NOT_CONNECTED = "# ERROR: GdbServer is not connected\n# Load this file after connecting to GdbServer"
SCRIPT_BANNER_END = "##############################################################"

ARGUMENT_TOO_FEW = "Incorrect usage. There are too few arguments in command"
ARGUMENT_TOO_MANY = "Incorrect usage. There are too many arguments in command"
ARGUMENT_INVALID_SUBINDEX = "invalid subindex parameter"
ARGUMENT_INVALID_INDEX = "invalid index parameter"
ARGUMENT_INVALID_SIZE = "invalid size parameter"
ARGUMENT_INVALID_PORT = "invalid port parameter"
ARGUMENT_MUST_BE_NUMBER_1_BASED = "The argument must be a number (1-based)"
ARGUMENT_1_TO_20_EXPECTED = "an integer from 1 to 20 is expected."
ARGUMENT_ON_OFF_EXPECTED = 'either an "on" or "off" expected.'
ARGUMENT_HEX_EXPECTED = "a hex address is expected."
IO_FAILURE = "Unexpected IO port access error."

CPUID_INPUT = "INDEX: %08x  SUBINDEX: %08x"
CPUID_OUTPUT = "EAX: %08x  EBX: %08x  ECX: %08x  EDX: %08x"
FAILED_TO_EXECUTE_COMMAND = "Unable to execute command: %s"
IO_WATCH_POINT_INFO = "IO Watchpoint %d: %X(%x)"
SETTING_PATH_MAPPING = "Set path mapping: `%s' -> `%s'"
EXCEPTION_INFO = "Target encountered an exception: Vector = %d, Error Code = %08x"
TARGET_IS_RUNNING_AFTER_RESET = "TARGET is resetting, continue running..."
LOADING_SYMBOL = "Loading symbol for address: 0x%x"
LOADING_SYMBOL_FOR_MODULE = "Loading symbol at address %s for %s"
SKIPPING_SYMBOL_FOR_MODULE = "Skipping symbol at address %s for %s"
UNSUPPORTED_DEBUG_INFORMATION = "Unsupported debug information found: PDB format"
MISSING_DEBUG_INFORMATION = "Missing debug information"
FAILED_TO_FIND_SYMBOL_FILE = "Unable to find the debug symbol file."
FAILED_TO_LOAD_SYMBOL = "Unable to load symbol file"
FAILED_TO_FIND_DEBUG_INFORMATION = "Unable to find the debug information for source level debugging."

class UdkCommandHelper:
    @staticmethod
    def to_long(s):
        """Turn a hex string into integer/long value, e.g.: '3412' -> 0x1234."""
        l = 0
        for i in range(len(s)/2):
            l += int(s[i*2:(i+1)*2], 16) << (8 * i)
        return l

    @staticmethod
    def to_string(l, n):
        """Turn a integer/long value into a hex encoded string, e.g.: 0x1234 -> '3412'."""
        s = ""
        for i in range(n):
            s += "%02x" % (l & 0xff)
            l >>= 8
        return s

    @staticmethod
    def checkParameterEx(arg, count_min, count_max):
        try:
            args = gdb.string_to_argv(arg)
        except AttributeError:
            # GDB 7.0 doesn't have string_to_argv() interface
            args = []
            for _arg in arg.split(' '):
                if _arg != '':
                    args.append(_arg)

        if len(args) >= count_min and len(args) <= count_max:
            return args
        if len(args) < count_min:
            print ARGUMENT_TOO_FEW
            return None
        else:
            print ARGUMENT_TOO_MANY
            return None

    @staticmethod
    def checkParameter(arg, count):
        return UdkCommandHelper.checkParameterEx(arg, count, count)

    _debugMode = False
    @classmethod
    def executeCommand(cls, cmd):
        try:
            if cls._debugMode:
                print "run: %s" % cmd

            try:
                lines = gdb.execute(cmd, True, True).split("\n")
            except TypeError:
                # GDB 7.0 's execute() doesn't have the 3rd parameter to return all the return string
                FILE = "%s/.udk-script.temp" % os.getenv('HOME')
                gdb.execute("set logging file %s" % FILE)
                gdb.execute("set logging overwrite")
                gdb.execute("set logging redirect on")
                gdb.execute("set logging on")
                gdb.execute(cmd)
                gdb.execute("set logging off")
                gdb.execute("set logging redirect off")
                file = open(FILE)
                lines = [ line.rstrip('\n') for line in file.readlines() ]
                file.close()

            if cls._debugMode:
                for line in lines:
                    print "result: %s" % line
            return lines
        except Exception, e:
            print e

    @staticmethod
    def executeUdkExtensionCommand(cmd):
        for line in UdkCommandHelper.executeCommand("maint packet qUdkExtension:%s" % (cmd)):
            if line.startswith("received: "):
                return line[11:-1]
        return ""

    @staticmethod
    def getTargetDebugInfo(pc):
        response = UdkCommandHelper.executeUdkExtensionCommand("symbol:0x%x" % pc)
        # /home/ray/a.dll;.text=0x1234;.data=0x4567
        array = response.split(";")
        if (len(array) > 1):
            debug_info = (array[0], {})
            for section in array[1:]:
                (section_name, section_addr) = section.split("=")
                debug_info[1][section_name] = long(section_addr, 16)
            return debug_info
        return None

    _supportExpat = None

    @classmethod
    def supportExpat(cls):
        if cls._supportExpat != None:
            return cls._supportExpat

        cls._supportExpat = False
        if UdkCommandHelper.executeUdkExtensionCommand("checkexpat:start") == "OK":
            UdkCommandHelper.executeCommand("info sharedlibrary")
            if UdkCommandHelper.executeUdkExtensionCommand("checkexpat:end") == "OK":
                # if qXfer:libraries:read is received by gdb server between check:start and check:end
                cls._supportExpat = True
        return cls._supportExpat

class Edk2Cpuid(gdb.Command):
    """Retrieves CPUID information: cpuid [INDEX] [SUBINDEX].
INDEX is the value of EAX priori to executing CPUID instruction (defaults to 1).
SUBINDEX is the value of ECX priori to executing CPUID instruction (defaults to 0)."""

    def __init__(self):
        super(Edk2Cpuid, self).__init__("cpuid", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameterEx(arg, 0, 2)
        if args == None:
            return

        if len(args) < 2:
            subindex = 0
        else:
            try:
                subindex = int(args[1], 16)
            except:
                print ARGUMENT_INVALID_SUBINDEX
                return
        if len(args) < 1:
            index = 1
        else:
            try:
                index = int(args[0], 16)
            except:
                print ARGUMENT_INVALID_INDEX
                return
        response = UdkCommandHelper.executeUdkExtensionCommand("cpuid,%08x,%08x" % (index, subindex))
        try:
            print CPUID_INPUT % (index, subindex)
            (eax, ebx, ecx, edx) = [int(v, 16) for v in response.split(",")]
            print CPUID_OUTPUT % (eax, ebx, ecx, edx)
        except Exception, ex:
            print FAILED_TO_EXECUTE_COMMAND % response
Edk2Cpuid()

class Edk2Io(gdb.Command):
    """Access IO: io/SIZE PORT [VALUE].
PORT is an expression for the IO address to Access.
SIZE letters are b(byte), h(halfword), w(word).
VALUE is an expression to write to the PORT."""

    def __init__(self, command_str = "io", command_class = gdb.COMMAND_DATA, complete_type = gdb.COMPLETE_NONE):
        super(Edk2Io, self).__init__(command_str, command_class, complete_type)

    def parse_port(self, args):
        size_dic = {"/b":1, "/h":2, "/w":4}
        try:
            size = size_dic[args[0]]
        except:
            print ARGUMENT_INVALID_SIZE
            raise

        try:
            port = int(args[1], 16)
        except:
            print ARGUMENT_INVALID_PORT
            raise

        return (port, size)

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameterEx(arg, 2, 3)
        if args == None:
            return

        try:
            (port, size) = self.parse_port(args)
        except:
            return

        if len(args) == 2:
            value = UdkCommandHelper.to_long(UdkCommandHelper.executeUdkExtensionCommand("io%x,%x" % (port, size)))
            print "%0*x" % (size * 2, value)
        else:
            value = int(args[2], 16)
            response = UdkCommandHelper.executeUdkExtensionCommand("IO%x,%x:%s" % (port, size, UdkCommandHelper.to_string(value, size)))
            if response != "OK":
                print FAILED_TO_EXECUTE_COMMAND % response
Edk2Io()

class Edk2IoWatch(Edk2Io):
    """Set a watchpoint for an IO address.
Usage: iowatch/SIZE PORT
A watchpoint stops execution of your program whenever the
IO address is either read or written.
PORT is an expression for the IO address to Access.
SIZE letters are b(byte), h(halfword), w(word).
VALUE is an expression to write to the PORT."""

    def __init__(self):
        super(Edk2IoWatch, self).__init__("iowatch", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
        self._watchpoints = {}
        self._num_watchpoints = 0

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameter(arg, 2)
        if args == None:
            return

        try:
            (port, size) = self.parse_port(args)
        except:
            return

        response = UdkCommandHelper.executeUdkExtensionCommand("Z5,%x,%x" % (port, size))
        if response != "OK":
            print FAILED_TO_EXECUTE_COMMAND % response
        else:
            self._num_watchpoints += 1
            self._watchpoints[self._num_watchpoints] = (port, size)
            print IO_WATCH_POINT_INFO % (self._num_watchpoints, port, size)

    def list(self, arg):
        args = UdkCommandHelper.checkParameterEx(arg, 0, 1)
        if args == None:
            return

        if len(args) == 1:
            try:
                args[0] = int(args[0], 10)
            except:
                args[0] = 0

            if args[0] == 0:
                print ARGUMENT_MUST_BE_NUMBER_1_BASED
                return

        print "Num\tPort\tSize"
        for index, (port, size) in self._watchpoints.items():
            if len(args) == 0 or args[0] == index:
                print "%d\t0x%x\t%d" % (index, port, size)

    def delete(self, arg):
        args = UdkCommandHelper.checkParameterEx(arg, 0, 1)
        if args == None:
            return

        if len(args) == 1:
            try:
                args[0] = int(args[0], 10)
            except:
                args[0] = 0

            if args[0] == 0:
                print ARGUMENT_MUST_BE_NUMBER_1_BASED
                return

        for index, (port, size) in self._watchpoints.items():
            if len(args) == 0 or args[0] == index:
                response = UdkCommandHelper.executeUdkExtensionCommand("z5,%x,%x" % (port, size))
                if response != "OK":
                    print FAILED_TO_EXECUTE_COMMAND % response
                else:
                    del self._watchpoints[index]
IoWatchpoints = Edk2IoWatch()

class Edk2InfoIoWatchpoints(gdb.Command):
    """Status of specified IO watchpoint (all watchpoints if no argument)."""

    def __init__(self):
        super(Edk2InfoIoWatchpoints, self).__init__("info iowatchpoints", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        IoWatchpoints.list(arg)
Edk2InfoIoWatchpoints()

class Edk2DelIoWatchpoints(gdb.Command):
    """Delete some IO watchpoints.
Argument is IO watchpoints number.
To delete all IO watchpoints, give no argument."""

    def __init__(self):
        super(Edk2DelIoWatchpoints, self).__init__("delete iowatchpoints", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        IoWatchpoints.delete(arg)
Edk2DelIoWatchpoints()

class Edk2Msr(gdb.Command):
    """Read/Write MSR."""

    def __init__(self):
        super(Edk2Msr, self).__init__("msr", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameterEx(arg, 1, 2)
        if args != None:
            if len(args) == 1:
                index = int(args[0], 16)
                response = UdkCommandHelper.executeUdkExtensionCommand("msr%x" % index)
                m = re.match('E([0-9A-Fa-f]+)', response)
                if m != None:
                    print FAILED_TO_EXECUTE_COMMAND % response
                else:
                    value = long(response, 16)
                    print "%016x" % value
            else:
                index = int(args[0], 16)
                value = long(args[1], 16)
                response = UdkCommandHelper.executeUdkExtensionCommand("MSR%x=%x" % (index, value))
                if response != "OK":
                    print FAILED_TO_EXECUTE_COMMAND % response
Edk2Msr()

class Edk2Pci(gdb.Command):
    """Display PCI device list or PCI function configuration space.
Usage: pci [Bus [Dev [Func]]]
Options:
  Bus           When only Bus is specified, it is the starting bus number for
                enumeration. 0 by default if not specified.
                Otherwise the bus number of the PCI device whose configuration
                space is to be dumped.
  Dev           Device number of the PCI device whose configuration space is
                to be dumped.
  Func          Function number of the PCI device whose configuration space
                is to be dumped. 0 by default if not specified."""

    def __init__(self):
        super(Edk2Pci, self).__init__("pci", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)

    def __read_pci_conf(self, bus, dev, func, reg, size):
        if not (bus in range (256) and dev in range (32) and func in range (8)):
            return None
        if not size in [1, 2, 4]:
            return None
        if not (reg + size - 1) in range (256):
            return None
        if (reg & (size - 1)) != 0:
            return None

        cfg_addr = (1 << 31) + (bus << 16) + (dev << 11) + (func << 8) + (reg & ~3)
        response = UdkCommandHelper.executeUdkExtensionCommand("IO%x,%x:%s" % (0xcf8, 4, UdkCommandHelper.to_string(cfg_addr, 4)))
        if response != "OK":
            print FAILED_TO_EXECUTE_COMMAND % response
            print IO_FAILURE
            return None
        cfg_data = UdkCommandHelper.to_long(UdkCommandHelper.executeUdkExtensionCommand("io%x,%x" % (0xcfc + (reg & 3), size)))
        return cfg_data

    def __get_device_class(self, base_class_code, sub_class_code, prog_interface):
        class_dic = {
            # list not complete. can be updated if needed.
            (0,    0,    0)   :"Backward compatible device",
            (0,    1,    0)   :"VGA-compatible device",

            (1,    0,    0)   :"SCSI bus controller",
            (1,    1,    0)   :"IDE controller",
            (1,    2,    0)   :"Floppy disk controller",
            (1,    3,    0)   :"IPI bus controller",
            (1,    4,    0)   :"RAID controller",
            (1,    5, 0x20)   :"ATA controller with ADMA interface - single stepping",
            (1,    5, 0x30)   :"ATA controller with ADMA interface - continuous operation",
            (1,    6,    0)   :"Serial ATA controller - vendor specific interface",
            (1,    6,    1)   :"Serial ATA controller - AHCI 1.0 interface",
            (1,    6,    2)   :"Serial Storage Bus Interface",
            (1,    7,    0)   :"Serial Attached SCSI (SAS) controller",
            (1,    7,    1)   :"Serial Storage Bus Interface",
            (1,    8,    0)   :"Solid State Storage Controller",
            (1,    8,    1)   :"Solid State Storage Controller - NVMHCI 1.0 interface",
            (1,    8,    2)   :"Solid State Storage Controller - Enterprise NVMHCI 1.0",
            (1,    0x80, 0)   :"Other mass storage controller",

            (2,    0,    0)   :"Ethernet controller",
            (2,    1,    0)   :"Token Ring controller",
            (2,    2,    0)   :"FDDI controller",
            (2,    3,    0)   :"ATM controller",
            (2,    4,    0)   :"ISDN controller",
            (2,    5,    0)   :"WorldFip controller",
            (2,    6,    0)   :"PICMG 2.14 Multi Computing",
            (2,    7,    0)   :"InfiniBand* Controller",
            (2,    0x80, 0)   :"Other network controller",

            (3,    0,    0)   :"VGA-compatible controller",
            (3,    0,    1)   :"8514-compatible controller",
            (3,    1,    0)   :"XGA controller",
            (3,    2,    0)   :"3D controller",
            (3,    0x80, 0)   :"Other display controller",

            (4,    0,    0)   :"Video device",
            (4,    1,    0)   :"Audio device",
            (4,    2,    0)   :"Computer telephony device",
            (4,    3,    0)   :"Mixed mode device",
            (4,    0x80, 0)   :"Other multimedia device",

            (5,    0,    0)   :"RAM",
            (5,    1,    0)   :"Flash",
            (5,    0x80, 0)   :"Other memory controller",

            (6,    0,    0)   :"Host bridge",
            (6,    1,    0)   :"ISA bridge",
            (6,    2,    0)   :"EISA bridge",
            (6,    3,    0)   :"MCA bridge",
            (6,    4,    0)   :"PCI-to-PCI bridge",
            (6,    4,    1)   :"Subtractive Decode PCI-to-PCI bridge",
            (6,    5,    0)   :"PCMCIA bridge",
            (6,    6,    0)   :"NuBus bridge",
            (6,    7,    0)   :"CardBus bridge",
            (6,    8,    0)   :"RACEway bridge",
            (6,    9, 0x40)   :"Semi-transparent PCI-to-PCI bridge with the primary PCI bus side facing the system host processor",
            (6,    9, 0x80)   :"Semi-transparent PCI-to-PCI bridge with the secondary PCI bus side facing the system host processor",
            (6,    0xA,  0)   :"InfiniBand-to-PCI host bridge",
            (6,    0xB,  0)   :"Advanced Switching to PCI host bridge - Custom Interface",
            (6,    0xB,  1)   :"Advanced Switching to PCI host bridge - ASI-SIG Defined Portal Interface",
            (6,    0x80, 0)   :"Other bridge device",

            (7,    0x80, 0)   :"Other communications device",

            (0xc,  3,    0)   :"UHCI controller",
            (0xc,  3,    0x10):"OHCI controller",
            (0xc,  3,    0x20):"EHCI controller",
            (0xc,  3,    0xFE):"USB device",
            (0xc,  4,    0)   :"Fibre Channel",
            (0xc,  5,    0)   :"SMBus (System Management Bus)",
            (0xc,  6,    0)   :"InfiniBand (deprecated)",
            (0xc,  7,    0)   :"IPMI SMIC Interface",
            (0xc,  7,    1)   :"IPMI Keyboard Controller Style Interface",
            (0xc,  7,    2)   :"IPMI Block Transfer Interface",
            (0xc,  8,    0)   :"SERCOS Interface Standard (IEC 61491)",
            (0xc,  9,    0)   :"CANbus",
            (0xc,  0x80, 0)   :"Other Serial Bus Controllers",

            (0xd,  0,    0)   :"iRDA compatible controller",
            (0xd,  1,    0)   :"Consumer IR controller",
            (0xd,  1, 0x10)   :"UWB Radio controller ",
            (0xd,  0x10, 0)   :"RF controller",
            (0xd,  0x11, 0)   :"Bluetooth",
            (0xd,  0x12, 0)   :"Broadband",
            (0xd,  0x20, 0)   :"Ethernet (802.11a - 5 GHz)",
            (0xd,  0x21, 0)   :"Ethernet (802.11b - 2.4 GHz)",
            (0xd,  0x80, 0)   :"Other type of wireless controller",

            (0x10, 0,    0)   :"Network and computing en/decryption",
            (0x10, 0x10, 0)   :"Entertainment en/decryption",
            (0x10, 0x80, 0)   :"Other en/decryption",

            (0x11, 0,    0)   :"DPIO modules",
            (0x11, 1,    0)   :"Performance counters",
            (0x11, 0x80, 0)   :"Other data acquisition/signal processing controllers",

            (0x12, 0,    0)   :"Processing Accelerator - vendor-specific interface"
            }

        if (base_class_code == 1 and sub_class_code == 1) or \
           (base_class_code == 2 and sub_class_code == 6) or \
           (base_class_code == 6 and sub_class_code == 8):
            prog_interface = 0
        if class_dic.has_key((base_class_code, sub_class_code, prog_interface)):
            return class_dic[(base_class_code, sub_class_code, prog_interface)]
        else:
            return "Unkown device class (Base:%02x Sub:%02x PI:%02x)" % (base_class_code, sub_class_code, prog_interface)

    def __enum_pci_bus(self, bus):
        for dev in range (32):
            for func in range (8):
                # Detect the presence of the PCI device
                vendor_id = self.__read_pci_conf(bus, dev, func, 0, 2)
                if vendor_id == 0xffff:
                    if func == 0:
                        break
                else:
                    device_id = self.__read_pci_conf(bus, dev, func, 2, 2)
                    base_class_code = self.__read_pci_conf(bus, dev, func, 0xb, 1)
                    sub_class_code = self.__read_pci_conf(bus, dev, func, 0xa, 1)
                    prog_interface = self.__read_pci_conf(bus, dev, func, 9, 1)
                    header_type = self.__read_pci_conf(bus, dev, func, 0xe, 1)

                    print "%02x  %02x  %1x    %04x   %04x   %s" % (bus, dev, func, vendor_id, device_id, self.__get_device_class(base_class_code, sub_class_code, prog_interface))

                    # Enumerate the PCI bus recursively if the PCI device is P2P bridge
                    if (header_type & 0x7f) == 1:
                        secondary_bus = self.__read_pci_conf(bus, dev, func, 0x19, 1)
                        if secondary_bus > bus:
                            self.__enum_pci_bus (secondary_bus)
                    if func == 0 and (header_type & 0x80) == 0:
                        break

    def __dump_pci_conf(self, bus, dev, func):
        print "Dump PCI configuration space for Bus %02x Device %02x Function %02x" % (bus, dev, func)
        print "     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"
        print "     -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"

        reg = 0
        for line in range (0x10):
            print "%02x: " % (line * 0x10),
            for index in range (0x10):
                value = self.__read_pci_conf(bus, dev, func, reg, 1)
                print "%02x" % value,
                reg = reg + 1
            print

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameterEx(arg, 0, 3)
        if args != None:
            if len(args) in range(2):
                # Display PCI device list
                if len(args) == 0:
                    bus = 0
                else:
                    bus = int(args[0], 16)

                if not bus in range(256):
                    print "Invalid bus number"
                    return
                print "Bus Dev Func Vendor Device Class"
                print "--- --- ---- ------ ------ -----"
                self.__enum_pci_bus(bus)
                return
            
            # Dump PCI function configuration space
            bus = int(args[0], 16)
            dev = int(args[1], 16)
            if len(args) == 2:
                func = 0
            else:
                func = int(args[2], 16)

            if not bus in range(256):
                print "Invalid bus number"
                return
            if not dev in range(32):
                print "Invalid device number"
                return
            if not func in range(8):
                print "Invalid function number"
                return

            self.__dump_pci_conf(bus, dev, func)

Edk2Pci()

class Edk2MapPath(gdb.Command):
    """Get the substitution rule for the source file names.
    """

    def __init__(self):
        super(Edk2MapPath, self).__init__("mappath", gdb.COMMAND_FILES, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args != None:
            response = UdkCommandHelper.executeUdkExtensionCommand("pathmapping")
            paths = map(os.path.normpath, map(str.strip, response.split(";")))
            if len(paths) == 2:
                (src_path, dst_path) = paths
                if src_path != dst_path:
                    print SETTING_PATH_MAPPING % (src_path, dst_path)
                    UdkCommandHelper.executeCommand("set substitute-path %s %s" % tuple(map(lambda s:s+os.sep, (src_path, dst_path))))
Edk2MapPath()

class Edk2InfoException(gdb.Command):
    """Show the exception information.
    """

    def __init__(self):
        super(Edk2InfoException, self).__init__("info exception", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args != None:
            response = UdkCommandHelper.executeUdkExtensionCommand("exception")
            if response != "":
                (vector, error_code) = response.split(";")
                vector = int(vector, 16)
                error_code = int(error_code, 16)
                print EXCEPTION_INFO % (vector, error_code)
Edk2InfoException()

class Edk2RefreshArchitecture(gdb.Command):
    """Refresh target architecture.
    """

    def __init__(self):
        super(Edk2RefreshArchitecture, self).__init__("refresharch", gdb.COMMAND_STATUS, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args == None:
            return

        response = UdkCommandHelper.executeUdkExtensionCommand("arch")

        if response == "use32":
            UdkCommandHelper.executeCommand("set architecture i386")
        elif response == "use64":
            UdkCommandHelper.executeCommand("set architecture i386:x86-64")
Edk2RefreshArchitecture()

class Edk2SmmEntryBreak(gdb.Command):
    """Configurate whether Debugger stop machine when entering SMM entry.
Usage: set smmentrybreak on|off
  on:  Enable break when entering SMM entry
  off: Disable break when entering SMM entry"""

    def __init__(self):
        super(Edk2SmmEntryBreak, self).__init__("set smmentrybreak", gdb.COMMAND_RUNNING)

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 1)
        if args != None:
            if args[0] not in ["on", "off"]:
                print ARGUMENT_ON_OFF_EXPECTED
                return
            UdkCommandHelper.executeUdkExtensionCommand("smmentrybreak:%s" % args[0])

    def complete(self, text, word):
        return ["on", "off"]
Edk2SmmEntryBreak()

class Edk2ResetTarget(gdb.Command):
    """Reset the target.
    """

    def __init__(self):
        super(Edk2ResetTarget, self).__init__("resettarget", gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args == None:
            return

        response = UdkCommandHelper.executeUdkExtensionCommand("resettarget")
        if response != "OK":
            print FAILED_TO_EXECUTE_COMMAND % response
        else:
            print TARGET_IS_RUNNING_AFTER_RESET
            UdkCommandHelper.executeCommand("c")
Edk2ResetTarget()

class Edk2ResetDelay(gdb.Command):
    """Configurate how long remote target do reset action.
    """

    def __init__(self):
        super(Edk2ResetDelay, self).__init__("set resetdelay", gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 1)
        if args == None:
            return
        try:
            delay = int(args[0])
        except:
            delay = 0
        if delay < 1 or delay > 20:
            print ARGUMENT_1_TO_20_EXPECTED
            return
        UdkCommandHelper.executeUdkExtensionCommand("resetdelay:%d" % delay)
Edk2ResetDelay()

class Edk2LoadImageAt(gdb.Command):
    """Load symbol file for the specified address.
    """

    def __init__(self):
        super(Edk2LoadImageAt, self).__init__("loadimageat", gdb.COMMAND_FILES, gdb.COMPLETE_NONE)
        self._symtbl = []

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 1)
        if args == None:
            return
        try:
            cur = long(args[0], 16)
        except:
            print ARGUMENT_HEX_EXPECTED
            return
        print LOADING_SYMBOL % cur
        self.loadsymbol(cur, True)

    def loadsymbol(self, cur, force = False, display = True):
        if force and self._issymbolpresent(cur):
            self._removesymbol(cur)
        if not self._issymbolpresent(cur):
            debug_info = UdkCommandHelper.getTargetDebugInfo(cur)
            if (debug_info != None):
                (debug_link, section_info) = debug_info
                if debug_link.endswith(".pdb"):
                    print UNSUPPORTED_DEBUG_INFORMATION
                    return False
                if debug_link == "" or \
                   not section_info.has_key(".text") or not section_info.has_key(".data"):
                    print MISSING_DEBUG_INFORMATION
                    return False
                cmd = "add-symbol-file %s 0x%x" % (debug_link, section_info[".text"])

                for section_name, section_addr in section_info.items():
                    if section_name not in [".debug", ".gnu_deb", ".reloc", ".text"]:
                        cmd += " -s %s 0x%x" % (section_name, section_addr)
                try:
                    if display:
                        gdb.execute(cmd)
                    else:
                        UdkCommandHelper.executeCommand(cmd)
                except RuntimeError, e:
                    if str(e).find("No such file or directory") != -1:
                        print FAILED_TO_FIND_SYMBOL_FILE
                    return False
                except Exception, e:
                    print FAILED_TO_LOAD_SYMBOL
                    return False
                self._addsymbol(section_info[".text"], section_info[".data"])
                return True
            else:
                print FAILED_TO_FIND_DEBUG_INFORMATION
                return False
        else:
            return True

    def _addsymbol(self, start, end):
        element = (start, end)
        self._symtbl.append(element)

    def _removesymbol(self, addr):
        removelist = []
        for sym in self._symtbl:
            if addr >= sym[0] and addr < sym[1]:
                removelist.append(sym)
        removelist.reverse()
        for sym in removelist:
            self._symtbl.remove(sym)

    def _issymbolpresent(self, addr):
        for sym in self._symtbl:
            if addr >= sym[0] and addr < sym[1]:
                return True
        return False

class Edk2LoadThis(gdb.Command):
    """Load symbol file for the current IP address.
    """

    def __init__(self, imageloader):
        super(Edk2LoadThis, self).__init__("loadthis", gdb.COMMAND_FILES, gdb.COMPLETE_NONE)
        self._imageloader = imageloader

    def invoke(self, arg, from_tty):
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args != None:
            self.dont_repeat()
            cur = gdb.selected_frame().pc()
            print LOADING_SYMBOL % cur
            self._imageloader.loadsymbol(cur)

class Edk2LoadAll(gdb.Command):
    """Load symbols for all loaded modules.
    """
    def __init__(self, imageloader):
        super(Edk2LoadAll, self).__init__("loadall", gdb.COMMAND_FILES, gdb.COMPLETE_NONE)
        self._imageloader = imageloader

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args == None:
            return

        response = UdkCommandHelper.executeUdkExtensionCommand("fmodules")
        while response != "l":
            (image_entry, image_base, image_name) = response.split(";")
            response = UdkCommandHelper.executeUdkExtensionCommand("smodules")
            pe_sig = UdkCommandHelper.executeCommand("x/hx %s" % image_base)[0].split()[1]
            if pe_sig in ["0x5a4d", "0x5a56"]:
                print LOADING_SYMBOL_FOR_MODULE % (image_base, image_name)
                self._imageloader.loadsymbol(long(image_entry, 16), False, False)
            else:
                print SKIPPING_SYMBOL_FOR_MODULE % (image_base, image_name)

class Edk2InfoModules(gdb.Command):
    """List information of the loaded modules or the specified module(s).
    """
    def __init__(self):
        super(Edk2InfoModules, self).__init__("info modules", gdb.COMMAND_DATA)

    def _query(self):
        image_info = []
        response = UdkCommandHelper.executeUdkExtensionCommand("fmodules")
        while response != "l":
            image_info.append(response.split(";"))
            response = UdkCommandHelper.executeUdkExtensionCommand("smodules")
        return image_info

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = map(str.lower, UdkCommandHelper.checkParameterEx(arg, 0, 0xffffffff))
        if args == None:
            return

        print "ENTRY\tBASE\tNAME"
        print "===================================="
        for (image_entry, image_base, image_name) in self._query():
            if len(args) == 0 or image_name.lower() in args:
                print "%s\t%s\t%s" % (image_entry, image_base, image_name)
        print ""

    def complete(self, text, word):
        suggestion = []
        for (image_entry, image_base, image_name) in self._query():
            if image_name.lower().startswith(word.lower()):
                suggestion.append(image_name)
        return suggestion

class Edk2StopHandler(gdb.Command):
    """Command to be run when target is stopped.
    """
    def __init__(self):
        super(Edk2StopHandler, self).__init__("stop-handler", gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 0)
        if args == None:
            return
        gdb.execute("refresharch")
        if not UdkCommandHelper.supportExpat():
            gdb.execute("loadthis")
        gdb.execute("info exception")
Edk2StopHandler()

class Edk2DebugScript(gdb.Command):
    """Command to turn on or off the script debugging.
    """
    def __init__(self):
        super(Edk2DebugScript, self).__init__("set debug udk-script", gdb.COMMAND_MAINTENANCE)

    def invoke(self, arg, from_tty):
        self.dont_repeat()
        args = UdkCommandHelper.checkParameter(arg, 1)
        if args == None:
            return
        if args[0] not in ["on", "off"]:
            print ARGUMENT_ON_OFF_EXPECTED
            return

        UdkCommandHelper._debugMode = (args[0] == "on")

    def complete(self, text, word):
        return ["on", "off"]
Edk2DebugScript()

try:
    print SCRIPT_BANNER_BEGIN
    # exception raised when GdbServer is not connected
    gdb.selected_frame()

    # provide additional command when gdb doesn't support Expat
    if UdkCommandHelper.supportExpat():
        print SUPPORT_PENDING_BREAKPOINTS
    else:
        print NOT_SUPPORT_PENDING_BREAKPOINTS
        Edk2InfoModules()  # info module
        ImageLoader = Edk2LoadImageAt()  # loadimageat
        Edk2LoadThis(ImageLoader) # loadthis
        Edk2LoadAll(ImageLoader) # loadall
    print SCRIPT_BANNER_END

    # Get the substitution rule for the source file names.
    gdb.execute("mappath")

    # Run the hook-stop script
    gdb.execute("stop-handler")

    # force to load the shared library in the first time
    gdb.execute("sharedlibrary")
except:
    print GDBSERVER_NOT_CONNECTED
    print SCRIPT_BANNER_END

# End of python
end
