| //===-- EmulateInstructionPPC64.cpp ---------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "EmulateInstructionPPC64.h" | 
 |  | 
 | #include <stdlib.h> | 
 |  | 
 | #include "lldb/Core/PluginManager.h" | 
 | #include "lldb/Symbol/UnwindPlan.h" | 
 | #include "lldb/Utility/ArchSpec.h" | 
 | #include "lldb/Utility/ConstString.h" | 
 |  | 
 | #include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" | 
 |  | 
 | #define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT | 
 | #include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" | 
 |  | 
 | #include "Plugins/Process/Utility/InstructionUtils.h" | 
 |  | 
 | using namespace lldb; | 
 | using namespace lldb_private; | 
 |  | 
 | LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionPPC64, InstructionPPC64) | 
 |  | 
 | EmulateInstructionPPC64::EmulateInstructionPPC64(const ArchSpec &arch) | 
 |     : EmulateInstruction(arch) {} | 
 |  | 
 | void EmulateInstructionPPC64::Initialize() { | 
 |   PluginManager::RegisterPlugin(GetPluginNameStatic(), | 
 |                                 GetPluginDescriptionStatic(), CreateInstance); | 
 | } | 
 |  | 
 | void EmulateInstructionPPC64::Terminate() { | 
 |   PluginManager::UnregisterPlugin(CreateInstance); | 
 | } | 
 |  | 
 | ConstString EmulateInstructionPPC64::GetPluginNameStatic() { | 
 |   ConstString g_plugin_name("lldb.emulate-instruction.ppc64"); | 
 |   return g_plugin_name; | 
 | } | 
 |  | 
 | ConstString EmulateInstructionPPC64::GetPluginName() { | 
 |   static ConstString g_plugin_name("EmulateInstructionPPC64"); | 
 |   return g_plugin_name; | 
 | } | 
 |  | 
 | const char *EmulateInstructionPPC64::GetPluginDescriptionStatic() { | 
 |   return "Emulate instructions for the PPC64 architecture."; | 
 | } | 
 |  | 
 | EmulateInstruction * | 
 | EmulateInstructionPPC64::CreateInstance(const ArchSpec &arch, | 
 |                                         InstructionType inst_type) { | 
 |   if (EmulateInstructionPPC64::SupportsEmulatingInstructionsOfTypeStatic( | 
 |           inst_type)) | 
 |     if (arch.GetTriple().isPPC64()) | 
 |       return new EmulateInstructionPPC64(arch); | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::SetTargetTriple(const ArchSpec &arch) { | 
 |   return arch.GetTriple().isPPC64(); | 
 | } | 
 |  | 
 | static bool LLDBTableGetRegisterInfo(uint32_t reg_num, RegisterInfo ®_info) { | 
 |   if (reg_num >= llvm::array_lengthof(g_register_infos_ppc64le)) | 
 |     return false; | 
 |   reg_info = g_register_infos_ppc64le[reg_num]; | 
 |   return true; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::GetRegisterInfo(RegisterKind reg_kind, | 
 |                                               uint32_t reg_num, | 
 |                                               RegisterInfo ®_info) { | 
 |   if (reg_kind == eRegisterKindGeneric) { | 
 |     switch (reg_num) { | 
 |     case LLDB_REGNUM_GENERIC_PC: | 
 |       reg_kind = eRegisterKindLLDB; | 
 |       reg_num = gpr_pc_ppc64le; | 
 |       break; | 
 |     case LLDB_REGNUM_GENERIC_SP: | 
 |       reg_kind = eRegisterKindLLDB; | 
 |       reg_num = gpr_r1_ppc64le; | 
 |       break; | 
 |     case LLDB_REGNUM_GENERIC_RA: | 
 |       reg_kind = eRegisterKindLLDB; | 
 |       reg_num = gpr_lr_ppc64le; | 
 |       break; | 
 |     case LLDB_REGNUM_GENERIC_FLAGS: | 
 |       reg_kind = eRegisterKindLLDB; | 
 |       reg_num = gpr_cr_ppc64le; | 
 |       break; | 
 |  | 
 |     default: | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   if (reg_kind == eRegisterKindLLDB) | 
 |     return LLDBTableGetRegisterInfo(reg_num, reg_info); | 
 |   return false; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::ReadInstruction() { | 
 |   bool success = false; | 
 |   m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, | 
 |                                 LLDB_INVALID_ADDRESS, &success); | 
 |   if (success) { | 
 |     Context ctx; | 
 |     ctx.type = eContextReadOpcode; | 
 |     ctx.SetNoArgs(); | 
 |     m_opcode.SetOpcode32(ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success), | 
 |                          GetByteOrder()); | 
 |   } | 
 |   if (!success) | 
 |     m_addr = LLDB_INVALID_ADDRESS; | 
 |   return success; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::CreateFunctionEntryUnwind( | 
 |     UnwindPlan &unwind_plan) { | 
 |   unwind_plan.Clear(); | 
 |   unwind_plan.SetRegisterKind(eRegisterKindLLDB); | 
 |  | 
 |   UnwindPlan::RowSP row(new UnwindPlan::Row); | 
 |  | 
 |   // Our previous Call Frame Address is the stack pointer | 
 |   row->GetCFAValue().SetIsRegisterPlusOffset(gpr_r1_ppc64le, 0); | 
 |  | 
 |   unwind_plan.AppendRow(row); | 
 |   unwind_plan.SetSourceName("EmulateInstructionPPC64"); | 
 |   unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); | 
 |   unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); | 
 |   unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); | 
 |   unwind_plan.SetReturnAddressRegister(gpr_lr_ppc64le); | 
 |   return true; | 
 | } | 
 |  | 
 | EmulateInstructionPPC64::Opcode * | 
 | EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) { | 
 |   static EmulateInstructionPPC64::Opcode g_opcodes[] = { | 
 |       {0xfc0007ff, 0x7c0002a6, &EmulateInstructionPPC64::EmulateMFSPR, | 
 |        "mfspr RT, SPR"}, | 
 |       {0xfc000003, 0xf8000000, &EmulateInstructionPPC64::EmulateSTD, | 
 |        "std RS, DS(RA)"}, | 
 |       {0xfc000003, 0xf8000001, &EmulateInstructionPPC64::EmulateSTD, | 
 |        "stdu RS, DS(RA)"}, | 
 |       {0xfc0007fe, 0x7c000378, &EmulateInstructionPPC64::EmulateOR, | 
 |        "or RA, RS, RB"}, | 
 |       {0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI, | 
 |        "addi RT, RA, SI"}, | 
 |       {0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD, | 
 |        "ld RT, DS(RA)"}}; | 
 |   static const size_t k_num_ppc_opcodes = llvm::array_lengthof(g_opcodes); | 
 |  | 
 |   for (size_t i = 0; i < k_num_ppc_opcodes; ++i) { | 
 |     if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value) | 
 |       return &g_opcodes[i]; | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { | 
 |   const uint32_t opcode = m_opcode.GetOpcode32(); | 
 |   // LLDB_LOG(log, "PPC64::EvaluateInstruction: opcode={0:X+8}", opcode); | 
 |   Opcode *opcode_data = GetOpcodeForInstruction(opcode); | 
 |   if (!opcode_data) | 
 |     return false; | 
 |  | 
 |   // LLDB_LOG(log, "PPC64::EvaluateInstruction: {0}", opcode_data->name); | 
 |   const bool auto_advance_pc = | 
 |       evaluate_options & eEmulateInstructionOptionAutoAdvancePC; | 
 |  | 
 |   bool success = false; | 
 |  | 
 |   uint32_t orig_pc_value = 0; | 
 |   if (auto_advance_pc) { | 
 |     orig_pc_value = | 
 |         ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); | 
 |     if (!success) | 
 |       return false; | 
 |   } | 
 |  | 
 |   // Call the Emulate... function. | 
 |   success = (this->*opcode_data->callback)(opcode); | 
 |   if (!success) | 
 |     return false; | 
 |  | 
 |   if (auto_advance_pc) { | 
 |     uint32_t new_pc_value = | 
 |         ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success); | 
 |     if (!success) | 
 |       return false; | 
 |  | 
 |     if (new_pc_value == orig_pc_value) { | 
 |       EmulateInstruction::Context context; | 
 |       context.type = eContextAdvancePC; | 
 |       context.SetNoArgs(); | 
 |       if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_pc_ppc64le, | 
 |                                  orig_pc_value + 4)) | 
 |         return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::EmulateMFSPR(uint32_t opcode) { | 
 |   uint32_t rt = Bits32(opcode, 25, 21); | 
 |   uint32_t spr = Bits32(opcode, 20, 11); | 
 |  | 
 |   enum { SPR_LR = 0x100 }; | 
 |  | 
 |   // For now, we're only insterested in 'mfspr r0, lr' | 
 |   if (rt != gpr_r0_ppc64le || spr != SPR_LR) | 
 |     return false; | 
 |  | 
 |   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); | 
 |   LLDB_LOG(log, "EmulateMFSPR: {0:X+8}: mfspr r0, lr", m_addr); | 
 |  | 
 |   bool success; | 
 |   uint64_t lr = | 
 |       ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); | 
 |   if (!success) | 
 |     return false; | 
 |   Context context; | 
 |   context.type = eContextWriteRegisterRandomBits; | 
 |   WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_r0_ppc64le, lr); | 
 |   LLDB_LOG(log, "EmulateMFSPR: success!"); | 
 |   return true; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::EmulateLD(uint32_t opcode) { | 
 |   uint32_t rt = Bits32(opcode, 25, 21); | 
 |   uint32_t ra = Bits32(opcode, 20, 16); | 
 |   uint32_t ds = Bits32(opcode, 15, 2); | 
 |  | 
 |   int32_t ids = llvm::SignExtend32<16>(ds << 2); | 
 |  | 
 |   // For now, tracking only loads from 0(r1) to r1 (0(r1) is the ABI defined | 
 |   // location to save previous SP) | 
 |   if (ra != gpr_r1_ppc64le || rt != gpr_r1_ppc64le || ids != 0) | 
 |     return false; | 
 |  | 
 |   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); | 
 |   LLDB_LOG(log, "EmulateLD: {0:X+8}: ld r{1}, {2}(r{3})", m_addr, rt, ids, ra); | 
 |  | 
 |   RegisterInfo r1_info; | 
 |   if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info)) | 
 |     return false; | 
 |  | 
 |   // restore SP | 
 |   Context ctx; | 
 |   ctx.type = eContextRestoreStackPointer; | 
 |   ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0); | 
 |  | 
 |   WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, 0); | 
 |   LLDB_LOG(log, "EmulateLD: success!"); | 
 |   return true; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::EmulateSTD(uint32_t opcode) { | 
 |   uint32_t rs = Bits32(opcode, 25, 21); | 
 |   uint32_t ra = Bits32(opcode, 20, 16); | 
 |   uint32_t ds = Bits32(opcode, 15, 2); | 
 |   uint32_t u = Bits32(opcode, 1, 0); | 
 |  | 
 |   // For now, tracking only stores to r1 | 
 |   if (ra != gpr_r1_ppc64le) | 
 |     return false; | 
 |   // ... and only stores of SP, FP and LR (moved into r0 by a previous mfspr) | 
 |   if (rs != gpr_r1_ppc64le && rs != gpr_r31_ppc64le && rs != gpr_r30_ppc64le && | 
 |       rs != gpr_r0_ppc64le) | 
 |     return false; | 
 |  | 
 |   bool success; | 
 |   uint64_t rs_val = ReadRegisterUnsigned(eRegisterKindLLDB, rs, 0, &success); | 
 |   if (!success) | 
 |     return false; | 
 |  | 
 |   int32_t ids = llvm::SignExtend32<16>(ds << 2); | 
 |   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); | 
 |   LLDB_LOG(log, "EmulateSTD: {0:X+8}: std{1} r{2}, {3}(r{4})", m_addr, | 
 |            u ? "u" : "", rs, ids, ra); | 
 |  | 
 |   // Make sure that r0 is really holding LR value (this won't catch unlikely | 
 |   // cases, such as r0 being overwritten after mfspr) | 
 |   uint32_t rs_num = rs; | 
 |   if (rs == gpr_r0_ppc64le) { | 
 |     uint64_t lr = | 
 |         ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success); | 
 |     if (!success || lr != rs_val) | 
 |       return false; | 
 |     rs_num = gpr_lr_ppc64le; | 
 |   } | 
 |  | 
 |   // set context | 
 |   RegisterInfo rs_info; | 
 |   if (!GetRegisterInfo(eRegisterKindLLDB, rs_num, rs_info)) | 
 |     return false; | 
 |   RegisterInfo ra_info; | 
 |   if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info)) | 
 |     return false; | 
 |  | 
 |   Context ctx; | 
 |   ctx.type = eContextPushRegisterOnStack; | 
 |   ctx.SetRegisterToRegisterPlusOffset(rs_info, ra_info, ids); | 
 |  | 
 |   // store | 
 |   uint64_t ra_val = ReadRegisterUnsigned(eRegisterKindLLDB, ra, 0, &success); | 
 |   if (!success) | 
 |     return false; | 
 |  | 
 |   lldb::addr_t addr = ra_val + ids; | 
 |   WriteMemory(ctx, addr, &rs_val, sizeof(rs_val)); | 
 |  | 
 |   // update RA? | 
 |   if (u) { | 
 |     Context ctx; | 
 |     // NOTE Currently, RA will always be equal to SP(r1) | 
 |     ctx.type = eContextAdjustStackPointer; | 
 |     WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, addr); | 
 |   } | 
 |  | 
 |   LLDB_LOG(log, "EmulateSTD: success!"); | 
 |   return true; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::EmulateOR(uint32_t opcode) { | 
 |   uint32_t rs = Bits32(opcode, 25, 21); | 
 |   uint32_t ra = Bits32(opcode, 20, 16); | 
 |   uint32_t rb = Bits32(opcode, 15, 11); | 
 |  | 
 |   // to be safe, process only the known 'mr r31/r30, r1' prologue instructions | 
 |   if (m_fp != LLDB_INVALID_REGNUM || rs != rb || | 
 |       (ra != gpr_r30_ppc64le && ra != gpr_r31_ppc64le) || rb != gpr_r1_ppc64le) | 
 |     return false; | 
 |  | 
 |   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); | 
 |   LLDB_LOG(log, "EmulateOR: {0:X+8}: mr r{1}, r{2}", m_addr, ra, rb); | 
 |  | 
 |   // set context | 
 |   RegisterInfo ra_info; | 
 |   if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info)) | 
 |     return false; | 
 |  | 
 |   Context ctx; | 
 |   ctx.type = eContextSetFramePointer; | 
 |   ctx.SetRegister(ra_info); | 
 |  | 
 |   // move | 
 |   bool success; | 
 |   uint64_t rb_val = ReadRegisterUnsigned(eRegisterKindLLDB, rb, 0, &success); | 
 |   if (!success) | 
 |     return false; | 
 |   WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, rb_val); | 
 |   m_fp = ra; | 
 |   LLDB_LOG(log, "EmulateOR: success!"); | 
 |   return true; | 
 | } | 
 |  | 
 | bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) { | 
 |   uint32_t rt = Bits32(opcode, 25, 21); | 
 |   uint32_t ra = Bits32(opcode, 20, 16); | 
 |   uint32_t si = Bits32(opcode, 15, 0); | 
 |  | 
 |   // handle stack adjustments only | 
 |   // (this is a typical epilogue operation, with ra == r1. If it's | 
 |   //  something else, then we won't know the correct value of ra) | 
 |   if (rt != gpr_r1_ppc64le || ra != gpr_r1_ppc64le) | 
 |     return false; | 
 |  | 
 |   int32_t si_val = llvm::SignExtend32<16>(si); | 
 |   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); | 
 |   LLDB_LOG(log, "EmulateADDI: {0:X+8}: addi r1, r1, {1}", m_addr, si_val); | 
 |  | 
 |   // set context | 
 |   RegisterInfo r1_info; | 
 |   if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info)) | 
 |     return false; | 
 |  | 
 |   Context ctx; | 
 |   ctx.type = eContextRestoreStackPointer; | 
 |   ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0); | 
 |  | 
 |   // adjust SP | 
 |   bool success; | 
 |   uint64_t r1 = | 
 |       ReadRegisterUnsigned(eRegisterKindLLDB, gpr_r1_ppc64le, 0, &success); | 
 |   if (!success) | 
 |     return false; | 
 |   WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val); | 
 |   LLDB_LOG(log, "EmulateADDI: success!"); | 
 |   return true; | 
 | } |