/*++ Copyright (c) 2012 Minoca Corp. All Rights Reserved Module Name: sstep.c Abstract: This module implements support for getting the next PC instruction on ARM. This is most commonly used to implement single stepping support. Author: Evan Green 11-Aug-2012 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include // // ---------------------------------------------------------------- Definitions // // // Definitions that relate to the condition code of an ARM instruction. The // values here are shifted down by one, as the low bit just negates the // condition. // #define ARM_CONDITION_CODE_MASK 0xF0000000 #define ARM_CONDITION_CODE_SHIFT 28 #define ARM_CONDITION_CODE_UNCONDITIONAL 0xF #define ARM_CONDITION_EQUAL 0x0 #define ARM_CONDITION_CARRY 0x1 #define ARM_CONDITION_NEGATIVE 0x2 #define ARM_CONDITION_OVERFLOW 0x3 #define ARM_CONDITION_UNSIGNED_GREATER 0x4 #define ARM_CONDITION_SIGNED_GREATER_OR_EQUAL 0x5 #define ARM_CONDITION_SIGNED_GREATER 0x6 #define ARM_CONDITION_ALWAYS 0x7 // // Branch and exchange instruction (BX). // #define BRANCH_EXCHANGE_MASK 0x0FFFFFF0 #define BRANCH_EXCHANGE_VALUE 0x012FFF10 #define BRANCH_EXCHANGE_LINK_REGISTER 14 #define BRANCH_EXCHANGE_X_MASK 0x0FFFFFF0 #define BRANCH_EXCHANGE_X_VALUE 0x012FFF30 // // Branch instruction. // #define BRANCH_MASK 0x0E000000 #define BRANCH_VALUE 0x0A000000 #define BRANCH_H_BIT 0x01000000 // // Media instructions. // #define MEDIA_MASK 0x0E000010 #define MEDIA_VALUE 0x06000010 // // Load/Store instructions (LDR/STR). // #define LOAD_STORE_SINGLE_MASK 0x0C000000 #define LOAD_STORE_SINGLE_VALUE 0x04000000 #define LOAD_STORE_BYTE_BIT 0x00400000 #define DESTINATION_REGISTER_MASK 0x0000F000 #define DESTINATION_REGISTER_SHIFT 12 #define LOAD_BIT 0x00100000 #define PREINDEX_BIT 0x01000000 #define IMMEDIATE_BIT 0x02000000 #define SET_FLAGS_BIT 0x00100000 #define ADD_SUBTRACT_BIT 0x00800000 #define SHIFT_TYPE 0x00000060 #define SHIFT_LSL 0x00000000 #define SHIFT_LSR 0x00000020 #define SHIFT_ASR 0x00000040 #define SHIFT_ROR 0x00000060 #define LOAD_STORE_BYTE_BIT 0x00400000 #define LOAD_STORE_BASE_MASK 0x000F0000 #define LOAD_STORE_BASE_SHIFT 16 #define REGISTER_REGISTER_SHIFT_BIT 0x00000010 #define REGISTER_PC 0xF // // Load/Store Multiple (LDM/STM). // #define LOAD_STORE_MULTIPLE_MASK 0x0E000000 #define LOAD_STORE_MULTIPLE_VALUE 0x08000000 #define PUSH_POP_TYPE_MASK 0x01800000 #define PUSH_POP_INCREMENT_AFTER 0x00800000 #define PUSH_POP_INCREMENT_BEFORE 0x01800000 #define PUSH_POP_DECREMENT_AFTER 0x00000000 #define PUSH_POP_DECREMENT_BEFORE 0x01000000 #define LOAD_STORE_MULTIPLE_PC_BIT 0x00008000 #define REGISTER_LIST_MASK 0x0000FFFF // // Data processing instructions. // #define DATA_PROCESSING_MASK 0x0C000000 #define DATA_PROCESSING_VALUE 0x00000000 #define DATA_PROCESSING_OPCODE_MASK 0x01E00000 #define DATA_PROCESSING_OPCODE_SHIFT 21 #define DATA_PROCESSING_OPERAND1_MASK 0x000F0000 #define DATA_PROCESSING_OPERAND1_SHIFT 16 #define DATA_PROCESSING_NOT_IMMEDIATE_MASK 0x01900000 #define DATA_PROCESSING_NOT_IMMEDIATE_VALUE 0x01000000 #define SHIFT_REGISTER_MASK 0x00000F00 #define SHIFT_REGISTER_SHIFT 8 #define SHIFT_REGISTER_EMPTY_BIT 0x00000080 #define SHIFT_IMMEDIATE_MASK 0x00000F80 #define SHIFT_IMMEDIATE_SHIFT 7 #define OPERAND2_REGISTER_MASK 0x0000000F #define IMMEDIATE8_MASK 0x000000FF #define IMMEDIATE_ROTATE_MASK 0x00000F00 #define IMMEDIATE_ROTATE_SHIFT 8 #define OPCODE_AND 0 #define OPCODE_EOR 1 #define OPCODE_SUB 2 #define OPCODE_RSB 3 #define OPCODE_ADD 4 #define OPCODE_ADC 5 #define OPCODE_SBC 6 #define OPCODE_RSC 7 #define OPCODE_TST 8 #define OPCODE_TEQ 9 #define OPCODE_CMP 10 #define OPCODE_CMN 11 #define OPCODE_ORR 12 #define OPCODE_MOV 13 #define OPCODE_BIC 14 #define OPCODE_MVN 15 // // Define RFE instruction bits. // #define ARM_RFE_MASK 0xFE50FFFF #define ARM_RFE_VALUE 0xF8100A00 #define ARM_RFE_PREINDEX (1 << 24) #define ARM_RFE_INCREMENT (1 << 23) #define ARM_RFE_REGISTER_MASK 0x000F0000 #define ARM_RFE_REGISTER_SHIFT 16 // // Define Thumb decoding constants. // // // Common Thumb definitions // #define THUMB_REGISTER8_MASK 0x7 #define THUMB_REGISTER16_MASK 0xF #define THUMB_CONDITION_MASK 0xF #define THUMB_IMMEDIATE5_MASK 0x1F #define THUMB_IMMEDIATE6_MASK 0x3F #define THUMB_IMMEDIATE8_MASK 0xFF #define THUMB_IMMEDIATE10_MASK 0x3FF #define THUMB_IMMEDIATE11_MASK 0x7FF // // 16-bit Thumb decoding constants // #define THUMB16_IT_MASK 0xFF00 #define THUMB16_IT_VALUE 0xBF00 #define THUMB16_IT_STATE_MASK 0x00FF #define THUMB16_BX_MASK 0xFF07 #define THUMB16_BX_VALUE 0x4700 #define THUMB16_BX_RM_SHIFT 3 #define THUMB16_B_CONDITIONAL_MASK 0xF000 #define THUMB16_B_CONDITIONAL_VALUE 0xD000 #define THUMB16_B_CONDITIONAL_CONDITION_SHIFT 8 #define THUMB16_B_UNCONDITIONAL_MASK 0xF800 #define THUMB16_B_UNCONDITIONAL_VALUE 0xE000 #define THUMB16_CBZ_MASK 0xF500 #define THUMB16_CBZ_VALUE 0xB100 #define THUMB16_CBZ_IMMEDIATE5_SHIFT 3 #define THUMB16_CBZ_IMMEDIATE5 (1 << 9) #define THUMB16_CBZ_NOT (1 << 11) #define THUMB16_POP_MASK 0xFE00 #define THUMB16_POP_VALUE 0xBC00 #define THUMB16_POP_PC (1 << 8) #define THUMB16_POP_REGISTER_LIST 0xFF // // 32-BIT Thumb decoding constants // #define THUMB32_RFE_MASK 0xFFD0FFFF #define THUMB32_RFEIA_VALUE 0xF810C000 #define THUMB32_RFEDB_VALUE 0xF990C000 #define THUMB32_RFE_REGISTER_MASK 0x000F0000 #define THUMB32_RFE_REGISTER_SHIFT 16 #define THUMB32_LDM_MASK 0xFE500000 #define THUMB32_LDM_VALUE 0xE8100000 #define THUMB32_LDM_RN_SHIFT 16 #define THUMB32_LDM_INCREMENT (1 << 23) #define THUMB32_TB_MASK 0xFFF0FFE0 #define THUMB32_TB_VALUE 0xE8D0F000 #define THUMB32_TB_RN_SHIFT 16 #define THUMB32_TB_RM_SHIFT 0 #define THUMB32_TB_HALF_WORD (1 << 4) #define THUMB32_SUBS_PC_LR_MASK 0xFFFFFF00 #define THUMB32_SUBS_PC_LR_VALUE 0xF3DE8F00 #define THUMB32_B_CONDITIONAL_MASK 0xF800D000 #define THUMB32_B_CONDITIONAL_VALUE 0xF0008000 #define THUMB32_B_IMMEDIATE11_SHIFT 0 #define THUMB32_B_IMMEDIATE11_MASK 0x7FF #define THUMB32_B_J2_BIT (1 << 11) #define THUMB32_B_J1_BIT (1 << 13) #define THUMB32_B_S_BIT (1 << 26) #define THUMB32_B_CONDITIONAL_IMMEDIATE6_SHIFT 16 #define THUMB32_B_CONDITIONAL_CONDITION_SHIFT 22 #define THUMB32_B_CONDITIONAL_CONDITION_MASK 0xF #define THUMB32_B_UNCONDITIONAL_MASK 0xF800D000 #define THUMB32_B_UNCONDITIONAL_VALUE 0xF0009000 #define THUMB32_B_UNCONDITIONAL_IMMEDIATE10_SHIFT 16 #define THUMB32_BL_MASK 0xF800C000 #define THUMB32_BL_VALUE 0xF000C000 #define THUMB32_BL_IMMEDIATE11_SHIFT 0 #define THUMB32_BL_IMMEDIATE10_SHIFT 16 #define THUMB32_BL_X_BIT (1 << 12) // // ----------------------------------------------- Internal Function Prototypes // KSTATUS ArpGetNextPcThumb ( PTRAP_FRAME TrapFrame, ULONG Instruction, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ); KSTATUS ArpGetNextPcThumb16 ( PTRAP_FRAME TrapFrame, ULONG Instruction, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ); KSTATUS ArpGetNextPcThumb32 ( PTRAP_FRAME TrapFrame, ULONG Instruction, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ); BOOL ArpIsMaskedByThumbItState ( ULONG Instruction, ULONG Cpsr, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PVOID *NextPc ); ULONG ArpGetArmRegister ( PTRAP_FRAME TrapFrame, ULONG RegisterNumber ); ULONG ArpDecodeShiftedOperand ( PTRAP_FRAME TrapFrame, ULONG Instruction ); ULONG ArpThumbGetInstructionSize ( ULONG Instruction ); BOOL ArpArmCheckConditionCode ( ULONG Cpsr, UCHAR Condition ); // // ------------------------------------------------------ Data Type Definitions // // // -------------------------------------------------------------------- Globals // // // ------------------------------------------------------------------ Functions // KSTATUS ArGetNextPc ( PTRAP_FRAME TrapFrame, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ) /*++ Routine Description: This routine attempts to predict the next instruction to be executed. It will decode the current instruction, check if the condition matches, and attempt to follow any branches. Arguments: TrapFrame - Supplies a pointer to the current machine state. ReadMemoryFunction - Supplies a pointer to a function this routine can call when it needs to read target memory. IsFunctionReturning - Supplies an optional pointer where a boolean will be stored indicating if the current instruction is a return of some kind. NextPcValue - Supplies a pointer of the next executing address. Return Value: Status code. This routine will attempt to make a guess at the next PC even if the status code is failing, but chances it's right go way down if a failing status is returned. --*/ { ULONG Address; ULONG BaseRegister; BOOL Condition; UCHAR ConditionCode; ULONG DestinationRegister; BOOL FunctionReturning; ULONG Instruction; ULONG NextPc; BOOL NotDataProcessing; ULONG Offset; ULONG Operand1; ULONG Operand1Register; ULONG Operand2; ULONG RegisterCount; ULONG Registers; ULONG Result; ULONG ShiftImmediate; KSTATUS Status; FunctionReturning = FALSE; Result = 0; // // Get the current instruction. // Address = REMOVE_THUMB_BIT((UINTN)(TrapFrame->Pc)); Status = ReadMemoryFunction((PVOID)Address, ARM_INSTRUCTION_LENGTH, &Instruction); if (!KSUCCESS(Status)) { goto GetNextPcEnd; } // // If executing in Thumb mode, use that encoding and skip all this ARM // mode stuff. // if ((TrapFrame->Cpsr & PSR_FLAG_THUMB) != 0) { Status = ArpGetNextPcThumb(TrapFrame, Instruction, ReadMemoryFunction, &FunctionReturning, (PVOID *)&NextPc); goto GetNextPcEnd; } // // The default guess is just PC + 4 in ARM mode. // NextPc = TrapFrame->Pc + 4; // // Determine whether the condition code is satisfied. If the condition is // not satisfied, there's no need to decode the instruction. // ConditionCode = (Instruction & ARM_CONDITION_CODE_MASK) >> ARM_CONDITION_CODE_SHIFT; if (ConditionCode != ARM_CONDITION_CODE_UNCONDITIONAL) { Condition = ArpArmCheckConditionCode(TrapFrame->Cpsr, ConditionCode); if (Condition == FALSE) { goto GetNextPcEnd; } } // // Attempt to decode a return from exception (RFE). // if ((Instruction & ARM_RFE_MASK) == ARM_RFE_VALUE) { BaseRegister = (Instruction & ARM_RFE_REGISTER_MASK) >> ARM_RFE_REGISTER_SHIFT; Address = ArpGetArmRegister(TrapFrame, BaseRegister); // // The RFE instruction pops the PC and CPSR. Determine the location of // the PC based on the mode. // if ((Instruction & ARM_RFE_INCREMENT) == 0) { Address -= (sizeof(PVOID) * 2); if ((Instruction & ARM_RFE_PREINDEX) == 0) { Address += sizeof(PVOID); } } else if ((Instruction & ARM_RFE_PREINDEX) != 0) { Address += sizeof(PVOID); } Status = ReadMemoryFunction((PVOID)Address, sizeof(PVOID), &NextPc); goto GetNextPcEnd; } // // Attempt to decode a branch and exchange instruction. It branches to the // contents of a register indexed by the last 4 bits of the instruction. // if ((ConditionCode != ARM_CONDITION_CODE_UNCONDITIONAL) && (((Instruction & BRANCH_EXCHANGE_MASK) == BRANCH_EXCHANGE_VALUE) || ((Instruction & BRANCH_EXCHANGE_X_MASK) == BRANCH_EXCHANGE_X_VALUE))) { if ((Instruction & 0xF) == BRANCH_EXCHANGE_LINK_REGISTER) { FunctionReturning = TRUE; } NextPc = ArpGetArmRegister(TrapFrame, Instruction & 0xF); goto GetNextPcEnd; } // // Attempt to decode a branch instruction. These instructions branch to // PC + immediate24, where the PC is 8 bytes ahead of the ARM instruction // being decoded. Recall that the guess of NextPc is already 4 ahead of the // current instruction. This mask works for both conditional and // unconditional branches. // if ((Instruction & BRANCH_MASK) == BRANCH_VALUE) { // // If this is an unconditional BLX instruction, the immediate value is // formed differently and the destination is Thumb, so the low bit // should be set in the address. // if (ConditionCode == ARM_CONDITION_CODE_UNCONDITIONAL) { Offset = (Instruction & 0x00FFFFFF) << 2; if ((Instruction & BRANCH_H_BIT) != 0) { Offset |= 0x2; } Offset |= ARM_THUMB_BIT; // // Otherwise The offset is formed by taking the lower 24 bits from the // instruction, right shifting by 2, and then sign extending. // } else { Offset = (Instruction & 0x00FFFFFF) << 2; } if ((Offset & 0x02000000) != 0) { Offset |= 0xFC000000; } NextPc += Offset + 4; goto GetNextPcEnd; } // // Attempt to decode a load register (LDR) instruction. // if ((ConditionCode != ARM_CONDITION_CODE_UNCONDITIONAL) && ((Instruction & LOAD_STORE_SINGLE_MASK) == LOAD_STORE_SINGLE_VALUE) && ((Instruction & MEDIA_MASK) != MEDIA_VALUE)) { DestinationRegister = (Instruction & DESTINATION_REGISTER_MASK) >> DESTINATION_REGISTER_SHIFT; // // This instruction only affects the PC if it's a load instruction and // the PC is the destination. Technically writebacks could affect the // PC too, but it's unlikely anyone would ever use that side effect // to manipulate the PC. // if (((Instruction & LOAD_BIT) != 0) && (DestinationRegister == REGISTER_PC)) { BaseRegister = (Instruction & LOAD_STORE_BASE_MASK) >> LOAD_STORE_BASE_SHIFT; // // In the immediate addressing form, the address is [Rn +/- #imm12], // where the immediate is in the lower 12 bits of the instruction. // if ((Instruction & IMMEDIATE_BIT) == 0) { Offset = 0; if ((Instruction & PREINDEX_BIT) != 0) { Offset = Instruction & 0x00000FFF; } // // In the pre-indexed register addressing form, the address is // [Rn +/- Rm #]. // } else if ((Instruction & PREINDEX_BIT) != 0) { Offset = ArpDecodeShiftedOperand(TrapFrame, Instruction); // // Post-indexing uses only the base register as the address. // } else { Offset = 0; } // // Now form the actual address. // if ((Instruction & ADD_SUBTRACT_BIT) != 0) { Address = ArpGetArmRegister(TrapFrame, BaseRegister) + Offset; } else { Address = ArpGetArmRegister(TrapFrame, BaseRegister) - Offset; } // // Get that byte or word. // if ((Instruction & LOAD_STORE_BYTE_BIT) != 0) { Status = ReadMemoryFunction((PVOID)Address, 1, &NextPc); if (!KSUCCESS(Status)) { goto GetNextPcEnd; } if ((NextPc & 0x00000080) != 0) { NextPc |= 0xFFFFFF00; } } else { Status = ReadMemoryFunction((PVOID)Address, 4, &NextPc); if (!KSUCCESS(Status)) { goto GetNextPcEnd; } } } goto GetNextPcEnd; } // // Attempt to decode a load/store multiple instruction. // if ((ConditionCode != ARM_CONDITION_CODE_UNCONDITIONAL) && ((Instruction & LOAD_STORE_MULTIPLE_MASK) == LOAD_STORE_MULTIPLE_VALUE)) { // // Only care about load instructions that affect the PC register. // if (((Instruction & LOAD_BIT) != 0) && ((Instruction & LOAD_STORE_MULTIPLE_PC_BIT) != 0)) { FunctionReturning = TRUE; BaseRegister = (Instruction & LOAD_STORE_BASE_MASK) >> LOAD_STORE_BASE_SHIFT; // // Count the number of registers being popped. // Registers = Instruction & REGISTER_LIST_MASK; RegisterCount = 0; while (Registers != 0) { if ((Registers & 0x1) != 0) { RegisterCount += 1; } Registers = Registers >> 1; } switch (Instruction & PUSH_POP_TYPE_MASK) { case PUSH_POP_INCREMENT_AFTER: Offset = RegisterCount - 1; break; case PUSH_POP_DECREMENT_AFTER: Offset = -(RegisterCount - 1); break; case PUSH_POP_INCREMENT_BEFORE: Offset = RegisterCount; break; case PUSH_POP_DECREMENT_BEFORE: Offset = -RegisterCount; break; default: goto GetNextPcEnd; } Address = ArpGetArmRegister(TrapFrame, BaseRegister) + (Offset * sizeof(ULONG)); Status = ReadMemoryFunction((PVOID)Address, 4, &NextPc); if (!KSUCCESS(Status)) { goto GetNextPcEnd; } } goto GetNextPcEnd; } // // Decode data processing instructions. // if ((ConditionCode != ARM_CONDITION_CODE_UNCONDITIONAL) && ((Instruction & DATA_PROCESSING_MASK) == DATA_PROCESSING_VALUE)) { NotDataProcessing = FALSE; // // The immediate form is an 8 bit value rotated right by 2 times the // shift amount. // if ((Instruction & IMMEDIATE_BIT) != 0) { // // The 16-bit immediate load and MSR instructions do not follow the // same pattern as the data processing instructions. // if ((Instruction & DATA_PROCESSING_NOT_IMMEDIATE_MASK) == DATA_PROCESSING_NOT_IMMEDIATE_VALUE) { NotDataProcessing = TRUE; } ShiftImmediate = 2 * ((Instruction & IMMEDIATE_ROTATE_MASK) >> IMMEDIATE_ROTATE_SHIFT); Operand2 = Instruction & IMMEDIATE8_MASK; while (ShiftImmediate > 0) { if ((Operand2 & 0x1) != 0) { Operand2 = (Operand2 >> 1) | 0x80000000; } else { Operand2 = (Operand2 >> 1) & 0x7FFFFFFF; } ShiftImmediate -= 1; } // // The register form is either an immediate shift or a register shift. // } else { // // If the immediate bit is not set and it's a register shift, then // check the bit that must be zero. If it's not zero, then this // isn't actually a data processing instruction (it's a multiply). // if (((Instruction & REGISTER_REGISTER_SHIFT_BIT) != 0) && ((Instruction & SHIFT_REGISTER_EMPTY_BIT) != 0)) { NotDataProcessing = TRUE; } Operand2 = ArpDecodeShiftedOperand(TrapFrame, Instruction); } Operand1Register = (Instruction & DATA_PROCESSING_OPERAND1_MASK) >> DATA_PROCESSING_OPERAND1_SHIFT; Operand1 = ArpGetArmRegister(TrapFrame, Operand1Register); // // Determine what to do based on the opcode. // switch ((Instruction & DATA_PROCESSING_OPCODE_MASK) >> DATA_PROCESSING_OPCODE_SHIFT) { case OPCODE_AND: Result = Operand1 & Operand2; break; case OPCODE_EOR: Result = Operand1 ^ Operand2; break; case OPCODE_SUB: Result = Operand1 - Operand2; break; case OPCODE_RSB: Result = Operand2 - Operand1; break; case OPCODE_ADD: Result = Operand1 + Operand2; break; case OPCODE_ADC: Result = Operand1 + Operand2; if ((TrapFrame->Cpsr & PSR_FLAG_CARRY) != 0) { Result += 1; } break; case OPCODE_SBC: Result = Operand1 - Operand2; if ((TrapFrame->Cpsr & PSR_FLAG_CARRY) == 0) { Result -= 1; } break; case OPCODE_RSC: Result = Operand2 - Operand1; if ((TrapFrame->Cpsr & PSR_FLAG_CARRY) == 0) { Result -= 1; } break; case OPCODE_ORR: Result = Operand1 | Operand2; break; case OPCODE_BIC: Result = Operand1 & (~Operand2); break; case OPCODE_MOV: Result = Operand2; if (Operand1 != 0) { NotDataProcessing = TRUE; } break; case OPCODE_MVN: Result = ~Operand2; if (Operand1 != 0) { NotDataProcessing = TRUE; } break; // // Compare instructions can't update the PC. If this is in fact a // data processing instruction, then there's nothing left to do. // case OPCODE_TST: case OPCODE_TEQ: case OPCODE_CMP: case OPCODE_CMN: if ((Instruction & SET_FLAGS_BIT) == 0) { NotDataProcessing = TRUE; } if (NotDataProcessing == FALSE) { goto GetNextPcEnd; } } // // If the destination register is the PC, then the next PC is the // result of the operation. // DestinationRegister = (Instruction & DESTINATION_REGISTER_MASK) >> DESTINATION_REGISTER_SHIFT; if (NotDataProcessing == FALSE) { if (DestinationRegister == REGISTER_PC) { NextPc = Result; } goto GetNextPcEnd; } } GetNextPcEnd: if (IsFunctionReturning != NULL) { *IsFunctionReturning = FunctionReturning; } *NextPcValue = (PVOID)NextPc; return Status; } VOID ArBackUpIfThenState ( PTRAP_FRAME TrapFrame ) /*++ Routine Description: This routine backs up the Thumb if-then state in the CPSR by one instruction, assuming that the previous instruction tested positively for being executed. Arguments: TrapFrame - Supplies a pointer to the current machine state. Return Value: Status code. This routine will attempt to make a guess at the next PC even if the status code is failing, but chances it's right go way down if a failing status is returned. --*/ { ULONG Condition; ULONG ItState; // // If the if-then state is no longer active, then it doesn't need to be // backed up (even if it was just previously active, as this instruction // is going to get executed). // if (!PSR_IS_IT_ACTIVE(TrapFrame->Cpsr)) { return; } ItState = PSR_GET_IT_STATE(TrapFrame->Cpsr); Condition = THUMB_CONDITION_FROM_IT_STATE(ItState); if (ArpArmCheckConditionCode(TrapFrame->Cpsr, Condition) != FALSE) { ItState = THUMB_RETREAT_IT_STATE(ItState, Condition & 0x1); } else { ItState = THUMB_RETREAT_IT_STATE(ItState, ((~Condition) & 0x1)); } TrapFrame->Cpsr = PSR_SET_IT_STATE(TrapFrame->Cpsr, ItState); return; } // // --------------------------------------------------------- Internal Functions // KSTATUS ArpGetNextPcThumb ( PTRAP_FRAME TrapFrame, ULONG Instruction, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ) /*++ Routine Description: This routine attempts to predict the next instruction to be executed for Thumb-2 execution. Arguments: TrapFrame - Supplies a pointer to the current machine state. Instruction - Supplies the instruction itself. ReadMemoryFunction - Supplies a pointer to a function this routine can call when it needs to read target memory. IsFunctionReturning - Supplies an optional pointer where a boolean will be stored indicating if the current instruction is a return of some kind. NextPcValue - Supplies a pointer of the next executing address. Return Value: Status code. This routine will attempt to make a guess at the next PC even if the status code is failing, but chances it's right go way down if a failing status is returned. --*/ { ULONG InstructionSize; BOOL IsMasked; KSTATUS Status; InstructionSize = ArpThumbGetInstructionSize(Instruction); *NextPcValue = (PVOID)((TrapFrame->Pc + InstructionSize) | ARM_THUMB_BIT); // // Determine if the if-then state dictates the next instruction. The // if-then instruction itself is also decoded in this routine. // IsMasked = ArpIsMaskedByThumbItState(Instruction, TrapFrame->Cpsr, ReadMemoryFunction, NextPcValue); if (IsMasked != FALSE) { return STATUS_SUCCESS; } // // Determine whether this is a 16 or 32-bit thumb instruction. // if (InstructionSize == THUMB32_INSTRUCTION_LENGTH) { // // Reverse the words of the 32-bit instruction. // Instruction = ((Instruction >> 16) & 0x0000FFFF) | ((Instruction << 16) & 0xFFFF0000); Status = ArpGetNextPcThumb32(TrapFrame, Instruction, ReadMemoryFunction, IsFunctionReturning, NextPcValue); return Status; } // // It's a 16-bit instruction. // Status = ArpGetNextPcThumb16(TrapFrame, Instruction, ReadMemoryFunction, IsFunctionReturning, NextPcValue); return Status; } KSTATUS ArpGetNextPcThumb16 ( PTRAP_FRAME TrapFrame, ULONG Instruction, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ) /*++ Routine Description: This routine attempts to predict the next instruction to be executed for 32-bit Thumb instructions. Arguments: TrapFrame - Supplies a pointer to the current machine state. Instruction - Supplies the instruction itself. ReadMemoryFunction - Supplies a pointer to a function this routine can call when it needs to read target memory. IsFunctionReturning - Supplies an optional pointer where a boolean will be stored indicating if the current instruction is a return of some kind. NextPcValue - Supplies a pointer of the next executing address. Return Value: Status code. --*/ { PVOID Address; UCHAR Condition; UINTN NextPc; ULONG RegisterCount; ULONG RegisterList; UINTN Rm; UINTN Rn; LONG SignedImmediate; KSTATUS Status; ULONG UnsignedImmediate; ULONG Value; NextPc = (UINTN)*NextPcValue; // // Handle bx and blx. Not adding the thumb bit is intentional, as blx may // change modes. // if ((Instruction & THUMB16_BX_MASK) == THUMB16_BX_VALUE) { Rm = (Instruction >> THUMB16_BX_RM_SHIFT) & THUMB_REGISTER16_MASK; if (Rm == 14) { *IsFunctionReturning = TRUE; } NextPc = ArpGetArmRegister(TrapFrame, Rm); // // Handle conditional branches. // } else if ((Instruction & THUMB16_B_CONDITIONAL_MASK) == THUMB16_B_CONDITIONAL_VALUE) { Condition = (Instruction >> THUMB16_B_CONDITIONAL_CONDITION_SHIFT) & THUMB_CONDITION_MASK; if (((Condition >> 1) != ARM_CONDITION_ALWAYS) && (ArpArmCheckConditionCode(TrapFrame->Cpsr, Condition) != FALSE)) { SignedImmediate = (CHAR)(Instruction & THUMB_IMMEDIATE8_MASK); if ((SignedImmediate & 0x80) != 0) { SignedImmediate |= 0xFFFFFF00; } SignedImmediate <<= 1; // // The signed offset is PC-relative, but the Next PC guess is only // 2 bytes ahead of the instruction pointer, when the real PC is // always 4 bytes ahead on Thumb. // NextPc += SignedImmediate + THUMB16_INSTRUCTION_LENGTH; } // // Handle unconditional branches. Sign extend the immediate. // } else if ((Instruction & THUMB16_B_UNCONDITIONAL_MASK) == THUMB16_B_UNCONDITIONAL_VALUE) { SignedImmediate = Instruction & THUMB_IMMEDIATE11_MASK; if ((SignedImmediate & (1 << 10)) != 0) { SignedImmediate |= 0xFFFFF800; } SignedImmediate <<= 1; // // The signed offset is PC-relative, but the Next PC guess is only // 2 bytes ahead of the instruction pointer, when the real PC is // always 4 bytes ahead on Thumb. // NextPc += SignedImmediate + THUMB16_INSTRUCTION_LENGTH; // // Handle compare and branch if zero (or not zero), cbz and cbnz. This // compares the encoded register value with zero (or not zero), and // branches if the comparison succeeded. // } else if ((Instruction & THUMB16_CBZ_MASK) == THUMB16_CBZ_VALUE) { Rn = Instruction & THUMB_REGISTER8_MASK; Value = ArpGetArmRegister(TrapFrame, Rn); UnsignedImmediate = (Instruction >> THUMB16_CBZ_IMMEDIATE5_SHIFT) & THUMB_IMMEDIATE5_MASK; if ((Instruction & THUMB16_CBZ_IMMEDIATE5) != 0) { UnsignedImmediate |= 1 << 5; } UnsignedImmediate <<= 1; Condition = (Value == 0); if ((Instruction & THUMB16_CBZ_NOT) != 0) { Condition = !Condition; } // // The offset is PC-relative, but the Next PC guess is only 2 bytes // ahead of the instruction pointer, when the real PC is always 4 bytes // ahead on Thumb. // if (Condition != 0) { NextPc += THUMB16_INSTRUCTION_LENGTH + UnsignedImmediate; } // // Handle a pop instruction. // } else if ((Instruction & THUMB16_POP_MASK) == THUMB16_POP_VALUE) { if ((Instruction & THUMB16_POP_PC) != 0) { *IsFunctionReturning = TRUE; RegisterList = Instruction & THUMB16_POP_REGISTER_LIST; // // Count the number of registers being popped. // RegisterCount = 0; while (RegisterList != 0) { if ((RegisterList & 0x1) != 0) { RegisterCount += 1; } RegisterList = RegisterList >> 1; } Address = (PVOID)ArpGetArmRegister(TrapFrame, 13); // // The pop action is always increment after. // Address += RegisterCount * sizeof(PVOID); Status = ReadMemoryFunction((PVOID)Address, sizeof(PVOID), &NextPc); if (!KSUCCESS(Status)) { return Status; } } } *NextPcValue = (PVOID)NextPc; return STATUS_SUCCESS; } KSTATUS ArpGetNextPcThumb32 ( PTRAP_FRAME TrapFrame, ULONG Instruction, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PBOOL IsFunctionReturning, PVOID *NextPcValue ) /*++ Routine Description: This routine attempts to predict the next instruction to be executed for 32-bit Thumb instructions. Arguments: TrapFrame - Supplies a pointer to the current machine state. Instruction - Supplies the instruction itself. ReadMemoryFunction - Supplies a pointer to a function this routine can call when it needs to read target memory. IsFunctionReturning - Supplies an optional pointer where a boolean will be stored indicating if the current instruction is a return of some kind. NextPcValue - Supplies a pointer of the next executing address. Return Value: Status code. --*/ { UINTN Address; ULONG Bit; ULONG Condition; LONG Immediate; UINTN Offset; ULONG Register; ULONG RegisterCount; ULONG RegisterList; ULONG Rm; ULONG Rn; ULONG SBit; KSTATUS Status; Status = STATUS_SUCCESS; // // Handle the rfe (return from exception) instruction. // if (((Instruction & THUMB32_RFE_MASK) == THUMB32_RFEIA_VALUE) || ((Instruction & THUMB32_RFE_MASK) == THUMB32_RFEDB_VALUE)) { *IsFunctionReturning = TRUE; Register = (Instruction & THUMB32_RFE_REGISTER_MASK) >> THUMB32_RFE_REGISTER_SHIFT; Address = ArpGetArmRegister(TrapFrame, Register); // // RFE pops the PC and CPSR from the register. For Thumb, there is only // IA and DB. For increment after, PC is located at the address stored // in the register. For decrement before, the register value minus 8 is // the location of the PC. // if ((Instruction & THUMB32_RFE_MASK) == THUMB32_RFEDB_VALUE) { Address -= 8; } Status = ReadMemoryFunction((PVOID)Address, sizeof(PVOID), NextPcValue); // // Handle ldm (load multiple) registers. They only matter if they pop the // PC. // } else if ((Instruction & THUMB32_LDM_MASK) == THUMB32_LDM_VALUE) { RegisterList = Instruction & REGISTER_LIST_MASK; if ((RegisterList & LOAD_STORE_MULTIPLE_PC_BIT) != 0) { *IsFunctionReturning = TRUE; // // Count the number of registers being popped. // RegisterCount = 0; while (RegisterList != 0) { if ((RegisterList & 0x1) != 0) { RegisterCount += 1; } RegisterList = RegisterList >> 1; } Rn = (Instruction >> THUMB32_LDM_RN_SHIFT) & THUMB_REGISTER16_MASK; Address = ArpGetArmRegister(TrapFrame, Rn); // // The pop action is either increment after or decrement before. // if ((Instruction & THUMB32_LDM_INCREMENT) != 0) { Address += (RegisterCount - 1) * sizeof(PVOID); } else { Address -= (RegisterCount + 1) * sizeof(PVOID); } Status = ReadMemoryFunction((PVOID)Address, sizeof(PVOID), NextPcValue); } // // Handle the tbb and tbh, which are table branch instructions. Rn // specifies a base of a table, and Rm specifies an index into the table. // Table branch causes a forward PC jump by the value in the table entry. // } else if ((Instruction & THUMB32_TB_MASK) == THUMB32_TB_VALUE) { Rm = (Instruction >> THUMB32_TB_RM_SHIFT) & THUMB_REGISTER16_MASK; Rn = (Instruction >> THUMB32_TB_RN_SHIFT) & THUMB_REGISTER16_MASK; Rm = ArpGetArmRegister(TrapFrame, Rm); Rn = ArpGetArmRegister(TrapFrame, Rn); Offset = 0; if ((Instruction & THUMB32_TB_HALF_WORD) != 0) { Address = Rn + (Rm << 1); Status = ReadMemoryFunction((PVOID)Address, 2, &Offset); } else { Address = Rn + Rm; Status = ReadMemoryFunction((PVOID)Address, 1, &Offset); } if (!KSUCCESS(Status)) { return Status; } // // The next PC value was already guessed to be 4 bytes ahead of the // instruction being decoded. Conveniently, that is the location of the // actually PC (from the instruction's perspective) and the offset is // PC-relative. // *NextPcValue += (Offset << 1); // // Handle the subs pc, lr, #imm8 instruction, which performs an exception // return without the stack. It copies SPSR into CPSR, and moves the link // register (offset by an unsigned immediate) to the PC. The ERET // instruction is the same as subs pc, lr, #0. // } else if ((Instruction & THUMB32_SUBS_PC_LR_MASK) == THUMB32_SUBS_PC_LR_VALUE) { *IsFunctionReturning = TRUE; Offset = Instruction & THUMB_IMMEDIATE8_MASK; *NextPcValue = (PVOID)(TrapFrame->SvcLink + Offset); // // Handle a conditional branch, which contains a signed however-many-bit // immediate and a condition code. // } else if ((Instruction & THUMB32_B_CONDITIONAL_MASK) == THUMB32_B_CONDITIONAL_VALUE) { Condition = (Instruction >> THUMB32_B_CONDITIONAL_CONDITION_SHIFT) & THUMB32_B_CONDITIONAL_CONDITION_MASK; if (((Condition >> 1) < ARM_CONDITION_ALWAYS) && (ArpArmCheckConditionCode(TrapFrame->Cpsr, Condition) != FALSE)) { Immediate = (Instruction >> THUMB32_B_IMMEDIATE11_SHIFT) & THUMB32_B_IMMEDIATE11_MASK; Immediate |= ((Instruction >> THUMB32_B_CONDITIONAL_IMMEDIATE6_SHIFT) & THUMB_IMMEDIATE6_MASK) << 11; if ((Instruction & THUMB32_B_J1_BIT) != 0) { Immediate |= (1 << 17); } if ((Instruction & THUMB32_B_J2_BIT) != 0) { Immediate |= (1 << 18); } if ((Instruction & THUMB32_B_S_BIT) != 0) { Immediate |= (1 << 19); } Immediate <<= 1; // // Sign extend. // if ((Immediate & 0x00100000) != 0) { Immediate |= 0xFFE00000; } // // This immediate offset is PC relative. On Thumb, the PC is 4 // bytes ahead of the current instruction. The original guess for // the next PC was four bytes ahead, so just add the immediate. // *NextPcValue += Immediate; } // // Handle an unconditional branch instruction. // } else if ((Instruction & THUMB32_B_UNCONDITIONAL_MASK) == THUMB32_B_UNCONDITIONAL_VALUE) { Immediate = (Instruction >> THUMB32_B_IMMEDIATE11_SHIFT) & THUMB32_B_IMMEDIATE11_MASK; Immediate |= ((Instruction >> THUMB32_B_UNCONDITIONAL_IMMEDIATE10_SHIFT) & THUMB_IMMEDIATE10_MASK) << 11; // // The next two bits are NOT(J2 EOR S) and NOT(J1 EOR S). // SBit = 0; if ((Instruction & THUMB32_B_S_BIT) != 0) { SBit = 1; } Bit = 0; if ((Instruction & THUMB32_B_J1_BIT) != 0) { Bit = 1; } Bit = !(Bit ^ SBit); if (Bit != 0) { Immediate |= 1 << 21; } Bit = 0; if ((Instruction & THUMB32_B_J2_BIT) != 0) { Bit = 1; } Bit = !(Bit ^ SBit); if (Bit != 0) { Immediate |= 1 << 22; } if (SBit != 0) { Immediate |= 1 << 23; } Immediate <<= 1; // // Sign extend. // if ((Immediate & 0x01000000) != 0) { Immediate |= 0xFE000000; } // // This immediate offset is PC relative. On Thumb, the PC is 4 bytes // ahead of the current instruction. The original guess for the next PC // was four bytes ahead, so just add the immediate. // *NextPcValue += Immediate; // // Handle the bl and blx (immediate) instructions. // } else if ((Instruction & THUMB32_BL_MASK) == THUMB32_BL_VALUE) { Immediate = ((Instruction >> THUMB32_BL_IMMEDIATE11_SHIFT) & THUMB_IMMEDIATE11_MASK) | (((Instruction >> THUMB32_BL_IMMEDIATE10_SHIFT) & THUMB_IMMEDIATE10_MASK) << 11); if ((Instruction & THUMB32_BL_X_BIT) == 0) { Immediate &= ~ARM_THUMB_BIT; } // // The next two bits are NOT(J1 EOR S) and NOT(J2 EOR S). // SBit = 0; if ((Instruction & THUMB32_B_S_BIT) != 0) { SBit = 1; } Bit = 0; if ((Instruction & THUMB32_B_J2_BIT) != 0) { Bit = 1; } Bit = !(Bit ^ SBit); if (Bit != 0) { Immediate |= 1 << 21; } Bit = 0; if ((Instruction & THUMB32_B_J1_BIT) != 0) { Bit = 1; } Bit = !(Bit ^ SBit); if (Bit != 0) { Immediate |= 1 << 22; } if (SBit != 0) { Immediate |= 1 << 23; } Immediate <<= 1; // // Sign extend. // if ((Immediate & 0x00200000) != 0) { Immediate |= 0xFFC00000; } // // BLX instructions transfer from Thumb to ARM. The low bit of the // address will be removed when aligning the PC down to a 4-byte // boundary. // Address = (UINTN)(*NextPcValue); if (((Instruction & THUMB32_BL_X_BIT) == 0) && ((Instruction & ARM_THUMB_BIT) == 0)) { Address = ALIGN_RANGE_DOWN(Address, ARM_INSTRUCTION_LENGTH); } Address += Immediate; *NextPcValue = (PVOID)Address; } return Status; } BOOL ArpIsMaskedByThumbItState ( ULONG Instruction, ULONG Cpsr, PGET_NEXT_PC_READ_MEMORY_FUNCTION ReadMemoryFunction, PVOID *NextPc ) /*++ Routine Description: This routine determines if the current instruction is disabled due to Thumb if/then state masking it. Arguments: Instruction - Supplies the next instruction itself to be executed. Cpsr - Supplies the current Program Status Register value. ReadMemoryFunction - Supplies a pointer to a function this routine can call when it needs to read target memory. NextPc - Supplies a pointer where the next PC value will be returned if it turns out that the If-Then state masks the normal next PC instruction. Return Value: TRUE if the instruction is not actually executed because the if/then state is disabling it. FALSE if the if/then state does not affect the next instruction's execution (or the if-then state dictates that the next instruction will be executed). --*/ { UCHAR Condition; ULONG InstructionSize; ULONG ItState; PVOID NextPcAddress; BOOL Result; KSTATUS Status; Result = FALSE; // // Figure out the current if-then state. If the next instruction is an // if-then instruction, use the mask set up by that. // if ((Instruction & THUMB16_IT_MASK) == THUMB16_IT_VALUE) { ItState = Instruction & THUMB16_IT_STATE_MASK; } else { ItState = PSR_GET_IT_STATE(Cpsr); // // Assume that the next instruction to execute is the one that the // breakpoint that got in here is sitting on, and advance beyond it. // ItState = THUMB_ADVANCE_IT_STATE(ItState); } // // Loop skipping instructions that are going to be masked by the if-then // state. // while (TRUE) { // // If all the if-then business is not on, return now. // if (!IS_THUMB_IT_STATE_ACTIVE(ItState)) { break; } // // If the if-then state works for the next instruction, return now. // Condition = THUMB_CONDITION_FROM_IT_STATE(ItState); if (ArpArmCheckConditionCode(Cpsr, Condition) != FALSE) { break; } // // The if-then state is going to mask the next instruction, so advance // the next PC and if-then state to the following instruction, maybe // it will get executed. The architecture specifies that branching out // in the middle of an if-then block is not allowed unless it's the // last instruction. It also specifies that instructions in an if-then // block are allowed to modify the flags. This loop doesn't handle // that case. // NextPcAddress = (PVOID)REMOVE_THUMB_BIT((UINTN)*NextPc); Status = ReadMemoryFunction(NextPcAddress, THUMB32_INSTRUCTION_LENGTH, &Instruction); if (!KSUCCESS(Status)) { break; } // // Skip over this instruction that won't get executed. // InstructionSize = ArpThumbGetInstructionSize(Instruction); *NextPc += InstructionSize; ItState = THUMB_ADVANCE_IT_STATE(ItState); Result = TRUE; } return Result; } ULONG ArpGetArmRegister ( PTRAP_FRAME TrapFrame, ULONG RegisterNumber ) /*++ Routine Description: This routine returns the register corresponding to one encoded in an ARM instruction. 0 returns the contents of r0, 1 returns the contents of r1, etc. Arguments: TrapFrame - Supplies a pointer to the current machine state. This is where the register contents come from. RegisterNumber - Supplies the register number to return. Return Value: Returns the contents of the desired register, or MAX_ULONG if the parameter was invalid. --*/ { BOOL UserMode; UserMode = FALSE; if ((TrapFrame->Cpsr & ARM_MODE_MASK) == ARM_MODE_USER) { UserMode = TRUE; } switch (RegisterNumber) { case 0: return TrapFrame->R0; case 1: return TrapFrame->R1; case 2: return TrapFrame->R2; case 3: return TrapFrame->R3; case 4: return TrapFrame->R4; case 5: return TrapFrame->R5; case 6: return TrapFrame->R6; case 7: return TrapFrame->R7; case 8: return TrapFrame->R8; case 9: return TrapFrame->R9; case 10: return TrapFrame->R10; case 11: return TrapFrame->R11; case 12: return TrapFrame->R12; case 13: if (UserMode != FALSE) { return TrapFrame->UserSp; } else { return TrapFrame->SvcSp; } case 14: if (UserMode != FALSE) { return TrapFrame->UserLink; } else { return TrapFrame->SvcLink; } // // When PC is used as an operand for a Thumb instruction, it is 4 ahead of // the current instruction (i.e. the PC stored in the trap frame). When PC // is used by an ARM instruction, it is 8 ahead of the current instruction. // case 15: if ((TrapFrame->Cpsr & PSR_FLAG_THUMB) != 0) { return TrapFrame->Pc + ARM_INSTRUCTION_LENGTH; } else { return TrapFrame->Pc + (ARM_INSTRUCTION_LENGTH * 2); } default: return MAX_ULONG; } return MAX_ULONG; } ULONG ArpDecodeShiftedOperand ( PTRAP_FRAME TrapFrame, ULONG Instruction ) /*++ Routine Description: This routine decodes the operand offset for instructions that have addressing modes of immediate shifts and register shifts. Arguments: TrapFrame - Supplies a pointer to the current machine state. This is where the register contents come from. Instruction - Supplies the instruction being decoded. It is assumed that the instruction has been decoded sufficiently to know that this is the correct addressing form. Return Value: Returns the contents of the shifted operand, which is usually added to the base register operand. --*/ { ULONG Offset; ULONG OffsetRegister; ULONG ShiftAmount; BOOL ShiftByImmediate; ULONG ShiftRegister; Offset = 0; // // Determine whether or not to shift by an immediate value or register // value. // if ((Instruction & REGISTER_REGISTER_SHIFT_BIT) != 0) { ShiftByImmediate = FALSE; ShiftRegister = (Instruction & SHIFT_REGISTER_MASK) >> SHIFT_REGISTER_SHIFT; ShiftAmount = ArpGetArmRegister(TrapFrame, ShiftRegister); if (ShiftAmount > 32) { ShiftAmount = 0; } } else { ShiftByImmediate = TRUE; ShiftAmount = (Instruction & SHIFT_IMMEDIATE_MASK) >> SHIFT_IMMEDIATE_SHIFT; } OffsetRegister = ArpGetArmRegister(TrapFrame, Instruction & OPERAND2_REGISTER_MASK); // // Determine the offset. // switch (Instruction & SHIFT_TYPE) { case SHIFT_LSL: Offset = OffsetRegister << ShiftAmount; break; // // Logical shift right fills the leftmost bits with zeroes. // Since in C this behavior is technically not // defined, enforce the zero fill by doing the shift manually. // case SHIFT_LSR: if ((ShiftByImmediate != FALSE) && (ShiftAmount == 0)) { ShiftAmount = 32; } Offset = OffsetRegister; while (ShiftAmount > 0) { Offset = (Offset >> 1) & 0x7FFFFFFF; ShiftAmount -= 1; } break; // // Arithmetic shift right fills the leftmost bits with zeroes // or ones depending on whether or not the previous bit was 0. // Again, since C is not well defined, enforce this manually. // case SHIFT_ASR: if ((ShiftByImmediate != FALSE) && (ShiftAmount == 0)) { ShiftAmount = 32; } Offset = OffsetRegister; while (ShiftAmount > 0) { if ((Offset & 0x80000000) != 0) { Offset = (Offset >> 1) | 0x80000000; } else { Offset = (Offset >> 1) & 0x7FFFFFFF; } ShiftAmount -= 1; } break; // // Rotate right is what it sounds like. Rotate right with // extend uses the carry bit as a 33rd bit. The extend is // specified with a shift immediate of 0. // case SHIFT_ROR: Offset = OffsetRegister; if ((ShiftByImmediate != FALSE) && (ShiftAmount == 0)) { if ((TrapFrame->Cpsr & PSR_FLAG_CARRY) != 0) { Offset = (Offset >> 1) | 0x80000000; } else { Offset = (Offset >> 1) & 0x7FFFFFFF; } // // Normal rotate right. // } else { while (ShiftAmount > 0) { if ((Offset & 0x00000001) != 0) { Offset = (Offset >> 1) | 0x80000000; } else { Offset = (Offset >> 1) & 0x7FFFFFFF; } ShiftAmount -= 1; } } break; } return Offset; } ULONG ArpThumbGetInstructionSize ( ULONG Instruction ) /*++ Routine Description: This routine determines the size of the given Thumb instruction. Arguments: Instruction - Supplies the instruction itself. Return Value: 2 or 4, depending on whether this is a 16-bit Thumb instruction or a 32-bit instruction. --*/ { ULONG Op; Op = (Instruction >> THUMB32_OP_SHIFT) & THUMB32_OP_MASK; // // Determine whether this is a 16 or 32-bit thumb instruction. // if (Op >= THUMB32_OP_MIN) { return THUMB32_INSTRUCTION_LENGTH; } return THUMB16_INSTRUCTION_LENGTH; } BOOL ArpArmCheckConditionCode ( ULONG Cpsr, UCHAR Condition ) /*++ Routine Description: This routine determines whether or not the condition code matches the current execution flags. Arguments: Cpsr - Supplies the current program status register flags. Condition - Supplies the condition code. Return Value: TRUE if the current condition applies. FALSE if the condition does not apply. --*/ { BOOL Result; Result = FALSE; // // Handle the upper three bits. // switch (Condition >> 1) { case ARM_CONDITION_EQUAL: if ((Cpsr & PSR_FLAG_ZERO) != 0) { Result = TRUE; } break; case ARM_CONDITION_CARRY: if ((Cpsr & PSR_FLAG_CARRY) != 0) { Result = TRUE; } break; case ARM_CONDITION_NEGATIVE: if ((Cpsr & PSR_FLAG_NEGATIVE) != 0) { Result = TRUE; } break; case ARM_CONDITION_OVERFLOW: if ((Cpsr & PSR_FLAG_OVERFLOW) != 0) { Result = TRUE; } break; case ARM_CONDITION_UNSIGNED_GREATER: if (((Cpsr & PSR_FLAG_CARRY) != 0) && ((Cpsr & PSR_FLAG_ZERO) == 0)) { Result = TRUE; } break; case ARM_CONDITION_SIGNED_GREATER_OR_EQUAL: // // Signed greater than or equal to is true when N == V. The XOR reports // when bits are different, so if it's zero, then N == V. The // comparisons against zero are necessary, otherwise different bits // are XORed. // if ((((Cpsr & PSR_FLAG_NEGATIVE) != 0) ^ ((Cpsr & PSR_FLAG_OVERFLOW) != 0)) == 0) { Result = TRUE; } break; case ARM_CONDITION_SIGNED_GREATER: // // Signed greater than is true when N == V and Z == 0. See above for // how N == V is computed if the XOR is throwing you. // if (((Cpsr & PSR_FLAG_ZERO) == 0) && ((((Cpsr & PSR_FLAG_NEGATIVE) != 0) ^ ((Cpsr & PSR_FLAG_OVERFLOW) != 0)) == 0)) { Result = TRUE; } break; case ARM_CONDITION_ALWAYS: default: Result = TRUE; break; } // // The lowest bit, if set, simply negates the result. // if ((Condition & 0x1) != 0) { Result = !Result; } return Result; }