/*++

Copyright (c) 1993  IBM Corporation and Microsoft Corporation

Module Name:

    vunwind.c

Abstract:

    This module contains the instruction classifying and virtual
    unwinding routines for structured exception handling on PowerPC.

    Virtual Unwind was moved to this file from exdsptch.c so that it
    can be used directly in the kernel debugger.

    WARNING!

    The kernel debugger and windbg need to be modified if the number
    and type of parameters for READ_ULONG and READ_DOUBLE are
    modified.

    Use CAUTION if you add include statements

Author:

    Rick Simpson  16-Aug-1993

    based on MIPS version by David N. Cutler (davec) 11-Sep-1990

Environment:

    Any mode.

Revision History:

    Tom Wood (twood) 1-Nov-1993
    Add back changes to RtlVirtualUnwind made when the MIPS version
    was ported.

    Tom Wood (twood) 1-Feb-1994
    Change to using a function table entry for register save/restore
    millicode.  Add forward execution of register restore millicode.
    Add the ITERATOR abstraction.

    Peter Johnston (plj@vnet.ibm.com) 14-Feb-1994
    Added InstrGetOut classification to allow a simulated return from
    dummy prologues such as those in system exception handling.

    Tom Wood (twood) 28-Feb-1994
    Added the WINDBG interface.

    Tom Wood (twood) 8-Jun-1994
    Added the _IMAGEHLP_SOURCE_ interface.

    Tom Wood (twood) 8-Jun-1994
    Removed the WINDBG interface.  Updated the _IMAGEHLP_SOURCE_
    interface to deal with the fact that ExceptionHandler and HandlerData
    cannot be relied upon.  Also, the FunctionEntry value may be a static
    buffer returned by RtlLookupFunctionEntry (i.e. FunctionTableAccess).
    The copy of vunwind.c in ntos/rtl/ppc is older and should be replaced
    with this version.

    Tom Wood (twood) 9-Aug-1994
    Added support for the new glue code sequences.  Added InstrGlue and
    InstrTocRestore.  The former replaces InstrGetOut.  RtlVirtualUnwind
    is now required to be called when there is no function table entry.

 --*/

typedef DOUBLE *PDOUBLE;

#ifdef ROS_DEBUG
#include "ntrtlp.h"
#define READ_ULONG(addr,dest) dest = (*((PULONG)(addr)))
#define READ_DOUBLE(addr,dest) dest = (*((PDOUBLE)(addr)))
#endif

#ifdef _IMAGEHLP_SOURCE_
#define FUNCTION_ENTRY_IS_IMAGE_STYLE
#define NOT_IMAGEHLP(E)
#else
#define NOT_IMAGEHLP(E) E
#endif

#ifdef KERNEL_DEBUGGER
#define FUNCTION_ENTRY_IS_IMAGE_STYLE
#define RtlVirtualUnwind VirtualUnwind
#endif

//
// The `ClassifyInstruction' function returns an enum that identifies
// the type of processing needed for an instruction.
//

typedef enum _INSTR_CLASS {
    InstrIgnore,        // Do not process
    InstrMFLR,          // Move from Link Register
    InstrMFCR,          // Move from Condition Register
    InstrSTW,           // Store word
    InstrSTWU,          // Store word with update
    InstrSTWUr12,       // Store word with update during UnwindR12
    InstrSTFD,          // Store float double
    InstrMR,            // Move register
    InstrMRr12,         // Move register during UnwindR12
    InstrMRfwd,         // Move register during UnwindForward
    InstrADDIr12,       // Add immediate during UnwindR12
    InstrADDIfwd,       // Add immediate during UnwindForward
    InstrSaveCode,      // Branch and link to GPR or FPR saving millicode
    InstrRestoreCode,   // Branch to GPR or FPR saving millicode
    InstrGlue,          // Branch or Branch and link to glue code
    InstrBLR,           // Branch to Link Register
    InstrTOCRestore,    // Load that restores the TOC [after a call to glue]
    InstrSetEstablisher // Special instruction used to set establisher frame
} INSTR_CLASS;

//
// If `ClassifyInstruction' returns `InstrSaveCode' or `InstrRestoreCode',
// the following information is completed.
//

typedef struct _MILLICODE_INFO {
    ULONG  TargetPc;    // Millicode entry point
    PRUNTIME_FUNCTION FunctionEntry; // Millicode function table entry
} MILLICODE_INFO, *PMILLICODE_INFO;

//
// `ClassifyInstruction' interprets the instruction based on the intent.
//

typedef enum _UNWIND_INTENT {
    UnwindForward,      // Performing a forward execution
    UnwindR12,          // Performing a reverse execution to get r.12
    UnwindReverse,      // Performing a reverse execution
    UnwindReverseR12    // Performing a reverse execution allowing r.12
} UNWIND_INTENT;

//
// The simulated execution by `RtlVirtualUnwind' is controlled by this
// data type.
//

typedef struct _ITERATOR {
    ULONG BeginPc;      // Address of first instruction to simulate
    ULONG EndPc;        // Address after the last instruction to simulate
    LONG  Increment;    // Simulation direction
    UNWIND_INTENT Intent; // Simulation intent
} ITERATOR, *PITERATOR;

#define GPR1     1      // GPR 1 in an RA, RB, RT, etc. field
#define GPR2     2      // GPR 2 in an RA, RB, RT, etc. field
#define GPR12    12     // GPR 12 in an RA, RB, RT, etc. field
#define LINKREG  0x100  // Link Reg in a MFSPR instruction
#define COUNTREG 0x120  // Count Reg in a MFSPR instruction

//
// TryReadUlong attempts to read memory from a possibly unsafe address.
// It is in a seperate routine to avoid optimization deficiencies caused
// by use of try/except.
//

#ifndef _IMAGEHLP_SOURCE_

static NTSTATUS
TryReadUlong(IN ULONG NextPc,
               OUT PULONG Value)
{
    try {
        READ_ULONG (NextPc, *Value);
    } except (EXCEPTION_EXECUTE_HANDLER) {
        return GetExceptionCode();
    }
    return STATUS_SUCCESS;
}

#endif

static INSTR_CLASS
ClassifyInstruction (PPC_INSTRUCTION *I,
                     UNWIND_INTENT Intent,
#ifdef _IMAGEHLP_SOURCE_
                     HANDLE hProcess,
                     PREAD_PROCESS_MEMORY_ROUTINE ReadMemory,
                     PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccess,
#endif
                     ULONG Pc,
                     PMILLICODE_INFO Info)

/*++

Routine description:

    This function inspects the instruction identified by the "Pc"
    argument and determines what sort of processing is needed in order
    to simulate its execution.  Some instructions can be safely
    ignored altogether, in which case "InstrIgnore" is returned.  For
    others, a value is returned indicating what kind of instruction
    was found.  The interpreation depends on the value of "Intent".

Arguments:

    I - Address of a struct containing the instruction to be examined.
    Intent - Type of unwinding being performed.
    Pc - Address of the instruction, used for computing relative branch
        addresses.
    Info - Address to store a description of the register save/restore
        millicode.

Return value:

    One of the enum values defined above is returned.

 --*/

{
// Unique value combining an opcode and an UNWIND_INTENT value.
#define OP_INTENT(OP,INTENT) ((OP) << 2 | (INTENT))

#ifdef _IMAGEHLP_SOURCE_
    DWORD ImagehlpCb = 0;
#endif

    switch (OP_INTENT (I->Primary_Op, Intent)) {

      //
      // Store word: recognize "stw r.n, disp(r.1)".  Allow a base of
      // r.12 if we have computed its value.
      //
      case OP_INTENT (STW_OP, UnwindReverseR12):
        if (I->Dform_RA == GPR12)
            return InstrSTW;
        // fall thru
      case OP_INTENT (STW_OP, UnwindReverse):
        if (I->Dform_RA == GPR1)
            return InstrSTW;
        break;

      //
      // Load word:  recognize "lwz r.n, disp(r.x)" in epilogue millicode.
      //
      case OP_INTENT (LWZ_OP, UnwindForward):
        return InstrSTW;

      //
      // Load word:  recognize "lwz r.toc, disp(r.1)" as TOC restore
      // instruction.
      //
      case OP_INTENT (LWZ_OP, UnwindReverse):
        if (I->Dform_RA == GPR1 &&
            I->Dform_RS == GPR2)
        return InstrTOCRestore;

      //
      // Store word with update:  recognize "stwu r.1, r.1, disp"
      //
      case OP_INTENT (STWU_OP, UnwindReverse):
      case OP_INTENT (STWU_OP, UnwindReverseR12):
      case OP_INTENT (STWU_OP, UnwindR12):
        if (I->Dform_RS == GPR1 &&
            I->Dform_RA == GPR1)
            return (Intent == UnwindR12 ? InstrSTWUr12 : InstrSTWU);
        break;

      //
      // Store float double: recognize "stfd f.n, disp(r.1)".  Allow a
      // base of r.12 if we have computed its value.
      //
      case OP_INTENT (STFD_OP, UnwindReverseR12):
        if (I->Dform_RA == GPR12)
            return InstrSTFD;
        // fall thru
      case OP_INTENT (STFD_OP, UnwindReverse):
        if (I->Dform_RA == GPR1)
            return InstrSTFD;
        break;

      //
      // Load float double:  recognize "lfd f.n, disp(r.x)"
      //
      case OP_INTENT (LFD_OP, UnwindForward):
        return InstrSTFD;

      //
      // Add immediate:  recognize "addi r.12, r.1, delta"
      //
      case OP_INTENT (ADDI_OP, UnwindR12):
        if (I->Dform_RS == GPR12 &&
            I->Dform_RA == GPR1)
            return InstrADDIr12;
        break;
      case OP_INTENT (ADDI_OP, UnwindForward):
        return InstrADDIfwd;

      //
      // Branch (long form):  recognize "bl[a] saveregs"and "b[a] restregs"
      //
      case OP_INTENT (B_OP, UnwindReverse):
        //
        // Compute branch target address, allowing for branch-relative
        // and branch-absolute.
        //
        Pc = ((LONG)(I->Iform_LI) << 2) + (I->Iform_AA ? 0 : Pc);

        //
        // Quickly distinguish "bl subroutine" from "bl[a] saveregs".
        // "mtlr" is not a valid instruction in register save millicode and
        // is usually the first instruction in "bl subroutine".
        //
        if (I->Iform_LK) {
            PPC_INSTRUCTION TempI;
            READ_ULONG (Pc, TempI.Long);
            if (TempI.Primary_Op == X31_OP &&
                TempI.Xform_XO == MFSPR_OP &&
                TempI.XFXform_spr == LINKREG) {
                break;
            }
        }

        //
        // Determine whether the target address is part of a register
        // save or register restore sequence or is a direct branch out
        // by checking it's function table entry.
        //
        if ((Info->FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc)) != NULL
#ifndef FUNCTION_ENTRY_IS_IMAGE_STYLE
            && Info->FunctionEntry->ExceptionHandler == 0
#endif
            ) {
            Info->TargetPc = Pc;
            switch (
#ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE
                    Info->FunctionEntry->BeginAddress -
                    Info->FunctionEntry->PrologEndAddress
#else
                    (ULONG)Info->FunctionEntry->HandlerData
#endif
                    ) {
            case 1:
                if (I->Iform_LK)
                    return InstrSaveCode;
                break;
            case 2:
                if (!I->Iform_LK)
                    return InstrRestoreCode;
                break;
#ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE
            default:
                if ((Info->FunctionEntry->PrologEndAddress & 3) == 1)
#else
            case 3:
#endif
                return InstrGlue;
                break;
            }
        }
        break; // unrecognized entry point

      //
      // Extended ops -- primary opcode 19
      //
      case OP_INTENT (X19_OP, UnwindForward):

        //
        // BLR: recognized "bclr 20,0".
        //
        if (I->Long == RETURN_INSTR)
            return InstrBLR;

        //
        // RFI: this instruction is used in special kernel fake prologues
        //      to indicate that the establisher frame address should be
        //      updated using the current value of sp.
        //
        if (I->Xform_XO == RFI_OP) {
            return InstrSetEstablisher;
        }

        break;

      //
      // Extended ops -- primary opcode 31
      //
      case OP_INTENT (X31_OP, UnwindForward):
      case OP_INTENT (X31_OP, UnwindR12):
      case OP_INTENT (X31_OP, UnwindReverse):
      case OP_INTENT (X31_OP, UnwindReverseR12):
        switch (OP_INTENT (I->Xform_XO, Intent)) {

          //
          // OR register: recognize "or r.x, r.y, r.y" as move-reg
          //
          case OP_INTENT (OR_OP, UnwindR12):
            if (I->Xform_RS == I->Xform_RB &&
                I->Xform_RA == GPR12 &&
                I->Xform_RB == GPR1)
                return InstrMRr12;
            break;
          case OP_INTENT (OR_OP, UnwindReverse):
          case OP_INTENT (OR_OP, UnwindReverseR12):
            if (I->Xform_RS == I->Xform_RB &&
                I->Xform_RB != GPR1)
                return InstrMR;
            break;
          case OP_INTENT (OR_OP, UnwindForward):
            if (I->Xform_RS == I->Xform_RB)
                return InstrMRfwd;
            break;

          //
          // Store word with update indexed:  recognize "stwux r.1, r.1, r.x"
          //
          case OP_INTENT (STWUX_OP, UnwindReverse):
          case OP_INTENT (STWUX_OP, UnwindReverseR12):
          case OP_INTENT (STWUX_OP, UnwindR12):
            if (I->Xform_RS == GPR1 && I->Xform_RA == GPR1)
                return (Intent == UnwindR12 ? InstrSTWUr12 : InstrSTWU);
            break;

          //
          // Move to/from special-purpose reg:  recognize "mflr", "mtlr"
          //
          case OP_INTENT (MFSPR_OP, UnwindReverse):
          case OP_INTENT (MTSPR_OP, UnwindForward):
            if (I->XFXform_spr == LINKREG)
                return InstrMFLR;
            break;

          //
          // Move from Condition Register:  "mfcr r.x"
          //
          case OP_INTENT (MFCR_OP, UnwindReverse):
          case OP_INTENT (MFCR_OP, UnwindReverseR12):
            return InstrMFCR;

          //
          // Move to Condition Register:  "mtcrf 255,r.x"
          //
          case OP_INTENT (MTCRF_OP, UnwindForward):
            if (I->XFXform_FXM == 255)
                return InstrMFCR;
            break;

          default:              // unrecognized
            break;
        }

      default:                  // unrecognized
        break;
    }

    //
    // Instruction not recognized; just ignore it and carry on
    //
    return InstrIgnore;
#undef OP_INTENT
}

#ifdef _IMAGEHLP_SOURCE_
static
#endif
ULONG
RtlVirtualUnwind (

#ifdef _IMAGEHLP_SOURCE_
    HANDLE hProcess,
    DWORD  ControlPc,
    PRUNTIME_FUNCTION FunctionEntry,
    PCONTEXT ContextRecord,
    PREAD_PROCESS_MEMORY_ROUTINE ReadMemory,
    PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccess
#define ContextPointers ((PKNONVOLATILE_CONTEXT_POINTERS)0)
#else
    IN ULONG ControlPc,
    IN PRUNTIME_FUNCTION FunctionEntry,
    IN OUT PCONTEXT ContextRecord,
    OUT PBOOLEAN InFunction,
    OUT PULONG EstablisherFrame,
    IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL,
    IN ULONG LowStackLimit,
    IN ULONG HighStackLimit
#endif
    )

/*++

Routine Description:

    This function virtually unwinds the specfified function by executing its
    prologue code backwards.

    If the function is a leaf function, then the address where control left
    the previous frame is obtained from the context record. If the function
    is a nested function, but not an exception or interrupt frame, then the
    prologue code is executed backwards and the address where control left
    the previous frame is obtained from the updated context record.

    If the function is register save millicode, it is treated as a leaf
    function.  If the function is register restore millicode, the remaining
    body is executed forwards and the address where control left the
    previous frame is obtained from the final blr instruction.

    If the function was called via glue code and is not that glue code,
    the prologe of the glue code is executed backwards in addition to the
    above actions.

    Otherwise, an exception or interrupt entry to the system is being
    unwound and a specially coded prologue restores the return address
    twice. Once from the fault instruction address and once from the saved
    return address register. The first restore is returned as the function
    value and the second restore is place in the updated context record.

    If a context pointers record is specified, then the address where each
    nonvolatile registers is restored from is recorded in the appropriate
    element of the context pointers record.

Arguments:

    ControlPc - Supplies the address where control left the specified
        function.

    FunctionEntry - Supplies the address of the function table entry for the
        specified function or NULL if the function is a leaf function.

    ContextRecord - Supplies the address of a context record.

    InFunction - Supplies a pointer to a variable that receives whether the
        control PC is within the current function.

    EstablisherFrame - Supplies a pointer to a variable that receives the
        the establisher frame pointer value.

    ContextPointers - Supplies an optional pointer to a context pointers
        record.

    LowStackLimit, HighStackLimit - Range of valid values for the stack
        pointer.  This indicates whether it is valid to examine NextPc.

Return Value:

    The address where control left the previous frame is returned as the
    function value.

 --*/

{
    ITERATOR Iterator[8];
    PITERATOR Piterator;
    ULONG Address;
    PDOUBLE FloatingRegister;
    PPC_INSTRUCTION I;
    PULONG IntegerRegister;
    ULONG NextPc, Pc;
    BOOLEAN RestoredLr = FALSE;
    BOOLEAN RestoredSp = FALSE;
    BOOLEAN ComputedSp = FALSE;
    ULONG Rt;
    MILLICODE_INFO Info;
    INSTR_CLASS InstrClass;
#ifdef _IMAGEHLP_SOURCE_
    DWORD ImagehlpCb = 0;
    RUNTIME_FUNCTION SavedFunctionEntry;
#else
    ULONG EstablisherFrameValue;
#endif

    //
    // Set the base address of the integer and floating register arrays.
    //

    FloatingRegister = &ContextRecord->Fpr0;
    IntegerRegister = &ContextRecord->Gpr0;

    //
    // If the function is a leaf function, perform the default unwinding
    // action and check to see if the function was called via glue.
    //
    if (FunctionEntry == NULL) {
        //
        // Set point at which control left the previous routine.
        //
        NextPc = ContextRecord->Lr - 4;

        //
        // If the next control PC is the same as the old control PC, then
        // the function table is not correctly formed.
        //
        if (NextPc == ControlPc)
            return NextPc;

        goto CheckForGlue;
    }
#ifdef _IMAGEHLP_SOURCE_
    else {
        SavedFunctionEntry = *FunctionEntry;
        FunctionEntry = &SavedFunctionEntry;
    }
#endif
    //
    // Set initial values for EstablisherFrame and Offset.
    // (this may need more careful planning IBMPLJ).
    //

    NOT_IMAGEHLP (*EstablisherFrame =
                  EstablisherFrameValue = ContextRecord->Gpr1);

    READ_ULONG (ControlPc, I.Long);
    if (I.Long == RETURN_INSTR) {
        //
        // If the instruction at the point where control left the specified
        // function is a return, then any saved registers have been restored
        // and the control PC is not considered to be in the function
        // (i.e., in an epilogue).
        //
        NOT_IMAGEHLP(*InFunction = FALSE);
        NextPc = ContextRecord->Lr;
        goto CheckForGlue;
    }
    InstrClass = ClassifyInstruction(&I, UnwindReverse,
#ifdef _IMAGEHLP_SOURCE_
                                     hProcess, ReadMemory, FunctionTableAccess,
#endif
                                     ControlPc, &Info);
    if (InstrClass == InstrRestoreCode) {
        //
        // If the instruction at the point where control left the
        // specified function is a branch to register restore
        // millicode, the state is restored by simulating the
        // execution of the restore millicode.  The control PC is in
        // an epilogue.
        //
        Iterator[0].BeginPc = Info.TargetPc;
        Iterator[0].EndPc = Info.FunctionEntry->EndAddress;
        Iterator[0].Increment = 4;
        Iterator[0].Intent = UnwindForward;
        NOT_IMAGEHLP(*InFunction = FALSE);

    } else if (
#ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE
               (FunctionEntry->BeginAddress -
                FunctionEntry->PrologEndAddress) == 2
#else
               FunctionEntry->ExceptionHandler == 0 &&
               (ULONG)FunctionEntry->HandlerData == 2
#endif
               ) {
        //
        // If the address is in register restore millicode, the state
        // is restored by completing the execution of the restore
        // millicode.  The control PC is in an epilogue.
        //
        Iterator[0].BeginPc = ControlPc;
        Iterator[0].EndPc = FunctionEntry->EndAddress;
        Iterator[0].Increment = 4;
        Iterator[0].Intent = UnwindForward;
        NOT_IMAGEHLP(*InFunction = FALSE);

    } else {
        //
        // If the address where control left the specified function is a
        // TOC restore instruction and the previous instruction is a call
        // via glue instruction, we must forward execute the TOC restore
        // instruction.
        //
        if (InstrClass == InstrTOCRestore) {
            PPC_INSTRUCTION Iprev;
            READ_ULONG (ControlPc - 4, Iprev.Long);
            if (ClassifyInstruction (&Iprev, UnwindReverse,
#ifdef _IMAGEHLP_SOURCE_
                                     hProcess, ReadMemory, FunctionTableAccess,
#endif
                                     ControlPc - 4, &Info) == InstrGlue) {
                //
                // Forward execute the TOC restore.  We assume (reasonably)
                // that the next instruction is covered by the same function
                // table entry and it isn't one of the above special cases
                // (InstrRestoreCode or InstrBLR).
                //
                ControlPc += 4;
                Address = IntegerRegister[I.Dform_RA] + I.Dform_D;
                Rt = I.Dform_RT;
                READ_ULONG (Address, IntegerRegister[Rt]);
                if (ARGUMENT_PRESENT (ContextPointers))
                    ContextPointers->IntegerContext[Rt] = (PULONG) Address;
            }
        }

        //
        // If the address where control left the specified function is
        // outside the limits of the prologue, then the control PC is
        // considered to be within the function and the control
        // address is set to the end of the prologue. Otherwise, the
        // control PC is not considered to be within the function
        // (i.e., in the prologue).
        //
        Iterator[0].EndPc = FunctionEntry->BeginAddress - 4;
        Iterator[0].Increment = -4;
        Iterator[0].Intent = UnwindReverse;
        if ((ControlPc < FunctionEntry->BeginAddress) ||
            (ControlPc >= (FunctionEntry->PrologEndAddress & ~3))) {
            NOT_IMAGEHLP(*InFunction = TRUE);
            Iterator[0].BeginPc = ((FunctionEntry->PrologEndAddress & ~3) - 4);
        } else {
            NOT_IMAGEHLP(*InFunction = FALSE);
            Iterator[0].BeginPc = ControlPc - 4;
        }
    }

    //
    // Scan through the given instructions and reload callee registers
    // as indicated.
    //
    NextPc = ContextRecord->Lr - 4;
  UnwindGlue:
    for (Piterator = Iterator; Piterator >= Iterator; Piterator--) {
        for (Pc = Piterator->BeginPc;
             Pc != Piterator->EndPc;
             Pc += Piterator->Increment) {

            READ_ULONG (Pc, I.Long);
            Address = IntegerRegister[I.Dform_RA] + I.Dform_D;
            Rt = I.Dform_RT;
            switch (ClassifyInstruction (&I, Piterator->Intent,
#ifdef _IMAGEHLP_SOURCE_
                                         hProcess, ReadMemory, FunctionTableAccess,
#endif
                                         Pc, &Info)) {

              //
              // Move from Link Register (save LR in a GPR)
              //
              // In the usual case, the link register gets set by a call
              // instruction so the PC value should point to the
              // instruction that sets the link register.  In an interrupt
              // or exception frame, the link register and PC value are
              // independent.  By convention, fake prologues for these
              // frames store the link register twice: once to the link
              // register location, once to the faulting PC location.
              //
              // If this is the first time that RA is being restored,
              // then set the address of where control left the previous
              // frame. Otherwise, this is an interrupt or exception and
              // the return PC should be biased by 4 and the link register
              // value should be updated.
              //
              case InstrMFLR:
                ContextRecord->Lr = IntegerRegister[Rt];
                if ( RestoredLr == FALSE ) {
                    NextPc = ContextRecord->Lr - 4;
                    RestoredLr = TRUE;
                } else {
                    NextPc += 4;
                }
                continue; // Next PC

              //
              // Branch to Link Register (forward execution).
              //
              case InstrBLR:
                NextPc = ContextRecord->Lr - 4;
                break; // Terminate simulation--start next iterator.

              //
              // Move from Condition Register (save CR in a GPR)
              //
              case InstrMFCR:
                ContextRecord->Cr = IntegerRegister[Rt];
                continue; // Next PC

              //
              // Store word (save a GPR)
              //
              case InstrSTW:
              //
              // Even though a stw  r.sp, xxxx in general is an invalid
              // proloque instruction there are places in the kernel
              // fake prologues (KiExceptionExit) where we must use this,
              // so handle it.
              //
                READ_ULONG (Address, IntegerRegister[Rt]);
                if (ARGUMENT_PRESENT (ContextPointers))
                    ContextPointers->IntegerContext[Rt] = (PULONG) Address;
                continue; // Next PC

              //
              // Store word with update, Store word with update indexed
              // (buy stack frame, updating stack pointer and link
              // cell in storage)
              //
              case InstrSTWU:
                Address = IntegerRegister[GPR1];
                READ_ULONG(Address,IntegerRegister[GPR1]);
                if (RestoredSp == FALSE) {
                    NOT_IMAGEHLP (*EstablisherFrame =
                                  EstablisherFrameValue = ContextRecord->Gpr1);
                    RestoredSp = TRUE;
                }
                if (ARGUMENT_PRESENT (ContextPointers))
                    ContextPointers->IntegerContext[Rt] = (PULONG) Address;
                continue; // Next PC

              //
              // Store floating point double (save an FPR)
              //
              case InstrSTFD:
                READ_DOUBLE (Address, FloatingRegister[Rt]);
                if (ARGUMENT_PRESENT (ContextPointers))
                    ContextPointers->FloatingContext[Rt] = (PDOUBLE) Address;
                continue; // Next PC

              //
              // Move register.  Certain forms are ignored based on the intent.
              //
              case InstrMR:
                IntegerRegister[I.Xform_RA] = IntegerRegister[Rt];
                continue; // Next PC
              case InstrMRfwd:
                IntegerRegister[Rt] = IntegerRegister[I.Xform_RA];
                continue; // Next PC
              case InstrMRr12:
                IntegerRegister[Rt] = IntegerRegister[I.Xform_RA];
                break; // Terminate search--start next iterator.

              //
              // Add immediate.  Certain forms are ignored based on the intent.
              //
              case InstrADDIfwd:
                IntegerRegister[Rt] = Address;
                continue; // Next PC
              case InstrADDIr12:
                if (!ComputedSp) {
                  // No intervening instruction changes r.1, so compute
                  // addi r.12,r.1,N instead of addi r.12,r.12,N.
                  IntegerRegister[Rt] = IntegerRegister[GPR1];
                }
                IntegerRegister[Rt] += I.Dform_SI;
                break; // Terminate search--start next iterator.

              //
              // Store with update while searching for the value of r.12
              //
              case InstrSTWUr12:
                ComputedSp = TRUE;
                Address = IntegerRegister[GPR1];
                READ_ULONG(Address,IntegerRegister[GPR12]);
                continue; // Next PC

              //
              // A call to a register save millicode.
              //
              case InstrSaveCode:
                //
                // Push an iterator to incorporate the actions of the
                // millicode.
                //
                Piterator++;
                Piterator->BeginPc = Info.FunctionEntry->EndAddress - 4;
                Piterator->EndPc = Info.TargetPc - 4;
                Piterator->Increment = -4;
                Piterator->Intent = UnwindReverseR12;
                //
                // Push an iterator to determine the current value of r.12
                //
                Piterator++;
                Piterator->BeginPc = Pc - 4;
                Piterator->EndPc = Piterator[-2].EndPc;
                Piterator->Increment = -4;
                Piterator->Intent = UnwindR12;
                ComputedSp = FALSE;
                //
                // Update the start of the original iterator so it can later
                // resume where it left off.
                //
                Piterator[-2].BeginPc = Pc - 4;
                Piterator++;
                break; // Start the next iterator.

              //
              // A branch was encountered in the prologue to a routine
              // identified as glue code.  This should only happen from
              // fake prologues such as those in the system exception
              // handler.
              //
              // We handle it by pushing an iterator to incorporate
              // the actions of the glue code prologue.
              //
              case InstrGlue:
                //
                // Check that we don't nest too deeply.  Verify that
                // we can push this iterator and the iterators for a
                // glue sequence.  There's no need to make this check
                // elsewhere because B_OP is only recognized during
                // UnwindReverse.  Returing zero is the only error action
                // we have.
                //
                if (Piterator - Iterator + 4
                    > sizeof (Iterator) / sizeof (Iterator[0]))
                    return 0;
                //
                // Push an iterator to incorporate the actions of the glue
                // code's prologue.  Check that we don't nest too deeply.
                // Verify that we can push this iterator and the iterators
                // for a glue sequence.
                //
                Piterator++;
                Piterator->BeginPc
                  = (Info.FunctionEntry->PrologEndAddress & ~3) - 4;
                Piterator->EndPc = Info.FunctionEntry->BeginAddress - 4;
                Piterator->Increment = -4;
                Piterator->Intent = UnwindReverse;
                //
                // Update the start of the original iterator so it can later
                // resume where it left off.
                //
                Piterator[-1].BeginPc = Pc - 4;
                Piterator++;
                break; // Start the next iterator.

              //
              // Special "set establisher" instruction (rfi).
              //
              // Kernel fake prologues that can't use stwu (KiExceptionExit,
              // KiAlternateExit) use an rfi instruction to tell the
              // unwinder to update the establisher frame pointer using
              // the current value of sp.
              //
              case InstrSetEstablisher:
                NOT_IMAGEHLP (*EstablisherFrame =
                                  EstablisherFrameValue = ContextRecord->Gpr1);
                continue; // Next PC

              //
              // None of the above.  Just ignore the instruction.  It
              // is presumed to be non-prologue code that has been
              // merged into the prologue for scheduling purposes.  It
              // may also be improper code in a register save/restore
              // millicode routine or unimportant code when
              // determining the value of r.12.
              //
              case InstrIgnore:
              default:
                continue; // Next PC
            }
            break; // Start the next iterator.
        } // end foreach Pc
    } // end foreach Iterator

  CheckForGlue:
    //
    // Check that we aren't at the end of the call chain.  We now require
    // that the link register at program start-up be zero.  Unfortunately,
    // this isn't always true.  Also verify that the stack pointer remains
    // valid.
    //
    if (NextPc == 0 || NextPc + 4 == 0
#ifdef _IMAGEHLP_SOURCE_
        || NextPc == 1
#else
        || EstablisherFrameValue < LowStackLimit
        || EstablisherFrameValue > HighStackLimit
        || (EstablisherFrameValue & 0x7) != 0
#endif
        )
        return NextPc;

    //
    // Is the instruction at NextPc an branch?
    //
#ifdef _IMAGEHLP_SOURCE_
    READ_ULONG (NextPc, I.Long);
#else
    if ( !NT_SUCCESS(TryReadUlong(NextPc, &I.Long)) ) {
        return NextPc;
    }
#endif
    if (I.Primary_Op != B_OP)
        return NextPc;

    //
    // Compute branch target address, allowing for branch-relative
    // and branch-absolute.
    //
    Pc = ((LONG)(I.Iform_LI) << 2) + (I.Iform_AA ? 0 : NextPc);

    //
    // If the branch target is contained in this function table entry,
    // either this function is glue or it wasn't called via glue.  This
    // is the usual case.
    //
    if (FunctionEntry != NULL
        && Pc >= FunctionEntry->BeginAddress
        && Pc < FunctionEntry->EndAddress)
        return NextPc;

    //
    // Allow for a stub glue in the thunk and a common ptrgl function
    // where the stub glue does not have a function table entry.
    //

    if ((FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc)) != NULL) {
        //
        // The target instruction must be "lwz r.x,disp(r.2)".
        //
        READ_ULONG (Pc, I.Long);
        if (I.Primary_Op == LWZ_OP && I.Dform_RA == GPR2) {
            //
            // The next instruction must be "b glue-code".
            //
            READ_ULONG (Pc + 4, I.Long);
            if (I.Primary_Op == B_OP && I.Iform_LK) {
                //
                // Compute branch target address, allowing for
                // branch-relative and branch-absolute.
                //
                Pc = ((LONG)(I.Iform_LI) << 2) + (I.Iform_AA ? 0 : Pc + 4);
                FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc);
            }
        }
    }

    //
    // Determine whether the branch target is glue code.
    //
    if (!(FunctionEntry != NULL
#ifndef FUNCTION_ENTRY_IS_IMAGE_STYLE
          && FunctionEntry->ExceptionHandler == 0
          && (ULONG)FunctionEntry->HandlerData == 3
#else
          && (FunctionEntry->BeginAddress <
              FunctionEntry->PrologEndAddress)
          && (FunctionEntry->PrologEndAddress & 3) == 1
#endif
          ))
        return NextPc;

    //
    // Unwind the glue code prologue.  We won't loop because
    // next time through, the branch target will be contained in
    // the function table entry.
    //
#ifdef _IMAGEHLP_SOURCE_
    SavedFunctionEntry = *FunctionEntry;
    FunctionEntry = &SavedFunctionEntry;
#endif
    Iterator[0].EndPc = FunctionEntry->BeginAddress - 4;
    Iterator[0].Increment = -4;
    Iterator[0].Intent = UnwindReverse;
    Iterator[0].BeginPc = ((FunctionEntry->PrologEndAddress & ~3) - 4);
    goto UnwindGlue;
}

#undef NOT_IMAGEHLP