1 /** @file
2   Specific relocation fixups for ARM architecture.
3 
4   Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5   Portions copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "BasePeCoffLibInternals.h"
11 #include <Library/BaseLib.h>
12 
13 
14 /**
15   Pass in a pointer to an ARM MOVT or MOVW immediate instruciton and
16   return the immediate data encoded in the instruction.
17 
18   @param  Instruction   Pointer to ARM MOVT or MOVW immediate instruction
19 
20   @return Immediate address encoded in the instruction
21 
22 **/
23 UINT16
ThumbMovtImmediateAddress(IN UINT16 * Instruction)24 ThumbMovtImmediateAddress (
25   IN UINT16 *Instruction
26   )
27 {
28   UINT32  Movt;
29   UINT16  Address;
30 
31   // Thumb2 is two 16-bit instructions working together. Not a single 32-bit instruction
32   // Example MOVT R0, #0 is 0x0000f2c0 or 0xf2c0 0x0000
33   Movt = (*Instruction << 16) | (*(Instruction + 1));
34 
35   // imm16 = imm4:i:imm3:imm8
36   //         imm4 -> Bit19:Bit16
37   //         i    -> Bit26
38   //         imm3 -> Bit14:Bit12
39   //         imm8 -> Bit7:Bit0
40   Address  = (UINT16)(Movt & 0x000000ff);         // imm8
41   Address |= (UINT16)((Movt >> 4) &  0x0000f700); // imm4 imm3
42   Address |= (((Movt & BIT26) != 0) ? BIT11 : 0); // i
43   return Address;
44 }
45 
46 
47 /**
48   Update an ARM MOVT or MOVW immediate instruction immediate data.
49 
50   @param  Instruction   Pointer to ARM MOVT or MOVW immediate instruction
51   @param  Address       New addres to patch into the instruction
52 **/
53 VOID
ThumbMovtImmediatePatch(IN OUT UINT16 * Instruction,IN UINT16 Address)54 ThumbMovtImmediatePatch (
55   IN OUT UINT16 *Instruction,
56   IN     UINT16 Address
57   )
58 {
59   UINT16  Patch;
60 
61   // First 16-bit chunk of instruciton
62   Patch  = ((Address >> 12) & 0x000f);            // imm4
63   Patch |= (((Address & BIT11) != 0) ? BIT10 : 0); // i
64   // Mask out instruction bits and or in address
65   *(Instruction) = (*Instruction & ~0x040f) | Patch;
66 
67   // Second 16-bit chunk of instruction
68   Patch  =  Address & 0x000000ff;          // imm8
69   Patch |=  ((Address << 4) & 0x00007000); // imm3
70   // Mask out instruction bits and or in address
71   Instruction++;
72   *Instruction = (*Instruction & ~0x70ff) | Patch;
73 }
74 
75 
76 
77 /**
78   Pass in a pointer to an ARM MOVW/MOVT instruciton pair and
79   return the immediate data encoded in the two` instruction.
80 
81   @param  Instructions  Pointer to ARM MOVW/MOVT insturction pair
82 
83   @return Immediate address encoded in the instructions
84 
85 **/
86 UINT32
ThumbMovwMovtImmediateAddress(IN UINT16 * Instructions)87 ThumbMovwMovtImmediateAddress (
88   IN UINT16 *Instructions
89   )
90 {
91   UINT16  *Word;
92   UINT16  *Top;
93 
94   Word = Instructions;  // MOVW
95   Top  = Word + 2;      // MOVT
96 
97   return (ThumbMovtImmediateAddress (Top) << 16) + ThumbMovtImmediateAddress (Word);
98 }
99 
100 
101 /**
102   Update an ARM MOVW/MOVT immediate instruction instruction pair.
103 
104   @param  Instructions  Pointer to ARM MOVW/MOVT instruction pair
105   @param  Address       New addres to patch into the instructions
106 **/
107 VOID
ThumbMovwMovtImmediatePatch(IN OUT UINT16 * Instructions,IN UINT32 Address)108 ThumbMovwMovtImmediatePatch (
109   IN OUT UINT16 *Instructions,
110   IN     UINT32 Address
111   )
112 {
113   UINT16  *Word;
114   UINT16  *Top;
115 
116   Word = Instructions;  // MOVW
117   Top  = Word + 2;      // MOVT
118 
119   ThumbMovtImmediatePatch (Word, (UINT16)(Address & 0xffff));
120   ThumbMovtImmediatePatch (Top, (UINT16)(Address >> 16));
121 }
122 
123 
124 
125 /**
126   Performs an ARM-based specific relocation fixup and is a no-op on other
127   instruction sets.
128 
129   @param  Reloc       The pointer to the relocation record.
130   @param  Fixup       The pointer to the address to fix up.
131   @param  FixupData   The pointer to a buffer to log the fixups.
132   @param  Adjust      The offset to adjust the fixup.
133 
134   @return Status code.
135 
136 **/
137 RETURN_STATUS
PeCoffLoaderRelocateImageEx(IN UINT16 * Reloc,IN OUT CHAR8 * Fixup,IN OUT CHAR8 ** FixupData,IN UINT64 Adjust)138 PeCoffLoaderRelocateImageEx (
139   IN UINT16      *Reloc,
140   IN OUT CHAR8   *Fixup,
141   IN OUT CHAR8   **FixupData,
142   IN UINT64      Adjust
143   )
144 {
145   UINT16      *Fixup16;
146   UINT32      FixupVal;
147 
148   Fixup16   = (UINT16 *) Fixup;
149 
150   switch ((*Reloc) >> 12) {
151 
152   case EFI_IMAGE_REL_BASED_ARM_MOV32T:
153     FixupVal = ThumbMovwMovtImmediateAddress (Fixup16) + (UINT32)Adjust;
154     ThumbMovwMovtImmediatePatch (Fixup16, FixupVal);
155 
156     if (*FixupData != NULL) {
157       *FixupData = ALIGN_POINTER(*FixupData, sizeof(UINT64));
158       // Fixup16 is not aligned so we must copy it. Thumb instructions are streams of 16 bytes.
159       CopyMem (*FixupData, Fixup16, sizeof (UINT64));
160       *FixupData = *FixupData + sizeof(UINT64);
161     }
162     break;
163 
164   case EFI_IMAGE_REL_BASED_ARM_MOV32A:
165      ASSERT (FALSE);
166      // break omitted - ARM instruction encoding not implemented
167   default:
168     return RETURN_UNSUPPORTED;
169   }
170 
171   return RETURN_SUCCESS;
172 }
173 
174 /**
175   Returns TRUE if the machine type of PE/COFF image is supported. Supported
176   does not mean the image can be executed it means the PE/COFF loader supports
177   loading and relocating of the image type. It's up to the caller to support
178   the entry point.
179 
180   @param  Machine   Machine type from the PE Header.
181 
182   @return TRUE if this PE/COFF loader can load the image
183 
184 **/
185 BOOLEAN
PeCoffLoaderImageFormatSupported(IN UINT16 Machine)186 PeCoffLoaderImageFormatSupported (
187   IN  UINT16  Machine
188   )
189 {
190   if ((Machine == IMAGE_FILE_MACHINE_ARMTHUMB_MIXED) || (Machine ==  IMAGE_FILE_MACHINE_EBC)) {
191     return TRUE;
192   }
193 
194   return FALSE;
195 }
196 
197 /**
198   Performs an ARM-based specific re-relocation fixup and is a no-op on other
199   instruction sets. This is used to re-relocated the image into the EFI virtual
200   space for runtime calls.
201 
202   @param  Reloc       The pointer to the relocation record.
203   @param  Fixup       The pointer to the address to fix up.
204   @param  FixupData   The pointer to a buffer to log the fixups.
205   @param  Adjust      The offset to adjust the fixup.
206 
207   @return Status code.
208 
209 **/
210 RETURN_STATUS
PeHotRelocateImageEx(IN UINT16 * Reloc,IN OUT CHAR8 * Fixup,IN OUT CHAR8 ** FixupData,IN UINT64 Adjust)211 PeHotRelocateImageEx (
212   IN UINT16      *Reloc,
213   IN OUT CHAR8   *Fixup,
214   IN OUT CHAR8   **FixupData,
215   IN UINT64      Adjust
216   )
217 {
218   UINT16  *Fixup16;
219   UINT32  FixupVal;
220 
221   Fixup16 = (UINT16 *)Fixup;
222 
223   switch ((*Reloc) >> 12) {
224 
225   case EFI_IMAGE_REL_BASED_ARM_MOV32T:
226     *FixupData  = ALIGN_POINTER (*FixupData, sizeof (UINT64));
227     if (*(UINT64 *) (*FixupData) == ReadUnaligned64 ((UINT64 *)Fixup16)) {
228       FixupVal = ThumbMovwMovtImmediateAddress (Fixup16) + (UINT32)Adjust;
229       ThumbMovwMovtImmediatePatch (Fixup16, FixupVal);
230     }
231     *FixupData = *FixupData + sizeof(UINT64);
232     break;
233 
234   case EFI_IMAGE_REL_BASED_ARM_MOV32A:
235     ASSERT (FALSE);
236     // break omitted - ARM instruction encoding not implemented
237   default:
238     DEBUG ((EFI_D_ERROR, "PeHotRelocateEx:unknown fixed type\n"));
239     return RETURN_UNSUPPORTED;
240   }
241 
242   return RETURN_SUCCESS;
243 }
244 
245