1*0eae32dcSDimitry Andric //===-- xray_hexagon.cpp --------------------------------------*- C++ ---*-===//
2*0eae32dcSDimitry Andric //
3*0eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0eae32dcSDimitry Andric //
7*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
8*0eae32dcSDimitry Andric //
9*0eae32dcSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system.
10*0eae32dcSDimitry Andric //
11*0eae32dcSDimitry Andric // Implementation of hexagon-specific routines (32-bit).
12*0eae32dcSDimitry Andric //
13*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
14*0eae32dcSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
15*0eae32dcSDimitry Andric #include "xray_defs.h"
16*0eae32dcSDimitry Andric #include "xray_interface_internal.h"
17*0eae32dcSDimitry Andric #include <assert.h>
18*0eae32dcSDimitry Andric #include <atomic>
19*0eae32dcSDimitry Andric 
20*0eae32dcSDimitry Andric namespace __xray {
21*0eae32dcSDimitry Andric 
22*0eae32dcSDimitry Andric // The machine codes for some instructions used in runtime patching.
23*0eae32dcSDimitry Andric enum PatchOpcodes : uint32_t {
24*0eae32dcSDimitry Andric   PO_JUMPI_14 = 0x5800c00a, // jump #0x014 (PC + 0x014)
25*0eae32dcSDimitry Andric   PO_CALLR_R6 = 0x50a6c000, // indirect call: callr r6
26*0eae32dcSDimitry Andric   PO_TFR_IMM = 0x78000000,  // transfer immed
27*0eae32dcSDimitry Andric                             // ICLASS 0x7 - S2-type A-type
28*0eae32dcSDimitry Andric   PO_IMMEXT = 0x00000000, // constant extender
29*0eae32dcSDimitry Andric };
30*0eae32dcSDimitry Andric 
31*0eae32dcSDimitry Andric enum PacketWordParseBits : uint32_t {
32*0eae32dcSDimitry Andric   PP_DUPLEX = 0x00 << 14,
33*0eae32dcSDimitry Andric   PP_NOT_END = 0x01 << 14,
34*0eae32dcSDimitry Andric   PP_PACKET_END = 0x03 << 14,
35*0eae32dcSDimitry Andric };
36*0eae32dcSDimitry Andric 
37*0eae32dcSDimitry Andric enum RegNum : uint32_t {
38*0eae32dcSDimitry Andric   RN_R6 = 0x6,
39*0eae32dcSDimitry Andric   RN_R7 = 0x7,
40*0eae32dcSDimitry Andric };
41*0eae32dcSDimitry Andric 
42*0eae32dcSDimitry Andric inline static uint32_t
encodeExtendedTransferImmediate(uint32_t Imm,RegNum DestReg,bool PacketEnd=false)43*0eae32dcSDimitry Andric encodeExtendedTransferImmediate(uint32_t Imm, RegNum DestReg,
44*0eae32dcSDimitry Andric                                 bool PacketEnd = false) XRAY_NEVER_INSTRUMENT {
45*0eae32dcSDimitry Andric   static const uint32_t REG_MASK = 0x1f;
46*0eae32dcSDimitry Andric   assert((DestReg & (~REG_MASK)) == 0);
47*0eae32dcSDimitry Andric   // The constant-extended register transfer encodes the 6 least
48*0eae32dcSDimitry Andric   // significant bits of the effective constant:
49*0eae32dcSDimitry Andric   Imm = Imm & 0x03f;
50*0eae32dcSDimitry Andric   const PacketWordParseBits ParseBits = PacketEnd ? PP_PACKET_END : PP_NOT_END;
51*0eae32dcSDimitry Andric 
52*0eae32dcSDimitry Andric   return PO_TFR_IMM | ParseBits | (Imm << 5) | (DestReg & REG_MASK);
53*0eae32dcSDimitry Andric }
54*0eae32dcSDimitry Andric 
55*0eae32dcSDimitry Andric inline static uint32_t
encodeConstantExtender(uint32_t Imm)56*0eae32dcSDimitry Andric encodeConstantExtender(uint32_t Imm) XRAY_NEVER_INSTRUMENT {
57*0eae32dcSDimitry Andric   // Bits   Name      Description
58*0eae32dcSDimitry Andric   // -----  -------   ------------------------------------------
59*0eae32dcSDimitry Andric   // 31:28  ICLASS    Instruction class = 0000
60*0eae32dcSDimitry Andric   // 27:16  high      High 12 bits of 26-bit constant extension
61*0eae32dcSDimitry Andric   // 15:14  Parse     Parse bits
62*0eae32dcSDimitry Andric   // 13:0   low       Low 14 bits of 26-bit constant extension
63*0eae32dcSDimitry Andric   static const uint32_t IMM_MASK_LOW = 0x03fff;
64*0eae32dcSDimitry Andric   static const uint32_t IMM_MASK_HIGH = 0x00fff << 14;
65*0eae32dcSDimitry Andric 
66*0eae32dcSDimitry Andric   // The extender encodes the 26 most significant bits of the effective
67*0eae32dcSDimitry Andric   // constant:
68*0eae32dcSDimitry Andric   Imm = Imm >> 6;
69*0eae32dcSDimitry Andric 
70*0eae32dcSDimitry Andric   const uint32_t high = (Imm & IMM_MASK_HIGH) << 16;
71*0eae32dcSDimitry Andric   const uint32_t low = Imm & IMM_MASK_LOW;
72*0eae32dcSDimitry Andric 
73*0eae32dcSDimitry Andric   return PO_IMMEXT | high | PP_NOT_END | low;
74*0eae32dcSDimitry Andric }
75*0eae32dcSDimitry Andric 
WriteInstFlushCache(void * Addr,uint32_t NewInstruction)76*0eae32dcSDimitry Andric static void WriteInstFlushCache(void *Addr, uint32_t NewInstruction) {
77*0eae32dcSDimitry Andric   asm volatile("icinva(%[inst_addr])\n\t"
78*0eae32dcSDimitry Andric                "isync\n\t"
79*0eae32dcSDimitry Andric                "memw(%[inst_addr]) = %[new_inst]\n\t"
80*0eae32dcSDimitry Andric                "dccleaninva(%[inst_addr])\n\t"
81*0eae32dcSDimitry Andric                "syncht\n\t"
82*0eae32dcSDimitry Andric                :
83*0eae32dcSDimitry Andric                : [ inst_addr ] "r"(Addr), [ new_inst ] "r"(NewInstruction)
84*0eae32dcSDimitry Andric                : "memory");
85*0eae32dcSDimitry Andric }
86*0eae32dcSDimitry Andric 
patchSled(const bool Enable,const uint32_t FuncId,const XRaySledEntry & Sled,void (* TracingHook)())87*0eae32dcSDimitry Andric inline static bool patchSled(const bool Enable, const uint32_t FuncId,
88*0eae32dcSDimitry Andric                              const XRaySledEntry &Sled,
89*0eae32dcSDimitry Andric                              void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
90*0eae32dcSDimitry Andric   // When |Enable| == true,
91*0eae32dcSDimitry Andric   // We replace the following compile-time stub (sled):
92*0eae32dcSDimitry Andric   //
93*0eae32dcSDimitry Andric   // .L_xray_sled_N:
94*0eae32dcSDimitry Andric   // <xray_sled_base>:
95*0eae32dcSDimitry Andric   // {  jump .Ltmp0 }
96*0eae32dcSDimitry Andric   // {  nop
97*0eae32dcSDimitry Andric   //    nop
98*0eae32dcSDimitry Andric   //    nop
99*0eae32dcSDimitry Andric   //    nop }
100*0eae32dcSDimitry Andric   // .Ltmp0:
101*0eae32dcSDimitry Andric 
102*0eae32dcSDimitry Andric   // With the following runtime patch:
103*0eae32dcSDimitry Andric   //
104*0eae32dcSDimitry Andric   // xray_sled_n (32-bit):
105*0eae32dcSDimitry Andric   //
106*0eae32dcSDimitry Andric   // <xray_sled_n>:
107*0eae32dcSDimitry Andric   // {  immext(#...) // upper 26-bits of func id
108*0eae32dcSDimitry Andric   //    r7 = ##...   // lower  6-bits of func id
109*0eae32dcSDimitry Andric   //    immext(#...) // upper 26-bits of trampoline
110*0eae32dcSDimitry Andric   //    r6 = ##... }  // lower 6 bits of trampoline
111*0eae32dcSDimitry Andric   // {  callr r6 }
112*0eae32dcSDimitry Andric   //
113*0eae32dcSDimitry Andric   // When |Enable|==false, we set back the first instruction in the sled to be
114*0eae32dcSDimitry Andric   // {  jump .Ltmp0 }
115*0eae32dcSDimitry Andric 
116*0eae32dcSDimitry Andric   uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.address());
117*0eae32dcSDimitry Andric   if (Enable) {
118*0eae32dcSDimitry Andric     uint32_t *CurAddress = FirstAddress + 1;
119*0eae32dcSDimitry Andric     *CurAddress = encodeExtendedTransferImmediate(FuncId, RN_R7);
120*0eae32dcSDimitry Andric     CurAddress++;
121*0eae32dcSDimitry Andric     *CurAddress = encodeConstantExtender(reinterpret_cast<uint32_t>(TracingHook));
122*0eae32dcSDimitry Andric     CurAddress++;
123*0eae32dcSDimitry Andric     *CurAddress =
124*0eae32dcSDimitry Andric         encodeExtendedTransferImmediate(reinterpret_cast<uint32_t>(TracingHook), RN_R6, true);
125*0eae32dcSDimitry Andric     CurAddress++;
126*0eae32dcSDimitry Andric 
127*0eae32dcSDimitry Andric     *CurAddress = uint32_t(PO_CALLR_R6);
128*0eae32dcSDimitry Andric 
129*0eae32dcSDimitry Andric     WriteInstFlushCache(FirstAddress, uint32_t(encodeConstantExtender(FuncId)));
130*0eae32dcSDimitry Andric   } else {
131*0eae32dcSDimitry Andric     WriteInstFlushCache(FirstAddress, uint32_t(PatchOpcodes::PO_JUMPI_14));
132*0eae32dcSDimitry Andric   }
133*0eae32dcSDimitry Andric   return true;
134*0eae32dcSDimitry Andric }
135*0eae32dcSDimitry Andric 
patchFunctionEntry(const bool Enable,const uint32_t FuncId,const XRaySledEntry & Sled,void (* Trampoline)())136*0eae32dcSDimitry Andric bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
137*0eae32dcSDimitry Andric                         const XRaySledEntry &Sled,
138*0eae32dcSDimitry Andric                         void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
139*0eae32dcSDimitry Andric   return patchSled(Enable, FuncId, Sled, Trampoline);
140*0eae32dcSDimitry Andric }
141*0eae32dcSDimitry Andric 
patchFunctionExit(const bool Enable,const uint32_t FuncId,const XRaySledEntry & Sled)142*0eae32dcSDimitry Andric bool patchFunctionExit(const bool Enable, const uint32_t FuncId,
143*0eae32dcSDimitry Andric                        const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
144*0eae32dcSDimitry Andric   return patchSled(Enable, FuncId, Sled, __xray_FunctionExit);
145*0eae32dcSDimitry Andric }
146*0eae32dcSDimitry Andric 
patchFunctionTailExit(const bool Enable,const uint32_t FuncId,const XRaySledEntry & Sled)147*0eae32dcSDimitry Andric bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId,
148*0eae32dcSDimitry Andric                            const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
149*0eae32dcSDimitry Andric   return patchSled(Enable, FuncId, Sled, __xray_FunctionExit);
150*0eae32dcSDimitry Andric }
151*0eae32dcSDimitry Andric 
patchCustomEvent(const bool Enable,const uint32_t FuncId,const XRaySledEntry & Sled)152*0eae32dcSDimitry Andric bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
153*0eae32dcSDimitry Andric                       const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
154*0eae32dcSDimitry Andric   // FIXME: Implement in hexagon?
155*0eae32dcSDimitry Andric   return false;
156*0eae32dcSDimitry Andric }
157*0eae32dcSDimitry Andric 
patchTypedEvent(const bool Enable,const uint32_t FuncId,const XRaySledEntry & Sled)158*0eae32dcSDimitry Andric bool patchTypedEvent(const bool Enable, const uint32_t FuncId,
159*0eae32dcSDimitry Andric                      const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
160*0eae32dcSDimitry Andric   // FIXME: Implement in hexagon?
161*0eae32dcSDimitry Andric   return false;
162*0eae32dcSDimitry Andric }
163*0eae32dcSDimitry Andric 
164*0eae32dcSDimitry Andric } // namespace __xray
165*0eae32dcSDimitry Andric 
__xray_ArgLoggerEntry()166*0eae32dcSDimitry Andric extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
167*0eae32dcSDimitry Andric   // FIXME: this will have to be implemented in the trampoline assembly file
168*0eae32dcSDimitry Andric }
169